| 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 |