Skip to content

Commit

Permalink
feat: Add AAC transmuxer (shaka-project#4632)
Browse files Browse the repository at this point in the history
  • Loading branch information
avelad authored Oct 28, 2022
1 parent 0d67ecd commit 8623a5d
Show file tree
Hide file tree
Showing 20 changed files with 128 additions and 56 deletions.
4 changes: 2 additions & 2 deletions demo/common/assets.js
Original file line number Diff line number Diff line change
Expand Up @@ -1306,7 +1306,7 @@ shakaAssets.testAssets = [
.setExtraConfig({
streaming: {
useNativeHlsOnSafari: false,
forceTransmuxTS: true,
forceTransmux: true,
},
lcevc: {
enabled: true,
Expand All @@ -1331,7 +1331,7 @@ shakaAssets.testAssets = [
.setExtraConfig({
streaming: {
useNativeHlsOnSafari: false,
forceTransmuxTS: true,
forceTransmux: true,
},
lcevc: {
enabled: true,
Expand Down
2 changes: 1 addition & 1 deletion demo/common/message_ids.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ shakaDemo.MessageIds = {
ENABLED: 'DEMO_ENABLED',
FAST_HALF_LIFE: 'DEMO_FAST_HALF_LIFE',
FORCE_HTTPS: 'DEMO_FORCE_HTTPS',
FORCE_TRANSMUX_TS: 'DEMO_FORCE_TRANSMUX_TS',
FORCE_TRANSMUX: 'DEMO_FORCE_TRANSMUX',
FUZZ_FACTOR: 'DEMO_FUZZ_FACTOR',
GAP_DETECTION_THRESHOLD: 'DEMO_GAP_DETECTION_THRESHOLD',
IGNORE_DASH_EMPTY_ADAPTATION_SET: 'DEMO_IGNORE_DASH_EMPTY_ADAPTATION_SET',
Expand Down
4 changes: 2 additions & 2 deletions demo/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,8 @@ shakaDemo.Config = class {
this.latestInput_.input().checked = true;
}

this.addBoolInput_(MessageIds.FORCE_TRANSMUX_TS,
'streaming.forceTransmuxTS')
this.addBoolInput_(MessageIds.FORCE_TRANSMUX,
'streaming.forceTransmux')
.addBoolInput_(MessageIds.START_AT_SEGMENT_BOUNDARY,
'streaming.startAtSegmentBoundary')
.addBoolInput_(MessageIds.IGNORE_TEXT_FAILURES,
Expand Down
2 changes: 1 addition & 1 deletion demo/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
"DEMO_FAIRPLAY": "Fairplay DRM",
"DEMO_FAST_HALF_LIFE": "Fast half life",
"DEMO_FORCE_HTTPS": "Force HTTPS",
"DEMO_FORCE_TRANSMUX_TS": "Force Transmux TS",
"DEMO_FORCE_TRANSMUX": "Force Transmux",
"DEMO_FRONT_INTRO_DISMISS": "Dismiss",
"DEMO_FRONT_INTRO_ONE": "This is a demo of Google's Shaka Player, a JavaScript library for adaptive video streaming.",
"DEMO_FRONT_INTRO_TWO": "Choose a video to playback; more assets are available via the \"all content\" tab.",
Expand Down
4 changes: 2 additions & 2 deletions demo/locales/source.json
Original file line number Diff line number Diff line change
Expand Up @@ -331,9 +331,9 @@
"description": "The name of a configuration value.",
"message": "Force [JARGON:HTTPS]"
},
"DEMO_FORCE_TRANSMUX_TS": {
"DEMO_FORCE_TRANSMUX": {
"description": "The name of a configuration value.",
"message": "Force Transmux [JARGON:MPEG-2 TS]"
"message": "Force Transmux"
},
"DEMO_FRONT_INTRO_DISMISS": {
"description": "A button allowing users to dismiss the intro message.",
Expand Down
2 changes: 1 addition & 1 deletion docs/tutorials/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ shaka.net.NetworkingEngine.registerScheme('file', shaka.net.HttpXHRPlugin);
contain said captions. Edge and Chromecast, however, have native TS support and
thus are not required to transmux.
In order to force those platforms to transmux, set the
{@link shaka.extern.StreamingConfiguration|`.streaming.forceTransmuxTS`}
{@link shaka.extern.StreamingConfiguration|`.streaming.forceTransmux`}
configuration to true.

<hr>
Expand Down
6 changes: 6 additions & 0 deletions docs/tutorials/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,9 @@ application:
clearly-named `lineBreak` (deprecated in v3.1.0)
- `IUIElement` plugins must have a `release()` method (not `destroy()`)
(deprecated in v3.0.0)

## v4.3

- Configuration changes:
- `streaming.forceTransmuxTS` has been deprecated and replaced by
`streaming.forceTransmux`
14 changes: 7 additions & 7 deletions externs/shaka/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -947,7 +947,7 @@ shaka.extern.ManifestConfiguration;
* startAtSegmentBoundary: boolean,
* gapDetectionThreshold: number,
* durationBackoff: number,
* forceTransmuxTS: boolean,
* forceTransmux: boolean,
* safeSeekOffset: number,
* stallEnabled: boolean,
* stallThreshold: number,
Expand Down Expand Up @@ -1010,12 +1010,12 @@ shaka.extern.ManifestConfiguration;
* seek to when the user tries to seek to or start playback at the duration.
* To disable this behavior, the config can be set to 0. We recommend using
* the default value unless you have a good reason not to.
* @property {boolean} forceTransmuxTS
* If this is <code>true</code>, we will transmux TS content even if not
* strictly necessary for the assets to be played. Shaka Player currently
* only supports CEA 708 captions by transmuxing, so this value is necessary
* for enabling them on platforms with native TS support like Edge or
* Chromecast. This value defaults to <code>false</code>.
* @property {boolean} forceTransmux
* If this is <code>true</code>, we will transmux AAC and TS content even if
* not strictly necessary for the assets to be played. Shaka Player
* currently only supports CEA 708 captions by transmuxing, so this value is
* necessary for enabling them on platforms with native TS support like Edge
* or Chromecast. This value defaults to <code>false</code>.
* @property {number} safeSeekOffset
* The amount of seconds that should be added when repositioning the playhead
* after falling out of the availability window or seek. This gives the player
Expand Down
2 changes: 1 addition & 1 deletion lib/media/drm_engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ shaka.media.DrmEngine = class {
if (shaka.media.Transmuxer.isSupported(realMimeType)) {
// This will be handled by the Transmuxer, so use the MIME type that the
// Transmuxer will produce.
return shaka.media.Transmuxer.convertTsCodecs(stream.type, realMimeType);
return shaka.media.Transmuxer.convertCodecs(stream.type, realMimeType);
}
return realMimeType;
}
Expand Down
11 changes: 6 additions & 5 deletions lib/media/media_source_engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,15 +342,16 @@ shaka.media.MediaSourceEngine = class {
* shaka.extern.Stream>} streamsByType
* A map of content types to streams. All streams must be supported
* according to MediaSourceEngine.isStreamSupported.
* @param {boolean} forceTransmuxTS
* If true, this will transmux TS content even if it is natively supported.
* @param {boolean} forceTransmux
* If true, this will transmux AAC and TS content even if it is natively
* supported.
* @param {boolean=} sequenceMode
* If true, the media segments are appended to the SourceBuffer in strict
* sequence.
*
* @return {!Promise}
*/
async init(streamsByType, forceTransmuxTS, sequenceMode=false) {
async init(streamsByType, forceTransmux, sequenceMode=false) {
const ContentType = shaka.util.ManifestParserUtils.ContentType;

await this.mediaSourceOpen_;
Expand All @@ -368,11 +369,11 @@ shaka.media.MediaSourceEngine = class {
if (contentType == ContentType.TEXT) {
this.reinitText(mimeType, sequenceMode);
} else {
if ((forceTransmuxTS || !MediaSource.isTypeSupported(mimeType)) &&
if ((forceTransmux || !MediaSource.isTypeSupported(mimeType)) &&
shaka.media.Transmuxer.isSupported(mimeType, contentType)) {
this.transmuxers_[contentType] = new shaka.media.Transmuxer();
mimeType =
shaka.media.Transmuxer.convertTsCodecs(contentType, mimeType);
shaka.media.Transmuxer.convertCodecs(contentType, mimeType);
}
const type = mimeType + this.config_.sourceBufferExtraFeatures;
const sourceBuffer = this.mediaSource_.addSourceBuffer(type);
Expand Down
4 changes: 2 additions & 2 deletions lib/media/streaming_engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -784,9 +784,9 @@ shaka.media.StreamingEngine = class {

// Init MediaSourceEngine.
const mediaSourceEngine = this.playerInterface_.mediaSourceEngine;
const forceTransmuxTS = this.config_.forceTransmuxTS;
const forceTransmux = this.config_.forceTransmux;

await mediaSourceEngine.init(streamsByType, forceTransmuxTS,
await mediaSourceEngine.init(streamsByType, forceTransmux,
this.manifest_.sequenceMode);
this.destroyer_.ensureNotDestroyed();

Expand Down
60 changes: 53 additions & 7 deletions lib/media/transmuxer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ goog.require('shaka.dependencies');

/**
* Transmuxer provides all operations for transmuxing from Transport
* Stream to MP4.
* Stream or AAC to MP4.
*
* @implements {shaka.util.IDestroyable}
*/
Expand Down Expand Up @@ -61,32 +61,50 @@ shaka.media.Transmuxer = class {


/**
* Check if the content type is Transport Stream, and if muxjs is loaded.
* Check if the content type is Transport Stream or AAC, and if muxjs is
* loaded.
* @param {string} mimeType
* @param {string=} contentType
* @return {boolean}
*/
static isSupported(mimeType, contentType) {
const Transmuxer = shaka.media.Transmuxer;

if (!shaka.dependencies.muxjs() || !Transmuxer.isTsContainer(mimeType)) {
const isTs = Transmuxer.isTsContainer(mimeType);
const isAac = Transmuxer.isAacContainer(mimeType);

if (!shaka.dependencies.muxjs() || (!isTs && !isAac)) {
return false;
}

if (isAac) {
return MediaSource.isTypeSupported(Transmuxer.convertAacCodecs_());
}

if (contentType) {
return MediaSource.isTypeSupported(
Transmuxer.convertTsCodecs(contentType, mimeType));
Transmuxer.convertTsCodecs_(contentType, mimeType));
}

const ContentType = shaka.util.ManifestParserUtils.ContentType;

const audioMime = Transmuxer.convertTsCodecs(ContentType.AUDIO, mimeType);
const videoMime = Transmuxer.convertTsCodecs(ContentType.VIDEO, mimeType);
const audioMime = Transmuxer.convertTsCodecs_(ContentType.AUDIO, mimeType);
const videoMime = Transmuxer.convertTsCodecs_(ContentType.VIDEO, mimeType);
return MediaSource.isTypeSupported(audioMime) ||
MediaSource.isTypeSupported(videoMime);
}


/**
* Check if the mimetype is 'audio/aac'.
* @param {string} mimeType
* @return {boolean}
*/
static isAacContainer(mimeType) {
return mimeType.toLowerCase().split(';')[0] == 'audio/aac';
}


/**
* Check if the mimetype contains 'mp2t'.
* @param {string} mimeType
Expand All @@ -97,13 +115,41 @@ shaka.media.Transmuxer = class {
}


/**
* For any stream, convert its codecs to MP4 codecs.
* @param {string} contentType
* @param {string} mimeType
* @return {string}
*/
static convertCodecs(contentType, mimeType) {
const Transmuxer = shaka.media.Transmuxer;
if (Transmuxer.isAacContainer(mimeType)) {
return Transmuxer.convertAacCodecs_();
} else if (Transmuxer.isTsContainer(mimeType)) {
return Transmuxer.convertTsCodecs_(contentType, mimeType);
}
return mimeType;
}


/**
* For aac stream, convert its codecs to MP4 codecs.
* @return {string}
* @private
*/
static convertAacCodecs_() {
return 'audio/mp4; codecs="mp4a.40.2"';
}


/**
* For transport stream, convert its codecs to MP4 codecs.
* @param {string} contentType
* @param {string} tsMimeType
* @return {string}
* @private
*/
static convertTsCodecs(contentType, tsMimeType) {
static convertTsCodecs_(contentType, tsMimeType) {
const ContentType = shaka.util.ManifestParserUtils.ContentType;
let mp4MimeType = tsMimeType.replace(/mp2t/i, 'mp4');
if (contentType == ContentType.AUDIO) {
Expand Down
12 changes: 11 additions & 1 deletion lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
const edge = shaka.util.Platform.isEdge() ||
shaka.util.Platform.isLegacyEdge();
if (edge) {
if (!config.streaming.forceTransmuxTS) {
if (!config.streaming.forceTransmux) {
// If forceTransmux is disabled for Microsoft Edge, LCEVC data
// is stripped out in case of a MPEG-2 TS container.
// Hence the warning for Microsoft Edge when playing content with
Expand Down Expand Up @@ -3162,6 +3162,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {

goog.asserts.assert(typeof(config) == 'object', 'Should be an object!');

// Deprecate 'streaming.forceTransmuxTS' configuration.
if (config['streaming'] && 'forceTransmuxTS' in config['streaming']) {
shaka.Deprecate.deprecateFeature(5,
'streaming.forceTransmuxTS configuration',
'Please Use streaming.forceTransmux instead.');
config['streaming']['forceTransmux'] =
config['streaming']['forceTransmuxTS'];
delete config['streaming']['forceTransmuxTS'];
}

// If lowLatencyMode is enabled, and inaccurateManifestTolerance and
// rebufferingGoal are not specified, set inaccurateManifestTolerance to 0
// and rebufferingGoal to 0.01 by default for low latency streaming.
Expand Down
6 changes: 4 additions & 2 deletions lib/util/mime_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ shaka.util.MimeUtils = class {
static getFullOrConvertedType(mimeType, codecs, contentType) {
const fullMimeType = shaka.util.MimeUtils.getFullType(mimeType, codecs);
const ContentType = shaka.util.ManifestParserUtils.ContentType;
const Transmuxer = shaka.media.Transmuxer;

if (shaka.media.Transmuxer.isTsContainer(fullMimeType)) {
if (Transmuxer.isTsContainer(fullMimeType) ||
Transmuxer.isAacContainer(fullMimeType)) {
if (shaka.dependencies.muxjs()) {
return shaka.media.Transmuxer.convertTsCodecs(
return shaka.media.Transmuxer.convertCodecs(
contentType, fullMimeType);
}
} else if (contentType == ContentType.AUDIO) {
Expand Down
2 changes: 1 addition & 1 deletion lib/util/player_configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ shaka.util.PlayerConfiguration = class {
startAtSegmentBoundary: false,
gapDetectionThreshold: 0.5,
durationBackoff: 1,
forceTransmuxTS: false,
forceTransmux: false,
// Offset by 5 seconds since Chromecast takes a few seconds to start
// playing after a seek, even when buffered.
safeSeekOffset: 5,
Expand Down
12 changes: 6 additions & 6 deletions test/media/media_source_engine_integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,9 @@ describe('MediaSourceEngine', () => {
const initObject = new Map();
initObject.set(ContentType.VIDEO, getFakeStream(metadata.video));
initObject.set(ContentType.TEXT, getFakeStream(metadata.text));
// Call with forceTransmuxTS = true, so that it will transmux even on
// Call with forceTransmux = true, so that it will transmux even on
// platforms with native TS support.
await mediaSourceEngine.init(initObject, /* forceTransmuxTS= */ true);
await mediaSourceEngine.init(initObject, /* forceTransmux= */ true);
mediaSourceEngine.setSelectedClosedCaptionId('CC1');
await append(ContentType.VIDEO, 0);

Expand All @@ -397,7 +397,7 @@ describe('MediaSourceEngine', () => {
initObject.set(videoType, getFakeStream(metadata.video));

await mediaSourceEngine.init(
initObject, /* forceTransmuxTS= */ false, /* sequenceMode= */ true);
initObject, /* forceTransmux= */ false, /* sequenceMode= */ true);
await mediaSourceEngine.setDuration(presentationDuration);
await mediaSourceEngine.setStreamProperties(
videoType,
Expand Down Expand Up @@ -432,7 +432,7 @@ describe('MediaSourceEngine', () => {
const initObject = new Map();
initObject.set(ContentType.VIDEO, getFakeStream(metadata.video));

await mediaSourceEngine.init(initObject, /* forceTransmuxTS= */ false);
await mediaSourceEngine.init(initObject, /* forceTransmux= */ false);
await mediaSourceEngine.setDuration(presentationDuration);
await appendInitWithClosedCaptions(ContentType.VIDEO);
mediaSourceEngine.setSelectedClosedCaptionId('CC1');
Expand All @@ -448,7 +448,7 @@ describe('MediaSourceEngine', () => {
const audioType = ContentType.AUDIO;
const initObject = new Map();
initObject.set(audioType, getFakeStream(metadata.audio));
await mediaSourceEngine.init(initObject, /* forceTransmuxTS= */ false);
await mediaSourceEngine.init(initObject, /* forceTransmux= */ false);
await append(ContentType.AUDIO, 0);

expect(onMetadata).toHaveBeenCalled();
Expand All @@ -464,7 +464,7 @@ describe('MediaSourceEngine', () => {
const audioType = ContentType.AUDIO;
const initObject = new Map();
initObject.set(audioType, getFakeStream(metadata.audio));
await mediaSourceEngine.init(initObject, /* forceTransmuxTS= */ false);
await mediaSourceEngine.init(initObject, /* forceTransmux= */ false);
await append(ContentType.AUDIO, 0);

expect(onMetadata).toHaveBeenCalled();
Expand Down
8 changes: 5 additions & 3 deletions test/media/media_source_engine_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,12 @@ describe('MediaSourceEngine', () => {
shaka.media.Transmuxer = /** @type {?} */ (function() {
return /** @type {?} */ (mockTransmuxer);
});
shaka.media.Transmuxer.convertTsCodecs = originalTransmuxer.convertTsCodecs;
shaka.media.Transmuxer.convertCodecs = originalTransmuxer.convertCodecs;
shaka.media.Transmuxer.isSupported = (mimeType, contentType) => {
return mimeType == 'tsMimetype';
};
shaka.media.Transmuxer.isAacContainer = originalTransmuxer.isAacContainer;
shaka.media.Transmuxer.isTsContainer = originalTransmuxer.isTsContainer;

shaka.text.TextEngine = createMockTextEngineCtor();

Expand Down Expand Up @@ -633,7 +635,7 @@ describe('MediaSourceEngine', () => {
videoSourceBuffer.mode = 'sequence';

await mediaSourceEngine.init(
initObject, /* forceTransmuxTS= */ false, /* sequenceMode= */ true);
initObject, /* forceTransmux= */ false, /* sequenceMode= */ true);

expect(videoSourceBuffer.timestampOffset).toBe(0);

Expand All @@ -659,7 +661,7 @@ describe('MediaSourceEngine', () => {
initObject.set(ContentType.VIDEO, fakeVideoStream);

await mediaSourceEngine.init(
initObject, /* forceTransmuxTS= */ false, /* sequenceMode= */ true);
initObject, /* forceTransmux= */ false, /* sequenceMode= */ true);

// First, mock the scenario where timestampOffset is set to help align
// text segments. In this case, SourceBuffer mode is still 'segments'.
Expand Down
Loading

0 comments on commit 8623a5d

Please sign in to comment.