| OLD | NEW |
| 1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser.media.remote; | 5 package org.chromium.chrome.browser.media.remote; |
| 6 | 6 |
| 7 import android.app.PendingIntent; | 7 import android.app.PendingIntent; |
| 8 import android.content.BroadcastReceiver; | 8 import android.content.BroadcastReceiver; |
| 9 import android.content.Context; | 9 import android.content.Context; |
| 10 import android.content.Intent; | 10 import android.content.Intent; |
| 11 import android.content.IntentFilter; | 11 import android.content.IntentFilter; |
| 12 import android.net.Uri; | 12 import android.net.Uri; |
| 13 import android.os.Bundle; | 13 import android.os.Bundle; |
| 14 import android.os.SystemClock; | 14 import android.os.SystemClock; |
| 15 import android.support.v7.media.MediaControlIntent; | 15 import android.support.v7.media.MediaControlIntent; |
| 16 import android.support.v7.media.MediaItemMetadata; | 16 import android.support.v7.media.MediaItemMetadata; |
| 17 import android.support.v7.media.MediaItemStatus; | 17 import android.support.v7.media.MediaItemStatus; |
| 18 import android.support.v7.media.MediaRouteSelector; | 18 import android.support.v7.media.MediaRouteSelector; |
| 19 import android.support.v7.media.MediaRouter; | 19 import android.support.v7.media.MediaRouter; |
| 20 import android.support.v7.media.MediaRouter.RouteInfo; | 20 import android.support.v7.media.MediaRouter.RouteInfo; |
| 21 import android.support.v7.media.MediaSessionStatus; | 21 import android.support.v7.media.MediaSessionStatus; |
| 22 import android.util.Log; | 22 import android.util.Log; |
| 23 import android.widget.Toast; | 23 import android.widget.Toast; |
| 24 | 24 |
| 25 import com.google.android.gms.cast.CastMediaControlIntent; | 25 import com.google.android.gms.cast.CastMediaControlIntent; |
| 26 | 26 |
| 27 import org.apache.http.Header; | |
| 28 import org.chromium.base.ApplicationState; | 27 import org.chromium.base.ApplicationState; |
| 29 import org.chromium.base.ApplicationStatus; | 28 import org.chromium.base.ApplicationStatus; |
| 30 import org.chromium.base.CommandLine; | 29 import org.chromium.base.CommandLine; |
| 31 import org.chromium.chrome.ChromeSwitches; | 30 import org.chromium.chrome.ChromeSwitches; |
| 32 import org.chromium.chrome.R; | 31 import org.chromium.chrome.R; |
| 33 import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState; | 32 import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState; |
| 34 | 33 |
| 35 import java.net.URI; | 34 import java.net.URI; |
| 36 import java.net.URISyntaxException; | 35 import java.net.URISyntaxException; |
| 37 | 36 |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 86 void onError(String message, Bundle data); | 85 void onError(String message, Bundle data); |
| 87 } | 86 } |
| 88 | 87 |
| 89 private static final String TAG = "DefaultMediaRouteController"; | 88 private static final String TAG = "DefaultMediaRouteController"; |
| 90 | 89 |
| 91 private static final String ACTION_RECEIVE_SESSION_STATUS_UPDATE = | 90 private static final String ACTION_RECEIVE_SESSION_STATUS_UPDATE = |
| 92 "com.google.android.apps.chrome.videofling.RECEIVE_SESSION_STATUS_UP
DATE"; | 91 "com.google.android.apps.chrome.videofling.RECEIVE_SESSION_STATUS_UP
DATE"; |
| 93 private static final String ACTION_RECEIVE_MEDIA_STATUS_UPDATE = | 92 private static final String ACTION_RECEIVE_MEDIA_STATUS_UPDATE = |
| 94 "com.google.android.apps.chrome.videofling.RECEIVE_MEDIA_STATUS_UPDA
TE"; | 93 "com.google.android.apps.chrome.videofling.RECEIVE_MEDIA_STATUS_UPDA
TE"; |
| 95 private static final String MIME_TYPE = "video/mp4"; | 94 private static final String MIME_TYPE = "video/mp4"; |
| 96 | |
| 97 private boolean mDebug; | 95 private boolean mDebug; |
| 98 private String mCurrentSessionId; | 96 private String mCurrentSessionId; |
| 99 private String mCurrentItemId; | 97 private String mCurrentItemId; |
| 100 private int mStreamPositionTimestamp; | 98 private int mStreamPositionTimestamp; |
| 101 private int mLastKnownStreamPosition; | 99 private int mLastKnownStreamPosition; |
| 102 private int mStreamDuration; | 100 private int mStreamDuration; |
| 103 private boolean mSeeking; | 101 private boolean mSeeking; |
| 104 private final String mIntentCategory; | 102 private final String mIntentCategory; |
| 105 private PendingIntent mSessionStatusUpdateIntent; | 103 private PendingIntent mSessionStatusUpdateIntent; |
| 106 private BroadcastReceiver mSessionStatusBroadcastReceiver; | 104 private BroadcastReceiver mSessionStatusBroadcastReceiver; |
| 107 private PendingIntent mMediaStatusUpdateIntent; | 105 private PendingIntent mMediaStatusUpdateIntent; |
| 108 private BroadcastReceiver mMediaStatusBroadcastReceiver; | 106 private BroadcastReceiver mMediaStatusBroadcastReceiver; |
| 109 private boolean mReconnecting = false; | 107 private boolean mReconnecting = false; |
| 110 | 108 |
| 111 private Uri mVideoUriToStart; | 109 private Uri mVideoUriToStart; |
| 112 private String mPreferredTitle; | 110 private String mPreferredTitle; |
| 113 private long mStartPositionMillis; | 111 private long mStartPositionMillis; |
| 114 | 112 |
| 115 private Uri mLocalVideoUri; | 113 private Uri mLocalVideoUri; |
| 116 | 114 |
| 117 private String mLocalVideoCookies; | 115 private String mLocalVideoCookies; |
| 118 | 116 |
| 119 private MediaUrlResolver mMediaUrlResolver; | 117 private MediaUrlResolver mMediaUrlResolver; |
| 120 | 118 |
| 121 private int mSessionState = MediaSessionStatus.SESSION_STATE_INVALIDATED; | 119 private int mSessionState = MediaSessionStatus.SESSION_STATE_INVALIDATED; |
| 122 | 120 |
| 123 // Media types supported for cast, see | |
| 124 // media/base/container_names.h for the actual enum where these are defined | |
| 125 private static final int UNKNOWN_MEDIA = 0; | |
| 126 private static final int SMOOTHSTREAM_MEDIA = 39; | |
| 127 private static final int DASH_MEDIA = 38; | |
| 128 private static final int HLS_MEDIA = 22; | |
| 129 private static final int MPEG4_MEDIA = 29; | |
| 130 | |
| 131 private final ApplicationStatus.ApplicationStateListener | 121 private final ApplicationStatus.ApplicationStateListener |
| 132 mApplicationStateListener = new ApplicationStatus.ApplicationStateLi
stener() { | 122 mApplicationStateListener = new ApplicationStatus.ApplicationStateLi
stener() { |
| 133 @Override | 123 @Override |
| 134 public void onApplicationStateChange(int newState) { | 124 public void onApplicationStateChange(int newState) { |
| 135 switch (newState) { | 125 switch (newState) { |
| 136 // HAS_DESTROYED_ACTIVITIES means all Chrome activities have
been destroyed. | 126 // HAS_DESTROYED_ACTIVITIES means all Chrome activities have
been destroyed. |
| 137 case ApplicationState.HAS_DESTROYED_ACTIVITIES: | 127 case ApplicationState.HAS_DESTROYED_ACTIVITIES: |
| 138 onActivitiesDestroyed(); | 128 onActivitiesDestroyed(); |
| 139 break; | 129 break; |
| 140 default: | 130 default: |
| 141 break; | 131 break; |
| 142 } | 132 } |
| 143 } | 133 } |
| 144 }; | 134 }; |
| 145 | 135 |
| 146 private final MediaUrlResolver.Delegate | 136 private final MediaUrlResolver.Delegate |
| 147 mMediaUrlResolverDelegate = new MediaUrlResolver.Delegate() { | 137 mMediaUrlResolverDelegate = new MediaUrlResolver.Delegate() { |
| 148 @Override | 138 @Override |
| 149 public Uri getUri() { | 139 public Uri getUri() { |
| 150 return mLocalVideoUri; | 140 return mLocalVideoUri; |
| 151 } | 141 } |
| 152 | 142 |
| 153 @Override | 143 @Override |
| 154 public String getCookies() { | 144 public String getCookies() { |
| 155 return mLocalVideoCookies; | 145 return mLocalVideoCookies; |
| 156 } | 146 } |
| 157 | 147 |
| 158 @Override | 148 @Override |
| 159 public void setUri(Uri uri, Header[] headers) { | 149 public void setUri(Uri uri, boolean playable) { |
| 160 if (canPlayMedia(uri, headers)) { | 150 if (playable) { |
| 161 mLocalVideoUri = uri; | 151 mLocalVideoUri = uri; |
| 162 playMedia(); | 152 playMedia(); |
| 163 return; | 153 return; |
| 164 } | 154 } |
| 165 mLocalVideoUri = null; | 155 mLocalVideoUri = null; |
| 166 showMessageToast( | 156 showMessageToast( |
| 167 getContext().getString(R.string.cast_permission_erro
r_playing_video)); | 157 getContext().getString(R.string.cast_permission_erro
r_playing_video)); |
| 168 release(); | 158 release(); |
| 169 } | 159 } |
| 170 }; | 160 }; |
| (...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 369 * Plays the given Uri on the currently selected player. This will replace a
ny currently playing | 359 * Plays the given Uri on the currently selected player. This will replace a
ny currently playing |
| 370 * video | 360 * video |
| 371 * | 361 * |
| 372 * @param videoUri Uri of the video to play | 362 * @param videoUri Uri of the video to play |
| 373 * @param preferredTitle the preferred title of the current playback session
to display | 363 * @param preferredTitle the preferred title of the current playback session
to display |
| 374 * @param startPositionMillis from which to start playing. | 364 * @param startPositionMillis from which to start playing. |
| 375 */ | 365 */ |
| 376 private void playUri(final Uri videoUri, | 366 private void playUri(final Uri videoUri, |
| 377 @Nullable final String preferredTitle, final long startPositionMilli
s) { | 367 @Nullable final String preferredTitle, final long startPositionMilli
s) { |
| 378 | 368 |
| 379 RecordCastAction.castMediaType(getMediaType(videoUri)); | 369 RecordCastAction.castMediaType(MediaUrlResolver.getMediaType(videoUri.to
String())); |
| 380 installBroadcastReceivers(); | 370 installBroadcastReceivers(); |
| 381 | 371 |
| 382 // Check if we are reconnecting or have reconnected and are playing the
same video | 372 // Check if we are reconnecting or have reconnected and are playing the
same video |
| 383 if ((mReconnecting || mCurrentSessionId != null) | 373 if ((mReconnecting || mCurrentSessionId != null) |
| 384 && videoUri.toString().equals(RemotePlaybackSettings.getUriPlayi
ng(getContext()))) { | 374 && videoUri.toString().equals(RemotePlaybackSettings.getUriPlayi
ng(getContext()))) { |
| 385 return; | 375 return; |
| 386 } | 376 } |
| 387 | 377 |
| 388 // If the session is already started (meaning we are casting a video alr
eady), we simply | 378 // If the session is already started (meaning we are casting a video alr
eady), we simply |
| 389 // load the new URL with one ACTION_PLAY intent. | 379 // load the new URL with one ACTION_PLAY intent. |
| (...skipping 650 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1040 if (mMediaUrlResolver != null) mMediaUrlResolver.cancel(true); | 1030 if (mMediaUrlResolver != null) mMediaUrlResolver.cancel(true); |
| 1041 | 1031 |
| 1042 // Create a new MediaUrlResolver since the previous one may still be run
ning despite the | 1032 // Create a new MediaUrlResolver since the previous one may still be run
ning despite the |
| 1043 // cancel() call. | 1033 // cancel() call. |
| 1044 mMediaUrlResolver = new MediaUrlResolver(getContext(), mMediaUrlResolver
Delegate); | 1034 mMediaUrlResolver = new MediaUrlResolver(getContext(), mMediaUrlResolver
Delegate); |
| 1045 | 1035 |
| 1046 mStartPositionMillis = startPositionMillis; | 1036 mStartPositionMillis = startPositionMillis; |
| 1047 mMediaUrlResolver.execute(); | 1037 mMediaUrlResolver.execute(); |
| 1048 } | 1038 } |
| 1049 | 1039 |
| 1050 private boolean canPlayMedia(Uri uri, Header[] headers) { | |
| 1051 if (uri == Uri.EMPTY) return false; | |
| 1052 | |
| 1053 // HLS media requires Cors headers. Since these are the only ones | |
| 1054 // sent now we can just check that headers is not empty but | |
| 1055 // if more headers are added we should be more strict in the check. | |
| 1056 if ((headers == null || headers.length == 0) && isEnhancedMedia(uri)) { | |
| 1057 if (mDebug) Log.d(TAG, "HLS stream without CORs header: " + uri); | |
| 1058 return false; | |
| 1059 } | |
| 1060 return true; | |
| 1061 } | |
| 1062 | |
| 1063 private void playMedia() { | 1040 private void playMedia() { |
| 1064 String title = getMediaStateListener().getTitle(); | 1041 String title = getMediaStateListener().getTitle(); |
| 1065 playUri(mLocalVideoUri, title, mStartPositionMillis); | 1042 playUri(mLocalVideoUri, title, mStartPositionMillis); |
| 1066 } | 1043 } |
| 1067 | 1044 |
| 1068 private void showMessageToast(String message) { | 1045 private void showMessageToast(String message) { |
| 1069 Toast toast = Toast.makeText(getContext(), message, Toast.LENGTH_SHORT); | 1046 Toast toast = Toast.makeText(getContext(), message, Toast.LENGTH_SHORT); |
| 1070 toast.show(); | 1047 toast.show(); |
| 1071 } | 1048 } |
| 1072 | 1049 |
| 1073 private void configureNewSession(Bundle data) { | 1050 private void configureNewSession(Bundle data) { |
| 1074 mCurrentSessionId = data.getString(MediaControlIntent.EXTRA_SESSION_ID); | 1051 mCurrentSessionId = data.getString(MediaControlIntent.EXTRA_SESSION_ID); |
| 1075 mSessionState = MediaSessionStatus.SESSION_STATE_INVALIDATED; | 1052 mSessionState = MediaSessionStatus.SESSION_STATE_INVALIDATED; |
| 1076 RemotePlaybackSettings.setSessionId(getContext(), mCurrentSessionId); | 1053 RemotePlaybackSettings.setSessionId(getContext(), mCurrentSessionId); |
| 1077 if (mDebug) Log.d(TAG, "Got a session id: " + mCurrentSessionId); | 1054 if (mDebug) Log.d(TAG, "Got a session id: " + mCurrentSessionId); |
| 1078 } | 1055 } |
| 1079 | |
| 1080 private int getMediaType(Uri videoUri) { | |
| 1081 String uriString = videoUri.toString(); | |
| 1082 if (uriString.contains(".m3u8")) { | |
| 1083 return HLS_MEDIA; | |
| 1084 } | |
| 1085 if (uriString.contains(".mp4")) { | |
| 1086 return MPEG4_MEDIA; | |
| 1087 } | |
| 1088 if (uriString.contains(".mpd")) { | |
| 1089 return DASH_MEDIA; | |
| 1090 } | |
| 1091 if (uriString.contains(".ism")) { | |
| 1092 return SMOOTHSTREAM_MEDIA; | |
| 1093 } | |
| 1094 return UNKNOWN_MEDIA; | |
| 1095 } | |
| 1096 | |
| 1097 private boolean isEnhancedMedia(Uri videoUri) { | |
| 1098 int mediaType = getMediaType(videoUri); | |
| 1099 return mediaType == HLS_MEDIA || mediaType == DASH_MEDIA || mediaType ==
SMOOTHSTREAM_MEDIA; | |
| 1100 } | |
| 1101 } | 1056 } |
| OLD | NEW |