Skip to content

Commit

Permalink
Add support for CEA with no channel and language info for Dash
Browse files Browse the repository at this point in the history
When the dash content has CEA closed captions signal but no detail
information about channel number and language, we show the default
caption information.

Issue #1404

Change-Id: Ie6ca77739a043d24832efc5a28d4ba708dc4b17f
  • Loading branch information
michellezhuogg committed Nov 8, 2018
1 parent 34408b9 commit f2b9e3f
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 20 deletions.
26 changes: 16 additions & 10 deletions lib/dash/dash_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1014,25 +1014,25 @@ shaka.dash.DashParser.prototype.parseAdaptationSet_ = function(context, elem) {
const accessibilities = XmlUtils.findChildren(elem, 'Accessibility');
const LanguageUtils = shaka.util.LanguageUtils;
let closedCaptions = new Map();
let captionId = 0;
for (const prop of accessibilities) {
let schemeId = prop.getAttribute('schemeIdUri');
if (schemeId == 'urn:scte:dash:cc:cea-608:2015' ||
schemeId == 'urn:scte:dash:cc:cea-708:2015') {
let channelId = 1;
let closedCaptionsValue = prop.getAttribute('value');
if (closedCaptionsValue != null) {
closedCaptionsValue.split(';').forEach((captionStr) => {
let channel;
let language;
// Some closed caption descriptions have channel number and language,
// like "CC1=eng" or "1=lang:eng", others may only have the language,
// like "eng".
// Since only odd numbers are used as channel numbers, like CC1, CC3,
// CC5, etc, when the channel number is not provided, use an odd
// number as the key. https://en.wikipedia.org/wiki/EIA-608
let channel;
let language;
if (!captionStr.includes('=')) {
channel = 'CC' + ((captionId * 2) + 1);
captionId++;
// Since only odd numbers are used as channel numbers, like CC1,
// CC3, CC5, etc, when the channel number is not provided, use an
// odd number as the key. https://en.wikipedia.org/wiki/EIA-608
channel = 'CC' + channelId;
channelId += 2;
language = captionStr;
} else {
const channelAndLanguage = captionStr.split('=');
Expand All @@ -1041,11 +1041,17 @@ shaka.dash.DashParser.prototype.parseAdaptationSet_ = function(context, elem) {
// as prefix so that it can be a full channel id (like 'CC1').
channel = channelAndLanguage[0].startsWith('CC') ?
channelAndLanguage[0] : 'CC' + channelAndLanguage[0];
// The language info can be 'eng' or 'lang:eng'.
language = channelAndLanguage[1].split(':').pop();
// The language info can be different formats, like 'eng',
// 'lang:eng', or 'lang:eng,war:1,er:1'. Extract the language info
// and convert it to 2-letter language code format.
language = channelAndLanguage[1].split(',')[0].split(':').pop();
}
closedCaptions.set(channel, LanguageUtils.normalize(language));
});
} else {
// If channel and language information has not been provided, assign
// 'CC1' as channel id and 'und' as language info.
closedCaptions.set('CC1', 'und');
}
}
}
Expand Down
63 changes: 53 additions & 10 deletions test/dash/dash_parser_manifest_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,9 @@ describe('DashParser Manifest', function() {
}, 0, null));
});


it('correctly parses closed captions with channel numbers', async () => {
let source = [
it('correctly parses closed captions with channels and languages',
async () => {
const source = [
'<MPD minBufferTime="PT75S">',
' <Period id="1" duration="PT30S">',
' <AdaptationSet mimeType="video/mp4" lang="en" group="1">',
Expand All @@ -323,26 +323,45 @@ describe('DashParser Manifest', function() {
' <SegmentTemplate media="1.mp4" duration="1" />',
' </Representation>',
' </AdaptationSet>',
' <AdaptationSet mimeType="video/mp4" lang="en" group="1">',
' <Accessibility schemeIdUri="urn:scte:dash:cc:cea-608:2015"',
' value="CC1=lang:eng;CC3=lang:swe"/>',
' <Representation bandwidth="200">',
' <SegmentTemplate media="1.mp4" duration="1" />',
' </Representation>',
' </AdaptationSet>',
' <AdaptationSet mimeType="video/mp4" lang="en" group="1">',
' <Accessibility schemeIdUri="urn:scte:dash:cc:cea-608:2015"',
' value="1=lang:eng;3=lang:swe,war:1,er:1"/>',
' <Representation bandwidth="200">',
' <SegmentTemplate media="1.mp4" duration="1" />',
' </Representation>',
' </AdaptationSet>',
' </Period>',
'</MPD>',
].join('\n');

fakeNetEngine.setResponseText('dummy://foo', source);

let manifest = await parser.start('dummy://foo', playerInterface);
const manifest = await parser.start('dummy://foo', playerInterface);
// First Representation should be dropped.
let period = manifest.periods[0];
let stream = period.variants[0].video;
const period = manifest.periods[0];
const stream1 = period.variants[0].video;
const stream2 = period.variants[1].video;
const stream3 = period.variants[2].video;

const expectedClosedCaptions = new Map(
[['CC1', shaka.util.LanguageUtils.normalize('eng')],
['CC3', shaka.util.LanguageUtils.normalize('swe')]]
);
expect(stream.closedCaptions).toEqual(expectedClosedCaptions);
expect(stream1.closedCaptions).toEqual(expectedClosedCaptions);
expect(stream2.closedCaptions).toEqual(expectedClosedCaptions);
expect(stream3.closedCaptions).toEqual(expectedClosedCaptions);
});


it('correctly parses closed captions without channel numbers', async () => {
let source = [
const source = [
'<MPD minBufferTime="PT75S">',
' <Period id="1" duration="PT30S">',
' <AdaptationSet mimeType="video/mp4" lang="en" group="1">',
Expand All @@ -358,8 +377,8 @@ describe('DashParser Manifest', function() {

fakeNetEngine.setResponseText('dummy://foo', source);

let manifest = await parser.start('dummy://foo', playerInterface);
let stream = manifest.periods[0].variants[0].video;
const manifest = await parser.start('dummy://foo', playerInterface);
const stream = manifest.periods[0].variants[0].video;
const expectedClosedCaptions = new Map(
[['CC1', shaka.util.LanguageUtils.normalize('eng')],
['CC3', shaka.util.LanguageUtils.normalize('swe')]]
Expand All @@ -368,6 +387,30 @@ describe('DashParser Manifest', function() {
});


it('correctly parses closed captions with no channel and language info',
async () => {
const source = [
'<MPD minBufferTime="PT75S">',
' <Period id="1" duration="PT30S">',
' <AdaptationSet mimeType="video/mp4" lang="en" group="1">',
' <Accessibility schemeIdUri="urn:scte:dash:cc:cea-608:2015"/>',
' <Representation bandwidth="200">',
' <SegmentTemplate media="1.mp4" duration="1" />',
' </Representation>',
' </AdaptationSet>',
' </Period>',
'</MPD>',
].join('\n');

fakeNetEngine.setResponseText('dummy://foo', source);

const manifest = await parser.start('dummy://foo', playerInterface);
const stream = manifest.periods[0].variants[0].video;
const expectedClosedCaptions = new Map([['CC1', 'und']]);
expect(stream.closedCaptions).toEqual(expectedClosedCaptions);
});


it('correctly parses UTF-8', async () => {
let source = [
'<MPD>',
Expand Down

0 comments on commit f2b9e3f

Please sign in to comment.