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

1,682 阅读3分钟

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

一、需求

用户手机端上传视频文件(确定为MP4格式),服务端如果是nodejs环境,应该如何迅速解析出视频时长并存储相关结构化信息呢? 能想到的做法无外乎几种:

  • a、借用三方插件完成解析;
  • b、通过执行本地命令的方式,比如nodejs调用ffmpeg或者python脚本等;
  • c、node自身通过解析mp4协议实现。 今天,就一起来看一下第三种方法,如何使用nodejs自带模块实现解析mp4文件。

在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理I/O操作中移动的数据时,就有可能使用 Buffer 库。原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。

Buffer类为我们操作原始文件提供了行之有效的方法,使我们能够看到二进制的数据。这里就不展开介绍,使用文档请看官方API介绍。在使用Buffer前,我们先简单学习一下MP4的文件结构。

二、MP4文件结构

我们可以看一下网友 z折腾转发的文章《  mp4编码全介绍 (二) 》,里面详细介绍了mp4格式的方方面面(没想到一个简单的视频竟然还有这么多规范),这里只选取跟视频时长有关的部分进行介绍。

image.png

虽然视频是非结构化数据,但是视频内的格式信息依旧是结构化字段。

一个MP4有且只有一个“ftyp”类型的box,有且只有一个“moov”类型的box(Movie Box),MP4文件的媒体数据包含在“mdat”类型的box(Midia Data Box)中。“moov”中会包含1个“mvhd”和若干个“trak”。而我们计算时长需要的两个参数就在mvhd中。

我们现在只要从mvhd中获取两个信息就可以,一个是”time scale”,另一个是”duration”,两个都是4字节,从mvhd之后的第16个字节开始读4个字节就是”time scale”,再读4个字节就是”duration”,duration/time scale = 视频时长的秒数。

三、实操

上面的理论铺垫已经完成,下面就是实际操作。

step1. 通过自带的fs模块读取本地文件yy.mp4

fs.open('./yy.mp4', 'r',function(err,fd){
        if (err) {throw err;}
        const buff = Buffer.alloc(100);
        fs.read(fd, buff , 0 ,100 ,0, function(err, bytesRead, buffer) {
            if (err) {throw err;}
            const time = getTime(buffer);
                console.log(time);
        })
})

step2.核心方法getTime

上述代码块中getTime方法是将buffer丢进去找到相关字段并计算出时长,具体的方法是这样的:

function getTime(buffer){
    if(buffer.indexOf(Buffer.from('mvhd')) != -1){
    //如果buffer中含有mvhd字段,就计算,否则返回false
        var start = buffer.indexOf(Buffer.from('mvhd'));
    }else{
        return false;
    } 
    const box_size = buffer.readUInt32BE(start);
    const box_type = buffer.readUInt32BE(start+4);
    const create_time = buffer.readUInt32BE(start+8);
    const modi_time = buffer.readUInt32BE(start+12);
    const timeScale = buffer.readUInt32BE(start+16);
    const duration = buffer.readUInt32BE(start + 20);
    const movieLength = Math.floor(duration / timeScale);
    // console.log(start,box_size,box_type,create_time,modi_time,duration,timeScale,movieLength);//注释打开,就能看到更多的信息
    return movieLength;
}

step3. 运行 node test.js

通过运行node命令测试,发现结果符合预期

image.png

本来体验到此结束,一切都是那么简单而美好,不幸的是,后来却出了bug,具体是什么坑,请看后面文章。