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



















