使用 JS 获取视频 Codec

如果第一次接触 Media Source ,大家都会注意到有一个关于 视频 codecs 的指定。

var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';

if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
  var mediaSource = new MediaSource();
  //console.log(mediaSource.readyState); // closed
  video.src = URL.createObjectURL(mediaSource);
  mediaSource.addEventListener('sourceopen', sourceOpen);
} else {
  console.error('Unsupported MIME type or codec: ', mimeCodec);
}

这个判断其实只是为了确认当前浏览器是否支持对这种视频编码方式解码播放。

很多人问了,究竟 codec 和 视频之间是什么关系?

视频编码器

视频随着诞生到发展,无疑在追求更高质量的画质和尽可能低的比特率。透过下图我们可以看到视频编码方式的发展。

其中可以看到,最新的就是大家关注的 H.265 以及 VP9。不过由于历史的积累,和浏览器支持的问题,现在大多数都还是以 H.264 编码的视频为主。

其中 H.264 ,也是近十多年来一直被各大厂商一直使用的视频编码格式,相比上古的 H.263 ,视频在参考帧和区块运动补偿都有更进一步的优化。同时 H.264 也还在发展中不断进行迭代,被广泛运用到电视台,视频发行商,以及互联网多媒体。

其中编码当中我们是需要指明当前视频采用的 Profiles , 也就是需要告诉解码器需要怎样的要求去进行码流的解码。

比如常见的 Baseline Profile, Extended Profile 以及 High Profile 等。

Levels 则去约定当前视频的所需要解码器的性能要求。不同级别对应着在宏块所能采用最大的解码速度。

我们使用 Mediainfo 这个 App 查看一般可以看到 这些信息

那么,我们如何获取这些信息呢?

如何获取 Codec

这个才是大家关心的话题,有很多视频工具可以帮助大家获取到当前视频编码的信息。比如常见的 FFmpeg 或者 mp4box

FFmpge

$ ffmpge -i ./test1.mp4

我们会看到这样一个内容

我们可以看到这一行

Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(tv, bt709), 480x480, 428 kb/s, 30 fps, 30 tbr, 15360 tbn, 60 tbc (default)

mp4box

$ mp4box -info ./test1.mp4

我们可以看到明确的

Codec Parameters: avc1.42C01E

JS 如何获取 Codec

前面可以通过工具可以获取,但是我们可能面临更大型的应用,我们可能会面临不同的视频编码,我们需要动态获取。这个时候我们如何通过 JS 获取视频的 Codec 值呢?

嗨皮的是, mp4box 有 JS 版本:

mp4box.js

是不是喜出望外了?

原理其实,及时我们先请求视频的 metadata 头部信息,这个比如我们一般请求 1-50*1000 字节的 Range 信息。

const ASSET_URL = './video/a3.mp4';
const video = document.querySelector('.js-player-mp4');
const mp4boxfile = MP4Box.createFile();


function playMp4() {
  log('Get Video Element');
  bindMp4box();
  // 设置 range 请求
  const range = 'bytes=0-50000';
  fetch(ASSET_URL, {
    headers: {
      range,
    }
  }).then(function(response) {
    return response.arrayBuffer();
  }).then(function(arrayBuffer) {
	// mp4box 需要自定起点
    arrayBuffer.fileStart = 0;
    mp4boxfile.appendBuffer(arrayBuffer);
  });
}

function bindMp4box() {
  mp4boxfile.onReady = function (info) {
    log('Get Video Info');
    video.src = ASSET_URL;
    video.play();
    showVideoInfo(info);
	}
}

function showVideoInfo(info) {
  console.log(info);
  log('Duration: ' + parseInt(info.duration / 1000) + 's');
  log('Brands: ' + info.brands.join(','));
  log('Video Metadata:  ');
  const videoTrack = info.tracks[0];
  log('Video Codec: "' + videoTrack.codec + '"; nb_samples: ' + videoTrack.nb_samples);
  log(videoTrack.name + ': size: ' + videoTrack.size + '; bitrate: ' + videoTrack.bitrate);
  log('Audio Metadata');
  const audioTrack = info.tracks[1];
  log('Audio Codec: "' + audioTrack.codec + '"; nb_samples: ' + audioTrack.nb_samples);
  log(audioTrack.name + ': size: ' + audioTrack.size + '; bitrate: ' + audioTrack.bitrate);

}

Demo

代码地址: https://github.com/JackPu/freleap.github.io/tree/master/js-get-codec

如果需要自己手写的话,可以参考 mp4box.js 的实现,需要对 Buffer 进行扫描,然后判断获取 Profile 和 level 值。

box-codecs.js

如果希望详细了解 codec 里面的组成,推荐阅读Web Video MimeType 究竟代表什么意思

English Version

扩展阅读


@ Cover - BluBlu Studios