我想播放低端监控摄像头的视频回放。回放以。mp4格式保存在相机上,最后是moov原子。可以使用digset身份验证通过http请求检索文件。每个视频文件的大小约为20mb,但下载速度只有3mbps,因此下载整个文件大约需要60秒。这太长了,所以我想在下载整个文件之前开始显示视频。
Web浏览器通过从开头读取文件的末尾来处理这类问题。我想用c#和libvlcsharp实现同样的目标,所以创建了HttpMediaInput类。public class HttpMediaInput : MediaInput
{
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private HttpClientHandler _handler;
private HttpClient _httpClient;
private string _url;
Stream _stream = null;
public HttpMediaInput(string url, string username, string password)
{
_url = url;
_handler = new HttpClientHandler() { Credentials = new NetworkCredential(username, password) };
_httpClient = new HttpClient(_handler);
}
public override bool Open(out ulong size)
{
size = ulong.MaxValue;
try
{
_stream = _httpClient.GetStreamAsync(_url).Result;
base.CanSeek = _stream.CanSeek;
return true;
}
catch (Exception ex)
{
logger.Error(ex, $"Exception occurred during sending stream request to url: {_url}");
return false;
}
}
public unsafe override int Read(IntPtr buf, uint len)
{
try
{
byte[] buffer = new byte[len];
int bytesReaded = _stream.Read(buffer, 0, buffer.Length);
logger.Trace($"Bytes readed: {bytesReaded}");
Span<byte> byteSpan = new Span<byte>(buf.ToPointer(), buffer.Length);
buffer.CopyTo(byteSpan);
return bytesReaded;
}
catch (Exception ex)
{
logger.Error(ex, "Stream read exception");
return -1;
}
}
...
}
它适用于mp4文件,所有必要的元数据存储在开始,但没有视频显示在我的相机的情况下。
假设我将能够使用http范围请求从mp4下载moov原子,如何将此数据提供给libvlc?这可能吗?
我正在使用c#, WPF, dotnet框架开发应用程序。
VLC无法从相机播放文件,因为使用md5的http digest auth被认为已弃用(相关问题在VLC repo中)。
然而,我能够按照cube45的建议解决这个问题,我实现了范围请求。
public override bool Open(out ulong size)
{
size = ulong.MaxValue;
try
{
HttpRequestMessage requestMessage = new HttpRequestMessage { RequestUri = new Uri(_url) };
requestMessage.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue();
requestMessage.Method = HttpMethod.Head;
var response = _httpClient.SendAsync(requestMessage).Result;
size = (ulong)response.Content.Headers.ContentLength;
_fileSize = size;
logger.Trace($"Received content lenght | {size}");
base.CanSeek = true;
return true;
}
catch (Exception ex)
{
logger.Error(ex, $"Exception occurred during sending head request to url: {_url}");
return false;
}
}
public unsafe override int Read(IntPtr buf, uint len)
{
try
{
HttpRequestMessage requestMessage = new HttpRequestMessage { RequestUri = new Uri(_url) };
long startReadPosition = (long)_currentPosition;
long stopReadPosition = (long)_currentPosition + ((long)_numberOfBytesToReadInOneRequest - 1);
if ((ulong)stopReadPosition > _fileSize)
{
stopReadPosition = (long)_fileSize;
}
requestMessage.Headers.Range = new System.Net.Http.Headers.RangeHeaderValue(startReadPosition, stopReadPosition);
requestMessage.Method = HttpMethod.Get;
HttpResponseMessage response = _httpClient.SendAsync(requestMessage).Result;
byte[] readedBytes = response.Content.ReadAsByteArrayAsync().Result;
int readedBytesCount = readedBytes.Length;
_currentPosition += (ulong)readedBytesCount;
logger.Trace($"Bytes readed | {readedBytesCount} | startReadPosition {startReadPosition} | stopReadPosition | {stopReadPosition}");
Span<byte> byteSpan = new Span<byte>(buf.ToPointer(), (int)len);
readedBytes.CopyTo(byteSpan);
return readedBytesCount;
}
catch (Exception ex)
{
logger.Error(ex, "Media reading general exception");
return -1;
}
}
public override bool Seek(ulong offset)
{
try
{
logger.Trace($"Seeking media with offset | {offset}");
_currentPosition = offset;
return true;
}
catch (Exception ex)
{
logger.Error(ex, "MediaInput seekeing general error");
return false;
}
}
这个解决方案似乎可以工作,但是有两个未解决的问题:
- libvlcsharp开始读取流和视频直播之间大约有8秒的延迟(在web浏览器中等待时间大约是2s)。
- 视频文件最后部分不显示,因为缓冲区太短,无法容纳整个文件。有关线程