diff --git a/externs/shaka/manifest.js b/externs/shaka/manifest.js index 67ebeb9965..7fb1c03693 100644 --- a/externs/shaka/manifest.js +++ b/externs/shaka/manifest.js @@ -344,6 +344,7 @@ shaka.extern.FetchCryptoKeysFunction; * drmInfos: !Array., * keyIds: !Set., * language: string, + * originalLanguage: ?string, * label: ?string, * type: string, * primary: boolean, @@ -429,6 +430,9 @@ shaka.extern.FetchCryptoKeysFunction; * The Stream's language, specified as a language code.
* Audio stream's language must be identical to the language of the containing * Variant. + * @property {?string} originalLanguage + * Optional.
+ * The original language, if any, that appeared in the manifest. * @property {?string} label * The Stream's label, unique text that should describe the audio/text track. * @property {string} type diff --git a/externs/shaka/offline.js b/externs/shaka/offline.js index 43ede57a32..4296dea087 100644 --- a/externs/shaka/offline.js +++ b/externs/shaka/offline.js @@ -123,6 +123,7 @@ shaka.extern.ManifestDB; * hdr: (string|undefined), * kind: (string|undefined), * language: string, + * originalLanguage: (?string|undefined), * label: ?string, * width: ?number, * height: ?number, @@ -162,6 +163,8 @@ shaka.extern.ManifestDB; * The kind of text stream; undefined for audio/video. * @property {string} language * The language of the stream; '' for video. + * @property {(?string|undefined)} originalLanguage + * The original language, if any, that appeared in the manifest. * @property {?string} label * The label of the stream; '' for video. * @property {?number} width diff --git a/externs/shaka/player.js b/externs/shaka/player.js index ba1a42c555..e19af7c06d 100644 --- a/externs/shaka/player.js +++ b/externs/shaka/player.js @@ -238,7 +238,8 @@ shaka.extern.BufferedInfo; * originalVideoId: ?string, * originalAudioId: ?string, * originalTextId: ?string, - * originalImageId: ?string + * originalImageId: ?string, + * originalLanguage: ?string * }} * * @description @@ -260,8 +261,10 @@ shaka.extern.BufferedInfo; * The bandwidth required to play the track, in bits/sec. * * @property {string} language - * The language of the track, or 'und' if not given. This is the - * exact value provided in the manifest; it may need to be normalized. + * The language of the track, or 'und' if not given. This value + * is normalized as follows - language part is always lowercase and translated + * to ISO-639-1 when possible, locale part is always uppercase, + * i.e. 'en-US'. * @property {?string} label * The track label, which is unique text that should describe the track. * @property {?string} kind @@ -340,6 +343,10 @@ shaka.extern.BufferedInfo; * @property {?string} originalImageId * (image tracks only) The original ID of the image track, if any, as it * appeared in the original manifest. + * @property {?string} originalLanguage + * The original language of the track, if any, as it appeared in the original + * manifest. This is the exact value provided in the manifest; for normalized + * value use language property. * @exportDoc */ shaka.extern.Track; diff --git a/lib/dash/dash_parser.js b/lib/dash/dash_parser.js index 8454130134..3a2780b085 100644 --- a/lib/dash/dash_parser.js +++ b/lib/dash/dash_parser.js @@ -1108,8 +1108,8 @@ shaka.dash.DashParser = class { this.config_.dash.ignoreDrmInfo, this.config_.dash.keySystemsByURI); - const language = - shaka.util.LanguageUtils.normalize(elem.getAttribute('lang') || 'und'); + const language = shaka.util.LanguageUtils.normalize( + context.adaptationSet.language || 'und'); // This attribute is currently non-standard, but it is supported by Kaltura. let label = elem.getAttribute('label'); @@ -1380,6 +1380,7 @@ shaka.dash.DashParser = class { drmInfos: contentProtection.drmInfos, keyIds, language, + originalLanguage: context.adaptationSet.language, label, type: context.adaptationSet.contentType, primary: isPrimary, @@ -1569,6 +1570,7 @@ shaka.dash.DashParser = class { pixelAspectRatio: pixelAspectRatio, emsgSchemeIdUris: emsgSchemeIdUris, id: elem.getAttribute('id'), + language: elem.getAttribute('lang'), numChannels: numChannels, audioSamplingRate: audioSamplingRate, availabilityTimeOffset: availabilityTimeOffset, @@ -1979,6 +1981,7 @@ shaka.dash.DashParser.RequestSegmentCallback; * pixelAspectRatio: (string|undefined), * emsgSchemeIdUris: !Array., * id: ?string, + * language: ?string, * numChannels: ?number, * audioSamplingRate: ?number, * availabilityTimeOffset: number @@ -2014,6 +2017,8 @@ shaka.dash.DashParser.RequestSegmentCallback; * emsg registered schemeIdUris. * @property {?string} id * The ID of the element. + * @property {?string} language + * The original language of the element. * @property {?number} numChannels * The number of audio channels, or null if unknown. * @property {?number} audioSamplingRate diff --git a/lib/hls/hls_parser.js b/lib/hls/hls_parser.js index 167a755c4c..9b5bc923e8 100644 --- a/lib/hls/hls_parser.js +++ b/lib/hls/hls_parser.js @@ -666,7 +666,7 @@ shaka.hls.HlsParser = class { const type = basicInfo.type; const mimeType = basicInfo.mimeType; const codecs = basicInfo.codecs; - const language = basicInfo.language || 'und'; + const languageValue = basicInfo.language; const height = basicInfo.height; const width = basicInfo.width; const channelsCount = basicInfo.channelCount; @@ -685,7 +685,7 @@ shaka.hls.HlsParser = class { // Make the stream info, with those values. const streamInfo = await this.convertParsedPlaylistIntoStreamInfo_( - playlist, uri, uri, codecs, type, language, primary, name, + playlist, uri, uri, codecs, type, languageValue, primary, name, channelsCount, closedCaptions, characteristics, forced, sampleRate, spatialAudio, mimeType); this.uriToStreamInfosMap_.set(uri, streamInfo); @@ -698,7 +698,7 @@ shaka.hls.HlsParser = class { // Wrap the stream from that stream info with a variant. variants.push({ id: 0, - language: language, + language: this.getLanguage_(languageValue), disabledUntilTime: 0, primary: true, audio: type == 'audio' ? streamInfo.stream : null, @@ -1609,16 +1609,15 @@ shaka.hls.HlsParser = class { } /** - * Get the language value. + * Get the normalized language value. * - * @param {!shaka.hls.Tag} tag + * @param {?string} languageValue * @return {string} * @private */ - getLanguage_(tag) { + getLanguage_(languageValue) { const LanguageUtils = shaka.util.LanguageUtils; - const languageValue = tag.getAttributeValue('LANGUAGE') || 'und'; - return LanguageUtils.normalize(languageValue); + return LanguageUtils.normalize(languageValue || 'und'); } /** @@ -1751,7 +1750,8 @@ shaka.hls.HlsParser = class { for (const tag of closedCaptionsTags) { goog.asserts.assert(tag.name == 'EXT-X-MEDIA', 'Should only be called on media tags!'); - const language = this.getLanguage_(tag); + const languageValue = tag.getAttributeValue('LANGUAGE'); + const language = this.getLanguage_(languageValue); // The GROUP-ID value is a quoted-string that specifies the group to which // the Rendition belongs. @@ -1797,7 +1797,7 @@ shaka.hls.HlsParser = class { return this.uriToStreamInfosMap_.get(verbatimMediaPlaylistUri); } - const language = this.getLanguage_(tag); + const language = tag.getAttributeValue('LANGUAGE'); const name = tag.getAttributeValue('NAME'); // NOTE: According to the HLS spec, "DEFAULT=YES" requires "AUTOSELECT=YES". @@ -1860,7 +1860,7 @@ shaka.hls.HlsParser = class { return this.uriToStreamInfosMap_.get(verbatimImagePlaylistUri); } - const language = this.getLanguage_(tag); + const language = tag.getAttributeValue('LANGUAGE'); const name = tag.getAttributeValue('NAME'); const characteristics = tag.getAttributeValue('CHARACTERISTICS'); @@ -1930,7 +1930,7 @@ shaka.hls.HlsParser = class { const closedCaptions = this.getClosedCaptions_(tag, type); const codecs = shaka.util.ManifestParserUtils.guessCodecs(type, allCodecs); const streamInfo = this.createStreamInfo_(verbatimMediaPlaylistUri, - codecs, type, /* language= */ 'und', /* primary= */ false, + codecs, type, /* language= */ null, /* primary= */ false, /* name= */ null, /* channelcount= */ null, closedCaptions, /* characteristics= */ null, /* forced= */ false, /* sampleRate= */ null, /* spatialAudio= */ false); @@ -1949,7 +1949,7 @@ shaka.hls.HlsParser = class { * @param {string} verbatimMediaPlaylistUri * @param {string} codecs * @param {string} type - * @param {string} language + * @param {?string} languageValue * @param {boolean} primary * @param {?string} name * @param {?number} channelsCount @@ -1961,7 +1961,7 @@ shaka.hls.HlsParser = class { * @return {!shaka.hls.HlsParser.StreamInfo} * @private */ - createStreamInfo_(verbatimMediaPlaylistUri, codecs, type, language, + createStreamInfo_(verbatimMediaPlaylistUri, codecs, type, languageValue, primary, name, channelsCount, closedCaptions, characteristics, forced, sampleRate, spatialAudio) { // TODO: Refactor, too many parameters @@ -1971,9 +1971,9 @@ shaka.hls.HlsParser = class { // This stream is lazy-loaded inside the createSegmentIndex function. // So we start out with a stream object that does not contain the actual // segment index, then download when createSegmentIndex is called. - const stream = this.makeStreamObject_(codecs, type, language, primary, name, - channelsCount, closedCaptions, characteristics, forced, sampleRate, - spatialAudio); + const stream = this.makeStreamObject_(codecs, type, languageValue, primary, + name, channelsCount, closedCaptions, characteristics, forced, + sampleRate, spatialAudio); const streamInfo = { stream, type, @@ -2013,7 +2013,7 @@ shaka.hls.HlsParser = class { const wasLive = this.isLive_(); const realStreamInfo = await this.convertParsedPlaylistIntoStreamInfo_( playlist, verbatimMediaPlaylistUri, absoluteMediaPlaylistUri, codecs, - type, language, primary, name, channelsCount, closedCaptions, + type, languageValue, primary, name, channelsCount, closedCaptions, characteristics, forced, sampleRate, spatialAudio); if (abortSignal.aborted) { return; @@ -2250,7 +2250,7 @@ shaka.hls.HlsParser = class { * @param {string} absoluteMediaPlaylistUri * @param {string} codecs * @param {string} type - * @param {string} language + * @param {?string} languageValue * @param {boolean} primary * @param {?string} name * @param {?number} channelsCount @@ -2264,7 +2264,7 @@ shaka.hls.HlsParser = class { * @private */ async convertParsedPlaylistIntoStreamInfo_(playlist, verbatimMediaPlaylistUri, - absoluteMediaPlaylistUri, codecs, type, language, primary, name, + absoluteMediaPlaylistUri, codecs, type, languageValue, primary, name, channelsCount, closedCaptions, characteristics, forced, sampleRate, spatialAudio, mimeType = undefined) { if (playlist.type != shaka.hls.PlaylistType.MEDIA) { @@ -2335,9 +2335,9 @@ shaka.hls.HlsParser = class { const {nextMediaSequence, nextPart} = this.getNextMediaSequenceAndPart_(mediaSequenceNumber, segments); - const stream = this.makeStreamObject_(codecs, type, language, primary, name, - channelsCount, closedCaptions, characteristics, forced, sampleRate, - spatialAudio); + const stream = this.makeStreamObject_(codecs, type, languageValue, primary, + name, channelsCount, closedCaptions, characteristics, forced, + sampleRate, spatialAudio); stream.segmentIndex = segmentIndex; stream.encrypted = encrypted; stream.drmInfos = drmInfos; @@ -2414,7 +2414,7 @@ shaka.hls.HlsParser = class { * manually on the object after creation. * @param {string} codecs * @param {string} type - * @param {string} language + * @param {?string} languageValue * @param {boolean} primary * @param {?string} name * @param {?number} channelsCount @@ -2426,7 +2426,7 @@ shaka.hls.HlsParser = class { * @return {!shaka.extern.Stream} * @private */ - makeStreamObject_(codecs, type, language, primary, name, channelsCount, + makeStreamObject_(codecs, type, languageValue, primary, name, channelsCount, closedCaptions, characteristics, forced, sampleRate, spatialAudio) { // Fill out a "best-guess" mimeType, for now. It will be replaced once the // stream is lazy-loaded. @@ -2445,7 +2445,8 @@ shaka.hls.HlsParser = class { encrypted: false, drmInfos: [], keyIds: new Set(), - language, + language: this.getLanguage_(languageValue), + originalLanguage: languageValue, label: name, // For historical reasons, since before "originalId". type, primary, diff --git a/lib/mss/mss_parser.js b/lib/mss/mss_parser.js index 6b99a056b2..b643dbd773 100644 --- a/lib/mss/mss_parser.js +++ b/lib/mss/mss_parser.js @@ -17,6 +17,7 @@ goog.require('shaka.media.SegmentReference'); goog.require('shaka.mss.ContentProtection'); goog.require('shaka.net.NetworkingEngine'); goog.require('shaka.util.Error'); +goog.require('shaka.util.LanguageUtils'); goog.require('shaka.util.ManifestParserUtils'); goog.require('shaka.util.Mp4Generator'); goog.require('shaka.util.OperationManager'); @@ -513,7 +514,8 @@ shaka.mss.MssParser = class { encrypted: drmInfos.length > 0, drmInfos: drmInfos, keyIds: new Set(), - language: lang || 'und', + language: shaka.util.LanguageUtils.normalize(lang || 'und'), + originalLanguage: lang, label: '', type: '', primary: false, diff --git a/lib/offline/indexeddb/v1_storage_cell.js b/lib/offline/indexeddb/v1_storage_cell.js index 3b17603617..f8893f5d40 100644 --- a/lib/offline/indexeddb/v1_storage_cell.js +++ b/lib/offline/indexeddb/v1_storage_cell.js @@ -159,6 +159,7 @@ shaka.offline.indexeddb.V1StorageCell = class hdr: undefined, kind: old.kind, language: old.language, + originalLanguage: old.language || null, label: old.label, width: old.width, height: old.height, diff --git a/lib/offline/indexeddb/v2_storage_cell.js b/lib/offline/indexeddb/v2_storage_cell.js index 33d9645911..47b201a649 100644 --- a/lib/offline/indexeddb/v2_storage_cell.js +++ b/lib/offline/indexeddb/v2_storage_cell.js @@ -108,6 +108,7 @@ shaka.offline.indexeddb.V2StorageCell = class hdr: undefined, kind: old.kind, language: old.language, + originalLanguage: old.language || null, label: old.label, width: old.width, height: old.height, diff --git a/lib/offline/manifest_converter.js b/lib/offline/manifest_converter.js index 3e56dab90e..dbce6e8e07 100644 --- a/lib/offline/manifest_converter.js +++ b/lib/offline/manifest_converter.js @@ -194,6 +194,7 @@ shaka.offline.ManifestConverter = class { drmInfos: [], keyIds: streamDB.keyIds, language: streamDB.language, + originalLanguage: streamDB.originalLanguage || null, label: streamDB.label, type: streamDB.type, primary: streamDB.primary, diff --git a/lib/offline/storage.js b/lib/offline/storage.js index 10c4346be5..4e489257db 100644 --- a/lib/offline/storage.js +++ b/lib/offline/storage.js @@ -1308,6 +1308,7 @@ shaka.offline.Storage = class { hdr: stream.hdr, kind: stream.kind, language: stream.language, + originalLanguage: stream.originalLanguage, label: stream.label, width: stream.width || null, height: stream.height || null, diff --git a/lib/player.js b/lib/player.js index d7ecc9f9e9..919c1fbbf3 100644 --- a/lib/player.js +++ b/lib/player.js @@ -2382,6 +2382,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { drmInfos: [], // Filled in by DrmEngine config. keyIds: new Set(), language: 'und', + originalLanguage: null, label: null, type: ContentType.VIDEO, primary: false, @@ -4873,6 +4874,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { drmInfos: [], keyIds: new Set(), language: language, + originalLanguage: language, label: label || null, type: ContentType.TEXT, primary: false, @@ -5024,6 +5026,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { drmInfos: [], keyIds: new Set(), language: 'und', + originalLanguage: null, label: null, type: ContentType.IMAGE, primary: false, @@ -5433,6 +5436,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget { // Add an empty segmentIndex, for the benefit of the period combiner // in our builtin DASH parser. const segmentIndex = new shaka.media.MetaSegmentIndex(); + const language = video.closedCaptions.get(id); const textStream = { id: this.nextExternalStreamId_++, // A globally unique ID. originalId: id, // The CC ID string, like 'CC1', 'CC3', etc. @@ -5444,7 +5448,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget { encrypted: false, drmInfos: [], keyIds: new Set(), - language: video.closedCaptions.get(id), + language, + originalLanguage: language, label: null, type: ContentType.TEXT, primary: false, diff --git a/lib/util/periods.js b/lib/util/periods.js index 3a3e6939a0..ef976297b6 100644 --- a/lib/util/periods.js +++ b/lib/util/periods.js @@ -1500,6 +1500,7 @@ shaka.util.PeriodCombiner = class { mimeType: '', codecs: '', language: '', + originalLanguage: null, label: null, width: null, height: null, @@ -1538,6 +1539,7 @@ shaka.util.PeriodCombiner = class { drmInfos: [], keyIds: new Set(), language: '', + originalLanguage: null, label: null, type, primary: false, diff --git a/lib/util/stream_utils.js b/lib/util/stream_utils.js index 2aa0338863..9640c727e8 100644 --- a/lib/util/stream_utils.js +++ b/lib/util/stream_utils.js @@ -1149,6 +1149,7 @@ shaka.util.StreamUtils = class { originalTextId: null, originalImageId: null, accessibilityPurpose: null, + originalLanguage: null, }; if (video) { @@ -1172,6 +1173,7 @@ shaka.util.StreamUtils = class { track.label = audio.label; track.audioRoles = audio.roles; track.accessibilityPurpose = audio.accessibilityPurpose; + track.originalLanguage = audio.originalLanguage; } return track; @@ -1222,6 +1224,7 @@ shaka.util.StreamUtils = class { originalTextId: stream.originalId, originalImageId: null, accessibilityPurpose: stream.accessibilityPurpose, + originalLanguage: stream.originalLanguage, }; return track; @@ -1297,6 +1300,7 @@ shaka.util.StreamUtils = class { originalTextId: null, originalImageId: stream.originalId, accessibilityPurpose: null, + originalLanguage: null, }; return track; @@ -1418,6 +1422,7 @@ shaka.util.StreamUtils = class { originalTextId: null, originalImageId: null, accessibilityPurpose: null, + originalLanguage: html5Track.language, }; return track; diff --git a/test/dash/dash_parser_manifest_unit.js b/test/dash/dash_parser_manifest_unit.js index 9d635cf7d3..6fd0cfb2e8 100644 --- a/test/dash/dash_parser_manifest_unit.js +++ b/test/dash/dash_parser_manifest_unit.js @@ -140,7 +140,7 @@ describe('DashParser Manifest', () => { ' ', ' ', ' ', + ' lang="spa" label="spanish">', ' ', ' ', ' ', @@ -194,6 +194,7 @@ describe('DashParser Manifest', () => { }); manifest.addPartialTextStream((stream) => { stream.language = 'es'; + stream.originalLanguage = 'spa'; stream.label = 'spanish'; stream.primary = true; stream.mimeType = 'text/vtt'; diff --git a/test/hls/hls_parser_unit.js b/test/hls/hls_parser_unit.js index 20bb088400..2e95381ca6 100644 --- a/test/hls/hls_parser_unit.js +++ b/test/hls/hls_parser_unit.js @@ -191,6 +191,7 @@ describe('HlsParser', () => { }); variant.addPartialStream(ContentType.AUDIO, (stream) => { stream.language = 'en'; + stream.originalLanguage = 'eng'; stream.channelsCount = 16; stream.audioSamplingRate = 48000; stream.spatialAudio = true; @@ -199,11 +200,13 @@ describe('HlsParser', () => { }); manifest.addPartialTextStream((stream) => { stream.language = 'en'; + stream.originalLanguage = 'eng'; stream.kind = TextStreamKind.SUBTITLE; stream.mime('text/vtt', ''); }); manifest.addPartialTextStream((stream) => { stream.language = 'es'; + stream.originalLanguage = 'es'; stream.kind = TextStreamKind.SUBTITLE; stream.mime('text/vtt', ''); }); @@ -269,6 +272,7 @@ describe('HlsParser', () => { }); variant.addPartialStream(ContentType.AUDIO, (stream) => { stream.language = 'en'; + stream.originalLanguage = 'eng'; stream.channelsCount = 16; stream.spatialAudio = true; stream.mime('audio/mp4', 'mp4a'); @@ -276,11 +280,13 @@ describe('HlsParser', () => { }); manifest.addPartialTextStream((stream) => { stream.language = 'en'; + stream.originalLanguage = 'eng'; stream.kind = TextStreamKind.SUBTITLE; stream.mime('text/vtt', ''); }); manifest.addPartialTextStream((stream) => { stream.language = 'es'; + stream.originalLanguage = 'es'; stream.kind = TextStreamKind.SUBTITLE; stream.mime('text/vtt', ''); }); @@ -934,6 +940,7 @@ describe('HlsParser', () => { }); variant.addPartialStream(ContentType.AUDIO, (stream) => { stream.language = 'en'; + stream.originalLanguage = 'eng'; }); }); manifest.addPartialVariant((variant) => { @@ -944,6 +951,7 @@ describe('HlsParser', () => { }); variant.addPartialStream(ContentType.AUDIO, (stream) => { stream.language = 'fr'; + stream.originalLanguage = 'fr'; }); }); manifest.sequenceMode = sequenceMode; @@ -981,6 +989,7 @@ describe('HlsParser', () => { variant.addPartialStream(ContentType.VIDEO); variant.addPartialStream(ContentType.AUDIO, (stream) => { stream.language = 'en'; + stream.originalLanguage = 'en'; }); }); manifest.addPartialVariant((variant) => { @@ -988,6 +997,7 @@ describe('HlsParser', () => { variant.addPartialStream(ContentType.VIDEO); variant.addPartialStream(ContentType.AUDIO, (stream) => { stream.language = 'fr'; + stream.originalLanguage = 'fr'; }); }); manifest.sequenceMode = sequenceMode; @@ -1141,6 +1151,7 @@ describe('HlsParser', () => { variant.addPartialStream(ContentType.VIDEO); variant.addPartialStream(ContentType.AUDIO, (stream) => { stream.language = 'en'; + stream.originalLanguage = 'en'; }); }); manifest.addPartialVariant((variant) => { @@ -1148,6 +1159,7 @@ describe('HlsParser', () => { variant.addPartialStream(ContentType.VIDEO); variant.addPartialStream(ContentType.AUDIO, (stream) => { stream.language = 'en'; + stream.originalLanguage = 'en'; stream.roles = [ 'public.accessibility.describes-video', 'public.accessibility.describes-music-and-sound', @@ -1365,6 +1377,7 @@ describe('HlsParser', () => { }); manifest.addPartialTextStream((stream) => { stream.language = 'en'; + stream.originalLanguage = 'eng'; stream.forced = true; stream.kind = TextStreamKind.SUBTITLE; stream.mime('text/vtt', ''); @@ -1433,11 +1446,13 @@ describe('HlsParser', () => { }); manifest.addPartialTextStream((stream) => { stream.language = 'en'; + stream.originalLanguage = 'eng'; stream.kind = TextStreamKind.SUBTITLE; stream.mime('text/vtt', ''); }); manifest.addPartialTextStream((stream) => { stream.language = 'es'; + stream.originalLanguage = 'es'; stream.kind = TextStreamKind.SUBTITLE; stream.mime('text/vtt', ''); }); @@ -1566,6 +1581,7 @@ describe('HlsParser', () => { }); manifest.addPartialTextStream((stream) => { stream.language = 'en'; + stream.originalLanguage = 'eng'; stream.forced = true; stream.kind = TextStreamKind.SUBTITLE; stream.mime('text/vtt', ''); @@ -2321,6 +2337,7 @@ describe('HlsParser', () => { }); manifest.addPartialTextStream((stream) => { stream.language = 'en'; + stream.originalLanguage = 'eng'; stream.mime('application/mp4', 'stpp.ttml.im1t'); }); manifest.sequenceMode = sequenceMode; @@ -2971,6 +2988,7 @@ describe('HlsParser', () => { }); variant.addPartialStream(ContentType.AUDIO, (stream) => { stream.language = 'en'; + stream.originalLanguage = 'eng'; }); }); manifest.addPartialVariant((variant) => { @@ -3917,6 +3935,7 @@ describe('HlsParser', () => { }); variant.addPartialStream(ContentType.AUDIO, (stream) => { stream.language = 'en'; + stream.originalLanguage = 'en'; }); }); manifest.addPartialVariant((variant) => { @@ -3926,6 +3945,7 @@ describe('HlsParser', () => { }); variant.addPartialStream(ContentType.AUDIO, (stream) => { stream.language = 'fr'; + stream.originalLanguage = 'fr'; }); }); manifest.addPartialVariant((variant) => { @@ -3935,6 +3955,7 @@ describe('HlsParser', () => { }); variant.addPartialStream(ContentType.AUDIO, (stream) => { stream.language = 'en'; + stream.originalLanguage = 'en'; }); }); manifest.addPartialVariant((variant) => { @@ -3944,6 +3965,7 @@ describe('HlsParser', () => { }); variant.addPartialStream(ContentType.AUDIO, (stream) => { stream.language = 'fr'; + stream.originalLanguage = 'fr'; }); }); manifest.sequenceMode = sequenceMode; diff --git a/test/media/adaptation_set_unit.js b/test/media/adaptation_set_unit.js index 81f811d2fa..af2960ec0e 100644 --- a/test/media/adaptation_set_unit.js +++ b/test/media/adaptation_set_unit.js @@ -211,6 +211,7 @@ describe('AdaptationSet', () => { keyIds: new Set(), label: null, language: '', + originalLanguage: null, mimeType: mimeType, originalId: String(id), primary: false, diff --git a/test/offline/manifest_convert_unit.js b/test/offline/manifest_convert_unit.js index 17a1770c4a..dd25a53466 100644 --- a/test/offline/manifest_convert_unit.js +++ b/test/offline/manifest_convert_unit.js @@ -303,6 +303,7 @@ describe('ManifestConverter', () => { mimeType: '', codecs: '', language: '', + originalLanguage: null, label: null, width: null, height: null, @@ -362,6 +363,7 @@ describe('ManifestConverter', () => { hdr: undefined, kind: undefined, language: '', + originalLanguage: null, label: null, width: 250, height: 100, @@ -412,6 +414,7 @@ describe('ManifestConverter', () => { hdr: undefined, kind: undefined, language: 'en', + originalLanguage: 'en', label: null, width: null, height: null, @@ -461,6 +464,7 @@ describe('ManifestConverter', () => { hdr: undefined, kind: undefined, language: 'en', + originalLanguage: 'en', label: null, width: null, height: null, @@ -522,6 +526,7 @@ describe('ManifestConverter', () => { encrypted: streamDb.encrypted, keyIds: streamDb.keyIds, language: streamDb.language, + originalLanguage: streamDb.originalLanguage, label: streamDb.label, type: streamDb.type, primary: streamDb.primary, diff --git a/test/offline/storage_integration.js b/test/offline/storage_integration.js index db865d2cfe..f6de41348d 100644 --- a/test/offline/storage_integration.js +++ b/test/offline/storage_integration.js @@ -1374,6 +1374,7 @@ filterDescribe('Storage', storageSupport, () => { type: 'variant', bandwidth: bandwidth, language: language, + originalLanguage: language, label: null, kind: null, width: height * (16 / 9), @@ -1419,6 +1420,7 @@ filterDescribe('Storage', storageSupport, () => { type: 'text', bandwidth: 1000, language: language, + originalLanguage: language, label: null, kind: null, width: null, diff --git a/test/player_unit.js b/test/player_unit.js index 54c892aff9..45b942a4e0 100644 --- a/test/player_unit.js +++ b/test/player_unit.js @@ -1542,6 +1542,7 @@ describe('Player', () => { manifest.addTextStream(50, (stream) => { stream.originalId = 'text-es'; stream.language = 'es'; + stream.originalLanguage = 'es'; stream.label = 'Spanish'; stream.bandwidth = 10; stream.mimeType = 'text/vtt'; @@ -1550,6 +1551,7 @@ describe('Player', () => { manifest.addTextStream(51, (stream) => { stream.originalId = 'text-en'; stream.language = 'en'; + stream.originalLanguage = 'en'; stream.label = 'English'; stream.bandwidth = 10; stream.mimeType = 'application/ttml+xml'; @@ -1559,6 +1561,7 @@ describe('Player', () => { manifest.addTextStream(52, (stream) => { stream.originalId = 'text-commentary'; stream.language = 'en'; + stream.originalLanguage = 'en'; stream.label = 'English'; stream.bandwidth = 10; stream.mimeType = 'application/ttml+xml'; @@ -1584,6 +1587,7 @@ describe('Player', () => { type: 'variant', bandwidth: 1300, language: 'en', + originalLanguage: 'en', label: null, kind: null, width: 100, @@ -1621,6 +1625,7 @@ describe('Player', () => { type: 'variant', bandwidth: 2300, language: 'en', + originalLanguage: 'en', label: null, kind: null, width: 200, @@ -1658,6 +1663,7 @@ describe('Player', () => { type: 'variant', bandwidth: 1100, language: 'en', + originalLanguage: 'en', label: null, kind: null, width: 100, @@ -1695,6 +1701,7 @@ describe('Player', () => { type: 'variant', bandwidth: 2100, language: 'en', + originalLanguage: 'en', label: null, kind: null, width: 200, @@ -1732,6 +1739,7 @@ describe('Player', () => { type: 'variant', bandwidth: 1100, language: 'en', + originalLanguage: 'en', label: null, kind: null, width: 100, @@ -1769,6 +1777,7 @@ describe('Player', () => { type: 'variant', bandwidth: 2100, language: 'en', + originalLanguage: 'en', label: null, kind: null, width: 200, @@ -1807,6 +1816,7 @@ describe('Player', () => { bandwidth: 1100, language: 'es', label: 'es-label', + originalLanguage: 'es', kind: null, width: 100, height: 200, @@ -1844,6 +1854,7 @@ describe('Player', () => { bandwidth: 2100, language: 'es', label: 'es-label', + originalLanguage: 'es', kind: null, width: 200, height: 400, @@ -1882,6 +1893,7 @@ describe('Player', () => { active: true, type: ContentType.TEXT, language: 'es', + originalLanguage: 'es', label: 'Spanish', kind: 'caption', mimeType: 'text/vtt', @@ -1919,6 +1931,7 @@ describe('Player', () => { active: false, type: ContentType.TEXT, language: 'en', + originalLanguage: 'en', label: 'English', kind: 'caption', mimeType: 'application/ttml+xml', @@ -1956,6 +1969,7 @@ describe('Player', () => { active: false, type: ContentType.TEXT, language: 'en', + originalLanguage: 'en', label: 'English', kind: 'caption', mimeType: 'application/ttml+xml', @@ -1996,6 +2010,7 @@ describe('Player', () => { active: false, type: ContentType.IMAGE, language: '', + originalLanguage: null, label: null, kind: null, mimeType: 'image/jpeg', diff --git a/test/test/util/manifest_generator.js b/test/test/util/manifest_generator.js index ec750ef018..04238cdfcd 100644 --- a/test/test/util/manifest_generator.js +++ b/test/test/util/manifest_generator.js @@ -455,7 +455,7 @@ shaka.test.ManifestGenerator.Stream = class { * @param {boolean} isPartial * @param {?number} id * @param {shaka.util.ManifestParserUtils.ContentType} type - * @param {string=} lang + * @param {?string=} lang * @param {string=} label */ constructor(manifest, isPartial, id, type, lang, label) { @@ -526,7 +526,9 @@ shaka.test.ManifestGenerator.Stream = class { /** @type {!Set.} */ this.keyIds = new Set(); /** @type {string} */ - this.language = lang || 'und'; + this.language = shaka.util.LanguageUtils.normalize(lang || 'und'); + /** @type {?string} */ + this.originalLanguage = lang || null; /** @type {?string} */ this.label = label || null; /** @type {boolean} */ diff --git a/test/test/util/offline_utils.js b/test/test/util/offline_utils.js index 7ca655e848..8106864607 100644 --- a/test/test/util/offline_utils.js +++ b/test/test/util/offline_utils.js @@ -42,6 +42,7 @@ shaka.test.OfflineUtils = class { pixelAspectRatio: undefined, kind: undefined, language: '', + originalLanguage: null, label: null, width: null, height: null, diff --git a/test/test/util/streaming_engine_util.js b/test/test/util/streaming_engine_util.js index 9620c90523..6e9310a8b3 100644 --- a/test/test/util/streaming_engine_util.js +++ b/test/test/util/streaming_engine_util.js @@ -402,6 +402,7 @@ shaka.test.StreamingEngineUtil = class { type: ContentType.AUDIO, label: '', language: 'und', + originalLanguage: null, drmInfos: [], encrypted: false, keyIds: new Set(), @@ -441,6 +442,7 @@ shaka.test.StreamingEngineUtil = class { type: ContentType.VIDEO, label: '', language: 'und', + originalLanguage: null, drmInfos: [], encrypted: false, keyIds: new Set(), @@ -477,6 +479,7 @@ shaka.test.StreamingEngineUtil = class { type: ManifestParserUtils.ContentType.TEXT, label: '', language: 'und', + originalLanguage: null, drmInfos: [], encrypted: false, keyIds: new Set(), diff --git a/test/util/periods_unit.js b/test/util/periods_unit.js index 4fe27da73a..e3ab2e5c4c 100644 --- a/test/util/periods_unit.js +++ b/test/util/periods_unit.js @@ -347,6 +347,7 @@ describe('PeriodCombiner', () => { const spanish = variants.find( (v) => v.video.height == 1080 && v.language == 'es'); expect(spanish.audio.originalId).toBe('es*,en,es'); + expect(spanish.audio.originalLanguage).toBe('es'); expect(spanish.video.originalId).toBe('1080,480,1080'); // The French track is primary in the last period and has English 480p in @@ -354,6 +355,7 @@ describe('PeriodCombiner', () => { const french = variants.find( (v) => v.video.height == 1080 && v.language == 'fr'); expect(french.audio.originalId).toBe('fr,en,fr*'); + expect(french.audio.originalLanguage).toBe('fr'); expect(french.video.originalId).toBe('1080,480,1080'); // Because there's no English in the first or last periods, the English @@ -361,6 +363,7 @@ describe('PeriodCombiner', () => { const english = variants.find( (v) => v.video.height == 1080 && v.language == 'en'); expect(english.audio.originalId).toBe('es*,en,fr*'); + expect(english.audio.originalLanguage).toBe('en'); expect(english.video.originalId).toBe('1080,480,1080'); }); @@ -406,7 +409,9 @@ describe('PeriodCombiner', () => { const english = variants.find( (v) => v.video.height == 1080 && v.language == 'en'); expect(spanish.audio.originalId).toBe('es,en'); + expect(spanish.audio.originalLanguage).toBe('es'); expect(english.audio.originalId).toBe('es,en'); + expect(english.audio.originalLanguage).toBe('en'); }); it('Multiple representations of the same resolution', async () => { @@ -680,7 +685,7 @@ describe('PeriodCombiner', () => { ], textStreams: [ makeTextStream('en'), - makeTextStream('es'), + makeTextStream('spa'), ], imageStreams: [], }, @@ -703,7 +708,9 @@ describe('PeriodCombiner', () => { const spanish = textStreams.find((s) => s.language == 'es'); const english = textStreams.find((s) => s.language == 'en'); expect(spanish.originalId).toBe(',,es'); + expect(spanish.originalLanguage).toBe('spa'); expect(english.originalId).toBe('en,,en'); + expect(english.originalLanguage).toBe('en'); }); it('handles image track gaps', async () => { @@ -1547,7 +1554,8 @@ describe('PeriodCombiner', () => { language); streamGenerator.primary = primary; streamGenerator.channelsCount = channels; - streamGenerator.originalId = primary ? language + '*' : language; + streamGenerator.originalId = primary ? + streamGenerator.language + '*' : streamGenerator.language; if (channels != 2) { streamGenerator.originalId += `-${channels}c`; } @@ -1568,7 +1576,8 @@ describe('PeriodCombiner', () => { /* type= */ shaka.util.ManifestParserUtils.ContentType.TEXT, language); streamGenerator.primary = primary; - streamGenerator.originalId = primary ? language + '*' : language; + streamGenerator.originalId = primary ? + streamGenerator.language + '*' : streamGenerator.language; return streamGenerator.build_(); } @@ -1583,8 +1592,7 @@ describe('PeriodCombiner', () => { /* manifest= */ null, /* isPartial= */ false, /* id= */ nextId++, - /* type= */ shaka.util.ManifestParserUtils.ContentType.IMAGE, - /* lang= */ 'und'); + /* type= */ shaka.util.ManifestParserUtils.ContentType.IMAGE); streamGenerator.size(width, height); streamGenerator.originalId = height.toString(); streamGenerator.mime('image/jpeg');