mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Force callers into MediaCodecUtil to catch any exceptions that occur.
Issue: #217 Issue: #228
This commit is contained in:
parent
286365ada8
commit
a879819dd3
6 changed files with 124 additions and 24 deletions
|
|
@ -20,6 +20,7 @@ import com.google.android.exoplayer.DefaultLoadControl;
|
||||||
import com.google.android.exoplayer.LoadControl;
|
import com.google.android.exoplayer.LoadControl;
|
||||||
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
|
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
|
||||||
import com.google.android.exoplayer.MediaCodecUtil;
|
import com.google.android.exoplayer.MediaCodecUtil;
|
||||||
|
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
|
||||||
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
|
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
|
||||||
import com.google.android.exoplayer.SampleSource;
|
import com.google.android.exoplayer.SampleSource;
|
||||||
import com.google.android.exoplayer.TrackRenderer;
|
import com.google.android.exoplayer.TrackRenderer;
|
||||||
|
|
@ -172,7 +173,13 @@ public class DashRendererBuilder implements RendererBuilder,
|
||||||
// Determine which video representations we should use for playback.
|
// Determine which video representations we should use for playback.
|
||||||
ArrayList<Integer> videoRepresentationIndexList = new ArrayList<Integer>();
|
ArrayList<Integer> videoRepresentationIndexList = new ArrayList<Integer>();
|
||||||
if (videoAdaptationSet != null) {
|
if (videoAdaptationSet != null) {
|
||||||
int maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize();
|
int maxDecodableFrameSize;
|
||||||
|
try {
|
||||||
|
maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize();
|
||||||
|
} catch (DecoderQueryException e) {
|
||||||
|
callback.onRenderersError(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
List<Representation> videoRepresentations = videoAdaptationSet.representations;
|
List<Representation> videoRepresentations = videoAdaptationSet.representations;
|
||||||
for (int i = 0; i < videoRepresentations.size(); i++) {
|
for (int i = 0; i < videoRepresentations.size(); i++) {
|
||||||
Format format = videoRepresentations.get(i).format;
|
Format format = videoRepresentations.get(i).format;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultLoadControl;
|
||||||
import com.google.android.exoplayer.LoadControl;
|
import com.google.android.exoplayer.LoadControl;
|
||||||
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
|
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
|
||||||
import com.google.android.exoplayer.MediaCodecUtil;
|
import com.google.android.exoplayer.MediaCodecUtil;
|
||||||
|
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
|
||||||
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
|
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
|
||||||
import com.google.android.exoplayer.TrackRenderer;
|
import com.google.android.exoplayer.TrackRenderer;
|
||||||
import com.google.android.exoplayer.chunk.ChunkSampleSource;
|
import com.google.android.exoplayer.chunk.ChunkSampleSource;
|
||||||
|
|
@ -125,7 +126,13 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain stream elements for playback.
|
// Obtain stream elements for playback.
|
||||||
int maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize();
|
int maxDecodableFrameSize;
|
||||||
|
try {
|
||||||
|
maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize();
|
||||||
|
} catch (DecoderQueryException e) {
|
||||||
|
callback.onRenderersError(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
int audioStreamElementCount = 0;
|
int audioStreamElementCount = 0;
|
||||||
int textStreamElementCount = 0;
|
int textStreamElementCount = 0;
|
||||||
int videoStreamElementIndex = -1;
|
int videoStreamElementIndex = -1;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultLoadControl;
|
||||||
import com.google.android.exoplayer.LoadControl;
|
import com.google.android.exoplayer.LoadControl;
|
||||||
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
|
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
|
||||||
import com.google.android.exoplayer.MediaCodecUtil;
|
import com.google.android.exoplayer.MediaCodecUtil;
|
||||||
|
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
|
||||||
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
|
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
|
||||||
import com.google.android.exoplayer.SampleSource;
|
import com.google.android.exoplayer.SampleSource;
|
||||||
import com.google.android.exoplayer.chunk.ChunkSampleSource;
|
import com.google.android.exoplayer.chunk.ChunkSampleSource;
|
||||||
|
|
@ -99,7 +100,14 @@ import java.util.List;
|
||||||
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
|
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
|
||||||
|
|
||||||
// Determine which video representations we should use for playback.
|
// Determine which video representations we should use for playback.
|
||||||
int maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize();
|
int maxDecodableFrameSize;
|
||||||
|
try {
|
||||||
|
maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize();
|
||||||
|
} catch (DecoderQueryException e) {
|
||||||
|
callback.onRenderersError(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int videoAdaptationSetIndex = period.getAdaptationSetIndex(AdaptationSet.TYPE_VIDEO);
|
int videoAdaptationSetIndex = period.getAdaptationSetIndex(AdaptationSet.TYPE_VIDEO);
|
||||||
List<Representation> videoRepresentations =
|
List<Representation> videoRepresentations =
|
||||||
period.adaptationSets.get(videoAdaptationSetIndex).representations;
|
period.adaptationSets.get(videoAdaptationSetIndex).representations;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import com.google.android.exoplayer.DefaultLoadControl;
|
||||||
import com.google.android.exoplayer.LoadControl;
|
import com.google.android.exoplayer.LoadControl;
|
||||||
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
|
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
|
||||||
import com.google.android.exoplayer.MediaCodecUtil;
|
import com.google.android.exoplayer.MediaCodecUtil;
|
||||||
|
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
|
||||||
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
|
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
|
||||||
import com.google.android.exoplayer.SampleSource;
|
import com.google.android.exoplayer.SampleSource;
|
||||||
import com.google.android.exoplayer.chunk.ChunkSampleSource;
|
import com.google.android.exoplayer.chunk.ChunkSampleSource;
|
||||||
|
|
@ -94,7 +95,13 @@ import java.util.ArrayList;
|
||||||
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
|
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
|
||||||
|
|
||||||
// Obtain stream elements for playback.
|
// Obtain stream elements for playback.
|
||||||
int maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize();
|
int maxDecodableFrameSize;
|
||||||
|
try {
|
||||||
|
maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize();
|
||||||
|
} catch (DecoderQueryException e) {
|
||||||
|
callback.onRenderersError(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
int audioStreamElementIndex = -1;
|
int audioStreamElementIndex = -1;
|
||||||
int videoStreamElementIndex = -1;
|
int videoStreamElementIndex = -1;
|
||||||
ArrayList<Integer> videoTrackIndexList = new ArrayList<Integer>();
|
ArrayList<Integer> videoTrackIndexList = new ArrayList<Integer>();
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer;
|
package com.google.android.exoplayer;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
|
||||||
import com.google.android.exoplayer.drm.DrmSessionManager;
|
import com.google.android.exoplayer.drm.DrmSessionManager;
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
import com.google.android.exoplayer.util.Util;
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
@ -67,8 +68,12 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||||
*/
|
*/
|
||||||
public static class DecoderInitializationException extends Exception {
|
public static class DecoderInitializationException extends Exception {
|
||||||
|
|
||||||
|
private static final int CUSTOM_ERROR_CODE_BASE = -50000;
|
||||||
|
private static final int NO_SUITABLE_DECODER_ERROR = CUSTOM_ERROR_CODE_BASE + 1;
|
||||||
|
private static final int DECODER_QUERY_ERROR = CUSTOM_ERROR_CODE_BASE + 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the decoder that failed to initialize.
|
* The name of the decoder that failed to initialize. Null if no suitable decoder was found.
|
||||||
*/
|
*/
|
||||||
public final String decoderName;
|
public final String decoderName;
|
||||||
|
|
||||||
|
|
@ -77,8 +82,14 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||||
*/
|
*/
|
||||||
public final String diagnosticInfo;
|
public final String diagnosticInfo;
|
||||||
|
|
||||||
public DecoderInitializationException(String decoderName, MediaFormat mediaFormat,
|
public DecoderInitializationException(MediaFormat mediaFormat, Throwable cause, int errorCode) {
|
||||||
Throwable cause) {
|
super("Decoder init failed: [" + errorCode + "], " + mediaFormat, cause);
|
||||||
|
this.decoderName = null;
|
||||||
|
this.diagnosticInfo = buildCustomDiagnosticInfo(errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecoderInitializationException(MediaFormat mediaFormat, Throwable cause,
|
||||||
|
String decoderName) {
|
||||||
super("Decoder init failed: " + decoderName + ", " + mediaFormat, cause);
|
super("Decoder init failed: " + decoderName + ", " + mediaFormat, cause);
|
||||||
this.decoderName = decoderName;
|
this.decoderName = decoderName;
|
||||||
this.diagnosticInfo = Util.SDK_INT >= 21 ? getDiagnosticInfoV21(cause) : null;
|
this.diagnosticInfo = Util.SDK_INT >= 21 ? getDiagnosticInfoV21(cause) : null;
|
||||||
|
|
@ -92,6 +103,11 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String buildCustomDiagnosticInfo(int errorCode) {
|
||||||
|
String sign = errorCode < 0 ? "neg_" : "";
|
||||||
|
return "com.google.android.exoplayer.MediaCodecTrackRenderer_" + sign + Math.abs(errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -281,21 +297,29 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderInfo selectedDecoderInfo = MediaCodecUtil.getDecoderInfo(mimeType,
|
DecoderInfo decoderInfo = null;
|
||||||
requiresSecureDecoder);
|
|
||||||
String selectedDecoderName = selectedDecoderInfo.name;
|
|
||||||
codecIsAdaptive = selectedDecoderInfo.adaptive;
|
|
||||||
try {
|
try {
|
||||||
codec = MediaCodec.createByCodecName(selectedDecoderName);
|
decoderInfo = MediaCodecUtil.getDecoderInfo(mimeType, requiresSecureDecoder);
|
||||||
|
} catch (DecoderQueryException e) {
|
||||||
|
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e,
|
||||||
|
DecoderInitializationException.DECODER_QUERY_ERROR));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decoderInfo == null) {
|
||||||
|
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, null,
|
||||||
|
DecoderInitializationException.NO_SUITABLE_DECODER_ERROR));
|
||||||
|
}
|
||||||
|
|
||||||
|
String decoderName = decoderInfo.name;
|
||||||
|
codecIsAdaptive = decoderInfo.adaptive;
|
||||||
|
try {
|
||||||
|
codec = MediaCodec.createByCodecName(decoderName);
|
||||||
configureCodec(codec, format.getFrameworkMediaFormatV16(), mediaCrypto);
|
configureCodec(codec, format.getFrameworkMediaFormatV16(), mediaCrypto);
|
||||||
codec.start();
|
codec.start();
|
||||||
inputBuffers = codec.getInputBuffers();
|
inputBuffers = codec.getInputBuffers();
|
||||||
outputBuffers = codec.getOutputBuffers();
|
outputBuffers = codec.getOutputBuffers();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
DecoderInitializationException exception = new DecoderInitializationException(
|
notifyAndThrowDecoderInitError(new DecoderInitializationException(format, e, decoderName));
|
||||||
selectedDecoderName, format, e);
|
|
||||||
notifyDecoderInitializationError(exception);
|
|
||||||
throw new ExoPlaybackException(exception);
|
|
||||||
}
|
}
|
||||||
codecHotswapTimeMs = getState() == TrackRenderer.STATE_STARTED ?
|
codecHotswapTimeMs = getState() == TrackRenderer.STATE_STARTED ?
|
||||||
SystemClock.elapsedRealtime() : -1;
|
SystemClock.elapsedRealtime() : -1;
|
||||||
|
|
@ -305,6 +329,12 @@ public abstract class MediaCodecTrackRenderer extends TrackRenderer {
|
||||||
codecCounters.codecInitCount++;
|
codecCounters.codecInitCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void notifyAndThrowDecoderInitError(DecoderInitializationException e)
|
||||||
|
throws ExoPlaybackException {
|
||||||
|
notifyDecoderInitializationError(e);
|
||||||
|
throw new ExoPlaybackException(e);
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean shouldInitCodec() {
|
protected boolean shouldInitCodec() {
|
||||||
return codec == null && format != null;
|
return codec == null && format != null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,20 @@ import java.util.HashMap;
|
||||||
@TargetApi(16)
|
@TargetApi(16)
|
||||||
public class MediaCodecUtil {
|
public class MediaCodecUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when an error occurs querying the device for its underlying media capabilities.
|
||||||
|
* <p>
|
||||||
|
* Such failures are not expected in normal operation and are normally temporary (e.g. if the
|
||||||
|
* mediaserver process has crashed and is yet to restart).
|
||||||
|
*/
|
||||||
|
public static class DecoderQueryException extends Exception {
|
||||||
|
|
||||||
|
private DecoderQueryException(Throwable cause) {
|
||||||
|
super("Failed to query underlying media codecs", cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private static final String TAG = "MediaCodecUtil";
|
private static final String TAG = "MediaCodecUtil";
|
||||||
|
|
||||||
private static final HashMap<CodecKey, Pair<String, CodecCapabilities>> codecs =
|
private static final HashMap<CodecKey, Pair<String, CodecCapabilities>> codecs =
|
||||||
|
|
@ -48,7 +62,8 @@ public class MediaCodecUtil {
|
||||||
* unless secure decryption really is required.
|
* unless secure decryption really is required.
|
||||||
* @return Information about the decoder that will be used, or null if no decoder exists.
|
* @return Information about the decoder that will be used, or null if no decoder exists.
|
||||||
*/
|
*/
|
||||||
public static DecoderInfo getDecoderInfo(String mimeType, boolean secure) {
|
public static DecoderInfo getDecoderInfo(String mimeType, boolean secure)
|
||||||
|
throws DecoderQueryException {
|
||||||
Pair<String, CodecCapabilities> info = getMediaCodecInfo(mimeType, secure);
|
Pair<String, CodecCapabilities> info = getMediaCodecInfo(mimeType, secure);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -66,14 +81,19 @@ public class MediaCodecUtil {
|
||||||
* unless secure decryption really is required.
|
* unless secure decryption really is required.
|
||||||
*/
|
*/
|
||||||
public static synchronized void warmCodec(String mimeType, boolean secure) {
|
public static synchronized void warmCodec(String mimeType, boolean secure) {
|
||||||
getMediaCodecInfo(mimeType, secure);
|
try {
|
||||||
|
getMediaCodecInfo(mimeType, secure);
|
||||||
|
} catch (DecoderQueryException e) {
|
||||||
|
// Codec warming is best effort, so we can swallow the exception.
|
||||||
|
Log.e(TAG, "Codec warming failed", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of the best decoder and its capabilities for the given mimeType.
|
* Returns the name of the best decoder and its capabilities for the given mimeType.
|
||||||
*/
|
*/
|
||||||
private static synchronized Pair<String, CodecCapabilities> getMediaCodecInfo(
|
private static synchronized Pair<String, CodecCapabilities> getMediaCodecInfo(
|
||||||
String mimeType, boolean secure) {
|
String mimeType, boolean secure) throws DecoderQueryException {
|
||||||
CodecKey key = new CodecKey(mimeType, secure);
|
CodecKey key = new CodecKey(mimeType, secure);
|
||||||
if (codecs.containsKey(key)) {
|
if (codecs.containsKey(key)) {
|
||||||
return codecs.get(key);
|
return codecs.get(key);
|
||||||
|
|
@ -95,6 +115,17 @@ public class MediaCodecUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Pair<String, CodecCapabilities> getMediaCodecInfo(CodecKey key,
|
private static Pair<String, CodecCapabilities> getMediaCodecInfo(CodecKey key,
|
||||||
|
MediaCodecListCompat mediaCodecList) throws DecoderQueryException {
|
||||||
|
try {
|
||||||
|
return getMediaCodecInfoInternal(key, mediaCodecList);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// If the underlying mediaserver is in a bad state, we may catch an IllegalStateException
|
||||||
|
// or an IllegalArgumentException here.
|
||||||
|
throw new DecoderQueryException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Pair<String, CodecCapabilities> getMediaCodecInfoInternal(CodecKey key,
|
||||||
MediaCodecListCompat mediaCodecList) {
|
MediaCodecListCompat mediaCodecList) {
|
||||||
String mimeType = key.mimeType;
|
String mimeType = key.mimeType;
|
||||||
int numberOfCodecs = mediaCodecList.getCodecCount();
|
int numberOfCodecs = mediaCodecList.getCodecCount();
|
||||||
|
|
@ -153,7 +184,8 @@ public class MediaCodecUtil {
|
||||||
* @param level An AVC profile level from {@link CodecProfileLevel}.
|
* @param level An AVC profile level from {@link CodecProfileLevel}.
|
||||||
* @return Whether the specified profile is supported at the specified level.
|
* @return Whether the specified profile is supported at the specified level.
|
||||||
*/
|
*/
|
||||||
public static boolean isH264ProfileSupported(int profile, int level) {
|
public static boolean isH264ProfileSupported(int profile, int level)
|
||||||
|
throws DecoderQueryException {
|
||||||
Pair<String, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264, false);
|
Pair<String, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264, false);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -173,7 +205,7 @@ public class MediaCodecUtil {
|
||||||
/**
|
/**
|
||||||
* @return the maximum frame size for an H264 stream that can be decoded on the device.
|
* @return the maximum frame size for an H264 stream that can be decoded on the device.
|
||||||
*/
|
*/
|
||||||
public static int maxH264DecodableFrameSize() {
|
public static int maxH264DecodableFrameSize() throws DecoderQueryException {
|
||||||
Pair<String, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264, false);
|
Pair<String, CodecCapabilities> info = getMediaCodecInfo(MimeTypes.VIDEO_H264, false);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -248,20 +280,23 @@ public class MediaCodecUtil {
|
||||||
@TargetApi(21)
|
@TargetApi(21)
|
||||||
private static final class MediaCodecListCompatV21 implements MediaCodecListCompat {
|
private static final class MediaCodecListCompatV21 implements MediaCodecListCompat {
|
||||||
|
|
||||||
private final MediaCodecInfo[] mediaCodecInfos;
|
private final int codecKind;
|
||||||
|
|
||||||
|
private MediaCodecInfo[] mediaCodecInfos;
|
||||||
|
|
||||||
public MediaCodecListCompatV21(boolean includeSecure) {
|
public MediaCodecListCompatV21(boolean includeSecure) {
|
||||||
int codecKind = includeSecure ? MediaCodecList.ALL_CODECS : MediaCodecList.REGULAR_CODECS;
|
codecKind = includeSecure ? MediaCodecList.ALL_CODECS : MediaCodecList.REGULAR_CODECS;
|
||||||
mediaCodecInfos = new MediaCodecList(codecKind).getCodecInfos();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCodecCount() {
|
public int getCodecCount() {
|
||||||
|
ensureMediaCodecInfosInitialized();
|
||||||
return mediaCodecInfos.length;
|
return mediaCodecInfos.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MediaCodecInfo getCodecInfoAt(int index) {
|
public MediaCodecInfo getCodecInfoAt(int index) {
|
||||||
|
ensureMediaCodecInfosInitialized();
|
||||||
return mediaCodecInfos[index];
|
return mediaCodecInfos[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -275,6 +310,12 @@ public class MediaCodecUtil {
|
||||||
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback);
|
return capabilities.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ensureMediaCodecInfosInitialized() {
|
||||||
|
if (mediaCodecInfos == null) {
|
||||||
|
mediaCodecInfos = new MediaCodecList(codecKind).getCodecInfos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue