摘要:常見方案在網上查找的大部分解決方案都是用諸如等網頁播放器,接收索引文件的方式來播放切片。根據實測,只用這一個庫即可在端直接播放視頻,如下是它的轉化流程。
最近項目中需要前端播放 .ts 格式視頻,搗鼓了幾天學習到很多知識,也發掘了一種優秀的解決方案,分享給有同樣需求的同學。
常見方案在網上查找的大部分解決方案都是用諸如videojs等網頁播放器,接收 .m3u8索引文件的方式來播放ts切片。這種方案的缺點是需要后端對原始ts切片做處理,生成 .m3u8索引文件
ffmpeg -i source.ts -c copy -map 0 -f segment -segment_list playlist.m3u8 -segment_time 10 output%03d.ts
項目中已存儲的 .ts 切片數量眾多,已經占用了NAS服務器絕大部分的資源,生成的 .m3u8 索引雖然非常小,但會生成一堆切片后的新 .ts 視頻,例如上述指令將會生成一堆 10s 長度的 ts新切片。出于各種考慮后端的同學拒絕了這種重新生成新切片加索引的方案。
邪道方案在我們的項目中,每一個ts切片已經就是一個獨立內容的視頻了,時長在20s以內,因此其實不用切割,只需要生成一個索引文件就可以了, .m3u8格式如下:
#EXTM3U #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-ALLOW-CACHE:YES #EXT-X-TARGETDURATION:93 #EXTINF:92.008578, test.ts #EXT-X-ENDLIST
定義好的時長并不影響最終網頁播放器計算出的時長,因此可以取一個統一的極大值,整體上就只有倒數第二行的ts文件名需要根據不同 ts 視頻修改,可以用腳本統一生成所有ts文件的索引文件。這個方案極其low,當然也被后端同學拒絕了。
插件方案VLC Web Plugin,一個需要VLC播放器以及瀏覽器插件的方案,并且不支持Chrome,使用復雜,感興趣的同學可以自行嘗試。
優雅方案在中文互聯網搜索無果后,果斷轉向了Google,然而也未果,正當我絕望地準備調整心態,接受下載后VLC播放的保底方案時,終于發現了一絲線索,在vediojs的Github頁面中,Issue1441 和 Issue4297 中,面對videojs能否直接播放 .ts 的疑問,開發團隊都表示雖然庫本身沒有直接的相關實現,但可以利用相關的邏輯自行實現。最重要的是都指出了mux.js這一工具。根據實測,只用這一個庫即可在web端直接播放 .ts 視頻,如下是它的轉化流程。
示例中是以 ajax 的方式接收 .ts 二進制數據,mux.js引入方式可以直接標簽引入,也可以npm install mux.js后 import進頁面。
var $ = document.querySelector.bind(document); var vjsParsed, video, mediaSource; // 定義通用的事件回調處理函數,只做打印事件類型 function logevent (event) { console.log(event); } // ajax let xhr = new XMLHttpRequest(); xhr.open("GET", "./test.ts"); // 接收的是 video/mp2t 二進制數據,Blob類型也可以,但arraybuffer類型方便后續直接處理 xhr.responseType = "arraybuffer"; xhr.send(); xhr.onreadystatechange = function () { if (xhr.readyState ==4) { if (xhr.status == 200) { transferFormat(xhr.response); } else { console.log("error"); } } } function transferFormat (data) { // 將源數據從ArrayBuffer格式保存為可操作的Uint8Array格式 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer var segment = new Uint8Array(data); var combined = false; // 接收無音頻ts文件,OutputType設置為"video",帶音頻ts設置為"combined" var outputType = "video"; var remuxedSegments = []; var remuxedBytesLength = 0; var remuxedInitSegment = null; // remux選項默認為true,將源數據的音頻視頻混合為mp4,設為false則不混合 var transmuxer = new muxjs.mp4.Transmuxer({remux: false}); // 監聽data事件,開始轉換流 transmuxer.on("data", function(event) { console.log(event); if (event.type === outputType) { remuxedSegments.push(event); remuxedBytesLength += event.data.byteLength; remuxedInitSegment = event.initSegment; } }); // 監聽轉換完成事件,拼接最后結果并傳入MediaSource transmuxer.on("done", function () { var offset = 0; var bytes = new Uint8Array(remuxedInitSegment.byteLength + remuxedBytesLength) bytes.set(remuxedInitSegment, offset); offset += remuxedInitSegment.byteLength; for (var j = 0, i = offset; j < remuxedSegments.length; j++) { bytes.set(remuxedSegments[j].data, i); i += remuxedSegments[j].byteLength; } remuxedSegments = []; remuxedBytesLength = 0; // 解析出轉換后的mp4相關信息,與最終轉換結果無關 vjsParsed = muxjs.mp4.tools.inspect(bytes); console.log("transmuxed", vjsParsed); prepareSourceBuffer(combined, outputType, bytes); }); // push方法可能會觸發"data"事件,因此要在事件注冊完成后調用 transmuxer.push(segment); // 傳入源二進制數據,分割為m2ts包,依次調用上圖中的流程 // flush的調用會直接觸發"done"事件,因此要事件注冊完成后調用 transmuxer.flush(); // 將所有數據從緩存區清出來 } function prepareSourceBuffer (combined, outputType, bytes) { var buffer; video = document.createElement("video"); video.controls = true; // MediaSource Web API: https://developer.mozilla.org/zh-CN/docs/Web/API/MediaSource mediaSource = new MediaSource(); video.src = URL.createObjectURL(mediaSource); $("#video-wrapper").appendChild(video); // 將H5 video元素添加到對應DOM節點下 // 轉換后mp4的音頻格式 視頻格式 var codecsArray = ["avc1.64001f", "mp4a.40.5"]; mediaSource.addEventListener("sourceopen", function () { // MediaSource 實例默認的duration屬性為NaN mediaSource.duration = 0; // 轉換為帶音頻、視頻的mp4 if (combined) { buffer = mediaSource.addSourceBuffer("video/mp4;codecs="" + "avc1.64001f,mp4a.40.5" + """); } else if (outputType === "video") { // 轉換為只含視頻的mp4 buffer = mediaSource.addSourceBuffer("video/mp4;codecs="" + codecsArray[0] + """); } else if (outputType === "audio") { // 轉換為只含音頻的mp4 buffer = mediaSource.addSourceBuffer("audio/mp4;codecs="" + (codecsArray[1] ||codecsArray[0]) + """); } buffer.addEventListener("updatestart", logevent); buffer.addEventListener("updateend", logevent); buffer.addEventListener("error", logevent); video.addEventListener("error", logevent); // mp4 buffer 準備完畢,傳入轉換后的數據 // 將 bytes 放入 MediaSource 創建的sourceBuffer中 // https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer buffer.appendBuffer(bytes); // 自動播放 // video.play(); }); };
IE8及以上 、 IE Edge 、Chrome 、 Firefox 瀏覽器下均能正常播放。希望本文能幫到各位開發同學。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/102520.html
閱讀 1618·2021-09-08 10:42
閱讀 3604·2021-08-11 10:23
閱讀 3959·2019-08-30 14:10
閱讀 2732·2019-08-29 17:29
閱讀 3090·2019-08-29 12:50
閱讀 637·2019-08-26 13:36
閱讀 3456·2019-08-26 11:59
閱讀 1487·2019-08-23 16:23