aboutsummaryrefslogtreecommitdiff
path: root/tuner/src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java
blob: b136e23585587da2dc3db31e5dec30802ee22e08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/*
 * Copyright (C) 2017 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.android.tv.tuner.exoplayer;

import android.content.Context;
import android.media.MediaCodec;
import android.os.Handler;
import android.util.Log;
import com.android.tv.tuner.TunerFeatures;
import com.google.android.exoplayer.DecoderInfo;
import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.MediaCodecUtil;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.MediaFormatHolder;
import com.google.android.exoplayer.MediaSoftwareCodecUtil;
import com.google.android.exoplayer.SampleSource;
import java.lang.reflect.Field;

/** MPEG-2 TS video track renderer */
public class MpegTsVideoTrackRenderer extends MediaCodecVideoTrackRenderer {
    private static final String TAG = "MpegTsVideoTrackRender";

    private static final int VIDEO_PLAYBACK_DEADLINE_IN_MS = 5000;
    // If DROPPED_FRAMES_NOTIFICATION_THRESHOLD frames are consecutively dropped, it'll be notified.
    private static final int DROPPED_FRAMES_NOTIFICATION_THRESHOLD = 10;
    private static final int MIN_HD_HEIGHT = 720;
    private static final String MIMETYPE_MPEG2 = "video/mpeg2";
    private static Field sRenderedFirstFrameField;

    private final boolean mIsSwCodecEnabled;
    private boolean mCodecIsSwPreferred;
    private boolean mSetRenderedFirstFrame;

    static {
        // Remove the reflection below once b/31223646 is resolved.
        try {
            sRenderedFirstFrameField =
                    MediaCodecVideoTrackRenderer.class.getDeclaredField("renderedFirstFrame");
            sRenderedFirstFrameField.setAccessible(true);
        } catch (NoSuchFieldException e) {
            // Null-checking for {@code sRenderedFirstFrameField} will do the error handling.
        }
    }

    public MpegTsVideoTrackRenderer(
            Context context,
            SampleSource source,
            Handler handler,
            MediaCodecVideoTrackRenderer.EventListener listener) {
        super(
                context,
                source,
                MediaCodecSelector.DEFAULT,
                MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT,
                VIDEO_PLAYBACK_DEADLINE_IN_MS,
                handler,
                listener,
                DROPPED_FRAMES_NOTIFICATION_THRESHOLD);
        mIsSwCodecEnabled = TunerFeatures.USE_SW_CODEC_FOR_SD.isEnabled(context);
    }

    @Override
    protected DecoderInfo getDecoderInfo(
            MediaCodecSelector codecSelector, String mimeType, boolean requiresSecureDecoder)
            throws MediaCodecUtil.DecoderQueryException {
        try {
            if (mIsSwCodecEnabled && mCodecIsSwPreferred) {
                DecoderInfo swCodec =
                        MediaSoftwareCodecUtil.getSoftwareDecoderInfo(
                                mimeType, requiresSecureDecoder);
                if (swCodec != null) {
                    return swCodec;
                }
            }
        } catch (MediaSoftwareCodecUtil.DecoderQueryException e) {
        }
        return super.getDecoderInfo(codecSelector, mimeType, requiresSecureDecoder);
    }

    @Override
    protected void onInputFormatChanged(MediaFormatHolder holder) throws ExoPlaybackException {
        mCodecIsSwPreferred =
                MIMETYPE_MPEG2.equalsIgnoreCase(holder.format.mimeType)
                        && holder.format.height < MIN_HD_HEIGHT;
        super.onInputFormatChanged(holder);
    }

    @Override
    protected void onDiscontinuity(long positionUs) throws ExoPlaybackException {
        super.onDiscontinuity(positionUs);
        // Disabling pre-rendering of the first frame in order to avoid a frozen picture when
        // starting the playback. We do this only once, when the renderer is enabled at first, since
        // we need to pre-render the frame in advance when we do trickplay backed by seeking.
        if (!mSetRenderedFirstFrame) {
            setRenderedFirstFrame(true);
            mSetRenderedFirstFrame = true;
        }
    }

    private void setRenderedFirstFrame(boolean renderedFirstFrame) {
        if (sRenderedFirstFrameField != null) {
            try {
                sRenderedFirstFrameField.setBoolean(this, renderedFirstFrame);
            } catch (IllegalAccessException e) {
                Log.w(
                        TAG,
                        "renderedFirstFrame is not accessible. Playback may start with a frozen"
                                + " picture.");
            }
        }
    }
}