工作需要研究下市面上显示实时视频方案。这里介绍下RTMP协议。
需求获取USB摄像头,手机谁摄像头。显示到web网页上。
一、 采集摄像头
这个使用opencvSharp来采集:
nuget:
var task = Task.Run(() =>
{
var capture = new VideoCapture(0);
VideoCaptureProperties captureProperties = new VideoCaptureProperties();
capture.Fps = 30;
//苹果测试流
//var capture = new VideoCapture("http://devimages.apple.***.edgekey.***/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8", VideoCaptureAPIs.FFMPEG);
if (!capture.IsOpened())
{
ShowContent("Failed to open capture");
return;
}
//capture.Set(captureProperties, 80);
var frame = new Mat();
var process = new Process();
while (true)
{
capture.Read(frame);
if (frame.Size().Width > 0 && frame.Size().Height > 0)
{
HersheyFonts fontFace = new HersheyFonts();
frame.PutText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), new OpenCvSharp.Point(10, 30), fontFace, 1.0, Scalar.Red);
//var buffer = frame.ToBytes();
//byte[] bytes = frame.ToBytes(".jpg");
#region 压缩尺寸变小
//Cv2.Resize(frame, frame, new OpenCvSharp.Size(frame.Width / 2, frame.Height / 2));
byte[] bytes = ***pressImEncode(frame); //***pressImEncodeNew(frame); //
//预览压缩
frame = Mat.FromImageData(bytes);
#endregion
MatToBitmapShow(frame);
MemoryStream memory = new MemoryStream();
memory.Write(bytes, 0, bytes.Length);
memory.Write(_delimiter, 0, _delimiter.Length);
SendTCP(memory.ToArray());
//Cv2.ImShow("Video", frame);
//Cv2.WaitKey(1);
// 延时一段时间
Cv2.WaitKey(1);
}
}
});
二、 准备RTMP 流服务器
这里使用 nginx-rtmp-http-flv
worker_processes 1;
events {
worker_connections 1024;
}
rtmp {
server {
listen 1935;
application live {
live on;
##打开 GOP 缓存,减少首屏等待时间
gop_cache on ; #打开GOP缓存,减少首屏等待时间
record_max_size 1K; #设置录制文件的最大值。
}
}
}
#推流地址:rtmp://192.168.1.194:1935/live/video1
#FLV 播放地址:http://127.0.0.1:8088/flv?port=1935&app=live&stream=video1
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8088;
server_name localhost;
location / {
add_header 'A***ess-Control-Allow-Origin' '*';
root html;
index index.html index.htm;
}
location /live {
flv_live on;
}
location /flv {
add_header 'A***ess-Control-Allow-Origin' '*';
flv_live on;
chunked_transfer_encoding on;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
写推流到流媒体服务器:
string rtmp1 = "rtmp://127.0.0.1:1935/live/video1";
string rtmp2 = "rtmp://127.0.0.1/live/test110";
task = Task.Run(() =>
{
// 初始化 FFmpeg 库
var capture = new VideoCapture(0);
//var capture = new VideoCapture("http://devimages.apple.***.edgekey.***/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8", VideoCaptureAPIs.FFMPEG);
if (!capture.IsOpened())
{
ShowContent("Failed to open capture");
return;
}
var frame = new Mat();
var process = new Process();
process.StartInfo.FileName = "ffmpeg";
//aac 音频 -r 25 帧率25 -acodec aac
process.StartInfo.Arguments = "-re -i - -vcodec libx264 -f flv " + rtmp1;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
//下面这设置使其后台运行
process.StartInfo.CreateNoWindow = true;
process.Start();
while (true)
{
capture.Read(frame);
if (frame.Size().Width > 0 && frame.Size().Height > 0)
{
HersheyFonts fontFace = new HersheyFonts();
frame.PutText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), new OpenCvSharp.Point(10, 30), fontFace, 1.0, Scalar.Red);
var buffer = frame.ToBytes();
process.StandardInput.BaseStream.Write(buffer, 0, buffer.Length);
MatToBitmapShow(frame);
//Cv2.ImShow("Video", frame);
//Cv2.WaitKey(1);
}
}
process.WaitForExit();
});
三、网页播放:
效果:
<!DOCTYPE html><html><head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<title>flv.js demo</title>
<style>
.mainContainer { display: block; width: 1024px; margin-left: auto; margin-right: auto;
} .urlInput { display: block; width: 100%; margin-left: auto; margin-right: auto; margin-top: 8px; margin-bottom: 8px;
} .centeredVideo { display: block; width: 100%; height: 576px; margin-left: auto; margin-right: auto; margin-bottom: auto;
} .controls { display: block; width: 100%; text-align: left; margin-left: auto; margin-right: auto;
}
</style>
</head>
<body>
<h1>FLV </h1>
<div class="mainContainer">
<video id="videoElement" class="centeredVideo" controls autoplay width="1024" height="576">Your browser is too old which doesn't support HTML5 video.</video>
</div>
<br>
<div class="controls">
<!--<button onclick="flv_load()">加载</button>-->
<button onclick="flv_start()">开始</button>
<button onclick="flv_pause()">暂停</button>
<button onclick="flv_destroy()">停止</button>
<input style="width:100px" type="text" name="seekpoint" />
<button onclick="flv_seekto()">跳转</button>
</div>
<script src="flv.min.js"></script>
<script>
var player = document.getElementById('videoElement');
if (flvjs.isSupported()) {
var flvPlayer = flvjs.createPlayer({
type: 'flv',
"isLive": true,//<====加个这个
url: 'http://192.168.1.194:8088/flv?port=1935&app=live&stream=video1',//<==自行修改
//url: 'http://127.0.0.1:8080/live/test110.flv',
});
flvPlayer.attachMediaElement(videoElement);
flvPlayer.load(); //加载
flv_start();
}
function flv_start() {
player.play();
}
function flv_pause() {
player.pause();
}
function flv_destroy() {
player.pause();
player.unload();
player.detachMediaElement();
player.destroy();
player = null;
}
function flv_seekto() {
player.currentTime = parseFloat(document.getElementsByName('seekpoint')[0].value);
}
$(function(){
flv_start();
});
// setInterval(() => {
// if (this.player.buffered.length) {
// let end = this.player.buffered.end(0);//获取当前buffered值
// let diff = end - this.player.currentTime;//获取buffered与currentTime的差值
// if (diff >= 0.5) {//如果差值大于等于0.5 手动跳帧 这里可根据自身需求来定
// this.player.currentTime = this.player.buffered.end(0);//手动跳帧
// }
// }
// }, 2000); //2000毫秒执行一次
</script>
</body>
</html>
未完待续。。。