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/webmediaplayer_impl.h" | |
6 | |
7 #include <algorithm> | |
8 #include <limits> | |
9 #include <string> | |
10 #include <vector> | |
11 | |
12 #include "base/bind.h" | |
13 #include "base/callback.h" | |
14 #include "base/command_line.h" | |
15 #include "base/debug/crash_logging.h" | |
16 #include "base/message_loop/message_loop_proxy.h" | |
17 #include "base/metrics/histogram.h" | |
18 #include "base/strings/string_number_conversions.h" | |
19 #include "base/synchronization/waitable_event.h" | |
20 #include "cc/layers/video_layer.h" | |
21 #include "gpu/GLES2/gl2extchromium.h" | |
22 #include "media/audio/null_audio_sink.h" | |
23 #include "media/base/bind_to_loop.h" | |
24 #include "media/base/filter_collection.h" | |
25 #include "media/base/limits.h" | |
26 #include "media/base/media_log.h" | |
27 #include "media/base/media_switches.h" | |
28 #include "media/base/pipeline.h" | |
29 #include "media/base/video_frame.h" | |
30 #include "media/filters/audio_renderer_impl.h" | |
31 #include "media/filters/chunk_demuxer.h" | |
32 #include "media/filters/ffmpeg_audio_decoder.h" | |
33 #include "media/filters/ffmpeg_demuxer.h" | |
34 #include "media/filters/ffmpeg_video_decoder.h" | |
35 #include "media/filters/opus_audio_decoder.h" | |
36 #include "media/filters/video_renderer_base.h" | |
37 #include "media/filters/vpx_video_decoder.h" | |
38 #include "third_party/WebKit/public/platform/WebRect.h" | |
39 #include "third_party/WebKit/public/platform/WebSize.h" | |
40 #include "third_party/WebKit/public/platform/WebString.h" | |
41 #include "third_party/WebKit/public/platform/WebURL.h" | |
42 #include "third_party/WebKit/public/web/WebMediaSource.h" | |
43 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" | |
44 #include "third_party/WebKit/public/web/WebView.h" | |
45 #include "v8/include/v8.h" | |
46 #include "webkit/plugins/ppapi/ppapi_webplugin_impl.h" | |
47 #include "webkit/renderer/compositor_bindings/web_layer_impl.h" | |
48 #include "webkit/renderer/media/buffered_data_source.h" | |
49 #include "webkit/renderer/media/crypto/key_systems.h" | |
50 #include "webkit/renderer/media/texttrack_impl.h" | |
51 #include "webkit/renderer/media/webaudiosourceprovider_impl.h" | |
52 #include "webkit/renderer/media/webinbandtexttrack_impl.h" | |
53 #include "webkit/renderer/media/webmediaplayer_delegate.h" | |
54 #include "webkit/renderer/media/webmediaplayer_params.h" | |
55 #include "webkit/renderer/media/webmediaplayer_util.h" | |
56 #include "webkit/renderer/media/webmediasourceclient_impl.h" | |
57 | |
58 using WebKit::WebCanvas; | |
59 using WebKit::WebMediaPlayer; | |
60 using WebKit::WebRect; | |
61 using WebKit::WebSize; | |
62 using WebKit::WebString; | |
63 using media::PipelineStatus; | |
64 | |
65 namespace { | |
66 | |
67 // Amount of extra memory used by each player instance reported to V8. | |
68 // It is not exact number -- first, it differs on different platforms, | |
69 // and second, it is very hard to calculate. Instead, use some arbitrary | |
70 // value that will cause garbage collection from time to time. We don't want | |
71 // it to happen on every allocation, but don't want 5k players to sit in memory | |
72 // either. Looks that chosen constant achieves both goals, at least for audio | |
73 // objects. (Do not worry about video objects yet, JS programs do not create | |
74 // thousands of them...) | |
75 const int kPlayerExtraMemory = 1024 * 1024; | |
76 | |
77 // Limits the range of playback rate. | |
78 // | |
79 // TODO(kylep): Revisit these. | |
80 // | |
81 // Vista has substantially lower performance than XP or Windows7. If you speed | |
82 // up a video too much, it can't keep up, and rendering stops updating except on | |
83 // the time bar. For really high speeds, audio becomes a bottleneck and we just | |
84 // use up the data we have, which may not achieve the speed requested, but will | |
85 // not crash the tab. | |
86 // | |
87 // A very slow speed, ie 0.00000001x, causes the machine to lock up. (It seems | |
88 // like a busy loop). It gets unresponsive, although its not completely dead. | |
89 // | |
90 // Also our timers are not very accurate (especially for ogg), which becomes | |
91 // evident at low speeds and on Vista. Since other speeds are risky and outside | |
92 // the norms, we think 1/16x to 16x is a safe and useful range for now. | |
93 const double kMinRate = 0.0625; | |
94 const double kMaxRate = 16.0; | |
95 | |
96 // Prefix for histograms related to Encrypted Media Extensions. | |
97 const char* kMediaEme = "Media.EME."; | |
98 } // namespace | |
99 | |
100 namespace webkit_media { | |
101 | |
102 #define COMPILE_ASSERT_MATCHING_ENUM(name) \ | |
103 COMPILE_ASSERT(static_cast<int>(WebMediaPlayer::CORSMode ## name) == \ | |
104 static_cast<int>(BufferedResourceLoader::k ## name), \ | |
105 mismatching_enums) | |
106 COMPILE_ASSERT_MATCHING_ENUM(Unspecified); | |
107 COMPILE_ASSERT_MATCHING_ENUM(Anonymous); | |
108 COMPILE_ASSERT_MATCHING_ENUM(UseCredentials); | |
109 #undef COMPILE_ASSERT_MATCHING_ENUM | |
110 | |
111 #define BIND_TO_RENDER_LOOP(function) \ | |
112 media::BindToLoop(main_loop_, base::Bind(function, AsWeakPtr())) | |
113 | |
114 #define BIND_TO_RENDER_LOOP_1(function, arg1) \ | |
115 media::BindToLoop(main_loop_, base::Bind(function, AsWeakPtr(), arg1)) | |
116 | |
117 #define BIND_TO_RENDER_LOOP_2(function, arg1, arg2) \ | |
118 media::BindToLoop(main_loop_, base::Bind(function, AsWeakPtr(), arg1, arg2)) | |
119 | |
120 static void LogMediaSourceError(const scoped_refptr<media::MediaLog>& media_log, | |
121 const std::string& error) { | |
122 media_log->AddEvent(media_log->CreateMediaSourceErrorEvent(error)); | |
123 } | |
124 | |
125 WebMediaPlayerImpl::WebMediaPlayerImpl( | |
126 WebKit::WebFrame* frame, | |
127 WebKit::WebMediaPlayerClient* client, | |
128 base::WeakPtr<WebMediaPlayerDelegate> delegate, | |
129 const WebMediaPlayerParams& params) | |
130 : frame_(frame), | |
131 network_state_(WebMediaPlayer::NetworkStateEmpty), | |
132 ready_state_(WebMediaPlayer::ReadyStateHaveNothing), | |
133 main_loop_(base::MessageLoopProxy::current()), | |
134 media_loop_(params.message_loop_proxy()), | |
135 paused_(true), | |
136 seeking_(false), | |
137 playback_rate_(0.0f), | |
138 pending_seek_(false), | |
139 pending_seek_seconds_(0.0f), | |
140 client_(client), | |
141 delegate_(delegate), | |
142 media_log_(params.media_log()), | |
143 accelerated_compositing_reported_(false), | |
144 incremented_externally_allocated_memory_(false), | |
145 gpu_factories_(params.gpu_factories()), | |
146 is_local_source_(false), | |
147 supports_save_(true), | |
148 starting_(false), | |
149 chunk_demuxer_(NULL), | |
150 pending_repaint_(false), | |
151 pending_size_change_(false), | |
152 video_frame_provider_client_(NULL), | |
153 text_track_index_(0) { | |
154 media_log_->AddEvent( | |
155 media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_CREATED)); | |
156 | |
157 pipeline_.reset(new media::Pipeline(media_loop_, media_log_.get())); | |
158 | |
159 // Let V8 know we started new thread if we did not do it yet. | |
160 // Made separate task to avoid deletion of player currently being created. | |
161 // Also, delaying GC until after player starts gets rid of starting lag -- | |
162 // collection happens in parallel with playing. | |
163 // | |
164 // TODO(enal): remove when we get rid of per-audio-stream thread. | |
165 main_loop_->PostTask( | |
166 FROM_HERE, | |
167 base::Bind(&WebMediaPlayerImpl::IncrementExternallyAllocatedMemory, | |
168 AsWeakPtr())); | |
169 | |
170 // Also we want to be notified of |main_loop_| destruction. | |
171 base::MessageLoop::current()->AddDestructionObserver(this); | |
172 | |
173 if (WebKit::WebRuntimeFeatures::isLegacyEncryptedMediaEnabled()) { | |
174 decryptor_.reset(new ProxyDecryptor( | |
175 #if defined(ENABLE_PEPPER_CDMS) | |
176 client, | |
177 frame, | |
178 #endif | |
179 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyAdded), | |
180 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyError), | |
181 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyMessage))); | |
182 } | |
183 | |
184 // Use the null sink if no sink was provided. | |
185 audio_source_provider_ = new WebAudioSourceProviderImpl( | |
186 params.audio_renderer_sink().get() | |
187 ? params.audio_renderer_sink() | |
188 : new media::NullAudioSink(media_loop_)); | |
189 } | |
190 | |
191 WebMediaPlayerImpl::~WebMediaPlayerImpl() { | |
192 SetVideoFrameProviderClient(NULL); | |
193 GetClient()->setWebLayer(NULL); | |
194 | |
195 DCHECK(main_loop_->BelongsToCurrentThread()); | |
196 media_log_->AddEvent( | |
197 media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_DESTROYED)); | |
198 | |
199 if (delegate_.get()) | |
200 delegate_->PlayerGone(this); | |
201 | |
202 Destroy(); | |
203 | |
204 // Remove destruction observer if we're being destroyed but the main thread is | |
205 // still running. | |
206 if (base::MessageLoop::current()) | |
207 base::MessageLoop::current()->RemoveDestructionObserver(this); | |
208 } | |
209 | |
210 namespace { | |
211 | |
212 // Helper enum for reporting scheme histograms. | |
213 enum URLSchemeForHistogram { | |
214 kUnknownURLScheme, | |
215 kMissingURLScheme, | |
216 kHttpURLScheme, | |
217 kHttpsURLScheme, | |
218 kFtpURLScheme, | |
219 kChromeExtensionURLScheme, | |
220 kJavascriptURLScheme, | |
221 kFileURLScheme, | |
222 kBlobURLScheme, | |
223 kDataURLScheme, | |
224 kFileSystemScheme, | |
225 kMaxURLScheme = kFileSystemScheme // Must be equal to highest enum value. | |
226 }; | |
227 | |
228 URLSchemeForHistogram URLScheme(const GURL& url) { | |
229 if (!url.has_scheme()) return kMissingURLScheme; | |
230 if (url.SchemeIs("http")) return kHttpURLScheme; | |
231 if (url.SchemeIs("https")) return kHttpsURLScheme; | |
232 if (url.SchemeIs("ftp")) return kFtpURLScheme; | |
233 if (url.SchemeIs("chrome-extension")) return kChromeExtensionURLScheme; | |
234 if (url.SchemeIs("javascript")) return kJavascriptURLScheme; | |
235 if (url.SchemeIs("file")) return kFileURLScheme; | |
236 if (url.SchemeIs("blob")) return kBlobURLScheme; | |
237 if (url.SchemeIs("data")) return kDataURLScheme; | |
238 if (url.SchemeIs("filesystem")) return kFileSystemScheme; | |
239 return kUnknownURLScheme; | |
240 } | |
241 | |
242 } // anonymous namespace | |
243 | |
244 void WebMediaPlayerImpl::load(const WebKit::WebURL& url, CORSMode cors_mode) { | |
245 DCHECK(main_loop_->BelongsToCurrentThread()); | |
246 | |
247 LoadSetup(url); | |
248 | |
249 // Otherwise it's a regular request which requires resolving the URL first. | |
250 GURL gurl(url); | |
251 data_source_.reset(new BufferedDataSource( | |
252 main_loop_, | |
253 frame_, | |
254 media_log_.get(), | |
255 base::Bind(&WebMediaPlayerImpl::NotifyDownloading, AsWeakPtr()))); | |
256 data_source_->Initialize( | |
257 url, static_cast<BufferedResourceLoader::CORSMode>(cors_mode), | |
258 base::Bind( | |
259 &WebMediaPlayerImpl::DataSourceInitialized, | |
260 AsWeakPtr(), gurl)); | |
261 | |
262 is_local_source_ = !gurl.SchemeIs("http") && !gurl.SchemeIs("https"); | |
263 } | |
264 | |
265 void WebMediaPlayerImpl::load(const WebKit::WebURL& url, | |
266 WebKit::WebMediaSource* media_source, | |
267 CORSMode cors_mode) { | |
268 LoadSetup(url); | |
269 | |
270 // Media source pipelines can start immediately. | |
271 supports_save_ = false; | |
272 StartPipeline(media_source); | |
273 } | |
274 | |
275 void WebMediaPlayerImpl::LoadSetup(const WebKit::WebURL& url) { | |
276 GURL gurl(url); | |
277 UMA_HISTOGRAM_ENUMERATION("Media.URLScheme", URLScheme(gurl), kMaxURLScheme); | |
278 | |
279 // Set subresource URL for crash reporting. | |
280 base::debug::SetCrashKeyValue("subresource_url", gurl.spec()); | |
281 | |
282 // Handle any volume/preload changes that occurred before load(). | |
283 setVolume(GetClient()->volume()); | |
284 setPreload(GetClient()->preload()); | |
285 | |
286 SetNetworkState(WebMediaPlayer::NetworkStateLoading); | |
287 SetReadyState(WebMediaPlayer::ReadyStateHaveNothing); | |
288 media_log_->AddEvent(media_log_->CreateLoadEvent(url.spec())); | |
289 } | |
290 | |
291 void WebMediaPlayerImpl::play() { | |
292 DCHECK(main_loop_->BelongsToCurrentThread()); | |
293 | |
294 paused_ = false; | |
295 pipeline_->SetPlaybackRate(playback_rate_); | |
296 | |
297 media_log_->AddEvent(media_log_->CreateEvent(media::MediaLogEvent::PLAY)); | |
298 | |
299 if (delegate_.get()) | |
300 delegate_->DidPlay(this); | |
301 } | |
302 | |
303 void WebMediaPlayerImpl::pause() { | |
304 DCHECK(main_loop_->BelongsToCurrentThread()); | |
305 | |
306 paused_ = true; | |
307 pipeline_->SetPlaybackRate(0.0f); | |
308 paused_time_ = pipeline_->GetMediaTime(); | |
309 | |
310 media_log_->AddEvent(media_log_->CreateEvent(media::MediaLogEvent::PAUSE)); | |
311 | |
312 if (delegate_.get()) | |
313 delegate_->DidPause(this); | |
314 } | |
315 | |
316 bool WebMediaPlayerImpl::supportsFullscreen() const { | |
317 DCHECK(main_loop_->BelongsToCurrentThread()); | |
318 return true; | |
319 } | |
320 | |
321 bool WebMediaPlayerImpl::supportsSave() const { | |
322 DCHECK(main_loop_->BelongsToCurrentThread()); | |
323 return supports_save_; | |
324 } | |
325 | |
326 void WebMediaPlayerImpl::seek(double seconds) { | |
327 DCHECK(main_loop_->BelongsToCurrentThread()); | |
328 | |
329 base::TimeDelta seek_time = ConvertSecondsToTimestamp(seconds); | |
330 | |
331 if (starting_ || seeking_) { | |
332 pending_seek_ = true; | |
333 pending_seek_seconds_ = seconds; | |
334 if (chunk_demuxer_) | |
335 chunk_demuxer_->CancelPendingSeek(seek_time); | |
336 return; | |
337 } | |
338 | |
339 media_log_->AddEvent(media_log_->CreateSeekEvent(seconds)); | |
340 | |
341 // Update our paused time. | |
342 if (paused_) | |
343 paused_time_ = seek_time; | |
344 | |
345 seeking_ = true; | |
346 | |
347 if (chunk_demuxer_) | |
348 chunk_demuxer_->StartWaitingForSeek(seek_time); | |
349 | |
350 // Kick off the asynchronous seek! | |
351 pipeline_->Seek( | |
352 seek_time, | |
353 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek)); | |
354 } | |
355 | |
356 void WebMediaPlayerImpl::setRate(double rate) { | |
357 DCHECK(main_loop_->BelongsToCurrentThread()); | |
358 | |
359 // TODO(kylep): Remove when support for negatives is added. Also, modify the | |
360 // following checks so rewind uses reasonable values also. | |
361 if (rate < 0.0) | |
362 return; | |
363 | |
364 // Limit rates to reasonable values by clamping. | |
365 if (rate != 0.0) { | |
366 if (rate < kMinRate) | |
367 rate = kMinRate; | |
368 else if (rate > kMaxRate) | |
369 rate = kMaxRate; | |
370 } | |
371 | |
372 playback_rate_ = rate; | |
373 if (!paused_) { | |
374 pipeline_->SetPlaybackRate(rate); | |
375 } | |
376 } | |
377 | |
378 void WebMediaPlayerImpl::setVolume(double volume) { | |
379 DCHECK(main_loop_->BelongsToCurrentThread()); | |
380 | |
381 pipeline_->SetVolume(volume); | |
382 } | |
383 | |
384 #define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, chromium_name) \ | |
385 COMPILE_ASSERT(static_cast<int>(WebMediaPlayer::webkit_name) == \ | |
386 static_cast<int>(webkit_media::chromium_name), \ | |
387 mismatching_enums) | |
388 COMPILE_ASSERT_MATCHING_ENUM(PreloadNone, NONE); | |
389 COMPILE_ASSERT_MATCHING_ENUM(PreloadMetaData, METADATA); | |
390 COMPILE_ASSERT_MATCHING_ENUM(PreloadAuto, AUTO); | |
391 #undef COMPILE_ASSERT_MATCHING_ENUM | |
392 | |
393 void WebMediaPlayerImpl::setPreload(WebMediaPlayer::Preload preload) { | |
394 DCHECK(main_loop_->BelongsToCurrentThread()); | |
395 | |
396 if (data_source_) | |
397 data_source_->SetPreload(static_cast<webkit_media::Preload>(preload)); | |
398 } | |
399 | |
400 bool WebMediaPlayerImpl::hasVideo() const { | |
401 DCHECK(main_loop_->BelongsToCurrentThread()); | |
402 | |
403 return pipeline_->HasVideo(); | |
404 } | |
405 | |
406 bool WebMediaPlayerImpl::hasAudio() const { | |
407 DCHECK(main_loop_->BelongsToCurrentThread()); | |
408 | |
409 return pipeline_->HasAudio(); | |
410 } | |
411 | |
412 WebKit::WebSize WebMediaPlayerImpl::naturalSize() const { | |
413 DCHECK(main_loop_->BelongsToCurrentThread()); | |
414 | |
415 gfx::Size size; | |
416 pipeline_->GetNaturalVideoSize(&size); | |
417 return WebKit::WebSize(size); | |
418 } | |
419 | |
420 bool WebMediaPlayerImpl::paused() const { | |
421 DCHECK(main_loop_->BelongsToCurrentThread()); | |
422 | |
423 return pipeline_->GetPlaybackRate() == 0.0f; | |
424 } | |
425 | |
426 bool WebMediaPlayerImpl::seeking() const { | |
427 DCHECK(main_loop_->BelongsToCurrentThread()); | |
428 | |
429 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) | |
430 return false; | |
431 | |
432 return seeking_; | |
433 } | |
434 | |
435 double WebMediaPlayerImpl::duration() const { | |
436 DCHECK(main_loop_->BelongsToCurrentThread()); | |
437 | |
438 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) | |
439 return std::numeric_limits<double>::quiet_NaN(); | |
440 | |
441 return GetPipelineDuration(); | |
442 } | |
443 | |
444 double WebMediaPlayerImpl::currentTime() const { | |
445 DCHECK(main_loop_->BelongsToCurrentThread()); | |
446 return (paused_ ? paused_time_ : pipeline_->GetMediaTime()).InSecondsF(); | |
447 } | |
448 | |
449 WebMediaPlayer::NetworkState WebMediaPlayerImpl::networkState() const { | |
450 DCHECK(main_loop_->BelongsToCurrentThread()); | |
451 return network_state_; | |
452 } | |
453 | |
454 WebMediaPlayer::ReadyState WebMediaPlayerImpl::readyState() const { | |
455 DCHECK(main_loop_->BelongsToCurrentThread()); | |
456 return ready_state_; | |
457 } | |
458 | |
459 const WebKit::WebTimeRanges& WebMediaPlayerImpl::buffered() { | |
460 DCHECK(main_loop_->BelongsToCurrentThread()); | |
461 WebKit::WebTimeRanges web_ranges( | |
462 ConvertToWebTimeRanges(pipeline_->GetBufferedTimeRanges())); | |
463 buffered_.swap(web_ranges); | |
464 return buffered_; | |
465 } | |
466 | |
467 double WebMediaPlayerImpl::maxTimeSeekable() const { | |
468 DCHECK(main_loop_->BelongsToCurrentThread()); | |
469 | |
470 // If we haven't even gotten to ReadyStateHaveMetadata yet then just | |
471 // return 0 so that the seekable range is empty. | |
472 if (ready_state_ < WebMediaPlayer::ReadyStateHaveMetadata) | |
473 return 0.0; | |
474 | |
475 // We don't support seeking in streaming media. | |
476 if (data_source_ && data_source_->IsStreaming()) | |
477 return 0.0; | |
478 return duration(); | |
479 } | |
480 | |
481 bool WebMediaPlayerImpl::didLoadingProgress() const { | |
482 DCHECK(main_loop_->BelongsToCurrentThread()); | |
483 return pipeline_->DidLoadingProgress(); | |
484 } | |
485 | |
486 void WebMediaPlayerImpl::paint(WebCanvas* canvas, | |
487 const WebRect& rect, | |
488 unsigned char alpha) { | |
489 DCHECK(main_loop_->BelongsToCurrentThread()); | |
490 | |
491 if (!accelerated_compositing_reported_) { | |
492 accelerated_compositing_reported_ = true; | |
493 // Normally paint() is only called in non-accelerated rendering, but there | |
494 // are exceptions such as webgl where compositing is used in the WebView but | |
495 // video frames are still rendered to a canvas. | |
496 UMA_HISTOGRAM_BOOLEAN( | |
497 "Media.AcceleratedCompositingActive", | |
498 frame_->view()->isAcceleratedCompositingActive()); | |
499 } | |
500 | |
501 // Avoid locking and potentially blocking the video rendering thread while | |
502 // painting in software. | |
503 scoped_refptr<media::VideoFrame> video_frame; | |
504 { | |
505 base::AutoLock auto_lock(lock_); | |
506 video_frame = current_frame_; | |
507 } | |
508 gfx::Rect gfx_rect(rect); | |
509 skcanvas_video_renderer_.Paint(video_frame.get(), canvas, gfx_rect, alpha); | |
510 } | |
511 | |
512 bool WebMediaPlayerImpl::hasSingleSecurityOrigin() const { | |
513 if (data_source_) | |
514 return data_source_->HasSingleOrigin(); | |
515 return true; | |
516 } | |
517 | |
518 bool WebMediaPlayerImpl::didPassCORSAccessCheck() const { | |
519 if (data_source_) | |
520 return data_source_->DidPassCORSAccessCheck(); | |
521 return false; | |
522 } | |
523 | |
524 double WebMediaPlayerImpl::mediaTimeForTimeValue(double timeValue) const { | |
525 return ConvertSecondsToTimestamp(timeValue).InSecondsF(); | |
526 } | |
527 | |
528 unsigned WebMediaPlayerImpl::decodedFrameCount() const { | |
529 DCHECK(main_loop_->BelongsToCurrentThread()); | |
530 | |
531 media::PipelineStatistics stats = pipeline_->GetStatistics(); | |
532 return stats.video_frames_decoded; | |
533 } | |
534 | |
535 unsigned WebMediaPlayerImpl::droppedFrameCount() const { | |
536 DCHECK(main_loop_->BelongsToCurrentThread()); | |
537 | |
538 media::PipelineStatistics stats = pipeline_->GetStatistics(); | |
539 return stats.video_frames_dropped; | |
540 } | |
541 | |
542 unsigned WebMediaPlayerImpl::audioDecodedByteCount() const { | |
543 DCHECK(main_loop_->BelongsToCurrentThread()); | |
544 | |
545 media::PipelineStatistics stats = pipeline_->GetStatistics(); | |
546 return stats.audio_bytes_decoded; | |
547 } | |
548 | |
549 unsigned WebMediaPlayerImpl::videoDecodedByteCount() const { | |
550 DCHECK(main_loop_->BelongsToCurrentThread()); | |
551 | |
552 media::PipelineStatistics stats = pipeline_->GetStatistics(); | |
553 return stats.video_bytes_decoded; | |
554 } | |
555 | |
556 void WebMediaPlayerImpl::SetVideoFrameProviderClient( | |
557 cc::VideoFrameProvider::Client* client) { | |
558 // This is called from both the main renderer thread and the compositor | |
559 // thread (when the main thread is blocked). | |
560 if (video_frame_provider_client_) | |
561 video_frame_provider_client_->StopUsingProvider(); | |
562 video_frame_provider_client_ = client; | |
563 } | |
564 | |
565 scoped_refptr<media::VideoFrame> WebMediaPlayerImpl::GetCurrentFrame() { | |
566 base::AutoLock auto_lock(lock_); | |
567 return current_frame_; | |
568 } | |
569 | |
570 void WebMediaPlayerImpl::PutCurrentFrame( | |
571 const scoped_refptr<media::VideoFrame>& frame) { | |
572 if (!accelerated_compositing_reported_) { | |
573 accelerated_compositing_reported_ = true; | |
574 DCHECK(frame_->view()->isAcceleratedCompositingActive()); | |
575 UMA_HISTOGRAM_BOOLEAN("Media.AcceleratedCompositingActive", true); | |
576 } | |
577 } | |
578 | |
579 bool WebMediaPlayerImpl::copyVideoTextureToPlatformTexture( | |
580 WebKit::WebGraphicsContext3D* web_graphics_context, | |
581 unsigned int texture, | |
582 unsigned int level, | |
583 unsigned int internal_format, | |
584 unsigned int type, | |
585 bool premultiply_alpha, | |
586 bool flip_y) { | |
587 scoped_refptr<media::VideoFrame> video_frame; | |
588 { | |
589 base::AutoLock auto_lock(lock_); | |
590 video_frame = current_frame_; | |
591 } | |
592 | |
593 if (!video_frame.get()) | |
594 return false; | |
595 if (video_frame->format() != media::VideoFrame::NATIVE_TEXTURE) | |
596 return false; | |
597 if (video_frame->texture_target() != GL_TEXTURE_2D) | |
598 return false; | |
599 | |
600 scoped_refptr<media::VideoFrame::MailboxHolder> mailbox_holder = | |
601 video_frame->texture_mailbox(); | |
602 | |
603 uint32 source_texture = web_graphics_context->createTexture(); | |
604 | |
605 web_graphics_context->waitSyncPoint(mailbox_holder->sync_point()); | |
606 web_graphics_context->bindTexture(GL_TEXTURE_2D, source_texture); | |
607 web_graphics_context->consumeTextureCHROMIUM(GL_TEXTURE_2D, | |
608 mailbox_holder->mailbox().name); | |
609 | |
610 // The video is stored in a unmultiplied format, so premultiply | |
611 // if necessary. | |
612 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, | |
613 premultiply_alpha); | |
614 // Application itself needs to take care of setting the right flip_y | |
615 // value down to get the expected result. | |
616 // flip_y==true means to reverse the video orientation while | |
617 // flip_y==false means to keep the intrinsic orientation. | |
618 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, flip_y); | |
619 web_graphics_context->copyTextureCHROMIUM(GL_TEXTURE_2D, | |
620 source_texture, | |
621 texture, | |
622 level, | |
623 internal_format, | |
624 type); | |
625 web_graphics_context->pixelStorei(GL_UNPACK_FLIP_Y_CHROMIUM, false); | |
626 web_graphics_context->pixelStorei(GL_UNPACK_PREMULTIPLY_ALPHA_CHROMIUM, | |
627 false); | |
628 | |
629 web_graphics_context->deleteTexture(source_texture); | |
630 | |
631 // The flush() operation is not necessary here. It is kept since the | |
632 // performance will be better when it is added than not. | |
633 web_graphics_context->flush(); | |
634 return true; | |
635 } | |
636 | |
637 // Helper functions to report media EME related stats to UMA. They follow the | |
638 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and | |
639 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is | |
640 // that UMA_* macros require the names to be constant throughout the process' | |
641 // lifetime. | |
642 static void EmeUMAHistogramEnumeration(const WebKit::WebString& key_system, | |
643 const std::string& method, | |
644 int sample, | |
645 int boundary_value) { | |
646 base::LinearHistogram::FactoryGet( | |
647 kMediaEme + KeySystemNameForUMA(key_system) + "." + method, | |
648 1, boundary_value, boundary_value + 1, | |
649 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample); | |
650 } | |
651 | |
652 static void EmeUMAHistogramCounts(const WebKit::WebString& key_system, | |
653 const std::string& method, | |
654 int sample) { | |
655 // Use the same parameters as UMA_HISTOGRAM_COUNTS. | |
656 base::Histogram::FactoryGet( | |
657 kMediaEme + KeySystemNameForUMA(key_system) + "." + method, | |
658 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample); | |
659 } | |
660 | |
661 // Helper enum for reporting generateKeyRequest/addKey histograms. | |
662 enum MediaKeyException { | |
663 kUnknownResultId, | |
664 kSuccess, | |
665 kKeySystemNotSupported, | |
666 kInvalidPlayerState, | |
667 kMaxMediaKeyException | |
668 }; | |
669 | |
670 static MediaKeyException MediaKeyExceptionForUMA( | |
671 WebMediaPlayer::MediaKeyException e) { | |
672 switch (e) { | |
673 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported: | |
674 return kKeySystemNotSupported; | |
675 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState: | |
676 return kInvalidPlayerState; | |
677 case WebMediaPlayer::MediaKeyExceptionNoError: | |
678 return kSuccess; | |
679 default: | |
680 return kUnknownResultId; | |
681 } | |
682 } | |
683 | |
684 // Helper for converting |key_system| name and exception |e| to a pair of enum | |
685 // values from above, for reporting to UMA. | |
686 static void ReportMediaKeyExceptionToUMA( | |
687 const std::string& method, | |
688 const WebString& key_system, | |
689 WebMediaPlayer::MediaKeyException e) { | |
690 MediaKeyException result_id = MediaKeyExceptionForUMA(e); | |
691 DCHECK_NE(result_id, kUnknownResultId) << e; | |
692 EmeUMAHistogramEnumeration( | |
693 key_system, method, result_id, kMaxMediaKeyException); | |
694 } | |
695 | |
696 WebMediaPlayer::MediaKeyException | |
697 WebMediaPlayerImpl::generateKeyRequest(const WebString& key_system, | |
698 const unsigned char* init_data, | |
699 unsigned init_data_length) { | |
700 WebMediaPlayer::MediaKeyException e = | |
701 GenerateKeyRequestInternal(key_system, init_data, init_data_length); | |
702 ReportMediaKeyExceptionToUMA("generateKeyRequest", key_system, e); | |
703 return e; | |
704 } | |
705 | |
706 WebMediaPlayer::MediaKeyException | |
707 WebMediaPlayerImpl::GenerateKeyRequestInternal( | |
708 const WebString& key_system, | |
709 const unsigned char* init_data, | |
710 unsigned init_data_length) { | |
711 DVLOG(1) << "generateKeyRequest: " << key_system.utf8().data() << ": " | |
712 << std::string(reinterpret_cast<const char*>(init_data), | |
713 static_cast<size_t>(init_data_length)); | |
714 | |
715 if (!IsSupportedKeySystem(key_system)) | |
716 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
717 | |
718 // We do not support run-time switching between key systems for now. | |
719 if (current_key_system_.isEmpty()) { | |
720 if (!decryptor_->InitializeCDM(key_system.utf8())) | |
721 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
722 current_key_system_ = key_system; | |
723 } | |
724 else if (key_system != current_key_system_) { | |
725 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
726 } | |
727 | |
728 // TODO(xhwang): We assume all streams are from the same container (thus have | |
729 // the same "type") for now. In the future, the "type" should be passed down | |
730 // from the application. | |
731 if (!decryptor_->GenerateKeyRequest(init_data_type_, | |
732 init_data, init_data_length)) { | |
733 current_key_system_.reset(); | |
734 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
735 } | |
736 | |
737 return WebMediaPlayer::MediaKeyExceptionNoError; | |
738 } | |
739 | |
740 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::addKey( | |
741 const WebString& key_system, | |
742 const unsigned char* key, | |
743 unsigned key_length, | |
744 const unsigned char* init_data, | |
745 unsigned init_data_length, | |
746 const WebString& session_id) { | |
747 WebMediaPlayer::MediaKeyException e = AddKeyInternal( | |
748 key_system, key, key_length, init_data, init_data_length, session_id); | |
749 ReportMediaKeyExceptionToUMA("addKey", key_system, e); | |
750 return e; | |
751 } | |
752 | |
753 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::AddKeyInternal( | |
754 const WebString& key_system, | |
755 const unsigned char* key, | |
756 unsigned key_length, | |
757 const unsigned char* init_data, | |
758 unsigned init_data_length, | |
759 const WebString& session_id) { | |
760 DCHECK(key); | |
761 DCHECK_GT(key_length, 0u); | |
762 DVLOG(1) << "addKey: " << key_system.utf8().data() << ": " | |
763 << std::string(reinterpret_cast<const char*>(key), | |
764 static_cast<size_t>(key_length)) << ", " | |
765 << std::string(reinterpret_cast<const char*>(init_data), | |
766 static_cast<size_t>(init_data_length)) | |
767 << " [" << session_id.utf8().data() << "]"; | |
768 | |
769 | |
770 if (!IsSupportedKeySystem(key_system)) | |
771 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
772 | |
773 if (current_key_system_.isEmpty() || key_system != current_key_system_) | |
774 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
775 | |
776 decryptor_->AddKey(key, key_length, | |
777 init_data, init_data_length, session_id.utf8()); | |
778 return WebMediaPlayer::MediaKeyExceptionNoError; | |
779 } | |
780 | |
781 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::cancelKeyRequest( | |
782 const WebString& key_system, | |
783 const WebString& session_id) { | |
784 WebMediaPlayer::MediaKeyException e = | |
785 CancelKeyRequestInternal(key_system, session_id); | |
786 ReportMediaKeyExceptionToUMA("cancelKeyRequest", key_system, e); | |
787 return e; | |
788 } | |
789 | |
790 WebMediaPlayer::MediaKeyException | |
791 WebMediaPlayerImpl::CancelKeyRequestInternal( | |
792 const WebString& key_system, | |
793 const WebString& session_id) { | |
794 if (!IsSupportedKeySystem(key_system)) | |
795 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
796 | |
797 if (current_key_system_.isEmpty() || key_system != current_key_system_) | |
798 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
799 | |
800 decryptor_->CancelKeyRequest(session_id.utf8()); | |
801 return WebMediaPlayer::MediaKeyExceptionNoError; | |
802 } | |
803 | |
804 void WebMediaPlayerImpl::WillDestroyCurrentMessageLoop() { | |
805 Destroy(); | |
806 } | |
807 | |
808 void WebMediaPlayerImpl::Repaint() { | |
809 DCHECK(main_loop_->BelongsToCurrentThread()); | |
810 | |
811 bool size_changed = false; | |
812 { | |
813 base::AutoLock auto_lock(lock_); | |
814 std::swap(pending_size_change_, size_changed); | |
815 pending_repaint_ = false; | |
816 } | |
817 | |
818 if (size_changed) | |
819 GetClient()->sizeChanged(); | |
820 | |
821 GetClient()->repaint(); | |
822 } | |
823 | |
824 void WebMediaPlayerImpl::OnPipelineSeek(PipelineStatus status) { | |
825 DCHECK(main_loop_->BelongsToCurrentThread()); | |
826 starting_ = false; | |
827 seeking_ = false; | |
828 if (pending_seek_) { | |
829 pending_seek_ = false; | |
830 seek(pending_seek_seconds_); | |
831 return; | |
832 } | |
833 | |
834 if (status != media::PIPELINE_OK) { | |
835 OnPipelineError(status); | |
836 return; | |
837 } | |
838 | |
839 // Update our paused time. | |
840 if (paused_) | |
841 paused_time_ = pipeline_->GetMediaTime(); | |
842 | |
843 GetClient()->timeChanged(); | |
844 } | |
845 | |
846 void WebMediaPlayerImpl::OnPipelineEnded() { | |
847 DCHECK(main_loop_->BelongsToCurrentThread()); | |
848 GetClient()->timeChanged(); | |
849 } | |
850 | |
851 void WebMediaPlayerImpl::OnPipelineError(PipelineStatus error) { | |
852 DCHECK(main_loop_->BelongsToCurrentThread()); | |
853 DCHECK_NE(error, media::PIPELINE_OK); | |
854 | |
855 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) { | |
856 // Any error that occurs before reaching ReadyStateHaveMetadata should | |
857 // be considered a format error. | |
858 SetNetworkState(WebMediaPlayer::NetworkStateFormatError); | |
859 Repaint(); | |
860 return; | |
861 } | |
862 | |
863 SetNetworkState(PipelineErrorToNetworkState(error)); | |
864 | |
865 if (error == media::PIPELINE_ERROR_DECRYPT) | |
866 EmeUMAHistogramCounts(current_key_system_, "DecryptError", 1); | |
867 | |
868 // Repaint to trigger UI update. | |
869 Repaint(); | |
870 } | |
871 | |
872 void WebMediaPlayerImpl::OnPipelineBufferingState( | |
873 media::Pipeline::BufferingState buffering_state) { | |
874 DVLOG(1) << "OnPipelineBufferingState(" << buffering_state << ")"; | |
875 | |
876 switch (buffering_state) { | |
877 case media::Pipeline::kHaveMetadata: | |
878 SetReadyState(WebMediaPlayer::ReadyStateHaveMetadata); | |
879 | |
880 if (hasVideo() && GetClient()->needsWebLayerForVideo()) { | |
881 DCHECK(!video_weblayer_); | |
882 video_weblayer_.reset( | |
883 new webkit::WebLayerImpl(cc::VideoLayer::Create(this))); | |
884 GetClient()->setWebLayer(video_weblayer_.get()); | |
885 } | |
886 break; | |
887 case media::Pipeline::kPrerollCompleted: | |
888 // Only transition to ReadyStateHaveEnoughData if we don't have | |
889 // any pending seeks because the transition can cause Blink to | |
890 // report that the most recent seek has completed. | |
891 if (!pending_seek_) | |
892 SetReadyState(WebMediaPlayer::ReadyStateHaveEnoughData); | |
893 break; | |
894 } | |
895 | |
896 // Repaint to trigger UI update. | |
897 Repaint(); | |
898 } | |
899 | |
900 void WebMediaPlayerImpl::OnDemuxerOpened( | |
901 scoped_ptr<WebKit::WebMediaSource> media_source) { | |
902 DCHECK(main_loop_->BelongsToCurrentThread()); | |
903 media_source->open(new WebMediaSourceClientImpl( | |
904 chunk_demuxer_, base::Bind(&LogMediaSourceError, media_log_))); | |
905 } | |
906 | |
907 void WebMediaPlayerImpl::OnKeyAdded(const std::string& session_id) { | |
908 DCHECK(main_loop_->BelongsToCurrentThread()); | |
909 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1); | |
910 GetClient()->keyAdded(current_key_system_, | |
911 WebString::fromUTF8(session_id)); | |
912 } | |
913 | |
914 void WebMediaPlayerImpl::OnNeedKey(const std::string& session_id, | |
915 const std::string& type, | |
916 scoped_ptr<uint8[]> init_data, | |
917 int init_data_size) { | |
918 DCHECK(main_loop_->BelongsToCurrentThread()); | |
919 | |
920 // Do not fire NeedKey event if encrypted media is not enabled. | |
921 if (!decryptor_) | |
922 return; | |
923 | |
924 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1); | |
925 | |
926 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_); | |
927 if (init_data_type_.empty()) | |
928 init_data_type_ = type; | |
929 | |
930 GetClient()->keyNeeded(WebString(), | |
931 WebString::fromUTF8(session_id), | |
932 init_data.get(), | |
933 init_data_size); | |
934 } | |
935 | |
936 scoped_ptr<media::TextTrack> | |
937 WebMediaPlayerImpl::OnTextTrack(media::TextKind kind, | |
938 const std::string& label, | |
939 const std::string& language) { | |
940 typedef WebInbandTextTrackImpl::Kind webkind_t; | |
941 const webkind_t webkind = static_cast<webkind_t>(kind); | |
942 const WebKit::WebString weblabel = WebKit::WebString::fromUTF8(label); | |
943 const WebKit::WebString weblanguage = WebKit::WebString::fromUTF8(language); | |
944 | |
945 WebInbandTextTrackImpl* const text_track = | |
946 new WebInbandTextTrackImpl(webkind, weblabel, weblanguage, | |
947 text_track_index_++); | |
948 GetClient()->addTextTrack(text_track); | |
949 | |
950 return scoped_ptr<media::TextTrack>(new TextTrackImpl(text_track)); | |
951 } | |
952 | |
953 void WebMediaPlayerImpl::OnKeyError(const std::string& session_id, | |
954 media::MediaKeys::KeyError error_code, | |
955 int system_code) { | |
956 DCHECK(main_loop_->BelongsToCurrentThread()); | |
957 | |
958 EmeUMAHistogramEnumeration(current_key_system_, "KeyError", | |
959 error_code, media::MediaKeys::kMaxKeyError); | |
960 | |
961 GetClient()->keyError( | |
962 current_key_system_, | |
963 WebString::fromUTF8(session_id), | |
964 static_cast<WebKit::WebMediaPlayerClient::MediaKeyErrorCode>(error_code), | |
965 system_code); | |
966 } | |
967 | |
968 void WebMediaPlayerImpl::OnKeyMessage(const std::string& session_id, | |
969 const std::string& message, | |
970 const std::string& default_url) { | |
971 DCHECK(main_loop_->BelongsToCurrentThread()); | |
972 | |
973 const GURL default_url_gurl(default_url); | |
974 DLOG_IF(WARNING, !default_url.empty() && !default_url_gurl.is_valid()) | |
975 << "Invalid URL in default_url: " << default_url; | |
976 | |
977 GetClient()->keyMessage(current_key_system_, | |
978 WebString::fromUTF8(session_id), | |
979 reinterpret_cast<const uint8*>(message.data()), | |
980 message.size(), | |
981 default_url_gurl); | |
982 } | |
983 | |
984 void WebMediaPlayerImpl::SetOpaque(bool opaque) { | |
985 DCHECK(main_loop_->BelongsToCurrentThread()); | |
986 | |
987 GetClient()->setOpaque(opaque); | |
988 } | |
989 | |
990 void WebMediaPlayerImpl::DataSourceInitialized(const GURL& gurl, bool success) { | |
991 DCHECK(main_loop_->BelongsToCurrentThread()); | |
992 | |
993 if (!success) { | |
994 SetNetworkState(WebMediaPlayer::NetworkStateFormatError); | |
995 Repaint(); | |
996 return; | |
997 } | |
998 | |
999 StartPipeline(NULL); | |
1000 } | |
1001 | |
1002 void WebMediaPlayerImpl::NotifyDownloading(bool is_downloading) { | |
1003 if (!is_downloading && network_state_ == WebMediaPlayer::NetworkStateLoading) | |
1004 SetNetworkState(WebMediaPlayer::NetworkStateIdle); | |
1005 else if (is_downloading && network_state_ == WebMediaPlayer::NetworkStateIdle) | |
1006 SetNetworkState(WebMediaPlayer::NetworkStateLoading); | |
1007 media_log_->AddEvent( | |
1008 media_log_->CreateBooleanEvent( | |
1009 media::MediaLogEvent::NETWORK_ACTIVITY_SET, | |
1010 "is_downloading_data", is_downloading)); | |
1011 } | |
1012 | |
1013 void WebMediaPlayerImpl::StartPipeline(WebKit::WebMediaSource* media_source) { | |
1014 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); | |
1015 bool increase_preroll_on_underflow = true; | |
1016 | |
1017 // Keep track if this is a MSE or non-MSE playback. | |
1018 UMA_HISTOGRAM_BOOLEAN("Media.MSE.Playback", (media_source != NULL)); | |
1019 | |
1020 // Figure out which demuxer to use. | |
1021 if (!media_source) { | |
1022 DCHECK(!chunk_demuxer_); | |
1023 DCHECK(data_source_); | |
1024 | |
1025 demuxer_.reset(new media::FFmpegDemuxer( | |
1026 media_loop_, data_source_.get(), | |
1027 BIND_TO_RENDER_LOOP_1(&WebMediaPlayerImpl::OnNeedKey, ""))); | |
1028 } else { | |
1029 DCHECK(!chunk_demuxer_); | |
1030 DCHECK(!data_source_); | |
1031 | |
1032 scoped_ptr<WebKit::WebMediaSource> ms(media_source); | |
1033 chunk_demuxer_ = new media::ChunkDemuxer( | |
1034 BIND_TO_RENDER_LOOP_1(&WebMediaPlayerImpl::OnDemuxerOpened, | |
1035 base::Passed(&ms)), | |
1036 BIND_TO_RENDER_LOOP_1(&WebMediaPlayerImpl::OnNeedKey, ""), | |
1037 base::Bind(&WebMediaPlayerImpl::OnTextTrack, base::Unretained(this)), | |
1038 base::Bind(&LogMediaSourceError, media_log_)); | |
1039 demuxer_.reset(chunk_demuxer_); | |
1040 | |
1041 // Disable GpuVideoDecoder creation until it supports codec config changes. | |
1042 // TODO(acolwell): Remove this once http://crbug.com/151045 is fixed. | |
1043 gpu_factories_ = NULL; | |
1044 | |
1045 // Disable preroll increases on underflow since the web application has no | |
1046 // way to detect that this is happening and runs the risk of triggering | |
1047 // unwanted garbage collection if it is to aggressive about appending data. | |
1048 // TODO(acolwell): Remove this once http://crbug.com/144683 is fixed. | |
1049 increase_preroll_on_underflow = false; | |
1050 } | |
1051 | |
1052 scoped_ptr<media::FilterCollection> filter_collection( | |
1053 new media::FilterCollection()); | |
1054 filter_collection->SetDemuxer(demuxer_.get()); | |
1055 | |
1056 // Figure out if EME is enabled. | |
1057 media::SetDecryptorReadyCB set_decryptor_ready_cb; | |
1058 if (decryptor_) { | |
1059 set_decryptor_ready_cb = base::Bind(&ProxyDecryptor::SetDecryptorReadyCB, | |
1060 base::Unretained(decryptor_.get())); | |
1061 } | |
1062 | |
1063 // Create our audio decoders and renderer. | |
1064 ScopedVector<media::AudioDecoder> audio_decoders; | |
1065 audio_decoders.push_back(new media::FFmpegAudioDecoder(media_loop_)); | |
1066 if (cmd_line->HasSwitch(switches::kEnableOpusPlayback)) { | |
1067 audio_decoders.push_back(new media::OpusAudioDecoder(media_loop_)); | |
1068 } | |
1069 | |
1070 scoped_ptr<media::AudioRenderer> audio_renderer( | |
1071 new media::AudioRendererImpl(media_loop_, | |
1072 audio_source_provider_.get(), | |
1073 audio_decoders.Pass(), | |
1074 set_decryptor_ready_cb, | |
1075 increase_preroll_on_underflow)); | |
1076 filter_collection->SetAudioRenderer(audio_renderer.Pass()); | |
1077 | |
1078 // Create our video decoders and renderer. | |
1079 ScopedVector<media::VideoDecoder> video_decoders; | |
1080 | |
1081 if (gpu_factories_.get()) { | |
1082 video_decoders.push_back(new media::GpuVideoDecoder( | |
1083 media_loop_, gpu_factories_)); | |
1084 } | |
1085 | |
1086 // TODO(phajdan.jr): Remove ifdefs when libvpx with vp9 support is released | |
1087 // (http://crbug.com/174287) . | |
1088 #if !defined(MEDIA_DISABLE_LIBVPX) | |
1089 video_decoders.push_back(new media::VpxVideoDecoder(media_loop_)); | |
1090 #endif // !defined(MEDIA_DISABLE_LIBVPX) | |
1091 | |
1092 video_decoders.push_back(new media::FFmpegVideoDecoder(media_loop_)); | |
1093 | |
1094 scoped_ptr<media::VideoRenderer> video_renderer( | |
1095 new media::VideoRendererBase( | |
1096 media_loop_, | |
1097 video_decoders.Pass(), | |
1098 set_decryptor_ready_cb, | |
1099 base::Bind(&WebMediaPlayerImpl::FrameReady, base::Unretained(this)), | |
1100 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::SetOpaque), | |
1101 true)); | |
1102 filter_collection->SetVideoRenderer(video_renderer.Pass()); | |
1103 | |
1104 // ... and we're ready to go! | |
1105 starting_ = true; | |
1106 pipeline_->Start( | |
1107 filter_collection.Pass(), | |
1108 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineEnded), | |
1109 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineError), | |
1110 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineSeek), | |
1111 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnPipelineBufferingState), | |
1112 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDurationChange)); | |
1113 } | |
1114 | |
1115 void WebMediaPlayerImpl::SetNetworkState(WebMediaPlayer::NetworkState state) { | |
1116 DCHECK(main_loop_->BelongsToCurrentThread()); | |
1117 DVLOG(1) << "SetNetworkState: " << state; | |
1118 network_state_ = state; | |
1119 // Always notify to ensure client has the latest value. | |
1120 GetClient()->networkStateChanged(); | |
1121 } | |
1122 | |
1123 void WebMediaPlayerImpl::SetReadyState(WebMediaPlayer::ReadyState state) { | |
1124 DCHECK(main_loop_->BelongsToCurrentThread()); | |
1125 DVLOG(1) << "SetReadyState: " << state; | |
1126 | |
1127 if (state == WebMediaPlayer::ReadyStateHaveEnoughData && | |
1128 is_local_source_ && | |
1129 network_state_ == WebMediaPlayer::NetworkStateLoading) | |
1130 SetNetworkState(WebMediaPlayer::NetworkStateLoaded); | |
1131 | |
1132 ready_state_ = state; | |
1133 // Always notify to ensure client has the latest value. | |
1134 GetClient()->readyStateChanged(); | |
1135 } | |
1136 | |
1137 void WebMediaPlayerImpl::Destroy() { | |
1138 DCHECK(main_loop_->BelongsToCurrentThread()); | |
1139 | |
1140 // Abort any pending IO so stopping the pipeline doesn't get blocked. | |
1141 if (data_source_) | |
1142 data_source_->Abort(); | |
1143 if (chunk_demuxer_) { | |
1144 chunk_demuxer_->Shutdown(); | |
1145 chunk_demuxer_ = NULL; | |
1146 } | |
1147 | |
1148 if (gpu_factories_.get()) { | |
1149 gpu_factories_->Abort(); | |
1150 gpu_factories_ = NULL; | |
1151 } | |
1152 | |
1153 // Make sure to kill the pipeline so there's no more media threads running. | |
1154 // Note: stopping the pipeline might block for a long time. | |
1155 base::WaitableEvent waiter(false, false); | |
1156 pipeline_->Stop(base::Bind( | |
1157 &base::WaitableEvent::Signal, base::Unretained(&waiter))); | |
1158 waiter.Wait(); | |
1159 | |
1160 // Let V8 know we are not using extra resources anymore. | |
1161 if (incremented_externally_allocated_memory_) { | |
1162 v8::V8::AdjustAmountOfExternalAllocatedMemory(-kPlayerExtraMemory); | |
1163 incremented_externally_allocated_memory_ = false; | |
1164 } | |
1165 | |
1166 // Release any final references now that everything has stopped. | |
1167 pipeline_.reset(); | |
1168 demuxer_.reset(); | |
1169 data_source_.reset(); | |
1170 } | |
1171 | |
1172 WebKit::WebMediaPlayerClient* WebMediaPlayerImpl::GetClient() { | |
1173 DCHECK(main_loop_->BelongsToCurrentThread()); | |
1174 DCHECK(client_); | |
1175 return client_; | |
1176 } | |
1177 | |
1178 WebKit::WebAudioSourceProvider* WebMediaPlayerImpl::audioSourceProvider() { | |
1179 return audio_source_provider_.get(); | |
1180 } | |
1181 | |
1182 void WebMediaPlayerImpl::IncrementExternallyAllocatedMemory() { | |
1183 DCHECK(main_loop_->BelongsToCurrentThread()); | |
1184 incremented_externally_allocated_memory_ = true; | |
1185 v8::V8::AdjustAmountOfExternalAllocatedMemory(kPlayerExtraMemory); | |
1186 } | |
1187 | |
1188 double WebMediaPlayerImpl::GetPipelineDuration() const { | |
1189 base::TimeDelta duration = pipeline_->GetMediaDuration(); | |
1190 | |
1191 // Return positive infinity if the resource is unbounded. | |
1192 // http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#dom-
media-duration | |
1193 if (duration == media::kInfiniteDuration()) | |
1194 return std::numeric_limits<double>::infinity(); | |
1195 | |
1196 return duration.InSecondsF(); | |
1197 } | |
1198 | |
1199 void WebMediaPlayerImpl::OnDurationChange() { | |
1200 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) | |
1201 return; | |
1202 | |
1203 GetClient()->durationChanged(); | |
1204 } | |
1205 | |
1206 void WebMediaPlayerImpl::FrameReady( | |
1207 const scoped_refptr<media::VideoFrame>& frame) { | |
1208 base::AutoLock auto_lock(lock_); | |
1209 | |
1210 if (current_frame_.get() && | |
1211 current_frame_->natural_size() != frame->natural_size() && | |
1212 !pending_size_change_) { | |
1213 pending_size_change_ = true; | |
1214 } | |
1215 | |
1216 current_frame_ = frame; | |
1217 | |
1218 if (pending_repaint_) | |
1219 return; | |
1220 | |
1221 pending_repaint_ = true; | |
1222 main_loop_->PostTask(FROM_HERE, base::Bind( | |
1223 &WebMediaPlayerImpl::Repaint, AsWeakPtr())); | |
1224 } | |
1225 | |
1226 } // namespace webkit_media | |
OLD | NEW |