Expose control over decoder selection.

This allows implementation and injection of custom MediaCodecSelector
instances. By injecting a custom selector, it's possible for applications
to exert more control over which decoder(s) they instantiate. For example,
applications can force use of a software decoder.

GitHub Issue #938
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=110369810
This commit is contained in:
olly 2015-12-16 09:50:53 -08:00 committed by Oliver Woodman
parent 8d6c4b8eb5
commit 7fbffc873c
10 changed files with 210 additions and 105 deletions

View File

@ -18,6 +18,7 @@ package com.google.android.exoplayer.demo.player;
import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.audio.AudioCapabilities;
@ -47,6 +48,7 @@ import com.google.android.exoplayer.util.ManifestFetcher;
import com.google.android.exoplayer.util.Util;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaCodec;
import android.os.Handler;
import android.util.Log;
@ -220,8 +222,8 @@ public class DashRendererBuilder implements RendererBuilder {
VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player,
DemoPlayer.TYPE_VIDEO);
TrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, videoSampleSource,
MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, drmSessionManager, true,
mainHandler, player, 50);
MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000,
drmSessionManager, true, mainHandler, player, 50);
// Build the audio renderer.
DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
@ -232,7 +234,8 @@ public class DashRendererBuilder implements RendererBuilder {
AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player,
DemoPlayer.TYPE_AUDIO);
TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource,
drmSessionManager, true, mainHandler, player, AudioCapabilities.getCapabilities(context));
MediaCodecSelector.DEFAULT, drmSessionManager, true, mainHandler, player,
AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
// Build the text renderer.
DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer.demo.player;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.audio.AudioCapabilities;
@ -30,6 +31,7 @@ import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer.upstream.DefaultUriDataSource;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaCodec;
import android.net.Uri;
@ -62,10 +64,11 @@ public class ExtractorRendererBuilder implements RendererBuilder {
ExtractorSampleSource sampleSource = new ExtractorSampleSource(uri, dataSource, allocator,
BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE);
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
sampleSource, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, player.getMainHandler(),
player, 50);
sampleSource, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000,
player.getMainHandler(), player, 50);
MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource,
null, true, player.getMainHandler(), player, AudioCapabilities.getCapabilities(context));
MediaCodecSelector.DEFAULT, null, true, player.getMainHandler(), player,
AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
TrackRenderer textRenderer = new TextTrackRenderer(sampleSource, player,
player.getMainHandler().getLooper());

View File

@ -18,6 +18,7 @@ package com.google.android.exoplayer.demo.player;
import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.TrackRenderer;
@ -40,6 +41,7 @@ import com.google.android.exoplayer.util.ManifestFetcher;
import com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaCodec;
import android.os.Handler;
@ -149,9 +151,11 @@ public class HlsRendererBuilder implements RendererBuilder {
HlsSampleSource sampleSource = new HlsSampleSource(chunkSource, loadControl,
BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, DemoPlayer.TYPE_VIDEO);
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
sampleSource, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, mainHandler, player, 50);
sampleSource, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT,
5000, mainHandler, player, 50);
MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource,
null, true, player.getMainHandler(), player, AudioCapabilities.getCapabilities(context));
MediaCodecSelector.DEFAULT, null, true, player.getMainHandler(), player,
AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
MetadataTrackRenderer<Map<String, Object>> id3Renderer = new MetadataTrackRenderer<>(
sampleSource, new Id3Parser(), player, mainHandler.getLooper());
Eia608TrackRenderer closedCaptionRenderer = new Eia608TrackRenderer(sampleSource, player,

View File

@ -18,6 +18,7 @@ package com.google.android.exoplayer.demo.player;
import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.audio.AudioCapabilities;
@ -44,6 +45,7 @@ import com.google.android.exoplayer.util.ManifestFetcher;
import com.google.android.exoplayer.util.Util;
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaCodec;
import android.os.Handler;
@ -164,8 +166,8 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder {
VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player,
DemoPlayer.TYPE_VIDEO);
TrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, videoSampleSource,
MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000, drmSessionManager, true, mainHandler,
player, 50);
MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000,
drmSessionManager, true, mainHandler, player, 50);
// Build the audio renderer.
DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
@ -176,7 +178,8 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder {
AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player,
DemoPlayer.TYPE_AUDIO);
TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource,
drmSessionManager, true, mainHandler, player, AudioCapabilities.getCapabilities(context));
MediaCodecSelector.DEFAULT, drmSessionManager, true, mainHandler, player,
AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
// Build the text renderer.
DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);

