Loading packages/MediaComponents/res/layout/media_controller.xml +1 −1 Original line number Diff line number Diff line Loading @@ -104,7 +104,7 @@ <view class="com.android.support.mediarouter.app.MediaRouteButton" android:id="@+id/cast" android:layout_centerVertical="true" android:visibility="visible" android:visibility="gone" style="@style/TitleBarButton" /> </LinearLayout> Loading packages/MediaComponents/src/com/android/media/MediaSession2Impl.java +1 −1 Original line number Diff line number Diff line Loading @@ -460,7 +460,7 @@ public class MediaSession2Impl implements MediaSession2Provider { /* final MediaPlayerBase player = mPlayer; if (player != null) { // TODO implement, use the SessionPlaylistController itf // TODO implement and use SessionPlaylistAgent //player.setPlaylist(playlist); mSessionStub.notifyPlaylistChanged(playlist); } else if (DEBUG) { Loading packages/MediaComponents/src/com/android/media/RoutePlayer.java 0 → 100644 +169 −0 Original line number Diff line number Diff line /* * Copyright 2018 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.media; import android.content.Context; import android.media.DataSourceDesc; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.annotation.RequiresApi; import com.android.support.mediarouter.media.MediaItemStatus; import com.android.support.mediarouter.media.MediaRouter; import com.android.support.mediarouter.media.MediaSessionStatus; import com.android.support.mediarouter.media.RemotePlaybackClient; import com.android.support.mediarouter.media.RemotePlaybackClient.ItemActionCallback; import com.android.support.mediarouter.media.RemotePlaybackClient.SessionActionCallback; import com.android.support.mediarouter.media.RemotePlaybackClient.StatusCallback; import java.util.Map; @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public class RoutePlayer extends MediaSession.Callback { public static final long PLAYBACK_ACTIONS = PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY | PlaybackState.ACTION_SEEK_TO | PlaybackState.ACTION_FAST_FORWARD | PlaybackState.ACTION_REWIND; private RemotePlaybackClient mClient; private String mSessionId; private String mItemId; private PlayerEventCallback mCallback; private StatusCallback mStatusCallback = new StatusCallback() { @Override public void onItemStatusChanged(Bundle data, String sessionId, MediaSessionStatus sessionStatus, String itemId, MediaItemStatus itemStatus) { updateSessionStatus(sessionId, sessionStatus); updateItemStatus(itemId, itemStatus); } }; public RoutePlayer(Context context, MediaRouter.RouteInfo route) { mClient = new RemotePlaybackClient(context, route); mClient.setStatusCallback(mStatusCallback); mClient.startSession(null, new SessionActionCallback() { @Override public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) { updateSessionStatus(sessionId, sessionStatus); } }); } @Override public void onPlay() { mClient.resume(null, new SessionActionCallback() { @Override public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) { updateSessionStatus(sessionId, sessionStatus); } }); } @Override public void onPause() { mClient.pause(null, new SessionActionCallback() { @Override public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) { updateSessionStatus(sessionId, sessionStatus); } }); } @Override public void onSeekTo(long pos) { mClient.seek(mItemId, pos, null, new ItemActionCallback() { @Override public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus, String itemId, MediaItemStatus itemStatus) { updateSessionStatus(sessionId, sessionStatus); updateItemStatus(itemId, itemStatus); } }); } @Override public void onStop() { mClient.stop(null, new SessionActionCallback() { @Override public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) { updateSessionStatus(sessionId, sessionStatus); } }); } public void setPlayerEventCallback(PlayerEventCallback callback) { mCallback = callback; } public void openVideo(DataSourceDesc dsd) { mClient.play(dsd.getUri(), "video/mp4", null, 0, null, new ItemActionCallback() { @Override public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus, String itemId, MediaItemStatus itemStatus) { updateSessionStatus(sessionId, sessionStatus); updateItemStatus(itemId, itemStatus); playInternal(dsd.getUri()); } }); } public void release() { if (mClient != null) { mClient.release(); mClient = null; } if (mCallback != null) { mCallback = null; } } private void playInternal(Uri uri) { mClient.play(uri, "video/mp4", null, 0, null, new ItemActionCallback() { @Override public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus, String itemId, MediaItemStatus itemStatus) { updateSessionStatus(sessionId, sessionStatus); updateItemStatus(itemId, itemStatus); } }); } private void updateSessionStatus(String sessionId, MediaSessionStatus sessionStatus) { mSessionId = sessionId; } private void updateItemStatus(String itemId, MediaItemStatus itemStatus) { mItemId = itemId; if (itemStatus == null || mCallback == null) return; mCallback.onPlayerStateChanged(itemStatus); } public static abstract class PlayerEventCallback { public void onPlayerStateChanged(MediaItemStatus itemStatus) { } } } packages/MediaComponents/src/com/android/widget/VideoView2Impl.java +142 −146 Original line number Diff line number Diff line Loading @@ -16,9 +16,7 @@ package com.android.widget; import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.media.AudioAttributes; import android.media.AudioFocusRequest; import android.media.AudioManager; Loading @@ -27,7 +25,6 @@ import android.media.MediaMetadata; import android.media.MediaPlayer2; import android.media.MediaPlayer2.MediaPlayer2EventCallback; import android.media.MediaPlayer2Impl; import android.media.MediaPlayerBase; import android.media.Cea708CaptionRenderer; import android.media.ClosedCaptionRenderer; import android.media.MediaItem2; Loading @@ -47,6 +44,7 @@ import android.media.SessionToken2; import android.media.update.VideoView2Provider; import android.media.update.ViewGroupProvider; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.ResultReceiver; import android.support.annotation.Nullable; Loading @@ -60,15 +58,12 @@ import android.view.accessibility.AccessibilityManager; import android.widget.MediaControlView2; import android.widget.VideoView2; import com.android.media.update.ApiHelper; import com.android.media.update.R; import com.android.media.RoutePlayer; import com.android.support.mediarouter.media.MediaItemStatus; import com.android.support.mediarouter.media.MediaControlIntent; import com.android.support.mediarouter.media.MediaRouter; import com.android.support.mediarouter.media.MediaRouteSelector; import com.android.support.mediarouter.media.RemotePlaybackClient; import com.android.support.mediarouter.media.MediaItemStatus; import com.android.support.mediarouter.media.MediaSessionStatus; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; Loading Loading @@ -106,12 +101,10 @@ public class VideoView2Impl extends BaseLayout private VideoSurfaceView mSurfaceView; private MediaPlayer2 mMediaPlayer; private DataSourceDesc mDsd; private MediaControlView2 mMediaControlView; private MediaSession mMediaSession; private MediaController mMediaController; private MediaSession.Callback mRouteSessionCallback = new RouteSessionCallback(); private MediaRouter mMediaRouter; private MediaRouteSelector mRouteSelector; private Metadata mMetadata; private MediaMetadata2 mMediaMetadata; private boolean mNeedUpdateMediaType; Loading Loading @@ -144,9 +137,80 @@ public class VideoView2Impl extends BaseLayout // TODO: Remove mFallbackSpeed when integration with MediaPlayer2's new setPlaybackParams(). // Refer: https://docs.google.com/document/d/1nzAfns6i2hJ3RkaUre3QMT6wsDedJ5ONLiA_OOBFFX8/edit private float mFallbackSpeed; // keep the original speed before 'pause' is called. private long mShowControllerIntervalMs; private MediaRouter mMediaRouter; private MediaRouteSelector mRouteSelector; private MediaRouter.RouteInfo mRoute; private RoutePlayer mRoutePlayer; private final MediaRouter.Callback mRouterCallback = new MediaRouter.Callback() { @Override public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) { if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { // Stop local playback (if necessary) resetPlayer(); mRoute = route; mRoutePlayer = new RoutePlayer(mInstance.getContext(), route); mRoutePlayer.setPlayerEventCallback(new RoutePlayer.PlayerEventCallback() { @Override public void onPlayerStateChanged(MediaItemStatus itemStatus) { PlaybackState.Builder psBuilder = new PlaybackState.Builder(); psBuilder.setActions(RoutePlayer.PLAYBACK_ACTIONS); long position = itemStatus.getContentPosition(); switch (itemStatus.getPlaybackState()) { case MediaItemStatus.PLAYBACK_STATE_PENDING: psBuilder.setState(PlaybackState.STATE_NONE, position, 0); mCurrentState = STATE_IDLE; break; case MediaItemStatus.PLAYBACK_STATE_PLAYING: psBuilder.setState(PlaybackState.STATE_PLAYING, position, 1); mCurrentState = STATE_PLAYING; break; case MediaItemStatus.PLAYBACK_STATE_PAUSED: psBuilder.setState(PlaybackState.STATE_PAUSED, position, 0); mCurrentState = STATE_PAUSED; break; case MediaItemStatus.PLAYBACK_STATE_BUFFERING: psBuilder.setState(PlaybackState.STATE_BUFFERING, position, 0); mCurrentState = STATE_PAUSED; break; case MediaItemStatus.PLAYBACK_STATE_FINISHED: psBuilder.setState(PlaybackState.STATE_STOPPED, position, 0); mCurrentState = STATE_PLAYBACK_COMPLETED; break; } PlaybackState pbState = psBuilder.build(); mMediaSession.setPlaybackState(pbState); MediaMetadata.Builder mmBuilder = new MediaMetadata.Builder(); mmBuilder.putLong(MediaMetadata.METADATA_KEY_DURATION, itemStatus.getContentDuration()); mMediaSession.setMetadata(mmBuilder.build()); } }); // Start remote playback (if necessary) mRoutePlayer.openVideo(mDsd); } } @Override public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route, int reason) { if (mRoute != null && mRoutePlayer != null) { mRoutePlayer.release(); mRoutePlayer = null; } if (mRoute == route) { mRoute = null; } if (reason != MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) { // TODO: Resume local playback (if necessary) openVideo(mDsd); } } }; public VideoView2Impl(VideoView2 instance, ViewGroupProvider superProvider, ViewGroupProvider privateProvider) { super(instance, superProvider, privateProvider); Loading Loading @@ -220,16 +284,20 @@ public class VideoView2Impl extends BaseLayout Log.d(TAG, "viewType attribute is textureView."); // TODO: implement } MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder(); builder.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); builder.addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO); builder.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO); mRouteSelector = builder.build(); } @Override public void setMediaControlView2_impl(MediaControlView2 mediaControlView, long intervalMs) { mMediaControlView = mediaControlView; mShowControllerIntervalMs = intervalMs; if (mRouteSelector != null) { ((MediaControlView2Impl) mMediaControlView.getProvider()) .setRouteSelector(mRouteSelector); } // TODO: Call MediaControlView2.setRouteSelector only when cast availalbe. ((MediaControlView2Impl) mMediaControlView.getProvider()).setRouteSelector(mRouteSelector); if (mInstance.isAttachedToWindow()) { attachMediaControlView(); Loading Loading @@ -328,30 +396,6 @@ public class VideoView2Impl extends BaseLayout mAudioAttributes = attributes; } @Override public void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerBase player) { // TODO: implement this. } @Override public void setRouteAttributes_impl(@NonNull List<String> routeCategories, MediaSession.Callback sessionPlayer) { MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder(); for (String category : routeCategories) { builder.addControlCategory(category); } mRouteSelector = builder.build(); if (mMediaControlView != null) { ((MediaControlView2Impl) mMediaControlView.getProvider()) .setRouteSelector(mRouteSelector); } mMediaRouter = MediaRouter.getInstance(mInstance.getContext()); mRouteSessionCallback = sessionPlayer; if (mMediaSession != null) { mMediaRouter.setMediaSession(mMediaSession); } } @Override public void setVideoPath_impl(String path) { mInstance.setVideoUri(Uri.parse(path)); Loading @@ -376,6 +420,7 @@ public class VideoView2Impl extends BaseLayout @Override public void setDataSource_impl(DataSourceDesc dsd) { mDsd = dsd; mSeekWhenPrepared = 0; openVideo(dsd); } Loading Loading @@ -434,10 +479,11 @@ public class VideoView2Impl extends BaseLayout // Create MediaSession mMediaSession = new MediaSession(mInstance.getContext(), "VideoView2MediaSession"); mMediaSession.setCallback(new MediaSessionCallback()); mMediaSession.setActive(true); mMediaController = mMediaSession.getController(); if (mMediaRouter != null) { mMediaRouter = MediaRouter.getInstance(mInstance.getContext()); mMediaRouter.setMediaSession(mMediaSession); } mMediaRouter.addCallback(mRouteSelector, mRouterCallback); attachMediaControlView(); // TODO: remove this after moving MediaSession creating code inside initializing VideoView2 if (mCurrentState == STATE_PREPARED) { Loading Loading @@ -548,16 +594,16 @@ public class VideoView2Impl extends BaseLayout } private boolean isInPlaybackState() { return (mMediaPlayer != null return (mMediaPlayer != null || mRoutePlayer != null) && mCurrentState != STATE_ERROR && mCurrentState != STATE_IDLE && mCurrentState != STATE_PREPARING); && mCurrentState != STATE_PREPARING; } private boolean needToStart() { return (mMediaPlayer != null return (mMediaPlayer != null || mRoutePlayer != null) && mCurrentState != STATE_PLAYING && mTargetState == STATE_PLAYING); && mTargetState == STATE_PLAYING; } // Creates a MediaPlayer2 instance and prepare playback. Loading @@ -565,6 +611,10 @@ public class VideoView2Impl extends BaseLayout Uri uri = dsd.getUri(); Map<String, String> headers = dsd.getUriHeaders(); resetPlayer(); if (isRemotePlayback()) { mRoutePlayer.openVideo(dsd); return; } if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) { // TODO this should have a focus listener AudioFocusRequest focusRequest; Loading Loading @@ -634,9 +684,18 @@ public class VideoView2Impl extends BaseLayout */ private void resetPlayer() { if (mMediaPlayer != null) { mMediaPlayer.reset(); mMediaPlayer.close(); final MediaPlayer2 player = mMediaPlayer; new AsyncTask<MediaPlayer2, Void, Void>() { @Override protected Void doInBackground(MediaPlayer2... players) { // TODO: Fix NPE while MediaPlayer2.close() //players[0].close(); return null; } }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, player); mMediaPlayer = null; mTextureView.setMediaPlayer(null); mSurfaceView.setMediaPlayer(null); //mPendingSubtitleTracks.clear(); mCurrentState = STATE_IDLE; mTargetState = STATE_IDLE; Loading Loading @@ -785,8 +844,8 @@ public class VideoView2Impl extends BaseLayout return false; } PlaybackInfo playbackInfo = mMediaController.getPlaybackInfo(); return (playbackInfo != null) && (playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE); return playbackInfo != null && playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE; } private void selectOrDeselectSubtitle(boolean select) { Loading Loading @@ -985,7 +1044,7 @@ public class VideoView2Impl extends BaseLayout @Override public void onCommand(String command, Bundle args, ResultReceiver receiver) { if (isRemotePlayback()) { mRouteSessionCallback.onCommand(command, args, receiver); mRoutePlayer.onCommand(command, args, receiver); } else { switch (command) { case MediaControlView2Impl.COMMAND_SHOW_SUBTITLE: Loading Loading @@ -1015,31 +1074,32 @@ public class VideoView2Impl extends BaseLayout @Override public void onPlay() { if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) { if (isRemotePlayback()) { mRouteSessionCallback.onPlay(); mRoutePlayer.onPlay(); } else { if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) { applySpeed(); mMediaPlayer.play(); mCurrentState = STATE_PLAYING; updatePlaybackState(); } mCurrentState = STATE_PLAYING; } mTargetState = STATE_PLAYING; if (DEBUG) { Log.d(TAG, "onPlay(). mCurrentState=" + mCurrentState + ", mTargetState=" + mTargetState); } } showController(); } @Override public void onPause() { if (isRemotePlayback()) { mRouteSessionCallback.onPause(); } else { if (isInPlaybackState()) { if (mMediaPlayer.isPlaying()) { if (isRemotePlayback()) { mRoutePlayer.onPause(); mCurrentState = STATE_PAUSED; } else if (mMediaPlayer.isPlaying()) { mMediaPlayer.pause(); mCurrentState = STATE_PAUSED; updatePlaybackState(); Loading @@ -1050,97 +1110,33 @@ public class VideoView2Impl extends BaseLayout Log.d(TAG, "onPause(). mCurrentState=" + mCurrentState + ", mTargetState=" + mTargetState); } } showController(); } @Override public void onSeekTo(long pos) { if (isInPlaybackState()) { if (isRemotePlayback()) { mRouteSessionCallback.onSeekTo(pos); mRoutePlayer.onSeekTo(pos); } else { if (isInPlaybackState()) { mMediaPlayer.seekTo(pos, MediaPlayer2.SEEK_PREVIOUS_SYNC); mSeekWhenPrepared = 0; updatePlaybackState(); } } else { mSeekWhenPrepared = pos; } } showController(); } @Override public void onStop() { if (isRemotePlayback()) { mRouteSessionCallback.onStop(); mRoutePlayer.onStop(); } else { resetPlayer(); } showController(); } } private class RouteSessionCallback extends MediaSession.Callback { RemotePlaybackClient mClient; RemotePlaybackClient.StatusCallback mStatusCallback = new RemotePlaybackClient.StatusCallback() { @Override public void onItemStatusChanged(Bundle data, String sessionId, MediaSessionStatus sessionStatus, String itemId, MediaItemStatus itemStatus) { // TODO: implement this } @Override public void onSessionStatusChanged(Bundle data, String sessionId, MediaSessionStatus sessionStatus) { // TODO: implement this } @Override public void onSessionChanged(String sessionId) { // TODO: implement this } }; @Override public void onCommand(String command, Bundle args, ResultReceiver receiver) { ensureClient(); // TODO: implement this } @Override public void onPlay() { ensureClient(); // TODO: implement this } @Override public void onPause() { ensureClient(); // TODO: implement this } @Override public void onSeekTo(long pos) { ensureClient(); // TODO: implement this } @Override public void onStop() { ensureClient(); // TODO: implement this } private void ensureClient() { if (mClient == null) { mClient = new RemotePlaybackClient( mInstance.getContext(), mMediaRouter.getSelectedRoute()); mClient.setStatusCallback(mStatusCallback); } } } } Loading
packages/MediaComponents/res/layout/media_controller.xml +1 −1 Original line number Diff line number Diff line Loading @@ -104,7 +104,7 @@ <view class="com.android.support.mediarouter.app.MediaRouteButton" android:id="@+id/cast" android:layout_centerVertical="true" android:visibility="visible" android:visibility="gone" style="@style/TitleBarButton" /> </LinearLayout> Loading
packages/MediaComponents/src/com/android/media/MediaSession2Impl.java +1 −1 Original line number Diff line number Diff line Loading @@ -460,7 +460,7 @@ public class MediaSession2Impl implements MediaSession2Provider { /* final MediaPlayerBase player = mPlayer; if (player != null) { // TODO implement, use the SessionPlaylistController itf // TODO implement and use SessionPlaylistAgent //player.setPlaylist(playlist); mSessionStub.notifyPlaylistChanged(playlist); } else if (DEBUG) { Loading
packages/MediaComponents/src/com/android/media/RoutePlayer.java 0 → 100644 +169 −0 Original line number Diff line number Diff line /* * Copyright 2018 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.media; import android.content.Context; import android.media.DataSourceDesc; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.annotation.RequiresApi; import com.android.support.mediarouter.media.MediaItemStatus; import com.android.support.mediarouter.media.MediaRouter; import com.android.support.mediarouter.media.MediaSessionStatus; import com.android.support.mediarouter.media.RemotePlaybackClient; import com.android.support.mediarouter.media.RemotePlaybackClient.ItemActionCallback; import com.android.support.mediarouter.media.RemotePlaybackClient.SessionActionCallback; import com.android.support.mediarouter.media.RemotePlaybackClient.StatusCallback; import java.util.Map; @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public class RoutePlayer extends MediaSession.Callback { public static final long PLAYBACK_ACTIONS = PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY | PlaybackState.ACTION_SEEK_TO | PlaybackState.ACTION_FAST_FORWARD | PlaybackState.ACTION_REWIND; private RemotePlaybackClient mClient; private String mSessionId; private String mItemId; private PlayerEventCallback mCallback; private StatusCallback mStatusCallback = new StatusCallback() { @Override public void onItemStatusChanged(Bundle data, String sessionId, MediaSessionStatus sessionStatus, String itemId, MediaItemStatus itemStatus) { updateSessionStatus(sessionId, sessionStatus); updateItemStatus(itemId, itemStatus); } }; public RoutePlayer(Context context, MediaRouter.RouteInfo route) { mClient = new RemotePlaybackClient(context, route); mClient.setStatusCallback(mStatusCallback); mClient.startSession(null, new SessionActionCallback() { @Override public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) { updateSessionStatus(sessionId, sessionStatus); } }); } @Override public void onPlay() { mClient.resume(null, new SessionActionCallback() { @Override public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) { updateSessionStatus(sessionId, sessionStatus); } }); } @Override public void onPause() { mClient.pause(null, new SessionActionCallback() { @Override public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) { updateSessionStatus(sessionId, sessionStatus); } }); } @Override public void onSeekTo(long pos) { mClient.seek(mItemId, pos, null, new ItemActionCallback() { @Override public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus, String itemId, MediaItemStatus itemStatus) { updateSessionStatus(sessionId, sessionStatus); updateItemStatus(itemId, itemStatus); } }); } @Override public void onStop() { mClient.stop(null, new SessionActionCallback() { @Override public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus) { updateSessionStatus(sessionId, sessionStatus); } }); } public void setPlayerEventCallback(PlayerEventCallback callback) { mCallback = callback; } public void openVideo(DataSourceDesc dsd) { mClient.play(dsd.getUri(), "video/mp4", null, 0, null, new ItemActionCallback() { @Override public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus, String itemId, MediaItemStatus itemStatus) { updateSessionStatus(sessionId, sessionStatus); updateItemStatus(itemId, itemStatus); playInternal(dsd.getUri()); } }); } public void release() { if (mClient != null) { mClient.release(); mClient = null; } if (mCallback != null) { mCallback = null; } } private void playInternal(Uri uri) { mClient.play(uri, "video/mp4", null, 0, null, new ItemActionCallback() { @Override public void onResult(Bundle data, String sessionId, MediaSessionStatus sessionStatus, String itemId, MediaItemStatus itemStatus) { updateSessionStatus(sessionId, sessionStatus); updateItemStatus(itemId, itemStatus); } }); } private void updateSessionStatus(String sessionId, MediaSessionStatus sessionStatus) { mSessionId = sessionId; } private void updateItemStatus(String itemId, MediaItemStatus itemStatus) { mItemId = itemId; if (itemStatus == null || mCallback == null) return; mCallback.onPlayerStateChanged(itemStatus); } public static abstract class PlayerEventCallback { public void onPlayerStateChanged(MediaItemStatus itemStatus) { } } }
packages/MediaComponents/src/com/android/widget/VideoView2Impl.java +142 −146 Original line number Diff line number Diff line Loading @@ -16,9 +16,7 @@ package com.android.widget; import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.media.AudioAttributes; import android.media.AudioFocusRequest; import android.media.AudioManager; Loading @@ -27,7 +25,6 @@ import android.media.MediaMetadata; import android.media.MediaPlayer2; import android.media.MediaPlayer2.MediaPlayer2EventCallback; import android.media.MediaPlayer2Impl; import android.media.MediaPlayerBase; import android.media.Cea708CaptionRenderer; import android.media.ClosedCaptionRenderer; import android.media.MediaItem2; Loading @@ -47,6 +44,7 @@ import android.media.SessionToken2; import android.media.update.VideoView2Provider; import android.media.update.ViewGroupProvider; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.ResultReceiver; import android.support.annotation.Nullable; Loading @@ -60,15 +58,12 @@ import android.view.accessibility.AccessibilityManager; import android.widget.MediaControlView2; import android.widget.VideoView2; import com.android.media.update.ApiHelper; import com.android.media.update.R; import com.android.media.RoutePlayer; import com.android.support.mediarouter.media.MediaItemStatus; import com.android.support.mediarouter.media.MediaControlIntent; import com.android.support.mediarouter.media.MediaRouter; import com.android.support.mediarouter.media.MediaRouteSelector; import com.android.support.mediarouter.media.RemotePlaybackClient; import com.android.support.mediarouter.media.MediaItemStatus; import com.android.support.mediarouter.media.MediaSessionStatus; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; Loading Loading @@ -106,12 +101,10 @@ public class VideoView2Impl extends BaseLayout private VideoSurfaceView mSurfaceView; private MediaPlayer2 mMediaPlayer; private DataSourceDesc mDsd; private MediaControlView2 mMediaControlView; private MediaSession mMediaSession; private MediaController mMediaController; private MediaSession.Callback mRouteSessionCallback = new RouteSessionCallback(); private MediaRouter mMediaRouter; private MediaRouteSelector mRouteSelector; private Metadata mMetadata; private MediaMetadata2 mMediaMetadata; private boolean mNeedUpdateMediaType; Loading Loading @@ -144,9 +137,80 @@ public class VideoView2Impl extends BaseLayout // TODO: Remove mFallbackSpeed when integration with MediaPlayer2's new setPlaybackParams(). // Refer: https://docs.google.com/document/d/1nzAfns6i2hJ3RkaUre3QMT6wsDedJ5ONLiA_OOBFFX8/edit private float mFallbackSpeed; // keep the original speed before 'pause' is called. private long mShowControllerIntervalMs; private MediaRouter mMediaRouter; private MediaRouteSelector mRouteSelector; private MediaRouter.RouteInfo mRoute; private RoutePlayer mRoutePlayer; private final MediaRouter.Callback mRouterCallback = new MediaRouter.Callback() { @Override public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) { if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { // Stop local playback (if necessary) resetPlayer(); mRoute = route; mRoutePlayer = new RoutePlayer(mInstance.getContext(), route); mRoutePlayer.setPlayerEventCallback(new RoutePlayer.PlayerEventCallback() { @Override public void onPlayerStateChanged(MediaItemStatus itemStatus) { PlaybackState.Builder psBuilder = new PlaybackState.Builder(); psBuilder.setActions(RoutePlayer.PLAYBACK_ACTIONS); long position = itemStatus.getContentPosition(); switch (itemStatus.getPlaybackState()) { case MediaItemStatus.PLAYBACK_STATE_PENDING: psBuilder.setState(PlaybackState.STATE_NONE, position, 0); mCurrentState = STATE_IDLE; break; case MediaItemStatus.PLAYBACK_STATE_PLAYING: psBuilder.setState(PlaybackState.STATE_PLAYING, position, 1); mCurrentState = STATE_PLAYING; break; case MediaItemStatus.PLAYBACK_STATE_PAUSED: psBuilder.setState(PlaybackState.STATE_PAUSED, position, 0); mCurrentState = STATE_PAUSED; break; case MediaItemStatus.PLAYBACK_STATE_BUFFERING: psBuilder.setState(PlaybackState.STATE_BUFFERING, position, 0); mCurrentState = STATE_PAUSED; break; case MediaItemStatus.PLAYBACK_STATE_FINISHED: psBuilder.setState(PlaybackState.STATE_STOPPED, position, 0); mCurrentState = STATE_PLAYBACK_COMPLETED; break; } PlaybackState pbState = psBuilder.build(); mMediaSession.setPlaybackState(pbState); MediaMetadata.Builder mmBuilder = new MediaMetadata.Builder(); mmBuilder.putLong(MediaMetadata.METADATA_KEY_DURATION, itemStatus.getContentDuration()); mMediaSession.setMetadata(mmBuilder.build()); } }); // Start remote playback (if necessary) mRoutePlayer.openVideo(mDsd); } } @Override public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route, int reason) { if (mRoute != null && mRoutePlayer != null) { mRoutePlayer.release(); mRoutePlayer = null; } if (mRoute == route) { mRoute = null; } if (reason != MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) { // TODO: Resume local playback (if necessary) openVideo(mDsd); } } }; public VideoView2Impl(VideoView2 instance, ViewGroupProvider superProvider, ViewGroupProvider privateProvider) { super(instance, superProvider, privateProvider); Loading Loading @@ -220,16 +284,20 @@ public class VideoView2Impl extends BaseLayout Log.d(TAG, "viewType attribute is textureView."); // TODO: implement } MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder(); builder.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); builder.addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO); builder.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO); mRouteSelector = builder.build(); } @Override public void setMediaControlView2_impl(MediaControlView2 mediaControlView, long intervalMs) { mMediaControlView = mediaControlView; mShowControllerIntervalMs = intervalMs; if (mRouteSelector != null) { ((MediaControlView2Impl) mMediaControlView.getProvider()) .setRouteSelector(mRouteSelector); } // TODO: Call MediaControlView2.setRouteSelector only when cast availalbe. ((MediaControlView2Impl) mMediaControlView.getProvider()).setRouteSelector(mRouteSelector); if (mInstance.isAttachedToWindow()) { attachMediaControlView(); Loading Loading @@ -328,30 +396,6 @@ public class VideoView2Impl extends BaseLayout mAudioAttributes = attributes; } @Override public void setRouteAttributes_impl(List<String> routeCategories, MediaPlayerBase player) { // TODO: implement this. } @Override public void setRouteAttributes_impl(@NonNull List<String> routeCategories, MediaSession.Callback sessionPlayer) { MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder(); for (String category : routeCategories) { builder.addControlCategory(category); } mRouteSelector = builder.build(); if (mMediaControlView != null) { ((MediaControlView2Impl) mMediaControlView.getProvider()) .setRouteSelector(mRouteSelector); } mMediaRouter = MediaRouter.getInstance(mInstance.getContext()); mRouteSessionCallback = sessionPlayer; if (mMediaSession != null) { mMediaRouter.setMediaSession(mMediaSession); } } @Override public void setVideoPath_impl(String path) { mInstance.setVideoUri(Uri.parse(path)); Loading @@ -376,6 +420,7 @@ public class VideoView2Impl extends BaseLayout @Override public void setDataSource_impl(DataSourceDesc dsd) { mDsd = dsd; mSeekWhenPrepared = 0; openVideo(dsd); } Loading Loading @@ -434,10 +479,11 @@ public class VideoView2Impl extends BaseLayout // Create MediaSession mMediaSession = new MediaSession(mInstance.getContext(), "VideoView2MediaSession"); mMediaSession.setCallback(new MediaSessionCallback()); mMediaSession.setActive(true); mMediaController = mMediaSession.getController(); if (mMediaRouter != null) { mMediaRouter = MediaRouter.getInstance(mInstance.getContext()); mMediaRouter.setMediaSession(mMediaSession); } mMediaRouter.addCallback(mRouteSelector, mRouterCallback); attachMediaControlView(); // TODO: remove this after moving MediaSession creating code inside initializing VideoView2 if (mCurrentState == STATE_PREPARED) { Loading Loading @@ -548,16 +594,16 @@ public class VideoView2Impl extends BaseLayout } private boolean isInPlaybackState() { return (mMediaPlayer != null return (mMediaPlayer != null || mRoutePlayer != null) && mCurrentState != STATE_ERROR && mCurrentState != STATE_IDLE && mCurrentState != STATE_PREPARING); && mCurrentState != STATE_PREPARING; } private boolean needToStart() { return (mMediaPlayer != null return (mMediaPlayer != null || mRoutePlayer != null) && mCurrentState != STATE_PLAYING && mTargetState == STATE_PLAYING); && mTargetState == STATE_PLAYING; } // Creates a MediaPlayer2 instance and prepare playback. Loading @@ -565,6 +611,10 @@ public class VideoView2Impl extends BaseLayout Uri uri = dsd.getUri(); Map<String, String> headers = dsd.getUriHeaders(); resetPlayer(); if (isRemotePlayback()) { mRoutePlayer.openVideo(dsd); return; } if (mAudioFocusType != AudioManager.AUDIOFOCUS_NONE) { // TODO this should have a focus listener AudioFocusRequest focusRequest; Loading Loading @@ -634,9 +684,18 @@ public class VideoView2Impl extends BaseLayout */ private void resetPlayer() { if (mMediaPlayer != null) { mMediaPlayer.reset(); mMediaPlayer.close(); final MediaPlayer2 player = mMediaPlayer; new AsyncTask<MediaPlayer2, Void, Void>() { @Override protected Void doInBackground(MediaPlayer2... players) { // TODO: Fix NPE while MediaPlayer2.close() //players[0].close(); return null; } }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, player); mMediaPlayer = null; mTextureView.setMediaPlayer(null); mSurfaceView.setMediaPlayer(null); //mPendingSubtitleTracks.clear(); mCurrentState = STATE_IDLE; mTargetState = STATE_IDLE; Loading Loading @@ -785,8 +844,8 @@ public class VideoView2Impl extends BaseLayout return false; } PlaybackInfo playbackInfo = mMediaController.getPlaybackInfo(); return (playbackInfo != null) && (playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE); return playbackInfo != null && playbackInfo.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE; } private void selectOrDeselectSubtitle(boolean select) { Loading Loading @@ -985,7 +1044,7 @@ public class VideoView2Impl extends BaseLayout @Override public void onCommand(String command, Bundle args, ResultReceiver receiver) { if (isRemotePlayback()) { mRouteSessionCallback.onCommand(command, args, receiver); mRoutePlayer.onCommand(command, args, receiver); } else { switch (command) { case MediaControlView2Impl.COMMAND_SHOW_SUBTITLE: Loading Loading @@ -1015,31 +1074,32 @@ public class VideoView2Impl extends BaseLayout @Override public void onPlay() { if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) { if (isRemotePlayback()) { mRouteSessionCallback.onPlay(); mRoutePlayer.onPlay(); } else { if (isInPlaybackState() && mCurrentView.hasAvailableSurface()) { applySpeed(); mMediaPlayer.play(); mCurrentState = STATE_PLAYING; updatePlaybackState(); } mCurrentState = STATE_PLAYING; } mTargetState = STATE_PLAYING; if (DEBUG) { Log.d(TAG, "onPlay(). mCurrentState=" + mCurrentState + ", mTargetState=" + mTargetState); } } showController(); } @Override public void onPause() { if (isRemotePlayback()) { mRouteSessionCallback.onPause(); } else { if (isInPlaybackState()) { if (mMediaPlayer.isPlaying()) { if (isRemotePlayback()) { mRoutePlayer.onPause(); mCurrentState = STATE_PAUSED; } else if (mMediaPlayer.isPlaying()) { mMediaPlayer.pause(); mCurrentState = STATE_PAUSED; updatePlaybackState(); Loading @@ -1050,97 +1110,33 @@ public class VideoView2Impl extends BaseLayout Log.d(TAG, "onPause(). mCurrentState=" + mCurrentState + ", mTargetState=" + mTargetState); } } showController(); } @Override public void onSeekTo(long pos) { if (isInPlaybackState()) { if (isRemotePlayback()) { mRouteSessionCallback.onSeekTo(pos); mRoutePlayer.onSeekTo(pos); } else { if (isInPlaybackState()) { mMediaPlayer.seekTo(pos, MediaPlayer2.SEEK_PREVIOUS_SYNC); mSeekWhenPrepared = 0; updatePlaybackState(); } } else { mSeekWhenPrepared = pos; } } showController(); } @Override public void onStop() { if (isRemotePlayback()) { mRouteSessionCallback.onStop(); mRoutePlayer.onStop(); } else { resetPlayer(); } showController(); } } private class RouteSessionCallback extends MediaSession.Callback { RemotePlaybackClient mClient; RemotePlaybackClient.StatusCallback mStatusCallback = new RemotePlaybackClient.StatusCallback() { @Override public void onItemStatusChanged(Bundle data, String sessionId, MediaSessionStatus sessionStatus, String itemId, MediaItemStatus itemStatus) { // TODO: implement this } @Override public void onSessionStatusChanged(Bundle data, String sessionId, MediaSessionStatus sessionStatus) { // TODO: implement this } @Override public void onSessionChanged(String sessionId) { // TODO: implement this } }; @Override public void onCommand(String command, Bundle args, ResultReceiver receiver) { ensureClient(); // TODO: implement this } @Override public void onPlay() { ensureClient(); // TODO: implement this } @Override public void onPause() { ensureClient(); // TODO: implement this } @Override public void onSeekTo(long pos) { ensureClient(); // TODO: implement this } @Override public void onStop() { ensureClient(); // TODO: implement this } private void ensureClient() { if (mClient == null) { mClient = new RemotePlaybackClient( mInstance.getContext(), mMediaRouter.getSelectedRoute()); mClient.setStatusCallback(mStatusCallback); } } } }