Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - Fast Forward #243

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
48 changes: 48 additions & 0 deletions app/src/main/java/ie/macinnes/tvheadend/TvhMappings.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@

package ie.macinnes.tvheadend;

import android.util.Log;

public class TvhMappings {
private static final String TAG = TvhMappings.class.getName();

private TvhMappings() {
throw new IllegalAccessError("Utility class");
Expand All @@ -33,4 +36,49 @@ public static int sriToRate(int sri)
{
return mSampleRates[sri & 0xf];
}

public static int androidSpeedToTvhSpeed(float speed) {
// Translate the speed value from what Android uses, to what TVHeadend expects.
// TVHeadend expects: 0=pause, 100=1x fwd, -100=1x backward)

int translatedSpeed;

switch((int) speed) {
case 1: // Normal Playback
translatedSpeed = 100;
break;

case -2: // 2x Rewind
translatedSpeed = -200;
break;
case -4: // 3x Rewind
translatedSpeed = -300;
break;
case -12: // 4x Rewind
translatedSpeed = -400;
break;
case -48: // 5x Rewind
translatedSpeed = -500;
break;

case 2: // 2x Fast forward
translatedSpeed = 200;
break;
case 8: // 3x Fast forward
translatedSpeed = 300;
break;
case 32: // 4x Fast forward
translatedSpeed = 400;
break;
case 128: // 5x Fast forward
translatedSpeed = 500;
break;

default:
throw new IllegalArgumentException("Unknown speed: " + speed);
}

Log.d(TAG, "Translated android speed " + speed + " to TVH speed " + translatedSpeed);
return translatedSpeed;
}
}
31 changes: 31 additions & 0 deletions app/src/main/java/ie/macinnes/tvheadend/player/ExoPlayerUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,35 @@ private static String joinWithSeparator(String first, String second) {
return first + ", " + second;
}
}

