1 // Copyright 2014 Samsung Electronics Inc. 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.
5 #include "media/base/tizen/webmediaplayer_tizen.h"
9 #include "cc/blink/web_layer_impl.h"
10 #include "cc/layers/video_layer.h"
11 #include "content/renderer/media/render_media_log.h"
12 #include "content/renderer/media/renderer_gpu_video_accelerator_factories.h"
13 #include "content/renderer/render_frame_impl.h"
14 #include "content/renderer/render_thread_impl.h"
15 #include "media/base/tizen/media_player_tizen.h"
16 #include "media/base/bind_to_current_loop.h"
17 #include "media/base/video_frame.h"
18 #include "media/blink/webmediaplayer_util.h"
19 #include "third_party/libyuv/include/libyuv/planar_functions.h"
20 #include "third_party/WebKit/public/platform/WebMediaPlayerClient.h"
21 #include "wrt/wrt_url_parse.h"
23 #define BIND_TO_RENDER_LOOP(function) \
24 (DCHECK(main_loop_->BelongsToCurrentThread()), \
25 media::BindToCurrentLoop(base::Bind(function, AsWeakPtr())))
29 // fourcc for gst-video-format
30 const uint32 GST_VIDEO_SN12 = GST_MAKE_FOURCC('S','N','1','2');
31 const uint32 GST_VIDEO_I420 = GST_MAKE_FOURCC('I','4','2','0');
32 const uint32 GST_VIDEO_NV12 = GST_MAKE_FOURCC('N','V','1','2');
35 const uint SN12_TILE_WIDTH = 64;
36 const uint SN12_TILE_HEIGHT = 32;
37 const uint SN12_TILE_SIZE = SN12_TILE_WIDTH * SN12_TILE_HEIGHT;
39 // Removes query string from URI
40 GURL GetCleanURL(std::string url) {
41 // FIXME: Need to consider "app://" scheme.
42 if (!url.compare(0, 7, "file://")) {
43 int position = url.find("?");
45 url.erase(url.begin() + position, url.end());
51 WebMediaPlayerTizen::WebMediaPlayerTizen(
52 content::RendererMediaPlayerManagerTizen* manager,
53 blink::WebLocalFrame* frame,
54 blink::WebMediaPlayerClient* client,
55 base::WeakPtr<media::WebMediaPlayerDelegate> delegate,
56 const WebMediaPlayerParams& params,
57 content::WrtUrlParseBase* wrt_url_parse)
59 network_state_(blink::WebMediaPlayer::NetworkStateEmpty),
60 ready_state_(blink::WebMediaPlayer::ReadyStateHaveNothing),
61 main_loop_(base::MessageLoopProxy::current()),
63 content::RenderThreadImpl::current()->GetMediaThreadTaskRunner()),
66 media_log_(new content::RenderMediaLog()),
68 compositor_task_runner_(
69 content::RenderThreadImpl::current()->compositor_message_loop_proxy()),
70 compositor_(new media::VideoFrameCompositor(
71 BIND_TO_RENDER_LOOP(&WebMediaPlayerTizen::OnNaturalSizeChanged),
72 BIND_TO_RENDER_LOOP(&WebMediaPlayerTizen::OnOpacityChanged))),
83 pending_seek_time_(0),
86 did_loading_progress_(false),
88 gpu_factories_(content::RenderThreadImpl::current()->GetGpuFactories()),
89 encrypted_media_support_(
90 params.CreateEncryptedMediaPlayerSupport(client)),
91 wrt_url_parse_(wrt_url_parse) {
93 DCHECK(encrypted_media_support_);
94 VLOG(1) << "WebMediaPlayerTizen::" << __FUNCTION__;
95 FrameAvailable_ = false;
96 // We want to be notified of |main_loop_| destruction.
97 base::MessageLoop::current()->AddDestructionObserver(this);
99 player_id_ = manager_->RegisterMediaPlayer(this);
101 // Threaded compositing isn't enabled universally yet.
102 if (!compositor_task_runner_.get())
103 compositor_task_runner_ = base::MessageLoopProxy::current();
105 media_log_->AddEvent(
106 media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_CREATED));
109 WebMediaPlayerTizen::~WebMediaPlayerTizen() {
110 VLOG(1) << "WebMediaPlayerTizen::" << __FUNCTION__;
112 manager_->DestroyPlayer(player_id_);
113 manager_->UnregisterMediaPlayer(player_id_);
116 SetVideoFrameProviderClient(NULL);
117 client_->setWebLayer(NULL);
119 delegate_->PlayerGone(this);
120 if (base::MessageLoop::current())
121 base::MessageLoop::current()->RemoveDestructionObserver(this);
122 compositor_task_runner_->DeleteSoon(FROM_HERE, compositor_);
123 if (media_source_delegate_) {
124 // Part of |media_source_delegate_| needs to be stopped
125 // on the media thread.
126 // Wait until |media_source_delegate_| is fully stopped
127 // before tearing down other objects.
128 base::WaitableEvent waiter(false, false);
129 media_source_delegate_->Stop(
130 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter)));
135 void WebMediaPlayerTizen::load(LoadType load_type,
136 const blink::WebURL& url,
137 CORSMode cors_mode) {
138 VLOG(1) << "WebMediaPlayerTizen::" << __FUNCTION__ << " load type - "
140 int demuxer_client_id = 0;
141 if (load_type == LoadTypeMediaSource) {
142 // FIXME: EFL GST-package on desktop cannot handle AAC decoding.
143 // Disabling MSE for desktop.
145 player_type_ = MEDIA_PLAYER_TYPE_MEDIA_SOURCE;
146 content::RendererDemuxerTizen* demuxer =
147 content::RenderThreadImpl::current()->renderer_demuxer();
148 demuxer_client_id = demuxer->GetNextDemuxerClientID();
149 media_source_delegate_.reset(new content::MediaSourceDelegateTizen(
150 demuxer, demuxer_client_id, media_task_runner_, media_log_.get()));
151 SetDecryptorReadyCB set_decryptor_ready_cb =
152 encrypted_media_support_->CreateSetDecryptorReadyCB();
153 Demuxer::NeedKeyCB need_key_cb =
154 encrypted_media_support_->CreateNeedKeyCB();
155 media_source_delegate_->InitializeMediaSource(
156 base::Bind(&WebMediaPlayerTizen::OnMediaSourceOpened,
157 weak_factory_.GetWeakPtr()),
159 set_decryptor_ready_cb,
160 base::Bind(&WebMediaPlayerTizen::SetNetworkState,
161 weak_factory_.GetWeakPtr()),
162 base::Bind(&WebMediaPlayerTizen::OnDurationChange,
163 weak_factory_.GetWeakPtr()));
165 // Posting Error Message to HTMLMediaElement.
166 SetNetworkState(WebMediaPlayer::NetworkStateDecodeError);
168 } else if (load_type == LoadTypeURL) {
169 player_type_ = MEDIA_PLAYER_TYPE_URL;
171 LOG(ERROR) << "Unsupported load type " << load_type;
175 blink::WebURL real_url;
177 real_url = wrt_url_parse_->parseUrl(url);
181 // FIXME: Check URL, Volume for MS.
182 manager_->Initialize(player_id_,
184 GetCleanURL(real_url.string().utf8()),
189 blink::WebMediaPlayer::MediaKeyException
190 WebMediaPlayerTizen::generateKeyRequest(const blink::WebString& key_system,
191 const unsigned char* init_data,
192 unsigned init_data_length) {
193 DCHECK(main_loop_->BelongsToCurrentThread());
195 return encrypted_media_support_->GenerateKeyRequest(
196 frame_, key_system, init_data, init_data_length);
199 blink::WebMediaPlayer::MediaKeyException WebMediaPlayerTizen::addKey(
200 const blink::WebString& key_system,
201 const unsigned char* key,
203 const unsigned char* init_data,
204 unsigned init_data_length,
205 const blink::WebString& session_id) {
206 DCHECK(main_loop_->BelongsToCurrentThread());
208 return encrypted_media_support_->AddKey(
209 key_system, key, key_length, init_data, init_data_length, session_id);
212 blink::WebMediaPlayer::MediaKeyException WebMediaPlayerTizen::cancelKeyRequest(
213 const blink::WebString& key_system,
214 const blink::WebString& session_id) {
215 DCHECK(main_loop_->BelongsToCurrentThread());
217 return encrypted_media_support_->CancelKeyRequest(key_system, session_id);
220 void WebMediaPlayerTizen::setContentDecryptionModule(
221 blink::WebContentDecryptionModule* cdm) {
222 DCHECK(main_loop_->BelongsToCurrentThread());
224 encrypted_media_support_->SetContentDecryptionModule(cdm);
227 void WebMediaPlayerTizen::setContentDecryptionModule(
228 blink::WebContentDecryptionModule* cdm,
229 blink::WebContentDecryptionModuleResult result) {
230 DCHECK(main_loop_->BelongsToCurrentThread());
232 encrypted_media_support_->SetContentDecryptionModule(cdm, result);
235 void WebMediaPlayerTizen::OnMediaSourceOpened(
236 blink::WebMediaSource* web_media_source) {
237 client_->mediaSourceOpened(web_media_source);
240 void WebMediaPlayerTizen::play() {
241 manager_->Play(player_id_);
242 // Has to be updated from |MediaPlayerBridgeGstreamer| but IPC causes delay.
243 // There are cases were play - pause are fired successively and would fail.
247 void WebMediaPlayerTizen::pause() {
248 manager_->Pause(player_id_, true);
249 // Has to be updated from |MediaPlayerBridgeGstreamer| but IPC causes delay.
250 // There are cases were play - pause are fired successively and would fail.
254 void WebMediaPlayerTizen::RequestPause() {
256 client_->playbackStateChanged();
259 bool WebMediaPlayerTizen::supportsSave() const {
263 void WebMediaPlayerTizen::seek(double seconds) {
264 VLOG(1) << "WebMediaPlayerTizen::" << __FUNCTION__ << " : " << seconds
265 << " ID " << player_id_;
266 DCHECK(main_loop_->BelongsToCurrentThread());
269 if (seconds == seek_time_) {
270 if (media_source_delegate_) {
271 if (!pending_seek_) {
272 // If using media source demuxer, only suppress redundant seeks if
273 // there is no pending seek. This enforces that any pending seek that
274 // results in a demuxer seek is preceded by matching
275 // CancelPendingSeek() and StartSeek() calls.
279 // Suppress all redundant seeks if unrestricted by media source
281 pending_seek_ = false;
286 pending_seek_ = true;
287 pending_seek_time_ = seconds;
288 if (media_source_delegate_)
289 media_source_delegate_->CancelPendingSeek(
290 media::ConvertSecondsToTimestamp(pending_seek_time_));
291 // Later, OnSeekComplete will trigger the pending seek.
296 seek_time_ = seconds;
298 // Once Chunk demuxer seeks GST seek will be intiated.
299 if (media_source_delegate_)
300 media_source_delegate_->StartWaitingForSeek(
301 media::ConvertSecondsToTimestamp(seek_time_));
302 manager_->Seek(player_id_, seek_time_);
304 // Draw empty frame during seek.
306 gfx::Size size(gst_width_, gst_height_);
307 scoped_refptr<VideoFrame> video_frame = VideoFrame::CreateBlackFrame(size);
308 FrameReady(video_frame);
312 void WebMediaPlayerTizen::setRate(double rate) {
313 manager_->SetRate(player_id_, rate);
316 void WebMediaPlayerTizen::setVolume(double volume) {
318 manager_->SetVolume(player_id_, volume);
321 blink::WebTimeRanges WebMediaPlayerTizen::buffered() const{
325 blink::WebTimeRanges WebMediaPlayerTizen::seekable() const {
326 const double seekable_end = duration();
327 if (std::isinf(seekable_end))
328 return blink::WebTimeRanges();
330 blink::WebTimeRange seekable_range(0.0, seekable_end);
331 return blink::WebTimeRanges(&seekable_range, 1);
334 void WebMediaPlayerTizen::paint(blink::WebCanvas* canvas,
335 const blink::WebRect& rect,
337 SkXfermode::Mode mode) {
338 scoped_refptr<media::VideoFrame> video_frame =
339 GetCurrentFrameFromCompositor();
341 gfx::Rect gfx_rect(rect);
342 skcanvas_video_renderer_.Paint(
343 video_frame.get(), canvas, gfx_rect, alpha,
344 SkXfermode::kSrcOver_Mode, media::VIDEO_ROTATION_0);
347 bool WebMediaPlayerTizen::hasVideo() const {
351 bool WebMediaPlayerTizen::hasAudio() const {
355 blink::WebSize WebMediaPlayerTizen::naturalSize() const {
356 return blink::WebSize(natural_size_);
359 bool WebMediaPlayerTizen::paused() const {
363 bool WebMediaPlayerTizen::seeking() const {
367 double WebMediaPlayerTizen::duration() const {
371 double WebMediaPlayerTizen::currentTime() const {
373 return pending_seek_ ? pending_seek_time_ : seek_time_;
374 return current_time_;
377 blink::WebMediaPlayer::NetworkState WebMediaPlayerTizen::networkState() const {
378 return network_state_;
381 blink::WebMediaPlayer::ReadyState WebMediaPlayerTizen::readyState() const {
385 bool WebMediaPlayerTizen::didLoadingProgress() {
386 if (did_loading_progress_) {
387 did_loading_progress_ = false;
393 bool WebMediaPlayerTizen::hasSingleSecurityOrigin() const {
397 bool WebMediaPlayerTizen::didPassCORSAccessCheck() const {
401 double WebMediaPlayerTizen::mediaTimeForTimeValue(double timeValue) const {
402 return media::ConvertSecondsToTimestamp(timeValue).InSecondsF();
405 void WebMediaPlayerTizen::SetVideoFrameProviderClient(
406 cc::VideoFrameProvider::Client* client) {
407 // This is called from both the main renderer thread and the compositor
408 // thread (when the main thread is blocked).
409 compositor_->SetVideoFrameProviderClient(client);
412 scoped_refptr<media::VideoFrame>WebMediaPlayerTizen::GetCurrentFrame() {
413 scoped_refptr<media::VideoFrame> current_frame =
414 GetCurrentFrameFromCompositor();
415 return current_frame;
418 void WebMediaPlayerTizen::SetReadyState(WebMediaPlayer::ReadyState state) {
419 ready_state_ = state;
420 client_->readyStateChanged();
423 void WebMediaPlayerTizen::SetNetworkState(WebMediaPlayer::NetworkState state) {
424 network_state_ = state;
425 client_->networkStateChanged();
428 void WebMediaPlayerTizen::OnNewFrameAvailable(base::SharedMemoryHandle Handle,
430 base::TimeDelta timestamp) {
431 base::SharedMemory shared_memory(Handle, false);
432 shared_memory.Map(yuv_size);
433 uint8* const yuv_buffer = static_cast<uint8*>(shared_memory.memory());
435 gfx::Size size(gst_width_, gst_height_);
436 scoped_refptr<VideoFrame> video_frame =
437 VideoFrame::CreateFrame(
438 VideoFrame::YV12, size, gfx::Rect(size), size, timestamp);
440 // decoded format is SN12 on Tizen device
441 // video format converted from SN12 to YV12
442 uint8* gst_buf = yuv_buffer;
443 switch(gst_video_format_) {
444 case GST_VIDEO_I420: {
445 const uint c_frm_size = yuv_size / 6;
446 const uint y_frm_size = c_frm_size << 2; // * 4;
448 uint8* gst_buf_u = gst_buf + y_frm_size;
450 uint8* gst_buf_v = gst_buf_u + c_frm_size;
453 // Get the videoframe stride size.
454 // Calculate the gstreamer buffer stride size.
455 const uint uv_rows = video_frame.get()->rows(VideoFrame::kUPlane);
456 const uint gst_stride = c_frm_size / uv_rows;
458 libyuv::I420Copy(gst_buf, 2 * gst_stride,
459 gst_buf_u, gst_stride,
460 gst_buf_v, gst_stride,
461 video_frame.get()->data(VideoFrame::kYPlane),
462 video_frame.get()->stride(VideoFrame::kYPlane),
463 video_frame.get()->data(VideoFrame::kUPlane),
464 video_frame.get()->stride(VideoFrame::kUPlane),
465 video_frame.get()->data(VideoFrame::kVPlane),
466 video_frame.get()->stride(VideoFrame::kVPlane),
467 gst_width_, gst_height_);
470 case GST_VIDEO_SN12: {
471 //const uint tile_w = (gst_width_ - 1) / SN12_TILE_WIDTH + 1;
472 //const uint tile_w_align = (tile_w + 1) & ~1;
473 //const uint tile_h_luma = (gst_height_ - 1) / SN12_TILE_HEIGHT + 1;
474 const uint tile_w_align = ((gst_width_ - 1) / SN12_TILE_WIDTH + 2) & ~1;
475 const uint tile_luma_stride = tile_w_align * SN12_TILE_WIDTH;
476 uint luma_size = SN12_TILE_SIZE * tile_w_align
477 * ((gst_height_-1)/SN12_TILE_HEIGHT + 1);
479 uint8* y_frm = video_frame.get()->data(VideoFrame::kYPlane);
480 const uint y_stride = video_frame.get()->stride(VideoFrame::kYPlane);
482 // actually copy and convert luma buffer
483 for(int i=0; i<gst_height_; i++) {
484 memcpy(y_frm, gst_buf, gst_width_);
486 gst_buf += tile_luma_stride;
489 gst_buf = yuv_buffer + luma_size;
490 uint8* gst_buf2 = gst_buf + 1;
491 uint8* u_frm = video_frame.get()->data(VideoFrame::kUPlane);
492 uint8* v_frm = video_frame.get()->data(VideoFrame::kVPlane);
493 const uint uv_stride = video_frame.get()->stride(VideoFrame::kUPlane);
494 const uint uv_rows = video_frame.get()->rows(VideoFrame::kUPlane);
496 // actually copy and convert chroma buffer
497 for(uint row=0; row<uv_rows; ++row) {
498 for(uint i=0; i<uv_stride; i++) {
499 u_frm[i] = gst_buf[i*2];
500 v_frm[i] = gst_buf2[i*2];
503 gst_buf += tile_luma_stride;
504 gst_buf2 += tile_luma_stride;
511 case GST_VIDEO_NV12: {
512 const uint c_frm_size = yuv_size / 6;
513 const uint y_frm_size = c_frm_size << 2; // * 4;
516 memcpy(video_frame.get()->data(VideoFrame::kYPlane),
517 gst_buf, y_frm_size);
519 gst_buf += y_frm_size;
520 uint8* gst_buf2 = gst_buf + 1;
521 uint8* u_plane = video_frame.get()->data(VideoFrame::kUPlane);
522 uint8* v_plane = video_frame.get()->data(VideoFrame::kVPlane);
524 for(uint i = 0; i < c_frm_size; i++){
526 u_plane[i] = gst_buf[i * 2];
528 v_plane[i] = gst_buf2[i * 2];
533 LOG(ERROR) << "WebMediaPlayerTizen::" << __FUNCTION__
534 << " not supported video format";
539 shared_memory.Close();
540 FrameReady(video_frame);
543 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
544 // FIXME: Graphics team need to merge painting of Video-Frame on to 2d-canvas
546 void WebMediaPlayerTizen::OnPlatformSurfaceUpdated(
548 base::TimeDelta timestamp) {
549 gfx::Size size(gst_width_, gst_height_);
550 scoped_refptr<VideoFrame> video_frame = VideoFrame::WrapNativePixmap(
551 VideoFrame::NATIVE_PIXMAP, size, gfx::Rect(size), size,
552 timestamp, pixmap_id);
553 FrameReady(video_frame);
557 void WebMediaPlayerTizen::FrameReady(
558 const scoped_refptr<media::VideoFrame>& frame) {
559 compositor_task_runner_->PostTask(
561 base::Bind(&media::VideoFrameCompositor::UpdateCurrentFrame,
562 base::Unretained(compositor_),
565 void WebMediaPlayerTizen::OnMediaDataChange(int format, int height, int width, int media) {
566 gst_video_format_ = static_cast<uint32>(format);
567 gst_height_ = height;
569 audio_ = media & media::MEDIA_AUDIO_MASK ? true : false;
570 video_ = media & media::MEDIA_VIDEO_MASK ? true : false;
571 natural_size_ = gfx::Size(width, height);
572 SetReadyState(WebMediaPlayer::ReadyStateHaveMetadata);
573 if (hasVideo() && !video_weblayer_.get()) {
574 scoped_refptr<cc::VideoLayer> layer =
575 cc::VideoLayer::Create(compositor_, media::VIDEO_ROTATION_0);
576 video_weblayer_.reset(new cc_blink::WebLayerImpl(layer));
577 video_weblayer_->setOpaque(opaque_);
578 client_->setWebLayer(video_weblayer_.get());
582 void WebMediaPlayerTizen::OnTimeChanged() {
585 // Handling pending seek for ME. For MSE |CancelPendingSeek|
586 // will handle the pending seeks.
587 if (!media_source_delegate_ && pending_seek_) {
588 pending_seek_ = false;
589 seek(pending_seek_time_);
592 client_->timeChanged();
595 void WebMediaPlayerTizen::OnDurationChange(double duration) {
596 duration_ = duration;
597 client_->durationChanged();
600 void WebMediaPlayerTizen::OnNaturalSizeChanged(gfx::Size size) {
601 DCHECK(main_loop_->BelongsToCurrentThread());
602 DCHECK_NE(ready_state_, WebMediaPlayer::ReadyStateHaveNothing);
603 TRACE_EVENT0("media", "WebMediaPlayerTizen::OnNaturalSizeChanged");
604 media_log_->AddEvent(
605 media_log_->CreateVideoSizeSetEvent(size.width(), size.height()));
606 natural_size_ = size;
608 client_->sizeChanged();
611 void WebMediaPlayerTizen::OnOpacityChanged(bool opaque) {
612 DCHECK(main_loop_->BelongsToCurrentThread());
613 DCHECK_NE(ready_state_, WebMediaPlayer::ReadyStateHaveNothing);
617 video_weblayer_->setOpaque(opaque_);
620 static void GetCurrentFrameAndSignal(
621 media::VideoFrameCompositor* compositor,
622 scoped_refptr<media::VideoFrame>* video_frame_out,
623 base::WaitableEvent* event) {
624 TRACE_EVENT0("media", "GetCurrentFrameAndSignal");
625 *video_frame_out = compositor->GetCurrentFrame();
629 scoped_refptr<media::VideoFrame>
630 WebMediaPlayerTizen::GetCurrentFrameFromCompositor() {
631 TRACE_EVENT0("media", "WebMediaPlayerImpl::GetCurrentFrameFromCompositor");
633 if (compositor_task_runner_->BelongsToCurrentThread())
634 return compositor_->GetCurrentFrame();
636 // Use a posted task and waitable event instead of a lock otherwise
637 // WebGL/Canvas can see different content than what the compositor is seeing.
638 scoped_refptr<media::VideoFrame> video_frame;
639 base::WaitableEvent event(false, false);
640 compositor_task_runner_->PostTask(FROM_HERE,
641 base::Bind(&GetCurrentFrameAndSignal,
642 base::Unretained(compositor_),
649 void WebMediaPlayerTizen::OnTimeUpdate(double current_time) {
650 current_time_ = current_time;
653 void WebMediaPlayerTizen::OnBufferUpdate(
654 std::vector<media::MediaPlayerTizen::TimeRanges> buffer_range) {
655 media::Ranges<base::TimeDelta> time_ranges;
656 std::vector<media::MediaPlayerTizen::TimeRanges>::iterator tr_it;
657 for ( tr_it = buffer_range.begin(); tr_it != buffer_range.end(); ++tr_it) {
659 base::TimeDelta::FromMicroseconds((*tr_it).start),
660 base::TimeDelta::FromMicroseconds((*tr_it).end));
662 blink::WebTimeRanges web_ranges(ConvertToWebTimeRanges(time_ranges));
663 buffered_.swap(web_ranges);
664 did_loading_progress_ = true;
667 void WebMediaPlayerTizen::OnPauseStateChange(bool state) {
668 VLOG(1) << "WebMediaPlayerTizen::" << __FUNCTION__ << " state:" << state;
670 if (delegate_.get()) {
672 delegate_->DidPause(this);
674 delegate_->DidPlay(this);
678 void WebMediaPlayerTizen::OnSeekStateChange(bool state) {
679 VLOG(1) << "WebMediaPlayerTizen::" << __FUNCTION__ << " state:" << state
680 << " ID " << player_id_;
682 // Draw empty frame during seek.
683 if (video_ && is_seeking_) {
684 gfx::Size size(gst_width_, gst_height_);
685 scoped_refptr<VideoFrame> video_frame = VideoFrame::CreateBlackFrame(size);
686 FrameReady(video_frame);
690 void WebMediaPlayerTizen::OnRequestSeek(double seek_time) {
691 client_->requestSeek(seek_time);