目录
背景
解决方案
效果
代码
前端代码
后端代码
下载
背景
项目中需要在浏览器中播放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);});}}}
下载
源码下载