Skip to content

Commit 446d831

Browse files
StaZhudalecurtis
authored andcommitted
Add H.265/VP8/VP9/AV1 samples to video decoding page.
This should be helpful for developer to test the browser implement or compare the performance of different codecs on different platforms. Note that the previous version of mp4box is not able to parse AV1 video codec string correctly, so to support AV1, we also need to upgrade mp4box to version 0.5.2. Safari doesn't support `gl.createShader` so change the default renderer to `2d`. All sample videos are encoded from `bbb-1920x1080-cfg06.mp4` using ffmpeg and shaka packager.
1 parent cad5683 commit 446d831

File tree

11 files changed

+99
-45
lines changed

11 files changed

+99
-45
lines changed

samples/audio-video-player/audio_video_player.html

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,27 @@
4646
with the appropriate HTTP headers.
4747
</p>
4848
<div id=controls>
49-
<p id=loading>Loading...</p>
50-
<button disabled=true>Play</button>
49+
<p>
50+
Video Codec:
51+
<label for="video_codec_h264">
52+
<input id="video_codec_h264" type="radio" name="video_codec" value="avc" checked> H.264
53+
</label>
54+
<label for="video_codec_h265">
55+
<input id="video_codec_h265" type="radio" name="video_codec" value="hevc"> H.265
56+
</label>
57+
<label for="video_codec_vp8">
58+
<input id="video_codec_vp8" type="radio" name="video_codec" value="vp8"> VP8
59+
</label>
60+
<label for="video_codec_vp9">
61+
<input id="video_codec_vp9" type="radio" name="video_codec" value="vp9"> VP9
62+
</label>
63+
<label for="video_codec_av1">
64+
<input id="video_codec_av1" type="radio" name="video_codec" value="av1"> AV1
65+
</label>
66+
</p>
67+
<button>Play</button>
5168
<label for=volume>Volume</label>
52-
<input id=volume type=range value=0.8 min=0 max=1.0 step=0.01></input>
69+
<input id=volume type=range value=0.8 min=0 max=1.0 step=0.01 disabled></input>
5370
</div>
5471
<canvas width=1280 height=720></canvas>
5572
</div>
@@ -65,35 +82,47 @@
6582
// Instantiate the "media worker" and start loading the files. The worker will
6683
// house and drive the demuxers and decoders.
6784
let mediaWorker = new Worker('./media_worker.js');
68-
mediaWorker.postMessage({command: 'initialize',
69-
audioFile: '../data/bbb_audio_aac_frag.mp4',
70-
videoFile: '../data/bbb_video_avc_frag.mp4',
71-
canvas: offscreenCanvas},
72-
{transfer: [offscreenCanvas]});
7385

74-
// Wait for worker initialization. Use metadata to init the WebAudioController.
75-
let initResolver = null;
76-
let initDone = new Promise(resolver => (initResolver = resolver));
86+
let initDone = false;
87+
7788
let audioController = new WebAudioController();
78-
mediaWorker.addEventListener('message', (e) => {
79-
console.assert(e.data.command == 'initialize-done');
80-
audioController.initialize(e.data.sampleRate, e.data.channelCount,
81-
e.data.sharedArrayBuffer);
82-
initResolver();
83-
initResolver = null;
84-
});
85-
await initDone;
8689

8790
// Set up volume slider.
8891
$('#volume').onchange = (e) => { audioController.setVolume(e.target.value); }
8992

90-
// Enable play now that we're loaded
9193
let playButton = $('button');
92-
let loadingElement = $('#loading');
93-
playButton.disabled = false;
94-
loadingElement.innerText = 'Ready! Click play.'
94+
playButton.onclick = async () => {
95+
if (!initDone) {
96+
document.querySelectorAll("input[name=\"video_codec\"]").forEach(input => input.disabled = true);
97+
playButton.innerText = "Loading...";
98+
playButton.disabled = true;
99+
100+
// Wait for worker initialization. Use metadata to init the WebAudioController.
101+
await new Promise(resolve => {
102+
const videoCodec = `${document.querySelector("input[name=\"video_codec\"]:checked").value}`;
103+
mediaWorker.postMessage(
104+
{
105+
command: 'initialize',
106+
audioFile: '../data/bbb_audio_aac_frag.mp4',
107+
videoFile: `../data/bbb_video_${videoCodec}_frag.mp4`,
108+
canvas: offscreenCanvas
109+
},
110+
{ transfer: [offscreenCanvas] }
111+
);
95112

96-
playButton.onclick = () => {
113+
mediaWorker.addEventListener('message', (e) => {
114+
console.assert(e.data.command == 'initialize-done');
115+
audioController.initialize(e.data.sampleRate, e.data.channelCount, e.data.sharedArrayBuffer);
116+
initDone = true;
117+
resolve();
118+
});
119+
});
120+
playButton.innerText = "Play";
121+
playButton.disabled = false;
122+
$('#volume').disabled = false;
123+
}
124+
125+
// Enable play now that we're loaded
97126
if (playButton.innerText == "Play") {
98127
console.log("playback start");
99128

samples/audio-video-player/mp4_pull_demuxer.js

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@ export class MP4PullDemuxer extends PullDemuxerBase {
4242
};
4343
} else {
4444
return {
45-
codec: this.videoTrack.codec,
45+
// Browser doesn't support parsing full vp8 codec (eg: `vp08.00.41.08`),
46+
// they only support `vp8`.
47+
codec: this.videoTrack.codec.startsWith('vp08') ? 'vp8' : this.videoTrack.codec,
4648
displayWidth: this.videoTrack.track_width,
4749
displayHeight: this.videoTrack.track_height,
48-
description: this._getAvcDescription(this.source.getAvccBox())
50+
description: this._getDescription(this.source.getDescriptionBox())
4951
}
5052
}
5153
}
@@ -64,9 +66,9 @@ export class MP4PullDemuxer extends PullDemuxerBase {
6466
});
6567
}
6668