View File

@ -18,6 +18,7 @@ package com.google.android.exoplayer.demo.vp9opus;
import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.chunk.ChunkSampleSource;
@ -133,7 +134,8 @@ public class DashRendererBuilder implements ManifestCallback<MediaPresentationDe
if (audioRepresentationIsOpus) {
audioRenderer = new LibopusAudioTrackRenderer(audioSampleSource);
} else {
audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource);
audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource,
MediaCodecSelector.DEFAULT);
}
}

View File

@ -86,14 +86,10 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
*/
public static final int MSG_SET_PLAYBACK_PARAMS = 2;
/**
* The name for the raw (passthrough) decoder OMX component.
*/
private static final String RAW_DECODER_NAME = "OMX.google.raw.decoder";
private final EventListener eventListener;
private final AudioTrack audioTrack;
private boolean passthroughEnabled;
private android.media.MediaFormat passthroughMediaFormat;
private int audioSessionId;
private long currentPositionUs;
@ -104,13 +100,15 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
/**
* @param source The upstream source from which the renderer obtains samples.
* @param mediaCodecSelector A decoder selector.
*/
public MediaCodecAudioTrackRenderer(SampleSource source) {
this(source, null, true);
public MediaCodecAudioTrackRenderer(SampleSource source, MediaCodecSelector mediaCodecSelector) {
this(source, mediaCodecSelector, null, true);
}
/**
* @param source The upstream source from which the renderer obtains samples.
* @param mediaCodecSelector A decoder selector.
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
* content is not required.
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
@ -119,24 +117,26 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
* has obtained the keys necessary to decrypt encrypted regions of the media.
*/
public MediaCodecAudioTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager,
boolean playClearSamplesWithoutKeys) {
this(source, drmSessionManager, playClearSamplesWithoutKeys, null, null);
public MediaCodecAudioTrackRenderer(SampleSource source, MediaCodecSelector mediaCodecSelector,
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys) {
this(source, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, null, null);
}
/**
* @param source The upstream source from which the renderer obtains samples.
* @param mediaCodecSelector A decoder selector.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
*/
public MediaCodecAudioTrackRenderer(SampleSource source, Handler eventHandler,
EventListener eventListener) {
this(source, null, true, eventHandler, eventListener);
public MediaCodecAudioTrackRenderer(SampleSource source, MediaCodecSelector mediaCodecSelector,
Handler eventHandler, EventListener eventListener) {
this(source, mediaCodecSelector, null, true, eventHandler, eventListener);
}
/**
* @param source The upstream source from which the renderer obtains samples.
* @param mediaCodecSelector A decoder selector.
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
* content is not required.
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
@ -148,36 +148,16 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
*/
public MediaCodecAudioTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager,
boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener) {
this(source, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, eventListener,
null);
}
/**
* @param source The upstream source from which the renderer obtains samples.
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
* content is not required.
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
* For example a media file may start with a short clear region so as to allow playback to
* begin in parallel with key acquisition. This parameter specifies whether the renderer is
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
* has obtained the keys necessary to decrypt encrypted regions of the media.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param audioCapabilities The audio capabilities for playback on this device. May be null if the
* default capabilities (no encoded audio passthrough support) should be assumed.
*/
public MediaCodecAudioTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager,
boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener,
AudioCapabilities audioCapabilities) {
this(source, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, eventListener,
audioCapabilities, AudioManager.STREAM_MUSIC);
public MediaCodecAudioTrackRenderer(SampleSource source, MediaCodecSelector mediaCodecSelector,
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
Handler eventHandler, EventListener eventListener) {
this(source, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler,
eventListener, null, AudioManager.STREAM_MUSIC);
}
/**
* @param source The upstream source from which the renderer obtains samples.
* @param mediaCodecSelector A decoder selector.
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
* content is not required.
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
@ -192,20 +172,38 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
* default capabilities (no encoded audio passthrough support) should be assumed.
* @param streamType The type of audio stream for the {@link AudioTrack}.
*/
public MediaCodecAudioTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager,
boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener,
AudioCapabilities audioCapabilities, int streamType) {
super(source, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, eventListener);
public MediaCodecAudioTrackRenderer(SampleSource source, MediaCodecSelector mediaCodecSelector,
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
Handler eventHandler, EventListener eventListener, AudioCapabilities audioCapabilities,
int streamType) {
super(source, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler,
eventListener);
this.eventListener = eventListener;
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
this.audioTrack = new AudioTrack(audioCapabilities, streamType);
}
@Override
protected DecoderInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
protected boolean handlesTrack(MediaCodecSelector mediaCodecSelector, MediaFormat mediaFormat)
throws DecoderQueryException {
return allowPassthrough(mimeType) ? new DecoderInfo(RAW_DECODER_NAME, true)
: super.getDecoderInfo(mimeType, requiresSecureDecoder);
String mimeType = mediaFormat.mimeType;
return MimeTypes.isAudio(mimeType) && (MimeTypes.AUDIO_UNKNOWN.equals(mimeType)
|| (allowPassthrough(mimeType) && mediaCodecSelector.getPassthroughDecoderName() != null)
|| mediaCodecSelector.getDecoderInfo(mediaFormat, false) != null);
}
@Override
protected DecoderInfo getDecoderInfo(MediaCodecSelector mediaCodecSelector, MediaFormat format,
boolean requiresSecureDecoder) throws DecoderQueryException {
if (allowPassthrough(format.mimeType)) {
String passthroughDecoderName = mediaCodecSelector.getPassthroughDecoderName();
if (passthroughDecoderName != null) {
passthroughEnabled = true;
return new DecoderInfo(passthroughDecoderName, false);
}
}
passthroughEnabled = false;
return super.getDecoderInfo(mediaCodecSelector, format, requiresSecureDecoder);
}
/**
@ -221,10 +219,10 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
}
@Override
protected void configureCodec(MediaCodec codec, String codecName, boolean codecIsAdaptive,
protected void configureCodec(MediaCodec codec, boolean codecIsAdaptive,
android.media.MediaFormat format, android.media.MediaCrypto crypto) {
String mimeType = format.getString(android.media.MediaFormat.KEY_MIME);
if (RAW_DECODER_NAME.equals(codecName) && !MimeTypes.AUDIO_RAW.equals(mimeType)) {
if (passthroughEnabled) {
// Override the MIME type used to configure the codec if we are using a passthrough decoder.
format.setString(android.media.MediaFormat.KEY_MIME, MimeTypes.AUDIO_RAW);
codec.configure(format, null, crypto, 0);
@ -241,14 +239,6 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
return this;
}
@Override
protected boolean handlesTrack(MediaFormat mediaFormat) throws DecoderQueryException {
// TODO: Use MediaCodecList.findDecoderForFormat on API 23.
String mimeType = mediaFormat.mimeType;
return MimeTypes.isAudio(mimeType) && (MimeTypes.AUDIO_UNKNOWN.equals(mimeType)
|| allowPassthrough(mimeType) || MediaCodecUtil.getDecoderInfo(mimeType, false) != null);
}
@Override
protected void onEnabled(int track, long positionUs, boolean joining)
throws ExoPlaybackException {

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer;
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import android.media.MediaCodec;
/**
* Selector of {@link MediaCodec} instances.
*/
public interface MediaCodecSelector {
/**
* Default implementation of {@link MediaCodecSelector}.
*/
public static final MediaCodecSelector DEFAULT = new MediaCodecSelector() {
/**
* The name for the raw (passthrough) decoder OMX component.
*/
private static final String RAW_DECODER_NAME = "OMX.google.raw.decoder";
@Override
public DecoderInfo getDecoderInfo(MediaFormat format, boolean requiresSecureDecoder)
throws DecoderQueryException {
return MediaCodecUtil.getDecoderInfo(format.mimeType, requiresSecureDecoder);
}
@Override
public String getPassthroughDecoderName() throws DecoderQueryException {
// TODO: Return null if the raw decoder doesn't exist.
return RAW_DECODER_NAME;
}
};
/**
* Selects a decoder to instantiate for a given format.
*
* @param format The format for which a decoder is required.
* @param requiresSecureDecoder Whether a secure decoder is required.
* @return A {@link DecoderInfo} describing the decoder to instantiate, or null if no suitable
* decoder exists.
* @throws DecoderQueryException Thrown if there was an error querying decoders.
*/
DecoderInfo getDecoderInfo(MediaFormat format, boolean requiresSecureDecoder)
throws DecoderQueryException;
/**
* Gets the name of a decoder suitable for audio passthrough.
*
* @return The name of a decoder suitable for audio passthrough, or null if no suitable decoder
* exists.
* @throws DecoderQueryException Thrown if there was an error querying decoders.
*/
String getPassthroughDecoderName() throws DecoderQueryException;
}

View File

@ -194,6 +194,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
public final CodecCounters codecCounters;
private final MediaCodecSelector mediaCodecSelector;
private final DrmSessionManager drmSessionManager;
private final boolean playClearSamplesWithoutKeys;
private final SampleHolder sampleHolder;
@ -229,21 +230,24 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
/**
* @param source The upstream source from which the renderer obtains samples.
* @param mediaCodecSelector A decoder selector.
* @param drmSessionManager For use with encrypted media. May be null if support for encrypted
* media is not required.
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
* For example a media file may start with a short clear region so as to allow playback to
* begin in parallel with key acquisision. This parameter specifies whether the renderer is
* begin in parallel with key acquisition. This parameter specifies whether the renderer is
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
* has obtained the keys necessary to decrypt encrypted regions of the media.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
*/
public MediaCodecTrackRenderer(SampleSource source, DrmSessionManager drmSessionManager,
boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener) {
public MediaCodecTrackRenderer(SampleSource source, MediaCodecSelector mediaCodecSelector,
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
Handler eventHandler, EventListener eventListener) {
super(source);
Assertions.checkState(Util.SDK_INT >= 16);
this.mediaCodecSelector = Assertions.checkNotNull(mediaCodecSelector);
this.drmSessionManager = drmSessionManager;
this.playClearSamplesWithoutKeys = playClearSamplesWithoutKeys;
this.eventHandler = eventHandler;
@ -264,18 +268,35 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
seekToInternal();
}
@Override
protected final boolean handlesTrack(MediaFormat mediaFormat) throws DecoderQueryException {
return handlesTrack(mediaCodecSelector, mediaFormat);
}
/**
* Returns a {@link DecoderInfo} for decoding media in the specified MIME type.
* Returns whether this renderer is capable of handling the provided track.
*
* @param mimeType The type of media to decode.
* @param requiresSecureDecoder Whether a secure decoder is needed for decoding {@code mimeType}.
* @return {@link DecoderInfo} for decoding media in the specified MIME type, or {@code null} if
* no suitable decoder is available.
* @param mediaCodecSelector The decoder selector.
* @param mediaFormat The format of the track.
* @return True if the renderer can handle the track, false otherwise.
* @throws DecoderQueryException Thrown if there was an error querying decoders.
*/
protected DecoderInfo getDecoderInfo(String mimeType, boolean requiresSecureDecoder)
throws DecoderQueryException {
return MediaCodecUtil.getDecoderInfo(mimeType, requiresSecureDecoder);
protected abstract boolean handlesTrack(MediaCodecSelector mediaCodecSelector,
MediaFormat mediaFormat) throws DecoderQueryException;
/**
* Returns a {@link DecoderInfo} for a given format.
*
* @param mediaCodecSelector The decoder selector.
* @param mediaFormat The format for which a decoder is required.
* @param requiresSecureDecoder Whether a secure decoder is required.
* @return A {@link DecoderInfo} describing the decoder to instantiate, or null if no suitable
* decoder exists.
* @throws DecoderQueryException Thrown if there was an error querying decoders.
*/
protected DecoderInfo getDecoderInfo(MediaCodecSelector mediaCodecSelector,
MediaFormat mediaFormat, boolean requiresSecureDecoder) throws DecoderQueryException {
return mediaCodecSelector.getDecoderInfo(format, requiresSecureDecoder);
}
/**
@ -283,12 +304,11 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
* wish to configure the codec with a non-null surface.
*
* @param codec The {@link MediaCodec} to configure.
* @param codecName The name of the codec.
* @param codecIsAdaptive Whether the codec is adaptive.
* @param format The format for which the codec is being configured.
* @param crypto For drm protected playbacks, a {@link MediaCrypto} to use for decryption.
*/
protected void configureCodec(MediaCodec codec, String codecName, boolean codecIsAdaptive,
protected void configureCodec(MediaCodec codec, boolean codecIsAdaptive,
android.media.MediaFormat format, MediaCrypto crypto) {
codec.configure(format, null, crypto, 0);
}
@ -325,7 +345,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
DecoderInfo decoderInfo = null;
try {
decoderInfo = getDecoderInfo(mimeType, requiresSecureDecoder);
decoderInfo = getDecoderInfo(mediaCodecSelector, format, requiresSecureDecoder);
} catch (DecoderQueryException e) {
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e,
requiresSecureDecoder, DecoderInitializationException.DECODER_QUERY_ERROR));
@ -346,8 +366,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
codec = MediaCodec.createByCodecName(codecName);
TraceUtil.endSection();
TraceUtil.beginSection("configureCodec");
configureCodec(codec, codecName, codecIsAdaptive, format.getFrameworkMediaFormatV16(),
mediaCrypto);
configureCodec(codec, decoderInfo.adaptive, format.getFrameworkMediaFormatV16(), mediaCrypto);
TraceUtil.endSection();
TraceUtil.beginSection("codec.start()");
codec.start();

View File

@ -128,29 +128,34 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
/**
* @param context A context.
* @param source The upstream source from which the renderer obtains samples.
* @param mediaCodecSelector A decoder selector.
* @param videoScalingMode The scaling mode to pass to
* {@link MediaCodec#setVideoScalingMode(int)}.
*/
public MediaCodecVideoTrackRenderer(Context context, SampleSource source, int videoScalingMode) {
this(context, source, videoScalingMode, 0);
public MediaCodecVideoTrackRenderer(Context context, SampleSource source,
MediaCodecSelector mediaCodecSelector, int videoScalingMode) {
this(context, source, mediaCodecSelector, videoScalingMode, 0);
}
/**
* @param context A context.
* @param source The upstream source from which the renderer obtains samples.
* @param mediaCodecSelector A decoder selector.
* @param videoScalingMode The scaling mode to pass to
* {@link MediaCodec#setVideoScalingMode(int)}.
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer
* can attempt to seamlessly join an ongoing playback.
*/
public MediaCodecVideoTrackRenderer(Context context, SampleSource source, int videoScalingMode,
long allowedJoiningTimeMs) {
this(context, source, videoScalingMode, allowedJoiningTimeMs, null, null, -1);
public MediaCodecVideoTrackRenderer(Context context, SampleSource source,
MediaCodecSelector mediaCodecSelector, int videoScalingMode, long allowedJoiningTimeMs) {
this(context, source, mediaCodecSelector, videoScalingMode, allowedJoiningTimeMs, null, null,
-1);
}
/**
* @param context A context.
* @param source The upstream source from which the renderer obtains samples.
* @param mediaCodecSelector A decoder selector.
* @param videoScalingMode The scaling mode to pass to
* {@link MediaCodec#setVideoScalingMode(int)}.
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer
@ -161,16 +166,17 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
* @param maxDroppedFrameCountToNotify The maximum number of frames that can be dropped between
* invocations of {@link EventListener#onDroppedFrames(int, long)}.
*/
public MediaCodecVideoTrackRenderer(Context context, SampleSource source, int videoScalingMode,
long allowedJoiningTimeMs, Handler eventHandler, EventListener eventListener,
int maxDroppedFrameCountToNotify) {
this(context, source, videoScalingMode, allowedJoiningTimeMs, null, false, eventHandler,
eventListener, maxDroppedFrameCountToNotify);
public MediaCodecVideoTrackRenderer(Context context, SampleSource source,
MediaCodecSelector mediaCodecSelector, int videoScalingMode, long allowedJoiningTimeMs,
Handler eventHandler, EventListener eventListener, int maxDroppedFrameCountToNotify) {
this(context, source, mediaCodecSelector, videoScalingMode, allowedJoiningTimeMs, null, false,
eventHandler, eventListener, maxDroppedFrameCountToNotify);
}
/**
* @param context A context.
* @param source The upstream source from which the renderer obtains samples.
* @param mediaCodecSelector A decoder selector.
* @param videoScalingMode The scaling mode to pass to
* {@link MediaCodec#setVideoScalingMode(int)}.
* @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer
@ -188,11 +194,12 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
* @param maxDroppedFrameCountToNotify The maximum number of frames that can be dropped between
* invocations of {@link EventListener#onDroppedFrames(int, long)}.
*/
public MediaCodecVideoTrackRenderer(Context context, SampleSource source, int videoScalingMode,
long allowedJoiningTimeMs, DrmSessionManager drmSessionManager,
boolean playClearSamplesWithoutKeys, Handler eventHandler, EventListener eventListener,
int maxDroppedFrameCountToNotify) {
super(source, drmSessionManager, playClearSamplesWithoutKeys, eventHandler, eventListener);
public MediaCodecVideoTrackRenderer(Context context, SampleSource source,
MediaCodecSelector mediaCodecSelector, int videoScalingMode, long allowedJoiningTimeMs,
DrmSessionManager drmSessionManager, boolean playClearSamplesWithoutKeys,
Handler eventHandler, EventListener eventListener, int maxDroppedFrameCountToNotify) {
super(source, mediaCodecSelector, drmSessionManager, playClearSamplesWithoutKeys, eventHandler,
eventListener);
this.frameReleaseTimeHelper = new VideoFrameReleaseTimeHelper(context);
this.videoScalingMode = videoScalingMode;
this.allowedJoiningTimeUs = allowedJoiningTimeMs * 1000;
@ -209,11 +216,11 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
}
@Override
protected boolean handlesTrack(MediaFormat mediaFormat) throws DecoderQueryException {
// TODO: Use MediaCodecList.findDecoderForFormat on API 23.
protected boolean handlesTrack(MediaCodecSelector mediaCodecSelector, MediaFormat mediaFormat)
throws DecoderQueryException {
String mimeType = mediaFormat.mimeType;
return MimeTypes.isVideo(mimeType) && (MimeTypes.VIDEO_UNKNOWN.equals(mimeType)
|| MediaCodecUtil.getDecoderInfo(mimeType, false) != null);
|| mediaCodecSelector.getDecoderInfo(mediaFormat, false) != null);
}
@Override
@ -316,7 +323,7 @@ public class MediaCodecVideoTrackRenderer extends MediaCodecTrackRenderer {
// Override configureCodec to provide the surface.
@Override
protected void configureCodec(MediaCodec codec, String codecName, boolean codecIsAdaptive,
protected void configureCodec(MediaCodec codec, boolean codecIsAdaptive,
android.media.MediaFormat format, MediaCrypto crypto) {
maybeSetMaxInputSize(format, codecIsAdaptive);
codec.configure(format, surface, crypto, 0);

View File

@ -20,6 +20,7 @@ import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.chunk.ChunkSampleSource;
@ -327,7 +328,8 @@ public final class DashTest extends ActivityInstrumentationTestCase2<HostActivit
VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, handler, logger, VIDEO_EVENT_ID,
MIN_LOADABLE_RETRY_COUNT);
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(host,
videoSampleSource, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 0, handler, logger, 50);
videoSampleSource, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT,
0, handler, logger, 50);
videoCounters = videoRenderer.codecCounters;
player.sendMessage(videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, surface);
@ -341,7 +343,7 @@ public final class DashTest extends ActivityInstrumentationTestCase2<HostActivit
AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, handler, logger, AUDIO_EVENT_ID,
MIN_LOADABLE_RETRY_COUNT);
MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(
audioSampleSource, handler, logger);
audioSampleSource, MediaCodecSelector.DEFAULT, handler, logger);
audioCounters = audioRenderer.codecCounters;
TrackRenderer[] renderers = new TrackRenderer[RENDERER_COUNT];