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