本地优化

This commit is contained in:
2026-06-03 22:05:03 +08:00
parent a6509ea9ee
commit fea67869f2
18 changed files with 996 additions and 65 deletions

View File

@@ -0,0 +1,381 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using Newtonsoft.Json;
using RenderStreaming;
using Stary.Evo;
using Unity.RenderStreaming;
using Unity.WebRTC;
using UnityEngine;
namespace Script.Recorder
{
public class ServerMixedRecorder : IVideoRecorder, IController
{
public bool IsRecording { get; }
public Action OnStartedRecordingVideo { get; set; }
public Action<string> OnStoppedRecordingVideoAction { get; set; }
private string recordingId;
private readonly Dictionary<string, RTCPeerConnection> recordingPeers = new();
// [Header("Tracks")] public RenderTexture sourceRenderTexture;
// public string microphoneDeviceName = ""; // 留空使用默认麦克风
// public int microphoneSampleRate = 48000;
//
// private AudioSource microphoneAudioSource;
// private bool microphoneStarted;
private VideoStreamTrack videoTrack;
private AudioStreamTrack audioTrack;
public async void StartRecording()
{
await CreateLocalTracks();
var messageChannel = GameObject.FindObjectOfType<MessageChannel>();
messageChannel.OnRecordingPeerRequestReceived += OnRecordingPeerRequestReceived;
messageChannel.OnRecordingAnswerReceived += OnRecordingAnswerReceived;
messageChannel.OnRecordingCandidateReceived += OnRecordingCandidateReceived;
messageChannel.OnRecordingStoppedReceived += OnRecordingStoppedReceived;
var data = new
{
connectionId = this.GetSystem<IGlobalConfigSystem>().GetConnectionId(),
layout = "grid",
format = "webm",
};
var result = await WebRequestSystem.Post(
this.GetSystem<IGlobalConfigSystem>().IP + "/api/recording-sessions",
JsonConvert.SerializeObject(data));
if (result != null)
{
RecordingSession session = JsonConvert.DeserializeObject<RecordingSession>(result);
if (string.IsNullOrEmpty(session.success))
{
Debug.LogError($"[ServerMixedRecorder] StartRecording 失败: {session}");
return;
}
recordingId = session.session.id;
}
}
public async UniTask CreateLocalTracks()
{
var RenderStreaming =GameObject.Find("RenderStreaming");
VideoStreamSender videoStreamSender = RenderStreaming.GetComponent<VideoStreamSender>();
videoTrack = videoStreamSender.Track as VideoStreamTrack;
AudioStreamSender audioStreamSender = RenderStreaming.GetComponent<AudioStreamSender>();
audioTrack = audioStreamSender.Track as AudioStreamTrack;
// sourceRenderTexture = this.GetSystem<IRenderStreamingSystem>().GetRenderStreamingTexture();
// if (sourceRenderTexture != null)
// {
// videoTrack = new VideoStreamTrack(sourceRenderTexture);
// }
//
//
// microphoneAudioSource =
// GameObject.Find("RenderStreaming/microphoneAudioSource").GetComponent<AudioSource>();
// microphoneAudioSource.loop = true;
// microphoneAudioSource.mute = true;
//
// string device = string.IsNullOrEmpty(microphoneDeviceName)
// ? null
// : microphoneDeviceName;
//
// microphoneAudioSource.clip = Microphone.Start(
// device,
// true,
// 1,
// microphoneSampleRate
// );
//
// microphoneStarted = true;
//await CreateMicrophoneTrackWhenReady(device);
}
// public async UniTask CreateMicrophoneTrackWhenReady(string device)
// {
// while (Microphone.GetPosition(device) <= 0)
// await UniTask.NextFrame();
//
// microphoneAudioSource.Play();
// audioTrack = new AudioStreamTrack(microphoneAudioSource);
// }
public async void OnRecordingPeerRequestReceived(RecordingRequest request)
{
if (string.IsNullOrEmpty(request.recordingId))
return;
OnRecordingStoppedReceived(request.recordingId);
var config = new RTCConfiguration
{
iceServers = new[]
{
new RTCIceServer { urls = new[] { "stun:stun.l.google.com:19302" } }
}
};
var pc = new RTCPeerConnection(ref config);
recordingPeers[request.recordingId] = pc;
pc.OnIceCandidate = candidate =>
{
if (candidate == null) return;
Send("recording-candidate", new RecordingCandidate
{
recordingId = request.recordingId,
connectionId = request.connectionId,
participantId = this.GetSystem<IGlobalConfigSystem>().GetParticipantId(),
candidate = candidate.Candidate,
sdpMid = candidate.SdpMid,
sdpMLineIndex = candidate.SdpMLineIndex ?? 0
});
};
if (videoTrack != null)
pc.AddTransceiver(videoTrack,
new RTCRtpTransceiverInit { direction = RTCRtpTransceiverDirection.SendOnly });
if (audioTrack != null)
pc.AddTransceiver(audioTrack,
new RTCRtpTransceiverInit { direction = RTCRtpTransceiverDirection.SendOnly });
var offerOp = pc.CreateOffer();
await offerOp;
var offer = offerOp.Desc;
var localOp = pc.SetLocalDescription(ref offer);
await localOp;
Send("recording-offer", new RecordingOffer
{
recordingId = request.recordingId,
connectionId = request.connectionId,
participantId = this.GetSystem<IGlobalConfigSystem>().GetParticipantId(),
sdp = offer.sdp
});
}
public async void OnRecordingAnswerReceived(RecordingAnswer answer)
{
if (!recordingPeers.TryGetValue(answer.recordingId, out var pc))
return;
var desc = new RTCSessionDescription
{
type = RTCSdpType.Answer,
sdp = answer.sdp
};
var op = pc.SetRemoteDescription(ref desc);
await op;
}
public void OnRecordingCandidateReceived(RecordingCandidate data)
{
if (!recordingPeers.TryGetValue(data.recordingId, out var pc))
return;
var candidate = new RTCIceCandidate(new RTCIceCandidateInit
{
candidate = data.candidate,
sdpMid = data.sdpMid,
sdpMLineIndex = data.sdpMLineIndex
});
pc.AddIceCandidate(candidate);
}
public void OnRecordingStoppedReceived(string recordingId)
{
if (!recordingPeers.TryGetValue(recordingId, out var pc))
return;
pc.Close();
recordingPeers.Remove(recordingId);
}
void Send(string type, object data)
{
var connectionId = this.GetSystem<IGlobalConfigSystem>().GetConnectionId();
var json = JsonConvert.SerializeObject(new Dictionary<string, object>
{
["type"] = "on-message",
["data"] = new Dictionary<string, object>
{
["connectionId"] = connectionId,
["message"] = new Dictionary<string, object>
{
["type"] = type,
["data"] = data
}
}
});
SignalingMessageHelper.SendMessage(json);
}
public async void StopRecording()
{
await WebRequestSystem.Delete(this.GetSystem<IGlobalConfigSystem>().IP,
$"/api/recording-sessions/{recordingId}");
var messageChannel = GameObject.FindObjectOfType<MessageChannel>();
messageChannel.OnRecordingPeerRequestReceived -= OnRecordingPeerRequestReceived;
messageChannel.OnRecordingAnswerReceived -= OnRecordingAnswerReceived;
messageChannel.OnRecordingCandidateReceived -= OnRecordingCandidateReceived;
messageChannel.OnRecordingStoppedReceived -= OnRecordingStoppedReceived;
}
void OnDestroy()
{
foreach (var pc in recordingPeers.Values)
pc.Close();
recordingPeers.Clear();
videoTrack?.Dispose();
audioTrack?.Dispose();
// if (microphoneStarted)
// {
// string device = string.IsNullOrEmpty(microphoneDeviceName)
// ? null
// : microphoneDeviceName;
//
// Microphone.End(device);
// }
}
public IArchitecture GetArchitecture()
{
return MainArchitecture.Interface;
}
}
[Serializable]
public class Session
{
/// <summary>
///
/// </summary>
public string id { get; set; }
/// <summary>
///
/// </summary>
public string connectionId { get; set; }
/// <summary>
///
/// </summary>
public string status { get; set; }
/// <summary>
///
/// </summary>
public string layout { get; set; }
/// <summary>
///
/// </summary>
public string format { get; set; }
/// <summary>
///
/// </summary>
public string createdAt { get; set; }
/// <summary>
///
/// </summary>
public string startedAt { get; set; }
/// <summary>
///
/// </summary>
public string updatedAt { get; set; }
}
[Serializable]
public class Agent
{
/// <summary>
///
/// </summary>
public string id { get; set; }
/// <summary>
///
/// </summary>
public string recordingId { get; set; }
/// <summary>
///
/// </summary>
public string connectionId { get; set; }
/// <summary>
///
/// </summary>
public string status { get; set; }
/// <summary>
///
/// </summary>
public string mediaMode { get; set; }
/// <summary>
///
/// </summary>
public string createdAt { get; set; }
/// <summary>
///
/// </summary>
public string updatedAt { get; set; }
}
[Serializable]
public class RecordingSession
{
/// <summary>
///
/// </summary>
public string success { get; set; }
/// <summary>
///
/// </summary>
public Session session { get; set; }
/// <summary>
///
/// </summary>
public Agent agent { get; set; }
/// <summary>
///
/// </summary>
public string notified { get; set; }
/// <summary>
///
/// </summary>
public string peerRequestNotified { get; set; }
}
}