Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(49)

Side by Side Diff: content/renderer/media/video_track_recorder.cc

Issue 2147753002: VideoTrackRecorder: add support for texture backed ARGB VideoFrames (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebased. Using RetrieveFrameOnMainThread() for texture-backed frames. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « content/browser/media/webrtc/webrtc_media_recorder_browsertest.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/renderer/media/video_track_recorder.h" 5 #include "content/renderer/media/video_track_recorder.h"
6 6
7 #include <utility> 7 #include <utility>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/macros.h" 11 #include "base/macros.h"
12 #include "base/sys_info.h" 12 #include "base/sys_info.h"
13 #include "base/task_runner_util.h" 13 #include "base/task_runner_util.h"
14 #include "base/threading/thread.h" 14 #include "base/threading/thread.h"
15 #include "base/threading/thread_task_runner_handle.h" 15 #include "base/threading/thread_task_runner_handle.h"
16 #include "base/time/time.h" 16 #include "base/time/time.h"
17 #include "base/trace_event/trace_event.h" 17 #include "base/trace_event/trace_event.h"
18 #include "content/common/gpu/client/context_provider_command_buffer.h"
18 #include "content/renderer/media/renderer_gpu_video_accelerator_factories.h" 19 #include "content/renderer/media/renderer_gpu_video_accelerator_factories.h"
19 #include "content/renderer/render_thread_impl.h" 20 #include "content/renderer/render_thread_impl.h"
20 #include "media/base/bind_to_current_loop.h" 21 #include "media/base/bind_to_current_loop.h"
21 #include "media/base/video_frame.h" 22 #include "media/base/video_frame.h"
22 #include "media/base/video_util.h" 23 #include "media/base/video_util.h"
23 #include "third_party/libyuv/include/libyuv/convert.h" 24 #include "media/filters/context_3d.h"
25 #include "media/renderers/skcanvas_video_renderer.h"
26 #include "skia/ext/platform_canvas.h"
27 #include "third_party/libyuv/include/libyuv.h"
28 #include "third_party/skia/include/core/SkCanvas.h"
29 #include "third_party/skia/include/core/SkSurface.h"
24 #include "ui/gfx/geometry/size.h" 30 #include "ui/gfx/geometry/size.h"
25 31
26 #if BUILDFLAG(RTC_USE_H264) 32 #if BUILDFLAG(RTC_USE_H264)
27 #include "third_party/openh264/src/codec/api/svc/codec_api.h" 33 #include "third_party/openh264/src/codec/api/svc/codec_api.h"
28 #include "third_party/openh264/src/codec/api/svc/codec_app_def.h" 34 #include "third_party/openh264/src/codec/api/svc/codec_app_def.h"
29 #include "third_party/openh264/src/codec/api/svc/codec_def.h" 35 #include "third_party/openh264/src/codec/api/svc/codec_def.h"
30 #endif // #if BUILDFLAG(RTC_USE_H264) 36 #endif // #if BUILDFLAG(RTC_USE_H264)
31 37
32 extern "C" { 38 extern "C" {
33 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide 39 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
98 return media::VIDEO_CODEC_PROFILE_UNKNOWN; 104 return media::VIDEO_CODEC_PROFILE_UNKNOWN;
99 } 105 }
100 106
101 } // anonymous namespace 107 } // anonymous namespace
102 108
103 // Base class to describe a generic Encoder, encapsulating all actual encoder 109 // Base class to describe a generic Encoder, encapsulating all actual encoder
104 // (re)configurations, encoding and delivery of received frames. This class is 110 // (re)configurations, encoding and delivery of received frames. This class is
105 // ref-counted to allow the MediaStreamVideoTrack to hold a reference to it (via 111 // ref-counted to allow the MediaStreamVideoTrack to hold a reference to it (via
106 // the callback that MediaStreamVideoSink passes along) and to jump back and 112 // the callback that MediaStreamVideoSink passes along) and to jump back and
107 // forth to an internal encoder thread. Moreover, this class: 113 // forth to an internal encoder thread. Moreover, this class:
108 // - is created and destroyed on its parent's thread (usually the main Render 114 // - is created on its parent's thread (usually the main Render thread),
109 // thread), |main_task_runner_|. 115 // that is, |main_task_runner_|.
110 // - receives VideoFrames on |origin_task_runner_| and runs OnEncodedVideoCB on 116 // - receives VideoFrames on |origin_task_runner_| and runs OnEncodedVideoCB on
111 // that thread as well. This task runner is cached on first frame arrival, and 117 // that thread as well. This task runner is cached on first frame arrival, and
112 // is supposed to be the render IO thread (but this is not enforced); 118 // is supposed to be the render IO thread (but this is not enforced);
113 // - uses an internal |encoding_task_runner_| for actual encoder interactions, 119 // - uses an internal |encoding_task_runner_| for actual encoder interactions,
114 // namely configuration, encoding (which might take some time) and destruction. 120 // namely configuration, encoding (which might take some time) and destruction.
115 // This task runner can be passed on the creation. If nothing is passed, a new 121 // This task runner can be passed on the creation. If nothing is passed, a new
116 // encoding thread is created and used. 122 // encoding thread is created and used.
117 class VideoTrackRecorder::Encoder : public base::RefCountedThreadSafe<Encoder> { 123 class VideoTrackRecorder::Encoder : public base::RefCountedThreadSafe<Encoder> {
118 public: 124 public:
119 Encoder(const OnEncodedVideoCB& on_encoded_video_callback, 125 Encoder(const OnEncodedVideoCB& on_encoded_video_callback,
120 int32_t bits_per_second, 126 int32_t bits_per_second,
121 scoped_refptr<base::SingleThreadTaskRunner> encoding_task_runner = 127 scoped_refptr<base::SingleThreadTaskRunner> encoding_task_runner =
122 nullptr) 128 nullptr)
123 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()), 129 : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
124 encoding_task_runner_(encoding_task_runner), 130 encoding_task_runner_(encoding_task_runner),
125 paused_(false), 131 paused_(false),
126 on_encoded_video_callback_(on_encoded_video_callback), 132 on_encoded_video_callback_(on_encoded_video_callback),
127 bits_per_second_(bits_per_second) { 133 bits_per_second_(bits_per_second) {
128 DCHECK(!on_encoded_video_callback_.is_null()); 134 DCHECK(!on_encoded_video_callback_.is_null());
129 if (encoding_task_runner_) 135 if (encoding_task_runner_)
130 return; 136 return;
131 encoding_thread_.reset(new base::Thread("EncodingThread")); 137 encoding_thread_.reset(new base::Thread("EncodingThread"));
132 encoding_thread_->Start(); 138 encoding_thread_->Start();
133 encoding_task_runner_ = encoding_thread_->task_runner(); 139 encoding_task_runner_ = encoding_thread_->task_runner();
134 } 140 }
135 141
136 // Start encoding |frame|, returning via |on_encoded_video_callback_|. This 142 // Start encoding |frame|, returning via |on_encoded_video_callback_|. This
137 // call will also trigger a ConfigureEncoderOnEncodingTaskRunner() upon first 143 // call will also trigger a ConfigureEncoderOnEncodingTaskRunner() upon first
138 // frame arrival or parameter change, and an EncodeOnEncodingTaskRunner() to 144 // frame arrival or parameter change, and an EncodeOnEncodingTaskRunner() to
139 // actually encode the frame. 145 // actually encode the frame. If the |frame|'s data is not directly available
146 // (e.g. it's a texture) then RetrieveFrameOnMainThread() is called, and if
147 // even that fails, black frames are sent instead.
140 void StartFrameEncode(const scoped_refptr<VideoFrame>& frame, 148 void StartFrameEncode(const scoped_refptr<VideoFrame>& frame,
141 base::TimeTicks capture_timestamp); 149 base::TimeTicks capture_timestamp);
150 void RetrieveFrameOnMainThread(const scoped_refptr<VideoFrame>& video_frame,
151 base::TimeTicks capture_timestamp);
142 152
143 void SetPaused(bool paused); 153 void SetPaused(bool paused);
144 154
145 protected: 155 protected:
146 friend class base::RefCountedThreadSafe<Encoder>; 156 friend class base::RefCountedThreadSafe<Encoder>;
147 virtual ~Encoder() {} 157 virtual ~Encoder() {
158 main_task_runner_->DeleteSoon(FROM_HERE, video_renderer_.release());
159 }
148 160
149 virtual void EncodeOnEncodingTaskRunner( 161 virtual void EncodeOnEncodingTaskRunner(
150 const scoped_refptr<VideoFrame>& frame, 162 const scoped_refptr<VideoFrame>& frame,
151 base::TimeTicks capture_timestamp) = 0; 163 base::TimeTicks capture_timestamp) = 0;
152 virtual void ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size) = 0; 164 virtual void ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size) = 0;
153 165
154 // Used to shutdown properly on the same thread we were created. 166 // Used to shutdown properly on the same thread we were created.
155 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; 167 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
156 168
157 // Task runner where frames to encode and reply callbacks must happen. 169 // Task runner where frames to encode and reply callbacks must happen.
158 scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_; 170 scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
159 171
160 // Task runner where encoding interactions happen. 172 // Task runner where encoding interactions happen.
161 scoped_refptr<base::SingleThreadTaskRunner> encoding_task_runner_; 173 scoped_refptr<base::SingleThreadTaskRunner> encoding_task_runner_;
162 174
163 // Optional thread for encoding. Active for the lifetime of VpxEncoder. 175 // Optional thread for encoding. Active for the lifetime of VpxEncoder.
164 std::unique_ptr<base::Thread> encoding_thread_; 176 std::unique_ptr<base::Thread> encoding_thread_;
165 177
166 // While |paused_|, frames are not encoded. Used only from |encoding_thread_|. 178 // While |paused_|, frames are not encoded. Used only from |encoding_thread_|.
167 bool paused_; 179 bool paused_;
168 180
169 // This callback should be exercised on IO thread. 181 // This callback should be exercised on IO thread.
170 const OnEncodedVideoCB on_encoded_video_callback_; 182 const OnEncodedVideoCB on_encoded_video_callback_;
171 183
172 // Target bitrate for video encoding. If 0, a standard bitrate is used. 184 // Target bitrate for video encoding. If 0, a standard bitrate is used.
173 const int32_t bits_per_second_; 185 const int32_t bits_per_second_;
174 186
187 // Used to retrieve incoming opaque VideoFrames (i.e. VideoFrames backed by
188 // textures). Created on-demand on |main_task_runner_|.
189 std::unique_ptr<media::SkCanvasVideoRenderer> video_renderer_;
190 sk_sp<SkSurface> surface_;
191
175 DISALLOW_COPY_AND_ASSIGN(Encoder); 192 DISALLOW_COPY_AND_ASSIGN(Encoder);
176 }; 193 };
177 194
178 void VideoTrackRecorder::Encoder::StartFrameEncode( 195 void VideoTrackRecorder::Encoder::StartFrameEncode(
179 const scoped_refptr<VideoFrame>& video_frame, 196 const scoped_refptr<VideoFrame>& video_frame,
180 base::TimeTicks capture_timestamp) { 197 base::TimeTicks capture_timestamp) {
181 // Cache the thread sending frames on first frame arrival. 198 // Cache the thread sending frames on first frame arrival.
182 if (!origin_task_runner_.get()) 199 if (!origin_task_runner_.get())
183 origin_task_runner_ = base::ThreadTaskRunnerHandle::Get(); 200 origin_task_runner_ = base::ThreadTaskRunnerHandle::Get();
184 DCHECK(origin_task_runner_->BelongsToCurrentThread()); 201 DCHECK(origin_task_runner_->BelongsToCurrentThread());
185 if (paused_) 202 if (paused_)
186 return; 203 return;
187 204
188 if (!(video_frame->format() == media::PIXEL_FORMAT_I420 || 205 if (!(video_frame->format() == media::PIXEL_FORMAT_I420 ||
189 video_frame->format() == media::PIXEL_FORMAT_YV12 || 206 video_frame->format() == media::PIXEL_FORMAT_YV12 ||
207 video_frame->format() == media::PIXEL_FORMAT_ARGB ||
190 video_frame->format() == media::PIXEL_FORMAT_YV12A)) { 208 video_frame->format() == media::PIXEL_FORMAT_YV12A)) {
191 NOTREACHED(); 209 NOTREACHED() << media::VideoPixelFormatToString(video_frame->format());
192 return; 210 return;
193 } 211 }
212
213 if (video_frame->HasTextures()) {
214 main_task_runner_->PostTask(
215 FROM_HERE, base::Bind(&Encoder::RetrieveFrameOnMainThread, this,
216 video_frame, capture_timestamp));
217 return;
218 }
219
194 scoped_refptr<media::VideoFrame> frame = video_frame; 220 scoped_refptr<media::VideoFrame> frame = video_frame;
195 // Drop alpha channel since we do not support it yet. 221 // Drop alpha channel since we do not support it yet.
196 if (frame->format() == media::PIXEL_FORMAT_YV12A) 222 if (frame->format() == media::PIXEL_FORMAT_YV12A)
197 frame = media::WrapAsI420VideoFrame(video_frame); 223 frame = media::WrapAsI420VideoFrame(video_frame);
198 224
199 encoding_task_runner_->PostTask( 225 encoding_task_runner_->PostTask(
200 FROM_HERE, base::Bind(&Encoder::EncodeOnEncodingTaskRunner, this, frame, 226 FROM_HERE, base::Bind(&Encoder::EncodeOnEncodingTaskRunner, this, frame,
201 capture_timestamp)); 227 capture_timestamp));
202 } 228 }
203 229
230 void VideoTrackRecorder::Encoder::RetrieveFrameOnMainThread(
231 const scoped_refptr<VideoFrame>& video_frame,
232 base::TimeTicks capture_timestamp) {
233 DCHECK(main_task_runner_->BelongsToCurrentThread());
234
235 scoped_refptr<media::VideoFrame> frame;
236
237 // |context_provider| is null if the GPU process has crashed or isn't there.
238 ContextProviderCommandBuffer* const context_provider =
239 RenderThreadImpl::current()->SharedMainThreadContextProvider().get();
240 if (!context_provider) {
241 // Send black frames (yuv = {0, 127, 127}).
242 frame = media::VideoFrame::CreateColorFrame(
243 video_frame->coded_size(), 0u, 0x80, 0x80, video_frame->timestamp());
emircan 2016/07/15 17:47:34 Replace coded_size() with visible_rect() here. Cre
mcasas 2016/07/15 18:03:09 Done.
244 } else {
245 // Accelerated decoders produce ARGB/ABGR texture-backed frames (see
246 // https://crbug.com/585242), fetch them using a SkCanvasVideoRenderer.
247 DCHECK(video_frame->HasTextures());
248 DCHECK_EQ(media::PIXEL_FORMAT_ARGB, video_frame->format());
249
250 frame = media::VideoFrame::CreateFrame(
251 media::PIXEL_FORMAT_I420, video_frame->coded_size(),
252 video_frame->visible_rect(), video_frame->natural_size(),
253 video_frame->timestamp());
254
255 const SkImageInfo info = SkImageInfo::MakeN32(
256 frame->visible_rect().width(), frame->visible_rect().height(),
257 kOpaque_SkAlphaType);
258
259 // Create |surface_| if it doesn't exist or incoming resolution has changed.
260 if (!surface_ || surface_->width() != info.width() ||
261 surface_->height() != info.height()) {
262 surface_ = SkSurface::MakeRaster(info);
263 }
264 if (!video_renderer_)
265 video_renderer_.reset(new media::SkCanvasVideoRenderer);
266
267 DCHECK(context_provider->ContextGL());
268 video_renderer_->Copy(video_frame.get(), surface_->getCanvas(),
269 media::Context3D(context_provider->ContextGL(),
270 context_provider->GrContext()));
271
272 SkPixmap pixmap;
273 if (!skia::GetWritablePixels(surface_->getCanvas(), &pixmap)) {
274 DLOG(ERROR) << "Error trying to map SkSurface's pixels";
275 return;
276 }
277
278 const uint32 source_pixel_format =
279 (kN32_SkColorType == kRGBA_8888_SkColorType) ? libyuv::FOURCC_ABGR
280 : libyuv::FOURCC_ARGB;
281 if (libyuv::ConvertToI420(static_cast<uint8*>(pixmap.writable_addr()),
282 pixmap.getSafeSize(),
283 frame->visible_data(media::VideoFrame::kYPlane),
284 frame->stride(media::VideoFrame::kYPlane),
285 frame->visible_data(media::VideoFrame::kUPlane),
286 frame->stride(media::VideoFrame::kUPlane),
287 frame->visible_data(media::VideoFrame::kVPlane),
288 frame->stride(media::VideoFrame::kVPlane),
289 0 /* crop_x */, 0 /* crop_y */,
290 pixmap.width(), pixmap.height(),
291 frame->coded_size().width(),
emircan 2016/07/15 17:47:34 Replace coded_size() with visible_rect() here. You
mcasas 2016/07/15 18:03:09 Done.
292 frame->coded_size().height(),
293 libyuv::kRotate0, source_pixel_format) != 0) {
emircan 2016/07/15 17:47:34 Consider the case for rotation. We have seen that
mcasas 2016/07/15 18:03:09 Added TODO, will revisit.
294 DLOG(ERROR) << "Error converting frame to I420";
295 return;
296 }
297 }
298
299 encoding_task_runner_->PostTask(
300 FROM_HERE, base::Bind(&Encoder::EncodeOnEncodingTaskRunner, this, frame,
301 capture_timestamp));
302 }
303
204 void VideoTrackRecorder::Encoder::SetPaused(bool paused) { 304 void VideoTrackRecorder::Encoder::SetPaused(bool paused) {
205 if (!encoding_task_runner_->BelongsToCurrentThread()) { 305 if (!encoding_task_runner_->BelongsToCurrentThread()) {
206 encoding_task_runner_->PostTask( 306 encoding_task_runner_->PostTask(
207 FROM_HERE, base::Bind(&Encoder::SetPaused, this, paused)); 307 FROM_HERE, base::Bind(&Encoder::SetPaused, this, paused));
208 return; 308 return;
209 } 309 }
210 paused_ = paused; 310 paused_ = paused;
211 } 311 }
212 312
213 namespace { 313 namespace {
(...skipping 822 matching lines...) Expand 10 before | Expand all | Expand 10 after
1036 encoder_->SetPaused(paused_before_init_); 1136 encoder_->SetPaused(paused_before_init_);
1037 1137
1038 // StartFrameEncode() will be called on Render IO thread. 1138 // StartFrameEncode() will be called on Render IO thread.
1039 MediaStreamVideoSink::ConnectToTrack( 1139 MediaStreamVideoSink::ConnectToTrack(
1040 track_, 1140 track_,
1041 base::Bind(&VideoTrackRecorder::Encoder::StartFrameEncode, encoder_), 1141 base::Bind(&VideoTrackRecorder::Encoder::StartFrameEncode, encoder_),
1042 false); 1142 false);
1043 } 1143 }
1044 1144
1045 } // namespace content 1145 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/media/webrtc/webrtc_media_recorder_browsertest.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698