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

Unified Diff: content/renderer/media_recorder/vea_encoder.cc

Issue 2793303003: Refactor VideoTrackRecorder into smaller classes (Closed)
Patch Set: Rebase Created 3 years, 8 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
Index: content/renderer/media_recorder/vea_encoder.cc
diff --git a/content/renderer/media_recorder/vea_encoder.cc b/content/renderer/media_recorder/vea_encoder.cc
new file mode 100644
index 0000000000000000000000000000000000000000..6bff8128902f4273f1d105d572a37117489bdc06
--- /dev/null
+++ b/content/renderer/media_recorder/vea_encoder.cc
@@ -0,0 +1,247 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/media_recorder/vea_encoder.h"
+
+#include <string>
+
+#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 "third_party/libyuv/include/libyuv.h"
+#include "ui/gfx/geometry/size.h"
+
+using media::VideoFrame;
+using video_track_recorder::kVEAEncoderMinResolutionWidth;
+using video_track_recorder::kVEAEncoderMinResolutionHeight;
+
+namespace content {
+
+namespace {
+
+// HW encoders expect a nonzero bitrate, so |kVEADefaultBitratePerPixel| is used
+// to estimate bits per second for ~30 fps with ~1/16 compression rate.
+const int kVEADefaultBitratePerPixel = 2;
+// Number of output buffers used to copy the encoded data coming from HW
+// encoders.
+const int kVEAEncoderOutputBufferCount = 4;
+
+} // anonymous namespace
+
+VEAEncoder::VEAEncoder(
+ const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback,
+ const VideoTrackRecorder::OnErrorCB& on_error_callback,
+ int32_t bits_per_second,
+ media::VideoCodecProfile codec,
+ const gfx::Size& size)
+ : Encoder(on_encoded_video_callback,
+ bits_per_second > 0 ? bits_per_second
+ : size.GetArea() * kVEADefaultBitratePerPixel,
+ RenderThreadImpl::current()->GetGpuFactories()->GetTaskRunner()),
+ gpu_factories_(RenderThreadImpl::current()->GetGpuFactories()),
+ codec_(codec),
+ error_notified_(false),
+ on_error_callback_(on_error_callback) {
+ DCHECK(gpu_factories_);
+ DCHECK_GE(size.width(), kVEAEncoderMinResolutionWidth);
+ DCHECK_GE(size.height(), kVEAEncoderMinResolutionHeight);
+
+ encoding_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&VEAEncoder::ConfigureEncoderOnEncodingTaskRunner,
+ this, size));
+}
+
+VEAEncoder::~VEAEncoder() {
+ base::WaitableEvent release_waiter(
+ base::WaitableEvent::ResetPolicy::MANUAL,
+ base::WaitableEvent::InitialState::NOT_SIGNALED);
+ // base::Unretained is safe because the class will be alive until
+ // |release_waiter| is signaled.
+ // TODO(emircan): Consider refactoring media::VideoEncodeAccelerator to avoid
+ // using naked pointers and using DeleteSoon() here, see
+ // http://crbug.com/701627.
+ // It is currently unsafe because |video_encoder_| might be in use on another
+ // function on |encoding_task_runner_|, see http://crbug.com/701030.
+ encoding_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&VEAEncoder::DestroyOnEncodingTaskRunner,
+ base::Unretained(this), &release_waiter));
+ release_waiter.Wait();
+}
+
+void VEAEncoder::RequireBitstreamBuffers(unsigned int /*input_count*/,
+ const gfx::Size& input_coded_size,
+ size_t output_buffer_size) {
+ DVLOG(3) << __func__;
+ DCHECK(encoding_task_runner_->BelongsToCurrentThread());
+
+ vea_requested_input_coded_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,
+ base::TimeDelta timestamp) {
+ DVLOG(3) << __func__;
+ 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), nullptr,
+ front_frame.second, keyframe));
+ UseOutputBitstreamBufferId(bitstream_buffer_id);
+}
+
+void VEAEncoder::NotifyError(media::VideoEncodeAccelerator::Error error) {
+ DVLOG(3) << __func__;
+ DCHECK(encoding_task_runner_->BelongsToCurrentThread());
+ on_error_callback_.Run();
+ error_notified_ = true;
+}
+
+void VEAEncoder::UseOutputBitstreamBufferId(int32_t bitstream_buffer_id) {
+ DVLOG(3) << __func__;
+ 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) << __func__;
+ DCHECK(encoding_task_runner_->BelongsToCurrentThread());
+ input_buffers_.push(std::move(shm));
+}
+
+void VEAEncoder::EncodeOnEncodingTaskRunner(scoped_refptr<VideoFrame> frame,
+ base::TimeTicks capture_timestamp) {
+ DVLOG(3) << __func__;
+ DCHECK(encoding_task_runner_->BelongsToCurrentThread());
+
+ if (input_visible_size_ != frame->visible_rect().size() && video_encoder_)
+ video_encoder_.reset();
+
+ if (!video_encoder_)
+ ConfigureEncoderOnEncodingTaskRunner(frame->visible_rect().size());
+
+ 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) << "Might drop frame.";
+ last_frame_.reset(new std::pair<scoped_refptr<VideoFrame>, base::TimeTicks>(
+ frame, capture_timestamp));
+ return;
+ }
+
+ // If first frame hasn't been encoded, do it first.
+ if (last_frame_) {
+ std::unique_ptr<VideoFrameAndTimestamp> last_frame(last_frame_.release());
+ EncodeOnEncodingTaskRunner(last_frame->first, last_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.
+ // Only STORAGE_SHMEM backed frames can be shared with GPU process, therefore
+ // a copy is required for other storage types.
+ scoped_refptr<media::VideoFrame> video_frame = frame;
+ if (video_frame->storage_type() != VideoFrame::STORAGE_SHMEM ||
+ vea_requested_input_coded_size_ != frame->coded_size() ||
+ input_visible_size_.width() < kVEAEncoderMinResolutionWidth ||
+ input_visible_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_coded_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_coded_size_,
+ gfx::Rect(input_visible_size_), input_visible_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_visible_size_.width(), input_visible_size_.height());
+ }
+ frames_in_encode_.push(std::make_pair(
+ media::WebmMuxer::VideoParameters(frame), capture_timestamp));
+
+ video_encoder_->Encode(video_frame, false);
+}
+
+void VEAEncoder::ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size) {
+ DVLOG(3) << __func__;
+ DCHECK(encoding_task_runner_->BelongsToCurrentThread());
+ DCHECK(gpu_factories_->GetTaskRunner()->BelongsToCurrentThread());
+ DCHECK_GT(bits_per_second_, 0);
+
+ input_visible_size_ = size;
+ video_encoder_ = gpu_factories_->CreateVideoEncodeAccelerator();
+ if (!video_encoder_ ||
+ !video_encoder_->Initialize(media::PIXEL_FORMAT_I420, input_visible_size_,
+ codec_, bits_per_second_, this)) {
+ NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
+ }
+}
+
+void VEAEncoder::DestroyOnEncodingTaskRunner(
+ base::WaitableEvent* async_waiter) {
+ DCHECK(encoding_task_runner_->BelongsToCurrentThread());
+ video_encoder_.reset();
+ async_waiter->Signal();
+}
+
+} // namespace content
« no previous file with comments | « content/renderer/media_recorder/vea_encoder.h ('k') | content/renderer/media_recorder/video_track_recorder.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698