mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-25 17:45:03 +03:00
cd2d25cbb2
This changes the text APIs to correctly handle buffered ranges of segmented text. b/25517444 Related to issue #150 Change-Id: I3a11b87e8d93376a5012566deb3bf0d015f52391
311 lines
11 KiB
JavaScript
311 lines
11 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.
|
|
*/
|
|
|
|
describe('TextEngine', function() {
|
|
var TextEngine;
|
|
var dummyData = new ArrayBuffer(0);
|
|
var dummyMimeType = 'text/fake';
|
|
|
|
var mockParser;
|
|
var mockTrack;
|
|
var textEngine;
|
|
|
|
beforeAll(function() {
|
|
TextEngine = shaka.media.TextEngine;
|
|
});
|
|
|
|
beforeEach(function() {
|
|
mockParser = jasmine.createSpy('mockParser');
|
|
mockTrack = createMockTrack();
|
|
TextEngine.registerParser(dummyMimeType, mockParser);
|
|
textEngine = new TextEngine(mockTrack, dummyMimeType);
|
|
});
|
|
|
|
afterEach(function() {
|
|
textEngine = null;
|
|
TextEngine.unregisterParser(dummyMimeType);
|
|
mockTrack = null;
|
|
mockParser = null;
|
|
});
|
|
|
|
describe('isTypeSupported', function() {
|
|
it('reports support only when a parser is installed', function() {
|
|
TextEngine.unregisterParser(dummyMimeType);
|
|
expect(TextEngine.isTypeSupported(dummyMimeType)).toBe(false);
|
|
TextEngine.registerParser(dummyMimeType, mockParser);
|
|
expect(TextEngine.isTypeSupported(dummyMimeType)).toBe(true);
|
|
TextEngine.unregisterParser(dummyMimeType);
|
|
expect(TextEngine.isTypeSupported(dummyMimeType)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('appendBuffer', function() {
|
|
it('works asynchronously', function(done) {
|
|
mockParser.and.returnValue([1, 2, 3]);
|
|
textEngine.appendBuffer(dummyData, 0, 3).catch(fail).then(done);
|
|
expect(mockTrack.addCue).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('adds cues to the track', function(done) {
|
|
mockParser.and.returnValue([1, 2, 3]);
|
|
|
|
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
|
expect(mockParser).toHaveBeenCalledWith(dummyData);
|
|
expect(mockTrack.addCue).toHaveBeenCalledWith(1);
|
|
expect(mockTrack.addCue).toHaveBeenCalledWith(2);
|
|
expect(mockTrack.addCue).toHaveBeenCalledWith(3);
|
|
expect(mockTrack.removeCue).not.toHaveBeenCalled();
|
|
|
|
mockTrack.addCue.calls.reset();
|
|
mockParser.calls.reset();
|
|
|
|
mockParser.and.returnValue([4, 5]);
|
|
return textEngine.appendBuffer(dummyData, 3, 5);
|
|
}).then(function() {
|
|
expect(mockParser).toHaveBeenCalledWith(dummyData);
|
|
expect(mockTrack.addCue).toHaveBeenCalledWith(4);
|
|
expect(mockTrack.addCue).toHaveBeenCalledWith(5);
|
|
}).catch(fail).then(done);
|
|
});
|
|
});
|
|
|
|
describe('remove', function() {
|
|
var cue1;
|
|
var cue2;
|
|
var cue3;
|
|
|
|
beforeEach(function() {
|
|
cue1 = createFakeCue(0, 1);
|
|
cue2 = createFakeCue(1, 2);
|
|
cue3 = createFakeCue(2, 3);
|
|
mockParser.and.returnValue([cue1, cue2, cue3]);
|
|
});
|
|
|
|
it('works asynchronously', function(done) {
|
|
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
|
var p = textEngine.remove(0, 1);
|
|
expect(mockTrack.removeCue).not.toHaveBeenCalled();
|
|
return p;
|
|
}).catch(fail).then(done);
|
|
});
|
|
|
|
it('removes cues which overlap the range', function(done) {
|
|
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
|
return textEngine.remove(0, 1);
|
|
}).then(function() {
|
|
expect(mockTrack.removeCue.calls.allArgs()).toEqual([[cue1]]);
|
|
|
|
mockTrack.removeCue.calls.reset();
|
|
return textEngine.remove(0.5, 1.001);
|
|
}).then(function() {
|
|
expect(mockTrack.removeCue.calls.allArgs()).toEqual([[cue2]]);
|
|
|
|
mockTrack.removeCue.calls.reset();
|
|
return textEngine.remove(3, 5);
|
|
}).then(function() {
|
|
expect(mockTrack.removeCue).not.toHaveBeenCalled();
|
|
|
|
mockTrack.removeCue.calls.reset();
|
|
return textEngine.remove(2.9999, Number.POSITIVE_INFINITY);
|
|
}).then(function() {
|
|
expect(mockTrack.removeCue.calls.allArgs()).toEqual([[cue3]]);
|
|
}).catch(fail).then(done);
|
|
});
|
|
|
|
it('does nothing when nothing is buffered', function(done) {
|
|
textEngine.remove(0, 1).then(function() {
|
|
expect(mockTrack.removeCue).not.toHaveBeenCalled();
|
|
}).catch(fail).then(done);
|
|
});
|
|
});
|
|
|
|
describe('setTimestampOffset', function() {
|
|
it('affects the timestamps of parsed cues', function(done) {
|
|
mockParser.and.callFake(function() {
|
|
return [createFakeCue(0, 1), createFakeCue(2, 3)];
|
|
});
|
|
|
|
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
|
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(0, 1));
|
|
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(2, 3));
|
|
|
|
mockTrack.addCue.calls.reset();
|
|
textEngine.setTimestampOffset(4);
|
|
return textEngine.appendBuffer(dummyData, 0, 3);
|
|
}).then(function() {
|
|
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(4, 5));
|
|
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(6, 7));
|
|
}).catch(fail).then(done);
|
|
});
|
|
});
|
|
|
|
describe('bufferStart/bufferEnd', function() {
|
|
beforeEach(function() {
|
|
mockParser.and.callFake(function() {
|
|
return [createFakeCue(0, 1), createFakeCue(1, 2), createFakeCue(2, 3)];
|
|
});
|
|
});
|
|
|
|
it('return null when there are no cues', function() {
|
|
expect(textEngine.bufferStart()).toBe(null);
|
|
expect(textEngine.bufferEnd()).toBe(null);
|
|
});
|
|
|
|
it('reflect newly-added cues', function(done) {
|
|
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
|
expect(textEngine.bufferStart()).toBe(0);
|
|
expect(textEngine.bufferEnd()).toBe(3);
|
|
|
|
textEngine.setTimestampOffset(3);
|
|
return textEngine.appendBuffer(dummyData, 0, 3);
|
|
}).then(function() {
|
|
expect(textEngine.bufferStart()).toBe(0);
|
|
expect(textEngine.bufferEnd()).toBe(6);
|
|
|
|
textEngine.setTimestampOffset(7);
|
|
return textEngine.appendBuffer(dummyData, 0, 3);
|
|
}).then(function() {
|
|
expect(textEngine.bufferStart()).toBe(0);
|
|
expect(textEngine.bufferEnd()).toBe(10);
|
|
}).catch(fail).then(done);
|
|
});
|
|
|
|
it('reflect newly-removed cues', function(done) {
|
|
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
|
textEngine.setTimestampOffset(3);
|
|
return textEngine.appendBuffer(dummyData, 0, 3);
|
|
}).then(function() {
|
|
textEngine.setTimestampOffset(7);
|
|
return textEngine.appendBuffer(dummyData, 0, 3);
|
|
}).then(function() {
|
|
expect(textEngine.bufferStart()).toBe(0);
|
|
expect(textEngine.bufferEnd()).toBe(10);
|
|
|
|
return textEngine.remove(0, 3);
|
|
}).then(function() {
|
|
expect(textEngine.bufferStart()).toBe(3);
|
|
expect(textEngine.bufferEnd()).toBe(10);
|
|
|
|
return textEngine.remove(8, 11);
|
|
}).then(function() {
|
|
expect(textEngine.bufferStart()).toBe(3);
|
|
expect(textEngine.bufferEnd()).toBe(8);
|
|
|
|
return textEngine.remove(11, 20);
|
|
}).then(function() {
|
|
expect(textEngine.bufferStart()).toBe(3);
|
|
expect(textEngine.bufferEnd()).toBe(8);
|
|
|
|
return textEngine.remove(0, Number.POSITIVE_INFINITY);
|
|
}).then(function() {
|
|
expect(textEngine.bufferStart()).toBe(null);
|
|
expect(textEngine.bufferEnd()).toBe(null);
|
|
}).catch(fail).then(done);
|
|
});
|
|
});
|
|
|
|
describe('bufferedAheadOf', function() {
|
|
beforeEach(function() {
|
|
mockParser.and.callFake(function() {
|
|
return [createFakeCue(0, 1), createFakeCue(1, 2), createFakeCue(2, 3)];
|
|
});
|
|
});
|
|
|
|
it('returns 0 when there are no cues', function() {
|
|
expect(textEngine.bufferedAheadOf(0)).toBe(0);
|
|
});
|
|
|
|
it('returns 0 if |t| is not buffered', function(done) {
|
|
textEngine.setTimestampOffset(3);
|
|
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
|
expect(textEngine.bufferedAheadOf(2.9)).toBe(0);
|
|
expect(textEngine.bufferedAheadOf(6.1)).toBe(0);
|
|
}).catch(fail).then(done);
|
|
});
|
|
|
|
it('returns the distance to the end if |t| is buffered', function(done) {
|
|
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
|
expect(textEngine.bufferedAheadOf(0)).toBe(3);
|
|
expect(textEngine.bufferedAheadOf(1)).toBe(2);
|
|
expect(textEngine.bufferedAheadOf(2.5)).toBeCloseTo(0.5);
|
|
}).catch(fail).then(done);
|
|
});
|
|
});
|
|
|
|
describe('setAppendWindowEnd', function() {
|
|
beforeEach(function() {
|
|
mockParser.and.callFake(function() {
|
|
return [createFakeCue(0, 1), createFakeCue(1, 2), createFakeCue(2, 3)];
|
|
});
|
|
});
|
|
|
|
it('limits appended cues', function(done) {
|
|
textEngine.setAppendWindowEnd(1.9);
|
|
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
|
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(0, 1));
|
|
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(1, 2));
|
|
|
|
mockTrack.addCue.calls.reset();
|
|
textEngine.setAppendWindowEnd(2.1);
|
|
return textEngine.appendBuffer(dummyData, 0, 3);
|
|
}).then(function() {
|
|
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(0, 1));
|
|
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(1, 2));
|
|
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(2, 3));
|
|
}).catch(fail).then(done);
|
|
});
|
|
|
|
it('limits bufferEnd', function(done) {
|
|
textEngine.setAppendWindowEnd(1.9);
|
|
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
|
expect(textEngine.bufferEnd()).toBe(1.9);
|
|
|
|
textEngine.setAppendWindowEnd(2.1);
|
|
return textEngine.appendBuffer(dummyData, 0, 3);
|
|
}).then(function() {
|
|
expect(textEngine.bufferEnd()).toBe(2.1);
|
|
|
|
textEngine.setAppendWindowEnd(4.1);
|
|
return textEngine.appendBuffer(dummyData, 0, 3);
|
|
}).then(function() {
|
|
expect(textEngine.bufferEnd()).toBe(3);
|
|
}).catch(fail).then(done);
|
|
});
|
|
});
|
|
|
|
function createMockTrack() {
|
|
var track = {
|
|
addCue: jasmine.createSpy('addCue'),
|
|
removeCue: jasmine.createSpy('removeCue'),
|
|
cues: []
|
|
};
|
|
track.addCue.and.callFake(function(cue) {
|
|
track.cues.push(cue);
|
|
});
|
|
track.removeCue.and.callFake(function(cue) {
|
|
var idx = track.cues.indexOf(cue);
|
|
shaka.asserts.assert(idx >= 0, 'cue does not exist');
|
|
track.cues.splice(idx, 1);
|
|
});
|
|
return track;
|
|
}
|
|
|
|
function createFakeCue(startTime, endTime) {
|
|
return { startTime: startTime, endTime: endTime };
|
|
}
|
|
});
|