我有一个Blazor托管应用程序,需要在客户端请求时从AXIS相机(通过RTSP PLAY命令)获取h264录制,并以使浏览器能够重现视频的方式返回它。如果查询AXIS相机的录制列表,答案将包括此录制,我正在尝试在浏览器上播放该录制。
我可以通过“打开网络流…”并输入相应内容,使用VLC成功地重现录音。
通过提供用户名和密码,我确信命令是正确的。通过在url中嵌入用户名和密码,录制可以与this项目一起播放,该项目使用了比我下面使用的更简单的语法,因此我的示例可能有点过于复杂。
服务器端,我可以成功地检索到流,这要归功于RtspClientSharp,但我无法以正确的方式返回它。到目前为止,我有以下代码:
请注意,上面的代码中我添加了一个构造函数到RtspRequestMessage中,以便更快地构建它。特别是我添加了以下代码:
当客户端通过GET方法调用此方法时,我很确定录像已正确检索,从带宽和Wireshark的输出可以看出。您可以在下一张图片中看到Wireshark的输出,其中192.168.0.125是摄像头,而192.168.0.120是服务器。 然而,似乎服务器返回的文件无法播放。即使使用VLC也无法播放返回的文件或流。客户端与服务器之间的通信显示在下图中,其中192.168.0.16是客户端,192.168.0.51是服务器。 我需要返回一个流,可以在html5视频元素中播放。请问你能指导我吗?谢谢。
编辑:如您所见,我已经找到了一种方法(下面有帖子)。但是我希望有更好的解决方案,不需要写入磁盘,并且不需要通过生成.ts文件来添加延迟。因此,如果有人愿意提供帮助,我会保持问题的开放状态。
<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>
我可以通过“打开网络流…”并输入相应内容,使用VLC成功地重现录音。
rtsp://192.168.0.125/axis-media/media.amp?recordingid=20211109_140710_E1A3_B8A44F2D0300
通过提供用户名和密码,我确信命令是正确的。通过在url中嵌入用户名和密码,录制可以与this项目一起播放,该项目使用了比我下面使用的更简单的语法,因此我的示例可能有点过于复杂。
服务器端,我可以成功地检索到流,这要归功于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);
}
请注意,上面的代码中我添加了一个构造函数到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;
}
当客户端通过GET方法调用此方法时,我很确定录像已正确检索,从带宽和Wireshark的输出可以看出。您可以在下一张图片中看到Wireshark的输出,其中192.168.0.125是摄像头,而192.168.0.120是服务器。 然而,似乎服务器返回的文件无法播放。即使使用VLC也无法播放返回的文件或流。客户端与服务器之间的通信显示在下图中,其中192.168.0.16是客户端,192.168.0.51是服务器。 我需要返回一个流,可以在html5视频元素中播放。请问你能指导我吗?谢谢。
编辑:如您所见,我已经找到了一种方法(下面有帖子)。但是我希望有更好的解决方案,不需要写入磁盘,并且不需要通过生成.ts文件来添加延迟。因此,如果有人愿意提供帮助,我会保持问题的开放状态。