public static float androidSpeedToExoPlayerSpeed(float speed) {
// Translate the speed value from what Android uses, to what ExoPlayer expects. Must be
// greater than zero, and cannot be used for rewind.
float translatedSpeed;

switch((int) speed) {
case 1: // Normal Playback
translatedSpeed = 1.0f;
break;

case 2: // 2x Fast forward
translatedSpeed = 2.0f;
break;
case 8: // 3x Fast forward
translatedSpeed = 3.0f;
break;
case 32: // 4x Fast forward
translatedSpeed = 4.0f;
break;
case 128: // 5x Fast forward
translatedSpeed = 5.0f;
break;

default:
throw new IllegalArgumentException("Unknown speed: " + speed);
}

Log.d(TAG, "Translated android speed " + speed + " to ExoPlayer speed " + translatedSpeed);
return translatedSpeed;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,9 @@ public long getTimeshiftOffsetPts() {

@Override
public void setSpeed(int speed) {

if (mSubscriber != null) {
mSubscriber.setSpeed(speed);
}
}

// Misc Internal Methods
Expand Down
123 changes: 83 additions & 40 deletions app/src/main/java/ie/macinnes/tvheadend/player/TvheadendPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import android.media.tv.TvTrackInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.util.SparseArray;
Expand Down Expand Up @@ -70,11 +71,14 @@
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import ie.macinnes.htsp.SimpleHtspConnection;
import ie.macinnes.tvheadend.Constants;
import ie.macinnes.tvheadend.R;
import ie.macinnes.tvheadend.TvContractUtils;
import ie.macinnes.tvheadend.TvhMappings;

public class TvheadendPlayer implements Player.EventListener {
private static final String TAG = TvheadendPlayer.class.getName();
Expand Down Expand Up @@ -112,6 +116,8 @@ public interface Listener {
private final SimpleHtspConnection mConnection;
private final Listener mListener;

private final Handler mHandler;
private final Timer mTimer;
private final SharedPreferences mSharedPreferences;

private SimpleExoPlayer mExoPlayer;
Expand All @@ -133,11 +139,16 @@ public interface Listener {

private Uri mCurrentChannelUri;

private float mSpeed = 1.0f;
private TimerTask mRewindTimerTask;

public TvheadendPlayer(Context context, SimpleHtspConnection connection, Listener listener) {
mContext = context;
mConnection = connection;
mListener = listener;

mHandler = new Handler();
mTimer = new Timer();
mSharedPreferences = mContext.getSharedPreferences(
Constants.PREFERENCE_TVHEADEND, Context.MODE_PRIVATE);

Expand Down Expand Up @@ -193,10 +204,12 @@ public boolean selectTrack(int type, String trackId) {
public void play() {
// Start playback when ready
mExoPlayer.setPlayWhenReady(true);
cancelRewind();
}

public void resume() {
mExoPlayer.setPlayWhenReady(true);
cancelRewind();

if (mDataSource != null) {
Log.d(TAG, "Resuming HtspDataSource");
Expand All @@ -208,6 +221,7 @@ public void resume() {

public void pause() {
mExoPlayer.setPlayWhenReady(false);
cancelRewind();

if (mDataSource != null) {
Log.d(TAG, "Pausing HtspDataSource");
Expand All @@ -233,49 +247,78 @@ public void seek(long timeMs) {

@RequiresApi(api = Build.VERSION_CODES.M)
public void setPlaybackParams(PlaybackParams params) {
float rawSpeed = params.getSpeed();
int speed = (int) rawSpeed;
int translatedSpeed;

switch(speed) {
case 0:
translatedSpeed = 100;
break;
case -2:
translatedSpeed = -200;
break;
case -4:
translatedSpeed = -300;
break;
case -12:
translatedSpeed = -400;
break;
case -48:
translatedSpeed = -500;
break;
case 2:
translatedSpeed = 200;
break;
case 4:
translatedSpeed = 300;
break;
case 12:
translatedSpeed = 400;
break;
case 48:
translatedSpeed = 500;
break;
default:
Log.d(TAG, "Unknown speed??? " + rawSpeed);
return;
Log.d(TAG, "setPlaybackParams: Speed: " + params.getSpeed());

if (mDataSource != null) {
mSpeed = params.getSpeed();

if (mSpeed == 1.0f) {
setVolume(1.0f);
} else {
setVolume(0f);
}

if (mSpeed > 0) {
// Forward Playback
// Convert from TIF speed format, over to TVH and ExoPlayer formats
int tvhSpeed = TvhMappings.androidSpeedToTvhSpeed(mSpeed);
float exoSpeed = ExoPlayerUtils.androidSpeedToExoPlayerSpeed(mSpeed);

mDataSource.setSpeed(tvhSpeed);
mExoPlayer.setPlaybackParameters(new PlaybackParameters(exoSpeed, 1));
} else {
// Reverse Playback
rewind();
}
}
}

Log.d(TAG, "Speed: " + params.getSpeed() + " / " + translatedSpeed);
private void rewind() {
cancelRewind();

if (mDataSource != null) {
mDataSource.setSpeed(translatedSpeed);
mExoPlayer.setPlaybackParameters(new PlaybackParameters(translatedSpeed, 0));
mExoPlayer.setPlayWhenReady(false);

mRewindTimerTask = new TimerTask() {
@Override
public void run() {
mHandler.post(new Runnable() {
@Override
public void run() {
int seekSize;

switch((int) mSpeed) {
case -2: // 2x Rewind
seekSize = -2000;
break;
case -4: // 3x Rewind
seekSize = -3000;
break;
case -12: // 4x Rewind
seekSize = -4000;
break;
case -48: // 5x Rewind
seekSize = -5000;
break;

default:
throw new IllegalArgumentException("Unknown speed: " + mSpeed);
}

seek(Math.max(getTimeshiftCurrentPosition() - seekSize, getTimeshiftStartPosition()));
}
});
}
};

mTimer.scheduleAtFixedRate(mRewindTimerTask, 0, 1000);
}

private void cancelRewind() {
if (mRewindTimerTask != null) {
mRewindTimerTask.cancel();
}

mExoPlayer.setPlayWhenReady(true);
}

private void stop() {
Expand Down Expand Up @@ -545,7 +588,7 @@ public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray tra
}
}

if(hasVideoTrack) {
if (hasVideoTrack) {
disableRadioInfoScreen();
} else {
enableRadioInfoScreen();
Expand Down
11 changes: 9 additions & 2 deletions app/src/main/java/ie/macinnes/tvheadend/tvinput/HtspSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ public void onSetStreamVolume(float volume) {
@Override
public View onCreateOverlayView() {
Log.d(TAG, "Session onCreateOverlayView (" + mSessionNumber + ")");

return mTvheadendPlayer.getOverlayView(
mCaptioningManager.getUserStyle(), mCaptioningManager.getFontScale());
}
Expand Down Expand Up @@ -190,6 +191,12 @@ public void onTimeShiftPause() {
public void onTimeShiftResume() {
Log.d(TAG, "onTimeShiftResume");
mTvheadendPlayer.resume();

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PlaybackParams normalParams = new PlaybackParams();
normalParams.setSpeed(1);
onTimeShiftSetPlaybackParams(normalParams);
}
}

@Override
Expand All @@ -202,9 +209,9 @@ public void onTimeShiftSeekTo(long timeMs) {
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onTimeShiftSetPlaybackParams(PlaybackParams params) {
Log.d(TAG, "onTimeShiftSetPlaybackParams: " + params);
Log.d(TAG, "onTimeShiftSetPlaybackParams: Speed: " + params.getSpeed());

Toast.makeText(mContext, "Unsupported", Toast.LENGTH_SHORT).show();
mTvheadendPlayer.setPlaybackParams(params);
}

@RequiresApi(api = Build.VERSION_CODES.M)
Expand Down