diff --git a/karma.conf.js b/karma.conf.js index adba5beb44..a01444c678 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -254,6 +254,7 @@ module.exports = (config) => { {pattern: 'test/test/assets/hls-ts-muxed-ac3-h264/*', included: false}, {pattern: 'test/test/assets/hls-ts-muxed-mp3-h264/*', included: false}, {pattern: 'test/test/assets/hls-ts-muxed-ec3-h264/*', included: false}, + {pattern: 'test/test/assets/hls-ts-raw-aac/*', included: false}, {pattern: 'test/test/assets/hls-ts-rollover/*', included: false}, {pattern: 'dist/shaka-player.ui.js', included: false}, {pattern: 'dist/locales.js', included: false}, diff --git a/lib/transmuxer/ts_transmuxer.js b/lib/transmuxer/ts_transmuxer.js index 7008541e76..6407ae3b79 100644 --- a/lib/transmuxer/ts_transmuxer.js +++ b/lib/transmuxer/ts_transmuxer.js @@ -7,6 +7,7 @@ goog.provide('shaka.transmuxer.TsTransmuxer'); goog.require('shaka.media.Capabilities'); +goog.require('shaka.transmuxer.AacTransmuxer'); goog.require('shaka.transmuxer.Ac3'); goog.require('shaka.transmuxer.ADTS'); goog.require('shaka.transmuxer.Ec3'); @@ -16,6 +17,7 @@ goog.require('shaka.transmuxer.MpegAudio'); goog.require('shaka.transmuxer.TransmuxerEngine'); goog.require('shaka.util.BufferUtils'); goog.require('shaka.util.Error'); +goog.require('shaka.util.Id3Utils'); goog.require('shaka.util.ManifestParserUtils'); goog.require('shaka.util.MimeUtils'); goog.require('shaka.util.Mp4Generator'); @@ -45,6 +47,9 @@ shaka.transmuxer.TsTransmuxer = class { /** @private {?shaka.util.TsParser} */ this.tsParser_ = null; + + /** @private {?shaka.transmuxer.AacTransmuxer} */ + this.aacTransmuxer_ = null; } @@ -54,6 +59,10 @@ shaka.transmuxer.TsTransmuxer = class { */ destroy() { this.initSegments.clear(); + + if (this.aacTransmuxer_) { + this.aacTransmuxer_.destroy(); + } } @@ -187,14 +196,44 @@ shaka.transmuxer.TsTransmuxer = class { */ transmux(data, stream, reference, duration, contentType) { const ContentType = shaka.util.ManifestParserUtils.ContentType; + const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils; + + const uint8ArrayData = shaka.util.BufferUtils.toUint8(data); + + if (contentType == ContentType.AUDIO && + !shaka.util.TsParser.probe(uint8ArrayData)) { + const id3Data = shaka.util.Id3Utils.getID3Data(uint8ArrayData); + let offset = id3Data.length; + for (; offset < uint8ArrayData.length; offset++) { + if (shaka.transmuxer.MpegAudio.probe(uint8ArrayData, offset)) { + return Promise.reject(new shaka.util.Error( + shaka.util.Error.Severity.CRITICAL, + shaka.util.Error.Category.MEDIA, + shaka.util.Error.Code.TRANSMUXING_FAILED)); + } + } + offset = id3Data.length; + for (; offset < uint8ArrayData.length; offset++) { + if (shaka.transmuxer.ADTS.probe(uint8ArrayData, offset)) { + if (!this.aacTransmuxer_) { + this.aacTransmuxer_ = + new shaka.transmuxer.AacTransmuxer('audio/aac'); + } + return this.aacTransmuxer_ + .transmux(data, stream, reference, duration, contentType); + } + } + return Promise.reject(new shaka.util.Error( + shaka.util.Error.Severity.CRITICAL, + shaka.util.Error.Category.MEDIA, + shaka.util.Error.Code.TRANSMUXING_FAILED)); + } + if (!this.tsParser_) { this.tsParser_ = new shaka.util.TsParser(); } else { this.tsParser_.clearData(); } - const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils; - - const uint8ArrayData = shaka.util.BufferUtils.toUint8(data); const tsParser = this.tsParser_.parse(uint8ArrayData); const streamInfos = []; diff --git a/test/test/assets/hls-ts-raw-aac/fileSequence0.ts b/test/test/assets/hls-ts-raw-aac/fileSequence0.ts new file mode 100644 index 0000000000..220a47823b Binary files /dev/null and b/test/test/assets/hls-ts-raw-aac/fileSequence0.ts differ diff --git a/test/test/assets/hls-ts-raw-aac/fileSequence1.ts b/test/test/assets/hls-ts-raw-aac/fileSequence1.ts new file mode 100644 index 0000000000..25a5739d35 Binary files /dev/null and b/test/test/assets/hls-ts-raw-aac/fileSequence1.ts differ diff --git a/test/test/assets/hls-ts-raw-aac/fileSequence2.ts b/test/test/assets/hls-ts-raw-aac/fileSequence2.ts new file mode 100644 index 0000000000..db4173420f Binary files /dev/null and b/test/test/assets/hls-ts-raw-aac/fileSequence2.ts differ diff --git a/test/test/assets/hls-ts-raw-aac/fileSequence3.ts b/test/test/assets/hls-ts-raw-aac/fileSequence3.ts new file mode 100644 index 0000000000..4097eae9ee Binary files /dev/null and b/test/test/assets/hls-ts-raw-aac/fileSequence3.ts differ diff --git a/test/test/assets/hls-ts-raw-aac/index.m3u8 b/test/test/assets/hls-ts-raw-aac/index.m3u8 new file mode 100644 index 0000000000..80534b2527 --- /dev/null +++ b/test/test/assets/hls-ts-raw-aac/index.m3u8 @@ -0,0 +1,4 @@ +#EXTM3U +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-STREAM-INF:BANDWIDTH=128000,CODECS="mp4a.40.2" +playlist.m3u8 \ No newline at end of file diff --git a/test/test/assets/hls-ts-raw-aac/playlist.m3u8 b/test/test/assets/hls-ts-raw-aac/playlist.m3u8 new file mode 100644 index 0000000000..d9bc663957 --- /dev/null +++ b/test/test/assets/hls-ts-raw-aac/playlist.m3u8 @@ -0,0 +1,14 @@ +#EXTM3U +#EXT-X-TARGETDURATION:11 +#EXT-X-VERSION:3 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXTINF:9.98458, +fileSequence0.ts +#EXTINF:9.98458, +fileSequence1.ts +#EXTINF:9.98459, +fileSequence2.ts +#EXTINF:10.03102, +fileSequence3.ts +#EXT-X-ENDLIST diff --git a/test/transmuxer/transmuxer_integration.js b/test/transmuxer/transmuxer_integration.js index 76a1dada17..624aa55c3c 100644 --- a/test/transmuxer/transmuxer_integration.js +++ b/test/transmuxer/transmuxer_integration.js @@ -189,6 +189,22 @@ describe('Transmuxer Player', () => { await player.unload(); }); + it('raw AAC with ts extension', async () => { + await player.load('/base/test/test/assets/hls-ts-raw-aac/index.m3u8'); + await video.play(); + expect(player.isLive()).toBe(false); + + // Wait for the video to start playback. If it takes longer than 10 + // seconds, fail the test. + await waiter.waitForMovementOrFailOnTimeout(video, 10); + + // Play for 15 seconds, but stop early if the video ends. If it takes + // longer than 45 seconds, fail the test. + await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 15, 45); + + await player.unload(); + }); + it('AAC in TS', async () => { await player.load('/base/test/test/assets/hls-ts-aac/playlist.m3u8'); await video.play();