diff options
Diffstat (limited to 'tuner/src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java')
-rw-r--r-- | tuner/src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/tuner/src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java new file mode 100644 index 00000000..b136e235 --- /dev/null +++ b/tuner/src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java @@ -0,0 +1,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."); + } + } + } +} |