运维开发网

利用 nodejs 解析 m3u8 格式文件,并下 ts 合并为 mp4

运维开发网 https://www.qedev.com 2020-07-21 20:09 出处:网络 作者:运维开发网整理
利用 nodejs 解析 m3u8 格式文件,并下 ts 合并为 mp4 以前看视频的时候,直接找到 video标签,查看视频地址,然后下载下来。。 后来发现,好多 video 标签打开元素审查,如下:   blob开始的东西,下载不了啦。。。   其实我们打开 network 还是能看见,加载了一堆的 .ts 文件。其实.ts文件就是被切成一段一段的视频。 理论上,把这些文件都下载下来,再合并,

利用 nodejs 解析 m3u8 格式文件,并下 ts 合并为 mp4

以前看视频的时候,直接找到 video标签,查看视频地址,然后下载下来。。

后来发现,好多 video 标签打开元素审查,如下:

利用 nodejs 解析 m3u8 格式文件,并下 ts 合并为 mp4

 

blob开始的东西,下载不了啦。。。

 

其实我们打开 network 还是能看见,加载了一堆的 .ts 文件。其实.ts文件就是被切成一段一段的视频。 理论上,把这些文件都下载下来,再合并,就完成了,,,

理论一句话,代码上千行...

 

一、问题

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

    答案: ts 相关的信息,都存在一个叫 m3u8 的文件。 如果仔细点观察 network 是可以找到这个文件的请求的。该文件内容大致如下:

  

利用 nodejs 解析 m3u8 格式文件,并下 ts 合并为 mp4

 

  这个文件,很显然,存了每个 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,
	outputName
})

  

里面使用到了 fs-extra 模块,所以先安装

npm i fs-extra

  

 

最后执行:

node main.js

0

精彩评论

暂无评论...
验证码 换一张
取 消