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

Unified Diff: content/renderer/media/video_track_recorder.cc

Issue 1990643002: Add VEAEncoder to VideoTrackRecorder (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: mcasas@ comments. Created 4 years, 7 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/renderer/media/video_track_recorder.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/renderer/media/video_track_recorder.cc
diff --git a/content/renderer/media/video_track_recorder.cc b/content/renderer/media/video_track_recorder.cc
index 8da8f3084922aa6e5cd6d32e9b3a918183e05ddf..29fcaedee7bd0e4ad8e1d799c5ee9064ce32e3a3 100644
--- a/content/renderer/media/video_track_recorder.cc
+++ b/content/renderer/media/video_track_recorder.cc
@@ -10,11 +10,16 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/sys_info.h"
+#include "base/task_runner_util.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
+#include "content/renderer/media/renderer_gpu_video_accelerator_factories.h"
+#include "content/renderer/render_thread_impl.h"
+#include "media/base/bind_to_current_loop.h"
#include "media/base/video_frame.h"
#include "media/base/video_util.h"
+#include "third_party/libyuv/include/libyuv/convert.h"
#include "ui/gfx/geometry/size.h"
#if BUILDFLAG(RTC_USE_H264)
@@ -36,6 +41,58 @@ using media::VideoFrameMetadata;
namespace content {
+namespace {
+
+const int kVEAEncoderMinResolutionWidth = 640;
+const int kVEAEncoderMinResolutionHeight = 480;
+const int kVEAEncoderOutputBufferCount = 4;
+
+static struct {
+ VideoTrackRecorder::CodecId codec_id;
+ media::VideoCodecProfile min_profile;
+ media::VideoCodecProfile max_profile;
+} const kSupportedVideoCodecIdToProfile[] = {
+ {VideoTrackRecorder::CodecId::VP8,
+ media::VP8PROFILE_MIN,
+ media::VP8PROFILE_MAX},
+ {VideoTrackRecorder::CodecId::VP9,
+ media::VP9PROFILE_MIN,
+ media::VP9PROFILE_MAX},
+ {VideoTrackRecorder::CodecId::H264,
+ media::H264PROFILE_MIN,
+ media::H264PROFILE_MAX}};
+
+// Returns the corresponding codec profile from VEA supported codecs. If no
+// profile is found, returns VIDEO_CODEC_PROFILE_UNKNOWN.
+media::VideoCodecProfile CodecIdToVEAProfile(
+ content::VideoTrackRecorder::CodecId codec) {
+ content::RenderThreadImpl* render_thread_impl =
+ content::RenderThreadImpl::current();
+ if (!render_thread_impl)
+ return media::VIDEO_CODEC_PROFILE_UNKNOWN;
+
+ media::GpuVideoAcceleratorFactories* gpu_factories =
+ content::RenderThreadImpl::current()->GetGpuFactories();
+ if (!gpu_factories || !gpu_factories->IsGpuVideoAcceleratorEnabled()) {
+ DVLOG(3) << "Couldn't initialize GpuVideoAcceleratorFactories";
+ return media::VIDEO_CODEC_PROFILE_UNKNOWN;
+ }
+
+ const media::VideoEncodeAccelerator::SupportedProfiles& vea_profiles =
+ gpu_factories->GetVideoEncodeAcceleratorSupportedProfiles();
+ for (const auto& vea_profile : vea_profiles) {
+ for (const auto& supported_profile : kSupportedVideoCodecIdToProfile) {
+ if (codec == supported_profile.codec_id &&
+ vea_profile.profile >= supported_profile.min_profile &&
+ vea_profile.profile <= supported_profile.max_profile)
+ return vea_profile.profile;
+ }
+ }
+ return media::VIDEO_CODEC_PROFILE_UNKNOWN;
+}
+
+} // anonymous namespace
+
// Base class to describe a generic Encoder, encapsulating all actual encoder
// (re)configurations, encoding and delivery of received frames. This class is
// ref-counted to allow the MediaStreamVideoTrack to hold a reference to it (via
@@ -62,7 +119,7 @@ class VideoTrackRecorder::Encoder : public base::RefCountedThreadSafe<Encoder> {
on_encoded_video_callback_(on_encoded_video_callback),
bits_per_second_(bits_per_second) {
DCHECK(!on_encoded_video_callback_.is_null());
- if (encoding_thread_)
+ if (encoding_task_runner_)
return;
encoding_thread_.reset(new base::Thread("EncodingThread"));
encoding_thread_->Start();
@@ -179,6 +236,68 @@ static int GetNumberOfThreadsForEncoding() {
return std::min(8, (base::SysInfo::NumberOfProcessors() + 1) / 2);
}
+// Class encapsulating VideoEncodeAccelerator interactions.
+// This class is created and destroyed in its owner thread. All other methods
+// operate on the task runner pointed by GpuFactories.
+class VEAEncoder final : public VideoTrackRecorder::Encoder,
+ public media::VideoEncodeAccelerator::Client {
+ public:
+ VEAEncoder(
+ const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback,
+ int32_t bits_per_second,
+ media::VideoCodecProfile codec);
+
+ // media::VideoEncodeAccelerator::Client implementation.
+ void RequireBitstreamBuffers(unsigned int input_count,
+ const gfx::Size& input_coded_size,
+ size_t output_buffer_size) override;
+ void BitstreamBufferReady(int32_t bitstream_buffer_id,
+ size_t payload_size,
+ bool key_frame) override;
+ void NotifyError(media::VideoEncodeAccelerator::Error error) override;
+
+ private:
+ using VideoFrameAndTimestamp =
+ std::pair<scoped_refptr<VideoFrame>, base::TimeTicks>;
+
+ void UseOutputBitstreamBufferId(int32_t bitstream_buffer_id);
+ void FrameFinished(std::unique_ptr<base::SharedMemory> shm);
+
+ // VideoTrackRecorder::Encoder implementation.
+ ~VEAEncoder() override;
+ void EncodeOnEncodingTaskRunner(const scoped_refptr<VideoFrame>& frame,
+ base::TimeTicks capture_timestamp) override;
+ void ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size) override;
+
+ media::GpuVideoAcceleratorFactories* const gpu_factories_;
+
+ const media::VideoCodecProfile codec_;
+
+ // The underlying VEA to perform encoding on.
+ std::unique_ptr<media::VideoEncodeAccelerator> video_encoder_;
+
+ // Shared memory buffers for output with the VEA.
+ std::vector<std::unique_ptr<base::SharedMemory>> output_buffers_;
+
+ // Shared memory buffers for output with the VEA as FIFO.
+ std::queue<std::unique_ptr<base::SharedMemory>> input_buffers_;
+
+ // Tracks error status.
+ bool error_notified_;
+
+ // Tracks the first frame to encode.
+ std::unique_ptr<VideoFrameAndTimestamp> first_frame_;
+
+ // Size used to initialize encoder.
+ gfx::Size input_size_;
+
+ // Coded size that encoder requests as input.
+ gfx::Size vea_requested_input_size_;
+
+ // Frames and corresponding timestamps in encode as FIFO.
+ std::queue<VideoFrameAndTimestamp> frames_in_encode_;
+};
+
// Class encapsulating all libvpx interactions for VP8/VP9 encoding.
class VpxEncoder final : public VideoTrackRecorder::Encoder {
public:
@@ -191,10 +310,10 @@ class VpxEncoder final : public VideoTrackRecorder::Encoder {
int32_t bits_per_second);
private:
- // VideoTrackRecorder::Encoder
+ // VideoTrackRecorder::Encoder implementation.
~VpxEncoder() override;
void EncodeOnEncodingTaskRunner(const scoped_refptr<VideoFrame>& frame,
- base::TimeTicks capture_timestamp) override;
+ base::TimeTicks capture_timestamp) override;
void ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size) override;
// Returns true if |codec_config_| has been filled in at least once.
@@ -243,7 +362,7 @@ class H264Encoder final : public VideoTrackRecorder::Encoder {
int32_t bits_per_second);
private:
- // VideoTrackRecorder::Encoder
+ // VideoTrackRecorder::Encoder implementation.
~H264Encoder() override;
void EncodeOnEncodingTaskRunner(const scoped_refptr<VideoFrame>& frame,
base::TimeTicks capture_timestamp) override;
@@ -264,6 +383,195 @@ class H264Encoder final : public VideoTrackRecorder::Encoder {
#endif // #if BUILDFLAG(RTC_USE_H264)
+VEAEncoder::VEAEncoder(
+ const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback,
+ int32_t bits_per_second,
+ media::VideoCodecProfile codec)
+ : Encoder(on_encoded_video_callback,
+ bits_per_second,
+ RenderThreadImpl::current()->GetGpuFactories()->GetTaskRunner()),
+ gpu_factories_(RenderThreadImpl::current()->GetGpuFactories()),
+ codec_(codec),
+ error_notified_(false) {
+ DCHECK(gpu_factories_);
+}
+
+VEAEncoder::~VEAEncoder() {
+ encoding_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&media::VideoEncodeAccelerator::Destroy,
+ base::Unretained(video_encoder_.release())));
+}
+
+void VEAEncoder::RequireBitstreamBuffers(unsigned int /*input_count*/,
+ const gfx::Size& input_coded_size,
+ size_t output_buffer_size) {
+ DVLOG(3) << __FUNCTION__;
+ DCHECK(encoding_task_runner_->BelongsToCurrentThread());
+
+ vea_requested_input_size_ = input_coded_size;
+ output_buffers_.clear();
+ std::queue<std::unique_ptr<base::SharedMemory>>().swap(input_buffers_);
+
+ for (int i = 0; i < kVEAEncoderOutputBufferCount; ++i) {
+ std::unique_ptr<base::SharedMemory> shm =
+ gpu_factories_->CreateSharedMemory(output_buffer_size);
+ if (shm)
+ output_buffers_.push_back(base::WrapUnique(shm.release()));
+ }
+
+ for (size_t i = 0; i < output_buffers_.size(); ++i)
+ UseOutputBitstreamBufferId(i);
+}
+
+void VEAEncoder::BitstreamBufferReady(int32_t bitstream_buffer_id,
+ size_t payload_size,
+ bool keyframe) {
+ DVLOG(3) << __FUNCTION__;
+ DCHECK(encoding_task_runner_->BelongsToCurrentThread());
+
+ base::SharedMemory* output_buffer =
+ output_buffers_[bitstream_buffer_id].get();
+
+ std::unique_ptr<std::string> data(new std::string);
+ data->append(reinterpret_cast<char*>(output_buffer->memory()), payload_size);
+
+ const auto front_frame = frames_in_encode_.front();
+ frames_in_encode_.pop();
+ origin_task_runner_->PostTask(
+ FROM_HERE, base::Bind(OnFrameEncodeCompleted, on_encoded_video_callback_,
+ front_frame.first, base::Passed(&data),
+ front_frame.second, keyframe));
+ UseOutputBitstreamBufferId(bitstream_buffer_id);
+}
+
+void VEAEncoder::NotifyError(media::VideoEncodeAccelerator::Error error) {
+ DVLOG(3) << __FUNCTION__;
+ DCHECK(encoding_task_runner_->BelongsToCurrentThread());
+
+ // TODO(emircan): Notify the owner via a callback.
+ error_notified_ = true;
+}
+
+void VEAEncoder::UseOutputBitstreamBufferId(int32_t bitstream_buffer_id) {
+ DVLOG(3) << __FUNCTION__;
+ DCHECK(encoding_task_runner_->BelongsToCurrentThread());
+
+ video_encoder_->UseOutputBitstreamBuffer(media::BitstreamBuffer(
+ bitstream_buffer_id, output_buffers_[bitstream_buffer_id]->handle(),
+ output_buffers_[bitstream_buffer_id]->mapped_size()));
+}
+
+void VEAEncoder::FrameFinished(std::unique_ptr<base::SharedMemory> shm) {
+ DVLOG(3) << __FUNCTION__;
+ DCHECK(encoding_task_runner_->BelongsToCurrentThread());
+ input_buffers_.push(std::move(shm));
+}
+
+void VEAEncoder::EncodeOnEncodingTaskRunner(
+ const scoped_refptr<VideoFrame>& frame,
+ base::TimeTicks capture_timestamp) {
+ DVLOG(3) << __FUNCTION__;
+ DCHECK(encoding_task_runner_->BelongsToCurrentThread());
+
+ if (input_size_ != frame->visible_rect().size() && video_encoder_) {
+ video_encoder_->Destroy();
+ video_encoder_.reset();
+ }
+
+ if (!video_encoder_) {
+ ConfigureEncoderOnEncodingTaskRunner(frame->visible_rect().size());
+ first_frame_.reset(
+ new std::pair<scoped_refptr<VideoFrame>, base::TimeTicks>(
+ frame, capture_timestamp));
+ }
+
+ if (error_notified_) {
+ DVLOG(3) << "An error occurred in VEA encoder";
+ return;
+ }
+
+ // Drop frames if there is no output buffers available.
+ if (output_buffers_.empty()) {
+ // TODO(emircan): Investigate if resetting encoder would help.
+ DVLOG(3) << "Dropped frame.";
+ return;
+ }
+
+ // If first frame hasn't been encoded, do it first.
+ if (first_frame_) {
+ std::unique_ptr<VideoFrameAndTimestamp> first_frame(first_frame_.release());
+ EncodeOnEncodingTaskRunner(first_frame->first, first_frame->second);
+ }
+
+ // Lower resolutions may fall back to SW encoder in some platforms, i.e. Mac.
+ // In that case, the encoder expects more frames before returning result.
+ // Therefore, a copy is necessary to release the current frame.
+ scoped_refptr<media::VideoFrame> video_frame = frame;
+ if (vea_requested_input_size_ != input_size_ ||
+ input_size_.width() < kVEAEncoderMinResolutionWidth ||
+ input_size_.height() < kVEAEncoderMinResolutionHeight) {
+ // Create SharedMemory backed input buffers as necessary. These SharedMemory
+ // instances will be shared with GPU process.
+ std::unique_ptr<base::SharedMemory> input_buffer;
+ const size_t desired_mapped_size = media::VideoFrame::AllocationSize(
+ media::PIXEL_FORMAT_I420, vea_requested_input_size_);
+ if (input_buffers_.empty()) {
+ input_buffer = gpu_factories_->CreateSharedMemory(desired_mapped_size);
+ } else {
+ do {
+ input_buffer = std::move(input_buffers_.front());
+ input_buffers_.pop();
+ } while (!input_buffers_.empty() &&
+ input_buffer->mapped_size() < desired_mapped_size);
+ if (!input_buffer || input_buffer->mapped_size() < desired_mapped_size)
+ return;
+ }
+
+ video_frame = media::VideoFrame::WrapExternalSharedMemory(
+ media::PIXEL_FORMAT_I420, vea_requested_input_size_,
+ gfx::Rect(input_size_), input_size_,
+ reinterpret_cast<uint8_t*>(input_buffer->memory()),
+ input_buffer->mapped_size(), input_buffer->handle(), 0,
+ frame->timestamp());
+ video_frame->AddDestructionObserver(media::BindToCurrentLoop(
+ base::Bind(&VEAEncoder::FrameFinished, this,
+ base::Passed(std::move(input_buffer)))));
+ libyuv::I420Copy(frame->visible_data(media::VideoFrame::kYPlane),
+ frame->stride(media::VideoFrame::kYPlane),
+ frame->visible_data(media::VideoFrame::kUPlane),
+ frame->stride(media::VideoFrame::kUPlane),
+ frame->visible_data(media::VideoFrame::kVPlane),
+ frame->stride(media::VideoFrame::kVPlane),
+ video_frame->visible_data(media::VideoFrame::kYPlane),
+ video_frame->stride(media::VideoFrame::kYPlane),
+ video_frame->visible_data(media::VideoFrame::kUPlane),
+ video_frame->stride(media::VideoFrame::kUPlane),
+ video_frame->visible_data(media::VideoFrame::kVPlane),
+ video_frame->stride(media::VideoFrame::kVPlane),
+ input_size_.width(), input_size_.height());
+ }
+ frames_in_encode_.push(std::make_pair(video_frame, capture_timestamp));
+
+ encoding_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&media::VideoEncodeAccelerator::Encode,
+ base::Unretained(video_encoder_.get()), video_frame, false));
+}
+
+void VEAEncoder::ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size) {
+ DVLOG(3) << __FUNCTION__;
+ DCHECK(encoding_task_runner_->BelongsToCurrentThread());
+ DCHECK(gpu_factories_->GetTaskRunner()->BelongsToCurrentThread());
+
+ input_size_ = size;
+ video_encoder_ = gpu_factories_->CreateVideoEncodeAccelerator();
+ if (!video_encoder_ ||
+ !video_encoder_->Initialize(media::PIXEL_FORMAT_I420, input_size_, codec_,
+ bits_per_second_, this)) {
+ NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
+ }
+}
+
// static
void VpxEncoder::ShutdownEncoder(std::unique_ptr<base::Thread> encoding_thread,
ScopedVpxCodecCtxPtr encoder) {
@@ -626,19 +934,26 @@ VideoTrackRecorder::VideoTrackRecorder(
DCHECK(!track_.isNull());
DCHECK(track_.getExtraData());
- switch (codec) {
+ const auto& vea_supported_profile = CodecIdToVEAProfile(codec);
+ // TODO(emircan): Prioritize software based encoders in lower resolutions.
+ if (vea_supported_profile != media::VIDEO_CODEC_PROFILE_UNKNOWN) {
+ encoder_ = new VEAEncoder(on_encoded_video_callback, bits_per_second,
+ vea_supported_profile);
+ } else {
+ switch (codec) {
#if BUILDFLAG(RTC_USE_H264)
- case CodecId::H264:
- encoder_ = new H264Encoder(on_encoded_video_callback, bits_per_second);
- break;
+ case CodecId::H264:
+ encoder_ = new H264Encoder(on_encoded_video_callback, bits_per_second);
+ break;
#endif
- case CodecId::VP8:
- case CodecId::VP9:
- encoder_ = new VpxEncoder(codec == CodecId::VP9,
- on_encoded_video_callback, bits_per_second);
- break;
- default:
- NOTREACHED() << "Unsupported codec";
+ case CodecId::VP8:
+ case CodecId::VP9:
+ encoder_ = new VpxEncoder(codec == CodecId::VP9,
+ on_encoded_video_callback, bits_per_second);
+ break;
+ default:
+ NOTREACHED() << "Unsupported codec";
+ }
}
// StartFrameEncode() will be called on Render IO thread.
« no previous file with comments | « content/renderer/media/video_track_recorder.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698