Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |