Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: webkit/media/android/webmediaplayer_android.cc

Issue 10073016: Upstream WebMediaPlayerAndroid as WebKit::WebMediaPlayer implementation on android. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: addressing feedbacks Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "webkit/media/android/webmediaplayer_android.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/file_path.h"
12 #include "base/logging.h"
13 #include "base/utf_string_conversions.h"
14 #include "media/base/android/media_player_bridge.h"
15 #include "net/base/mime_util.h"
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayerClient. h"
17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
18 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCookieJar .h"
19 #include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebRect.h"
20 #include "webkit/media/android/webmediaplayer_proxy_android.h"
21 #include "webkit/media/webmediaplayer_util.h"
22 #include "webkit/media/webvideoframe_impl.h"
23
24 using WebKit::WebCanvas;
25 using WebKit::WebMediaPlayerClient;
26 using WebKit::WebMediaPlayer;
27 using WebKit::WebRect;
28 using WebKit::WebSize;
29 using WebKit::WebTimeRanges;
30 using WebKit::WebURL;
31 using WebKit::WebVideoFrame;
32 using media::MediaPlayerBridge;
33 using media::VideoFrame;
34 using webkit_media::WebVideoFrameImpl;
35
36 namespace webkit_media {
37
38 bool WebMediaPlayerAndroid::incognito_mode_ = false;
39
40 WebMediaPlayerAndroid::WebMediaPlayerAndroid(
41 WebMediaPlayerClient* client,
42 WebKit::WebCookieJar* cookie_jar)
43 : client_(client),
44 proxy_(new WebMediaPlayerProxyAndroid(base::MessageLoopProxy::current(),
45 AsWeakPtr())),
46 prepared_(false),
47 duration_(0),
48 pending_seek_(0),
49 seeking_(false),
50 playback_completed_(false),
51 buffered_bytes_(0),
52 cookie_jar_(cookie_jar),
53 pending_play_event_(false),
54 network_state_(WebMediaPlayer::Empty),
55 ready_state_(WebMediaPlayer::HaveNothing) {
56 video_frame_.reset(new WebVideoFrameImpl(VideoFrame::CreateEmptyFrame()));
57 }
58
59 WebMediaPlayerAndroid::~WebMediaPlayerAndroid() {
60 if (media_player_.get()) {
61 media_player_->Stop();
62 }
63 }
64
65 void WebMediaPlayerAndroid::InitIncognito(bool incognito_mode) {
66 incognito_mode_ = incognito_mode;
67 }
68
69 void WebMediaPlayerAndroid::load(const WebURL& url) {
70 url_ = url;
71
72 UpdateNetworkState(WebMediaPlayer::Loading);
73 UpdateReadyState(WebMediaPlayer::HaveNothing);
74
75 // Calling InitializeMediaPlayer will cause android mediaplayer to start
scherkus (not reviewing) 2012/04/21 03:02:05 nit: append () to the end of function names in com
qinmin 2012/04/23 18:57:04 Done.
76 // buffering and decoding the data. On mobile devices, this costs a lot of
77 // data usage and could even introduce performance issues. So we don't
78 // initialize the player unless it is a local file. We will start loading
79 // the media only when play/seek/fullsceen button is clicked.
80 if (url_.SchemeIs("file")) {
81 InitializeMediaPlayer();
82 return;
83 }
84
85 // TODO(qinmin): we need a method to calculate the duration of the media.
86 // Android does not provide any function to do that.
87 // Set the initial duration value to 100 seconds so that user can touch
88 // the seek bar to perform seek. We will scale the seek position later
89 // when we got the actual duration.
90 duration_ = 100;
scherkus (not reviewing) 2012/04/21 03:02:05 can we get a constant for this? also does 100 val
qinmin 2012/04/23 18:57:04 Done. Added a const and TODO in the beginning of t
91
92 // Pretend everything has been loaded so that webkit can
93 // still call play() and seek().
94 UpdateReadyState(WebMediaPlayer::HaveMetadata);
95 UpdateReadyState(WebMediaPlayer::HaveEnoughData);
96 }
97
98 void WebMediaPlayerAndroid::cancelLoad() {
99 NOTIMPLEMENTED();
100 }
101
102 void WebMediaPlayerAndroid::play() {
103 if (media_player_.get()) {
104 if (!prepared_)
105 pending_play_event_ = true;
106 else
107 PlayInternal();
108 } else {
109 pending_play_event_ = true;
110 InitializeMediaPlayer();
111 }
112 }
113
114 void WebMediaPlayerAndroid::pause() {
115 if (media_player_.get()) {
116 if (!prepared_)
117 pending_play_event_ = false;
118 else
119 PauseInternal();
120 } else {
121 // We don't need to load media if pause() is called.
122 pending_play_event_ = false;
123 }
124 }
125
126 void WebMediaPlayerAndroid::seek(float seconds) {
127 // Record the time to seek when OnMediaPrepared() is called.
128 pending_seek_ = seconds;
129
130 // Reset playback_completed_ so that we return the correct current time.
scherkus (not reviewing) 2012/04/21 03:02:05 nit: add || around variable names
qinmin 2012/04/23 18:57:04 Done.
131 playback_completed_ = false;
132
133 if (media_player_.get()) {
134 if (prepared_)
135 SeekInternal(seconds);
136 } else {
137 InitializeMediaPlayer();
138 }
139 }
140
141 bool WebMediaPlayerAndroid::supportsFullscreen() const {
142 return true;
143 }
144
145 bool WebMediaPlayerAndroid::supportsSave() const {
146 return false;
147 }
148
149 void WebMediaPlayerAndroid::setEndTime(float seconds) {
150 // Deprecated.
151 // TODO(qinmin): Remove this from WebKit::WebMediaPlayer as it is never used.
152 }
153
154 void WebMediaPlayerAndroid::setRate(float rate) {
155 NOTIMPLEMENTED();
156 }
157
158 void WebMediaPlayerAndroid::setVolume(float volume) {
159 if (media_player_.get())
160 media_player_->SetVolume(volume, volume);
161 }
162
163 void WebMediaPlayerAndroid::setVisible(bool visible) {
164 // Deprecated.
165 // TODO(qinmin): Remove this from WebKit::WebMediaPlayer as it is never used.
166 }
167
168 bool WebMediaPlayerAndroid::totalBytesKnown() {
169 NOTIMPLEMENTED();
170 return false;
171 }
172
173 bool WebMediaPlayerAndroid::hasVideo() const {
174 // TODO(qinmin): need a better method to determine whether the current media
175 // content contains video. Android does not provide any function to do
176 // this.
177 // We don't know whether the current media content has video unless
178 // the player is prepared. If the player is not prepared, we fall back
179 // to the mime-type. There may be no mime-type on a redirect URL.
180 // In that case, we conservatively assume it contains video so that
181 // enterfullscreen call will not fail.
182 if (!prepared_) {
183 if (!url_.has_path())
184 return false;
185 std::string mime;
186 if(!net::GetMimeTypeFromFile(FilePath(url_.path()), &mime))
187 return true;
188 return mime.find("audio/") == std::string::npos;
189 }
190
191 return !natural_size_.isEmpty();
192 }
193
194 bool WebMediaPlayerAndroid::hasAudio() const {
195 // TODO(hclam): Query status of audio and return the actual value.
196 return true;
197 }
198
199 bool WebMediaPlayerAndroid::paused() const {
200 if (!prepared_)
201 return !pending_play_event_;
202 return !media_player_->IsPlaying();
203 }
204
205 bool WebMediaPlayerAndroid::seeking() const {
206 return seeking_;
207 }
208
209 float WebMediaPlayerAndroid::duration() const {
210 return duration_;
211 }
212
213 float WebMediaPlayerAndroid::currentTime() const {
214 // If the player is pending for a seek, return the seek time.
215 if (!prepared_ || seeking())
216 return pending_seek_;
217
218 // When playback is about to finish, android media player often stops
219 // at a time which is smaller than the duration. This makes webkit never
220 // know that the playback has finished. To solve this, we set the
221 // current time to media duration when OnPlaybackComplete() get called.
222 // And return the greater of the two values so that the current
223 // time is most updated.
224 if (playback_completed_)
225 return duration();
226 return static_cast<float> (media_player_->GetCurrentTime().InSecondsF());
scherkus (not reviewing) 2012/04/21 03:02:05 nit: no space between > and (
qinmin 2012/04/23 18:57:04 Done.
227 }
228
229 int WebMediaPlayerAndroid::dataRate() const {
230 // Deprecated.
231 return 0;
232 }
233
234 WebSize WebMediaPlayerAndroid::naturalSize() const {
235 return natural_size_;
236 }
237
238 WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const {
239 return network_state_;
240 }
241
242 WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const {
243 return ready_state_;
244 }
245
246 const WebTimeRanges& WebMediaPlayerAndroid::buffered() {
247 return time_ranges_;
scherkus (not reviewing) 2012/04/21 03:02:05 this should really be called buffered_
qinmin 2012/04/23 18:57:04 Done.
248 }
249
250 float WebMediaPlayerAndroid::maxTimeSeekable() const {
251 // TODO(hclam): If this stream is not seekable this should return 0.
252 return duration();
253 }
254
255 unsigned long long WebMediaPlayerAndroid::bytesLoaded() const {
256 return buffered_bytes_;
257 }
258
259 unsigned long long WebMediaPlayerAndroid::totalBytes() const {
260 // Deprecated.
261 return 0;
262 }
263
264 void WebMediaPlayerAndroid::setSize(const WebSize& size) {
265 texture_size_ = size;
266 }
267
268 void WebMediaPlayerAndroid::paint(WebCanvas* canvas, const WebRect& rect) {
269 NOTIMPLEMENTED();
270 }
271
272 bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const {
273 return false;
274 }
275
276 WebMediaPlayer::MovieLoadType
277 WebMediaPlayerAndroid::movieLoadType() const {
278 // Deprecated.
279 return WebMediaPlayer::Unknown;
280 }
281
282 float WebMediaPlayerAndroid::mediaTimeForTimeValue(float timeValue) const {
283 return ConvertSecondsToTimestamp(timeValue).InSecondsF();
284 }
285
286 unsigned WebMediaPlayerAndroid::decodedFrameCount() const {
287 NOTIMPLEMENTED();
288 return 0;
289 }
290
291 unsigned WebMediaPlayerAndroid::droppedFrameCount() const {
292 NOTIMPLEMENTED();
293 return 0;
294 }
295
296 unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const {
297 NOTIMPLEMENTED();
298 return 0;
299 }
300
301 unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const {
302 NOTIMPLEMENTED();
303 return 0;
304 }
305
306 void WebMediaPlayerAndroid::OnMediaPrepared() {
307 WebKit::WebTimeRanges new_buffered(static_cast<size_t>(1));
scherkus (not reviewing) 2012/04/21 03:02:05 is there any harm in always having time_ranges_ se
qinmin 2012/04/23 18:57:04 Done.
308 time_ranges_.swap(new_buffered);
309 prepared_ = true;
310
311 // Update the media duration first so that webkit will get the correct
312 // duration when UpdateReadyState is called.
313 float dur = duration_;
314 duration_ = media_player_->GetDuration().InSecondsF();
315
316 if (url_.SchemeIs("file"))
317 UpdateNetworkState(WebMediaPlayer::Loaded);
318
319 if (ready_state_ != WebMediaPlayer::HaveEnoughData) {
320 UpdateReadyState(WebMediaPlayer::HaveMetadata);
321 UpdateReadyState(WebMediaPlayer::HaveEnoughData);
322 } else {
323 // If the status is already set to HaveEnoughData, set it again to make sure
324 // that Videolayerchromium will get created.
325 UpdateReadyState(WebMediaPlayer::HaveEnoughData);
326 }
327
328 if (!url_.SchemeIs("file")) {
329 // In we have skipped loading, the duration was preset to 100 sec.
330 // We have to update webkit about the new duration.
331 if (duration_ != dur) {
332 // Scale the pending_seek_ according to the new duration.
scherkus (not reviewing) 2012/04/21 03:02:05 nit: add || around var names
qinmin 2012/04/23 18:57:04 Done.
333 pending_seek_ = pending_seek_ * duration_ / 100.0f;
334 client_->durationChanged();
335 }
336 }
337
338 // If media player was recovered from a saved state, consume all the pending
339 // events.
340 seek(pending_seek_);
341
342 if (pending_play_event_)
343 PlayInternal();
344
345 pending_play_event_ = false;
346 }
347
348 void WebMediaPlayerAndroid::OnPlaybackComplete() {
349 // Set the current time equal to duration to let webkit know that play back
350 // is completed.
351 playback_completed_ = true;
352 client_->timeChanged();
353 }
354
355 void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) {
356 if (time_ranges_.size() > 0) {
357 time_ranges_[0].end = duration() * percentage / 100;
358 // Implement a trick here to fake progress event, as WebKit checks
359 // consecutive bytesLoaded() to see if any progress made.
360 // See HTMLMediaElement::progressEventTimerFired.
361 // TODO(qinmin): need a method to calculate the buffered bytes.
362 buffered_bytes_++;
363 }
364 }
365
366 void WebMediaPlayerAndroid::OnSeekComplete() {
367 seeking_ = false;
368
369 UpdateReadyState(WebMediaPlayer::HaveEnoughData);
370
371 client_->timeChanged();
372 }
373
374 void WebMediaPlayerAndroid::OnMediaError(int error_type) {
375 switch (error_type) {
376 case MediaPlayerBridge::MEDIA_ERROR_UNKNOWN:
377 // When playing an bogus URL or bad file we fire a MEDIA_ERROR_UNKNOWN.
378 // As WebKit uses FormatError to indicate an error for bogus URL or bad
379 // file we default a MEDIA_ERROR_UNKNOWN to FormatError.
380 UpdateNetworkState(WebMediaPlayer::FormatError);
381 break;
382 case MediaPlayerBridge::MEDIA_ERROR_SERVER_DIED:
383 // TODO(zhenghao): Media server died. In this case, the application must
384 // release the MediaPlayer object and instantiate a new one.
385 UpdateNetworkState(WebMediaPlayer::DecodeError);
386 break;
387 case MediaPlayerBridge::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:
388 UpdateNetworkState(WebMediaPlayer::FormatError);
389 break;
390 case MediaPlayerBridge::MEDIA_ERROR_INVALID_CODE:
391 break;
392 }
393 client_->repaint();
394 }
395
396 void WebMediaPlayerAndroid::OnMediaInfo(int info_type) {
397 NOTIMPLEMENTED();
398 }
399
400 void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) {
401 natural_size_.width = width;
402 natural_size_.height = height;
403 }
404
405 void WebMediaPlayerAndroid::UpdateNetworkState(
406 WebMediaPlayer::NetworkState state) {
407 network_state_ = state;
408 client_->networkStateChanged();
409 }
410
411 void WebMediaPlayerAndroid::UpdateReadyState(
412 WebMediaPlayer::ReadyState state) {
413 ready_state_ = state;
414 client_->readyStateChanged();
415 }
416
417 void WebMediaPlayerAndroid::SetVideoSurface(jobject j_surface) {
418 if (media_player_.get())
419 media_player_->SetVideoSurface(j_surface);
420 }
421
422 void WebMediaPlayerAndroid::InitializeMediaPlayer() {
423 CHECK(!media_player_.get());
424 prepared_ = false;
425 media_player_.reset(new MediaPlayerBridge());
426 media_player_->SetStayAwakeWhilePlaying();
427
428 std::string cookies;
429 if (cookie_jar_ != NULL) {
430 WebURL url(url_);
431 cookies = UTF16ToUTF8(cookie_jar_->cookies(url, url));
432 }
433 media_player_->SetDataSource(url_.spec(), cookies, incognito_mode_);
434
435 media_player_->Prepare(
436 base::Bind(&WebMediaPlayerProxyAndroid::MediaInfoCallback, proxy_),
437 base::Bind(&WebMediaPlayerProxyAndroid::MediaErrorCallback, proxy_),
438 base::Bind(&WebMediaPlayerProxyAndroid::VideoSizeChangedCallback, proxy_),
439 base::Bind(&WebMediaPlayerProxyAndroid::BufferingUpdateCallback, proxy_),
440 base::Bind(&WebMediaPlayerProxyAndroid::MediaPreparedCallback, proxy_));
441 }
442
443 void WebMediaPlayerAndroid::PlayInternal() {
444 CHECK(prepared_);
445
446 if (paused())
447 media_player_->Start(base::Bind(
448 &WebMediaPlayerProxyAndroid::PlaybackCompleteCallback, proxy_));
449 }
450
451 void WebMediaPlayerAndroid::PauseInternal() {
452 CHECK(prepared_);
453 media_player_->Pause();
454 }
455
456 void WebMediaPlayerAndroid::SeekInternal(float seconds) {
457 CHECK(prepared_);
458 seeking_ = true;
459 media_player_->SeekTo(ConvertSecondsToTimestamp(seconds), base::Bind(
460 &WebMediaPlayerProxyAndroid::SeekCompleteCallback, proxy_));
461 }
462
463 WebVideoFrame* WebMediaPlayerAndroid::getCurrentFrame() {
464 return video_frame_.get();
465 }
466
467 void WebMediaPlayerAndroid::putCurrentFrame(
468 WebVideoFrame* web_video_frame) {
469 }
470
471 } // namespace webkit_media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698