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/threading/thread.h" | 14 #include "base/threading/thread.h" |
| 14 #include "base/time/time.h" | 15 #include "base/time/time.h" |
| 15 #include "base/trace_event/trace_event.h" | 16 #include "base/trace_event/trace_event.h" |
| 17 #include "content/renderer/media/renderer_gpu_video_accelerator_factories.h" | |
| 18 #include "content/renderer/render_thread_impl.h" | |
| 19 #include "media/base/bind_to_current_loop.h" | |
| 16 #include "media/base/video_frame.h" | 20 #include "media/base/video_frame.h" |
| 17 #include "media/base/video_util.h" | 21 #include "media/base/video_util.h" |
| 22 #include "third_party/libyuv/include/libyuv/convert.h" | |
| 18 #include "ui/gfx/geometry/size.h" | 23 #include "ui/gfx/geometry/size.h" |
| 19 | 24 |
| 20 #if BUILDFLAG(RTC_USE_H264) | 25 #if BUILDFLAG(RTC_USE_H264) |
| 21 #include "third_party/openh264/src/codec/api/svc/codec_api.h" | 26 #include "third_party/openh264/src/codec/api/svc/codec_api.h" |
| 22 #include "third_party/openh264/src/codec/api/svc/codec_app_def.h" | 27 #include "third_party/openh264/src/codec/api/svc/codec_app_def.h" |
| 23 #include "third_party/openh264/src/codec/api/svc/codec_def.h" | 28 #include "third_party/openh264/src/codec/api/svc/codec_def.h" |
| 24 #endif // #if BUILDFLAG(RTC_USE_H264) | 29 #endif // #if BUILDFLAG(RTC_USE_H264) |
| 25 | 30 |
| 26 extern "C" { | 31 extern "C" { |
| 27 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide | 32 // VPX_CODEC_DISABLE_COMPAT excludes parts of the libvpx API that provide |
| 28 // backwards compatibility for legacy applications using the library. | 33 // backwards compatibility for legacy applications using the library. |
| 29 #define VPX_CODEC_DISABLE_COMPAT 1 | 34 #define VPX_CODEC_DISABLE_COMPAT 1 |
| 30 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" | 35 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" |
| 31 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h" | 36 #include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h" |
| 32 } | 37 } |
| 33 | 38 |
| 34 using media::VideoFrame; | 39 using media::VideoFrame; |
| 35 using media::VideoFrameMetadata; | 40 using media::VideoFrameMetadata; |
| 36 | 41 |
| 42 namespace { | |
|
mcasas
2016/05/18 22:22:58
IIRC, you can move this anonymous namespace
inside
emircan
2016/05/21 00:01:17
Done.
| |
| 43 | |
| 44 const int kVEAEncoderMinResolutionWidth = 640; | |
| 45 const int kVEAEncoderMinResolutionHeight = 480; | |
| 46 const int kVEAEncoderOutputBufferCount = 4; | |
| 47 | |
| 48 // Returns the preferred codec profile from VEA supported profiles. If no | |
| 49 // profile is found, returns VIDEO_CODEC_PROFILE_UNKNOWN. | |
| 50 media::VideoCodecProfile GetVEASupportedProfile( | |
|
mcasas
2016/05/18 22:22:58
nit: s/GetVEASupportedProfile/CodecIdToVEAProfile/
emircan
2016/05/21 00:01:17
Done.
| |
| 51 content::VideoTrackRecorder::CodecId codec) { | |
| 52 content::RenderThreadImpl* render_thread_impl = | |
| 53 content::RenderThreadImpl::current(); | |
| 54 if (!render_thread_impl) | |
| 55 return media::VIDEO_CODEC_PROFILE_UNKNOWN; | |
| 56 | |
| 57 media::GpuVideoAcceleratorFactories* gpu_factories = | |
| 58 content::RenderThreadImpl::current()->GetGpuFactories(); | |
| 59 if (!gpu_factories || !gpu_factories->IsGpuVideoAcceleratorEnabled()) { | |
| 60 DVLOG(3) << "Couldn't initialize GpuVideoAcceleratorFactories"; | |
| 61 return media::VIDEO_CODEC_PROFILE_UNKNOWN; | |
| 62 } | |
| 63 | |
| 64 const media::VideoEncodeAccelerator::SupportedProfiles& profiles = | |
| 65 gpu_factories->GetVideoEncodeAcceleratorSupportedProfiles(); | |
| 66 for (const auto& profile : profiles) { | |
| 67 if ((codec == content::VideoTrackRecorder::CodecId::VP8 && | |
| 68 profile.profile >= media::VP8PROFILE_MIN && | |
| 69 profile.profile <= media::VP8PROFILE_MAX) || | |
| 70 (codec == content::VideoTrackRecorder::CodecId::VP9 && | |
| 71 profile.profile >= media::VP9PROFILE_MIN && | |
| 72 profile.profile <= media::VP9PROFILE_MAX) || | |
| 73 (codec == content::VideoTrackRecorder::CodecId::H264 && | |
| 74 profile.profile >= media::H264PROFILE_MIN && | |
| 75 profile.profile <= media::H264PROFILE_MAX)) | |
| 76 return profile.profile; | |
| 77 } | |
| 78 return media::VIDEO_CODEC_PROFILE_UNKNOWN; | |
|
mcasas
2016/05/18 22:22:58
Just some side nit, if you could refactor this blo
emircan
2016/05/21 00:01:17
Done.
| |
| 79 } | |
| 80 | |
| 81 } // anonymous namespace | |
| 82 | |
| 37 namespace content { | 83 namespace content { |
| 38 | 84 |
| 39 // Base class to describe a generic Encoder, encapsulating all actual encoder | 85 // Base class to describe a generic Encoder, encapsulating all actual encoder |
| 40 // (re)configurations, encoding and delivery of received frames. This class is | 86 // (re)configurations, encoding and delivery of received frames. This class is |
| 41 // ref-counted to allow the MediaStreamVideoTrack to hold a reference to it (via | 87 // ref-counted to allow the MediaStreamVideoTrack to hold a reference to it (via |
| 42 // the callback that MediaStreamVideoSink passes along) and to jump back and | 88 // the callback that MediaStreamVideoSink passes along) and to jump back and |
| 43 // forth to an internal encoder thread. Moreover, this class: | 89 // forth to an internal encoder thread. Moreover, this class: |
| 44 // - is created and destroyed on its parent's thread (usually the main Render | 90 // - is created and destroyed on its parent's thread (usually the main Render |
| 45 // thread), |main_task_runner_|. | 91 // thread), |main_task_runner_|. |
| 46 // - receives VideoFrames on |origin_task_runner_| and runs OnEncodedVideoCB on | 92 // - receives VideoFrames on |origin_task_runner_| and runs OnEncodedVideoCB on |
| 47 // that thread as well. This task runner is cached on first frame arrival, and | 93 // that thread as well. This task runner is cached on first frame arrival, and |
| 48 // is supposed to be the render IO thread (but this is not enforced); | 94 // is supposed to be the render IO thread (but this is not enforced); |
| 49 // - uses an internal |encoding_task_runner_| for actual encoder interactions, | 95 // - uses an internal |encoding_task_runner_| for actual encoder interactions, |
| 50 // namely configuration, encoding (which might take some time) and destruction. | 96 // namely configuration, encoding (which might take some time) and destruction. |
| 51 // This task runner can be passed on the creation. If nothing is passed, a new | 97 // This task runner can be passed on the creation. If nothing is passed, a new |
| 52 // encoding thread is created and used. | 98 // encoding thread is created and used. |
| 53 class VideoTrackRecorder::Encoder : public base::RefCountedThreadSafe<Encoder> { | 99 class VideoTrackRecorder::Encoder : public base::RefCountedThreadSafe<Encoder> { |
| 54 public: | 100 public: |
| 55 Encoder(const OnEncodedVideoCB& on_encoded_video_callback, | 101 Encoder(const OnEncodedVideoCB& on_encoded_video_callback, |
| 56 int32_t bits_per_second, | 102 int32_t bits_per_second, |
| 57 scoped_refptr<base::SingleThreadTaskRunner> encoding_task_runner = | 103 scoped_refptr<base::SingleThreadTaskRunner> encoding_task_runner = |
| 58 nullptr) | 104 nullptr) |
| 59 : main_task_runner_(base::MessageLoop::current()->task_runner()), | 105 : main_task_runner_(base::MessageLoop::current()->task_runner()), |
| 60 encoding_task_runner_(encoding_task_runner), | 106 encoding_task_runner_(encoding_task_runner), |
| 61 paused_(false), | 107 paused_(false), |
| 62 on_encoded_video_callback_(on_encoded_video_callback), | 108 on_encoded_video_callback_(on_encoded_video_callback), |
| 63 bits_per_second_(bits_per_second) { | 109 bits_per_second_(bits_per_second) { |
| 64 DCHECK(!on_encoded_video_callback_.is_null()); | 110 DCHECK(!on_encoded_video_callback_.is_null()); |
| 65 if (encoding_thread_) | 111 if (encoding_task_runner_) |
| 66 return; | 112 return; |
| 67 encoding_thread_.reset(new base::Thread("EncodingThread")); | 113 encoding_thread_.reset(new base::Thread("EncodingThread")); |
| 68 encoding_thread_->Start(); | 114 encoding_thread_->Start(); |
| 69 encoding_task_runner_ = encoding_thread_->task_runner(); | 115 encoding_task_runner_ = encoding_thread_->task_runner(); |
| 70 } | 116 } |
| 71 | 117 |
| 72 // Start encoding |frame|, returning via |on_encoded_video_callback_|. This | 118 // Start encoding |frame|, returning via |on_encoded_video_callback_|. This |
| 73 // call will also trigger a ConfigureEncoderOnEncodingTaskRunner() upon first | 119 // call will also trigger a ConfigureEncoderOnEncodingTaskRunner() upon first |
| 74 // frame arrival or parameter change, and an EncodeOnEncodingTaskRunner() to | 120 // frame arrival or parameter change, and an EncodeOnEncodingTaskRunner() to |
| 75 // actually encode the frame. | 121 // actually encode the frame. |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 172 on_encoded_video_cb.Run(frame, std::move(data), capture_timestamp, keyframe); | 218 on_encoded_video_cb.Run(frame, std::move(data), capture_timestamp, keyframe); |
| 173 } | 219 } |
| 174 | 220 |
| 175 static int GetNumberOfThreadsForEncoding() { | 221 static int GetNumberOfThreadsForEncoding() { |
| 176 // Do not saturate CPU utilization just for encoding. On a lower-end system | 222 // Do not saturate CPU utilization just for encoding. On a lower-end system |
| 177 // with only 1 or 2 cores, use only one thread for encoding. On systems with | 223 // with only 1 or 2 cores, use only one thread for encoding. On systems with |
| 178 // more cores, allow half of the cores to be used for encoding. | 224 // more cores, allow half of the cores to be used for encoding. |
| 179 return std::min(8, (base::SysInfo::NumberOfProcessors() + 1) / 2); | 225 return std::min(8, (base::SysInfo::NumberOfProcessors() + 1) / 2); |
| 180 } | 226 } |
| 181 | 227 |
| 228 // Class encapsulating VideoEncodeAccelerator interactions. | |
| 229 class VEAEncoder final : public VideoTrackRecorder::Encoder, | |
|
mcasas
2016/05/18 22:22:58
Could you comment on the threading here plz?
I se
emircan
2016/05/21 00:01:17
Done.
| |
| 230 public media::VideoEncodeAccelerator::Client { | |
| 231 public: | |
| 232 VEAEncoder( | |
| 233 const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback, | |
| 234 int32_t bits_per_second, | |
| 235 media::VideoCodecProfile codec); | |
| 236 | |
| 237 // media::VideoEncodeAccelerator::Client implementation. | |
| 238 void RequireBitstreamBuffers(unsigned int input_count, | |
| 239 const gfx::Size& input_coded_size, | |
| 240 size_t output_buffer_size) override; | |
| 241 void BitstreamBufferReady(int32_t bitstream_buffer_id, | |
| 242 size_t payload_size, | |
| 243 bool key_frame) override; | |
| 244 void NotifyError(media::VideoEncodeAccelerator::Error error) override; | |
| 245 | |
| 246 private: | |
| 247 void UseOutputBitstreamBufferId(int32_t bitstream_buffer_id); | |
| 248 void FrameFinished(std::unique_ptr<base::SharedMemory> shm); | |
| 249 | |
| 250 // VideoTrackRecorder::Encoder | |
|
mcasas
2016/05/18 22:22:58
... implementation
and that should only apply to t
emircan
2016/05/21 00:01:17
See l.303 and 355. I think it should apply to dtor
| |
| 251 ~VEAEncoder() override; | |
| 252 void EncodeOnEncodingTaskRunner(const scoped_refptr<VideoFrame>& frame, | |
| 253 base::TimeTicks capture_timestamp) override; | |
| 254 void ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size) override; | |
| 255 | |
| 256 media::GpuVideoAcceleratorFactories* const gpu_factories_; | |
| 257 | |
| 258 media::VideoCodecProfile codec_; | |
|
mcasas
2016/05/18 22:22:58
const?
emircan
2016/05/21 00:01:17
Done.
| |
| 259 | |
| 260 // The underlying VEA to perform encoding on. | |
| 261 std::unique_ptr<media::VideoEncodeAccelerator> video_encoder_; | |
| 262 | |
| 263 // Shared memory buffers for output with the VEA. | |
| 264 ScopedVector<base::SharedMemory> output_buffers_; | |
|
mcasas
2016/05/18 22:22:58
ScopedVector is deprecated [1], instead, use
std::
emircan
2016/05/21 00:01:17
Done.
| |
| 265 | |
| 266 // Shared memory buffers for output with the VEA as FIFO. | |
| 267 std::queue<std::unique_ptr<base::SharedMemory>> input_buffers_; | |
| 268 | |
| 269 // The number of output buffers ready to be filled. | |
| 270 int output_buffers_free_count_; | |
| 271 | |
| 272 // Tracks error status. | |
| 273 bool error_notified_; | |
| 274 | |
| 275 // Tracks first encode frame and its usage. | |
| 276 std::unique_ptr<std::pair<scoped_refptr<VideoFrame>, base::TimeTicks>> | |
|
mcasas
2016/05/18 22:22:58
I suggest
using VideoFrameAndTimestamp
= st
emircan
2016/05/21 00:01:18
Done.
| |
| 277 first_frame_; | |
| 278 bool first_frame_encoded_; | |
| 279 | |
| 280 // Size used to initialize encoder. | |
| 281 gfx::Size input_size_; | |
| 282 | |
| 283 // Coded size that encoder requests as input. | |
| 284 gfx::Size vea_requested_input_size_; | |
| 285 | |
| 286 // Frames and corresponding timestamps in encode as FIFO. | |
| 287 std::queue<std::pair<scoped_refptr<VideoFrame>, base::TimeTicks>> | |
| 288 frames_in_encode_; | |
| 289 }; | |
| 290 | |
| 182 // Class encapsulating all libvpx interactions for VP8/VP9 encoding. | 291 // Class encapsulating all libvpx interactions for VP8/VP9 encoding. |
| 183 class VpxEncoder final : public VideoTrackRecorder::Encoder { | 292 class VpxEncoder final : public VideoTrackRecorder::Encoder { |
| 184 public: | 293 public: |
| 185 static void ShutdownEncoder(std::unique_ptr<base::Thread> encoding_thread, | 294 static void ShutdownEncoder(std::unique_ptr<base::Thread> encoding_thread, |
| 186 ScopedVpxCodecCtxPtr encoder); | 295 ScopedVpxCodecCtxPtr encoder); |
| 187 | 296 |
| 188 VpxEncoder( | 297 VpxEncoder( |
| 189 bool use_vp9, | 298 bool use_vp9, |
| 190 const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback, | 299 const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback, |
| 191 int32_t bits_per_second); | 300 int32_t bits_per_second); |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 257 | 366 |
| 258 // The |VideoFrame::timestamp()| of the first received frame. Only used on | 367 // The |VideoFrame::timestamp()| of the first received frame. Only used on |
| 259 // |encoding_thread_|. | 368 // |encoding_thread_|. |
| 260 base::TimeTicks first_frame_timestamp_; | 369 base::TimeTicks first_frame_timestamp_; |
| 261 | 370 |
| 262 DISALLOW_COPY_AND_ASSIGN(H264Encoder); | 371 DISALLOW_COPY_AND_ASSIGN(H264Encoder); |
| 263 }; | 372 }; |
| 264 | 373 |
| 265 #endif // #if BUILDFLAG(RTC_USE_H264) | 374 #endif // #if BUILDFLAG(RTC_USE_H264) |
| 266 | 375 |
| 376 VEAEncoder::VEAEncoder( | |
| 377 const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback, | |
| 378 int32_t bits_per_second, | |
| 379 media::VideoCodecProfile codec) | |
| 380 : Encoder(on_encoded_video_callback, | |
| 381 bits_per_second, | |
| 382 RenderThreadImpl::current()->GetGpuFactories()->GetTaskRunner()), | |
| 383 gpu_factories_(RenderThreadImpl::current()->GetGpuFactories()), | |
| 384 codec_(codec), | |
| 385 output_buffers_free_count_(0), | |
| 386 error_notified_(false), | |
| 387 first_frame_encoded_(false) { | |
| 388 DCHECK(gpu_factories_); | |
| 389 } | |
| 390 | |
| 391 VEAEncoder::~VEAEncoder() { | |
| 392 encoding_task_runner_->PostTask( | |
| 393 FROM_HERE, base::Bind(&media::VideoEncodeAccelerator::Destroy, | |
| 394 base::Unretained(video_encoder_.release()))); | |
| 395 } | |
| 396 | |
| 397 void VEAEncoder::RequireBitstreamBuffers(unsigned int input_count, | |
|
mcasas
2016/05/18 22:22:58
Shouldn't we at least
DCHECK_GE(kVEAEncoderOutputB
emircan
2016/05/21 00:01:17
Not really as those two aren't related. I am comme
| |
| 398 const gfx::Size& input_coded_size, | |
| 399 size_t output_buffer_size) { | |
| 400 DVLOG(3) << __FUNCTION__; | |
| 401 DCHECK(encoding_task_runner_->BelongsToCurrentThread()); | |
| 402 | |
| 403 vea_requested_input_size_ = input_coded_size; | |
| 404 output_buffers_.clear(); | |
| 405 output_buffers_free_count_ = 0; | |
| 406 std::queue<std::unique_ptr<base::SharedMemory>>().swap(input_buffers_); | |
| 407 | |
| 408 for (int i = 0; i < kVEAEncoderOutputBufferCount; ++i) { | |
| 409 DCHECK(gpu_factories_); | |
|
mcasas
2016/05/18 22:22:58
Needed? It's already checked in ctor l.388?
emircan
2016/05/21 00:01:18
Done.
| |
| 410 std::unique_ptr<base::SharedMemory> shm = | |
| 411 gpu_factories_->CreateSharedMemory(output_buffer_size); | |
| 412 if (!shm) { | |
| 413 NOTREACHED(); | |
| 414 return; | |
| 415 } | |
| 416 output_buffers_.push_back(shm.release()); | |
| 417 } | |
| 418 | |
| 419 for (size_t i = 0; i < output_buffers_.size(); ++i) { | |
| 420 video_encoder_->UseOutputBitstreamBuffer(media::BitstreamBuffer( | |
| 421 i, output_buffers_[i]->handle(), output_buffers_[i]->mapped_size())); | |
| 422 output_buffers_free_count_++; | |
| 423 } | |
| 424 } | |
| 425 | |
| 426 void VEAEncoder::BitstreamBufferReady(int32_t bitstream_buffer_id, | |
| 427 size_t payload_size, | |
| 428 bool keyframe) { | |
| 429 DVLOG(3) << __FUNCTION__; | |
| 430 DCHECK(encoding_task_runner_->BelongsToCurrentThread()); | |
| 431 | |
| 432 output_buffers_free_count_--; | |
| 433 base::SharedMemory* output_buffer = output_buffers_[bitstream_buffer_id]; | |
| 434 | |
| 435 std::unique_ptr<std::string> data(new std::string); | |
| 436 data->append(reinterpret_cast<char*>(output_buffer->memory()), payload_size); | |
| 437 | |
| 438 const auto front_frame = frames_in_encode_.front(); | |
| 439 frames_in_encode_.pop(); | |
| 440 origin_task_runner_->PostTask( | |
| 441 FROM_HERE, base::Bind(OnFrameEncodeCompleted, on_encoded_video_callback_, | |
| 442 front_frame.first, base::Passed(&data), | |
| 443 front_frame.second, keyframe)); | |
| 444 UseOutputBitstreamBufferId(bitstream_buffer_id); | |
| 445 } | |
| 446 | |
| 447 void VEAEncoder::NotifyError(media::VideoEncodeAccelerator::Error error) { | |
| 448 DVLOG(3) << __FUNCTION__; | |
| 449 DCHECK(encoding_task_runner_->BelongsToCurrentThread()); | |
| 450 | |
| 451 error_notified_ = true; | |
|
mcasas
2016/05/18 22:22:58
It's unfortunate we don't have a callback
of sorts
emircan
2016/05/21 00:01:18
Done.
| |
| 452 } | |
| 453 | |
| 454 void VEAEncoder::UseOutputBitstreamBufferId(int32_t bitstream_buffer_id) { | |
| 455 DVLOG(3) << __FUNCTION__; | |
| 456 DCHECK(encoding_task_runner_->BelongsToCurrentThread()); | |
| 457 | |
| 458 video_encoder_->UseOutputBitstreamBuffer(media::BitstreamBuffer( | |
| 459 bitstream_buffer_id, output_buffers_[bitstream_buffer_id]->handle(), | |
| 460 output_buffers_[bitstream_buffer_id]->mapped_size())); | |
| 461 output_buffers_free_count_++; | |
| 462 } | |
| 463 | |
| 464 void VEAEncoder::FrameFinished(std::unique_ptr<base::SharedMemory> shm) { | |
| 465 DVLOG(3) << __FUNCTION__; | |
| 466 DCHECK(encoding_task_runner_->BelongsToCurrentThread()); | |
| 467 input_buffers_.push(std::move(shm)); | |
| 468 } | |
| 469 | |
| 470 void VEAEncoder::EncodeOnEncodingTaskRunner( | |
| 471 const scoped_refptr<VideoFrame>& frame, | |
| 472 base::TimeTicks capture_timestamp) { | |
| 473 DVLOG(3) << __FUNCTION__; | |
| 474 DCHECK(encoding_task_runner_->BelongsToCurrentThread()); | |
| 475 | |
| 476 if (input_size_ != frame->visible_rect().size() && video_encoder_) { | |
| 477 video_encoder_->Destroy(); | |
| 478 video_encoder_.reset(); | |
| 479 } | |
| 480 | |
| 481 if (!video_encoder_) { | |
| 482 ConfigureEncoderOnEncodingTaskRunner(frame->visible_rect().size()); | |
| 483 first_frame_.reset( | |
|
mcasas
2016/05/18 22:22:58
So, if we hypothetically were to receive
a few |fr
emircan
2016/05/21 00:01:18
Done.
| |
| 484 new std::pair<scoped_refptr<VideoFrame>, base::TimeTicks>( | |
| 485 frame, capture_timestamp)); | |
| 486 } | |
| 487 | |
| 488 if (error_notified_) { | |
| 489 DVLOG(3) << "An error occurred in VEA encoder"; | |
|
mcasas
2016/05/18 22:22:58
Would it be a good idea to destroy the
|video_enco
emircan
2016/05/21 00:01:18
We had that logic in place for decoder. I will lea
| |
| 490 return; | |
| 491 } | |
| 492 | |
| 493 // Drop frames if there is no output buffers available. | |
| 494 if (output_buffers_free_count_ == 0) { | |
| 495 DVLOG(3) << "Dropped frame."; | |
| 496 return; | |
| 497 } | |
| 498 | |
| 499 // If first frame hasn't been encoded, do it first. | |
| 500 if (!first_frame_encoded_ && first_frame_) { | |
| 501 std::unique_ptr<std::pair<scoped_refptr<VideoFrame>, base::TimeTicks>> | |
| 502 first_frame(first_frame_.release()); | |
| 503 EncodeOnEncodingTaskRunner(first_frame->first, first_frame->second); | |
|
mcasas
2016/05/18 22:22:58
I don't understand, we recursively call
ourselves
emircan
2016/05/21 00:01:17
Second time around it wouldn't fall here since |fi
| |
| 504 first_frame_encoded_ = true; | |
|
mcasas
2016/05/18 22:22:58
Couldn't we drop |first_frame_encoded_| and
instea
emircan
2016/05/21 00:01:17
Done.
| |
| 505 } | |
| 506 | |
| 507 // Lower resolutions may fall back to SW encoder in some platforms, i.e. Mac. | |
| 508 // In that case, the encoder expects more frames before returning result. | |
| 509 // Therefore, a copy is necessary to release the current frame. | |
| 510 scoped_refptr<media::VideoFrame> video_frame = frame; | |
| 511 if (vea_requested_input_size_ != input_size_ || | |
| 512 input_size_.width() < kVEAEncoderMinResolutionWidth || | |
| 513 input_size_.height() < kVEAEncoderMinResolutionHeight) { | |
| 514 // Create SharedMemory backed input buffers as necessary. These SharedMemory | |
| 515 // instances will be shared with GPU process. | |
| 516 if (input_buffers_.empty()) { | |
| 517 std::unique_ptr<base::SharedMemory> shm = | |
| 518 gpu_factories_->CreateSharedMemory(media::VideoFrame::AllocationSize( | |
| 519 media::PIXEL_FORMAT_I420, vea_requested_input_size_)); | |
| 520 input_buffers_.push(std::move(shm)); | |
| 521 } | |
| 522 std::unique_ptr<base::SharedMemory> input_buffer = | |
| 523 std::move(input_buffers_.front()); | |
| 524 input_buffers_.pop(); | |
|
mcasas
2016/05/18 22:22:58
Suggestion of alternative for l.516-524 ?
st
emircan
2016/05/21 00:01:17
Done.
| |
| 525 while (input_buffer->mapped_size() < | |
| 526 media::VideoFrame::AllocationSize(media::PIXEL_FORMAT_I420, | |
| 527 vea_requested_input_size_)) { | |
| 528 input_buffer.reset(input_buffers_.front().release()); | |
| 529 input_buffers_.pop(); | |
| 530 } | |
| 531 video_frame = media::VideoFrame::WrapExternalSharedMemory( | |
| 532 media::PIXEL_FORMAT_I420, vea_requested_input_size_, | |
| 533 gfx::Rect(input_size_), input_size_, | |
| 534 reinterpret_cast<uint8_t*>(input_buffer->memory()), | |
| 535 input_buffer->mapped_size(), input_buffer->handle(), 0, | |
| 536 frame->timestamp()); | |
| 537 video_frame->AddDestructionObserver(media::BindToCurrentLoop( | |
| 538 base::Bind(&VEAEncoder::FrameFinished, this, | |
| 539 base::Passed(std::move(input_buffer))))); | |
| 540 libyuv::I420Copy(frame->visible_data(media::VideoFrame::kYPlane), | |
| 541 frame->stride(media::VideoFrame::kYPlane), | |
| 542 frame->visible_data(media::VideoFrame::kUPlane), | |
| 543 frame->stride(media::VideoFrame::kUPlane), | |
| 544 frame->visible_data(media::VideoFrame::kVPlane), | |
| 545 frame->stride(media::VideoFrame::kVPlane), | |
| 546 video_frame->visible_data(media::VideoFrame::kYPlane), | |
| 547 video_frame->stride(media::VideoFrame::kYPlane), | |
| 548 video_frame->visible_data(media::VideoFrame::kUPlane), | |
| 549 video_frame->stride(media::VideoFrame::kUPlane), | |
| 550 video_frame->visible_data(media::VideoFrame::kVPlane), | |
| 551 video_frame->stride(media::VideoFrame::kVPlane), | |
| 552 input_size_.width(), input_size_.height()); | |
| 553 } | |
| 554 frames_in_encode_.push(std::make_pair(video_frame, capture_timestamp)); | |
| 555 | |
| 556 encoding_task_runner_->PostTask( | |
| 557 FROM_HERE, base::Bind(&media::VideoEncodeAccelerator::Encode, | |
| 558 base::Unretained(video_encoder_.get()), video_frame, | |
| 559 first_frame_encoded_ ? false : true)); | |
| 560 } | |
| 561 | |
| 562 void VEAEncoder::ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size) { | |
| 563 DVLOG(3) << __FUNCTION__; | |
| 564 DCHECK(encoding_task_runner_->BelongsToCurrentThread()); | |
| 565 DCHECK(gpu_factories_->GetTaskRunner()->BelongsToCurrentThread()); | |
| 566 | |
| 567 input_size_ = size; | |
| 568 video_encoder_ = gpu_factories_->CreateVideoEncodeAccelerator(); | |
| 569 if (!video_encoder_) { | |
| 570 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError); | |
| 571 return; | |
| 572 } | |
| 573 if (!video_encoder_->Initialize(media::PIXEL_FORMAT_I420, input_size_, | |
|
mcasas
2016/05/18 22:22:58
Bundle these two if()s ?
emircan
2016/05/21 00:01:17
Done.
| |
| 574 codec_, bits_per_second_, this)) { | |
| 575 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError); | |
| 576 } | |
| 577 first_frame_encoded_ = false; | |
| 578 } | |
| 579 | |
| 267 // static | 580 // static |
| 268 void VpxEncoder::ShutdownEncoder(std::unique_ptr<base::Thread> encoding_thread, | 581 void VpxEncoder::ShutdownEncoder(std::unique_ptr<base::Thread> encoding_thread, |
| 269 ScopedVpxCodecCtxPtr encoder) { | 582 ScopedVpxCodecCtxPtr encoder) { |
| 270 DCHECK(encoding_thread->IsRunning()); | 583 DCHECK(encoding_thread->IsRunning()); |
| 271 encoding_thread->Stop(); | 584 encoding_thread->Stop(); |
| 272 // Both |encoding_thread| and |encoder| will be destroyed at end-of-scope. | 585 // Both |encoding_thread| and |encoder| will be destroyed at end-of-scope. |
| 273 } | 586 } |
| 274 | 587 |
| 275 VpxEncoder::VpxEncoder( | 588 VpxEncoder::VpxEncoder( |
| 276 bool use_vp9, | 589 bool use_vp9, |
| (...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 619 VideoTrackRecorder::VideoTrackRecorder( | 932 VideoTrackRecorder::VideoTrackRecorder( |
| 620 CodecId codec, | 933 CodecId codec, |
| 621 const blink::WebMediaStreamTrack& track, | 934 const blink::WebMediaStreamTrack& track, |
| 622 const OnEncodedVideoCB& on_encoded_video_callback, | 935 const OnEncodedVideoCB& on_encoded_video_callback, |
| 623 int32_t bits_per_second) | 936 int32_t bits_per_second) |
| 624 : track_(track) { | 937 : track_(track) { |
| 625 DCHECK(main_render_thread_checker_.CalledOnValidThread()); | 938 DCHECK(main_render_thread_checker_.CalledOnValidThread()); |
| 626 DCHECK(!track_.isNull()); | 939 DCHECK(!track_.isNull()); |
| 627 DCHECK(track_.getExtraData()); | 940 DCHECK(track_.getExtraData()); |
| 628 | 941 |
| 629 switch (codec) { | 942 const auto& vea_supported_profile = GetVEASupportedProfile(codec); |
| 943 // TODO(emircan): Prioritize software based encoders in lower resolutions. | |
| 944 if (vea_supported_profile != media::VIDEO_CODEC_PROFILE_UNKNOWN) { | |
| 945 encoder_ = new VEAEncoder(on_encoded_video_callback, bits_per_second, | |
| 946 vea_supported_profile); | |
| 947 } else { | |
| 948 switch (codec) { | |
| 630 #if BUILDFLAG(RTC_USE_H264) | 949 #if BUILDFLAG(RTC_USE_H264) |
| 631 case CodecId::H264: | 950 case CodecId::H264: |
| 632 encoder_ = new H264Encoder(on_encoded_video_callback, bits_per_second); | 951 encoder_ = new H264Encoder(on_encoded_video_callback, bits_per_second); |
| 633 break; | 952 break; |
| 634 #endif | 953 #endif |
| 635 case CodecId::VP8: | 954 case CodecId::VP8: |
| 636 case CodecId::VP9: | 955 case CodecId::VP9: |
| 637 encoder_ = new VpxEncoder(codec == CodecId::VP9, | 956 encoder_ = new VpxEncoder(codec == CodecId::VP9, |
| 638 on_encoded_video_callback, bits_per_second); | 957 on_encoded_video_callback, bits_per_second); |
| 639 break; | 958 break; |
| 640 default: | 959 default: |
| 641 NOTREACHED() << "Unsupported codec"; | 960 NOTREACHED() << "Unsupported codec"; |
| 961 } | |
| 642 } | 962 } |
| 643 | 963 |
| 644 // StartFrameEncode() will be called on Render IO thread. | 964 // StartFrameEncode() will be called on Render IO thread. |
| 645 MediaStreamVideoSink::ConnectToTrack( | 965 MediaStreamVideoSink::ConnectToTrack( |
| 646 track_, | 966 track_, |
| 647 base::Bind(&VideoTrackRecorder::Encoder::StartFrameEncode, encoder_), | 967 base::Bind(&VideoTrackRecorder::Encoder::StartFrameEncode, encoder_), |
| 648 false); | 968 false); |
| 649 } | 969 } |
| 650 | 970 |
| 651 VideoTrackRecorder::~VideoTrackRecorder() { | 971 VideoTrackRecorder::~VideoTrackRecorder() { |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 666 encoder_->SetPaused(false); | 986 encoder_->SetPaused(false); |
| 667 } | 987 } |
| 668 | 988 |
| 669 void VideoTrackRecorder::OnVideoFrameForTesting( | 989 void VideoTrackRecorder::OnVideoFrameForTesting( |
| 670 const scoped_refptr<media::VideoFrame>& frame, | 990 const scoped_refptr<media::VideoFrame>& frame, |
| 671 base::TimeTicks timestamp) { | 991 base::TimeTicks timestamp) { |
| 672 encoder_->StartFrameEncode(frame, timestamp); | 992 encoder_->StartFrameEncode(frame, timestamp); |
| 673 } | 993 } |
| 674 | 994 |
| 675 } // namespace content | 995 } // namespace content |
| OLD | NEW |