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

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

Powered by Google App Engine
This is Rietveld 408576698