OLD | NEW |
| (Empty) |
1 // Copyright 2013 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/renderer/media/android/webmediaplayer_android.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/command_line.h" | |
9 #include "base/files/file_path.h" | |
10 #include "base/logging.h" | |
11 #include "base/metrics/histogram.h" | |
12 #include "base/strings/string_number_conversions.h" | |
13 #include "cc/layers/video_layer.h" | |
14 #include "gpu/GLES2/gl2extchromium.h" | |
15 #include "media/base/android/media_player_android.h" | |
16 #include "media/base/bind_to_loop.h" | |
17 #include "media/base/media_switches.h" | |
18 #include "media/base/video_frame.h" | |
19 #include "net/base/mime_util.h" | |
20 #include "third_party/WebKit/public/platform/WebString.h" | |
21 #include "third_party/WebKit/public/web/WebDocument.h" | |
22 #include "third_party/WebKit/public/web/WebFrame.h" | |
23 #include "third_party/WebKit/public/web/WebMediaPlayerClient.h" | |
24 #include "third_party/WebKit/public/web/WebMediaSource.h" | |
25 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" | |
26 #include "third_party/WebKit/public/web/WebView.h" | |
27 #include "webkit/renderer/compositor_bindings/web_layer_impl.h" | |
28 #include "webkit/renderer/media/android/webmediaplayer_manager_android.h" | |
29 #include "webkit/renderer/media/android/webmediaplayer_proxy_android.h" | |
30 #include "webkit/renderer/media/crypto/key_systems.h" | |
31 #include "webkit/renderer/media/webmediaplayer_delegate.h" | |
32 #include "webkit/renderer/media/webmediaplayer_util.h" | |
33 | |
34 #if defined(GOOGLE_TV) | |
35 #include "webkit/renderer/media/media_stream_audio_renderer.h" | |
36 #include "webkit/renderer/media/media_stream_client.h" | |
37 #endif | |
38 | |
39 static const uint32 kGLTextureExternalOES = 0x8D65; | |
40 | |
41 using WebKit::WebMediaPlayer; | |
42 using WebKit::WebMediaSource; | |
43 using WebKit::WebSize; | |
44 using WebKit::WebString; | |
45 using WebKit::WebTimeRanges; | |
46 using WebKit::WebURL; | |
47 using media::MediaPlayerAndroid; | |
48 using media::VideoFrame; | |
49 | |
50 namespace { | |
51 // Prefix for histograms related to Encrypted Media Extensions. | |
52 const char* kMediaEme = "Media.EME."; | |
53 } // namespace | |
54 | |
55 namespace webkit_media { | |
56 | |
57 #define BIND_TO_RENDER_LOOP(function) \ | |
58 media::BindToLoop(main_loop_, base::Bind(function, AsWeakPtr())) | |
59 | |
60 WebMediaPlayerAndroid::WebMediaPlayerAndroid( | |
61 WebKit::WebFrame* frame, | |
62 WebKit::WebMediaPlayerClient* client, | |
63 base::WeakPtr<WebMediaPlayerDelegate> delegate, | |
64 WebMediaPlayerManagerAndroid* manager, | |
65 WebMediaPlayerProxyAndroid* proxy, | |
66 StreamTextureFactory* factory, | |
67 media::MediaLog* media_log) | |
68 : frame_(frame), | |
69 client_(client), | |
70 delegate_(delegate), | |
71 buffered_(1u), | |
72 main_loop_(base::MessageLoopProxy::current()), | |
73 ignore_metadata_duration_change_(false), | |
74 pending_seek_(0), | |
75 seeking_(false), | |
76 did_loading_progress_(false), | |
77 manager_(manager), | |
78 network_state_(WebMediaPlayer::NetworkStateEmpty), | |
79 ready_state_(WebMediaPlayer::ReadyStateHaveNothing), | |
80 is_playing_(false), | |
81 needs_establish_peer_(true), | |
82 stream_texture_proxy_initialized_(false), | |
83 has_size_info_(false), | |
84 has_media_metadata_(false), | |
85 has_media_info_(false), | |
86 stream_texture_factory_(factory), | |
87 needs_external_surface_(false), | |
88 video_frame_provider_client_(NULL), | |
89 #if defined(GOOGLE_TV) | |
90 demuxer_(NULL), | |
91 #endif // defined(GOOGLE_TV) | |
92 source_type_(MediaPlayerAndroid::SOURCE_TYPE_URL), | |
93 proxy_(proxy), | |
94 current_time_(0), | |
95 media_log_(media_log), | |
96 media_stream_client_(NULL) { | |
97 DCHECK(proxy_); | |
98 DCHECK(manager_); | |
99 | |
100 // We want to be notified of |main_loop_| destruction. | |
101 base::MessageLoop::current()->AddDestructionObserver(this); | |
102 | |
103 player_id_ = manager_->RegisterMediaPlayer(this); | |
104 | |
105 if (stream_texture_factory_) { | |
106 stream_texture_proxy_.reset(stream_texture_factory_->CreateProxy()); | |
107 stream_id_ = stream_texture_factory_->CreateStreamTexture(&texture_id_); | |
108 ReallocateVideoFrame(); | |
109 } | |
110 | |
111 if (WebKit::WebRuntimeFeatures::isLegacyEncryptedMediaEnabled()) { | |
112 // |decryptor_| is owned, so Unretained() is safe here. | |
113 decryptor_.reset(new ProxyDecryptor( | |
114 #if defined(ENABLE_PEPPER_CDMS) | |
115 client, | |
116 frame, | |
117 #endif // defined(ENABLE_PEPPER_CDMS) | |
118 #if defined(OS_ANDROID) && !defined(GOOGLE_TV) | |
119 proxy_, | |
120 player_id_, // TODO(xhwang): Use media_keys_id when MediaKeys are | |
121 // separated from WebMediaPlayer. | |
122 #endif // defined(OS_ANDROID) && !defined(GOOGLE_TV) | |
123 base::Bind(&WebMediaPlayerAndroid::OnKeyAdded, base::Unretained(this)), | |
124 base::Bind(&WebMediaPlayerAndroid::OnKeyError, base::Unretained(this)), | |
125 base::Bind(&WebMediaPlayerAndroid::OnKeyMessage, | |
126 base::Unretained(this)))); | |
127 } | |
128 } | |
129 | |
130 WebMediaPlayerAndroid::~WebMediaPlayerAndroid() { | |
131 SetVideoFrameProviderClient(NULL); | |
132 client_->setWebLayer(NULL); | |
133 | |
134 if (proxy_) | |
135 proxy_->DestroyPlayer(player_id_); | |
136 | |
137 if (stream_id_) | |
138 stream_texture_factory_->DestroyStreamTexture(texture_id_); | |
139 | |
140 if (manager_) | |
141 manager_->UnregisterMediaPlayer(player_id_); | |
142 | |
143 if (base::MessageLoop::current()) | |
144 base::MessageLoop::current()->RemoveDestructionObserver(this); | |
145 | |
146 if (source_type_ == MediaPlayerAndroid::SOURCE_TYPE_MSE && delegate_) | |
147 delegate_->PlayerGone(this); | |
148 | |
149 #if defined(GOOGLE_TV) | |
150 if (audio_renderer_) { | |
151 if (audio_renderer_->IsLocalRenderer()) { | |
152 audio_renderer_->Stop(); | |
153 } else if (!paused()) { | |
154 // The |audio_renderer_| can be shared by multiple remote streams, and | |
155 // it will be stopped when WebRtcAudioDeviceImpl goes away. So we simply | |
156 // pause the |audio_renderer_| here to avoid re-creating the | |
157 // |audio_renderer_|. | |
158 audio_renderer_->Pause(); | |
159 } | |
160 } | |
161 if (demuxer_ && !destroy_demuxer_cb_.is_null()) { | |
162 media_source_delegate_.reset(); | |
163 destroy_demuxer_cb_.Run(); | |
164 } | |
165 #endif | |
166 } | |
167 | |
168 void WebMediaPlayerAndroid::load(const WebURL& url, CORSMode cors_mode) { | |
169 load(url, NULL, cors_mode); | |
170 } | |
171 | |
172 void WebMediaPlayerAndroid::load(const WebURL& url, | |
173 WebMediaSource* media_source, | |
174 CORSMode cors_mode) { | |
175 source_type_ = MediaPlayerAndroid::SOURCE_TYPE_URL; | |
176 has_media_metadata_ = false; | |
177 has_media_info_ = false; | |
178 | |
179 if (media_source) | |
180 source_type_ = MediaPlayerAndroid::SOURCE_TYPE_MSE; | |
181 #if defined(GOOGLE_TV) | |
182 if (media_stream_client_) { | |
183 DCHECK(!media_source); | |
184 source_type_ = MediaPlayerAndroid::SOURCE_TYPE_STREAM; | |
185 } | |
186 #endif | |
187 | |
188 if (source_type_ != MediaPlayerAndroid::SOURCE_TYPE_URL) { | |
189 has_media_info_ = true; | |
190 media_source_delegate_.reset( | |
191 new MediaSourceDelegate(proxy_, player_id_, media_log_)); | |
192 // |media_source_delegate_| is owned, so Unretained() is safe here. | |
193 if (source_type_ == MediaPlayerAndroid::SOURCE_TYPE_MSE) { | |
194 media_source_delegate_->InitializeMediaSource( | |
195 media_source, | |
196 base::Bind(&WebMediaPlayerAndroid::OnNeedKey, base::Unretained(this)), | |
197 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState, | |
198 base::Unretained(this)), | |
199 BIND_TO_RENDER_LOOP(&WebMediaPlayerAndroid::OnDurationChange)); | |
200 } | |
201 #if defined(GOOGLE_TV) | |
202 if (source_type_ == MediaPlayerAndroid::SOURCE_TYPE_STREAM) { | |
203 media_source_delegate_->InitializeMediaStream( | |
204 demuxer_, | |
205 base::Bind(&WebMediaPlayerAndroid::UpdateNetworkState, | |
206 base::Unretained(this))); | |
207 audio_renderer_ = media_stream_client_->GetAudioRenderer(url); | |
208 if (audio_renderer_) | |
209 audio_renderer_->Start(); | |
210 } | |
211 #endif | |
212 } else { | |
213 info_loader_.reset( | |
214 new MediaInfoLoader( | |
215 url, | |
216 cors_mode, | |
217 base::Bind(&WebMediaPlayerAndroid::DidLoadMediaInfo, | |
218 base::Unretained(this)))); | |
219 info_loader_->Start(frame_); | |
220 } | |
221 | |
222 InitializeMediaPlayer(url); | |
223 } | |
224 | |
225 void WebMediaPlayerAndroid::InitializeMediaPlayer(const WebURL& url) { | |
226 url_ = url; | |
227 GURL first_party_url = frame_->document().firstPartyForCookies(); | |
228 proxy_->Initialize(player_id_, url, source_type_, first_party_url); | |
229 if (manager_->IsInFullscreen(frame_)) | |
230 proxy_->EnterFullscreen(player_id_); | |
231 | |
232 UpdateNetworkState(WebMediaPlayer::NetworkStateLoading); | |
233 UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing); | |
234 } | |
235 | |
236 void WebMediaPlayerAndroid::DidLoadMediaInfo( | |
237 MediaInfoLoader::Status status) { | |
238 DCHECK(!media_source_delegate_); | |
239 if (status == MediaInfoLoader::kFailed) { | |
240 info_loader_.reset(); | |
241 UpdateNetworkState(WebMediaPlayer::NetworkStateNetworkError); | |
242 return; | |
243 } | |
244 | |
245 has_media_info_ = true; | |
246 if (has_media_metadata_ && | |
247 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) { | |
248 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata); | |
249 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData); | |
250 } | |
251 } | |
252 | |
253 void WebMediaPlayerAndroid::play() { | |
254 #if defined(GOOGLE_TV) | |
255 if (hasVideo() && needs_external_surface_) { | |
256 DCHECK(!needs_establish_peer_); | |
257 proxy_->RequestExternalSurface(player_id_, last_computed_rect_); | |
258 } | |
259 if (audio_renderer_ && paused()) | |
260 audio_renderer_->Play(); | |
261 #endif | |
262 if (hasVideo() && needs_establish_peer_) | |
263 EstablishSurfaceTexturePeer(); | |
264 | |
265 if (paused()) | |
266 proxy_->Start(player_id_); | |
267 UpdatePlayingState(true); | |
268 } | |
269 | |
270 void WebMediaPlayerAndroid::pause() { | |
271 #if defined(GOOGLE_TV) | |
272 if (audio_renderer_ && !paused()) | |
273 audio_renderer_->Pause(); | |
274 #endif | |
275 proxy_->Pause(player_id_); | |
276 UpdatePlayingState(false); | |
277 } | |
278 | |
279 void WebMediaPlayerAndroid::seek(double seconds) { | |
280 pending_seek_ = seconds; | |
281 if (seeking_ && media_source_delegate_) | |
282 media_source_delegate_->CancelPendingSeek(); | |
283 seeking_ = true; | |
284 | |
285 base::TimeDelta seek_time = ConvertSecondsToTimestamp(seconds); | |
286 #if defined(GOOGLE_TV) | |
287 // TODO(qinmin): check if GTV can also defer the seek until the browser side | |
288 // player is ready. | |
289 if (media_source_delegate_) | |
290 media_source_delegate_->Seek(seek_time); | |
291 #endif | |
292 proxy_->Seek(player_id_, seek_time); | |
293 } | |
294 | |
295 bool WebMediaPlayerAndroid::supportsFullscreen() const { | |
296 return true; | |
297 } | |
298 | |
299 bool WebMediaPlayerAndroid::supportsSave() const { | |
300 return false; | |
301 } | |
302 | |
303 void WebMediaPlayerAndroid::setRate(double rate) { | |
304 NOTIMPLEMENTED(); | |
305 } | |
306 | |
307 void WebMediaPlayerAndroid::setVolume(double volume) { | |
308 NOTIMPLEMENTED(); | |
309 } | |
310 | |
311 bool WebMediaPlayerAndroid::hasVideo() const { | |
312 // If we have obtained video size information before, use it. | |
313 if (has_size_info_) | |
314 return !natural_size_.isEmpty(); | |
315 | |
316 // TODO(qinmin): need a better method to determine whether the current media | |
317 // content contains video. Android does not provide any function to do | |
318 // this. | |
319 // We don't know whether the current media content has video unless | |
320 // the player is prepared. If the player is not prepared, we fall back | |
321 // to the mime-type. There may be no mime-type on a redirect URL. | |
322 // In that case, we conservatively assume it contains video so that | |
323 // enterfullscreen call will not fail. | |
324 if (!url_.has_path()) | |
325 return false; | |
326 std::string mime; | |
327 if(!net::GetMimeTypeFromFile(base::FilePath(url_.path()), &mime)) | |
328 return true; | |
329 return mime.find("audio/") == std::string::npos; | |
330 } | |
331 | |
332 bool WebMediaPlayerAndroid::hasAudio() const { | |
333 // TODO(hclam): Query status of audio and return the actual value. | |
334 return true; | |
335 } | |
336 | |
337 bool WebMediaPlayerAndroid::paused() const { | |
338 return !is_playing_; | |
339 } | |
340 | |
341 bool WebMediaPlayerAndroid::seeking() const { | |
342 return seeking_; | |
343 } | |
344 | |
345 double WebMediaPlayerAndroid::duration() const { | |
346 // HTML5 spec requires duration to be NaN if readyState is HAVE_NOTHING | |
347 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) | |
348 return std::numeric_limits<double>::quiet_NaN(); | |
349 | |
350 // TODO(wolenetz): Correctly handle durations that MediaSourcePlayer | |
351 // considers unseekable, including kInfiniteDuration(). | |
352 // See http://crbug.com/248396 | |
353 return duration_.InSecondsF(); | |
354 } | |
355 | |
356 double WebMediaPlayerAndroid::currentTime() const { | |
357 // If the player is pending for a seek, return the seek time. | |
358 if (seeking()) | |
359 return pending_seek_; | |
360 return current_time_; | |
361 } | |
362 | |
363 WebSize WebMediaPlayerAndroid::naturalSize() const { | |
364 return natural_size_; | |
365 } | |
366 | |
367 WebMediaPlayer::NetworkState WebMediaPlayerAndroid::networkState() const { | |
368 return network_state_; | |
369 } | |
370 | |
371 WebMediaPlayer::ReadyState WebMediaPlayerAndroid::readyState() const { | |
372 return ready_state_; | |
373 } | |
374 | |
375 const WebTimeRanges& WebMediaPlayerAndroid::buffered() { | |
376 if (media_source_delegate_) | |
377 return media_source_delegate_->Buffered(); | |
378 return buffered_; | |
379 } | |
380 | |
381 double WebMediaPlayerAndroid::maxTimeSeekable() const { | |
382 // If we haven't even gotten to ReadyStateHaveMetadata yet then just | |
383 // return 0 so that the seekable range is empty. | |
384 if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata) | |
385 return 0.0; | |
386 | |
387 // TODO(hclam): If this stream is not seekable this should return 0. | |
388 return duration(); | |
389 } | |
390 | |
391 bool WebMediaPlayerAndroid::didLoadingProgress() const { | |
392 bool ret = did_loading_progress_; | |
393 did_loading_progress_ = false; | |
394 return ret; | |
395 } | |
396 | |
397 void WebMediaPlayerAndroid::paint(WebKit::WebCanvas* canvas, | |
398 const WebKit::WebRect& rect, | |
399 unsigned char alpha) { | |
400 NOTIMPLEMENTED(); | |
401 } | |
402 | |
403 bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture( | |
404 WebKit::WebGraphicsContext3D* web_graphics_context, | |
405 unsigned int texture, | |
406 unsigned int level, | |
407 unsigned int internal_format, | |
408 unsigned int type, | |
409 bool premultiply_alpha, | |
410 bool flip_y) { | |
411 if (!texture_id_) | |
412 return false; | |
413 | |
414 // The video is stored in an unmultiplied format, so premultiply if | |
415 // necessary. | |
416 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, | |
417 premultiply_alpha); | |
418 | |
419 // Application itself needs to take care of setting the right flip_y | |
420 // value down to get the expected result. | |
421 // flip_y==true means to reverse the video orientation while | |
422 // flip_y==false means to keep the intrinsic orientation. | |
423 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y); | |
424 web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D, texture_id_, | |
425 texture, level, internal_format, | |
426 type); | |
427 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false); | |
428 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, | |
429 false); | |
430 return true; | |
431 } | |
432 | |
433 bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const { | |
434 if (info_loader_) | |
435 return info_loader_->HasSingleOrigin(); | |
436 // The info loader may have failed. | |
437 if (source_type_ == MediaPlayerAndroid::SOURCE_TYPE_URL) | |
438 return false; | |
439 return true; | |
440 } | |
441 | |
442 bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const { | |
443 if (info_loader_) | |
444 return info_loader_->DidPassCORSAccessCheck(); | |
445 return false; | |
446 } | |
447 | |
448 double WebMediaPlayerAndroid::mediaTimeForTimeValue(double timeValue) const { | |
449 return ConvertSecondsToTimestamp(timeValue).InSecondsF(); | |
450 } | |
451 | |
452 unsigned WebMediaPlayerAndroid::decodedFrameCount() const { | |
453 if (media_source_delegate_) | |
454 return media_source_delegate_->DecodedFrameCount(); | |
455 NOTIMPLEMENTED(); | |
456 return 0; | |
457 } | |
458 | |
459 unsigned WebMediaPlayerAndroid::droppedFrameCount() const { | |
460 if (media_source_delegate_) | |
461 return media_source_delegate_->DroppedFrameCount(); | |
462 NOTIMPLEMENTED(); | |
463 return 0; | |
464 } | |
465 | |
466 unsigned WebMediaPlayerAndroid::audioDecodedByteCount() const { | |
467 if (media_source_delegate_) | |
468 return media_source_delegate_->AudioDecodedByteCount(); | |
469 NOTIMPLEMENTED(); | |
470 return 0; | |
471 } | |
472 | |
473 unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const { | |
474 if (media_source_delegate_) | |
475 return media_source_delegate_->VideoDecodedByteCount(); | |
476 NOTIMPLEMENTED(); | |
477 return 0; | |
478 } | |
479 | |
480 void WebMediaPlayerAndroid::OnMediaMetadataChanged( | |
481 base::TimeDelta duration, int width, int height, bool success) { | |
482 bool need_to_signal_duration_changed = false; | |
483 | |
484 if (url_.SchemeIs("file")) | |
485 UpdateNetworkState(WebMediaPlayer::NetworkStateLoaded); | |
486 | |
487 // Update duration, if necessary, prior to ready state updates that may | |
488 // cause duration() query. | |
489 // TODO(wolenetz): Correctly handle durations that MediaSourcePlayer | |
490 // considers unseekable, including kInfiniteDuration(). | |
491 // See http://crbug.com/248396 | |
492 if (!ignore_metadata_duration_change_ && duration_ != duration) { | |
493 duration_ = duration; | |
494 | |
495 // Client readyState transition from HAVE_NOTHING to HAVE_METADATA | |
496 // already triggers a durationchanged event. If this is a different | |
497 // transition, remember to signal durationchanged. | |
498 // Do not ever signal durationchanged on metadata change in MSE case | |
499 // because OnDurationChange() handles this. | |
500 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing && | |
501 source_type_ != MediaPlayerAndroid::SOURCE_TYPE_MSE) { | |
502 need_to_signal_duration_changed = true; | |
503 } | |
504 } | |
505 | |
506 has_media_metadata_ = true; | |
507 if (has_media_info_ && | |
508 ready_state_ != WebMediaPlayer::ReadyStateHaveEnoughData) { | |
509 UpdateReadyState(WebMediaPlayer::ReadyStateHaveMetadata); | |
510 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData); | |
511 } | |
512 | |
513 // TODO(wolenetz): Should we just abort early and set network state to an | |
514 // error if success == false? See http://crbug.com/248399 | |
515 if (success) | |
516 OnVideoSizeChanged(width, height); | |
517 | |
518 if (hasVideo() && !video_weblayer_ && client_->needsWebLayerForVideo()) { | |
519 video_weblayer_.reset( | |
520 new webkit::WebLayerImpl(cc::VideoLayer::Create(this))); | |
521 client_->setWebLayer(video_weblayer_.get()); | |
522 } | |
523 | |
524 if (need_to_signal_duration_changed) | |
525 client_->durationChanged(); | |
526 } | |
527 | |
528 void WebMediaPlayerAndroid::OnPlaybackComplete() { | |
529 // When playback is about to finish, android media player often stops | |
530 // at a time which is smaller than the duration. This makes webkit never | |
531 // know that the playback has finished. To solve this, we set the | |
532 // current time to media duration when OnPlaybackComplete() get called. | |
533 OnTimeUpdate(duration_); | |
534 client_->timeChanged(); | |
535 } | |
536 | |
537 void WebMediaPlayerAndroid::OnBufferingUpdate(int percentage) { | |
538 buffered_[0].end = duration() * percentage / 100; | |
539 did_loading_progress_ = true; | |
540 } | |
541 | |
542 void WebMediaPlayerAndroid::OnSeekComplete(base::TimeDelta current_time) { | |
543 seeking_ = false; | |
544 | |
545 OnTimeUpdate(current_time); | |
546 | |
547 UpdateReadyState(WebMediaPlayer::ReadyStateHaveEnoughData); | |
548 | |
549 client_->timeChanged(); | |
550 } | |
551 | |
552 void WebMediaPlayerAndroid::OnMediaError(int error_type) { | |
553 switch (error_type) { | |
554 case MediaPlayerAndroid::MEDIA_ERROR_FORMAT: | |
555 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError); | |
556 break; | |
557 case MediaPlayerAndroid::MEDIA_ERROR_DECODE: | |
558 UpdateNetworkState(WebMediaPlayer::NetworkStateDecodeError); | |
559 break; | |
560 case MediaPlayerAndroid::MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK: | |
561 UpdateNetworkState(WebMediaPlayer::NetworkStateFormatError); | |
562 break; | |
563 case MediaPlayerAndroid::MEDIA_ERROR_INVALID_CODE: | |
564 break; | |
565 } | |
566 client_->repaint(); | |
567 } | |
568 | |
569 void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) { | |
570 has_size_info_ = true; | |
571 if (natural_size_.width == width && natural_size_.height == height) | |
572 return; | |
573 | |
574 #if defined(GOOGLE_TV) | |
575 static bool has_switch = CommandLine::ForCurrentProcess()->HasSwitch( | |
576 switches::kUseExternalVideoSurfaceThresholdInPixels); | |
577 static int threshold = 0; | |
578 static bool parsed_arg = | |
579 has_switch && | |
580 base::StringToInt( | |
581 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
582 switches::kUseExternalVideoSurfaceThresholdInPixels), | |
583 &threshold); | |
584 | |
585 if ((parsed_arg && threshold <= width * height) || | |
586 // Use H/W surface for MSE as the content is protected. | |
587 media_source_delegate_) { | |
588 if (stream_texture_factory_) { | |
589 stream_texture_factory_->DestroyStreamTexture(texture_id_); | |
590 stream_id_ = 0; | |
591 texture_id_ = 0; | |
592 } | |
593 needs_external_surface_ = true; | |
594 SetNeedsEstablishPeer(false); | |
595 if (!paused()) | |
596 proxy_->RequestExternalSurface(player_id_, last_computed_rect_); | |
597 } | |
598 #endif | |
599 | |
600 natural_size_.width = width; | |
601 natural_size_.height = height; | |
602 ReallocateVideoFrame(); | |
603 } | |
604 | |
605 void WebMediaPlayerAndroid::OnTimeUpdate(base::TimeDelta current_time) { | |
606 current_time_ = current_time.InSecondsF(); | |
607 } | |
608 | |
609 void WebMediaPlayerAndroid::OnDidEnterFullscreen() { | |
610 if (!manager_->IsInFullscreen(frame_)) { | |
611 frame_->view()->willEnterFullScreen(); | |
612 frame_->view()->didEnterFullScreen(); | |
613 manager_->DidEnterFullscreen(frame_); | |
614 } | |
615 } | |
616 | |
617 void WebMediaPlayerAndroid::OnDidExitFullscreen() { | |
618 // |needs_external_surface_| is always false on non-TV devices. | |
619 if (!needs_external_surface_) | |
620 SetNeedsEstablishPeer(true); | |
621 // We had the fullscreen surface connected to Android MediaPlayer, | |
622 // so reconnect our surface texture for embedded playback. | |
623 if (!paused()) | |
624 EstablishSurfaceTexturePeer(); | |
625 | |
626 frame_->view()->willExitFullScreen(); | |
627 frame_->view()->didExitFullScreen(); | |
628 manager_->DidExitFullscreen(); | |
629 client_->repaint(); | |
630 } | |
631 | |
632 void WebMediaPlayerAndroid::OnMediaPlayerPlay() { | |
633 UpdatePlayingState(true); | |
634 client_->playbackStateChanged(); | |
635 } | |
636 | |
637 void WebMediaPlayerAndroid::OnMediaPlayerPause() { | |
638 UpdatePlayingState(false); | |
639 client_->playbackStateChanged(); | |
640 } | |
641 | |
642 void WebMediaPlayerAndroid::OnMediaSeekRequest(base::TimeDelta time_to_seek) { | |
643 if (!media_source_delegate_) | |
644 return; | |
645 | |
646 if (!seeking_) | |
647 media_source_delegate_->CancelPendingSeek(); | |
648 media_source_delegate_->Seek(time_to_seek); | |
649 OnTimeUpdate(time_to_seek); | |
650 } | |
651 | |
652 void WebMediaPlayerAndroid::OnMediaConfigRequest() { | |
653 if (!media_source_delegate_) | |
654 return; | |
655 | |
656 media_source_delegate_->OnMediaConfigRequest(); | |
657 } | |
658 | |
659 void WebMediaPlayerAndroid::OnDurationChange(const base::TimeDelta& duration) { | |
660 // Only MSE |source_type_| registers this callback. | |
661 DCHECK(source_type_ == MediaPlayerAndroid::SOURCE_TYPE_MSE); | |
662 | |
663 // Cache the new duration value and trust it over any subsequent duration | |
664 // values received in OnMediaMetadataChanged(). | |
665 // TODO(wolenetz): Correctly handle durations that MediaSourcePlayer | |
666 // considers unseekable, including kInfiniteDuration(). | |
667 // See http://crbug.com/248396 | |
668 duration_ = duration; | |
669 ignore_metadata_duration_change_ = true; | |
670 | |
671 // Send message to Android MediaSourcePlayer to update duration. | |
672 if (proxy_) | |
673 proxy_->DurationChanged(player_id_, duration_); | |
674 | |
675 // Notify MediaPlayerClient that duration has changed, if > HAVE_NOTHING. | |
676 if (ready_state_ > WebMediaPlayer::ReadyStateHaveNothing) | |
677 client_->durationChanged(); | |
678 } | |
679 | |
680 void WebMediaPlayerAndroid::UpdateNetworkState( | |
681 WebMediaPlayer::NetworkState state) { | |
682 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing && | |
683 (state == WebMediaPlayer::NetworkStateNetworkError || | |
684 state == WebMediaPlayer::NetworkStateDecodeError)) { | |
685 // Any error that occurs before reaching ReadyStateHaveMetadata should | |
686 // be considered a format error. | |
687 network_state_ = WebMediaPlayer::NetworkStateFormatError; | |
688 } else { | |
689 network_state_ = state; | |
690 } | |
691 client_->networkStateChanged(); | |
692 } | |
693 | |
694 void WebMediaPlayerAndroid::UpdateReadyState( | |
695 WebMediaPlayer::ReadyState state) { | |
696 ready_state_ = state; | |
697 client_->readyStateChanged(); | |
698 } | |
699 | |
700 void WebMediaPlayerAndroid::OnPlayerReleased() { | |
701 // |needs_external_surface_| is always false on non-TV devices. | |
702 if (!needs_external_surface_) | |
703 needs_establish_peer_ = true; | |
704 } | |
705 | |
706 void WebMediaPlayerAndroid::ReleaseMediaResources() { | |
707 switch (network_state_) { | |
708 // Pause the media player and inform WebKit if the player is in a good | |
709 // shape. | |
710 case WebMediaPlayer::NetworkStateIdle: | |
711 case WebMediaPlayer::NetworkStateLoading: | |
712 case WebMediaPlayer::NetworkStateLoaded: | |
713 pause(); | |
714 client_->playbackStateChanged(); | |
715 break; | |
716 // If a WebMediaPlayer instance has entered into one of these states, | |
717 // the internal network state in HTMLMediaElement could be set to empty. | |
718 // And calling playbackStateChanged() could get this object deleted. | |
719 case WebMediaPlayer::NetworkStateEmpty: | |
720 case WebMediaPlayer::NetworkStateFormatError: | |
721 case WebMediaPlayer::NetworkStateNetworkError: | |
722 case WebMediaPlayer::NetworkStateDecodeError: | |
723 break; | |
724 } | |
725 proxy_->ReleaseResources(player_id_); | |
726 OnPlayerReleased(); | |
727 } | |
728 | |
729 void WebMediaPlayerAndroid::WillDestroyCurrentMessageLoop() { | |
730 if (manager_) | |
731 manager_->UnregisterMediaPlayer(player_id_); | |
732 Detach(); | |
733 } | |
734 | |
735 void WebMediaPlayerAndroid::Detach() { | |
736 if (stream_id_) { | |
737 stream_texture_factory_->DestroyStreamTexture(texture_id_); | |
738 stream_id_ = 0; | |
739 } | |
740 | |
741 media_source_delegate_.reset(); | |
742 current_frame_ = NULL; | |
743 manager_ = NULL; | |
744 proxy_ = NULL; | |
745 } | |
746 | |
747 void WebMediaPlayerAndroid::ReallocateVideoFrame() { | |
748 if (needs_external_surface_) { | |
749 // VideoFrame::CreateHoleFrame is only defined under GOOGLE_TV. | |
750 #if defined(GOOGLE_TV) | |
751 if (!natural_size_.isEmpty()) { | |
752 current_frame_ = VideoFrame::CreateHoleFrame(natural_size_); | |
753 // Force the client to grab the hole frame. | |
754 client_->repaint(); | |
755 } | |
756 #else | |
757 NOTIMPLEMENTED() << "Hole punching not supported outside of Google TV"; | |
758 #endif | |
759 } else if (texture_id_) { | |
760 current_frame_ = VideoFrame::WrapNativeTexture( | |
761 texture_id_, kGLTextureExternalOES, natural_size_, | |
762 gfx::Rect(natural_size_), natural_size_, base::TimeDelta(), | |
763 VideoFrame::ReadPixelsCB(), | |
764 base::Closure()); | |
765 } | |
766 } | |
767 | |
768 void WebMediaPlayerAndroid::SetVideoFrameProviderClient( | |
769 cc::VideoFrameProvider::Client* client) { | |
770 // This is called from both the main renderer thread and the compositor | |
771 // thread (when the main thread is blocked). | |
772 if (video_frame_provider_client_) | |
773 video_frame_provider_client_->StopUsingProvider(); | |
774 video_frame_provider_client_ = client; | |
775 | |
776 // Set the callback target when a frame is produced. | |
777 if (stream_texture_proxy_) | |
778 stream_texture_proxy_->SetClient(client); | |
779 } | |
780 | |
781 scoped_refptr<media::VideoFrame> WebMediaPlayerAndroid::GetCurrentFrame() { | |
782 if (!stream_texture_proxy_initialized_ && stream_texture_proxy_ && | |
783 stream_id_ && !needs_external_surface_) { | |
784 gfx::Size natural_size = current_frame_->natural_size(); | |
785 stream_texture_proxy_->BindToCurrentThread( | |
786 stream_id_, natural_size.width(), natural_size.height()); | |
787 stream_texture_proxy_initialized_ = true; | |
788 } | |
789 return current_frame_; | |
790 } | |
791 | |
792 void WebMediaPlayerAndroid::PutCurrentFrame( | |
793 const scoped_refptr<media::VideoFrame>& frame) { | |
794 } | |
795 | |
796 void WebMediaPlayerAndroid::EstablishSurfaceTexturePeer() { | |
797 if (media_source_delegate_ && stream_texture_factory_) { | |
798 // MediaCodec will release the old surface when it goes away, we need to | |
799 // recreate a new one each time this is called. | |
800 stream_texture_factory_->DestroyStreamTexture(texture_id_); | |
801 stream_id_ = 0; | |
802 texture_id_ = 0; | |
803 stream_id_ = stream_texture_factory_->CreateStreamTexture(&texture_id_); | |
804 ReallocateVideoFrame(); | |
805 stream_texture_proxy_initialized_ = false; | |
806 } | |
807 if (stream_texture_factory_.get() && stream_id_) | |
808 stream_texture_factory_->EstablishPeer(stream_id_, player_id_); | |
809 needs_establish_peer_ = false; | |
810 } | |
811 | |
812 void WebMediaPlayerAndroid::SetNeedsEstablishPeer(bool needs_establish_peer) { | |
813 needs_establish_peer_ = needs_establish_peer; | |
814 } | |
815 | |
816 void WebMediaPlayerAndroid::UpdatePlayingState(bool is_playing) { | |
817 is_playing_ = is_playing; | |
818 if (source_type_ != MediaPlayerAndroid::SOURCE_TYPE_MSE || !delegate_) | |
819 return; | |
820 if (is_playing) | |
821 delegate_->DidPlay(this); | |
822 else | |
823 delegate_->DidPause(this); | |
824 } | |
825 | |
826 #if defined(GOOGLE_TV) | |
827 bool WebMediaPlayerAndroid::RetrieveGeometryChange(gfx::RectF* rect) { | |
828 if (!video_weblayer_) | |
829 return false; | |
830 | |
831 // Compute the geometry of video frame layer. | |
832 cc::Layer* layer = video_weblayer_->layer(); | |
833 rect->set_size(layer->bounds()); | |
834 while (layer) { | |
835 rect->Offset(layer->position().OffsetFromOrigin()); | |
836 layer = layer->parent(); | |
837 } | |
838 | |
839 // Return false when the geometry hasn't been changed from the last time. | |
840 if (last_computed_rect_ == *rect) | |
841 return false; | |
842 | |
843 // Store the changed geometry information when it is actually changed. | |
844 last_computed_rect_ = *rect; | |
845 return true; | |
846 } | |
847 #endif | |
848 | |
849 // The following EME related code is copied from WebMediaPlayerImpl. | |
850 // TODO(xhwang): Remove duplicate code between WebMediaPlayerAndroid and | |
851 // WebMediaPlayerImpl. | |
852 // TODO(kjyoun): Update Google TV EME implementation to use IPC. | |
853 | |
854 // Helper functions to report media EME related stats to UMA. They follow the | |
855 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and | |
856 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is | |
857 // that UMA_* macros require the names to be constant throughout the process' | |
858 // lifetime. | |
859 static void EmeUMAHistogramEnumeration(const std::string& key_system, | |
860 const std::string& method, | |
861 int sample, | |
862 int boundary_value) { | |
863 base::LinearHistogram::FactoryGet( | |
864 kMediaEme + KeySystemNameForUMA(key_system) + "." + method, | |
865 1, boundary_value, boundary_value + 1, | |
866 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample); | |
867 } | |
868 | |
869 static void EmeUMAHistogramCounts(const std::string& key_system, | |
870 const std::string& method, | |
871 int sample) { | |
872 // Use the same parameters as UMA_HISTOGRAM_COUNTS. | |
873 base::Histogram::FactoryGet( | |
874 kMediaEme + KeySystemNameForUMA(key_system) + "." + method, | |
875 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample); | |
876 } | |
877 | |
878 // Helper enum for reporting generateKeyRequest/addKey histograms. | |
879 enum MediaKeyException { | |
880 kUnknownResultId, | |
881 kSuccess, | |
882 kKeySystemNotSupported, | |
883 kInvalidPlayerState, | |
884 kMaxMediaKeyException | |
885 }; | |
886 | |
887 static MediaKeyException MediaKeyExceptionForUMA( | |
888 WebMediaPlayer::MediaKeyException e) { | |
889 switch (e) { | |
890 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported: | |
891 return kKeySystemNotSupported; | |
892 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState: | |
893 return kInvalidPlayerState; | |
894 case WebMediaPlayer::MediaKeyExceptionNoError: | |
895 return kSuccess; | |
896 default: | |
897 return kUnknownResultId; | |
898 } | |
899 } | |
900 | |
901 // Helper for converting |key_system| name and exception |e| to a pair of enum | |
902 // values from above, for reporting to UMA. | |
903 static void ReportMediaKeyExceptionToUMA( | |
904 const std::string& method, | |
905 const WebString& key_system, | |
906 WebMediaPlayer::MediaKeyException e) { | |
907 MediaKeyException result_id = MediaKeyExceptionForUMA(e); | |
908 DCHECK_NE(result_id, kUnknownResultId) << e; | |
909 EmeUMAHistogramEnumeration( | |
910 key_system.utf8(), method, result_id, kMaxMediaKeyException); | |
911 } | |
912 | |
913 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::generateKeyRequest( | |
914 const WebString& key_system, | |
915 const unsigned char* init_data, | |
916 unsigned init_data_length) { | |
917 WebMediaPlayer::MediaKeyException e = | |
918 GenerateKeyRequestInternal(key_system, init_data, init_data_length); | |
919 ReportMediaKeyExceptionToUMA("generateKeyRequest", key_system, e); | |
920 return e; | |
921 } | |
922 | |
923 WebMediaPlayer::MediaKeyException | |
924 WebMediaPlayerAndroid::GenerateKeyRequestInternal( | |
925 const WebString& key_system, | |
926 const unsigned char* init_data, | |
927 unsigned init_data_length) { | |
928 DVLOG(1) << "generateKeyRequest: " << key_system.utf8().data() << ": " | |
929 << std::string(reinterpret_cast<const char*>(init_data), | |
930 static_cast<size_t>(init_data_length)); | |
931 | |
932 if (!IsSupportedKeySystem(key_system)) | |
933 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
934 | |
935 // We do not support run-time switching between key systems for now. | |
936 if (current_key_system_.isEmpty()) { | |
937 if (!decryptor_->InitializeCDM(key_system.utf8())) | |
938 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
939 current_key_system_ = key_system; | |
940 } else if (key_system != current_key_system_) { | |
941 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
942 } | |
943 | |
944 // TODO(xhwang): We assume all streams are from the same container (thus have | |
945 // the same "type") for now. In the future, the "type" should be passed down | |
946 // from the application. | |
947 if (!decryptor_->GenerateKeyRequest(init_data_type_, | |
948 init_data, init_data_length)) { | |
949 current_key_system_.reset(); | |
950 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
951 } | |
952 | |
953 return WebMediaPlayer::MediaKeyExceptionNoError; | |
954 } | |
955 | |
956 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::addKey( | |
957 const WebString& key_system, | |
958 const unsigned char* key, | |
959 unsigned key_length, | |
960 const unsigned char* init_data, | |
961 unsigned init_data_length, | |
962 const WebString& session_id) { | |
963 WebMediaPlayer::MediaKeyException e = AddKeyInternal( | |
964 key_system, key, key_length, init_data, init_data_length, session_id); | |
965 ReportMediaKeyExceptionToUMA("addKey", key_system, e); | |
966 return e; | |
967 } | |
968 | |
969 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::AddKeyInternal( | |
970 const WebString& key_system, | |
971 const unsigned char* key, | |
972 unsigned key_length, | |
973 const unsigned char* init_data, | |
974 unsigned init_data_length, | |
975 const WebString& session_id) { | |
976 DCHECK(key); | |
977 DCHECK_GT(key_length, 0u); | |
978 DVLOG(1) << "addKey: " << key_system.utf8().data() << ": " | |
979 << std::string(reinterpret_cast<const char*>(key), | |
980 static_cast<size_t>(key_length)) << ", " | |
981 << std::string(reinterpret_cast<const char*>(init_data), | |
982 static_cast<size_t>(init_data_length)) | |
983 << " [" << session_id.utf8().data() << "]"; | |
984 | |
985 if (!IsSupportedKeySystem(key_system)) | |
986 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
987 | |
988 if (current_key_system_.isEmpty() || key_system != current_key_system_) | |
989 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
990 | |
991 decryptor_->AddKey(key, key_length, init_data, init_data_length, | |
992 session_id.utf8()); | |
993 return WebMediaPlayer::MediaKeyExceptionNoError; | |
994 } | |
995 | |
996 WebMediaPlayer::MediaKeyException WebMediaPlayerAndroid::cancelKeyRequest( | |
997 const WebString& key_system, | |
998 const WebString& session_id) { | |
999 WebMediaPlayer::MediaKeyException e = | |
1000 CancelKeyRequestInternal(key_system, session_id); | |
1001 ReportMediaKeyExceptionToUMA("cancelKeyRequest", key_system, e); | |
1002 return e; | |
1003 } | |
1004 | |
1005 WebMediaPlayer::MediaKeyException | |
1006 WebMediaPlayerAndroid::CancelKeyRequestInternal( | |
1007 const WebString& key_system, | |
1008 const WebString& session_id) { | |
1009 if (!IsSupportedKeySystem(key_system)) | |
1010 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
1011 | |
1012 if (current_key_system_.isEmpty() || key_system != current_key_system_) | |
1013 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
1014 | |
1015 decryptor_->CancelKeyRequest(session_id.utf8()); | |
1016 return WebMediaPlayer::MediaKeyExceptionNoError; | |
1017 } | |
1018 | |
1019 void WebMediaPlayerAndroid::OnKeyAdded(const std::string& session_id) { | |
1020 EmeUMAHistogramCounts(current_key_system_.utf8(), "KeyAdded", 1); | |
1021 | |
1022 if (media_source_delegate_) | |
1023 media_source_delegate_->NotifyKeyAdded(current_key_system_.utf8()); | |
1024 | |
1025 client_->keyAdded(current_key_system_, WebString::fromUTF8(session_id)); | |
1026 } | |
1027 | |
1028 void WebMediaPlayerAndroid::OnKeyError(const std::string& session_id, | |
1029 media::MediaKeys::KeyError error_code, | |
1030 int system_code) { | |
1031 EmeUMAHistogramEnumeration(current_key_system_.utf8(), "KeyError", | |
1032 error_code, media::MediaKeys::kMaxKeyError); | |
1033 | |
1034 client_->keyError( | |
1035 current_key_system_, | |
1036 WebString::fromUTF8(session_id), | |
1037 static_cast<WebKit::WebMediaPlayerClient::MediaKeyErrorCode>(error_code), | |
1038 system_code); | |
1039 } | |
1040 | |
1041 void WebMediaPlayerAndroid::OnKeyMessage(const std::string& session_id, | |
1042 const std::string& message, | |
1043 const std::string& destination_url) { | |
1044 const GURL destination_url_gurl(destination_url); | |
1045 DLOG_IF(WARNING, !destination_url.empty() && !destination_url_gurl.is_valid()) | |
1046 << "Invalid URL in destination_url: " << destination_url; | |
1047 | |
1048 client_->keyMessage(current_key_system_, | |
1049 WebString::fromUTF8(session_id), | |
1050 reinterpret_cast<const uint8*>(message.data()), | |
1051 message.size(), | |
1052 destination_url_gurl); | |
1053 } | |
1054 | |
1055 void WebMediaPlayerAndroid::OnNeedKey(const std::string& session_id, | |
1056 const std::string& type, | |
1057 scoped_ptr<uint8[]> init_data, | |
1058 int init_data_size) { | |
1059 // Do not fire NeedKey event if encrypted media is not enabled. | |
1060 if (!WebKit::WebRuntimeFeatures::isEncryptedMediaEnabled() && | |
1061 !WebKit::WebRuntimeFeatures::isLegacyEncryptedMediaEnabled()) { | |
1062 return; | |
1063 } | |
1064 | |
1065 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1); | |
1066 | |
1067 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_); | |
1068 if (init_data_type_.empty()) | |
1069 init_data_type_ = type; | |
1070 | |
1071 client_->keyNeeded(WebString(), | |
1072 WebString::fromUTF8(session_id), | |
1073 init_data.get(), | |
1074 init_data_size); | |
1075 } | |
1076 | |
1077 #if defined(GOOGLE_TV) | |
1078 bool WebMediaPlayerAndroid::InjectMediaStream( | |
1079 MediaStreamClient* media_stream_client, | |
1080 media::Demuxer* demuxer, | |
1081 const base::Closure& destroy_demuxer_cb) { | |
1082 DCHECK(!demuxer); | |
1083 media_stream_client_ = media_stream_client; | |
1084 demuxer_ = demuxer; | |
1085 destroy_demuxer_cb_ = destroy_demuxer_cb; | |
1086 return true; | |
1087 } | |
1088 #endif | |
1089 | |
1090 void WebMediaPlayerAndroid::OnReadFromDemuxer( | |
1091 media::DemuxerStream::Type type, bool seek_done) { | |
1092 if (media_source_delegate_) | |
1093 media_source_delegate_->OnReadFromDemuxer(type, seek_done); | |
1094 else | |
1095 NOTIMPLEMENTED(); | |
1096 } | |
1097 | |
1098 void WebMediaPlayerAndroid::enterFullscreen() { | |
1099 if (manager_->CanEnterFullscreen(frame_)) { | |
1100 proxy_->EnterFullscreen(player_id_); | |
1101 SetNeedsEstablishPeer(false); | |
1102 } | |
1103 } | |
1104 | |
1105 void WebMediaPlayerAndroid::exitFullscreen() { | |
1106 proxy_->ExitFullscreen(player_id_); | |
1107 } | |
1108 | |
1109 bool WebMediaPlayerAndroid::canEnterFullscreen() const { | |
1110 return manager_->CanEnterFullscreen(frame_); | |
1111 } | |
1112 | |
1113 } // namespace webkit_media | |
OLD | NEW |