blob: c8a9c01bd5d910d6a934e2dac808d7eacf64b663 [file] [log] [blame]
/*
* 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.features.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.");
}
}
}
}