67-
_getAvcDescription(avccBox) {
69+
_getDescription(descriptionBox) {
6870
const stream = new DataStream(undefined, 0, DataStream.BIG_ENDIAN);
69-
avccBox.write(stream);
71+
descriptionBox.write(stream);
7072
return new Uint8Array(stream.buffer, 8); // Remove the box header.
7173
}
7274

@@ -94,7 +96,6 @@ export class MP4PullDemuxer extends PullDemuxerBase {
9496
console.assert(this._pending_read_resolver);
9597
this.source.start(this._onSamples.bind(this));
9698
return promise;
97-
9899
}
99100

100101
_onSamples(samples) {
@@ -167,9 +168,14 @@ class MP4Source {
167168
return new Promise((resolver) => { this._info_resolver = resolver; });
168169
}
169170

170-
getAvccBox() {
171+
getDescriptionBox() {
171172
// TODO: make sure this is coming from the right track.
172-
return this.file.moov.traks[0].mdia.minf.stbl.stsd.entries[0].avcC
173+
const entry = this.file.moov.traks[0].mdia.minf.stbl.stsd.entries[0];
174+
const box = entry.avcC || entry.hvcC || entry.vpcC || entry.av1C;
175+
if (!box) {
176+
throw new Error("avcC, hvcC, vpcC, or av1C box not found!");
177+
}
178+
return box;
173179
}
174180

175181
getAudioSpecificConfig() {

samples/data/bbb_audio_aac_frag.mp4

-8.88 MB
Binary file not shown.

samples/data/bbb_video_av1_frag.mp4

4.69 MB
Binary file not shown.

samples/data/bbb_video_avc_frag.mp4

-78.3 MB
Binary file not shown.

samples/data/bbb_video_hevc_frag.mp4

4.94 MB
Binary file not shown.

samples/data/bbb_video_vp8_frag.mp4

11.9 MB
Binary file not shown.

samples/data/bbb_video_vp9_frag.mp4

5 MB
Binary file not shown.

samples/third_party/mp4boxjs/mp4box.all.min.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

samples/video-decode-display/demuxer_mp4.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,21 +60,18 @@ class MP4Demuxer {
6060
}
6161

6262
// Get the appropriate `description` for a specific track. Assumes that the
63-
// track is H.264 or H.265.
63+
// track is H.264, H.265, VP8, VP9, or AV1.
6464
#description(track) {
6565
const trak = this.#file.getTrackById(track.id);
6666
for (const entry of trak.mdia.minf.stbl.stsd.entries) {
67-
if (entry.avcC || entry.hvcC) {
67+
const box = entry.avcC || entry.hvcC || entry.vpcC || entry.av1C;
68+
if (box) {
6869
const stream = new DataStream(undefined, 0, DataStream.BIG_ENDIAN);
69-
if (entry.avcC) {
70-
entry.avcC.write(stream);
71-
} else {
72-
entry.hvcC.write(stream);
73-
}
70+
box.write(stream);
7471
return new Uint8Array(stream.buffer, 8); // Remove the box header.
7572
}
7673
}
77-
throw "avcC or hvcC not found";
74+
throw new Error("avcC, hvcC, vpcC, or av1C box not found");
7875
}
7976

8077
#onReady(info) {
@@ -83,7 +80,9 @@ class MP4Demuxer {
8380

8481
// Generate and emit an appropriate VideoDecoderConfig.
8582
this.#onConfig({
86-
codec: track.codec,
83+
// Browser doesn't support parsing full vp8 codec (eg: `vp08.00.41.08`),
84+
// they only support `vp8`.
85+
codec: track.codec.startsWith('vp08') ? 'vp8' : track.codec,
8786
codedHeight: track.video.height,
8887
codedWidth: track.video.width,
8988
description: this.#description(track),

0 commit comments

Comments
 (0)