aboutsummaryrefslogtreecommitdiff
path: root/tuner/src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java
diff options
context:
space:
mode:
Diffstat (limited to 'tuner/src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java')
-rw-r--r--tuner/src/com/android/tv/tuner/exoplayer/MpegTsVideoTrackRenderer.java126
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.");
+ }
+ }
+ }
+}