利用 nodejs 解析 m3u8 格式文件,並下 ts 合並為 mp4


利用 nodejs 解析 m3u8 格式文件,並下 ts 合並為 mp4

以前看視頻的時候,直接找到 video標簽,查看視頻地址,然后下載下來。。

后來發現,好多 video 標簽打開元素審查,如下:

 

blob開始的東西,下載不了啦。。。

 

其實我們打開 network 還是能看見,加載了一堆的 .ts 文件。其實.ts文件就是被切成一段一段的視頻。 理論上,把這些文件都下載下來,再合並,就完成了,,,

理論一句話,代碼上千行...

 

一、問題

1、ts文件到底有多少和,地址從哪來。。。

    答案: ts 相關的信息,都存在一個叫 m3u8 的文件。 如果仔細點觀察 network 是可以找到這個文件的請求的。該文件內容大致如下:

  

 

  這個文件,很顯然,存了每個 ts 的文件名稱,當然也有存完整的地址的。。只需要提取出里面的ts文件名稱,再加上目標網站的域名,就可以下載了。。

  我這里是手動的把 m3u8 下載到了本地,當然也可以自己寫腳本來下載m3u8文件

  解析代碼如下:

  

const fs  = require("fs");
var source = fs.readFileSync("./test.m3u8","utf-8"); //讀取 m3u8
var arr  = source.split("\n");
arr = arr.filter((item)=>{
	return item.match(/\.ts$/);
});

  

2、使用什么技術來合並這些 ts

   這里我嘗試了兩種辦法

  第一種: 使用node js 直接讀取文件流,合並到一個文件。。。最后結果,合並確實成功了,也能播放,但是有卡頓現象,應該是視頻幀被破壞了。

 第二種: 使用一款強大的工具, ffmpeg 來合並,成功了。具體 ffmpeg 安裝看這里 :https://www.cnblogs.com/xswl/p/10042195.html

 

其中  ffmpeg 的視頻合成指令,我找到到了三類:

 

ffmpeg -i "concat:1.ts|2.ts" -acodec copy out.mp3

  

ffmpeg -i "concat:1.ts|2.ts" -acodec copy -vcodec copy -absf aac_adtstoasc output.mp4

  

前兩類,都是要文件名稱拼接到 指令里面,,考慮到 cmd 指令的長度有限制,所以並未采用。

采用了如下文件輸入辦法:

ffmpeg -i input.txt -acodec copy -vcodec copy -absf aac_adtstoasc output.mp4  

 

其中 input.txt 是一個輸入配置文件,內容為需要合並的文件名稱,如下:

ffconcat version 1.0
file  0.ts
file  1.ts

  

二、正式開始

 新建 down.js 寫入:

 

const request = require("request");
const fs  = require("fs");
const path  = require("path");
const child_process = require('child_process');
const fsextra = require('fs-extra');

module.exports = function(opt){
	opt = opt || {};
	var arr = opt.arr || []; //所有 ts的文件名或者地址
	var host = opt.host || ""; //下載 ts 的 域名,如果 arr 里面的元素已經包含,可以不傳
	var outputName = opt.name ||  `output${(new Date()).getTime()}.mp4`; //導出視頻的名稱
	
	const tsFile = path.join(__dirname,`./source/${arr[0].split(".")[0]}`,);
	createDir(tsFile);//遞歸創建文件
	console.log("本次資源臨時文件:",tsFile);
	
	const resultDir = path.join(__dirname,"./result");
	createDir(resultDir);//遞歸創建文件
	const resultFile = path.join(resultDir,outputName);
	
	var localPath = [] ; //下載到本地的路徑
	//開始下載ts文件
	load();
	function load(){
		if(arr.length > 0){
			var u =  arr.shift();
			var url = host + u;
			console.log("progress---:",url);
			down(url);
		}else{
			//下載完成
			console.log("下載完成--開始生成配置");
			localPath.unshift("ffconcat version 1.0");
			try{
				fs.writeFileSync(path.join(tsFile,"./input.txt"), localPath.join("\n") , undefined, 'utf-8')
			}catch(e){
				console.log("寫入配置出錯--",e);
				return ;
			}
			
			//開始依賴配置合成
			console.log("開始合成-----");
			child_process.exec(`cd ${tsFile} &&  ffmpeg -i input.txt -acodec copy -vcodec copy -absf aac_adtstoasc ${resultFile}`,function(error, stdout, stderr){
				if(error){
					console.error("合成失敗---",error);
				}else{
					console.log("合成成功--",stdout);
					//刪除臨時文件
					fsextra.remove(tsFile, err => {
					  if (err) return console.error("刪除文件是失敗",err)
					  console.log('刪除文件成功!')
					});
				}
			});
		}
	}
	
	//下載 ts 文件
	function down(url){
		var p = url.split("?")[0];
		var nm = path.parse(p);
		var nme = nm["name"] + nm["ext"];
		rpath = path.join(tsFile,nme);
		
		localPath.push(`file ${nme}`); //緩存本地路徑,用來合成
		
		request({
		    url:url,
		    headers:{
		        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
		        'X-Requested-With': 'XMLHttpRequest'
		    }
		},function (err, response, body) {
	        if (!err && response.statusCode == 200) {
				load();
	        }else{
				console.log("錯誤",err)
			}
	    }).pipe(fs.createWriteStream(rpath));
	}
	
	

	//遞歸的創建文件夾
	function mkdirs(dirpath) {
	    if (!fs.existsSync(path.dirname(dirpath))) {
	      mkdirs(path.dirname(dirpath));
	    }
	    fs.mkdirSync(dirpath);
	}
	   
	function createDir(myPath){
	    fs.existsSync(myPath) == false && mkdirs(myPath);
	}
}


//ffmpeg -i "concat:1.ts|2.ts" -acodec copy out.mp3

//ffmpeg -i "concat:1.ts|2.ts" -acodec copy -vcodec copy -absf aac_adtstoasc output.mp4

// ffmpeg -i input.txt -acodec copy -vcodec copy -absf aac_adtstoasc output.mp4
/*
ffconcat version 1.0
file  0.ts
file  1.ts
*/

/* 
 
 //文件移動
 function moveFile(oldPath,newPath){
 	try {
 	 fs.renameSync(oldPath, newPath);
 	}
 	catch (e) {
 		console.log("報錯后強制移動",e);
 		fs.renameSync(oldPath, newPath);
 	}
 }
 
 
 */

  

 

然后再新建 main.js

const fs  = require("fs");
const down = require("./down");
var host = 'https://xxxx/';目標網站
var outputName = "output.mp4";


var source = fs.readFileSync("./test.m3u8","utf-8"); //讀取 m3u8
var arr  = source.split("\n");
arr = arr.filter((item)=>{
	return item.match(/\.ts$/);
});

down({
	arr,
	host,
	name:outputName
})

  

里面使用到了 fs-extra 模塊,所以先安裝

npm i fs-extra

  

 

最后執行:

node main.js

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM