1 // Copyright 2014 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/webrtc/media_stream_remote_video_source.h"
6
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/location.h"
10 #include "base/message_loop/message_loop_proxy.h"
11 #include "content/renderer/media/native_handle_impl.h"
12 #include "media/base/bind_to_current_loop.h"
13 #include "media/base/video_frame.h"
14 #include "media/base/video_frame_pool.h"
15 #include "media/base/video_util.h"
16 #include "third_party/libjingle/source/talk/media/base/videoframe.h"
17
18 namespace content {
19
20 // Internal class used for receiving frames from the webrtc track on a
21 // libjingle thread and forward it to the IO-thread.
22 class MediaStreamRemoteVideoSource::RemoteVideoSourceDelegate
23 : public base::RefCountedThreadSafe<RemoteVideoSourceDelegate>,
24 public webrtc::VideoRendererInterface {
25 public:
26 RemoteVideoSourceDelegate(
27 const scoped_refptr<base::MessageLoopProxy>& io_message_loop,
28 const VideoCaptureDeliverFrameCB& new_frame_callback);
29
30 protected:
31 friend class base::RefCountedThreadSafe<RemoteVideoSourceDelegate>;
32 virtual ~RemoteVideoSourceDelegate();
33
34 // Implements webrtc::VideoRendererInterface used for receiving video frames
35 // from the PeerConnection video track. May be called on a libjingle internal
36 // thread.
37 virtual void SetSize(int width, int height) OVERRIDE;
38 virtual void RenderFrame(const cricket::VideoFrame* frame) OVERRIDE;
39
40 void DoRenderFrameOnIOThread(scoped_refptr<media::VideoFrame> video_frame,
41 const media::VideoCaptureFormat& format);
42 private:
43 // Bound to the render thread.
44 base::ThreadChecker thread_checker_;
45
46 scoped_refptr<base::MessageLoopProxy> io_message_loop_;
47 // |frame_pool_| is only accessed on whatever
48 // thread webrtc::VideoRendererInterface::RenderFrame is called on.
49 media::VideoFramePool frame_pool_;
50
51 // |frame_callback_| is accessed on the IO thread.
52 VideoCaptureDeliverFrameCB frame_callback_;
53 };
54
55 MediaStreamRemoteVideoSource::
RemoteVideoSourceDelegate(const scoped_refptr<base::MessageLoopProxy> & io_message_loop,const VideoCaptureDeliverFrameCB & new_frame_callback)56 RemoteVideoSourceDelegate::RemoteVideoSourceDelegate(
57 const scoped_refptr<base::MessageLoopProxy>& io_message_loop,
58 const VideoCaptureDeliverFrameCB& new_frame_callback)
59 : io_message_loop_(io_message_loop),
60 frame_callback_(new_frame_callback) {
61 }
62
63 MediaStreamRemoteVideoSource::
~RemoteVideoSourceDelegate()64 RemoteVideoSourceDelegate::~RemoteVideoSourceDelegate() {
65 }
66
67 void MediaStreamRemoteVideoSource::
SetSize(int width,int height)68 RemoteVideoSourceDelegate::SetSize(int width, int height) {
69 }
70
71 void MediaStreamRemoteVideoSource::
RenderFrame(const cricket::VideoFrame * frame)72 RemoteVideoSourceDelegate::RenderFrame(
73 const cricket::VideoFrame* frame) {
74 base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
75 frame->GetElapsedTime() / rtc::kNumNanosecsPerMicrosec);
76
77 scoped_refptr<media::VideoFrame> video_frame;
78 if (frame->GetNativeHandle() != NULL) {
79 NativeHandleImpl* handle =
80 static_cast<NativeHandleImpl*>(frame->GetNativeHandle());
81 video_frame = static_cast<media::VideoFrame*>(handle->GetHandle());
82 video_frame->set_timestamp(timestamp);
83 } else {
84 gfx::Size size(frame->GetWidth(), frame->GetHeight());
85 video_frame = frame_pool_.CreateFrame(
86 media::VideoFrame::YV12, size, gfx::Rect(size), size, timestamp);
87
88 // Non-square pixels are unsupported.
89 DCHECK_EQ(frame->GetPixelWidth(), 1u);
90 DCHECK_EQ(frame->GetPixelHeight(), 1u);
91
92 int y_rows = frame->GetHeight();
93 int uv_rows = frame->GetChromaHeight();
94 CopyYPlane(
95 frame->GetYPlane(), frame->GetYPitch(), y_rows, video_frame.get());
96 CopyUPlane(
97 frame->GetUPlane(), frame->GetUPitch(), uv_rows, video_frame.get());
98 CopyVPlane(
99 frame->GetVPlane(), frame->GetVPitch(), uv_rows, video_frame.get());
100 }
101
102 media::VideoPixelFormat pixel_format =
103 (video_frame->format() == media::VideoFrame::YV12) ?
104 media::PIXEL_FORMAT_YV12 : media::PIXEL_FORMAT_TEXTURE;
105
106 media::VideoCaptureFormat format(
107 gfx::Size(video_frame->natural_size().width(),
108 video_frame->natural_size().height()),
109 MediaStreamVideoSource::kUnknownFrameRate,
110 pixel_format);
111
112 io_message_loop_->PostTask(
113 FROM_HERE,
114 base::Bind(&RemoteVideoSourceDelegate::DoRenderFrameOnIOThread,
115 this, video_frame, format));
116 }
117
118 void MediaStreamRemoteVideoSource::
DoRenderFrameOnIOThread(scoped_refptr<media::VideoFrame> video_frame,const media::VideoCaptureFormat & format)119 RemoteVideoSourceDelegate::DoRenderFrameOnIOThread(
120 scoped_refptr<media::VideoFrame> video_frame,
121 const media::VideoCaptureFormat& format) {
122 DCHECK(io_message_loop_->BelongsToCurrentThread());
123 // TODO(hclam): Give the estimated capture time.
124 frame_callback_.Run(video_frame, format, base::TimeTicks());
125 }
126
MediaStreamRemoteVideoSource(webrtc::VideoTrackInterface * remote_track)127 MediaStreamRemoteVideoSource::MediaStreamRemoteVideoSource(
128 webrtc::VideoTrackInterface* remote_track)
129 : remote_track_(remote_track),
130 last_state_(remote_track->state()) {
131 remote_track_->RegisterObserver(this);
132 }
133
~MediaStreamRemoteVideoSource()134 MediaStreamRemoteVideoSource::~MediaStreamRemoteVideoSource() {
135 remote_track_->UnregisterObserver(this);
136 }
137
GetCurrentSupportedFormats(int max_requested_width,int max_requested_height,double max_requested_frame_rate,const VideoCaptureDeviceFormatsCB & callback)138 void MediaStreamRemoteVideoSource::GetCurrentSupportedFormats(
139 int max_requested_width,
140 int max_requested_height,
141 double max_requested_frame_rate,
142 const VideoCaptureDeviceFormatsCB& callback) {
143 DCHECK(thread_checker_.CalledOnValidThread());
144 media::VideoCaptureFormats formats;
145 // Since the remote end is free to change the resolution at any point in time
146 // the supported formats are unknown.
147 callback.Run(formats);
148 }
149
StartSourceImpl(const media::VideoCaptureFormat & format,const VideoCaptureDeliverFrameCB & frame_callback)150 void MediaStreamRemoteVideoSource::StartSourceImpl(
151 const media::VideoCaptureFormat& format,
152 const VideoCaptureDeliverFrameCB& frame_callback) {
153 DCHECK(thread_checker_.CalledOnValidThread());
154 DCHECK(!delegate_.get());
155 delegate_ = new RemoteVideoSourceDelegate(io_message_loop(), frame_callback);
156 remote_track_->AddRenderer(delegate_.get());
157 OnStartDone(MEDIA_DEVICE_OK);
158 }
159
StopSourceImpl()160 void MediaStreamRemoteVideoSource::StopSourceImpl() {
161 DCHECK(thread_checker_.CalledOnValidThread());
162 DCHECK(state() != MediaStreamVideoSource::ENDED);
163 remote_track_->RemoveRenderer(delegate_.get());
164 }
165
166 webrtc::VideoRendererInterface*
RenderInterfaceForTest()167 MediaStreamRemoteVideoSource::RenderInterfaceForTest() {
168 return delegate_.get();
169 }
170
OnChanged()171 void MediaStreamRemoteVideoSource::OnChanged() {
172 DCHECK(thread_checker_.CalledOnValidThread());
173 webrtc::MediaStreamTrackInterface::TrackState state = remote_track_->state();
174 if (state != last_state_) {
175 last_state_ = state;
176 switch (state) {
177 case webrtc::MediaStreamTrackInterface::kInitializing:
178 // Ignore the kInitializing state since there is no match in
179 // WebMediaStreamSource::ReadyState.
180 break;
181 case webrtc::MediaStreamTrackInterface::kLive:
182 SetReadyState(blink::WebMediaStreamSource::ReadyStateLive);
183 break;
184 case webrtc::MediaStreamTrackInterface::kEnded:
185 SetReadyState(blink::WebMediaStreamSource::ReadyStateEnded);
186 break;
187 default:
188 NOTREACHED();
189 break;
190 }
191 }
192 }
193
194 } // namespace content
195