You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
254 lines
8.7 KiB
254 lines
8.7 KiB
'use strict';
|
|
|
|
var segments = require('data-files!segments');
|
|
|
|
var probe = require('../lib/mp4/probe');
|
|
var CaptionParser = require('../lib/mp4').CaptionParser;
|
|
var captionParser;
|
|
|
|
var dashInit = segments['dash-608-captions-init.mp4']();
|
|
// This file includes 2 segments data to force a flush
|
|
// of the first caption. The second caption is at 200s
|
|
var dashSegment = segments['dash-608-captions-seg.m4s']();
|
|
|
|
var mp4Helpers = require('./utils/mp4-helpers');
|
|
var box = mp4Helpers.box;
|
|
var seiNalUnitGenerator = require('./utils/sei-nal-unit-generator');
|
|
var makeMdatFromCaptionPackets = seiNalUnitGenerator.makeMdatFromCaptionPackets;
|
|
var characters = seiNalUnitGenerator.characters;
|
|
|
|
var packets0;
|
|
var version0Moof;
|
|
var version0Segment;
|
|
|
|
var packets1;
|
|
var version1Moof;
|
|
var version1Segment;
|
|
|
|
QUnit.module('MP4 Caption Parser', {
|
|
beforeEach: function() {
|
|
captionParser = new CaptionParser();
|
|
captionParser.init();
|
|
},
|
|
|
|
afterEach: function() {
|
|
captionParser.reset();
|
|
}
|
|
});
|
|
|
|
QUnit.test('parse captions from real segment', function(assert) {
|
|
var trackIds;
|
|
var timescales;
|
|
var cc;
|
|
|
|
trackIds = probe.videoTrackIds(dashInit);
|
|
timescales = probe.timescale(dashInit);
|
|
|
|
cc = captionParser.parse(dashSegment, trackIds, timescales);
|
|
|
|
assert.equal(cc.captions.length, 1);
|
|
assert.equal(cc.captions[0].text, '00:00:00',
|
|
'real segment caption has correct text');
|
|
assert.equal(cc.captions[0].stream, 'CC1',
|
|
'real segment caption has correct stream');
|
|
assert.equal(cc.captions[0].startTime, 0,
|
|
'real segment caption has correct startTime');
|
|
assert.equal(cc.captions[0].endTime, 119,
|
|
'real segment caption has correct endTime');
|
|
assert.equal(cc.captionStreams.CC1, true,
|
|
'real segment caption streams have correct settings');
|
|
});
|
|
|
|
QUnit.test('parse captions when init segment received late', function(assert) {
|
|
var trackIds;
|
|
var timescales;
|
|
var cc;
|
|
|
|
trackIds = probe.videoTrackIds(dashInit);
|
|
timescales = probe.timescale(dashInit);
|
|
|
|
cc = captionParser.parse(dashSegment, [], {});
|
|
assert.ok(!cc, 'there should not be any parsed captions yet');
|
|
|
|
cc = captionParser.parse(dashSegment, trackIds, timescales);
|
|
assert.equal(cc.captions.length, 1);
|
|
});
|
|
|
|
QUnit.test('parseTrackId for version 0 and version 1 boxes', function(assert) {
|
|
var v0Captions;
|
|
var v1Captions;
|
|
|
|
v0Captions = captionParser.parse(
|
|
new Uint8Array(version0Segment), // segment
|
|
[1], // trackIds
|
|
{ 1: 90000 }); // timescales);
|
|
|
|
assert.equal(v0Captions.captions.length, 1, 'got 1 version0 caption');
|
|
assert.equal(v0Captions.captions[0].text, 'test string #1',
|
|
'got the expected version0 caption text');
|
|
assert.equal(v0Captions.captions[0].stream, 'CC1',
|
|
'returned the correct caption stream CC1');
|
|
assert.equal(v0Captions.captions[0].startTime, 10 / 90000,
|
|
'the start time for version0 caption is correct');
|
|
assert.equal(v0Captions.captions[0].endTime, 10 / 90000,
|
|
'the end time for version0 caption is correct');
|
|
assert.equal(v0Captions.captionStreams.CC1, true,
|
|
'stream is CC1');
|
|
assert.ok(!v0Captions.captionStreams.CC4,
|
|
'stream is not CC4');
|
|
|
|
// Clear parsed captions
|
|
captionParser.clearParsedCaptions();
|
|
|
|
v1Captions = captionParser.parse(
|
|
new Uint8Array(version1Segment),
|
|
[2], // trackIds
|
|
{ 2: 90000 }); // timescales
|
|
|
|
assert.equal(v1Captions.captions.length, 1, 'got version1 caption');
|
|
assert.equal(v1Captions.captions[0].text, 'test string #2',
|
|
'got the expected version1 caption text');
|
|
assert.equal(v1Captions.captions[0].stream, 'CC4',
|
|
'returned the correct caption stream CC4');
|
|
assert.equal(v1Captions.captions[0].startTime, 30 / 90000,
|
|
'the start time for version1 caption is correct');
|
|
assert.equal(v1Captions.captions[0].endTime, 30 / 90000,
|
|
'the end time for version1 caption is correct');
|
|
assert.equal(v1Captions.captionStreams.CC4, true,
|
|
'stream is CC4');
|
|
assert.ok(!v1Captions.captionStreams.CC1,
|
|
'stream is not CC1');
|
|
});
|
|
|
|
// ---------
|
|
// Test Data
|
|
// ---------
|
|
|
|
// "test string #1", channel 1, field 1
|
|
packets0 = [
|
|
// Send another command so that the second EOC isn't ignored
|
|
{ ccData: 0x1420, type: 0 },
|
|
// RCL, resume caption loading
|
|
{ ccData: 0x1420, type: 0 },
|
|
// 'test string #1'
|
|
{ ccData: characters('te'), type: 0 },
|
|
{ ccData: characters('st'), type: 0 },
|
|
{ ccData: characters(' s'), type: 0 },
|
|
// 'test string #1' continued
|
|
{ ccData: characters('tr'), type: 0 },
|
|
{ ccData: characters('in'), type: 0 },
|
|
{ ccData: characters('g '), type: 0 },
|
|
{ ccData: characters('#1'), type: 0 },
|
|
// EOC, End of Caption. End display
|
|
{ ccData: 0x142f, type: 0 },
|
|
// EOC, End of Caption. Finished transmitting, begin display
|
|
{ ccData: 0x142f, type: 0 },
|
|
// Send another command so that the second EOC isn't ignored
|
|
{ ccData: 0x1420, type: 0 },
|
|
// EOC, End of Caption. End display
|
|
{ ccData: 0x142f, type: 0 }
|
|
];
|
|
|
|
// "test string #2", channel 2, field 2
|
|
packets1 = [
|
|
// Send another command so that the second EOC isn't ignored
|
|
{ ccData: 0x1d20, type: 1 },
|
|
// RCL, resume caption loading
|
|
{ ccData: 0x1d20, type: 1 },
|
|
// 'test string #2'
|
|
{ ccData: characters('te'), type: 1 },
|
|
{ ccData: characters('st'), type: 1 },
|
|
{ ccData: characters(' s'), type: 1 },
|
|
// 'test string #2' continued
|
|
{ ccData: characters('tr'), type: 1 },
|
|
{ ccData: characters('in'), type: 1 },
|
|
{ ccData: characters('g '), type: 1 },
|
|
{ ccData: characters('#2'), type: 1 },
|
|
// EOC, End of Caption. End display
|
|
{ ccData: 0x1d2f, type: 1 },
|
|
// EOC, End of Caption. Finished transmitting, begin display
|
|
{ ccData: 0x1d2f, type: 1 },
|
|
// Send another command so that the second EOC isn't ignored
|
|
{ ccData: 0x1d20, type: 1 },
|
|
// EOC, End of Caption. End display
|
|
{ ccData: 0x1d2f, type: 1 }
|
|
];
|
|
|
|
/**
|
|
* version 0:
|
|
* Uses version 0 boxes, no first sample flags
|
|
* sample size, flags, duration, composition time offset included.
|
|
**/
|
|
version0Moof =
|
|
box('moof',
|
|
box('traf',
|
|
box('tfhd',
|
|
0x00, // version
|
|
0x00, 0x00, 0x00, // flags
|
|
0x00, 0x00, 0x00, 0x01, // track_ID
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, // base_data_offset
|
|
0x00, 0x00, 0x00, 0x00, // sample_description_index
|
|
0x00, 0x00, 0x00, 0x00, // default_sample_duration
|
|
0x00, 0x00, 0x00, 0x00, // default_sample_size
|
|
0x00, 0x00, 0x00, 0x00), // default_sample_flags
|
|
box('tfdt',
|
|
0x00, // version
|
|
0x00, 0x00, 0x00, // flags
|
|
0x00, 0x00, 0x00, 0x00), // baseMediaDecodeTime,
|
|
box('trun',
|
|
0x00, // version
|
|
0x00, 0x0f, 0x01, // flags: dataOffsetPresent, sampleDurationPresent,
|
|
// sampleSizePresent, sampleFlagsPresent,
|
|
// sampleCompositionTimeOffsetsPresent
|
|
0x00, 0x00, 0x00, 0x02, // sample_count
|
|
0x00, 0x00, 0x00, 0x00, // data_offset, no first_sample_flags
|
|
// sample 1
|
|
0x00, 0x00, 0x00, 0x0a, // sample_duration = 10
|
|
0x00, 0x00, 0x00, 0x0a, // sample_size = 10
|
|
0x00, 0x00, 0x00, 0x00, // sample_flags
|
|
0x00, 0x00, 0x00, 0x0a, // signed sample_composition_time_offset = 10
|
|
// sample 2
|
|
0x00, 0x00, 0x00, 0x0a, // sample_duration = 10
|
|
0x00, 0x00, 0x00, 0x0a, // sample_size = 10
|
|
0x00, 0x00, 0x00, 0x00, // sample_flags
|
|
0x00, 0x00, 0x00, 0x14))); // signed sample_composition_time_offset = 20
|
|
|
|
version0Segment = version0Moof.concat(makeMdatFromCaptionPackets(packets0));
|
|
|
|
/**
|
|
* version 1:
|
|
* Uses version 1 boxes, has first sample flags,
|
|
* other samples include flags and composition time offset only.
|
|
**/
|
|
version1Moof =
|
|
box('moof',
|
|
box('traf',
|
|
box('tfhd',
|
|
0x01, // version
|
|
0x00, 0x00, 0x18, // flags
|
|
0x00, 0x00, 0x00, 0x02, // track_ID
|
|
// no base_data_offset, sample_description_index
|
|
0x00, 0x00, 0x00, 0x0a, // default_sample_duration = 10
|
|
0x00, 0x00, 0x00, 0x0a), // default_sample_size = 10
|
|
box('tfdt',
|
|
0x01, // version
|
|
0x00, 0x00, 0x00, // flags
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x14), // baseMediaDecodeTime = 20,
|
|
box('trun',
|
|
0x01, // version
|
|
0x00, 0x0c, 0x05, // flags: dataOffsetPresent, sampleFlagsPresent,
|
|
// firstSampleFlagsPresent,
|
|
// sampleCompositionTimeOffsetsPresent
|
|
0x00, 0x00, 0x00, 0x02, // sample_count
|
|
0x00, 0x00, 0x00, 0x00, // data_offset, has first_sample_flags
|
|
// sample 1
|
|
0x00, 0x00, 0x00, 0x00, // sample_flags
|
|
0x00, 0x00, 0x00, 0x0a, // signed sample_composition_time_offset = 10
|
|
// sample 2
|
|
0x00, 0x00, 0x00, 0x00, // sample_flags
|
|
0x00, 0x00, 0x00, 0x14))); // signed sample_composition_time_offset = 20
|
|
|
|
version1Segment = version1Moof.concat(makeMdatFromCaptionPackets(packets1));
|