From d9071710cfda98f743f575a8c7f8f64a0e834f84 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Fri, 8 May 2015 17:08:13 +0100 Subject: [PATCH] Read AC-3 tracks in MPEG TSs only if AC-3 playback is supported. Partly fixes #434 as the AC-3 stream will now be ignored if the audio capabilities don't allow it to be played back. --- .../exoplayer/demo/PlayerActivity.java | 5 ++-- .../demo/player/HlsRendererBuilder.java | 8 +++-- .../exoplayer/extractor/ts/TsExtractor.java | 30 +++++++++++++++++-- .../android/exoplayer/hls/HlsChunkSource.java | 16 +++++++--- 4 files changed, 49 insertions(+), 10 deletions(-) diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java index 7f17e98a59..e824ed0115 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/PlayerActivity.java @@ -229,7 +229,8 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, return new DashRendererBuilder(this, userAgent, contentUri.toString(), new WidevineTestMediaDrmCallback(contentId), debugTextView, audioCapabilities); case DemoUtil.TYPE_HLS: - return new HlsRendererBuilder(this, userAgent, contentUri.toString(), debugTextView); + return new HlsRendererBuilder(this, userAgent, contentUri.toString(), debugTextView, + audioCapabilities); case DemoUtil.TYPE_M4A: // There are no file format differences between M4A and MP4. case DemoUtil.TYPE_MP4: return new ExtractorRendererBuilder(userAgent, contentUri, debugTextView, @@ -239,7 +240,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, new Mp3Extractor()); case DemoUtil.TYPE_TS: return new ExtractorRendererBuilder(userAgent, contentUri, debugTextView, - new TsExtractor()); + new TsExtractor(0, audioCapabilities)); case DemoUtil.TYPE_AAC: return new ExtractorRendererBuilder(userAgent, contentUri, debugTextView, new AdtsExtractor()); diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java b/demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java index de1b087be8..171433c0d8 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/player/HlsRendererBuilder.java @@ -19,6 +19,7 @@ import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; import com.google.android.exoplayer.TrackRenderer; +import com.google.android.exoplayer.audio.AudioCapabilities; import com.google.android.exoplayer.chunk.VideoFormatSelectorUtil; import com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder; import com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilderCallback; @@ -56,15 +57,18 @@ public class HlsRendererBuilder implements RendererBuilder, ManifestCallback tsPayloadReaders; // Indexed by pid private final long firstSampleTimestampUs; private final ParsableBitArray tsScratch; @@ -61,14 +64,15 @@ public final class TsExtractor implements Extractor, SeekMap { private long lastPts; public TsExtractor() { - this(0); + this(0, null); } - public TsExtractor(long firstSampleTimestampUs) { + public TsExtractor(long firstSampleTimestampUs, AudioCapabilities audioCapabilities) { this.firstSampleTimestampUs = firstSampleTimestampUs; tsScratch = new ParsableBitArray(new byte[3]); tsPacketBuffer = new ParsableByteArray(TS_PACKET_SIZE); streamTypes = new SparseBooleanArray(); + allowedPassthroughStreamTypes = getPassthroughStreamTypes(audioCapabilities); tsPayloadReaders = new SparseArray(); tsPayloadReaders.put(TS_PAT_PID, new PatReader()); lastPts = Long.MIN_VALUE; @@ -173,6 +177,24 @@ public final class TsExtractor implements Extractor, SeekMap { return timeUs + timestampOffsetUs; } + /** + * Returns a sparse boolean array of stream types that can be played back based on + * {@code audioCapabilities}. + */ + private static SparseBooleanArray getPassthroughStreamTypes(AudioCapabilities audioCapabilities) { + SparseBooleanArray streamTypes = new SparseBooleanArray(); + if (audioCapabilities != null) { + if (audioCapabilities.supportsEncoding(C.ENCODING_AC3)) { + streamTypes.put(TS_STREAM_TYPE_ATSC_AC3, true); + } + if (audioCapabilities.supportsEncoding(C.ENCODING_E_AC3)) { + // TODO: Uncomment when Ac3Reader supports enhanced AC-3. + // streamTypes.put(TS_STREAM_TYPE_ATSC_E_AC3, true); + } + } + return streamTypes; + } + /** * Parses TS packet payload data. */ @@ -313,7 +335,11 @@ public final class TsExtractor implements Extractor, SeekMap { case TS_STREAM_TYPE_AAC: pesPayloadReader = new AdtsReader(output.track(TS_STREAM_TYPE_AAC)); break; + case TS_STREAM_TYPE_ATSC_E_AC3: case TS_STREAM_TYPE_ATSC_AC3: + if (!allowedPassthroughStreamTypes.get(streamType)) { + continue; + } pesPayloadReader = new Ac3Reader(output.track(streamType)); break; case TS_STREAM_TYPE_H264: diff --git a/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java b/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java index 2883782af0..7afa4be013 100644 --- a/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer.hls; import com.google.android.exoplayer.C; import com.google.android.exoplayer.MediaFormat; +import com.google.android.exoplayer.audio.AudioCapabilities; import com.google.android.exoplayer.chunk.BaseChunkSampleSourceEventListener; import com.google.android.exoplayer.chunk.Chunk; import com.google.android.exoplayer.chunk.DataChunk; @@ -125,6 +126,7 @@ public class HlsChunkSource { private final int maxHeight; private final long minBufferDurationToSwitchUpUs; private final long maxBufferDurationToSwitchDownUs; + private final AudioCapabilities audioCapabilities; /* package */ byte[] scratchSpace; /* package */ final HlsMediaPlaylist[] mediaPlaylists; @@ -140,9 +142,11 @@ public class HlsChunkSource { private byte[] encryptionIv; public HlsChunkSource(DataSource dataSource, String playlistUrl, HlsPlaylist playlist, - BandwidthMeter bandwidthMeter, int[] variantIndices, int adaptiveMode) { + BandwidthMeter bandwidthMeter, int[] variantIndices, int adaptiveMode, + AudioCapabilities audioCapabilities) { this(dataSource, playlistUrl, playlist, bandwidthMeter, variantIndices, adaptiveMode, - DEFAULT_MIN_BUFFER_TO_SWITCH_UP_MS, DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS); + DEFAULT_MIN_BUFFER_TO_SWITCH_UP_MS, DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS, + audioCapabilities); } /** @@ -160,13 +164,17 @@ public class HlsChunkSource { * for a switch to a higher quality variant to be considered. * @param maxBufferDurationToSwitchDownMs The maximum duration of media that needs to be buffered * for a switch to a lower quality variant to be considered. + * @param audioCapabilities The audio capabilities for playback on this device, or {@code null} if + * the default capabilities should be assumed. */ public HlsChunkSource(DataSource dataSource, String playlistUrl, HlsPlaylist playlist, BandwidthMeter bandwidthMeter, int[] variantIndices, int adaptiveMode, - long minBufferDurationToSwitchUpMs, long maxBufferDurationToSwitchDownMs) { + long minBufferDurationToSwitchUpMs, long maxBufferDurationToSwitchDownMs, + AudioCapabilities audioCapabilities) { this.dataSource = dataSource; this.bandwidthMeter = bandwidthMeter; this.adaptiveMode = adaptiveMode; + this.audioCapabilities = audioCapabilities; minBufferDurationToSwitchUpUs = minBufferDurationToSwitchUpMs * 1000; maxBufferDurationToSwitchDownUs = maxBufferDurationToSwitchDownMs * 1000; baseUri = playlist.baseUri; @@ -334,7 +342,7 @@ public class HlsChunkSource { if (previousTsChunk == null || segment.discontinuity || switchingVariant || liveDiscontinuity) { Extractor extractor = chunkUri.getLastPathSegment().endsWith(AAC_FILE_EXTENSION) ? new AdtsExtractor(startTimeUs) - : new TsExtractor(startTimeUs); + : new TsExtractor(startTimeUs, audioCapabilities); extractorWrapper = new HlsExtractorWrapper(trigger, format, startTimeUs, extractor, switchingVariantSpliced); } else {