From 6e555569c0d91183b19402f28bdcbfc5ae25a5b2 Mon Sep 17 00:00:00 2001 From: Cal <42492716+ly3027929699@users.noreply.github.com> Date: Mon, 18 Nov 2024 20:12:00 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BD=95=E5=83=8F=E5=B8=A7?= =?UTF-8?q?=E7=8E=87=EF=BC=8C=E6=9C=80=E5=A4=A7=E5=B8=A7=E7=8E=87=E6=8F=90?= =?UTF-8?q?=E5=8D=87=E5=88=B060=E5=B8=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/RTMPPublisher.cs | 134 ++++++++++++++++++++++++++++--------- Build/nginx/logs/error.log | 11 +++ Build/播放器.bat | 2 +- 3 files changed, 114 insertions(+), 33 deletions(-) diff --git a/Assets/RTMPPublisher.cs b/Assets/RTMPPublisher.cs index 4004b73..90a040c 100644 --- a/Assets/RTMPPublisher.cs +++ b/Assets/RTMPPublisher.cs @@ -1,4 +1,7 @@ using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; @@ -17,6 +20,7 @@ namespace ZC public class RTMPPublisher : MonoBehaviour { Thread _thread; + private Timer _timer; Process _process; private RectInt _rectInt; private RTMPConfig _config; @@ -30,12 +34,16 @@ namespace ZC private volatile int _sendData; - private NativeArray _textureData; private AsyncGPUReadbackRequest _asyncGPUReadbackRequest; private bool _isRequesting; private byte[] _bitmapArray; + private ConcurrentQueue> _textureDataPool; + private Queue<(AsyncGPUReadbackRequest, NativeArray)> _textureDatas; + private ConcurrentQueue<(AsyncGPUReadbackRequest, NativeArray)> _doneTextureDatas; + private int _targetTextureWidth; + private int _targetTextureHeight; public void SetCamera(Camera camera) { @@ -52,10 +60,40 @@ namespace ZC return; } + _textureDataPool = new ConcurrentQueue>(); + _textureDatas = new Queue<(AsyncGPUReadbackRequest, NativeArray)>(); + _doneTextureDatas = new ConcurrentQueue<(AsyncGPUReadbackRequest, NativeArray)>(); _config.Load(); + _timer = new Timer(Tick, null, TimeSpan.FromMilliseconds(0), TimeSpan.FromMilliseconds(16)); Setup(Camera.main); } + /// + /// 其他线程tick + /// + /// + private void Tick(object state) + { + try + { + if (this._doneTextureDatas.TryDequeue(out var result)) + { + var textureData = result.Item2; + Profiler.BeginSample("Conversion"); + Bitmap.EncodeToBitmap(textureData, _bitmapArray, 0, textureData.Length, _targetTextureWidth, this._targetTextureHeight); + Profiler.EndSample(); + Profiler.BeginSample("WriteBuffer"); + _process.StandardInput.BaseStream.Write(_bitmapArray, 0, _bitmapArray.Length); + Profiler.EndSample(); + _textureDataPool.Enqueue(result.Item2); + } + } + catch (Exception e) + { + UnityEngine.Debug.LogError(e.ToString()); + } + } + private void CreateCaptureThread() { this._thread = new Thread(this.CreateThread); @@ -72,37 +110,59 @@ namespace ZC { if (_sendData != 0) { - if (_camera) + if (_camera && !_isRequesting) { - if (!_isRequesting || _asyncGPUReadbackRequest.done) + var cameraActiveTexture = _camera.targetTexture; + RenderTexture.active = cameraActiveTexture; + if (!_textureDataPool.TryDequeue(out var nativeArray)) { - var cameraActiveTexture = _camera.targetTexture; - RenderTexture.active = cameraActiveTexture; - _asyncGPUReadbackRequest = AsyncGPUReadback.RequestIntoNativeArray(ref _textureData, cameraActiveTexture, 0, - GraphicsFormat.B8G8R8_SRGB, ReadTextureCallback); - _isRequesting = true; + var targetTextureByteCount = this._camera.targetTexture.width * _camera.targetTexture.height * 3; + nativeArray = new NativeArray(targetTextureByteCount, Allocator.Persistent); + } + + var asyncGPUReadbackRequest = AsyncGPUReadback.RequestIntoNativeArray(ref nativeArray, cameraActiveTexture, 0, GraphicsFormat.B8G8R8_SRGB, null); + _textureDatas.Enqueue((asyncGPUReadbackRequest, nativeArray)); + } + + if (_textureDatas.TryPeek(out var result)) + { + if (result.Item1.done) + { + this._textureDatas.Dequeue(); + if (result.Item1.hasError) + { + Debug.LogError("Request GPU DATA has error"); + } + else + { + //放入多线程计算 + this._doneTextureDatas.Enqueue(result); + } } } } } - private void ReadTextureCallback(AsyncGPUReadbackRequest obj) - { - if (obj is { done: true, hasError: false } && this._isRequesting) - { - Profiler.BeginSample("Conversion"); - Bitmap.EncodeToBitmap(_textureData, _bitmapArray, 0, _textureData.Length, _output.width, _output.height); -// var encodeNativeArrayToJPG = ImageConversion.EncodeNativeArrayToJPG(this._textureData, GraphicsFormat.R8G8B8_UNorm, (uint)this._output.width, (uint)this._output.height); - Profiler.EndSample(); - Profiler.BeginSample("WriteBuffer"); - _process.StandardInput.BaseStream.Write(_bitmapArray, 0, _bitmapArray.Length); - Profiler.EndSample(); -// encodeNativeArrayToJPG.Dispose();/ - } - } - private void DisposeCaptureThread() { + foreach (var textureData in this._textureDatas) + { + textureData.Item2.Dispose(); + } + + _textureDatas.Clear(); + foreach (var doneTextureData in this._doneTextureDatas) + { + doneTextureData.Item2.Dispose(); + } + + _doneTextureDatas.Clear(); + + foreach (var nativeArray in this._textureDataPool) + { + nativeArray.Dispose(); + } + if (_process != null && !this._process.HasExited) { GenerateConsoleCtrlEvent(ConsoleCtrlEvent.CTRL_C, 0); @@ -130,11 +190,11 @@ namespace ZC processStartInfo.Arguments = $" -probesize 32 -thread_queue_size 5096 -fflags discardcorrupt -flags low_delay -analyzeduration 0 " + $" -rtbufsize 100M -f dshow -i audio=\"virtual-audio-capturer\" " + - $" -f image2pipe -use_wallclock_as_timestamps 1 -i - " + + $" -f image2pipe -use_wallclock_as_timestamps 1 -r 60 -i - " + $" -loglevel info " + $" -map 0:a:0 -map 1:v:0 " + - $" -c:a aac -b:a 128k " + - $" -c:v:0 libx264 -g 1 -max_delay 0 -vf scale={this._config.resolution} -preset:v ultrafast -tune:v zerolatency -crf 10 -pix_fmt yuv420p -strict -2 " + + $" -c:a aac " + + $" -c:v:0 libx264 -g 1 -bf 0 -max_delay 0 -vf scale={this._config.resolution} -preset:v ultrafast -tune:v zerolatency -crf 10 -pix_fmt yuv420p -strict -2 " + $" -f flv {this._config.server}{this._config.appName} -bf 0 "; Debug.Log(processStartInfo.Arguments); @@ -166,28 +226,38 @@ namespace ZC throw new NullReferenceException("camera is null"); if (!camera.targetTexture) throw new Exception($"The camera[{camera}]'s targetTexture is null;"); - _isRequesting = false; + _isRequesting = true; SetCamera(camera); _sendData = 0; Object.Destroy(_output); - var targetTextureByteCount = camera.targetTexture.width * camera.targetTexture.height * 3; + this._targetTextureWidth = camera.targetTexture.width; + this._targetTextureHeight = camera.targetTexture.height; + var targetTextureByteCount = this._targetTextureWidth * this._targetTextureHeight * 3; if (_bitmapArray == null || _bitmapArray.Length < Bitmap.FileHeaderSize + Bitmap.ImageHeaderSize + targetTextureByteCount) { _bitmapArray = new byte[Bitmap.FileHeaderSize + Bitmap.ImageHeaderSize + targetTextureByteCount]; } - _output = new Texture2D(camera.targetTexture.width, camera.targetTexture.height); - _textureData.Dispose(); - _textureData = new NativeArray(targetTextureByteCount, Allocator.Persistent); + _output = new Texture2D(this._targetTextureWidth, this._targetTextureHeight); + CreateCaptureThread(); + + StartCoroutine(this.Test()); + } + + + IEnumerator Test() + { + yield return new WaitForSeconds(0.3f); + _isRequesting = false; } public void Dispose() { try { - this._isRequesting = false; + this._isRequesting = true; _sendData = 0; _camera = null; Object.Destroy(_output); diff --git a/Build/nginx/logs/error.log b/Build/nginx/logs/error.log index 40803ce..91ee68c 100644 --- a/Build/nginx/logs/error.log +++ b/Build/nginx/logs/error.log @@ -102,3 +102,14 @@ 2024/11/18 14:13:55 [notice] 27524#13420: sockinit() attempting to access sockapi 2024/11/18 14:13:55 [notice] 27524#13420: Access to sockapi succeded! 2024/11/18 14:13:55 [notice] 27524#13420: using sockapi from "4;80;" +2024/11/18 14:30:25 [notice] 12968#16444: Fatal: wait for sockapi failed +2024/11/18 14:30:28 [error] 27524#15664: *8 live: already publishing, client: 127.0.0.1, server: 0.0.0.0:1935 +2024/11/18 14:30:39 [notice] 30504#8424: Fatal: wait for sockapi failed +2024/11/18 14:31:04 [notice] 1792#18588: Fatal: wait for sockapi failed +2024/11/18 16:17:32 [error] 27524#15664: *42 live: already publishing, client: 127.0.0.1, server: 0.0.0.0:1935 +2024/11/18 16:19:00 [error] 27524#15664: *43 live: already publishing, client: 127.0.0.1, server: 0.0.0.0:1935 +2024/11/18 16:21:20 [error] 27524#15664: *44 live: already publishing, client: 127.0.0.1, server: 0.0.0.0:1935 +2024/11/18 16:24:23 [error] 27524#15664: *45 live: already publishing, client: 127.0.0.1, server: 0.0.0.0:1935 +2024/11/18 16:25:49 [error] 27524#15664: *46 live: already publishing, client: 127.0.0.1, server: 0.0.0.0:1935 +2024/11/18 16:27:59 [error] 27524#15664: *47 live: already publishing, client: 127.0.0.1, server: 0.0.0.0:1935 +2024/11/18 16:30:06 [error] 27524#15664: *48 live: already publishing, client: 127.0.0.1, server: 0.0.0.0:1935 diff --git a/Build/播放器.bat b/Build/播放器.bat index e436f2a..720b3ba 100644 --- a/Build/播放器.bat +++ b/Build/播放器.bat @@ -1,2 +1,2 @@ cd client-pull -ffplay.exe rtmp://127.0.0.1:1935/live/test1 -fflags nobuffer -probesize 512 -analyzeduration 0 -flags low_delay -flags2 fast -fflags discardcorrupt \ No newline at end of file +ffplay.exe rtmp://127.0.0.1:1935/live/test1 -fflags nobuffer -probesize 2048 -analyzeduration 0 -flags low_delay -flags2 fast -fflags discardcorrupt \ No newline at end of file