OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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/glue/webmediaplayer_impl.h" | |
6 | |
7 #include <limits> | |
8 #include <string> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/callback.h" | |
12 #include "base/command_line.h" | |
13 #include "base/metrics/histogram.h" | |
14 #include "media/base/composite_data_source_factory.h" | |
15 #include "media/base/filter_collection.h" | |
16 #include "media/base/limits.h" | |
17 #include "media/base/media_log.h" | |
18 #include "media/base/media_switches.h" | |
19 #include "media/base/pipeline_impl.h" | |
20 #include "media/base/video_frame.h" | |
21 #include "media/filters/chunk_demuxer_factory.h" | |
22 #include "media/filters/dummy_demuxer_factory.h" | |
23 #include "media/filters/ffmpeg_audio_decoder.h" | |
24 #include "media/filters/ffmpeg_demuxer_factory.h" | |
25 #include "media/filters/ffmpeg_video_decoder.h" | |
26 #include "media/filters/null_audio_renderer.h" | |
27 #include "third_party/WebKit/Source/WebKit/chromium/public/WebRect.h" | |
28 #include "third_party/WebKit/Source/WebKit/chromium/public/WebSize.h" | |
29 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURL.h" | |
30 #include "third_party/WebKit/Source/WebKit/chromium/public/WebVideoFrame.h" | |
31 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" | |
32 #include "v8/include/v8.h" | |
33 #include "webkit/glue/media/buffered_data_source.h" | |
34 #include "webkit/glue/media/simple_data_source.h" | |
35 #include "webkit/glue/media/media_stream_client.h" | |
36 #include "webkit/glue/media/video_renderer_impl.h" | |
37 #include "webkit/glue/media/web_video_renderer.h" | |
38 #include "webkit/glue/webmediaplayer_delegate.h" | |
39 #include "webkit/glue/webmediaplayer_proxy.h" | |
40 #include "webkit/glue/webvideoframe_impl.h" | |
41 | |
42 using WebKit::WebCanvas; | |
43 using WebKit::WebRect; | |
44 using WebKit::WebSize; | |
45 using media::PipelineStatus; | |
46 | |
47 namespace { | |
48 | |
49 // Amount of extra memory used by each player instance reported to V8. | |
50 // It is not exact number -- first, it differs on different platforms, | |
51 // and second, it is very hard to calculate. Instead, use some arbitrary | |
52 // value that will cause garbage collection from time to time. We don't want | |
53 // it to happen on every allocation, but don't want 5k players to sit in memory | |
54 // either. Looks that chosen constant achieves both goals, at least for audio | |
55 // objects. (Do not worry about video objects yet, JS programs do not create | |
56 // thousands of them...) | |
57 const int kPlayerExtraMemory = 1024 * 1024; | |
58 | |
59 // Limits the range of playback rate. | |
60 // | |
61 // TODO(kylep): Revisit these. | |
62 // | |
63 // Vista has substantially lower performance than XP or Windows7. If you speed | |
64 // up a video too much, it can't keep up, and rendering stops updating except on | |
65 // the time bar. For really high speeds, audio becomes a bottleneck and we just | |
66 // use up the data we have, which may not achieve the speed requested, but will | |
67 // not crash the tab. | |
68 // | |
69 // A very slow speed, ie 0.00000001x, causes the machine to lock up. (It seems | |
70 // like a busy loop). It gets unresponsive, although its not completely dead. | |
71 // | |
72 // Also our timers are not very accurate (especially for ogg), which becomes | |
73 // evident at low speeds and on Vista. Since other speeds are risky and outside | |
74 // the norms, we think 1/16x to 16x is a safe and useful range for now. | |
75 const float kMinRate = 0.0625f; | |
76 const float kMaxRate = 16.0f; | |
77 | |
78 // Platform independent method for converting and rounding floating point | |
79 // seconds to an int64 timestamp. | |
80 // | |
81 // Refer to https://bugs.webkit.org/show_bug.cgi?id=52697 for details. | |
82 base::TimeDelta ConvertSecondsToTimestamp(float seconds) { | |
83 float microseconds = seconds * base::Time::kMicrosecondsPerSecond; | |
84 float integer = ceilf(microseconds); | |
85 float difference = integer - microseconds; | |
86 | |
87 // Round down if difference is large enough. | |
88 if ((microseconds > 0 && difference > 0.5f) || | |
89 (microseconds <= 0 && difference >= 0.5f)) { | |
90 integer -= 1.0f; | |
91 } | |
92 | |
93 // Now we can safely cast to int64 microseconds. | |
94 return base::TimeDelta::FromMicroseconds(static_cast<int64>(integer)); | |
95 } | |
96 | |
97 } // namespace | |
98 | |
99 namespace webkit_glue { | |
100 | |
101 WebMediaPlayerImpl::WebMediaPlayerImpl( | |
102 WebKit::WebMediaPlayerClient* client, | |
103 base::WeakPtr<WebMediaPlayerDelegate> delegate, | |
104 media::FilterCollection* collection, | |
105 media::MessageLoopFactory* message_loop_factory, | |
106 MediaStreamClient* media_stream_client, | |
107 media::MediaLog* media_log) | |
108 : network_state_(WebKit::WebMediaPlayer::Empty), | |
109 ready_state_(WebKit::WebMediaPlayer::HaveNothing), | |
110 main_loop_(NULL), | |
111 filter_collection_(collection), | |
112 pipeline_(NULL), | |
113 message_loop_factory_(message_loop_factory), | |
114 paused_(true), | |
115 seeking_(false), | |
116 playback_rate_(0.0f), | |
117 pending_seek_(false), | |
118 client_(client), | |
119 proxy_(NULL), | |
120 delegate_(delegate), | |
121 media_stream_client_(media_stream_client), | |
122 media_log_(media_log), | |
123 incremented_externally_allocated_memory_(false) { | |
124 // Saves the current message loop. | |
125 DCHECK(!main_loop_); | |
126 main_loop_ = MessageLoop::current(); | |
127 media_log_->AddEvent( | |
128 media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_CREATED)); | |
129 } | |
130 | |
131 bool WebMediaPlayerImpl::Initialize( | |
132 WebKit::WebFrame* frame, | |
133 bool use_simple_data_source, | |
134 scoped_refptr<WebVideoRenderer> web_video_renderer) { | |
135 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
136 MessageLoop* pipeline_message_loop = | |
137 message_loop_factory_->GetMessageLoop("PipelineThread"); | |
138 if (!pipeline_message_loop) { | |
139 NOTREACHED() << "Could not start PipelineThread"; | |
140 return false; | |
141 } | |
142 | |
143 // Let V8 know we started new thread if we did not did it yet. | |
144 // Made separate task to avoid deletion of player currently being created. | |
145 // Also, delaying GC until after player starts gets rid of starting lag -- | |
146 // collection happens in parallel with playing. | |
147 // TODO(enal): remove when we get rid of per-audio-stream thread. | |
148 if (!incremented_externally_allocated_memory_) { | |
149 MessageLoop::current()->PostTask( | |
150 FROM_HERE, | |
151 base::Bind(&WebMediaPlayerImpl::IncrementExternallyAllocatedMemory, | |
152 AsWeakPtr())); | |
153 } | |
154 | |
155 UMA_HISTOGRAM_BOOLEAN("Media.AcceleratedCompositingActive", | |
156 frame->view()->isAcceleratedCompositingActive()); | |
157 | |
158 pipeline_ = new media::PipelineImpl(pipeline_message_loop, media_log_); | |
159 | |
160 // Also we want to be notified of |main_loop_| destruction. | |
161 main_loop_->AddDestructionObserver(this); | |
162 | |
163 // Creates the proxy. | |
164 proxy_ = new WebMediaPlayerProxy(main_loop_, this); | |
165 web_video_renderer->SetWebMediaPlayerProxy(proxy_); | |
166 proxy_->SetVideoRenderer(web_video_renderer); | |
167 | |
168 // Set our pipeline callbacks. | |
169 pipeline_->Init( | |
170 base::Bind(&WebMediaPlayerProxy::PipelineEndedCallback, | |
171 proxy_.get()), | |
172 base::Bind(&WebMediaPlayerProxy::PipelineErrorCallback, | |
173 proxy_.get()), | |
174 base::Bind(&WebMediaPlayerProxy::NetworkEventCallback, | |
175 proxy_.get())); | |
176 | |
177 // A simple data source that keeps all data in memory. | |
178 scoped_ptr<media::DataSourceFactory> simple_data_source_factory( | |
179 SimpleDataSource::CreateFactory(MessageLoop::current(), frame, | |
180 media_log_, | |
181 proxy_->GetBuildObserver())); | |
182 | |
183 // A sophisticated data source that does memory caching. | |
184 scoped_ptr<media::DataSourceFactory> buffered_data_source_factory( | |
185 BufferedDataSource::CreateFactory(MessageLoop::current(), frame, | |
186 media_log_, | |
187 proxy_->GetBuildObserver())); | |
188 | |
189 scoped_ptr<media::CompositeDataSourceFactory> data_source_factory( | |
190 new media::CompositeDataSourceFactory()); | |
191 | |
192 if (use_simple_data_source) { | |
193 data_source_factory->AddFactory(simple_data_source_factory.release()); | |
194 data_source_factory->AddFactory(buffered_data_source_factory.release()); | |
195 } else { | |
196 data_source_factory->AddFactory(buffered_data_source_factory.release()); | |
197 data_source_factory->AddFactory(simple_data_source_factory.release()); | |
198 } | |
199 | |
200 scoped_ptr<media::DemuxerFactory> demuxer_factory( | |
201 new media::FFmpegDemuxerFactory(data_source_factory.release(), | |
202 pipeline_message_loop)); | |
203 | |
204 std::string source_url = GetClient()->sourceURL().spec(); | |
205 | |
206 if (!source_url.empty()) { | |
207 demuxer_factory.reset( | |
208 new media::ChunkDemuxerFactory(source_url, | |
209 demuxer_factory.release(), | |
210 proxy_)); | |
211 } | |
212 filter_collection_->SetDemuxerFactory(demuxer_factory.release()); | |
213 | |
214 // Add in the default filter factories. | |
215 filter_collection_->AddAudioDecoder(new media::FFmpegAudioDecoder( | |
216 message_loop_factory_->GetMessageLoop("AudioDecoderThread"))); | |
217 filter_collection_->AddVideoDecoder(new media::FFmpegVideoDecoder( | |
218 message_loop_factory_->GetMessageLoop("VideoDecoderThread"))); | |
219 filter_collection_->AddAudioRenderer(new media::NullAudioRenderer()); | |
220 | |
221 return true; | |
222 } | |
223 | |
224 WebMediaPlayerImpl::~WebMediaPlayerImpl() { | |
225 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
226 Destroy(); | |
227 media_log_->AddEvent( | |
228 media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_DESTROYED)); | |
229 | |
230 if (delegate_) | |
231 delegate_->PlayerGone(this); | |
232 | |
233 // Finally tell the |main_loop_| we don't want to be notified of destruction | |
234 // event. | |
235 if (main_loop_) { | |
236 main_loop_->RemoveDestructionObserver(this); | |
237 } | |
238 } | |
239 | |
240 void WebMediaPlayerImpl::load(const WebKit::WebURL& url) { | |
241 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
242 DCHECK(proxy_); | |
243 | |
244 if (media_stream_client_) { | |
245 bool has_video = false; | |
246 bool has_audio = false; | |
247 scoped_refptr<media::VideoDecoder> new_decoder = | |
248 media_stream_client_->GetVideoDecoder(url, message_loop_factory_.get()); | |
249 if (new_decoder.get()) { | |
250 // Remove the default decoder. | |
251 scoped_refptr<media::VideoDecoder> old_videodecoder; | |
252 filter_collection_->SelectVideoDecoder(&old_videodecoder); | |
253 filter_collection_->AddVideoDecoder(new_decoder.get()); | |
254 has_video = true; | |
255 } | |
256 | |
257 // TODO(wjia): add audio decoder handling when it's available. | |
258 if (has_video || has_audio) | |
259 filter_collection_->SetDemuxerFactory( | |
260 new media::DummyDemuxerFactory(has_video, has_audio)); | |
261 } | |
262 | |
263 // Handle any volume changes that occured before load(). | |
264 setVolume(GetClient()->volume()); | |
265 // Get the preload value. | |
266 setPreload(GetClient()->preload()); | |
267 | |
268 // Initialize the pipeline. | |
269 SetNetworkState(WebKit::WebMediaPlayer::Loading); | |
270 SetReadyState(WebKit::WebMediaPlayer::HaveNothing); | |
271 pipeline_->Start( | |
272 filter_collection_.release(), | |
273 url.spec(), | |
274 base::Bind(&WebMediaPlayerProxy::PipelineInitializationCallback, | |
275 proxy_.get())); | |
276 | |
277 media_log_->AddEvent(media_log_->CreateLoadEvent(url.spec())); | |
278 } | |
279 | |
280 void WebMediaPlayerImpl::cancelLoad() { | |
281 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
282 } | |
283 | |
284 void WebMediaPlayerImpl::play() { | |
285 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
286 | |
287 paused_ = false; | |
288 pipeline_->SetPlaybackRate(playback_rate_); | |
289 | |
290 media_log_->AddEvent(media_log_->CreateEvent(media::MediaLogEvent::PLAY)); | |
291 | |
292 if (delegate_) | |
293 delegate_->DidPlay(this); | |
294 } | |
295 | |
296 void WebMediaPlayerImpl::pause() { | |
297 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
298 | |
299 paused_ = true; | |
300 pipeline_->SetPlaybackRate(0.0f); | |
301 paused_time_ = pipeline_->GetCurrentTime(); | |
302 | |
303 media_log_->AddEvent(media_log_->CreateEvent(media::MediaLogEvent::PAUSE)); | |
304 | |
305 if (delegate_) | |
306 delegate_->DidPause(this); | |
307 } | |
308 | |
309 bool WebMediaPlayerImpl::supportsFullscreen() const { | |
310 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
311 return true; | |
312 } | |
313 | |
314 bool WebMediaPlayerImpl::supportsSave() const { | |
315 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
316 return true; | |
317 } | |
318 | |
319 void WebMediaPlayerImpl::seek(float seconds) { | |
320 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
321 | |
322 // WebKit fires a seek(0) at the very start, however pipeline already does a | |
323 // seek(0) internally. Avoid doing seek(0) the second time because this will | |
324 // cause extra pre-rolling and will break servers without range request | |
325 // support. | |
326 // | |
327 // We still have to notify WebKit that time has changed otherwise | |
328 // HTMLMediaElement gets into an inconsistent state. | |
329 if (pipeline_->GetCurrentTime().ToInternalValue() == 0 && seconds == 0) { | |
330 GetClient()->timeChanged(); | |
331 return; | |
332 } | |
333 | |
334 if (seeking_) { | |
335 pending_seek_ = true; | |
336 pending_seek_seconds_ = seconds; | |
337 return; | |
338 } | |
339 | |
340 media_log_->AddEvent(media_log_->CreateSeekEvent(seconds)); | |
341 | |
342 base::TimeDelta seek_time = ConvertSecondsToTimestamp(seconds); | |
343 | |
344 // Update our paused time. | |
345 if (paused_) { | |
346 paused_time_ = seek_time; | |
347 } | |
348 | |
349 seeking_ = true; | |
350 | |
351 proxy_->DemuxerFlush(); | |
352 | |
353 // Kick off the asynchronous seek! | |
354 pipeline_->Seek( | |
355 seek_time, | |
356 base::Bind(&WebMediaPlayerProxy::PipelineSeekCallback, | |
357 proxy_.get())); | |
358 } | |
359 | |
360 void WebMediaPlayerImpl::setEndTime(float seconds) { | |
361 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
362 | |
363 // TODO(hclam): add method call when it has been implemented. | |
364 return; | |
365 } | |
366 | |
367 void WebMediaPlayerImpl::setRate(float rate) { | |
368 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
369 | |
370 // TODO(kylep): Remove when support for negatives is added. Also, modify the | |
371 // following checks so rewind uses reasonable values also. | |
372 if (rate < 0.0f) | |
373 return; | |
374 | |
375 // Limit rates to reasonable values by clamping. | |
376 if (rate != 0.0f) { | |
377 if (rate < kMinRate) | |
378 rate = kMinRate; | |
379 else if (rate > kMaxRate) | |
380 rate = kMaxRate; | |
381 } | |
382 | |
383 playback_rate_ = rate; | |
384 if (!paused_) { | |
385 pipeline_->SetPlaybackRate(rate); | |
386 } | |
387 } | |
388 | |
389 void WebMediaPlayerImpl::setVolume(float volume) { | |
390 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
391 | |
392 pipeline_->SetVolume(volume); | |
393 } | |
394 | |
395 void WebMediaPlayerImpl::setVisible(bool visible) { | |
396 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
397 | |
398 // TODO(hclam): add appropriate method call when pipeline has it implemented. | |
399 return; | |
400 } | |
401 | |
402 #define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, chromium_name) \ | |
403 COMPILE_ASSERT(static_cast<int>(WebKit::WebMediaPlayer::webkit_name) == \ | |
404 static_cast<int>(media::chromium_name), \ | |
405 mismatching_enums) | |
406 COMPILE_ASSERT_MATCHING_ENUM(None, NONE); | |
407 COMPILE_ASSERT_MATCHING_ENUM(MetaData, METADATA); | |
408 COMPILE_ASSERT_MATCHING_ENUM(Auto, AUTO); | |
409 | |
410 void WebMediaPlayerImpl::setPreload(WebKit::WebMediaPlayer::Preload preload) { | |
411 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
412 | |
413 pipeline_->SetPreload(static_cast<media::Preload>(preload)); | |
414 } | |
415 | |
416 bool WebMediaPlayerImpl::totalBytesKnown() { | |
417 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
418 | |
419 return pipeline_->GetTotalBytes() != 0; | |
420 } | |
421 | |
422 bool WebMediaPlayerImpl::hasVideo() const { | |
423 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
424 | |
425 return pipeline_->HasVideo(); | |
426 } | |
427 | |
428 bool WebMediaPlayerImpl::hasAudio() const { | |
429 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
430 | |
431 return pipeline_->HasAudio(); | |
432 } | |
433 | |
434 WebKit::WebSize WebMediaPlayerImpl::naturalSize() const { | |
435 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
436 | |
437 gfx::Size size; | |
438 pipeline_->GetNaturalVideoSize(&size); | |
439 return WebKit::WebSize(size); | |
440 } | |
441 | |
442 bool WebMediaPlayerImpl::paused() const { | |
443 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
444 | |
445 return pipeline_->GetPlaybackRate() == 0.0f; | |
446 } | |
447 | |
448 bool WebMediaPlayerImpl::seeking() const { | |
449 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
450 | |
451 if (ready_state_ == WebKit::WebMediaPlayer::HaveNothing) | |
452 return false; | |
453 | |
454 return seeking_; | |
455 } | |
456 | |
457 float WebMediaPlayerImpl::duration() const { | |
458 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
459 | |
460 base::TimeDelta duration = pipeline_->GetMediaDuration(); | |
461 if (duration.InMicroseconds() == media::Limits::kMaxTimeInMicroseconds) | |
462 return std::numeric_limits<float>::infinity(); | |
463 return static_cast<float>(duration.InSecondsF()); | |
464 } | |
465 | |
466 float WebMediaPlayerImpl::currentTime() const { | |
467 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
468 if (paused_) { | |
469 return static_cast<float>(paused_time_.InSecondsF()); | |
470 } | |
471 return static_cast<float>(pipeline_->GetCurrentTime().InSecondsF()); | |
472 } | |
473 | |
474 int WebMediaPlayerImpl::dataRate() const { | |
475 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
476 | |
477 // TODO(hclam): Add this method call if pipeline has it in the interface. | |
478 return 0; | |
479 } | |
480 | |
481 WebKit::WebMediaPlayer::NetworkState WebMediaPlayerImpl::networkState() const { | |
482 return network_state_; | |
483 } | |
484 | |
485 WebKit::WebMediaPlayer::ReadyState WebMediaPlayerImpl::readyState() const { | |
486 return ready_state_; | |
487 } | |
488 | |
489 const WebKit::WebTimeRanges& WebMediaPlayerImpl::buffered() { | |
490 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
491 | |
492 // Update buffered_ with the most recent buffered time. | |
493 if (buffered_.size() > 0) { | |
494 float buffered_time = static_cast<float>( | |
495 pipeline_->GetBufferedTime().InSecondsF()); | |
496 if (buffered_time >= buffered_[0].start) | |
497 buffered_[0].end = buffered_time; | |
498 } | |
499 | |
500 return buffered_; | |
501 } | |
502 | |
503 float WebMediaPlayerImpl::maxTimeSeekable() const { | |
504 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
505 | |
506 // If we are performing streaming, we report that we cannot seek at all. | |
507 // We are using this flag to indicate if the data source supports seeking | |
508 // or not. We should be able to seek even if we are performing streaming. | |
509 // TODO(hclam): We need to update this when we have better caching. | |
510 if (pipeline_->IsStreaming()) | |
511 return 0.0f; | |
512 return static_cast<float>(pipeline_->GetMediaDuration().InSecondsF()); | |
513 } | |
514 | |
515 unsigned long long WebMediaPlayerImpl::bytesLoaded() const { | |
516 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
517 | |
518 return pipeline_->GetBufferedBytes(); | |
519 } | |
520 | |
521 unsigned long long WebMediaPlayerImpl::totalBytes() const { | |
522 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
523 | |
524 return pipeline_->GetTotalBytes(); | |
525 } | |
526 | |
527 void WebMediaPlayerImpl::setSize(const WebSize& size) { | |
528 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
529 DCHECK(proxy_); | |
530 | |
531 proxy_->SetSize(gfx::Rect(0, 0, size.width, size.height)); | |
532 } | |
533 | |
534 void WebMediaPlayerImpl::paint(WebCanvas* canvas, | |
535 const WebRect& rect) { | |
536 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
537 DCHECK(proxy_); | |
538 | |
539 #if WEBKIT_USING_SKIA | |
540 proxy_->Paint(canvas, rect); | |
541 #elif WEBKIT_USING_CG | |
542 // Get the current scaling in X and Y. | |
543 CGAffineTransform mat = CGContextGetCTM(canvas); | |
544 float scale_x = sqrt(mat.a * mat.a + mat.b * mat.b); | |
545 float scale_y = sqrt(mat.c * mat.c + mat.d * mat.d); | |
546 float inverse_scale_x = SkScalarNearlyZero(scale_x) ? 0.0f : 1.0f / scale_x; | |
547 float inverse_scale_y = SkScalarNearlyZero(scale_y) ? 0.0f : 1.0f / scale_y; | |
548 int scaled_width = static_cast<int>(rect.width * fabs(scale_x)); | |
549 int scaled_height = static_cast<int>(rect.height * fabs(scale_y)); | |
550 | |
551 // Make sure we don't create a huge canvas. | |
552 // TODO(hclam): Respect the aspect ratio. | |
553 if (scaled_width > static_cast<int>(media::Limits::kMaxCanvas)) | |
554 scaled_width = media::Limits::kMaxCanvas; | |
555 if (scaled_height > static_cast<int>(media::Limits::kMaxCanvas)) | |
556 scaled_height = media::Limits::kMaxCanvas; | |
557 | |
558 // If there is no preexisting platform canvas, or if the size has | |
559 // changed, recreate the canvas. This is to avoid recreating the bitmap | |
560 // buffer over and over for each frame of video. | |
561 if (!skia_canvas_.get() || | |
562 skia_canvas_->getDevice()->width() != scaled_width || | |
563 skia_canvas_->getDevice()->height() != scaled_height) { | |
564 skia_canvas_.reset( | |
565 new skia::PlatformCanvas(scaled_width, scaled_height, true)); | |
566 } | |
567 | |
568 // Draw to our temporary skia canvas. | |
569 gfx::Rect normalized_rect(scaled_width, scaled_height); | |
570 proxy_->Paint(skia_canvas_.get(), normalized_rect); | |
571 | |
572 // The mac coordinate system is flipped vertical from the normal skia | |
573 // coordinates. During painting of the frame, flip the coordinates | |
574 // system and, for simplicity, also translate the clip rectangle to | |
575 // start at 0,0. | |
576 CGContextSaveGState(canvas); | |
577 CGContextTranslateCTM(canvas, rect.x, rect.height + rect.y); | |
578 CGContextScaleCTM(canvas, inverse_scale_x, -inverse_scale_y); | |
579 | |
580 // We need a local variable CGRect version for DrawToContext. | |
581 CGRect normalized_cgrect = | |
582 CGRectMake(normalized_rect.x(), normalized_rect.y(), | |
583 normalized_rect.width(), normalized_rect.height()); | |
584 | |
585 // Copy the frame rendered to our temporary skia canvas onto the passed in | |
586 // canvas. | |
587 skia::DrawToNativeContext(skia_canvas_.get(), canvas, 0, 0, | |
588 &normalized_cgrect); | |
589 | |
590 CGContextRestoreGState(canvas); | |
591 #else | |
592 NOTIMPLEMENTED() << "We only support rendering to skia or CG"; | |
593 #endif | |
594 } | |
595 | |
596 bool WebMediaPlayerImpl::hasSingleSecurityOrigin() const { | |
597 if (proxy_) | |
598 return proxy_->HasSingleOrigin(); | |
599 return true; | |
600 } | |
601 | |
602 WebKit::WebMediaPlayer::MovieLoadType | |
603 WebMediaPlayerImpl::movieLoadType() const { | |
604 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
605 | |
606 // TODO(hclam): If the pipeline is performing streaming, we say that this is | |
607 // a live stream. But instead it should be a StoredStream if we have proper | |
608 // caching. | |
609 if (pipeline_->IsStreaming()) | |
610 return WebKit::WebMediaPlayer::LiveStream; | |
611 return WebKit::WebMediaPlayer::Unknown; | |
612 } | |
613 | |
614 float WebMediaPlayerImpl::mediaTimeForTimeValue(float timeValue) const { | |
615 return ConvertSecondsToTimestamp(timeValue).InSecondsF(); | |
616 } | |
617 | |
618 unsigned WebMediaPlayerImpl::decodedFrameCount() const { | |
619 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
620 | |
621 media::PipelineStatistics stats = pipeline_->GetStatistics(); | |
622 return stats.video_frames_decoded; | |
623 } | |
624 | |
625 unsigned WebMediaPlayerImpl::droppedFrameCount() const { | |
626 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
627 | |
628 media::PipelineStatistics stats = pipeline_->GetStatistics(); | |
629 return stats.video_frames_dropped; | |
630 } | |
631 | |
632 unsigned WebMediaPlayerImpl::audioDecodedByteCount() const { | |
633 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
634 | |
635 media::PipelineStatistics stats = pipeline_->GetStatistics(); | |
636 return stats.audio_bytes_decoded; | |
637 } | |
638 | |
639 unsigned WebMediaPlayerImpl::videoDecodedByteCount() const { | |
640 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
641 | |
642 media::PipelineStatistics stats = pipeline_->GetStatistics(); | |
643 return stats.video_bytes_decoded; | |
644 } | |
645 | |
646 WebKit::WebVideoFrame* WebMediaPlayerImpl::getCurrentFrame() { | |
647 scoped_refptr<media::VideoFrame> video_frame; | |
648 proxy_->GetCurrentFrame(&video_frame); | |
649 if (video_frame.get()) | |
650 return new WebVideoFrameImpl(video_frame); | |
651 return NULL; | |
652 } | |
653 | |
654 void WebMediaPlayerImpl::putCurrentFrame( | |
655 WebKit::WebVideoFrame* web_video_frame) { | |
656 if (web_video_frame) { | |
657 scoped_refptr<media::VideoFrame> video_frame( | |
658 WebVideoFrameImpl::toVideoFrame(web_video_frame)); | |
659 proxy_->PutCurrentFrame(video_frame); | |
660 delete web_video_frame; | |
661 } | |
662 } | |
663 | |
664 bool WebMediaPlayerImpl::sourceAppend(const unsigned char* data, | |
665 unsigned length) { | |
666 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
667 return proxy_->DemuxerAppend(data, length); | |
668 } | |
669 | |
670 void WebMediaPlayerImpl::sourceEndOfStream( | |
671 WebKit::WebMediaPlayer::EndOfStreamStatus status) { | |
672 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
673 media::PipelineStatus pipeline_status = media::PIPELINE_OK; | |
674 | |
675 switch(status) { | |
676 case WebKit::WebMediaPlayer::EosNoError: | |
677 break; | |
678 case WebKit::WebMediaPlayer::EosNetworkError: | |
679 pipeline_status = media::PIPELINE_ERROR_NETWORK; | |
680 break; | |
681 case WebKit::WebMediaPlayer::EosDecodeError: | |
682 pipeline_status = media::PIPELINE_ERROR_DECODE; | |
683 break; | |
684 default: | |
685 NOTIMPLEMENTED(); | |
686 } | |
687 | |
688 proxy_->DemuxerEndOfStream(pipeline_status); | |
689 } | |
690 | |
691 void WebMediaPlayerImpl::WillDestroyCurrentMessageLoop() { | |
692 Destroy(); | |
693 main_loop_ = NULL; | |
694 } | |
695 | |
696 void WebMediaPlayerImpl::Repaint() { | |
697 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
698 GetClient()->repaint(); | |
699 } | |
700 | |
701 void WebMediaPlayerImpl::OnPipelineInitialize(PipelineStatus status) { | |
702 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
703 if (status == media::PIPELINE_OK) { | |
704 // Only keep one time range starting from 0. | |
705 WebKit::WebTimeRanges new_buffered(static_cast<size_t>(1)); | |
706 new_buffered[0].start = 0.0f; | |
707 new_buffered[0].end = | |
708 static_cast<float>(pipeline_->GetMediaDuration().InSecondsF()); | |
709 buffered_.swap(new_buffered); | |
710 | |
711 if (pipeline_->IsLoaded()) { | |
712 SetNetworkState(WebKit::WebMediaPlayer::Loaded); | |
713 } | |
714 | |
715 // Since we have initialized the pipeline, say we have everything otherwise | |
716 // we'll remain either loading/idle. | |
717 // TODO(hclam): change this to report the correct status. | |
718 SetReadyState(WebKit::WebMediaPlayer::HaveMetadata); | |
719 SetReadyState(WebKit::WebMediaPlayer::HaveEnoughData); | |
720 } else { | |
721 // TODO(hclam): should use |status| to determine the state | |
722 // properly and reports error using MediaError. | |
723 // WebKit uses FormatError to indicate an error for bogus URL or bad file. | |
724 // Since we are at the initialization stage we can safely treat every error | |
725 // as format error. Should post a task to call to |webmediaplayer_|. | |
726 SetNetworkState(WebKit::WebMediaPlayer::FormatError); | |
727 } | |
728 | |
729 // Repaint to trigger UI update. | |
730 Repaint(); | |
731 } | |
732 | |
733 void WebMediaPlayerImpl::OnPipelineSeek(PipelineStatus status) { | |
734 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
735 seeking_ = false; | |
736 if (pending_seek_) { | |
737 pending_seek_ = false; | |
738 seek(pending_seek_seconds_); | |
739 return; | |
740 } | |
741 | |
742 if (status == media::PIPELINE_OK) { | |
743 // Update our paused time. | |
744 if (paused_) { | |
745 paused_time_ = pipeline_->GetCurrentTime(); | |
746 } | |
747 | |
748 SetReadyState(WebKit::WebMediaPlayer::HaveEnoughData); | |
749 GetClient()->timeChanged(); | |
750 } | |
751 } | |
752 | |
753 void WebMediaPlayerImpl::OnPipelineEnded(PipelineStatus status) { | |
754 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
755 if (status == media::PIPELINE_OK) { | |
756 GetClient()->timeChanged(); | |
757 } | |
758 } | |
759 | |
760 void WebMediaPlayerImpl::OnPipelineError(PipelineStatus error) { | |
761 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
762 switch (error) { | |
763 case media::PIPELINE_OK: | |
764 LOG(DFATAL) << "PIPELINE_OK isn't an error!"; | |
765 break; | |
766 | |
767 case media::PIPELINE_ERROR_NETWORK: | |
768 SetNetworkState(WebMediaPlayer::NetworkError); | |
769 break; | |
770 | |
771 case media::PIPELINE_ERROR_INITIALIZATION_FAILED: | |
772 case media::PIPELINE_ERROR_REQUIRED_FILTER_MISSING: | |
773 case media::PIPELINE_ERROR_COULD_NOT_RENDER: | |
774 case media::PIPELINE_ERROR_URL_NOT_FOUND: | |
775 case media::PIPELINE_ERROR_READ: | |
776 case media::DEMUXER_ERROR_COULD_NOT_OPEN: | |
777 case media::DEMUXER_ERROR_COULD_NOT_PARSE: | |
778 case media::DEMUXER_ERROR_NO_SUPPORTED_STREAMS: | |
779 case media::DEMUXER_ERROR_COULD_NOT_CREATE_THREAD: | |
780 case media::DECODER_ERROR_NOT_SUPPORTED: | |
781 case media::DATASOURCE_ERROR_URL_NOT_SUPPORTED: | |
782 // Format error. | |
783 SetNetworkState(WebMediaPlayer::FormatError); | |
784 break; | |
785 | |
786 case media::PIPELINE_ERROR_DECODE: | |
787 case media::PIPELINE_ERROR_ABORT: | |
788 case media::PIPELINE_ERROR_OUT_OF_MEMORY: | |
789 case media::PIPELINE_ERROR_AUDIO_HARDWARE: | |
790 case media::PIPELINE_ERROR_OPERATION_PENDING: | |
791 case media::PIPELINE_ERROR_INVALID_STATE: | |
792 // Decode error. | |
793 SetNetworkState(WebMediaPlayer::DecodeError); | |
794 break; | |
795 } | |
796 | |
797 // Repaint to trigger UI update. | |
798 Repaint(); | |
799 } | |
800 | |
801 void WebMediaPlayerImpl::OnNetworkEvent(bool is_downloading_data) { | |
802 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
803 if (is_downloading_data) | |
804 SetNetworkState(WebKit::WebMediaPlayer::Loading); | |
805 else | |
806 SetNetworkState(WebKit::WebMediaPlayer::Idle); | |
807 } | |
808 | |
809 void WebMediaPlayerImpl::OnDemuxerOpened() { | |
810 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
811 | |
812 GetClient()->sourceOpened(); | |
813 } | |
814 | |
815 void WebMediaPlayerImpl::SetNetworkState( | |
816 WebKit::WebMediaPlayer::NetworkState state) { | |
817 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
818 // Always notify to ensure client has the latest value. | |
819 network_state_ = state; | |
820 GetClient()->networkStateChanged(); | |
821 } | |
822 | |
823 void WebMediaPlayerImpl::SetReadyState( | |
824 WebKit::WebMediaPlayer::ReadyState state) { | |
825 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
826 // Always notify to ensure client has the latest value. | |
827 ready_state_ = state; | |
828 GetClient()->readyStateChanged(); | |
829 } | |
830 | |
831 void WebMediaPlayerImpl::Destroy() { | |
832 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
833 | |
834 // Tell the data source to abort any pending reads so that the pipeline is | |
835 // not blocked when issuing stop commands to the other filters. | |
836 if (proxy_) { | |
837 proxy_->AbortDataSources(); | |
838 proxy_->DemuxerShutdown(); | |
839 } | |
840 | |
841 // Make sure to kill the pipeline so there's no more media threads running. | |
842 // Note: stopping the pipeline might block for a long time. | |
843 if (pipeline_) { | |
844 media::PipelineStatusNotification note; | |
845 pipeline_->Stop(note.Callback()); | |
846 note.Wait(); | |
847 | |
848 // Let V8 know we are not using extra resources anymore. | |
849 if (incremented_externally_allocated_memory_) { | |
850 v8::V8::AdjustAmountOfExternalAllocatedMemory(-kPlayerExtraMemory); | |
851 incremented_externally_allocated_memory_ = false; | |
852 } | |
853 } | |
854 | |
855 message_loop_factory_.reset(); | |
856 | |
857 // And then detach the proxy, it may live on the render thread for a little | |
858 // longer until all the tasks are finished. | |
859 if (proxy_) { | |
860 proxy_->Detach(); | |
861 proxy_ = NULL; | |
862 } | |
863 } | |
864 | |
865 WebKit::WebMediaPlayerClient* WebMediaPlayerImpl::GetClient() { | |
866 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
867 DCHECK(client_); | |
868 return client_; | |
869 } | |
870 | |
871 void WebMediaPlayerImpl::IncrementExternallyAllocatedMemory() { | |
872 DCHECK_EQ(main_loop_, MessageLoop::current()); | |
873 incremented_externally_allocated_memory_ = true; | |
874 v8::V8::AdjustAmountOfExternalAllocatedMemory(kPlayerExtraMemory); | |
875 } | |
876 | |
877 } // namespace webkit_glue | |
OLD | NEW |