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

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

Powered by Google App Engine
This is Rietveld 408576698