目录
背景
项目中需要在浏览器中播放RTSP流,实在是不想折腾ActiveX控件
1、麻烦(开发麻烦、使用时设置也麻烦)
2、非IE浏览器不兼容
解决方案
使用OpenCvSharp+Nancy写一个解码服务,提供http接口,返回解码后Mat对象的Base64字符串,前端页面循环调用并展示。
效果
浏览器播放RTSP流
代码
前端代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>rtsp播放测试</title>
<link rel="stylesheet" href="bootstrap.min.css" />
<script src="jquery-1.8.0.js" type="text/javascript"></script>
</head>
<body>
<div class="container">
<br />
<div class="row">
<div class="panel panel-primary">
<div class="panel-heading">
<span class="label label-primary">rtsp播放测试</span>
<br />
</div>
<div class="panel-body">
<input type="text" value="" id="txtRTSPURL" style="width: 500px;" /><br />
<br />
<input type="button" value="打开" id="btnOpen" />
<input type="button" value="播放" id="btnPlay" />
<input type="button" value="停止" id="btnStop" />
<input type="button" value="测试获取一帧" id="btnTest" />
</br> </br>
<img id="imgId" src="" style="width: 1024px" />
</div>
</div>
</div>
</div>
</body>
<script type="text/jscript">
$(function () {
$("#btnOpen").click(function () {
var rtsp_url = $("#txtRTSPURL").val();
$.ajax({
type: "post",
url: base_url + "/open",
dataType: 'json',
data: { "rtsp_url": rtsp_url },
success: function (d) {
if (d.code == 1) {
alert("打开成功")
} else {
alert("打开失败:" + d.message)
}
}
})
})
$("#btnTest").click(function () {
$.ajax({
type: "post",
url: base_url + "/getframe",
dataType: 'json',
data: "",
success: function (d) {
if (d.code == 1) {
console.log(d.data);
$("#imgId").attr("src", "data:image/jpg;base64," + d.data);
} else {
alert("播放失败:" + d.message)
}
}
})
})
$("#btnPlay").click(function () {
try {
flag = true;
showImage();
} catch (e) {
}
})
$("#btnStop").click(function () {
flag = false;
$.ajax({
type: "post",
url: base_url + "/close",
dataType: 'json',
data: "",
success: function (d) {
if (d.code == 1) {
} else {
alert("关闭失败:" + d.message)
}
}
})
})
})
var base_url = "http://127.0.0.1:8082";
var flag = false;
function showImage() {
$.ajax({
type: "post",
url: base_url + "/getframe",
dataType: 'json',
data: "",
success: function (d) {
if (d.code == 1) {
$("#imgId").attr("src", "data:image/jpg;base64," + d.data);
if (flag) {
showImage();
}
} else {
alert("播放失败:" + d.message)
}
}
})
}
</script>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>rtsp播放测试</title>
<link rel="stylesheet" href="bootstrap.min.css" />
<script src="jquery-1.8.0.js" type="text/javascript"></script>
</head>
<body>
<div class="container">
<br />
<div class="row">
<div class="panel panel-primary">
<div class="panel-heading">
<span class="label label-primary">rtsp播放测试</span>
<br />
</div>
<div class="panel-body">
<input type="text" value="" id="txtRTSPURL" style="width: 500px;" /><br />
<br />
<input type="button" value="打开" id="btnOpen" />
<input type="button" value="播放" id="btnPlay" />
<input type="button" value="停止" id="btnStop" />
<input type="button" value="测试获取一帧" id="btnTest" />
</br> </br>
<img id="imgId" src="" style="width: 1024px" />
</div>
</div>
</div>
</div>
</body>
<script type="text/jscript">
$(function () {
$("#btnOpen").click(function () {
var rtsp_url = $("#txtRTSPURL").val();
$.ajax({
type: "post",
url: base_url + "/open",
dataType: 'json',
data: { "rtsp_url": rtsp_url },
success: function (d) {
if (d.code == 1) {
alert("打开成功")
} else {
alert("打开失败:" + d.message)
}
}
})
})
$("#btnTest").click(function () {
$.ajax({
type: "post",
url: base_url + "/getframe",
dataType: 'json',
data: "",
success: function (d) {
if (d.code == 1) {
console.log(d.data);
$("#imgId").attr("src", "data:image/jpg;base64," + d.data);
} else {
alert("播放失败:" + d.message)
}
}
})
})
$("#btnPlay").click(function () {
try {
flag = true;
showImage();
} catch (e) {
}
})
$("#btnStop").click(function () {
flag = false;
$.ajax({
type: "post",
url: base_url + "/close",
dataType: 'json',
data: "",
success: function (d) {
if (d.code == 1) {
} else {
alert("关闭失败:" + d.message)
}
}
})
})
})
var base_url = "http://127.0.0.1:8082";
var flag = false;
function showImage() {
$.ajax({
type: "post",
url: base_url + "/getframe",
dataType: 'json',
data: "",
success: function (d) {
if (d.code == 1) {
$("#imgId").attr("src", "data:image/jpg;base64," + d.data);
if (flag) {
showImage();
}
} else {
alert("播放失败:" + d.message)
}
}
})
}
</script>
</html>
后端代码
using Nancy;
using Newtonsoft.Json;
using NLog;
using OpenCvSharp;
using OpenVINO.OCRService.Common;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace CaptureService
{
public class CaptureModule : NancyModule
{
private Logger _log = NLog.LogManager.GetCurrentClassLogger();
public static readonly object _locker = new object();
public CaptureModule()
{
//跨域处理
After.AddItemToEndOfPipeline((ctx) => ctx.Response
.WithHeader("Access-Control-Allow-Origin", "*")
.WithHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS")
.WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type"));
Get("/", p =>
{
return "Hello MediaCaptureService";
});
Post("/open", p =>
{
AjaxReturn ar = new AjaxReturn();
if (Program.open)
{
ar.code = 0;
ar.message = "已开启,如需重新开启,请先关闭!";
_log.Info(JsonConvert.SerializeObject(ar));
return Response.AsJson<AjaxReturn>(ar);
}
string rtsp_url = Request.Form["rtsp_url"];
if (string.IsNullOrEmpty(rtsp_url))
{
ar.code = 0;
ar.message = "参数[rtsp_url]不能为空";
_log.Info(JsonConvert.SerializeObject(ar));
return Response.AsJson<AjaxReturn>(ar);
}
Program.rtsp_url = rtsp_url;
Program.ctsCapture = new CancellationTokenSource();
Program.open = true;
try
{
Task.Factory.StartNew(() =>
{
Program.capture = new VideoCapture(Program.rtsp_url);
if (Program.capture.IsOpened())
{
int index = 0;
Mat frame = new Mat();
while (true)
{
if (Program.ctsCapture.IsCancellationRequested) break;
Program.capture.Read(frame);
if (Program.matQueue.Count >= 5)
{
continue;
}
Program.matQueue.Enqueue(frame);
//_log.Info(Program.matQueue.Count);
//Cv2.ImWrite(index + ".jpg", frame);
//index++;
}
if (Program.capture != null)
{
Program.capture.Release();
}
}
});
ar.code = 1;
ar.message = "success";
}
catch (Exception ex)
{
ar.code = 0;
ar.message = ex.Message;
_log.Error(ex, "开启异常");
}
return Response.AsJson<AjaxReturn>(ar);
});
Post("/close", p =>
{
AjaxReturn ar = new AjaxReturn();
try
{
Program.open = false;
if (Program.ctsCapture != null)
{
Program.ctsCapture.Cancel();
}
ar.code = 1;
ar.message = "success";
}
catch (Exception ex)
{
ar.code = 0;
ar.message = ex.Message;
_log.Error(ex, "关闭异常");
}
return Response.AsJson<AjaxReturn>(ar);
});
Post("/getframe", p =>
{
AjaxReturn ar = new AjaxReturn();
if (!Program.open)
{
ar.code = 0;
ar.message = "网络流未打开,请先打开!";
_log.Info(JsonConvert.SerializeObject(ar));
return Response.AsJson<AjaxReturn>(ar);
}
if (Program.matQueue.Count == 0)
{
ar.code = 1;
ar.message = "图像队列为空";
_log.Info(JsonConvert.SerializeObject(ar));
return Response.AsJson<AjaxReturn>(ar);
}
try
{
Mat frame = new Mat();
if (!Program.matQueue.TryDequeue(out frame))
{
ar.code = 0;
ar.message = "获取图像失败";
_log.Info(JsonConvert.SerializeObject(ar));
return Response.AsJson<AjaxReturn>(ar);
}
ar.code = 1;
ar.message = "success";
var bytes = frame.ToBytes();
ar.data = Convert.ToBase64String(bytes);
}
catch (Exception ex)
{
ar.code = 0;
ar.message = ex.Message;
_log.Error(ex, "获取摄像头画面异常");
}
return Response.AsJson<AjaxReturn>(ar);
});
}
}
}