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.
5 #include "content/renderer/media/webmediaplayer_ms.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/metrics/histogram.h"
14 #include "cc/blink/context_provider_web_context.h"
15 #include "cc/blink/web_layer_impl.h"
16 #include "cc/layers/video_frame_provider_client_impl.h"
17 #include "cc/layers/video_layer.h"
18 #include "content/public/renderer/media_stream_audio_renderer.h"
19 #include "content/public/renderer/media_stream_renderer_factory.h"
20 #include "content/public/renderer/render_view.h"
21 #include "content/public/renderer/video_frame_provider.h"
22 #include "content/renderer/render_frame_impl.h"
23 #include "content/renderer/render_thread_impl.h"
24 #include "gpu/blink/webgraphicscontext3d_impl.h"
25 #include "media/base/media_log.h"
26 #include "media/base/video_frame.h"
27 #include "media/base/video_rotation.h"
28 #include "media/base/video_util.h"
29 #include "media/blink/webmediaplayer_delegate.h"
30 #include "third_party/WebKit/public/platform/WebMediaPlayerClient.h"
31 #include "third_party/WebKit/public/platform/WebRect.h"
32 #include "third_party/WebKit/public/platform/WebSize.h"
33 #include "third_party/WebKit/public/platform/WebURL.h"
34 #include "third_party/WebKit/public/web/WebFrame.h"
35 #include "third_party/WebKit/public/web/WebView.h"
36 #include "third_party/skia/include/core/SkBitmap.h"
38 using blink::WebCanvas
;
39 using blink::WebMediaPlayer
;
47 // This function copies |frame| to a new YV12 media::VideoFrame.
48 scoped_refptr
<media::VideoFrame
> CopyFrameToYV12(
49 const scoped_refptr
<media::VideoFrame
>& frame
,
50 media::SkCanvasVideoRenderer
* video_renderer
) {
51 const scoped_refptr
<media::VideoFrame
> new_frame
=
52 media::VideoFrame::CreateFrame(media::PIXEL_FORMAT_YV12
,
53 frame
->coded_size(), frame
->visible_rect(),
54 frame
->natural_size(), frame
->timestamp());
56 if (frame
->HasTextures()) {
57 DCHECK(frame
->format() == media::PIXEL_FORMAT_ARGB
||
58 frame
->format() == media::PIXEL_FORMAT_XRGB
);
60 bitmap
.allocN32Pixels(frame
->visible_rect().width(),
61 frame
->visible_rect().height());
62 SkCanvas
canvas(bitmap
);
64 cc::ContextProvider
* const provider
=
65 RenderThreadImpl::current()->SharedMainThreadContextProvider().get();
67 const media::Context3D context_3d
=
68 media::Context3D(provider
->ContextGL(), provider
->GrContext());
69 DCHECK(context_3d
.gl
);
70 video_renderer
->Copy(frame
.get(), &canvas
, context_3d
);
72 // GPU Process crashed.
73 bitmap
.eraseColor(SK_ColorTRANSPARENT
);
75 media::CopyRGBToVideoFrame(reinterpret_cast<uint8
*>(bitmap
.getPixels()),
77 frame
->visible_rect(),
80 DCHECK(frame
->IsMappable());
81 DCHECK(frame
->format() == media::PIXEL_FORMAT_YV12
||
82 frame
->format() == media::PIXEL_FORMAT_I420
);
83 for (size_t i
= 0; i
< media::VideoFrame::NumPlanes(frame
->format()); ++i
) {
84 media::CopyPlane(i
, frame
->data(i
), frame
->stride(i
),
85 frame
->rows(i
), new_frame
.get());
91 } // anonymous namespace
93 WebMediaPlayerMS::WebMediaPlayerMS(
94 blink::WebFrame
* frame
,
95 blink::WebMediaPlayerClient
* client
,
96 base::WeakPtr
<media::WebMediaPlayerDelegate
> delegate
,
97 media::MediaLog
* media_log
,
98 scoped_ptr
<MediaStreamRendererFactory
> factory
,
99 const scoped_refptr
<base::SingleThreadTaskRunner
>& compositor_task_runner
)
101 network_state_(WebMediaPlayer::NetworkStateEmpty
),
102 ready_state_(WebMediaPlayer::ReadyStateHaveNothing
),
103 buffered_(static_cast<size_t>(0)),
108 received_first_frame_(false),
109 media_log_(media_log
),
110 renderer_factory_(factory
.Pass()),
111 compositor_(new Compositor(compositor_task_runner
)),
112 compositor_task_runner_(compositor_task_runner
) {
113 DVLOG(1) << "WebMediaPlayerMS::ctor";
114 media_log_
->AddEvent(
115 media_log_
->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_CREATED
));
118 WebMediaPlayerMS::~WebMediaPlayerMS() {
119 DVLOG(1) << "WebMediaPlayerMS::dtor";
120 DCHECK(thread_checker_
.CalledOnValidThread());
122 compositor_task_runner_
->DeleteSoon(FROM_HERE
, compositor_
.release());
124 GetClient()->setWebLayer(NULL
);
126 if (video_frame_provider_
.get())
127 video_frame_provider_
->Stop();
129 if (audio_renderer_
.get())
130 audio_renderer_
->Stop();
132 media_log_
->AddEvent(
133 media_log_
->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_DESTROYED
));
136 delegate_
->PlayerGone(this);
139 void WebMediaPlayerMS::load(LoadType load_type
,
140 const blink::WebURL
& url
,
141 CORSMode cors_mode
) {
142 DVLOG(1) << "WebMediaPlayerMS::load";
143 DCHECK(thread_checker_
.CalledOnValidThread());
145 // TODO(acolwell): Change this to DCHECK_EQ(load_type,
146 // LoadTypeMediaStream) once Blink-side changes land.
147 DCHECK_NE(load_type
, LoadTypeMediaSource
);
151 SetNetworkState(WebMediaPlayer::NetworkStateLoading
);
152 SetReadyState(WebMediaPlayer::ReadyStateHaveNothing
);
153 media_log_
->AddEvent(media_log_
->CreateLoadEvent(url
.spec()));
155 video_frame_provider_
= renderer_factory_
->GetVideoFrameProvider(
157 base::Bind(&WebMediaPlayerMS::OnSourceError
, AsWeakPtr()),
158 base::Bind(&WebMediaPlayerMS::OnFrameAvailable
, AsWeakPtr()));
160 RenderFrame
* const frame
= RenderFrame::FromWebFrame(frame_
);
161 audio_renderer_
= renderer_factory_
->GetAudioRenderer(
163 frame
->GetRoutingID());
165 if (!video_frame_provider_
&& !audio_renderer_
) {
166 SetNetworkState(WebMediaPlayer::NetworkStateNetworkError
);
170 if (audio_renderer_
) {
171 audio_renderer_
->SetVolume(volume_
);
172 audio_renderer_
->Start();
174 if (video_frame_provider_
)
175 video_frame_provider_
->Start();
176 if (audio_renderer_
&& !video_frame_provider_
) {
177 // This is audio-only mode.
178 SetReadyState(WebMediaPlayer::ReadyStateHaveMetadata
);
179 SetReadyState(WebMediaPlayer::ReadyStateHaveEnoughData
);
183 void WebMediaPlayerMS::play() {
184 DVLOG(1) << "WebMediaPlayerMS::play";
185 DCHECK(thread_checker_
.CalledOnValidThread());
188 if (video_frame_provider_
.get())
189 video_frame_provider_
->Play();
191 compositor_task_runner_
->PostTask(
192 FROM_HERE
, base::Bind(&WebMediaPlayerMS::Compositor::StartRendering
,
193 base::Unretained(compositor_
.get())));
195 if (audio_renderer_
.get())
196 audio_renderer_
->Play();
199 delegate_
->DidPlay(this);
204 media_log_
->AddEvent(media_log_
->CreateEvent(media::MediaLogEvent::PLAY
));
207 void WebMediaPlayerMS::pause() {
208 DVLOG(1) << "WebMediaPlayerMS::pause";
209 DCHECK(thread_checker_
.CalledOnValidThread());
211 if (video_frame_provider_
.get())
212 video_frame_provider_
->Pause();
214 compositor_task_runner_
->PostTask(
215 FROM_HERE
, base::Bind(&WebMediaPlayerMS::Compositor::StopRendering
,
216 base::Unretained(compositor_
.get())));
217 compositor_
->ReplaceCurrentFrameWithACopy(&video_renderer_
);
220 if (audio_renderer_
.get())
221 audio_renderer_
->Pause();
224 delegate_
->DidPause(this);
229 media_log_
->AddEvent(media_log_
->CreateEvent(media::MediaLogEvent::PAUSE
));
232 bool WebMediaPlayerMS::supportsSave() const {
233 DCHECK(thread_checker_
.CalledOnValidThread());
237 void WebMediaPlayerMS::seek(double seconds
) {
238 DCHECK(thread_checker_
.CalledOnValidThread());
241 void WebMediaPlayerMS::setRate(double rate
) {
242 DCHECK(thread_checker_
.CalledOnValidThread());
245 void WebMediaPlayerMS::setVolume(double volume
) {
246 DCHECK(thread_checker_
.CalledOnValidThread());
247 DVLOG(1) << "WebMediaPlayerMS::setVolume(volume=" << volume
<< ")";
249 if (audio_renderer_
.get())
250 audio_renderer_
->SetVolume(volume_
);
253 void WebMediaPlayerMS::setSinkId(const blink::WebString
& device_id
,
254 media::WebSetSinkIdCB
* web_callback
) {
255 DCHECK(thread_checker_
.CalledOnValidThread());
256 DVLOG(1) << __FUNCTION__
;
257 media::SwitchOutputDeviceCB callback
=
258 media::ConvertToSwitchOutputDeviceCB(web_callback
);
259 if (audio_renderer_
.get()) {
260 media::OutputDevice
* output_device
= audio_renderer_
->GetOutputDevice();
262 const std::string
device_id_str(device_id
.utf8());
263 const url::Origin
security_origin(
264 GURL(frame_
->securityOrigin().toString().utf8()));
265 output_device
->SwitchOutputDevice(device_id_str
, security_origin
,
270 callback
.Run(media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_INTERNAL
);
273 void WebMediaPlayerMS::setPreload(WebMediaPlayer::Preload preload
) {
274 DCHECK(thread_checker_
.CalledOnValidThread());
277 bool WebMediaPlayerMS::hasVideo() const {
278 DCHECK(thread_checker_
.CalledOnValidThread());
279 return (video_frame_provider_
.get() != NULL
);
282 bool WebMediaPlayerMS::hasAudio() const {
283 DCHECK(thread_checker_
.CalledOnValidThread());
284 return (audio_renderer_
.get() != NULL
);
287 blink::WebSize
WebMediaPlayerMS::naturalSize() const {
288 DCHECK(thread_checker_
.CalledOnValidThread());
290 gfx::Size size
= compositor_
->GetCurrentSize();
292 DVLOG(3) << "WebMediaPlayerMS::naturalSize, " << size
.ToString();
293 return blink::WebSize(size
);
296 bool WebMediaPlayerMS::paused() const {
297 DCHECK(thread_checker_
.CalledOnValidThread());
301 bool WebMediaPlayerMS::seeking() const {
302 DCHECK(thread_checker_
.CalledOnValidThread());
306 double WebMediaPlayerMS::duration() const {
307 DCHECK(thread_checker_
.CalledOnValidThread());
308 return std::numeric_limits
<double>::infinity();
311 double WebMediaPlayerMS::currentTime() const {
312 DCHECK(thread_checker_
.CalledOnValidThread());
313 base::TimeDelta current_time
= compositor_
->GetCurrentTime();
314 if (current_time
.ToInternalValue() != 0) {
315 return current_time
.InSecondsF();
316 } else if (audio_renderer_
.get()) {
317 return audio_renderer_
->GetCurrentRenderTime().InSecondsF();
322 WebMediaPlayer::NetworkState
WebMediaPlayerMS::networkState() const {
323 DCHECK(thread_checker_
.CalledOnValidThread());
324 DVLOG(1) << "WebMediaPlayerMS::networkState, state:" << network_state_
;
325 return network_state_
;
328 WebMediaPlayer::ReadyState
WebMediaPlayerMS::readyState() const {
329 DCHECK(thread_checker_
.CalledOnValidThread());
330 DVLOG(1) << "WebMediaPlayerMS::readyState, state:" << ready_state_
;
334 blink::WebTimeRanges
WebMediaPlayerMS::buffered() const {
335 DCHECK(thread_checker_
.CalledOnValidThread());
339 blink::WebTimeRanges
WebMediaPlayerMS::seekable() const {
340 DCHECK(thread_checker_
.CalledOnValidThread());
341 return blink::WebTimeRanges();
344 bool WebMediaPlayerMS::didLoadingProgress() {
345 DCHECK(thread_checker_
.CalledOnValidThread());
349 void WebMediaPlayerMS::paint(blink::WebCanvas
* canvas
,
350 const blink::WebRect
& rect
,
352 SkXfermode::Mode mode
) {
353 DVLOG(3) << "WebMediaPlayerMS::paint";
354 DCHECK(thread_checker_
.CalledOnValidThread());
356 scoped_refptr
<media::VideoFrame
> frame
= compositor_
->GetCurrentFrame();
358 media::Context3D context_3d
;
359 if (frame
.get() && frame
->HasTextures()) {
360 cc::ContextProvider
* provider
=
361 RenderThreadImpl::current()->SharedMainThreadContextProvider().get();
362 // GPU Process crashed.
365 context_3d
= media::Context3D(provider
->ContextGL(), provider
->GrContext());
366 DCHECK(context_3d
.gl
);
368 gfx::RectF
dest_rect(rect
.x
, rect
.y
, rect
.width
, rect
.height
);
369 video_renderer_
.Paint(frame
, canvas
, dest_rect
, alpha
, mode
,
370 media::VIDEO_ROTATION_0
, context_3d
);
373 bool WebMediaPlayerMS::hasSingleSecurityOrigin() const {
374 DCHECK(thread_checker_
.CalledOnValidThread());
378 bool WebMediaPlayerMS::didPassCORSAccessCheck() const {
379 DCHECK(thread_checker_
.CalledOnValidThread());
383 double WebMediaPlayerMS::mediaTimeForTimeValue(double timeValue
) const {
384 return base::TimeDelta::FromSecondsD(timeValue
).InSecondsF();
387 unsigned WebMediaPlayerMS::decodedFrameCount() const {
388 DCHECK(thread_checker_
.CalledOnValidThread());
389 unsigned total_frame_count
= compositor_
->GetTotalFrameCount();
390 DVLOG(1) << "WebMediaPlayerMS::decodedFrameCount, " << total_frame_count
;
391 return total_frame_count
;
394 unsigned WebMediaPlayerMS::droppedFrameCount() const {
395 DCHECK(thread_checker_
.CalledOnValidThread());
396 unsigned dropped_frame_count
= compositor_
->GetDroppedFrameCount();
397 DVLOG(1) << "WebMediaPlayerMS::droppedFrameCount, " << dropped_frame_count
;
398 return dropped_frame_count
;
401 unsigned WebMediaPlayerMS::audioDecodedByteCount() const {
402 DCHECK(thread_checker_
.CalledOnValidThread());
407 unsigned WebMediaPlayerMS::videoDecodedByteCount() const {
408 DCHECK(thread_checker_
.CalledOnValidThread());
413 bool WebMediaPlayerMS::copyVideoTextureToPlatformTexture(
414 blink::WebGraphicsContext3D
* web_graphics_context
,
415 unsigned int texture
,
416 unsigned int internal_format
,
418 bool premultiply_alpha
,
420 TRACE_EVENT0("media", "WebMediaPlayerMS:copyVideoTextureToPlatformTexture");
421 DCHECK(thread_checker_
.CalledOnValidThread());
423 scoped_refptr
<media::VideoFrame
> video_frame
= compositor_
->GetCurrentFrame();
425 if (!video_frame
.get() || video_frame
->HasTextures() ||
426 media::VideoFrame::NumPlanes(video_frame
->format()) != 1) {
430 // TODO(dshwang): need more elegant way to convert WebGraphicsContext3D to
432 gpu::gles2::GLES2Interface
* const gl
=
433 static_cast<gpu_blink::WebGraphicsContext3DImpl
*>(web_graphics_context
)
435 media::SkCanvasVideoRenderer::CopyVideoFrameSingleTextureToGLTexture(
436 gl
, video_frame
.get(), texture
, internal_format
, type
, premultiply_alpha
,
441 void WebMediaPlayerMS::OnFrameAvailable(
442 const scoped_refptr
<media::VideoFrame
>& frame
) {
443 DVLOG(3) << "WebMediaPlayerMS::OnFrameAvailable";
444 DCHECK(thread_checker_
.CalledOnValidThread());
446 base::TimeTicks render_time
;
447 if (!frame
->metadata()->GetTimeTicks(
448 media::VideoFrameMetadata::REFERENCE_TIME
, &render_time
)) {
449 render_time
= base::TimeTicks();
451 TRACE_EVENT1("webrtc", "WebMediaPlayerMS::OnFrameAvailable",
452 "Ideal Render Instant", render_time
.ToInternalValue());
454 if (!received_first_frame_
) {
455 received_first_frame_
= true;
456 SetReadyState(WebMediaPlayer::ReadyStateHaveMetadata
);
457 SetReadyState(WebMediaPlayer::ReadyStateHaveEnoughData
);
459 if (video_frame_provider_
.get()) {
460 video_weblayer_
.reset(new cc_blink::WebLayerImpl(
461 cc::VideoLayer::Create(cc_blink::WebLayerImpl::LayerSettings(),
462 compositor_
.get(), media::VIDEO_ROTATION_0
)));
463 video_weblayer_
->layer()->SetContentsOpaque(true);
464 video_weblayer_
->SetContentsOpaqueIsFixed(true);
465 GetClient()->setWebLayer(video_weblayer_
.get());
469 bool size_changed
= compositor_
->GetCurrentSize() != frame
->natural_size();
471 compositor_
->EnqueueFrame(frame
);
474 GetClient()->sizeChanged();
477 void WebMediaPlayerMS::RepaintInternal() {
478 DVLOG(1) << "WebMediaPlayerMS::RepaintInternal";
479 DCHECK(thread_checker_
.CalledOnValidThread());
480 GetClient()->repaint();
483 void WebMediaPlayerMS::OnSourceError() {
484 DVLOG(1) << "WebMediaPlayerMS::OnSourceError";
485 DCHECK(thread_checker_
.CalledOnValidThread());
486 SetNetworkState(WebMediaPlayer::NetworkStateFormatError
);
490 void WebMediaPlayerMS::SetNetworkState(WebMediaPlayer::NetworkState state
) {
491 DCHECK(thread_checker_
.CalledOnValidThread());
492 network_state_
= state
;
493 // Always notify to ensure client has the latest value.
494 GetClient()->networkStateChanged();
497 void WebMediaPlayerMS::SetReadyState(WebMediaPlayer::ReadyState state
) {
498 DCHECK(thread_checker_
.CalledOnValidThread());
499 ready_state_
= state
;
500 // Always notify to ensure client has the latest value.
501 GetClient()->readyStateChanged();
504 blink::WebMediaPlayerClient
* WebMediaPlayerMS::GetClient() {
505 DCHECK(thread_checker_
.CalledOnValidThread());
510 WebMediaPlayerMS::Compositor::Compositor(
511 const scoped_refptr
<base::SingleThreadTaskRunner
>& compositor_task_runner
)
512 : compositor_task_runner_(compositor_task_runner
),
513 video_frame_provider_client_(NULL
),
514 current_frame_used_(false),
515 last_deadline_max_(base::TimeTicks()),
516 total_frame_count_(0),
517 dropped_frame_count_(0),
520 WebMediaPlayerMS::Compositor::~Compositor() {
521 DCHECK(compositor_task_runner_
->BelongsToCurrentThread());
522 if (video_frame_provider_client_
)
523 video_frame_provider_client_
->StopUsingProvider();
526 void WebMediaPlayerMS::Compositor::EnqueueFrame(
527 scoped_refptr
<media::VideoFrame
> const& frame
) {
528 base::AutoLock
auto_lock(current_frame_lock_
);
529 ++total_frame_count_
;
531 if (base::TimeTicks::Now() > last_deadline_max_
) {
532 // TODO(qiangchen): This shows vsyncs stops rendering frames. A probable
533 // cause is that the tab is not in the front. But we still have to let
534 // old frames go. Call VRA::RemoveExpiredFrames.
538 if (!current_frame_used_
) {
539 ++dropped_frame_count_
;
542 // TODO(qiangchen): Instead of using one variable to hold one frame, use
543 // VideoRendererAlgorithm.
544 current_frame_
= frame
;
545 current_frame_used_
= false;
548 bool WebMediaPlayerMS::Compositor::UpdateCurrentFrame(
549 base::TimeTicks deadline_min
,
550 base::TimeTicks deadline_max
) {
551 DCHECK(compositor_task_runner_
->BelongsToCurrentThread());
552 base::AutoLock
auto_lock(current_frame_lock_
);
553 TRACE_EVENT_BEGIN2("webrtc", "WebMediaPlayerMS::UpdateCurrentFrame",
554 "Actual Render Begin", deadline_min
.ToInternalValue(),
555 "Actual Render End", deadline_max
.ToInternalValue());
556 last_deadline_max_
= deadline_max
;
558 // TODO(dalecurtis): This should make use of the deadline interval to ensure
559 // the painted frame is correct for the given interval.
565 base::TimeTicks render_time
;
566 if (!current_frame_
->metadata()->GetTimeTicks(
567 media::VideoFrameMetadata::REFERENCE_TIME
, &render_time
)) {
568 render_time
= base::TimeTicks();
570 TRACE_EVENT_END1("webrtc", "WebMediaPlayerMS::UpdateCurrentFrame",
571 "Ideal Render Instant", render_time
.ToInternalValue());
572 return !current_frame_used_
;
575 bool WebMediaPlayerMS::Compositor::HasCurrentFrame() {
576 base::AutoLock
auto_lock(current_frame_lock_
);
577 return !!current_frame_
.get();
580 scoped_refptr
<media::VideoFrame
>
581 WebMediaPlayerMS::Compositor::GetCurrentFrame() {
582 DVLOG(3) << "WebMediaPlayerMS::Compositor::GetCurrentFrame";
583 base::AutoLock
auto_lock(current_frame_lock_
);
584 if (!current_frame_
.get())
586 return current_frame_
;
589 void WebMediaPlayerMS::Compositor::PutCurrentFrame() {
590 DVLOG(3) << "WebMediaPlayerMS::PutCurrentFrame";
591 current_frame_used_
= true;
594 void WebMediaPlayerMS::Compositor::SetVideoFrameProviderClient(
595 cc::VideoFrameProvider::Client
* client
) {
596 DCHECK(compositor_task_runner_
->BelongsToCurrentThread());
597 if (video_frame_provider_client_
)
598 video_frame_provider_client_
->StopUsingProvider();
600 video_frame_provider_client_
= client
;
601 if (video_frame_provider_client_
)
602 video_frame_provider_client_
->StartRendering();
605 void WebMediaPlayerMS::Compositor::StartRendering() {
606 DCHECK(compositor_task_runner_
->BelongsToCurrentThread());
608 if (video_frame_provider_client_
)
609 video_frame_provider_client_
->StartRendering();
612 void WebMediaPlayerMS::Compositor::StopRendering() {
613 DCHECK(compositor_task_runner_
->BelongsToCurrentThread());
615 if (video_frame_provider_client_
)
616 video_frame_provider_client_
->StopRendering();
619 void WebMediaPlayerMS::Compositor::ReplaceCurrentFrameWithACopy(
620 media::SkCanvasVideoRenderer
* renderer
) {
621 base::AutoLock
auto_lock(current_frame_lock_
);
622 if (!current_frame_
.get())
625 // Copy the frame so that rendering can show the last received frame.
626 // The original frame must not be referenced when the player is paused since
627 // there might be a finite number of available buffers. E.g, video that
628 // originates from a video camera.
629 scoped_refptr
<media::VideoFrame
> new_frame
=
630 CopyFrameToYV12(current_frame_
, renderer
);
632 current_frame_
= new_frame
;
635 gfx::Size
WebMediaPlayerMS::Compositor::GetCurrentSize() {
636 base::AutoLock
auto_lock(current_frame_lock_
);
637 return current_frame_
.get() ? current_frame_
->natural_size() : gfx::Size();
640 base::TimeDelta
WebMediaPlayerMS::Compositor::GetCurrentTime() {
641 base::AutoLock
auto_lock(current_frame_lock_
);
642 return current_frame_
.get() ? current_frame_
->timestamp() : base::TimeDelta();
645 unsigned WebMediaPlayerMS::Compositor::GetTotalFrameCount() {
646 return total_frame_count_
;
649 unsigned WebMediaPlayerMS::Compositor::GetDroppedFrameCount() {
650 return dropped_frame_count_
;
652 } // namespace content