aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tv/dvr/ui/playback/DvrPlayer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/tv/dvr/ui/playback/DvrPlayer.java')
-rw-r--r--src/com/android/tv/dvr/ui/playback/DvrPlayer.java583
1 files changed, 0 insertions, 583 deletions
diff --git a/src/com/android/tv/dvr/ui/playback/DvrPlayer.java b/src/com/android/tv/dvr/ui/playback/DvrPlayer.java
deleted file mode 100644
index 780bfb2f..00000000
--- a/src/com/android/tv/dvr/ui/playback/DvrPlayer.java
+++ /dev/null
@@ -1,583 +0,0 @@
-/*
- * Copyright (C) 2016 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.dvr.ui.playback;
-
-import android.media.PlaybackParams;
-import android.media.tv.TvContentRating;
-import android.media.tv.TvInputManager;
-import android.media.tv.TvTrackInfo;
-import android.media.tv.TvView;
-import android.media.session.PlaybackState;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.tv.dvr.data.RecordedProgram;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-class DvrPlayer {
- private static final String TAG = "DvrPlayer";
- private static final boolean DEBUG = false;
-
- /**
- * The max rewinding speed supported by DVR player.
- */
- public static final int MAX_REWIND_SPEED = 256;
- /**
- * The max fast-forwarding speed supported by DVR player.
- */
- public static final int MAX_FAST_FORWARD_SPEED = 256;
-
- private static final long SEEK_POSITION_MARGIN_MS = TimeUnit.SECONDS.toMillis(2);
- private static final long REWIND_POSITION_MARGIN_MS = 32; // Workaround value. b/29994826
-
- private RecordedProgram mProgram;
- private long mInitialSeekPositionMs;
- private final TvView mTvView;
- private DvrPlayerCallback mCallback;
- private OnAspectRatioChangedListener mOnAspectRatioChangedListener;
- private OnContentBlockedListener mOnContentBlockedListener;
- private OnTracksAvailabilityChangedListener mOnTracksAvailabilityChangedListener;
- private OnTrackSelectedListener mOnAudioTrackSelectedListener;
- private OnTrackSelectedListener mOnSubtitleTrackSelectedListener;
- private String mSelectedAudioTrackId;
- private String mSelectedSubtitleTrackId;
- private float mAspectRatio = Float.NaN;
- private int mPlaybackState = PlaybackState.STATE_NONE;
- private long mTimeShiftCurrentPositionMs;
- private boolean mPauseOnPrepared;
- private boolean mHasClosedCaption;
- private boolean mHasMultiAudio;
- private final PlaybackParams mPlaybackParams = new PlaybackParams();
- private final DvrPlayerCallback mEmptyCallback = new DvrPlayerCallback();
- private long mStartPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
- private boolean mTimeShiftPlayAvailable;
-
- public static class DvrPlayerCallback {
- /**
- * Called when the playback position is changed. The normal updating frequency is
- * around 1 sec., which is restricted to the implementation of
- * {@link android.media.tv.TvInputService}.
- */
- public void onPlaybackPositionChanged(long positionMs) { }
- /**
- * Called when the playback state or the playback speed is changed.
- */
- public void onPlaybackStateChanged(int playbackState, int playbackSpeed) { }
- /**
- * Called when the playback toward the end.
- */
- public void onPlaybackEnded() { }
- }
-
- public interface OnAspectRatioChangedListener {
- /**
- * Called when the Video's aspect ratio is changed.
- *
- * @param videoAspectRatio The aspect ratio of video. 0 stands for unknown ratios.
- * Listeners should handle it carefully.
- */
- void onAspectRatioChanged(float videoAspectRatio);
- }
-
- public interface OnContentBlockedListener {
- /**
- * Called when the Video's aspect ratio is changed.
- */
- void onContentBlocked(TvContentRating rating);
- }
-
- public interface OnTracksAvailabilityChangedListener {
- /**
- * Called when the Video's subtitle or audio tracks are changed.
- */
- void onTracksAvailabilityChanged(boolean hasClosedCaption, boolean hasMultiAudio);
- }
-
- public interface OnTrackSelectedListener {
- /**
- * Called when certain subtitle or audio track is selected.
- */
- void onTrackSelected(String selectedTrackId);
- }
-
- public DvrPlayer(TvView tvView) {
- mTvView = tvView;
- mTvView.setCaptionEnabled(true);
- mPlaybackParams.setSpeed(1.0f);
- setTvViewCallbacks();
- setCallback(null);
- }
-
- /**
- * Prepares playback.
- *
- * @param doPlay indicates DVR player do or do not start playback after media is prepared.
- */
- public void prepare(boolean doPlay) throws IllegalStateException {
- if (DEBUG) Log.d(TAG, "prepare()");
- if (mProgram == null) {
- throw new IllegalStateException("Recorded program not set");
- } else if (mPlaybackState != PlaybackState.STATE_NONE) {
- throw new IllegalStateException("Playback is already prepared");
- }
- mTvView.timeShiftPlay(mProgram.getInputId(), mProgram.getUri());
- mPlaybackState = PlaybackState.STATE_CONNECTING;
- mPauseOnPrepared = !doPlay;
- mCallback.onPlaybackStateChanged(mPlaybackState, 1);
- }
-
- /**
- * Resumes playback.
- */
- public void play() throws IllegalStateException {
- if (DEBUG) Log.d(TAG, "play()");
- if (!isPlaybackPrepared()) {
- throw new IllegalStateException("Recorded program not set or video not ready yet");
- }
- switch (mPlaybackState) {
- case PlaybackState.STATE_FAST_FORWARDING:
- case PlaybackState.STATE_REWINDING:
- setPlaybackSpeed(1);
- break;
- default:
- mTvView.timeShiftResume();
- }
- mPlaybackState = PlaybackState.STATE_PLAYING;
- mCallback.onPlaybackStateChanged(mPlaybackState, 1);
- }
-
- /**
- * Pauses playback.
- */
- public void pause() throws IllegalStateException {
- if (DEBUG) Log.d(TAG, "pause()");
- if (!isPlaybackPrepared()) {
- throw new IllegalStateException("Recorded program not set or playback not started yet");
- }
- switch (mPlaybackState) {
- case PlaybackState.STATE_FAST_FORWARDING:
- case PlaybackState.STATE_REWINDING:
- setPlaybackSpeed(1);
- // falls through
- case PlaybackState.STATE_PLAYING:
- mTvView.timeShiftPause();
- mPlaybackState = PlaybackState.STATE_PAUSED;
- break;
- default:
- break;
- }
- mCallback.onPlaybackStateChanged(mPlaybackState, 1);
- }
-
- /**
- * Fast-forwards playback with the given speed. If the given speed is larger than
- * {@value #MAX_FAST_FORWARD_SPEED}, uses {@value #MAX_FAST_FORWARD_SPEED}.
- */
- public void fastForward(int speed) throws IllegalStateException {
- if (DEBUG) Log.d(TAG, "fastForward()");
- if (!isPlaybackPrepared()) {
- throw new IllegalStateException("Recorded program not set or playback not started yet");
- }
- if (speed <= 0) {
- throw new IllegalArgumentException("Speed cannot be negative or 0");
- }
- if (mTimeShiftCurrentPositionMs >= mProgram.getDurationMillis() - SEEK_POSITION_MARGIN_MS) {
- return;
- }
- speed = Math.min(speed, MAX_FAST_FORWARD_SPEED);
- if (DEBUG) Log.d(TAG, "Let's play with speed: " + speed);
- setPlaybackSpeed(speed);
- mPlaybackState = PlaybackState.STATE_FAST_FORWARDING;
- mCallback.onPlaybackStateChanged(mPlaybackState, speed);
- }
-
- /**
- * Rewinds playback with the given speed. If the given speed is larger than
- * {@value #MAX_REWIND_SPEED}, uses {@value #MAX_REWIND_SPEED}.
- */
- public void rewind(int speed) throws IllegalStateException {
- if (DEBUG) Log.d(TAG, "rewind()");
- if (!isPlaybackPrepared()) {
- throw new IllegalStateException("Recorded program not set or playback not started yet");
- }
- if (speed <= 0) {
- throw new IllegalArgumentException("Speed cannot be negative or 0");
- }
- if (mTimeShiftCurrentPositionMs <= REWIND_POSITION_MARGIN_MS) {
- return;
- }
- speed = Math.min(speed, MAX_REWIND_SPEED);
- if (DEBUG) Log.d(TAG, "Let's play with speed: " + speed);
- setPlaybackSpeed(-speed);
- mPlaybackState = PlaybackState.STATE_REWINDING;
- mCallback.onPlaybackStateChanged(mPlaybackState, speed);
- }
-
- /**
- * Seeks playback to the specified position.
- */
- public void seekTo(long positionMs) throws IllegalStateException {
- if (DEBUG) Log.d(TAG, "seekTo()");
- if (!isPlaybackPrepared()) {
- throw new IllegalStateException("Recorded program not set or playback not started yet");
- }
- if (mProgram == null || mPlaybackState == PlaybackState.STATE_NONE) {
- return;
- }
- positionMs = getRealSeekPosition(positionMs, SEEK_POSITION_MARGIN_MS);
- if (DEBUG) Log.d(TAG, "Now: " + getPlaybackPosition() + ", shift to: " + positionMs);
- mTvView.timeShiftSeekTo(positionMs + mStartPositionMs);
- if (mPlaybackState == PlaybackState.STATE_FAST_FORWARDING ||
- mPlaybackState == PlaybackState.STATE_REWINDING) {
- mPlaybackState = PlaybackState.STATE_PLAYING;
- mTvView.timeShiftResume();
- mCallback.onPlaybackStateChanged(mPlaybackState, 1);
- }
- }
-
- /**
- * Resets playback.
- */
- public void reset() {
- if (DEBUG) Log.d(TAG, "reset()");
- mCallback.onPlaybackStateChanged(PlaybackState.STATE_NONE, 1);
- mPlaybackState = PlaybackState.STATE_NONE;
- mTvView.reset();
- mTimeShiftPlayAvailable = false;
- mStartPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
- mTimeShiftCurrentPositionMs = 0;
- mPlaybackParams.setSpeed(1.0f);
- mProgram = null;
- mSelectedAudioTrackId = null;
- mSelectedSubtitleTrackId = null;
- }
-
- /**
- * Sets callbacks for playback.
- */
- public void setCallback(DvrPlayerCallback callback) {
- if (callback != null) {
- mCallback = callback;
- } else {
- mCallback = mEmptyCallback;
- }
- }
-
- /**
- * Sets the listener to aspect ratio changing.
- */
- public void setOnAspectRatioChangedListener(OnAspectRatioChangedListener listener) {
- mOnAspectRatioChangedListener = listener;
- }
-
- /**
- * Sets the listener to content blocking.
- */
- public void setOnContentBlockedListener(OnContentBlockedListener listener) {
- mOnContentBlockedListener = listener;
- }
-
- /**
- * Sets the listener to tracks changing.
- */
- public void setOnTracksAvailabilityChangedListener(
- OnTracksAvailabilityChangedListener listener) {
- mOnTracksAvailabilityChangedListener = listener;
- }
-
- /**
- * Sets the listener to tracks of the given type being selected.
- *
- * @param trackType should be either {@link TvTrackInfo#TYPE_AUDIO}
- * or {@link TvTrackInfo#TYPE_SUBTITLE}.
- */
- public void setOnTrackSelectedListener(int trackType, OnTrackSelectedListener listener) {
- if (trackType == TvTrackInfo.TYPE_AUDIO) {
- mOnAudioTrackSelectedListener = listener;
- } else if (trackType == TvTrackInfo.TYPE_SUBTITLE) {
- mOnSubtitleTrackSelectedListener = listener;
- }
- }
-
- /**
- * Gets the listener to tracks of the given type being selected.
- */
- public OnTrackSelectedListener getOnTrackSelectedListener(int trackType) {
- if (trackType == TvTrackInfo.TYPE_AUDIO) {
- return mOnAudioTrackSelectedListener;
- } else if (trackType == TvTrackInfo.TYPE_SUBTITLE) {
- return mOnSubtitleTrackSelectedListener;
- }
- return null;
- }
-
- /**
- * Sets recorded programs for playback. If the player is playing another program, stops it.
- */
- public void setProgram(RecordedProgram program, long initialSeekPositionMs) {
- if (mProgram != null && mProgram.equals(program)) {
- return;
- }
- if (mPlaybackState != PlaybackState.STATE_NONE) {
- reset();
- }
- mInitialSeekPositionMs = initialSeekPositionMs;
- mProgram = program;
- }
-
- /**
- * Returns the recorded program now playing.
- */
- public RecordedProgram getProgram() {
- return mProgram;
- }
-
- /**
- * Returns the currrent playback posistion in msecs.
- */
- public long getPlaybackPosition() {
- return mTimeShiftCurrentPositionMs;
- }
-
- /**
- * Returns the playback speed currently used.
- */
- public int getPlaybackSpeed() {
- return (int) mPlaybackParams.getSpeed();
- }
-
- /**
- * Returns the playback state defined in {@link android.media.session.PlaybackState}.
- */
- public int getPlaybackState() {
- return mPlaybackState;
- }
-
- /**
- * Returns the subtitle tracks of the current playback.
- */
- public ArrayList<TvTrackInfo> getSubtitleTracks() {
- return new ArrayList<>(mTvView.getTracks(TvTrackInfo.TYPE_SUBTITLE));
- }
-
- /**
- * Returns the audio tracks of the current playback.
- */
- public ArrayList<TvTrackInfo> getAudioTracks() {
- return new ArrayList<>(mTvView.getTracks(TvTrackInfo.TYPE_AUDIO));
- }
-
- /**
- * Returns the ID of the selected track of the given type.
- */
- public String getSelectedTrackId(int trackType) {
- if (trackType == TvTrackInfo.TYPE_AUDIO) {
- return mSelectedAudioTrackId;
- } else if (trackType == TvTrackInfo.TYPE_SUBTITLE) {
- return mSelectedSubtitleTrackId;
- }
- return null;
- }
-
- /**
- * Returns if playback of the recorded program is started.
- */
- public boolean isPlaybackPrepared() {
- return mPlaybackState != PlaybackState.STATE_NONE
- && mPlaybackState != PlaybackState.STATE_CONNECTING;
- }
-
- /**
- * Selects the given track.
- *
- * @return ID of the selected track.
- */
- String selectTrack(int trackType, TvTrackInfo selectedTrack) {
- String oldSelectedTrackId = getSelectedTrackId(trackType);
- String newSelectedTrackId = selectedTrack == null ? null : selectedTrack.getId();
- if (!TextUtils.equals(oldSelectedTrackId, newSelectedTrackId)) {
- if (selectedTrack == null) {
- mTvView.selectTrack(trackType, null);
- return null;
- } else {
- List<TvTrackInfo> tracks = mTvView.getTracks(trackType);
- if (tracks != null && tracks.contains(selectedTrack)) {
- mTvView.selectTrack(trackType, newSelectedTrackId);
- return newSelectedTrackId;
- } else if (trackType == TvTrackInfo.TYPE_SUBTITLE && oldSelectedTrackId != null) {
- // Track not found, disabled closed caption.
- mTvView.selectTrack(trackType, null);
- return null;
- }
- }
- }
- return oldSelectedTrackId;
- }
-
- private void setSelectedTrackId(int trackType, String trackId) {
- if (trackType == TvTrackInfo.TYPE_AUDIO) {
- mSelectedAudioTrackId = trackId;
- } else if (trackType == TvTrackInfo.TYPE_SUBTITLE) {
- mSelectedSubtitleTrackId = trackId;
- }
- }
-
- private void setPlaybackSpeed(int speed) {
- mPlaybackParams.setSpeed(speed);
- mTvView.timeShiftSetPlaybackParams(mPlaybackParams);
- }
-
- private long getRealSeekPosition(long seekPositionMs, long endMarginMs) {
- return Math.max(0, Math.min(seekPositionMs, mProgram.getDurationMillis() - endMarginMs));
- }
-
- private void setTvViewCallbacks() {
- mTvView.setTimeShiftPositionCallback(new TvView.TimeShiftPositionCallback() {
- @Override
- public void onTimeShiftStartPositionChanged(String inputId, long timeMs) {
- if (DEBUG) Log.d(TAG, "onTimeShiftStartPositionChanged:" + timeMs);
- mStartPositionMs = timeMs;
- if (mTimeShiftPlayAvailable) {
- resumeToWatchedPositionIfNeeded();
- }
- }
-
- @Override
- public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) {
- if (DEBUG) Log.d(TAG, "onTimeShiftCurrentPositionChanged: " + timeMs);
- if (!mTimeShiftPlayAvailable) {
- // Workaround of b/31436263
- return;
- }
- // Workaround of b/32211561, TIF won't report start position when TIS report
- // its start position as 0. In that case, we have to do the prework of playback
- // on the first time we get current position, and the start position should be 0
- // at that time.
- if (mStartPositionMs == TvInputManager.TIME_SHIFT_INVALID_TIME) {
- mStartPositionMs = 0;
- resumeToWatchedPositionIfNeeded();
- }
- timeMs -= mStartPositionMs;
- if (mPlaybackState == PlaybackState.STATE_REWINDING
- && timeMs <= REWIND_POSITION_MARGIN_MS) {
- play();
- } else {
- mTimeShiftCurrentPositionMs = getRealSeekPosition(timeMs, 0);
- mCallback.onPlaybackPositionChanged(mTimeShiftCurrentPositionMs);
- if (timeMs >= mProgram.getDurationMillis()) {
- pause();
- mCallback.onPlaybackEnded();
- }
- }
- }
- });
- mTvView.setCallback(new TvView.TvInputCallback() {
- @Override
- public void onTimeShiftStatusChanged(String inputId, int status) {
- if (DEBUG) Log.d(TAG, "onTimeShiftStatusChanged:" + status);
- if (status == TvInputManager.TIME_SHIFT_STATUS_AVAILABLE
- && mPlaybackState == PlaybackState.STATE_CONNECTING) {
- mTimeShiftPlayAvailable = true;
- if (mStartPositionMs != TvInputManager.TIME_SHIFT_INVALID_TIME) {
- // onTimeShiftStatusChanged is sometimes called after
- // onTimeShiftStartPositionChanged is called. In this case,
- // resumeToWatchedPositionIfNeeded needs to be called here.
- resumeToWatchedPositionIfNeeded();
- }
- }
- }
-
- @Override
- public void onTracksChanged(String inputId, List<TvTrackInfo> tracks) {
- boolean hasClosedCaption =
- !mTvView.getTracks(TvTrackInfo.TYPE_SUBTITLE).isEmpty();
- boolean hasMultiAudio = mTvView.getTracks(TvTrackInfo.TYPE_AUDIO).size() > 1;
- if ((hasClosedCaption != mHasClosedCaption || hasMultiAudio != mHasMultiAudio)
- && mOnTracksAvailabilityChangedListener != null) {
- mOnTracksAvailabilityChangedListener
- .onTracksAvailabilityChanged(hasClosedCaption, hasMultiAudio);
- }
- mHasClosedCaption = hasClosedCaption;
- mHasMultiAudio = hasMultiAudio;
- }
-
- @Override
- public void onTrackSelected(String inputId, int type, String trackId) {
- if (type == TvTrackInfo.TYPE_AUDIO || type == TvTrackInfo.TYPE_SUBTITLE) {
- setSelectedTrackId(type, trackId);
- OnTrackSelectedListener listener = getOnTrackSelectedListener(type);
- if (listener != null) {
- listener.onTrackSelected(trackId);
- }
- } else if (type == TvTrackInfo.TYPE_VIDEO && trackId != null
- && mOnAspectRatioChangedListener != null) {
- List<TvTrackInfo> trackInfos = mTvView.getTracks(TvTrackInfo.TYPE_VIDEO);
- if (trackInfos != null) {
- for (TvTrackInfo trackInfo : trackInfos) {
- if (trackInfo.getId().equals(trackId)) {
- float videoAspectRatio;
- int videoWidth = trackInfo.getVideoWidth();
- int videoHeight = trackInfo.getVideoHeight();
- if (videoWidth > 0 && videoHeight > 0) {
- videoAspectRatio = trackInfo.getVideoPixelAspectRatio()
- * trackInfo.getVideoWidth() / trackInfo.getVideoHeight();
- } else {
- // Aspect ratio is unknown. Pass the message to listeners.
- videoAspectRatio = 0;
- }
- if (DEBUG) Log.d(TAG, "Aspect Ratio: " + videoAspectRatio);
- if (mAspectRatio != videoAspectRatio || videoAspectRatio == 0) {
- mOnAspectRatioChangedListener
- .onAspectRatioChanged(videoAspectRatio);
- mAspectRatio = videoAspectRatio;
- return;
- }
- }
- }
- }
- }
- }
-
- @Override
- public void onContentBlocked(String inputId, TvContentRating rating) {
- if (mOnContentBlockedListener != null) {
- mOnContentBlockedListener.onContentBlocked(rating);
- }
- }
- });
- }
-
- private void resumeToWatchedPositionIfNeeded() {
- if (mInitialSeekPositionMs != TvInputManager.TIME_SHIFT_INVALID_TIME) {
- mTvView.timeShiftSeekTo(getRealSeekPosition(mInitialSeekPositionMs,
- SEEK_POSITION_MARGIN_MS) + mStartPositionMs);
- mInitialSeekPositionMs = TvInputManager.TIME_SHIFT_INVALID_TIME;
- }
- if (mPauseOnPrepared) {
- mTvView.timeShiftPause();
- mPlaybackState = PlaybackState.STATE_PAUSED;
- mPauseOnPrepared = false;
- } else {
- mTvView.timeShiftResume();
- mPlaybackState = PlaybackState.STATE_PLAYING;
- }
- mCallback.onPlaybackStateChanged(mPlaybackState, 1);
- }
-} \ No newline at end of file