mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-15 16:06:41 +03:00
235e4e11ad
As part of Period-flattening, I'm trying to reduce our dependence on the "position" field of SegmentReference. If it can be eliminated, we can more easily concatenate Arrays of SegmentReferences without modifying them. SegmentIndex can now track the last reference you asked for and iterate through the list of references. This means we don't need the "position" field of SegmentReference, which means we don't need to know positions in advance or globally. StreamingEngine will no longer use position to request segments. The old methods find(time):position and get(position):SegmentReference have been replaced with seek(time), current(), and next(), all of which return a SegmentReference and maintain an internal pointer to the "current" reference. Care has been taken to maintain that pointer during the evict() and fit() operations. Recent changes to merge() made sure that the pointer does not need to change during that operation. All test updates are related to the SegmentIndex API change, not changing expectations or behavior. Issue #892 (refactor StreamingEngine) Issue #1339 (period flattening) Change-Id: I1682dcc2dd625c6e390711538e46d31e6eb6cea8
361 lines
14 KiB
JavaScript
361 lines
14 KiB
JavaScript
/** @license
|
|
* Copyright 2016 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
describe('DashParser SegmentBase', () => {
|
|
const Dash = shaka.test.Dash;
|
|
|
|
const indexSegmentUri = '/base/test/test/assets/index-segment.mp4';
|
|
|
|
/** @type {!shaka.test.FakeNetworkingEngine} */
|
|
let fakeNetEngine;
|
|
/** @type {!shaka.dash.DashParser} */
|
|
let parser;
|
|
/** @type {shaka.extern.ManifestParser.PlayerInterface} */
|
|
let playerInterface;
|
|
/** @type {!ArrayBuffer} */
|
|
let indexSegment;
|
|
|
|
beforeAll(async () => {
|
|
indexSegment = await shaka.test.Util.fetch(indexSegmentUri);
|
|
});
|
|
|
|
beforeEach(() => {
|
|
fakeNetEngine = new shaka.test.FakeNetworkingEngine();
|
|
parser = shaka.test.Dash.makeDashParser();
|
|
|
|
playerInterface = {
|
|
networkingEngine: fakeNetEngine,
|
|
filterNewPeriod: () => {},
|
|
filterAllPeriods: () => {},
|
|
onTimelineRegionAdded: fail, // Should not have any EventStream elements.
|
|
onEvent: fail,
|
|
onError: fail,
|
|
};
|
|
});
|
|
|
|
it('requests init data for WebM', async () => {
|
|
const source = [
|
|
'<MPD mediaPresentationDuration="PT75S">',
|
|
' <BaseURL>http://example.com</BaseURL>',
|
|
' <Period>',
|
|
' <AdaptationSet mimeType="video/webm">',
|
|
' <Representation id="1" bandwidth="1">',
|
|
' <BaseURL>media-1.webm</BaseURL>',
|
|
' <SegmentBase indexRange="100-200" timescale="9000">',
|
|
' <Initialization sourceURL="init-1.webm" range="201-300" />',
|
|
' </SegmentBase>',
|
|
' </Representation>',
|
|
' <Representation id="2" bandwidth="1">',
|
|
' <BaseURL>media-2.webm</BaseURL>',
|
|
' <SegmentBase indexRange="1100-1200" timescale="9000">',
|
|
' <Initialization sourceURL="init-2.webm" range="1201-1300" />',
|
|
' </SegmentBase>',
|
|
' </Representation>',
|
|
' </AdaptationSet>',
|
|
' </Period>',
|
|
'</MPD>',
|
|
].join('\n');
|
|
|
|
fakeNetEngine
|
|
.setResponseText('dummy://foo', source)
|
|
.setResponseText('http://example.com/media-1.webm', '')
|
|
.setResponseText('http://example.com/media-2.webm', '')
|
|
.setResponseText('http://example.com/init-1.webm', '')
|
|
.setResponseText('http://example.com/init-2.webm', '');
|
|
|
|
/** @type {shaka.extern.Manifest} */
|
|
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
|
|
// Call createSegmentIndex() on each stream to make the requests, but expect
|
|
// failure from the actual parsing, since the data is bogus.
|
|
const stream1 = manifest.periods[0].variants[0].video;
|
|
await expectAsync(stream1.createSegmentIndex()).toBeRejected();
|
|
const stream2 = manifest.periods[0].variants[1].video;
|
|
await expectAsync(stream2.createSegmentIndex()).toBeRejected();
|
|
|
|
expect(fakeNetEngine.request).toHaveBeenCalledTimes(5);
|
|
|
|
// Expect calls to fetch part of the media and init segments of each stream.
|
|
fakeNetEngine.expectRangeRequest(
|
|
'http://example.com/media-1.webm', 100, 200);
|
|
fakeNetEngine.expectRangeRequest(
|
|
'http://example.com/init-1.webm', 201, 300);
|
|
fakeNetEngine.expectRangeRequest(
|
|
'http://example.com/media-2.webm', 1100, 1200);
|
|
fakeNetEngine.expectRangeRequest(
|
|
'http://example.com/init-2.webm', 1201, 1300);
|
|
});
|
|
|
|
it('inherits from Period', async () => {
|
|
const source = [
|
|
'<MPD mediaPresentationDuration="PT75S">',
|
|
' <Period>',
|
|
' <BaseURL>http://example.com</BaseURL>',
|
|
' <SegmentBase indexRange="100-200" timescale="9000">',
|
|
' <Initialization sourceURL="init.mp4" range="201-300" />',
|
|
' </SegmentBase>',
|
|
' <AdaptationSet mimeType="video/mp4">',
|
|
' <Representation bandwidth="1" />',
|
|
' </AdaptationSet>',
|
|
' </Period>',
|
|
'</MPD>',
|
|
].join('\n');
|
|
|
|
fakeNetEngine
|
|
.setResponseText('dummy://foo', source)
|
|
.setResponseValue('http://example.com', indexSegment);
|
|
|
|
/** @type {shaka.extern.Manifest} */
|
|
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
const segmentReference = await Dash.getFirstVideoSegmentReference(manifest);
|
|
const initSegmentReference = segmentReference.initSegmentReference;
|
|
expect(initSegmentReference.getUris()).toEqual(
|
|
['http://example.com/init.mp4']);
|
|
expect(initSegmentReference.getStartByte()).toBe(201);
|
|
expect(initSegmentReference.getEndByte()).toBe(300);
|
|
|
|
expect(fakeNetEngine.request).toHaveBeenCalledTimes(2);
|
|
fakeNetEngine.expectRangeRequest('http://example.com', 100, 200);
|
|
});
|
|
|
|
it('inherits from AdaptationSet', async () => {
|
|
const source = [
|
|
'<MPD mediaPresentationDuration="PT75S">',
|
|
' <Period>',
|
|
' <AdaptationSet mimeType="video/mp4">',
|
|
' <BaseURL>http://example.com</BaseURL>',
|
|
' <SegmentBase indexRange="100-200" timescale="9000">',
|
|
' <Initialization sourceURL="init.mp4" range="201-300" />',
|
|
' </SegmentBase>',
|
|
' <Representation bandwidth="1" />',
|
|
' </AdaptationSet>',
|
|
' </Period>',
|
|
'</MPD>',
|
|
].join('\n');
|
|
|
|
fakeNetEngine
|
|
.setResponseText('dummy://foo', source)
|
|
.setResponseValue('http://example.com', indexSegment);
|
|
|
|
/** @type {shaka.extern.Manifest} */
|
|
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
const segmentReference = await Dash.getFirstVideoSegmentReference(manifest);
|
|
const initSegmentReference = segmentReference.initSegmentReference;
|
|
expect(initSegmentReference.getUris()).toEqual(
|
|
['http://example.com/init.mp4']);
|
|
expect(initSegmentReference.getStartByte()).toBe(201);
|
|
expect(initSegmentReference.getEndByte()).toBe(300);
|
|
|
|
expect(fakeNetEngine.request).toHaveBeenCalledTimes(2);
|
|
fakeNetEngine.expectRangeRequest('http://example.com', 100, 200);
|
|
});
|
|
|
|
it('does not require sourceURL in Initialization', async () => {
|
|
const source = [
|
|
'<MPD mediaPresentationDuration="PT75S">',
|
|
' <Period>',
|
|
' <AdaptationSet mimeType="video/mp4">',
|
|
' <Representation bandwidth="1">',
|
|
' <BaseURL>http://example.com/stream.mp4</BaseURL>',
|
|
' <SegmentBase indexRange="100-200" timescale="9000">',
|
|
' <Initialization range="201-300" />',
|
|
' </SegmentBase>',
|
|
' </Representation>',
|
|
' </AdaptationSet>',
|
|
' </Period>',
|
|
'</MPD>',
|
|
].join('\n');
|
|
|
|
fakeNetEngine
|
|
.setResponseText('dummy://foo', source)
|
|
.setResponseValue('http://example.com/stream.mp4', indexSegment);
|
|
|
|
/** @type {shaka.extern.Manifest} */
|
|
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
const segmentReference = await Dash.getFirstVideoSegmentReference(manifest);
|
|
const initSegmentReference = segmentReference.initSegmentReference;
|
|
expect(initSegmentReference.getUris()).toEqual(
|
|
['http://example.com/stream.mp4']);
|
|
expect(initSegmentReference.getStartByte()).toBe(201);
|
|
expect(initSegmentReference.getEndByte()).toBe(300);
|
|
|
|
expect(fakeNetEngine.request).toHaveBeenCalledTimes(2);
|
|
fakeNetEngine.expectRangeRequest('http://example.com/stream.mp4', 100, 200);
|
|
});
|
|
|
|
it('merges across levels', async () => {
|
|
const source = [
|
|
'<MPD mediaPresentationDuration="PT75S">',
|
|
' <Period>',
|
|
' <BaseURL>http://example.com</BaseURL>',
|
|
' <SegmentBase timescale="9000">',
|
|
' <Initialization sourceURL="init.mp4" range="201-300" />',
|
|
' </SegmentBase>',
|
|
' <AdaptationSet mimeType="video/mp4">',
|
|
' <SegmentBase presentationTimeOffset="90000">',
|
|
' <Initialization sourceURL="init.mp4" range="201-300" />',
|
|
' </SegmentBase>',
|
|
' <Representation bandwidth="1">',
|
|
' <SegmentBase>',
|
|
' <RepresentationIndex sourceURL="index.mp4" range="5-2000" />',
|
|
' </SegmentBase>',
|
|
' </Representation>',
|
|
' </AdaptationSet>',
|
|
' </Period>',
|
|
'</MPD>',
|
|
].join('\n');
|
|
|
|
fakeNetEngine
|
|
.setResponseText('dummy://foo', source)
|
|
.setResponseValue('http://example.com/index.mp4', indexSegment);
|
|
|
|
/** @type {shaka.extern.Manifest} */
|
|
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
const segmentReference = await Dash.getFirstVideoSegmentReference(manifest);
|
|
const initSegmentReference = segmentReference.initSegmentReference;
|
|
expect(initSegmentReference.getUris()).toEqual(
|
|
['http://example.com/init.mp4']);
|
|
expect(initSegmentReference.getStartByte()).toBe(201);
|
|
expect(initSegmentReference.getEndByte()).toBe(300);
|
|
expect(segmentReference.timestampOffset).toBe(-10);
|
|
|
|
expect(fakeNetEngine.request).toHaveBeenCalledTimes(2);
|
|
fakeNetEngine.expectRangeRequest('http://example.com/index.mp4', 5, 2000);
|
|
});
|
|
|
|
it('merges and overrides across levels', async () => {
|
|
const source = [
|
|
'<MPD mediaPresentationDuration="PT75S">',
|
|
' <Period>',
|
|
' <BaseURL>http://example.com</BaseURL>',
|
|
' <SegmentBase indexRange="0-10" timescale="9000">',
|
|
' <Initialization sourceURL="init.mp4" range="201-300" />',
|
|
' </SegmentBase>',
|
|
' <AdaptationSet mimeType="video/mp4">',
|
|
' <SegmentBase timescale="10" presentationTimeOffset="10">',
|
|
' <Initialization sourceURL="special.mp4" />',
|
|
' </SegmentBase>',
|
|
' <Representation bandwidth="1">',
|
|
' <SegmentBase indexRange="30-900" ',
|
|
' presentationTimeOffset="200" />',
|
|
' </Representation>',
|
|
' </AdaptationSet>',
|
|
' </Period>',
|
|
'</MPD>',
|
|
].join('\n');
|
|
|
|
fakeNetEngine
|
|
.setResponseText('dummy://foo', source)
|
|
.setResponseValue('http://example.com', indexSegment);
|
|
|
|
/** @type {shaka.extern.Manifest} */
|
|
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
const segmentReference = await Dash.getFirstVideoSegmentReference(manifest);
|
|
const initSegmentReference = segmentReference.initSegmentReference;
|
|
expect(initSegmentReference.getUris()).toEqual(
|
|
['http://example.com/special.mp4']);
|
|
expect(initSegmentReference.getStartByte()).toBe(0);
|
|
expect(initSegmentReference.getEndByte()).toBe(null);
|
|
expect(segmentReference.timestampOffset).toBe(-20);
|
|
|
|
expect(fakeNetEngine.request).toHaveBeenCalledTimes(2);
|
|
fakeNetEngine.expectRangeRequest('http://example.com', 30, 900);
|
|
});
|
|
|
|
it('does not assume the same timescale as media', async () => {
|
|
const source = [
|
|
'<MPD mediaPresentationDuration="PT75S">',
|
|
' <Period>',
|
|
' <AdaptationSet mimeType="video/mp4">',
|
|
' <Representation bandwidth="1">',
|
|
' <BaseURL>http://example.com/index.mp4</BaseURL>',
|
|
' <SegmentBase indexRange="30-900" ',
|
|
' timescale="1000"',
|
|
' presentationTimeOffset="2000" />',
|
|
' </Representation>',
|
|
' </AdaptationSet>',
|
|
' </Period>',
|
|
'</MPD>',
|
|
].join('\n');
|
|
|
|
fakeNetEngine
|
|
.setResponseText('dummy://foo', source)
|
|
.setResponseValue('http://example.com/index.mp4', indexSegment);
|
|
|
|
/** @type {shaka.extern.Manifest} */
|
|
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
const video = manifest.periods[0].variants[0].video;
|
|
await video.createSegmentIndex(); // real data, should succeed
|
|
|
|
const reference = video.segmentIndex.seek(0);
|
|
expect(reference.startTime).toBe(-2);
|
|
expect(reference.endTime).toBe(10); // would be 12 without PTO
|
|
});
|
|
|
|
describe('fails for', () => {
|
|
it('unsupported container', async () => {
|
|
const source = [
|
|
'<MPD mediaPresentationDuration="PT75S">',
|
|
' <Period>',
|
|
' <BaseURL>http://example.com</BaseURL>',
|
|
' <AdaptationSet mimeType="video/cat">',
|
|
' <Representation bandwidth="1">',
|
|
' <SegmentBase indexRange="30-900" />',
|
|
' </Representation>',
|
|
' </AdaptationSet>',
|
|
' </Period>',
|
|
'</MPD>',
|
|
].join('\n');
|
|
const error = new shaka.util.Error(
|
|
shaka.util.Error.Severity.CRITICAL,
|
|
shaka.util.Error.Category.MANIFEST,
|
|
shaka.util.Error.Code.DASH_UNSUPPORTED_CONTAINER);
|
|
await Dash.testFails(source, error);
|
|
});
|
|
|
|
it('missing init segment for WebM', async () => {
|
|
const source = [
|
|
'<MPD mediaPresentationDuration="PT75S">',
|
|
' <Period>',
|
|
' <BaseURL>http://example.com</BaseURL>',
|
|
' <AdaptationSet mimeType="video/webm">',
|
|
' <Representation bandwidth="1">',
|
|
' <SegmentBase indexRange="30-900" />',
|
|
' </Representation>',
|
|
' </AdaptationSet>',
|
|
' </Period>',
|
|
'</MPD>',
|
|
].join('\n');
|
|
const error = new shaka.util.Error(
|
|
shaka.util.Error.Severity.CRITICAL,
|
|
shaka.util.Error.Category.MANIFEST,
|
|
shaka.util.Error.Code.DASH_WEBM_MISSING_INIT);
|
|
await Dash.testFails(source, error);
|
|
});
|
|
|
|
it('no @indexRange nor RepresentationIndex', async () => {
|
|
const source = [
|
|
'<MPD mediaPresentationDuration="PT75S">',
|
|
' <Period>',
|
|
' <BaseURL>http://example.com</BaseURL>',
|
|
' <AdaptationSet mimeType="video/webm">',
|
|
' <Representation bandwidth="1">',
|
|
' <SegmentBase>',
|
|
' <Initialization sourceURL="test.webm" />',
|
|
' </SegmentBase>',
|
|
' </Representation>',
|
|
' </AdaptationSet>',
|
|
' </Period>',
|
|
'</MPD>',
|
|
].join('\n');
|
|
const error = new shaka.util.Error(
|
|
shaka.util.Error.Severity.CRITICAL,
|
|
shaka.util.Error.Category.MANIFEST,
|
|
shaka.util.Error.Code.DASH_NO_SEGMENT_INFO);
|
|
await Dash.testFails(source, error);
|
|
});
|
|
});
|
|
});
|