mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-24 17:35:10 +03:00
c927300045
Instead of storing uris for SegmentDataDB in SegmentDB, store the actual key so that we don't need to convert back-and-forth so often. Issue #1047 Change-Id: I2f6d1bc1e58abd7a210097dea757d1e2fc9d4cac
480 lines
14 KiB
JavaScript
480 lines
14 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('OfflineUtils', function() {
|
|
/** @const */
|
|
var OfflineUtils = shaka.offline.OfflineUtils;
|
|
|
|
var drmInfos;
|
|
var timeline;
|
|
|
|
beforeEach(function() {
|
|
drmInfos = [{
|
|
keySystem: 'com.example.drm',
|
|
licenseServerUri: 'https://example.com/drm',
|
|
distinctiveIdentifierRequired: false,
|
|
persistentStateRequired: true,
|
|
audioRobustness: 'weak',
|
|
videoRobustness: 'awesome',
|
|
serverCertificate: null,
|
|
initData: [{initData: new Uint8Array([1]), initDataType: 'foo'}],
|
|
keyIds: ['key1', 'key2']
|
|
}];
|
|
timeline = new shaka.media.PresentationTimeline(null, 0);
|
|
});
|
|
|
|
describe('recreateVariants', function() {
|
|
var OfflineUtils = shaka.offline.OfflineUtils;
|
|
|
|
/** @const {string} */
|
|
var audioType = 'audio';
|
|
/** @const {string} */
|
|
var videoType = 'video';
|
|
|
|
it('will create variants with variant ids', function() {
|
|
/** @type {!Array.<shakaExtern.StreamDB>} */
|
|
var audios = [
|
|
createStreamDB(0, audioType, [0]),
|
|
createStreamDB(1, audioType, [1])
|
|
];
|
|
/** @type {!Array.<shakaExtern.StreamDB>} */
|
|
var videos = [
|
|
createStreamDB(2, videoType, [0]),
|
|
createStreamDB(3, videoType, [1])
|
|
];
|
|
/** @type {!Array.<shakaExtern.DrmInfo>} */
|
|
var drm = [];
|
|
|
|
/** @type {!Array.<shakaExtern.Variant>} */
|
|
var variants = OfflineUtils.recreateVariants(audios, videos, drm);
|
|
|
|
expect(variants.length).toBe(2);
|
|
|
|
expect(variants[0].audio.id).toBe(0);
|
|
expect(variants[0].video.id).toBe(2);
|
|
|
|
expect(variants[1].audio.id).toBe(1);
|
|
expect(variants[1].video.id).toBe(3);
|
|
});
|
|
|
|
it('will create variants when there is only audio', function() {
|
|
/** @type {!Array.<shakaExtern.StreamDB>} */
|
|
var audios = [
|
|
createStreamDB(0, audioType, [0]),
|
|
createStreamDB(1, audioType, [1])
|
|
];
|
|
/** @type {!Array.<shakaExtern.StreamDB>} */
|
|
var videos = [];
|
|
/** @type {!Array.<shakaExtern.DrmInfo>} */
|
|
var drm = [];
|
|
|
|
/** @type {!Array.<shakaExtern.Variant>} */
|
|
var variants = OfflineUtils.recreateVariants(audios, videos, drm);
|
|
|
|
expect(variants.length).toBe(2);
|
|
});
|
|
|
|
it('will create variants when there is only video', function() {
|
|
/** @type {!Array.<shakaExtern.StreamDB>} */
|
|
var audios = [];
|
|
/** @type {!Array.<shakaExtern.StreamDB>} */
|
|
var videos = [
|
|
createStreamDB(2, videoType, [0]),
|
|
createStreamDB(3, videoType, [1])
|
|
];
|
|
/** @type {!Array.<shakaExtern.DrmInfo>} */
|
|
var drm = [];
|
|
|
|
/** @type {!Array.<shakaExtern.Variant>} */
|
|
var variants = OfflineUtils.recreateVariants(audios, videos, drm);
|
|
|
|
expect(variants.length).toBe(2);
|
|
});
|
|
|
|
/**
|
|
* @param {number} id
|
|
* @param {string} type
|
|
* @param {!Array.<number>} variants
|
|
* @return {shakaExtern.StreamDB}
|
|
*/
|
|
function createStreamDB(id, type, variants) {
|
|
/** @type {shakaExtern.StreamDB} */
|
|
var streamDB = {
|
|
id: id,
|
|
primary: false,
|
|
presentationTimeOffset: 0,
|
|
contentType: type,
|
|
mimeType: '',
|
|
codecs: '',
|
|
language: '',
|
|
label: null,
|
|
width: null,
|
|
height: null,
|
|
initSegmentKey: null,
|
|
encrypted: false,
|
|
keyId: null,
|
|
segments: [],
|
|
variantIds: variants
|
|
};
|
|
|
|
return streamDB;
|
|
}
|
|
});
|
|
|
|
describe('reconstructPeriod', function() {
|
|
it('will reconstruct Periods correctly', function() {
|
|
/** @type {shakaExtern.PeriodDB} */
|
|
var periodDb = {
|
|
startTime: 60,
|
|
streams: [createVideoStreamDb(1, [0]), createAudioStreamDb(2, [0])]
|
|
};
|
|
var period = OfflineUtils.reconstructPeriod(periodDb, drmInfos, timeline);
|
|
expect(period).toBeTruthy();
|
|
expect(period.startTime).toBe(periodDb.startTime);
|
|
expect(period.textStreams).toEqual([]);
|
|
expect(period.variants.length).toBe(1);
|
|
|
|
var variant = period.variants[0];
|
|
expect(variant.id).toEqual(jasmine.any(Number));
|
|
expect(variant.language).toBe(periodDb.streams[1].language);
|
|
expect(variant.primary).toBe(false);
|
|
expect(variant.bandwidth).toEqual(jasmine.any(Number));
|
|
expect(variant.drmInfos).toBe(drmInfos);
|
|
expect(variant.allowedByApplication).toBe(true);
|
|
expect(variant.allowedByKeySystem).toBe(true);
|
|
|
|
verifyStream(variant.video, periodDb.streams[0]);
|
|
verifyStream(variant.audio, periodDb.streams[1]);
|
|
});
|
|
|
|
it('supports video-only content', function() {
|
|
/** @type {shakaExtern.PeriodDB} */
|
|
var periodDb = {
|
|
startTime: 60,
|
|
streams: [createVideoStreamDb(1, [0]), createVideoStreamDb(2, [1])]
|
|
};
|
|
|
|
var period = OfflineUtils.reconstructPeriod(periodDb, drmInfos, timeline);
|
|
expect(period).toBeTruthy();
|
|
expect(period.variants.length).toBe(2);
|
|
expect(period.variants[0].audio).toBe(null);
|
|
expect(period.variants[0].video).toBeTruthy();
|
|
});
|
|
|
|
it('supports audio-only content', function() {
|
|
/** @type {shakaExtern.PeriodDB} */
|
|
var periodDb = {
|
|
startTime: 60,
|
|
streams: [createAudioStreamDb(1, [0]), createAudioStreamDb(2, [1])]
|
|
};
|
|
|
|
var period = OfflineUtils.reconstructPeriod(periodDb, drmInfos, timeline);
|
|
expect(period).toBeTruthy();
|
|
expect(period.variants.length).toBe(2);
|
|
expect(period.variants[0].audio).toBeTruthy();
|
|
expect(period.variants[0].video).toBe(null);
|
|
});
|
|
|
|
it('supports text streams', function() {
|
|
/** @type {shakaExtern.PeriodDB} */
|
|
var periodDb = {
|
|
startTime: 60,
|
|
streams: [
|
|
createVideoStreamDb(1, [0]),
|
|
createTextStreamDb(2)
|
|
]
|
|
};
|
|
|
|
var period = OfflineUtils.reconstructPeriod(periodDb, drmInfos, timeline);
|
|
expect(period).toBeTruthy();
|
|
expect(period.variants.length).toBe(1);
|
|
expect(period.textStreams.length).toBe(1);
|
|
|
|
verifyStream(period.textStreams[0], periodDb.streams[1]);
|
|
});
|
|
|
|
it('combines Variants according to variantIds field', function() {
|
|
/** @const {number} */
|
|
var audio1 = 0;
|
|
/** @const {number} */
|
|
var audio2 = 1;
|
|
/** @const {number} */
|
|
var video1 = 2;
|
|
/** @const {number} */
|
|
var video2 = 3;
|
|
|
|
/** @const {number} */
|
|
var variant1 = 0;
|
|
/** @const {number} */
|
|
var variant2 = 1;
|
|
/** @const {number} */
|
|
var variant3 = 2;
|
|
|
|
/** @type {shakaExtern.PeriodDB} */
|
|
var periodDb = {
|
|
startTime: 60,
|
|
streams: [
|
|
// Audio
|
|
createAudioStreamDb(audio1, [variant2]),
|
|
createAudioStreamDb(audio2, [variant1, variant3]),
|
|
|
|
// Video
|
|
createVideoStreamDb(video1, [variant1]),
|
|
createVideoStreamDb(video2, [variant2, variant3])
|
|
]
|
|
};
|
|
|
|
/** @type {shakaExtern.Period} */
|
|
var period = OfflineUtils.reconstructPeriod(periodDb, drmInfos, timeline);
|
|
|
|
expect(period).toBeTruthy();
|
|
expect(period.variants.length).toBe(3);
|
|
|
|
// Variant 1
|
|
expect(findVariant(period.variants, audio2, video1)).toBeTruthy();
|
|
// Variant 2
|
|
expect(findVariant(period.variants, audio1, video2)).toBeTruthy();
|
|
// Variant 3
|
|
expect(findVariant(period.variants, audio2, video2)).toBeTruthy();
|
|
});
|
|
|
|
|
|
/**
|
|
* @param {number} id
|
|
* @param {!Array.<number>} variantIds
|
|
* @return {shakaExtern.StreamDB}
|
|
*/
|
|
function createVideoStreamDb(id, variantIds) {
|
|
var ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
return {
|
|
id: id,
|
|
primary: false,
|
|
presentationTimeOffset: 25,
|
|
contentType: ContentType.VIDEO,
|
|
mimeType: 'video/mp4',
|
|
codecs: 'avc1.42c01e',
|
|
frameRate: 22,
|
|
kind: undefined,
|
|
language: '',
|
|
label: null,
|
|
width: 250,
|
|
height: 100,
|
|
initSegmentKey: null,
|
|
encrypted: true,
|
|
keyId: 'key1',
|
|
segments: [
|
|
createSegment(
|
|
/* start time */ 0,
|
|
/* end time */ 10,
|
|
/* data key */ 1),
|
|
createSegment(
|
|
/* start time */ 10,
|
|
/* end time */ 20,
|
|
/* data key */ 2),
|
|
createSegment(
|
|
/* start time */ 20,
|
|
/* end time */ 25,
|
|
/* data key */ 3)
|
|
],
|
|
variantIds: variantIds
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param {number} id
|
|
* @param {!Array.<number>} variantIds
|
|
* @return {shakaExtern.StreamDB}
|
|
*/
|
|
function createAudioStreamDb(id, variantIds) {
|
|
var ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
return {
|
|
id: id,
|
|
primary: false,
|
|
presentationTimeOffset: 10,
|
|
contentType: ContentType.AUDIO,
|
|
mimeType: 'audio/mp4',
|
|
codecs: 'mp4a.40.2',
|
|
frameRate: undefined,
|
|
kind: undefined,
|
|
language: 'en',
|
|
label: null,
|
|
width: null,
|
|
height: null,
|
|
initSegmentKey: 0,
|
|
encrypted: false,
|
|
keyId: null,
|
|
segments: [
|
|
createSegment(
|
|
/* start time */ 0,
|
|
/* end time */ 10,
|
|
/* data key */ 1),
|
|
createSegment(
|
|
/* start time */ 10,
|
|
/* end time */ 20,
|
|
/* data key */ 2),
|
|
createSegment(
|
|
/* start time */ 20,
|
|
/* end time */ 25,
|
|
/* data key */ 3)
|
|
],
|
|
variantIds: variantIds
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param {number} id
|
|
* @return {shakaExtern.StreamDB}
|
|
*/
|
|
function createTextStreamDb(id) {
|
|
var ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
return {
|
|
id: id,
|
|
primary: false,
|
|
presentationTimeOffset: 10,
|
|
contentType: ContentType.TEXT,
|
|
mimeType: 'text/vtt',
|
|
codecs: '',
|
|
frameRate: undefined,
|
|
kind: undefined,
|
|
language: 'en',
|
|
label: null,
|
|
width: null,
|
|
height: null,
|
|
initSegmentKey: 0,
|
|
encrypted: false,
|
|
keyId: null,
|
|
segments: [
|
|
createSegment(
|
|
/* start time */ 0,
|
|
/* end time */ 10,
|
|
/* data key */ 1),
|
|
createSegment(
|
|
/* start time */ 10,
|
|
/* end time */ 20,
|
|
/* data key */ 2),
|
|
createSegment(
|
|
/* start time */ 20,
|
|
/* end time */ 25,
|
|
/* data key */ 3)
|
|
],
|
|
variantIds: [5]
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param {?shakaExtern.Stream} stream
|
|
* @param {?shakaExtern.StreamDB} streamDb
|
|
*/
|
|
function verifyStream(stream, streamDb) {
|
|
if (!streamDb) {
|
|
expect(stream).toBeFalsy();
|
|
return;
|
|
}
|
|
|
|
var expectedStream = {
|
|
id: jasmine.any(Number),
|
|
createSegmentIndex: jasmine.any(Function),
|
|
findSegmentPosition: jasmine.any(Function),
|
|
getSegmentReference: jasmine.any(Function),
|
|
initSegmentReference: streamDb.initSegmentKey != null ?
|
|
jasmine.any(shaka.media.InitSegmentReference) :
|
|
null,
|
|
presentationTimeOffset: streamDb.presentationTimeOffset,
|
|
mimeType: streamDb.mimeType,
|
|
codecs: streamDb.codecs,
|
|
frameRate: streamDb.frameRate,
|
|
width: streamDb.width || undefined,
|
|
height: streamDb.height || undefined,
|
|
kind: streamDb.kind,
|
|
encrypted: streamDb.encrypted,
|
|
keyId: streamDb.keyId,
|
|
language: streamDb.language,
|
|
label: streamDb.label,
|
|
type: streamDb.contentType,
|
|
primary: streamDb.primary,
|
|
trickModeVideo: null,
|
|
containsEmsgBoxes: false,
|
|
roles: [],
|
|
channelsCount: null
|
|
};
|
|
|
|
expect(stream).toEqual(expectedStream);
|
|
|
|
// Assume that we don't have to call createSegmentIndex.
|
|
|
|
streamDb.segments.forEach(function(segmentDb, i) {
|
|
/** @type {?string} */
|
|
var uri = shaka.offline.OfflineUri.segmentIdToUri(segmentDb.dataKey);
|
|
|
|
expect(stream.findSegmentPosition(segmentDb.startTime)).toBe(i);
|
|
expect(stream.findSegmentPosition(segmentDb.endTime - 0.1)).toBe(i);
|
|
|
|
/** @type {shaka.media.SegmentReference} */
|
|
var segment = stream.getSegmentReference(i);
|
|
expect(segment).toBeTruthy();
|
|
expect(segment.position).toBe(i);
|
|
expect(segment.startTime).toBe(segmentDb.startTime);
|
|
expect(segment.endTime).toBe(segmentDb.endTime);
|
|
expect(segment.startByte).toBe(0);
|
|
expect(segment.endByte).toBe(null);
|
|
expect(segment.getUris()).toEqual([uri]);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {!Array.<shakaExtern.Variant>} variants
|
|
* @param {?number} audioId
|
|
* @param {?number} videoId
|
|
* @return {?shakaExtern.Variant}
|
|
*/
|
|
function findVariant(variants, audioId, videoId) {
|
|
/*** @type {?shakaExtern.Variant} */
|
|
var found = null;
|
|
|
|
variants.forEach(function(variant) {
|
|
|
|
/** @type {?shakaExtern.Stream} */
|
|
var audio = variant.audio;
|
|
/** @type {?shakaExtern.Stream} */
|
|
var video = variant.video;
|
|
|
|
/** @type {boolean } */
|
|
var audioMatch = audio ? audioId == audio.id : audioId == null;
|
|
/** @type {boolean } */
|
|
var videoMatch = video ? videoId == video.id : videoId == null;
|
|
|
|
if (audioMatch && videoMatch) {
|
|
found = variant;
|
|
}
|
|
});
|
|
|
|
return found;
|
|
}
|
|
});
|
|
|
|
function createSegment(startTime, endTime, dataKey) {
|
|
/** @type {shakaExtern.SegmentDB} */
|
|
var segment = {
|
|
startTime: startTime,
|
|
endTime: endTime,
|
|
dataKey: dataKey
|
|
};
|
|
|
|
return segment;
|
|
}
|
|
});
|