From 95a50dfa1568d8cff25ec343d31dd48b7fbb65e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?A=CC=81lvaro=20Velad=20Galva=CC=81n?= Date: Wed, 19 Jun 2024 11:55:29 +0200 Subject: [PATCH 1/3] fix: Fix audio properties detection --- lib/media/segment_utils.js | 48 +++++++++++++++++++++++++----- lib/util/mp4_box_parsers.js | 8 ++--- test/test/assets/audio-ac-4.mp4 | Bin 0 -> 606 bytes test/util/mp4_box_parsers_unit.js | 34 +++++++++++++++++++-- 4 files changed, 75 insertions(+), 15 deletions(-) create mode 100644 test/test/assets/audio-ac-4.mp4 diff --git a/lib/media/segment_utils.js b/lib/media/segment_utils.js index dea1f40fbc..4b53ef99c7 100644 --- a/lib/media/segment_utils.js +++ b/lib/media/segment_utils.js @@ -179,6 +179,7 @@ shaka.media.SegmentUtils = class { break; case 'ac-3': case 'ec-3': + case 'ac-4': case 'opus': case 'flac': audioCodecs.push(codecLC); @@ -235,9 +236,10 @@ shaka.media.SegmentUtils = class { // AUDIO // These are the various boxes that signal a codec. .box('mp4a', (box) => { - const parsedMP4ABox = shaka.util.Mp4BoxParsers.parseMP4A(box.reader); - channelCount = parsedMP4ABox.channelCount; - sampleRate = parsedMP4ABox.sampleRate; + const parsedAudioSampleEntryBox = + shaka.util.Mp4BoxParsers.audioSampleEntry(box.reader); + channelCount = parsedAudioSampleEntryBox.channelCount; + sampleRate = parsedAudioSampleEntryBox.sampleRate; if (box.reader.hasMoreData()) { Mp4Parser.children(box); } else { @@ -249,11 +251,41 @@ shaka.media.SegmentUtils = class { audioCodecs.push(parsedESDSBox.codec); hasAudio = true; }) - .box('ac-3', codecBoxParser) - .box('ec-3', codecBoxParser) - .box('opus', codecBoxParser) - .box('Opus', codecBoxParser) - .box('fLaC', codecBoxParser) + .box('ac-3', (box) => { + const parsedAudioSampleEntryBox = + shaka.util.Mp4BoxParsers.audioSampleEntry(box.reader); + channelCount = parsedAudioSampleEntryBox.channelCount; + sampleRate = parsedAudioSampleEntryBox.sampleRate; + codecBoxParser(box); + }) + .box('ec-3', (box) => { + const parsedAudioSampleEntryBox = + shaka.util.Mp4BoxParsers.audioSampleEntry(box.reader); + channelCount = parsedAudioSampleEntryBox.channelCount; + sampleRate = parsedAudioSampleEntryBox.sampleRate; + codecBoxParser(box); + }) + .box('ac-4', (box) => { + const parsedAudioSampleEntryBox = + shaka.util.Mp4BoxParsers.audioSampleEntry(box.reader); + channelCount = parsedAudioSampleEntryBox.channelCount; + sampleRate = parsedAudioSampleEntryBox.sampleRate; + codecBoxParser(box); + }) + .box('Opus', (box) => { + const parsedAudioSampleEntryBox = + shaka.util.Mp4BoxParsers.audioSampleEntry(box.reader); + channelCount = parsedAudioSampleEntryBox.channelCount; + sampleRate = parsedAudioSampleEntryBox.sampleRate; + codecBoxParser(box); + }) + .box('fLaC', (box) => { + const parsedAudioSampleEntryBox = + shaka.util.Mp4BoxParsers.audioSampleEntry(box.reader); + channelCount = parsedAudioSampleEntryBox.channelCount; + sampleRate = parsedAudioSampleEntryBox.sampleRate; + codecBoxParser(box); + }) // VIDEO // These are the various boxes that signal a codec. diff --git a/lib/util/mp4_box_parsers.js b/lib/util/mp4_box_parsers.js index 96ee0b42aa..3aa360a9aa 100644 --- a/lib/util/mp4_box_parsers.js +++ b/lib/util/mp4_box_parsers.js @@ -275,11 +275,11 @@ shaka.util.Mp4BoxParsers = class { } /** - * Parses a MP4A box. + * Parses a audio sample entry box. * @param {!shaka.util.DataViewReader} reader - * @return {!shaka.util.ParsedMP4ABox} + * @return {!shaka.util.ParsedAudioSampleEntryBox} */ - static parseMP4A(reader) { + static audioSampleEntry(reader) { reader.skip(6); // Skip "reserved" reader.skip(2); // Skip "data_reference_index" reader.skip(8); // Skip "reserved" @@ -820,7 +820,7 @@ shaka.util.ParsedTENCBox; * * @exportDoc */ -shaka.util.ParsedMP4ABox; +shaka.util.ParsedAudioSampleEntryBox; /** * @typedef {{ diff --git a/test/test/assets/audio-ac-4.mp4 b/test/test/assets/audio-ac-4.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..5b0eeb45a6607a3ab46b9bf759ab29b204f503ab GIT binary patch literal 606 zcmZutKTE?v6n|+!K^y`)R?Og_gCLUbp_7Y~2o5^LTuMW8Ny%M;I>qlIqMtzrhklQ` z2s*n7qQ7^|ky?Ck_xtnS-FtUL)EnySiJbU{MC33gDwkM&qRv3(c?Ca})kpwo=6|m6 zBI>>ql@H|L5GQQ>5ipl@VC(+D9|R8E>mnQ@eXhr*=jzP=xa9?}xea~W=ooXUkFB(2 zfh7i=ffPvy>PcbWWqUl;Tyi?)o%$n@7KUL{d6`+xIEXWy``!s`;T?sUNMlp`CX;N4 zysPB=^5)QvnEO3Z#GL2gvDeEY^&0-D^koWup|mm|-uTC1wC{s+v`<|lmyP+zv?s#I z_wJm4=wKUv`RZ(Z)kJ4CZG8s;b)PXXy`bJzS}ULpXcbuj+=RYa&`ePi { const videoSegmentUri = '/base/test/test/assets/sintel-video-segment.mp4'; const audioInitSegmentXheAacUri = '/base/test/test/assets/audio-xhe-aac.mp4'; + const audioInitSegmentAC4Uri = '/base/test/test/assets/audio-ac-4.mp4'; /** @type {!ArrayBuffer} */ let videoInitSegment; @@ -16,16 +17,20 @@ describe('Mp4BoxParsers', () => { let videoSegment; /** @type {!ArrayBuffer} */ let audioInitSegmentXheAac; + /** @type {!ArrayBuffer} */ + let audioInitSegmentAC4; beforeAll(async () => { const responses = await Promise.all([ shaka.test.Util.fetch(videoInitSegmentUri), shaka.test.Util.fetch(videoSegmentUri), shaka.test.Util.fetch(audioInitSegmentXheAacUri), + shaka.test.Util.fetch(audioInitSegmentAC4Uri), ]); videoInitSegment = responses[0]; videoSegment = responses[1]; audioInitSegmentXheAac = responses[2]; + audioInitSegmentAC4 = responses[3]; }); it('parses init segment', () => { @@ -177,9 +182,10 @@ describe('Mp4BoxParsers', () => { .box('stbl', Mp4Parser.children) .fullBox('stsd', Mp4Parser.sampleDescription) .box('mp4a', (box) => { - const parsedMP4ABox = shaka.util.Mp4BoxParsers.parseMP4A(box.reader); - channelCount = parsedMP4ABox.channelCount; - sampleRate = parsedMP4ABox.sampleRate; + const parsedAudioSampleEntryBox = + shaka.util.Mp4BoxParsers.audioSampleEntry(box.reader); + channelCount = parsedAudioSampleEntryBox.channelCount; + sampleRate = parsedAudioSampleEntryBox.sampleRate; if (box.reader.hasMoreData()) { Mp4Parser.children(box); } @@ -193,6 +199,28 @@ describe('Mp4BoxParsers', () => { expect(codec).toBe('mp4a.40.42'); }); + it('parses ac-4 box for ac-4 segment', () => { + let channelCount; + let sampleRate; + + const Mp4Parser = shaka.util.Mp4Parser; + new Mp4Parser() + .box('moov', Mp4Parser.children) + .box('trak', Mp4Parser.children) + .box('mdia', Mp4Parser.children) + .box('minf', Mp4Parser.children) + .box('stbl', Mp4Parser.children) + .fullBox('stsd', Mp4Parser.sampleDescription) + .box('ac-4', (box) => { + const parsedAudioSampleEntryBox = + shaka.util.Mp4BoxParsers.audioSampleEntry(box.reader); + channelCount = parsedAudioSampleEntryBox.channelCount; + sampleRate = parsedAudioSampleEntryBox.sampleRate; + }).parse(audioInitSegmentAC4, /* partialOkay= */ false); + expect(channelCount).toBe(10); + expect(sampleRate).toBe(48000); + }); + /** * * Explanation on the Uint8Array: From 01840a2c5420bf9b71248302df6876ca2e213b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?A=CC=81lvaro=20Velad=20Galva=CC=81n?= Date: Thu, 20 Jun 2024 08:19:12 +0200 Subject: [PATCH 2/3] Fix typo --- lib/util/mp4_box_parsers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util/mp4_box_parsers.js b/lib/util/mp4_box_parsers.js index c19bbb78bc..2f9469f9e6 100644 --- a/lib/util/mp4_box_parsers.js +++ b/lib/util/mp4_box_parsers.js @@ -275,7 +275,7 @@ shaka.util.Mp4BoxParsers = class { } /** - * Parses a audio sample entry box. + * Parses an audio sample entry box. * @param {!shaka.util.DataViewReader} reader * @return {!shaka.util.ParsedAudioSampleEntryBox} */ From a4ecad139808d73751dc7ac0515182c1567cda04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?A=CC=81lvaro=20Velad=20Galva=CC=81n?= Date: Thu, 20 Jun 2024 08:20:13 +0200 Subject: [PATCH 3/3] Simplify the code --- lib/media/segment_utils.js | 103 ++++++++++--------------------------- 1 file changed, 28 insertions(+), 75 deletions(-) diff --git a/lib/media/segment_utils.js b/lib/media/segment_utils.js index 581112251a..84bf64e75b 100644 --- a/lib/media/segment_utils.js +++ b/lib/media/segment_utils.js @@ -208,6 +208,19 @@ shaka.media.SegmentUtils = class { /** @type {?string} */ let baseBox; + const genericAudioBox = (box) => { + const parsedAudioSampleEntryBox = + shaka.util.Mp4BoxParsers.audioSampleEntry(box.reader); + channelCount = parsedAudioSampleEntryBox.channelCount; + sampleRate = parsedAudioSampleEntryBox.sampleRate; + codecBoxParser(box); + }; + + const genericVideoBox = (box) => { + baseBox = box.name; + Mp4Parser.visualSampleEntry(box); + }; + new Mp4Parser() .box('moov', Mp4Parser.children) .box('trak', Mp4Parser.children) @@ -251,84 +264,24 @@ shaka.media.SegmentUtils = class { audioCodecs.push(parsedESDSBox.codec); hasAudio = true; }) - .box('ac-3', (box) => { - const parsedAudioSampleEntryBox = - shaka.util.Mp4BoxParsers.audioSampleEntry(box.reader); - channelCount = parsedAudioSampleEntryBox.channelCount; - sampleRate = parsedAudioSampleEntryBox.sampleRate; - codecBoxParser(box); - }) - .box('ec-3', (box) => { - const parsedAudioSampleEntryBox = - shaka.util.Mp4BoxParsers.audioSampleEntry(box.reader); - channelCount = parsedAudioSampleEntryBox.channelCount; - sampleRate = parsedAudioSampleEntryBox.sampleRate; - codecBoxParser(box); - }) - .box('ac-4', (box) => { - const parsedAudioSampleEntryBox = - shaka.util.Mp4BoxParsers.audioSampleEntry(box.reader); - channelCount = parsedAudioSampleEntryBox.channelCount; - sampleRate = parsedAudioSampleEntryBox.sampleRate; - codecBoxParser(box); - }) - .box('Opus', (box) => { - const parsedAudioSampleEntryBox = - shaka.util.Mp4BoxParsers.audioSampleEntry(box.reader); - channelCount = parsedAudioSampleEntryBox.channelCount; - sampleRate = parsedAudioSampleEntryBox.sampleRate; - codecBoxParser(box); - }) - .box('fLaC', (box) => { - const parsedAudioSampleEntryBox = - shaka.util.Mp4BoxParsers.audioSampleEntry(box.reader); - channelCount = parsedAudioSampleEntryBox.channelCount; - sampleRate = parsedAudioSampleEntryBox.sampleRate; - codecBoxParser(box); - }) + .box('ac-3', genericAudioBox) + .box('ec-3', genericAudioBox) + .box('ac-4', genericAudioBox) + .box('Opus', genericAudioBox) + .box('fLaC', genericAudioBox) // VIDEO // These are the various boxes that signal a codec. - .box('avc1', (box) => { - baseBox = box.name; - Mp4Parser.visualSampleEntry(box); - }) - .box('avc3', (box) => { - baseBox = box.name; - Mp4Parser.visualSampleEntry(box); - }) - .box('hev1', (box) => { - baseBox = box.name; - Mp4Parser.visualSampleEntry(box); - }) - .box('hvc1', (box) => { - baseBox = box.name; - Mp4Parser.visualSampleEntry(box); - }) - .box('dva1', (box) => { - baseBox = box.name; - Mp4Parser.visualSampleEntry(box); - }) - .box('dvav', (box) => { - baseBox = box.name; - Mp4Parser.visualSampleEntry(box); - }) - .box('dvh1', (box) => { - baseBox = box.name; - Mp4Parser.visualSampleEntry(box); - }) - .box('dvhe', (box) => { - baseBox = box.name; - Mp4Parser.visualSampleEntry(box); - }) - .box('vp09', (box) => { - baseBox = box.name; - Mp4Parser.visualSampleEntry(box); - }) - .box('av01', (box) => { - baseBox = box.name; - Mp4Parser.visualSampleEntry(box); - }) + .box('avc1', genericVideoBox) + .box('avc3', genericVideoBox) + .box('hev1', genericVideoBox) + .box('hvc1', genericVideoBox) + .box('dva1', genericVideoBox) + .box('dvav', genericVideoBox) + .box('dvh1', genericVideoBox) + .box('dvhe', genericVideoBox) + .box('vp09', genericVideoBox) + .box('av01', genericVideoBox) .box('avcC', (box) => { let codecBase = baseBox || ''; switch (baseBox) {