Ale*_*lli 5 c# rtsp ip-camera asp.net-core blazor
我有一个 Blazor 托管应用程序,我需要根据客户请求从 AXIS 摄像机获取 h264 录制内容(通过 RTSP PLAY 命令)并以浏览器可以重现视频的方式返回它。如果向 AXIS 摄像机查询录音列表,答案包括这个,即我尝试在浏览器上播放的那个
<recording diskid="SD_DISK" recordingid="20211109_122753_1AB3_B8A44F2D0300" starttime="2021-11-09T11:27:53.060281Z" starttimelocal="2021-11-09T12:27:53.060281+01:00" stoptime="2021-11-09T11:43:01.125987Z" stoptimelocal="2021-11-09T12:43:01.125987+01:00" recordingtype="continuous" eventid="continuous" eventtrigger="continuous" recordingstatus="completed" source="1" locked="No">
<video mimetype="video/x-h264" width="800" height="600" framerate="15:1" resolution="800x600"/>
</recording>
Run Code Online (Sandbox Code Playgroud)
我可以通过“开放网络流...”并输入来成功地使用 VLC 重现录音
rtsp://192.168.0.125/axis-media/media.amp?recordingid=20211109_140710_E1A3_B8A44F2D0300
Run Code Online (Sandbox Code Playgroud)
然后提供用户名和密码,所以我确信命令是正确的。通过在 url 中嵌入用户名和密码,也可以在该项目中播放录音,其中使用了我下面使用的更简单的语法,因此我的示例可能有点过于复杂。
服务器端由于RtspClientSharp ,我可以成功检索流,但我无法以正确的方式返回它。到目前为止我有这个:
[HttpGet("RecordingsDemo")]
public async Task<IActionResult> RecordingsDemo() {
string deviceIp = "rtsp://192.168.0.125";
string recordingUri = "rtsp://192.168.0.125/axis-media/media.amp?recordingid=20211109_140710_E1A3_B8A44F2D0300";
Uri playRequestUri = new Uri(recordingUri);
CancellationTokenSource cts = new CancellationTokenSource();
NetworkCredential networkCredential = new NetworkCredential("user", "password");
ConnectionParameters connectionParameters = new ConnectionParameters(new Uri(deviceIp), networkCredential);
RtspTcpTransportClient RtspTcpClient = new RtspTcpTransportClient(connectionParameters);
await RtspTcpClient.ConnectAsync(cts.Token);
RtspRequestMessage message = new RtspRequestMessage(RtspMethod.SETUP, playRequestUri);
message.AddHeader("Transport", "RTP/AVP/TCP;unicast");
RtspResponseMessage response = await RtspTcpClient.EnsureExecuteRequest(message, cts.Token);
System.Collections.Specialized.NameValueCollection headers = response.Headers;
string sessionId = headers["SESSION"];
if (sessionId == null) { throw new Exception("RTSP initialization failed: no session id returned from SETUP command"); }
message = new RtspRequestMessage(RtspMethod.PLAY, playRequestUri, sessionId);
response = await RtspTcpClient.EnsureExecuteRequest(message, cts.Token);
Stream stream = RtspTcpClient.GetStream();
if (stream != null) {
Response.Headers.Add("Cache-Control", "no-cache");
FileStreamResult result = new FileStreamResult(stream, "video/x-h264") {
EnableRangeProcessing = true
};
return result;
} else {
return new StatusCodeResult((int)HttpStatusCode.ServiceUnavailable);
}
return new StatusCodeResult((int)HttpStatusCode.OK);
}
Run Code Online (Sandbox Code Playgroud)
请注意,在上面的代码中,我向 RtspRequestMessage 添加了一个构造函数,以便更快地构建它。特别是我添加了以下代码:
public uint _lastCSeqUsed { get; private set; }
/// <param name="method">SETUP, PLAY etc</param>
/// <param name="connectionUri">rtsp://<servername>/axis-media/media.amp?recordingid=...</param>
/// <param name="cSeq">Method that generate the sequence number. The receiver will reply with the same sequence number</param>
/// <param name="protocolVersion">Default to 1.0 if omitted or null</param>
/// <param name="userAgent">Doesn't matter really</param>
/// <param name="session">This parameter has to be initialized with the value returned by the SETUP method</param>
public RtspRequestMessage(RtspMethod method, Uri connectionUri, string session = "", Func<uint> cSeqProvider = null,
Version protocolVersion = null, string userAgent = "client")
: base((protocolVersion != null) ? protocolVersion : new Version("1.0"))
{
Method = method;
ConnectionUri = connectionUri;
UserAgent = userAgent;
_cSeqProvider = (cSeqProvider != null) ? cSeqProvider : myfun;
CSeq = (cSeqProvider != null) ? _cSeqProvider() : 0;
if (!string.IsNullOrEmpty(session))
Headers.Add("Session", session);
}
public void AddHeader(string name, string value)
{
Headers.Add(name, value);
}
private uint myfun()
{
return ++CSeq;
}
Run Code Online (Sandbox Code Playgroud)
当客户端通过 GET 方法调用此方法时,我非常确定通过查看 bandwitdh 和 Wireshark 可以正确检索记录。您可以在下图中看到 Wireshark 输出,其中 192.168.0.125 是摄像头,192.168.0.120 是服务器。
但是服务器返回的文件似乎无法播放。即使使用 VLC,我也无法播放返回的文件或流。客户端与服务器的通信如下图所示,其中192.168.0.16是客户端,192.168.0.51是服务器。
我需要能够返回 html5 视频元素可以播放的流。
你能指出我正确的方向吗?谢谢
编辑:正如你所看到的,我找到了一种方法,发布在下面。不过,我希望有一个更好的解决方案,不需要在磁盘上写入,也不会因生成 .ts 文件而增加延迟。因此,我对是否有人愿意做出贡献的问题保持开放。
最终我能够达到目标,即使不是以我想要的方式。这些是我必须完成的步骤,我在下面详细说明了它们。
我使用以下 FFmpeg 命令从 RTSP 流中提取视频,这样我就可以通过命令返回它
ffmpeg.exe -i rtsp://username:password@192.168.0.125/axis-media/media.amp?recordingid=20211109_122753_1AB3_B8A44F2D0300 -fflags flush_packets -max_delay 5 -flags -global_header -hls_time 3 -hls_list_size 0 -vcodec copy -y .\example.m3u8
Run Code Online (Sandbox Code Playgroud)
我必须使用 -hls_list_size 0 因为在我的情况下我必须转换录音,并且由于用户需要能够在录音中前后查找,所以我必须设置“删除没有下载的 .ts 段”,请请参阅FFmpeg 文档。我可以利用这个.m3u8 播放器演示来检查问题是否出在我生成的视频或其他内容上。这个视频如何通过 NodeJS、FFMPEG 和 ReactJS 将 IP 摄像机 RTSP 流作为 HLS 流式传输到浏览器中也对我有帮助。
这里我遇到了两个问题:由于缺少跨域标头,请求被阻止。此外,一旦浏览器检索到 .m3u8 文件,它期望能够向控制器请求 .ts 文件。所以我必须像这样构造代码:
[ApiController]
[Route("[controller]")]
public class CameraSystemController : ControllerBase {
[HttpGet("Example")]
public async Task<IActionResult> Example() {
Response.Headers.Add("Access-Control-Allow-Origin", "*");
return File(System.IO.File.OpenRead("Output/Video/example.m3u8"), "application/octet-stream", enableRangeProcessing: true);
}
[HttpGet("{tsFileName}")]
public async Task<IActionResult> Example_GetTS(string tsFileName) {
Response.Headers.Add("Access-Control-Allow-Origin", "*");
return File(System.IO.File.OpenRead("Output/Video/" + tsFileName), "application/octet-stream", enableRangeProcessing: true);
}
}
Run Code Online (Sandbox Code Playgroud)
我必须感谢这篇关于CORS的文章和这篇关于实现动态控制器操作的文章。
最后,为了在浏览器中播放 .m3u8 文件,我必须使用这个HLS javascript 项目,这是我通过这篇文章发现的。
我制作的工作 html 页面的示例如下:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Example</title>
<link href="https://unpkg.com/video.js/dist/video-js.css" rel="stylesheet">
<script src="https://unpkg.com/video.js/dist/video.js"></script>
<script src="https://unpkg.com/videojs-contrib-hls/dist/videojs-contrib-hls.js"></script>
</head>
<body>
<video id="my_video_1" class="video-js vjs-fluid vjs-default-skin" controls preload="auto"
data-setup='{}'>
<source src="http://localhost:5000/CameraSystem/Example" type="application/x-mpegURL">
</video>
<script>
var player = videojs('my_video_1');
player.play();
</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
10703 次 |
| 最近记录: |