Home | History | Annotate | Download | only in media
      1 // Copyright (c) 2012 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 "content/renderer/media/rtc_video_renderer.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/debug/trace_event.h"
      9 #include "base/location.h"
     10 #include "base/logging.h"
     11 #include "base/message_loop/message_loop_proxy.h"
     12 #include "media/base/video_frame.h"
     13 #include "media/base/video_util.h"
     14 #include "third_party/libjingle/source/talk/media/base/videoframe.h"
     15 
     16 using media::CopyYPlane;
     17 using media::CopyUPlane;
     18 using media::CopyVPlane;
     19 
     20 namespace content {
     21 
     22 RTCVideoRenderer::RTCVideoRenderer(
     23     webrtc::VideoTrackInterface* video_track,
     24     const base::Closure& error_cb,
     25     const RepaintCB& repaint_cb)
     26     : error_cb_(error_cb),
     27       repaint_cb_(repaint_cb),
     28       message_loop_proxy_(base::MessageLoopProxy::current()),
     29       state_(kStopped),
     30       video_track_(video_track) {
     31 }
     32 
     33 RTCVideoRenderer::~RTCVideoRenderer() {
     34 }
     35 
     36 void RTCVideoRenderer::Start() {
     37   DCHECK(message_loop_proxy_->BelongsToCurrentThread());
     38   DCHECK_EQ(state_, kStopped);
     39 
     40   if (video_track_.get()) {
     41     video_track_->AddRenderer(this);
     42     video_track_->RegisterObserver(this);
     43   }
     44   state_ = kStarted;
     45   MaybeRenderSignalingFrame();
     46 }
     47 
     48 void RTCVideoRenderer::Stop() {
     49   DCHECK(message_loop_proxy_->BelongsToCurrentThread());
     50   if (video_track_.get()) {
     51     state_ = kStopped;
     52     video_track_->RemoveRenderer(this);
     53     video_track_->UnregisterObserver(this);
     54     video_track_ = NULL;
     55   }
     56 }
     57 
     58 void RTCVideoRenderer::Play() {
     59   DCHECK(message_loop_proxy_->BelongsToCurrentThread());
     60   if (video_track_.get() && state_ == kPaused) {
     61     state_ = kStarted;
     62   }
     63 }
     64 
     65 void RTCVideoRenderer::Pause() {
     66   DCHECK(message_loop_proxy_->BelongsToCurrentThread());
     67   if (video_track_.get() && state_ == kStarted) {
     68     state_ = kPaused;
     69   }
     70 }
     71 
     72 void RTCVideoRenderer::SetSize(int width, int height) {
     73 }
     74 
     75 void RTCVideoRenderer::RenderFrame(const cricket::VideoFrame* frame) {
     76   base::TimeDelta timestamp = base::TimeDelta::FromMilliseconds(
     77       frame->GetTimeStamp() / talk_base::kNumNanosecsPerMillisec);
     78 
     79   TRACE_EVENT_INSTANT2("rtc_video_renderer",
     80                        "RenderFrame",
     81                        TRACE_EVENT_SCOPE_THREAD,
     82                        "elapsed time",
     83                        frame->GetElapsedTime(),
     84                        "timestamp_ms",
     85                        timestamp.InMilliseconds());
     86 
     87   gfx::Size size(frame->GetWidth(), frame->GetHeight());
     88   scoped_refptr<media::VideoFrame> video_frame =
     89       media::VideoFrame::CreateFrame(media::VideoFrame::YV12,
     90                                      size,
     91                                      gfx::Rect(size),
     92                                      size,
     93                                      timestamp);
     94 
     95   // Aspect ratio unsupported; DCHECK when there are non-square pixels.
     96   DCHECK_EQ(frame->GetPixelWidth(), 1u);
     97   DCHECK_EQ(frame->GetPixelHeight(), 1u);
     98 
     99   int y_rows = frame->GetHeight();
    100   int uv_rows = frame->GetHeight() / 2;  // YV12 format.
    101   CopyYPlane(frame->GetYPlane(), frame->GetYPitch(), y_rows, video_frame.get());
    102   CopyUPlane(
    103       frame->GetUPlane(), frame->GetUPitch(), uv_rows, video_frame.get());
    104   CopyVPlane(
    105       frame->GetVPlane(), frame->GetVPitch(), uv_rows, video_frame.get());
    106 
    107   message_loop_proxy_->PostTask(
    108       FROM_HERE, base::Bind(&RTCVideoRenderer::DoRenderFrameOnMainThread,
    109                             this, video_frame));
    110 }
    111 
    112 void RTCVideoRenderer::OnChanged() {
    113   DCHECK(message_loop_proxy_->BelongsToCurrentThread());
    114   MaybeRenderSignalingFrame();
    115 }
    116 
    117 void RTCVideoRenderer::MaybeRenderSignalingFrame() {
    118   // Render a small black frame if the track transition to ended.
    119   // This is necessary to make sure audio can play if the video tag src is
    120   // a MediaStream video track that has been rejected or ended.
    121   if (video_track_->state() == webrtc::MediaStreamTrackInterface::kEnded) {
    122     const int kMinFrameSize = 2;
    123     const gfx::Size size(kMinFrameSize, kMinFrameSize);
    124     scoped_refptr<media::VideoFrame> video_frame =
    125         media::VideoFrame::CreateBlackFrame(size);
    126     DoRenderFrameOnMainThread(video_frame);
    127   }
    128 }
    129 
    130 void RTCVideoRenderer::DoRenderFrameOnMainThread(
    131     scoped_refptr<media::VideoFrame> video_frame) {
    132   DCHECK(message_loop_proxy_->BelongsToCurrentThread());
    133 
    134   if (state_ != kStarted) {
    135     return;
    136   }
    137 
    138   TRACE_EVENT0("video", "DoRenderFrameOnMainThread");
    139   repaint_cb_.Run(video_frame);
    140 }
    141 
    142 }  // namespace content
    143