nodejs环境下如何获取MP4视频时长(二)

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

一、问题

在上一篇小文中,已经介绍了如何从mp4文件结构信息中获取time scale duration两个参数,进而计算出视频时长。本来事情到此结束,却在后面的测试中出了问题。上文中的getTime函数直接返回了false,也就是没有找到mvhd字段。

二、查找原因

mp4文件内部是由一系列的box组成,我们所关心的是moov>mvhd,下表截取了部分mp4文件结构的描述,包含了我们所关心的moov,其中打勾的字段是必选的。

一级 二级 三级 是否必选 字段描述
ftyp file type and compatibility
pdin         progressive download information
moov      √  container for all the metadata
  mvhd   √  movie header, overall declarations
  trak     √  container for an individual track or stream
    tkhd   √  track header, overall information about the track

我们可以看到mvhd也是必选的。其实网上的描述具有一定的误导性,往往我们认为moov会放在文件的开头,事实上并非如此。举一个现实的例子,比如某些监控的录像,由于采用流存储,也不可能都放到内存,文件不断的写入到磁盘,在最终结束之前,谁也不知道文件有多长,除非事先定义好。因此,moov字段的位置不固定,可以在头部,也可以在尾部。 为了验证我们的猜想,使用专用的软件mp4info进行查看,结果如下图

这是常规结构,moov在文件头部 头部.png

这是异常结构,moov在文件尾部

图片.png

三、解决方案

既然找到了原因,那么我们就对症下药。想当然的,我们可以把整个文件放到buffer中,但是要考虑到视频文件有可能在10GB级别,放到内存既不明智也不现实,我们不妨利用循环,从文件头部一直向后扫描,直到找到moov字段为止。由于存在异步操作,我们使用async/await关键字,使结构更清晰。下面一起看如何实现。

step 1

正常读取文件,构造read()函数 返回mp4文件数据

async function read(){
    return new Promise((resolve,reject)=>{
        fs.open('./6.mp4', 'r',(err,fd)=>{
            if(err){
                reject(err)
            }else{
                resolve(fd)
            }
        })
    })
}
复制代码

step 2

声明readfile函数,将mp4文件数据放入buffer中,参数是文件流、buffer数组,开始位置,这里也比较简单,就不展开了。


async function readfile(fd,buff,start_position){
  return new Promise((resolve,reject)=>{
    fs.read(fd, buff , 0 ,10000 ,start_position, function(err, bytesRead, buffer) {
        if(err){
            reject(err)
        }else{
            resolve(buffer)
        }
    })
  })
}
复制代码

step 3

主函数,首先创建一个10kb大小的buffer区,将文件不断送入进行检测,每次指针移动9900个字节位置,直到检测到moov字段为止。这里有个疑问,为什么10kb的buffer,但是下一次循环指针只移动9900呢,主要考虑到可能恰巧将moov字段切断了,有一定冗余空间,虽然这个概率可能比彩票中500万高不了多少。这里简单定义循环参数i最大为100w,这样理论上可以检测的文件大小约为9.9x100w/1024= 9668MB。这里考虑比较简单,没有对文件参数进行过多限制,主要是为了验证可行性。

var main = async function (){
    var start_position = 0
    const buff = Buffer.alloc(10000);
    var fd = await read();
    for(i = 0 ; i<1000000 ;i++){
        var buff_data = await readfile(fd,buff,start_position);
        const time = getTime(buff_data);
        if(time){
            console.log(i);
            console.log(time);
            i = 1000000;
        }else{
            start_position = start_position + 9900;
        }
    }
  };
复制代码

step 4

运行验证

扫描二维码关注公众号,回复: 14232473 查看本文章
main()
复制代码

本人测试了多个视频,均能准确给出结果,最小的文件2.46M,最大的959M。

图片.png

结束

nodejs环境下获取mp4视频时长的分享到这里就结束了。这是本人原创文章,也可以在本人博客上找到。如果不想自己这么麻烦的话,可以用npm上的开源包,比如 get-video-duration

(完)

猜你喜欢

转载自juejin.im/post/7105585510273581093
今日推荐