Files
shaka-player/test/util/dash_parser_util.js
T
Jacob Trimble 926083e032 Namespaced test utility functions.
Now all the utility functions that are in test/util belong to
namespaces (shaka.test.Util and shaka.test.Dash).

Change-Id: I0bcb359616b02d58c63b1054425003e5a5646aaf
2016-01-21 12:01:54 -08:00

404 lines
12 KiB
JavaScript

/**
* @license
* Copyright 2015 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.
*/
goog.provide('shaka.test.Dash');
/**
* A helper test type used to fake networking requests and check the correct
* requests are made.
*
* @param {string} text The data to be returned on each request.
*
* @constructor
* @struct
* @extends {shaka.net.NetworkingEngine}
* @suppress {missingProvide}
*/
function dashFakeNetEngine(text) {
var data = shaka.util.Uint8ArrayUtils.fromString(text).buffer;
var request = jasmine.createSpy('request').and.callFake(function() {
return Promise.resolve({headers: {}, data: data});
});
return {
// Since we are returning an object, the prototype will not be applied.
expectRangeRequest: dashFakeNetEngine.prototype.expectRangeRequest,
request: request
};
}
/**
* Expects that a request for the given URI has occurred. This is a prototype
* method to work with the compiler. Manually added to the object in the
* constructor.
*
* @param {string} uri
* @param {number} startByte
* @param {?number} endByte
*/
dashFakeNetEngine.prototype.expectRangeRequest = function(
uri, startByte, endByte) {
var range = 'bytes=' + startByte + '-';
if (endByte != null) range += endByte;
expect(this.request)
.toHaveBeenCalledWith(
shaka.net.NetworkingEngine.RequestType.SEGMENT,
jasmine.objectContaining({
uris: [uri],
headers: jasmine.objectContaining({'Range': range})
}));
};
/**
* Verifies the segment references in a manifest.
*
* @param {shaka.media.Manifest} manifest
* @param {!Array.<shaka.media.SegmentReference>} references
*/
shaka.test.Dash.verifySegmentIndex = function(manifest, references) {
expect(manifest).toBeTruthy();
var stream = manifest.periods[0].streamSets[0].streams[0];
expect(stream).toBeTruthy();
expect(stream.findSegmentPosition).toBeTruthy();
expect(stream.getSegmentReference).toBeTruthy();
var positionBeforeFirst =
stream.findSegmentPosition(references[0].startTime - 1);
expect(positionBeforeFirst).toBe(null);
for (var i = 0; i < references.length - 1; i++) {
var expectedRef = references[i];
var position = stream.findSegmentPosition(expectedRef.startTime);
expect(position).not.toBe(null);
var actualRef = stream.getSegmentReference(position);
expect(actualRef).toEqual(expectedRef);
}
// Make sure that the references stop at the end.
var positionAfterEnd =
stream.findSegmentPosition(references[references.length - 1].endTime);
expect(positionAfterEnd).toBe(null);
};
/**
* Tests the segment index produced by the DASH manifest parser.
*
* @param {function()} done
* @param {string} manifestText
* @param {!Array.<shaka.media.SegmentReference>} references
*/
shaka.test.Dash.testSegmentIndex =
function(done, manifestText, references) {
var dummyUri = 'dummy://foo';
var fakeNetEngine = new dashFakeNetEngine(manifestText);
var dashParser = new shaka.dash.DashParser(fakeNetEngine, {}, function() {});
dashParser.start(dummyUri)
.then(function(manifest) {
shaka.test.Dash.verifySegmentIndex(manifest, references);
})
.catch(fail)
.then(done);
};
/**
* Tests that the DASH manifest parser fails to parse the given manifest.
*
* @param {function()} done
* @param {string} manifestText
* @param {!shaka.util.Error} expectedError
*/
shaka.test.Dash.testFails = function(done, manifestText, expectedError) {
var fakeNetEngine = new dashFakeNetEngine(manifestText);
var dashParser = new shaka.dash.DashParser(fakeNetEngine, {}, function() {});
shaka.log.setLevel(shaka.log.Level.NONE);
dashParser.start('')
.then(fail)
.catch(function(error) { expect(error).toEqual(expectedError); })
.then(function() { shaka.log.setLevel(shaka.log.MAX_LEVEL); })
.then(done);
};
/**
* Makes a simple manifest with the given representation contents.
*
* @param {!Array.<string>} lines
* @param {number=} opt_duration
* @param {number=} opt_start
* @return {string}
*/
shaka.test.Dash.makeSimpleManifestText =
function(lines, opt_duration, opt_start) {
var attr = '';
if (opt_duration)
attr = 'duration="PT' + opt_duration + 'S"';
if (opt_start)
attr += ' start="PT' + opt_start + 'S"';
var start = [
'<MPD>',
' <Period ' + attr + '>',
' <AdaptationSet mimeType="video/mp4">',
' <Representation bandwidth="500">',
' <BaseURL>http://example.com</BaseURL>'
];
var end = [
' </Representation>',
' </AdaptationSet>',
' </Period>',
'</MPD>'
];
return start.concat(lines, end).join('\n');
};
/**
* Makes a simple manifest object for jasmine.toEqual; this does not do any
* checking. This only constructs one period with the given stream sets.
*
* @param {!Array.<shaka.media.StreamSet>} streamSets
* @return {shaka.media.Manifest}
*/
shaka.test.Dash.makeManifestFromStreamSets = function(streamSets) {
return jasmine.objectContaining({
periods: [
jasmine.objectContaining({
streamSets: streamSets
})
]
});
};
/**
* Makes a simple manifest object for jasmine.toEqual; this does not do any
* checking. This only constructs one period with one stream with the given
* initialization segment data.
*
* @param {string} uri The URI of the initialization segment.
* @param {number} startByte
* @param {?number} endByte
* @param {number=} opt_pto The presentationTimeOffset of the stream.
* @return {shaka.media.Manifest}
*/
shaka.test.Dash.makeManifestFromInit = function(
uri, startByte, endByte, opt_pto) {
return shaka.test.Dash.makeManifestFromStreamSets([jasmine.objectContaining({
streams: [jasmine.objectContaining({
presentationTimeOffset: (opt_pto || 0),
createSegmentIndex: jasmine.any(Function),
findSegmentPosition: jasmine.any(Function),
initSegmentReference: new shaka.media.InitSegmentReference(
['http://example.com/' + uri], startByte, endByte)
})]
})]);
};
/**
* Calls the createSegmentIndex function of the manifest. Because we are
* returning fake data, the parser will fail to parse the segment index; we
* swallow the error and return a promise that will resolve.
*
* @param {shaka.media.Manifest} manifest
* @return {!Promise}
*/
shaka.test.Dash.callCreateSegmentIndex = function(manifest) {
var stream = manifest.periods[0].streamSets[0].streams[0];
shaka.log.setLevel(shaka.log.Level.NONE);
return stream.createSegmentIndex().then(fail).catch(function() {
shaka.log.setLevel(shaka.log.MAX_LOG_LEVEL);
});
};
/**
* Creates a segment reference using a relative URI.
*
* @param {string} uri A relative URI to http://example.com
* @param {number} position
* @param {number} start
* @param {number} end
* @param {number=} opt_startByte
* @param {?number=} opt_endByte
* @return {!shaka.media.SegmentReference}
*/
shaka.test.Dash.makeReference =
function(uri, position, start, end, opt_startByte, opt_endByte) {
var base = 'http://example.com/';
var startByte = opt_startByte || 0;
var endByte = opt_endByte || null;
return new shaka.media.SegmentReference(
position, start, end, [base + uri], startByte, endByte);
};
/**
* Makes a set of tests for SegmentTimeline. This is used to test
* SegmentTimeline within both SegmentList and SegmentTemplate.
*
* @param {string} type The type of manifest being tested; either
* 'SegmentTemplate' or 'SegmentList'.
* @param {string} extraAttrs
* @param {!Array.<string>} extraChildren
*/
shaka.test.Dash.makeTimelineTests = function(type, extraAttrs, extraChildren) {
describe('SegmentTimeline', function() {
var Dash = shaka.test.Dash;
/**
* @param {!Array.<string>} timeline
* @param {string} testAttrs
* @param {number=} opt_dur
* @param {number=} opt_start
* return {string}
*/
function makeManifestText(timeline, testAttrs, opt_dur, opt_start) {
var start = '<' + type + ' ' + extraAttrs + ' ' + testAttrs + '>';
var end = '</' + type + '>';
var lines = [].concat(start, extraChildren, timeline, end);
return Dash.makeSimpleManifestText(lines, opt_dur, opt_start);
};
// All tests should have 5 segments and have the relative URIs:
// s1.mp4 s2.mp4 s3.mp4 s4.mp4 s5.mp4
it('basic support', function(done) {
var timeline = [
'<SegmentTimeline>',
' <S d="12" t="34" />',
' <S d="21" />',
' <S d="44" />',
' <S d="10" />',
' <S d="10" />',
'</SegmentTimeline>'
];
var source = makeManifestText(timeline, '');
var references = [
Dash.makeReference('s1.mp4', 1, 34, 46),
Dash.makeReference('s2.mp4', 2, 46, 67),
Dash.makeReference('s3.mp4', 3, 67, 111),
Dash.makeReference('s4.mp4', 4, 111, 121),
Dash.makeReference('s5.mp4', 5, 121, 131)
];
Dash.testSegmentIndex(done, source, references);
});
it('supports repetitions', function(done) {
var timeline = [
'<SegmentTimeline>',
' <S d="12" t="34" />',
' <S d="10" r="2" />',
' <S d="44" />',
'</SegmentTimeline>'
];
var source = makeManifestText(timeline, '');
var references = [
Dash.makeReference('s1.mp4', 1, 34, 46),
Dash.makeReference('s2.mp4', 2, 46, 56),
Dash.makeReference('s3.mp4', 3, 56, 66),
Dash.makeReference('s4.mp4', 4, 66, 76),
Dash.makeReference('s5.mp4', 5, 76, 120)
];
Dash.testSegmentIndex(done, source, references);
});
it('supports negative repetitions', function(done) {
var timeline = [
'<SegmentTimeline>',
' <S d="8" t="22" />',
' <S d="10" r="-1" />',
' <S d="12" t="50" />',
' <S d="10" />',
'</SegmentTimeline>'
];
var source = makeManifestText(timeline, '');
var references = [
Dash.makeReference('s1.mp4', 1, 22, 30),
Dash.makeReference('s2.mp4', 2, 30, 40),
Dash.makeReference('s3.mp4', 3, 40, 50),
Dash.makeReference('s4.mp4', 4, 50, 62),
Dash.makeReference('s5.mp4', 5, 62, 72)
];
Dash.testSegmentIndex(done, source, references);
});
it('supports negative repetitions at end', function(done) {
var timeline = [
'<SegmentTimeline>',
' <S d="5" t="5" />',
' <S d="10" r="-1" />',
'</SegmentTimeline>'
];
var source = makeManifestText(timeline, '', 50 /* duration */);
var references = [
Dash.makeReference('s1.mp4', 1, 5, 10),
Dash.makeReference('s2.mp4', 2, 10, 20),
Dash.makeReference('s3.mp4', 3, 20, 30),
Dash.makeReference('s4.mp4', 4, 30, 40),
Dash.makeReference('s5.mp4', 5, 40, 50)
];
Dash.testSegmentIndex(done, source, references);
});
it('gives times relative to period', function(done) {
var timeline = [
'<SegmentTimeline>',
' <S t="0" d="10" r="-1" />',
'</SegmentTimeline>'
];
var source =
makeManifestText(timeline, '', 50 /* duration */, 30 /* start */);
var references = [
Dash.makeReference('s1.mp4', 1, 0, 10),
Dash.makeReference('s2.mp4', 2, 10, 20),
Dash.makeReference('s3.mp4', 3, 20, 30),
Dash.makeReference('s4.mp4', 4, 30, 40),
Dash.makeReference('s5.mp4', 5, 40, 50)
];
Dash.testSegmentIndex(done, source, references);
});
it('supports @timescale', function(done) {
var timeline = [
'<SegmentTimeline>',
' <S d="4500" t="18000" />',
' <S d="9000" />',
' <S d="31500" />',
' <S d="9000" />',
' <S d="9000" />',
'</SegmentTimeline>'
];
var source = makeManifestText(timeline, 'timescale="9000"');
var references = [
Dash.makeReference('s1.mp4', 1, 2, 2.5),
Dash.makeReference('s2.mp4', 2, 2.5, 3.5),
Dash.makeReference('s3.mp4', 3, 3.5, 7),
Dash.makeReference('s4.mp4', 4, 7, 8),
Dash.makeReference('s5.mp4', 5, 8, 9)
];
Dash.testSegmentIndex(done, source, references);
});
});
};