【发布时间】:2021-08-30 11:47:50
【问题描述】:
我正在从事一个项目,该项目从 RTSP-Stream 获取单个图像并对其进行操作(绘制边界框)。这些图像应在其他地址上的单独 RTSP 流上重新流式传输(h264 编码),不应保存在本地磁盘上。
我目前的代码是:
{
// OpenCV VideoCapture: Sample RTSP-Stream
var capture = new VideoCapture("rtsp://195.200.199.8/mpeg4/media.amp");
capture.Set(VideoCaptureProperties.FourCC, FourCC.FromFourChars('M', 'P', 'G', '4'));
var mat = new Mat();
// LibVlcSharpStreamer
Core.Initialize();
var libvlc = new LibVLC();
var player = new MediaPlayer(libvlc);
player.Play();
while (true)
{
if (capture.Grab())
{
mat = capture.RetrieveMat();
// Do some manipulation in here
var media = new Media(libvlc, new StreamMediaInput(mat.ToMemoryStream(".jpg")));
media.AddOption(":no-audio");
media.AddOption(":sout=#transcode{vcodec=h264,fps=10,vb=1024,acodec=none}:rtp{mux=ts,sdp=rtsp://192.168.xxx.xxx:554/video}");
media.AddOption(":sout-keep");
player.Media = media;
// Display screen
Cv2.ImShow("image", mat);
Cv2.WaitKey(1);
}
}
}
出于测试目的,这有点混乱,但如果我只使用给定的 RTSP-Stream 作为媒体而不是获取的图像,它就可以工作。我在将图像(作为字节)传送到cvlc 命令行(python get_images.py | cvlc -v --demux=rawvideo --rawvid-fps=25 --rawvid-chroma=RV24 --sout '#transcode{vcodec=h264,fps=25,vb=1024,acodec=none}:rtp{sdp="rtsp://:554/video"}')方面取得了一些成功,但它应该集成在c# 中。 get_images.py 只是读取while 循环中的图像,在其上写入文本并将它们转发到std-out。
我解决这个问题的想法是,通过StreamMediaInput-class 输入图像并动态更改媒体,如果已检索到新图像。但它不起作用,用 VLC 或 FFPlay 什么都看不到。
有人遇到过类似的问题吗?如何动态更改StreamMediaInput-Object,以便正确广播新图像?
感谢您抽出宝贵时间阅读这篇文章。祝你有美好的一天!
编辑:
我尝试通过修改UpdateMemoryStream() 来实现我自己的MediaInput 类(非常类似于MemoryStramMediaInput)。每个新检索到的图像都会更新 MemoryStream,但不会再次调用 read()(read() 每个 Medium 调用一次)。我正在尝试实现阻塞read(),但我正在努力寻找实现它的好方法。到目前为止的代码是:
编辑 2:
我决定使用 ManualResetEvent 来实现阻塞,如果 Position 位于 Stream 的末尾,它会阻塞 read()。此外,读取会在一段时间内循环以保持流中的数据更新。它仍然不起作用。到目前为止我的代码:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using LibVLCSharp.Shared;
namespace LibVlcSharpStreamer
{
/// <summary>
/// A <see cref="MediaInput"/> implementation that reads from a .NET stream
/// </summary>
public class MemoryStreamMediaInput : MediaInput
{
private Stream _stream;
private ManualResetEvent manualResetEvent = new ManualResetEvent(false);
#if NET40
private readonly byte[] _readBuffer = new byte[0x4000];
#endif
/// <summary>
/// Initializes a new instance of <see cref="StreamMediaInput"/>, which reads from the given .NET stream.
/// </summary>
/// <remarks>You are still responsible to dispose the stream you give as input.</remarks>
/// <param name="stream">The stream to be read from.</param>
public MemoryStreamMediaInput(Stream stream)
{
_stream = stream ?? throw new ArgumentNullException(nameof(stream));
CanSeek = stream.CanSeek;
}
/// <summary>
/// Initializes a new instance of <see cref="StreamMediaInput"/>, which reads from the given .NET stream.
/// </summary>
/// <remarks>You are still responsible to dispose the stream you give as input.</remarks>
/// <param name="stream">The stream to be read from.</param>
public void UpdateMemoryStream(Stream stream)
{
stream.CopyTo(_stream);
_stream.Position = 0;
manualResetEvent.Set();
manualResetEvent.Reset();
Console.WriteLine("released");
}
/// <summary>
/// LibVLC calls this method when it wants to open the media
/// </summary>
/// <param name="size">This value must be filled with the length of the media (or ulong.MaxValue if unknown)</param>
/// <returns><c>true</c> if the stream opened successfully</returns>
public override bool Open(out ulong size)
{
try
{
try
{
size = (ulong)_stream.Length;
}
catch (Exception)
{
// byte length of the bitstream or UINT64_MAX if unknown
size = ulong.MaxValue;
}
if (_stream.CanSeek)
{
_stream.Seek(0L, SeekOrigin.Begin);
}
return true;
}
catch (Exception)
{
size = 0UL;
return false;
}
}
/// <summary>
/// LibVLC calls this method when it wants to read the media
/// </summary>
/// <param name="buf">The buffer where read data must be written</param>
/// <param name="len">The buffer length</param>
/// <returns>strictly positive number of bytes read, 0 on end-of-stream, or -1 on non-recoverable error</returns>
public unsafe override int Read(IntPtr buf, uint len)
{
try
{
while (_stream.CanSeek)
{
if (_stream.Position >= _stream.Length)
{
manualResetEvent.WaitOne();
}
var read = _stream.Read(new Span<byte>(buf.ToPointer(), (int)Math.Min(len, int.MaxValue)));
// Debug Purpose
Console.WriteLine(read);
}
return -1;
}
catch (Exception)
{
return -1;
}
}
/// <summary>
/// LibVLC calls this method when it wants to seek to a specific position in the media
/// </summary>
/// <param name="offset">The offset, in bytes, since the beginning of the stream</param>
/// <returns><c>true</c> if the seek succeeded, false otherwise</returns>
public override bool Seek(ulong offset)
{
try
{
_stream.Seek((long)offset, SeekOrigin.Begin);
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// LibVLC calls this method when it wants to close the media.
/// </summary>
public override void Close()
{
try
{
if (_stream.CanSeek)
_stream.Seek(0, SeekOrigin.Begin);
}
catch (Exception)
{
// ignored
}
}
}
}
我已经在代码中标记了我认为阻塞子句非常适合的位置。
【问题讨论】:
-
你能帮我看看如何解决这个问题吗? stackoverflow.com/questions/69210874/…
标签: c# opencv .net-core libvlc libvlcsharp