本文主要介紹一種在H5頁面中,跨瀏覽器預覽目前主流直播攝像頭使用的rtsp流的預覽方案。由於本人第一次接觸這類需求,所以只是做到功能完整,還有各種不足之處,望各位大神指正。
目前直播攝像頭主要採用rtsp視訊流的方式進行傳輸,而rtsp流是無法直接被瀏覽器所載入的。之前常用的方式是通過VLC media player外掛來對視訊進行預覽。但隨著時間的推移,VLC已經無法被大多數瀏覽器所相容,所以無奈只能上網查詢其他可以在瀏覽器直接瀏覽直播視訊的方式。(VLC相容性)。學習後做如下總結,留於有該類需求的小夥伴及自己檢視使用。
既然已經知道直播攝像頭所採用的rtsp視訊流無法直接被進行載入,那擺在我們面前的就只有幾條路可以走。
1.自己獨立開發rtsp流的解碼及播放功能。
2.將rtsp流想辦法處理為可以被瀏覽器所支援的視訊流格式。
3.更換攝像頭。。。(完全不現實,甲方不會允許的)
饒小弟才疏學淺,對視訊流方面的涉獵真心不多。無奈只能選擇第二條路。於是就有了如下的解決方案。
伺服器端:
第一、需要對rtsp的直播流實時進行轉換,將其轉換為可以被瀏覽器播放的其他視訊流。
第二、需要在將轉換後的視訊流推播給使用者端。
第三、當用戶端關閉視訊預覽後,需要同時刪除轉換後的視訊流檔案及關閉伺服器端對應的視訊轉換程序。
使用者端:
第一、將視訊需要的使用者名稱等資訊傳遞到伺服器,用於生成rtsp視訊流。(我的程式是從前臺讀取的登入資訊,也可以直接在後臺從資料庫中查詢)
第二、將伺服器端轉換好的視訊流載入入頁面。
第三、需要考慮視訊在頁面中載入時對瀏覽器的通用性。
第四、將關閉視訊的訊號傳送回伺服器端,使其釋放視訊資源。
FFmpeg是一套可以用來記錄、轉換數位音訊、視訊,並能將其轉化為流的開源計算機程式(下載)。
之所以選擇用FFmpeg原因有三:一是因為其開源且免費(特別重要),二是因為功能比較全面,三是因為使用人數較多,網上的貼子也別較多,遇到問題比較容易查詢解決。
下載好對應版本的ffmpeg應用包,進行解壓。解壓後包含bin和doc兩個資料夾。其中doc資料夾為使用demo及一些說明。bin資料夾則為需要用到的內容,包括ffmpeg.exe、ffplay.exe、ffprobe.exe三個檔案。
解壓後無需安裝,只需要在環境變數中新增好地址即可。方法:我的電腦----右鍵"屬性" —選擇 「高階系統屬性」----在「環境變數」中的【PATH】追加路徑 (剛剛解壓bin資料夾路徑),重新啟動電腦即可。例如我的就解壓在「E:\HKVideoDemo\ffmpegToHLS\ffmpeg-w64\bin;」
在執行視窗(Win+R)中,輸入CMD。輸入ffmpeg回車即可看到如下內容。
在執行視窗(Win+R)中,輸入ffmpeg -i 直播攝像頭rtsp流資訊 其他ffmpeg引數即可。
附:
rtsp直播流獲取方式 rtsp://視訊使用者名稱:視訊密碼@視訊IP地址:埠號(預設554)/位元速率/通道號/main/av_stream
ffmpeg完成命令設定拼接 ffmpeg -i rtsp://視訊使用者名稱:視訊密碼@視訊IP地址:埠號(預設554)/位元速率/通道號/main/av_stream -fflags flush_packets -max_delay 1 -an -flags -global_header -hls_time 1 -hls_list_size 3 -hls_wrap 3 -vcodec copy -y 轉換後輸出hls流的地址
具體ffmpeg的各引數說明在此就不詳細敘述了,想了解的同學可以參考(ffmpeg引數說明)
上述啟動方式需要先手動在命令視窗將ffmpeg命令執行起來,如果需要同時檢視多個直播視訊,則需要執行多個命令視窗,非常不便於操作。於是我通過C#的程序啟動方式將其進行了程式碼化。直接上程式碼:
/// <summary>
/// 播放
/// </summary>
/// <param name="param">前臺傳過來的視訊使用者名稱等資訊</param>
private void videoPlay(string param)
{
if (!string.IsNullOrEmpty(param))
{
CloseProessAndDelFile();//結束正在執行的程序
string _HLSFileName = Guid.NewGuid().ToString(); //轉換名稱可以隨便起,停止的時候帶回去用於刪除檔案
VideoInfo vi = JsonConvert.DeserializeObject<VideoInfo>(param);
vi.HLSFileName = _HLSFileName;
Session["_VideoInfo"] = vi; //Session變數儲存視訊資訊
if (Directory.Exists(vi.HLSPath) == false)//如果不存在就建立file資料夾
{
Directory.CreateDirectory(vi.HLSPath);
}
#region 啟動ffmpeg服務
//拼接命令
string strML = "-i";
strML += " rtsp://" + vi.username + ":" + vi.password + "@" + vi.ip + ":" + vi.port + "/H.264/ch1/main/av_stream";
strML += " -fflags flush_packets";
strML += " -max_delay 1";
strML += " -an";
strML += " -flags";
strML += " -global_header";
strML += " -hls_time 1";
strML += " -hls_list_size 3";
strML += " -hls_wrap 3";
strML += " -vcodec copy";
strML += " -y";
strML += " "+vi.HLSPath;
strML += vi.HLSFileName+ ".m3u8";
ExcuteProcess("ffmpeg.exe", strML, (s, e) => Console.WriteLine(e.Data));
#endregion
while (Session["_ProcessID"] != null)
{
Thread.Sleep(3000);//啟動切換服務後,形成流檔案需要一點時間。所以先暫停。時間根據實際情況設定
//向用戶端寫回資料
Response.ContentType = "text/plain";
Response.ContentEncoding = System.Text.Encoding.UTF8;
Response.Write(vi.HLSFileName);
Response.End();
}
}
}
/// <summary>
/// 啟動程序
/// </summary>
/// <param name="exe">啟動程式程序名</param>
/// <param name="arg">啟動引數</param>
/// <param name="output">輸出</param>
private void ExcuteProcess(string exe, string arg, DataReceivedEventHandler output)
{
var p = new Process();
p.StartInfo.FileName = exe;
p.StartInfo.Arguments = arg;
p.StartInfo.UseShellExecute = false; //輸出資訊重定向
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.OutputDataReceived += output;
p.ErrorDataReceived += output;
p.Start(); //啟動執行緒
p.BeginOutputReadLine();
p.BeginErrorReadLine();
Session["_ProcessID"] = p.Id; //儲存啟動的程序ID
//p.WaitForExit(); //等待程序結束
}
/// <summary>
/// 判斷 關閉程序並刪除檔案
/// </summary>
private string CloseProessAndDelFile() {
string FileName = "";
#region 判斷並關閉已經開啟的轉換程序
if (Session["_ProcessID"] != null)
{
//根據程序ID,關閉程序
Process[] processes = System.Diagnostics.Process.GetProcesses();
foreach (Process item in processes)
{
if (item.Id == int.Parse(Session["_ProcessID"].ToString()))
{
item.Kill();
break;
}
}
}
#endregion
#region 刪除視訊流檔案
if (Session["_VideoInfo"] != null)
{
VideoInfo vi = Session["_VideoInfo"] as VideoInfo;
//需要刪除的檔案
string fileFullPath1 = vi.HLSPath + vi.HLSFileName + ".m3u8";//HLS流檔案
string fileFullPath2 = vi.HLSPath + vi.HLSFileName + "0.ts";//ts檔案
string fileFullPath3 = vi.HLSPath + vi.HLSFileName + "1.ts";//ts檔案
string fileFullPath4 = vi.HLSPath + vi.HLSFileName + "2.ts";//ts檔案
FileName = vi.HLSFileName;
// 判斷檔案路徑是否存在
if (File.Exists(fileFullPath1))
{
//刪除檔案
File.Delete(fileFullPath1);
}
// 判斷檔案路徑是否存在
if (File.Exists(fileFullPath2))
{
//刪除檔案
File.Delete(fileFullPath2);
}
// 判斷檔案路徑是否存在
if (File.Exists(fileFullPath3))
{
//刪除檔案
File.Delete(fileFullPath3);
}
// 判斷檔案路徑是否存在
if (File.Exists(fileFullPath4))
{
//刪除檔案
File.Delete(fileFullPath4);
}
}
#endregion
return FileName;
}
/// <summary>
/// 視訊資訊實體
/// </summary>
public class VideoInfo
{
/// <summary>
/// 使用者名稱
/// </summary>
public string username { get; set; }
/// <summary>
/// 密碼
/// </summary>
public string password { get; set; }
/// <summary>
/// IP
/// </summary>
public string ip { get; set; }
/// <summary>
/// 埠(預設554)
/// </summary>
public string port { get; set; }
/// <summary>
/// 轉換後HLS地址
/// </summary>
public string HLSPath { get; set; }
/// <summary>
/// 轉換後HLS檔名
/// </summary>
public string HLSFileName { get; set; }
}
為了將轉換好的hls直播流可以顯示出來,我引入了Jquery、VideoJS及videojs-contrib-hlsJS外掛(下載地址)。外掛均為引入頁面即可使用的,同時我還新增了判斷使用者離開頁面的方法。在使用者離開當前頁面時非同步呼叫後臺程式碼,將視訊資源釋放。HTML完整程式碼如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta HTTP-EQUIV="pragma" CONTENT="no-cache">
<meta HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate">
<meta HTTP-EQUIV="expires" CONTENT="0">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta content="email=no" name="format-detection" />
<meta content="telephone=no" name="format-detection" />
<script type="text/javascript" src="VideoJs/js/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="VideoJs/js/video.min.js"></script>
<script type="text/javascript" src="VideoJs/js/videojs-contrib-hls.min.js"></script>
<link href="VideoJs/css/video-js.min.css" rel="stylesheet">
<title>demo</title>
</head>
<body>
<div>
<button id="btnPlay">播放</button>||
<a href="http://www.baidu.com" target="_self">測試跳轉後清理視訊快取</a>
</div>
<div id="dvVideo">
<video id="my-video" style='margin-top:5px;' poster="b.gif" class="video-js vjs-big-play-centered" controls preload="auto" width="580" height="435" data-setup="{}"></video>
</div>
</body>
</html>
<script type="text/javascript">
//初始化VideoJS
$("#dvVideo").hide();
var videoObj = videojs('my-video');
var option = {};
var myPlayer;
myPlayer = videojs('my-video', option, function onPlayerReady() {
預設載入視訊
//var myPlayer = this;
//myPlayer.src({ src: "ffmpeg/1.m3u8", type: "application/x-mpegURL" });
//myPlayer.play();
});
myPlayer.controls = false;
</script>
<script>
$(function () {
//播放按鈕
$("#btnPlay").click(function () {
$("#dvVideo").show();
var param = new Object();
param.username="admin"; //攝像頭使用者名稱
param.password = "ty123456"; //密碼
param.ip = "192.168.16.35"; //IP地址
param.port = "554"; //埠號,預設554
param.HLSPath = "E:/HKVideoDemo/ffmpegToHLS/DataDemo/DataDemo/Demo/ffmpeg/"; //轉換後HLS地址,這裡可以將專案中資料夾的相對路徑轉為絕對路徑傳入
$.ajax({
url: 'DataForm.aspx?param=' + JSON.stringify(param)+'&type=add',
contentType: "application/json",
success: function (data) {
console.log(data);
//注意:必須要參照videojs-contrib-hls.min.js,只參照video.js無效
//這裡src中的相對路徑根據實際專案設定
myPlayer.src({ src: "ffmpeg/" + data + ".m3u8", type: "application/x-mpegURL" });
myPlayer.play();
}
});
});
//離開頁面,釋放視訊。包括重新整理、跳轉、關閉網頁、關閉瀏覽器等操作
$(window).bind('beforeunload',function () {
console.log("離開頁面,釋放視訊。");
$.ajax({
url: 'DataForm.aspx?type=stop',
dataType: "json",
success: function (data) {
}
});
});
});
</script>
至此,前端和後端的開發就此完畢。由於只是一個簡單的DEMO事例,所以功能和用法都略顯粗糙。只是為了做一個簡單的記錄,將思路及方法表述出來。希望可以給以後的小夥伴提供一些思路,以及留待後用。
附原始碼下載地址:https://download.csdn.net/download/fwl562213140/12919777
碼字不易,轉載望留痕。
原文出處:https://blog.csdn.net/fwl562213140/article/details/109056188