Compare commits
No commits in common. "6f8d9b5fa66b6a7bec2df43031d808d73159b677" and "6e555569c0d91183b19402f28bdcbfc5ae25a5b2" have entirely different histories.
6f8d9b5fa6
...
6e555569c0
|
@ -1,4 +1,4 @@
|
|||
/*.rar
|
||||
/**.rar
|
||||
/Build/client-push
|
||||
/Logs
|
||||
/obj
|
||||
|
|
|
@ -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<byte> _textureData;
|
||||
private AsyncGPUReadbackRequest _asyncGPUReadbackRequest;
|
||||
private bool _isRequesting;
|
||||
|
||||
|
||||
private byte[] _bitmapArray;
|
||||
private ConcurrentQueue<NativeArray<byte>> _textureDataPool;
|
||||
private Queue<(AsyncGPUReadbackRequest, NativeArray<byte>)> _textureDatas;
|
||||
private ConcurrentQueue<(AsyncGPUReadbackRequest, NativeArray<byte>)> _doneTextureDatas;
|
||||
private int _targetTextureWidth;
|
||||
private int _targetTextureHeight;
|
||||
|
||||
public void SetCamera(Camera camera)
|
||||
{
|
||||
|
@ -52,10 +60,40 @@ namespace ZC
|
|||
return;
|
||||
}
|
||||
|
||||
_textureDataPool = new ConcurrentQueue<NativeArray<byte>>();
|
||||
_textureDatas = new Queue<(AsyncGPUReadbackRequest, NativeArray<byte>)>();
|
||||
_doneTextureDatas = new ConcurrentQueue<(AsyncGPUReadbackRequest, NativeArray<byte>)>();
|
||||
_config.Load();
|
||||
_timer = new Timer(Tick, null, TimeSpan.FromMilliseconds(0), TimeSpan.FromMilliseconds(16));
|
||||
Setup(Camera.main);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 其他线程tick
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
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<byte>(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<byte>(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);
|
||||
|
|
BIN
Build/Build.rar
BIN
Build/Build.rar
Binary file not shown.
|
@ -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
|
||||
|
|
|
@ -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
|
||||
ffplay.exe rtmp://127.0.0.1:1935/live/test1 -fflags nobuffer -probesize 2048 -analyzeduration 0 -flags low_delay -flags2 fast -fflags discardcorrupt
|
Loading…
Reference in New Issue