UNPKG

shaka-player

Version:
374 lines (319 loc) 12.9 kB
/*! @license * Shaka Player * Copyright 2016 Google LLC * SPDX-License-Identifier: Apache-2.0 */ describe('Mp4Parser', () => { const Util = shaka.test.Util; let boxData; let fullBoxData; let boxWithChildData; let fullBoxWithChildData; let boxWithSampleDescription; let partialBoxWithSampleDescription; let multipleSingleLevelBoxes; let twoLevelBoxStructure; beforeAll(() => { boxData = new Uint8Array([ 0x00, 0x00, 0x00, 0x0C, // size 0x62, 0x30, 0x30, 0x31, // type 0x00, 0x11, 0x22, 0x33, // payload ]); fullBoxData = new Uint8Array([ 0x00, 0x00, 0x00, 0x10, // size 0x62, 0x30, 0x30, 0x31, // type 0x01, // version 0x12, 0x34, 0x56, // flags 0x00, 0x11, 0x22, 0x33, // payload ]); boxWithChildData = new Uint8Array([ 0x00, 0x00, 0x00, 0x14, // size 0x62, 0x30, 0x30, 0x33, // type 0x00, 0x00, 0x00, 0x0C, // child [0] size 0x62, 0x30, 0x33, 0x31, // child [0] type 0x00, 0x11, 0x22, 0x33, // child [0] payload 0x00, 0x00, 0x00, 0x0C, // child [1] size 0x62, 0x30, 0x33, 0x32, // child [1] type 0x44, 0x55, 0x66, 0x77, // child [1] payload ]); fullBoxWithChildData = new Uint8Array([ 0x00, 0x00, 0x00, 0x18, // size 0x62, 0x30, 0x30, 0x33, // type 0x01, // version 0x12, 0x34, 0x56, // flags 0x00, 0x00, 0x00, 0x0C, // child [0] size 0x62, 0x30, 0x33, 0x31, // child [0] type 0x00, 0x11, 0x22, 0x33, // child [0] payload 0x00, 0x00, 0x00, 0x0C, // child [1] size 0x62, 0x30, 0x33, 0x32, // child [1] type 0x44, 0x55, 0x66, 0x77, // child [1] payload ]); boxWithSampleDescription = new Uint8Array([ 0x00, 0x00, 0x00, 0x24, // size 0x62, 0x30, 0x30, 0x33, // type 0x00, 0x00, 0x00, 0x02, // number of chidren 0x00, 0x00, 0x00, 0x0C, // child [0] size 0x62, 0x30, 0x33, 0x32, // child [0] type 0x00, 0x11, 0x22, 0x33, // child [0] payload 0x00, 0x00, 0x00, 0x0C, // child [1] size 0x62, 0x30, 0x33, 0x33, // child [1] type 0x44, 0x55, 0x66, 0x77, // child [1] payload ]); partialBoxWithSampleDescription = new Uint8Array([ 0x00, 0x00, 0x00, 0x24, // size 0x62, 0x30, 0x30, 0x33, // type 0x00, 0x00, 0x00, 0x02, // number of chidren 0x00, 0x00, 0x00, 0x0C, // child [0] size 0x62, 0x30, 0x33, 0x32, // child [0] type 0x00, 0x11, 0x22, 0x33, // child [0] payload // Omit child [1] ]); multipleSingleLevelBoxes = new Uint8Array([ 0x00, 0x00, 0x00, 0x0C, // box [0] size 0x62, 0x30, 0x30, 0x31, // box [0] type 0x00, 0x11, 0x22, 0x33, // box [0] payload 0x00, 0x00, 0x00, 0x0C, // box [1] size 0x62, 0x30, 0x30, 0x32, // box [1] type 0x00, 0x11, 0x22, 0x33, // box [1] payload 0x00, 0x00, 0x00, 0x0C, // box [2] size 0x62, 0x30, 0x30, 0x33, // box [2] type 0x00, 0x11, 0x22, 0x33, // box [2] payload 0x00, 0x00, 0x00, 0x0C, // box [3] size 0x62, 0x30, 0x30, 0x34, // box [3] type 0x00, 0x11, 0x22, 0x33, // box [3] payload ]); twoLevelBoxStructure = new Uint8Array([ 0x00, 0x00, 0x00, 0x14, // box [0] size 0x62, 0x30, 0x31, 0x30, // box [0] type 0x00, 0x00, 0x00, 0x0C, // box [0] [0] size 0x00, 0x30, 0x31, 0x31, // box [0] [0] type 0x00, 0x11, 0x22, 0x33, // box [0] [0] payload 0x00, 0x00, 0x00, 0x14, // box [1] size 0x62, 0x30, 0x32, 0x30, // box [1] type 0x00, 0x00, 0x00, 0x0C, // box [1] [0] size 0x62, 0x30, 0x32, 0x31, // box [1] [0] type 0x00, 0x11, 0x22, 0x33, // box [1] [0] payload 0x00, 0x00, 0x00, 0x14, // box [2] size 0x62, 0x30, 0x33, 0x30, // box [2] type 0x00, 0x00, 0x00, 0x0C, // box [2] [0] size 0x62, 0x30, 0x33, 0x31, // box [2] [0] type 0x00, 0x11, 0x22, 0x33, // box [2] [0] payload 0x00, 0x00, 0x00, 0x14, // box [3] size 0x62, 0x30, 0x34, 0x30, // box [3] type 0x00, 0x00, 0x00, 0x0C, // box [3] [0] size 0x62, 0x30, 0x34, 0x31, // box [3] [0] type 0x00, 0x11, 0x22, 0x33, // box [3] [0] payload ]); }); describe('headerDefinitions', () => { it('reads box header', () => { const callback = jasmine.createSpy('parser callback').and.callFake( (box) => { expect(box.start).toBe(0); expect(box.size).toBe(12); expect(box.version).toBe(null); expect(box.flags).toBe(null); }); new shaka.util.Mp4Parser() .box('b001', Util.spyFunc(callback)).parse(boxData); expect(callback).toHaveBeenCalled(); }); it('reads full box header', () => { const callback = jasmine.createSpy('parser callback').and.callFake( (box) => { expect(box.start).toBe(0); expect(box.size).toBe(16); expect(box.version).toBe(1); expect(box.flags).toBe(0x123456); }); new shaka.util.Mp4Parser() .fullBox('b001', Util.spyFunc(callback)).parse(fullBoxData); expect(callback).toHaveBeenCalled(); }); }); describe('boxDefinitions', () => { it('reads children definition', () => { const parentBox = jasmine.createSpy('parent box').and.callFake( shaka.util.Mp4Parser.children); const childBox1 = jasmine.createSpy('child box 1'); const childBox2 = jasmine.createSpy('child box 2'); new shaka.util.Mp4Parser() .box('b003', Util.spyFunc(parentBox)) .box('b031', Util.spyFunc(childBox1)) .box('b032', Util.spyFunc(childBox2)).parse(boxWithChildData); expect(parentBox).toHaveBeenCalled(); expect(childBox1).toHaveBeenCalledWith(jasmine.objectContaining({ start: 8, size: 12, version: null, flags: null, })); expect(childBox2).toHaveBeenCalledWith(jasmine.objectContaining({ start: 20, size: 12, version: null, flags: null, })); }); it('reads children definition with full boxes', () => { const parentBox = jasmine.createSpy('parent box').and.callFake( shaka.util.Mp4Parser.children); const childBox1 = jasmine.createSpy('child box 1'); const childBox2 = jasmine.createSpy('child box 2'); new shaka.util.Mp4Parser() .fullBox('b003', Util.spyFunc(parentBox)) .box('b031', Util.spyFunc(childBox1)) .box('b032', Util.spyFunc(childBox2)).parse(fullBoxWithChildData); expect(parentBox).toHaveBeenCalled(); expect(childBox1).toHaveBeenCalledWith(jasmine.objectContaining({ start: 12, size: 12, version: null, flags: null, })); expect(childBox2).toHaveBeenCalledWith(jasmine.objectContaining({ start: 24, size: 12, version: null, flags: null, })); }); it('stops reading children when asked to', () => { const parentBox = jasmine.createSpy('parent box').and.callFake( shaka.util.Mp4Parser.children); const childBox1 = jasmine.createSpy('child box 1').and.callFake( (box) => { box.parser.stop(); }); const childBox2 = jasmine.createSpy('child box 2'); new shaka.util.Mp4Parser() .box('b003', Util.spyFunc(parentBox)) .box('b031', Util.spyFunc(childBox1)) .box('b032', Util.spyFunc(childBox2)).parse(boxWithChildData); expect(parentBox).toHaveBeenCalled(); expect(childBox1).toHaveBeenCalled(); expect(childBox2).not.toHaveBeenCalled(); }); it('reads all data definition', () => { let payload = []; new shaka.util.Mp4Parser() .box('b001', shaka.util.Mp4Parser.allData( (data) => { payload = data; })).parse(boxData); expect(payload.length).toBe(4); expect(payload[0]).toBe(0x00); expect(payload[1]).toBe(0x11); expect(payload[2]).toBe(0x22); expect(payload[3]).toBe(0x33); }); it('reads sample description definition', () => { const parentBox = jasmine.createSpy('parent box').and.callFake( shaka.util.Mp4Parser.sampleDescription); const childBox1 = jasmine.createSpy('child box 1'); const childBox2 = jasmine.createSpy('child box 2'); new shaka.util.Mp4Parser() .box('b003', Util.spyFunc(parentBox)) .box('b032', Util.spyFunc(childBox1)) .box('b033', Util.spyFunc(childBox2)).parse(boxWithSampleDescription); expect(parentBox).toHaveBeenCalledTimes(1); expect(childBox1).toHaveBeenCalledTimes(1); expect(childBox1).toHaveBeenCalledWith(jasmine.objectContaining({ start: 12, size: 12, version: null, flags: null, })); expect(childBox2).toHaveBeenCalledTimes(1); expect(childBox2).toHaveBeenCalledWith(jasmine.objectContaining({ start: 24, size: 12, version: null, flags: null, })); }); it('stops reading sample description when asked to', () => { const parentBox = jasmine.createSpy('parent box').and.callFake( shaka.util.Mp4Parser.sampleDescription); const childBox1 = jasmine.createSpy('child box 1').and.callFake( (box) => { box.parser.stop(); }); const childBox2 = jasmine.createSpy('child box 2'); new shaka.util.Mp4Parser() .box('b003', Util.spyFunc(parentBox)) .box('b032', Util.spyFunc(childBox1)) .box('b033', Util.spyFunc(childBox2)).parse(boxWithSampleDescription); expect(parentBox).toHaveBeenCalledTimes(1); expect(childBox1).toHaveBeenCalledTimes(1); expect(childBox2).not.toHaveBeenCalled(); }); }); describe('parsing', () => { it('finds all top level boxes', () => { const box1 = jasmine.createSpy('box 1'); const box2 = jasmine.createSpy('box 2'); const box3 = jasmine.createSpy('box 3'); new shaka.util.Mp4Parser() .box('b001', Util.spyFunc(box1)) .box('b002', Util.spyFunc(box2)) .box('b003', Util.spyFunc(box3)).parse(multipleSingleLevelBoxes); expect(box1).toHaveBeenCalled(); expect(box2).toHaveBeenCalled(); expect(box3).toHaveBeenCalled(); }); it('skips undefined top level boxes', () => { // By leaving a single box undefined, it should not interfere // with the other boxes (on the same level) from being read. const box1 = jasmine.createSpy('box 1'); const box3 = jasmine.createSpy('box 3'); new shaka.util.Mp4Parser() .box('b001', Util.spyFunc(box1)) .box('b003', Util.spyFunc(box3)).parse(multipleSingleLevelBoxes); expect(box1).toHaveBeenCalled(); expect(box3).toHaveBeenCalled(); }); it('does not parse child boxes with undefined parent box', () => { const box1 = jasmine.createSpy('box 1'); const box2Child = jasmine.createSpy('box 2 child'); const box3 = jasmine.createSpy('box 3'); // Listing a definition for box 2's child but not for box 2 should mean // box 2's child is never parsed. new shaka.util.Mp4Parser() .box('b010', Util.spyFunc(box1)) .box('b021', Util.spyFunc(box2Child)) .box('b030', Util.spyFunc(box3)).parse(twoLevelBoxStructure); expect(box1).toHaveBeenCalled(); expect(box2Child).not.toHaveBeenCalled(); expect(box3).toHaveBeenCalled(); }); it('can parse partial parent box and find first child', () => { const parentBox = jasmine.createSpy('parent box').and.callFake( shaka.util.Mp4Parser.sampleDescription); const childBox1 = jasmine.createSpy('child box 1').and.callFake( (box) => { // We found what we were looking for, so stop parsing. box.parser.stop(); }); const expected = Util.jasmineError(new shaka.util.Error( shaka.util.Error.Severity.CRITICAL, shaka.util.Error.Category.MEDIA, shaka.util.Error.Code.BUFFER_READ_OUT_OF_BOUNDS)); expect(() => { new shaka.util.Mp4Parser() .box('b003', Util.spyFunc(parentBox)) .box('b032', Util.spyFunc(childBox1)) .parse(partialBoxWithSampleDescription, /* partialOkay= */ false); }).toThrow(expected); parentBox.calls.reset(); childBox1.calls.reset(); // With the partialOkay flag set to true, this should succeed. new shaka.util.Mp4Parser() .box('b003', Util.spyFunc(parentBox)) .box('b032', Util.spyFunc(childBox1)) .parse(partialBoxWithSampleDescription, /* partialOkay= */ true); expect(parentBox).toHaveBeenCalledTimes(1); expect(childBox1).toHaveBeenCalledTimes(1); }); }); });