1package com.android.tv.tuner.exoplayer; 2 3import android.content.Context; 4import android.media.MediaCodec; 5import android.os.Handler; 6import android.util.Log; 7 8import com.google.android.exoplayer.DecoderInfo; 9import com.google.android.exoplayer.ExoPlaybackException; 10import com.google.android.exoplayer.MediaCodecSelector; 11import com.google.android.exoplayer.MediaCodecUtil; 12import com.google.android.exoplayer.MediaCodecVideoTrackRenderer; 13import com.google.android.exoplayer.MediaFormatHolder; 14import com.google.android.exoplayer.MediaSoftwareCodecUtil; 15import com.google.android.exoplayer.SampleSource; 16import com.android.tv.common.feature.CommonFeatures; 17 18import java.lang.reflect.Field; 19 20/** 21 * MPEG-2 TS video track renderer 22 */ 23public class MpegTsVideoTrackRenderer extends MediaCodecVideoTrackRenderer { 24 private static final String TAG = "MpegTsVideoTrackRender"; 25 26 private static final int VIDEO_PLAYBACK_DEADLINE_IN_MS = 5000; 27 // If DROPPED_FRAMES_NOTIFICATION_THRESHOLD frames are consecutively dropped, it'll be notified. 28 private static final int DROPPED_FRAMES_NOTIFICATION_THRESHOLD = 10; 29 private static final int MIN_HD_HEIGHT = 720; 30 private static final String MIMETYPE_MPEG2 = "video/mpeg2"; 31 private static Field sRenderedFirstFrameField; 32 33 private final boolean mIsSwCodecEnabled; 34 private boolean mCodecIsSwPreferred; 35 private boolean mSetRenderedFirstFrame; 36 37 static { 38 // Remove the reflection below once b/31223646 is resolved. 39 try { 40 sRenderedFirstFrameField = MediaCodecVideoTrackRenderer.class.getDeclaredField( 41 "renderedFirstFrame"); 42 sRenderedFirstFrameField.setAccessible(true); 43 } catch (NoSuchFieldException e) { 44 // Null-checking for {@code sRenderedFirstFrameField} will do the error handling. 45 } 46 } 47 48 public MpegTsVideoTrackRenderer(Context context, SampleSource source, Handler handler, 49 MediaCodecVideoTrackRenderer.EventListener listener) { 50 super(context, source, MediaCodecSelector.DEFAULT, 51 MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, VIDEO_PLAYBACK_DEADLINE_IN_MS, handler, 52 listener, DROPPED_FRAMES_NOTIFICATION_THRESHOLD); 53 mIsSwCodecEnabled = CommonFeatures.USE_SW_CODEC_FOR_SD.isEnabled(context); 54 } 55 56 @Override 57 protected DecoderInfo getDecoderInfo(MediaCodecSelector codecSelector, String mimeType, 58 boolean requiresSecureDecoder) throws MediaCodecUtil.DecoderQueryException { 59 try { 60 if (mIsSwCodecEnabled && mCodecIsSwPreferred) { 61 DecoderInfo swCodec = MediaSoftwareCodecUtil.getSoftwareDecoderInfo( 62 mimeType, requiresSecureDecoder); 63 if (swCodec != null) { 64 return swCodec; 65 } 66 } 67 } catch (MediaSoftwareCodecUtil.DecoderQueryException e) { 68 } 69 return super.getDecoderInfo(codecSelector, mimeType,requiresSecureDecoder); 70 } 71 72 @Override 73 protected void onInputFormatChanged(MediaFormatHolder holder) throws ExoPlaybackException { 74 mCodecIsSwPreferred = MIMETYPE_MPEG2.equalsIgnoreCase(holder.format.mimeType) 75 && holder.format.height < MIN_HD_HEIGHT; 76 super.onInputFormatChanged(holder); 77 } 78 79 @Override 80 protected void onDiscontinuity(long positionUs) throws ExoPlaybackException { 81 super.onDiscontinuity(positionUs); 82 // Disabling pre-rendering of the first frame in order to avoid a frozen picture when 83 // starting the playback. We do this only once, when the renderer is enabled at first, since 84 // we need to pre-render the frame in advance when we do trickplay backed by seeking. 85 if (!mSetRenderedFirstFrame) { 86 setRenderedFirstFrame(true); 87 mSetRenderedFirstFrame = true; 88 } 89 } 90 91 private void setRenderedFirstFrame(boolean renderedFirstFrame) { 92 if (sRenderedFirstFrameField != null) { 93 try { 94 sRenderedFirstFrameField.setBoolean(this, renderedFirstFrame); 95 } catch (IllegalAccessException e) { 96 Log.w(TAG, "renderedFirstFrame is not accessible. Playback may start with a frozen" 97 +" picture."); 98 } 99 } 100 } 101} 102