mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-14 15:56:38 +03:00
83de1ad634
This ensures that we get the expected types and that type coercion doesn't convert between types. This also ensures we are consistent in how we check for equality of primitives in tests. Change-Id: I9f3aacdf25ab1afe5e8d6e4b895b5299ee687d54
331 lines
12 KiB
JavaScript
331 lines
12 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2016 Google Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
describe('Mp4Parser', () => {
|
|
const Util = shaka.test.Util;
|
|
|
|
let boxData;
|
|
let fullBoxData;
|
|
let boxWithChildData;
|
|
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
|
|
]).buffer;
|
|
|
|
fullBoxData = new Uint8Array([
|
|
0x00, 0x00, 0x00, 0x10, // size
|
|
0x62, 0x30, 0x30, 0x31, // type
|
|
0x01, // version
|
|
0x12, 0x34, 0x56, // flags
|
|
0x00, 0x11, 0x22, 0x33, // payload
|
|
]).buffer;
|
|
|
|
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
|
|
]).buffer;
|
|
|
|
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
|
|
]).buffer;
|
|
|
|
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]
|
|
]).buffer;
|
|
|
|
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
|
|
]).buffer;
|
|
|
|
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
|
|
]).buffer;
|
|
});
|
|
|
|
describe('headerDefinitions', () => {
|
|
it('reads box header', () => {
|
|
const callback = jasmine.createSpy('parser callback').and.callFake(
|
|
(box) => {
|
|
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.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').and.callFake(
|
|
(box) => {
|
|
expect(box.size).toBe(12);
|
|
expect(box.version).toBe(null);
|
|
expect(box.flags).toBe(null);
|
|
});
|
|
|
|
const childBox2 = jasmine.createSpy('child box 2').and.callFake(
|
|
(box) => {
|
|
expect(box.size).toBe(12);
|
|
expect(box.version).toBe(null);
|
|
expect(box.flags).toBe(null);
|
|
});
|
|
|
|
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).toHaveBeenCalled();
|
|
});
|
|
|
|
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(childBox2).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
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, false /* partialOkay */);
|
|
}).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, true /* partialOkay */);
|
|
|
|
expect(parentBox).toHaveBeenCalledTimes(1);
|
|
expect(childBox1).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
});
|