I am trying to implement a custom seek (forward/rewind) functionality on top of Google's CastCompanionLibrary-android. I'm noticing a strange behavior where despite the user seeking to a non-zero seekBar position, the video playing on the cast is forced to restart from the beginning, totally ignoring the requested seek position.
My video player's SeekBar listener implementation
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
int progress = seekBar.getProgress();
if (mPlaybackState == PlaybackState.PLAYING) {
// determine local or remote playback
switch (mPlaybackLocation) {
case PlaybackLocation.LOCAL:
mLocalVideoView.seekTo(progress);
break;
case PlaybackLocation.REMOTE:
try {
mVideoCastManager.play(progress);
} catch (Exception e) {
CastUtils.handleException(mParentActivity, e);
}
break;
default:
Log.d(TAG, "unknown playback location");
break;
}
} else if (mPlaybackState != PlaybackState.IDLE) {
mLocalVideoView.seekTo(progress);
}
}
Unlike in this other related question, I am confident our streaming server is working as it should because when I use the default VideoCastControllerActivity class, we notice correct seeking behavior. But our aim is not to use the default class as it is a full-screen activity and that doesn't work well with our app. Basically, we'd like precisely the same functionality as the Youtube app where the normal seekbar controls both the local and remote player without having to leave the current screen to a full-screen one.
The thing is that my implementation is very similar, if not identical, to the reference example posted below. The key line is that both call:
mVideoCastManager.play(progress)
;
(And no success either calling VideoCastManager.seekAndPlay(int position). In fact, VideoCastControllerActivity actually calls play() which in turn calls seekAndPlay())
Reference Player (Cast-Videos) seeking implementation
private void play(int position) {
startControllersTimer();
switch (mLocation) {
case LOCAL:
mVideoView.seekTo(position);
mVideoView.start();
break;
case REMOTE:
mPlaybackState = PlaybackState.BUFFERING;
updatePlayButton(mPlaybackState);
try {
mCastManager.play(position);
} catch (Exception e) {
Utils.handleException(this, e);
}
break;
default:
break;
}
restartTrickplayTimer();
}
By the way, I notice that the when seek bar on the remote player momentarily shows up, it doesn't even fill up to the correct position that was sought, instead filling up to only about 10% of the entire bar. See the attached image where I'd sought to about 1:10 out of a 1:29 video (~80%)
Is it a bug with the CCL or what could I not be doing right?
EDIT: Receiver & Sender Logs during seeking
[565.374s] [cast.receiver.IpcChannel] Received message: {"data":"{\"requestId\":7,\"type\":\"SEEK\",\"mediaSessionId\":2,\"currentTime\":0.087,\"resumeState\":\"PLAYBACK_START\"}","namespace":"urn:x-cast:com.google.cast.media","senderId":"14:net.ajplus.beta-19"}
cast_receiver.js:40 [565.382s] [cast.receiver.CastMessageBus] Dispatching CastMessageBus message
cast_receiver.js:40 [565.397s] [cast.receiver.MediaManager] MediaManager message received
cast_receiver.js:40 [565.404s] [cast.receiver.MediaManager] Dispatching MediaManager seek event
cast_receiver.js:40 [565.409s] [cast.receiver.MediaManager] onSeek: {"requestId":7,"mediaSessionId":2,"currentTime":0.087,"resumeState":"PLAYBACK_START"}
cast_receiver.js:40 [565.420s] [cast.receiver.MediaManager] Sending broadcast status message
cast_receiver.js:40 [565.426s] [cast.receiver.IpcChannel] IPC message sent: {"namespace":"urn:x-cast:com.google.cast.media","senderId":"*:*","data":"{\"type\":\"MEDIA_STATUS\",\"status\":[{\"mediaSessionId\":2,\"playbackRate\":1,\"playerState\":\"BUFFERING\",\"currentTime\":0.087,\"supportedMediaCommands\":15,\"volume\":{\"level\":1,\"muted\":false},\"activeTrackIds\":[],\"currentItemId\":2,\"repeatMode\":\"REPEAT_OFF\"}],\"requestId\":7}"}
player.html:1 Mixed Content: The page at 'https://www.gstatic.com/eureka/player/player.html?skin' was loaded over HTTPS, but requested an insecure video 'http://bc29.domain.me/fms/2848955552001/201512/video.mp4'. This content should also be served over HTTPS.
cast_receiver.js:40 [568.490s] [cast.receiver.MediaManager] Buffering state changed, isPlayerBuffering: false old time: 0.12 current time: 1.167672
cast_receiver.js:40 [568.515s] [cast.receiver.MediaManager] Sending broadcast status message
cast_receiver.js:40 [568.538s] [cast.receiver.IpcChannel] IPC message sent: {"namespace":"urn:x-cast:com.google.cast.media","senderId":"*:*","data":"{\"type\":\"MEDIA_STATUS\",\"status\":[{\"mediaSessionId\":2,\"playbackRate\":1,\"playerState\":\"PLAYING\",\"currentTime\":1.229581,\"supportedMediaCommands\":15,\"volume\":{\"level\":1,\"muted\":false},\"activeTrackIds\":[],\"currentItemId\":2,\"repeatMode\":\"REPEAT_OFF\"}],\"requestId\":0}"}
sender
12-17 15:19:55.803 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] attempting to play media at position 87 seconds
12-17 15:19:55.803 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] attempting to seek media
12-17 15:19:55.902 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] RemoteMediaPlayer::onStatusUpdated() is reached
12-17 15:19:55.902 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] onRemoteMediaPlayerStatusUpdated() reached
12-17 15:19:55.902 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] onQueueUpdated() reached
12-17 15:19:55.902 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] Queue Items size: 1, Item: com.google.android.gms.cast.MediaQueueItem@f2d65998, Repeat Mode: 0, Shuffle: false
12-17 15:19:55.902 5639-5639/net.domain.android D/QueueDataProvider: Queue is updated with a list of size: 1
12-17 15:19:55.902 5639-5639/net.domain.android D/QueueDataProvider: Queue was updated
12-17 15:19:55.905 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] [queue] Queue Item is: {"media":{"contentId":"http://bc29.domain.me/fms/2848955552001/201512/video.mp4","streamType":"BUFFERED","contentType":"video\/mp4","metadata":{"metadataType":1,"images":[{"url":"http:\/\/bc29.ajmn.me\/pd\/2848955552001\/201512\/1316\/2848955552001_4663173896001_RT-60-GOPCNN-TECHNOLOGYSECURITY-151215-FINAL-SUB-thumbnail.jpg?pubId=2848955552001","width":0,"height":0},{"url":"http:\/\/api.domain.net\/pictures\/video_image\/video_Still004.jpg","width":0,"height":0}],"title":"When the GOP geeks out on surveillance technologies","subtitle":"During the fifth GOP debate, candidates went on and on how technologies could prevent the next “terrorist attack.”\r\n"},"duration":89.931},"itemId":2,"autoplay":true,"startTime":0,"preloadTime":20}
12-17 15:19:55.905 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] onRemoteMediaPlayerStatusUpdated(): Player status = buffering
12-17 15:19:55.905 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] updateMiniControllersVisibility() reached with visibility: true
12-17 15:19:55.905 5639-5639/net.domain.android D/AJPBaseActivity: onRemoteMediaPlayerStatusUpdated()
12-17 15:19:55.905 5639-5639/net.domain.android D/ccl_VideoCastNotificat: [v2.5.1] onRemoteMediaPlayerStatusUpdated() reached with status: 4
12-17 15:19:59.123 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] RemoteMediaPlayer::onStatusUpdated() is reached
12-17 15:19:59.123 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] onRemoteMediaPlayerStatusUpdated() reached
12-17 15:19:59.124 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] onQueueUpdated() reached
12-17 15:19:59.124 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] Queue Items size: 1, Item: com.google.android.gms.cast.MediaQueueItem@f2d65998, Repeat Mode: 0, Shuffle: false
12-17 15:19:59.124 5639-5639/net.domain.android D/QueueDataProvider: Queue is updated with a list of size: 1
12-17 15:19:59.124 5639-5639/net.domain.android D/QueueDataProvider: Queue was updated
12-17 15:19:59.124 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] [queue] Queue Item is: {"media":{"contentId":"http://bc29.domain.me/fms/2848955552001/201512/video.mp4","streamType":"BUFFERED","contentType":"video\/mp4","metadata":{"metadataType":1,"images":[{"url":"http://bc29.domain.me/fms/2848955552001/201512/video_thumbnail.jpg?pubId=2848955552001","width":0,"height":0},{"url":"http://bc29.domain.me/fms/2848955552001/201512/video_Still004.jpg","width":0,"height":0}],"title":"When the GOP geeks out on surveillance technologies","subtitle":"During the fifth GOP debate, candidates went on and on how technologies could prevent the next “terrorist attack.”\r\n"},"duration":89.931},"itemId":2,"autoplay":true,"startTime":0,"preloadTime":20}
12-17 15:19:59.124 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] onRemoteMediaPlayerStatusUpdated(): Player status = playing
12-17 15:19:59.125 5639-5639/net.domain.android D/ccl_BaseCastManager: [v2.5.1] startReconnectionService() for media length lef = 88700
12-17 15:19:59.127 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] startNotificationService()
12-17 15:19:59.129 5639-5639/net.domain.android D/ccl_VideoCastManager: [v2.5.1] updateMiniControllersVisibility() reached with visibility: true
12-17 15:19:59.129 5639-5639/net.domain.android D/AJPBaseActivity: onRemoteMediaPlayerStatusUpdated()
12-17 15:19:59.129 5639-5639/net.domain.android D/ccl_VideoCastNotificat: [v2.5.1] onRemoteMediaPlayerStatusUpdated() reached with status: 2
12-17 15:19:59.129 5639-5639/net.domain.android D/ccl_ReconnectionService: [v2.5.1] onStartCommand() is called
12-17 15:19:59.129 5639-5639/net.domain.android D/ccl_ReconnectionService: [v2.5.1] setUpEndTimer(): setting up a timer for the end of current media
12-17 15:19:59.139 5639-5639/net.domain.android D/ccl_VideoCastNotificat: [v2.5.1] onStartCommand
12-17 15:19:59.139 5639-5639/net.domain.android D/ccl_VideoCastNotificat: [v2.5.1] onStartCommand(): Action: ACTION_VISIBILITY false
The time you are passing to "play(position)" should be in milliseconds but based on your logs, you are passing in seconds, so if you want to seek to 87 seconds, you need to pass 87000 to that method (see the javadoc on that method). The log statement in CCL should be corrected to reflect that(will fix in the next update) but the JavaDoc is correct.