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 |