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

Side by Side Diff: content/renderer/media_recorder/vea_encoder.cc

Issue 2793303003: Refactor VideoTrackRecorder into smaller classes (Closed)
Patch Set: 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/renderer/media_recorder/vea_encoder.h"
6
7 #include <string>
8
9 #include "content/renderer/media/renderer_gpu_video_accelerator_factories.h"
10 #include "content/renderer/render_thread_impl.h"
11 #include "media/base/bind_to_current_loop.h"
12 #include "media/base/video_frame.h"
13 #include "third_party/libyuv/include/libyuv.h"
14 #include "ui/gfx/geometry/size.h"
15
16 using media::VideoFrame;
17 using video_track_recorder::kVEAEncoderMinResolutionWidth;
18 using video_track_recorder::kVEAEncoderMinResolutionHeight;
19
20 namespace content {
21
22 namespace {
23
24 // HW encoders expect a nonzero bitrate, so |kVEADefaultBitratePerPixel| is used
25 // to estimate bits per second for ~30 fps with ~1/16 compression rate.
26 const int kVEADefaultBitratePerPixel = 2;
27 // Number of output buffers used to copy the encoded data coming from HW
28 // encoders.
29 const int kVEAEncoderOutputBufferCount = 4;
30
31 } // anonymous namespace
32
33 VEAEncoder::VEAEncoder(
34 const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_callback,
35 int32_t bits_per_second,
36 media::VideoCodecProfile codec,
37 const gfx::Size& size)
38 : Encoder(on_encoded_video_callback,
39 bits_per_second > 0 ? bits_per_second
40 : size.GetArea() * kVEADefaultBitratePerPixel,
41 RenderThreadImpl::current()->GetGpuFactories()->GetTaskRunner()),
42 gpu_factories_(RenderThreadImpl::current()->GetGpuFactories()),
43 codec_(codec),
44 error_notified_(false) {
45 DCHECK(gpu_factories_);
46 DCHECK_GE(size.width(), kVEAEncoderMinResolutionWidth);
47 DCHECK_GE(size.height(), kVEAEncoderMinResolutionHeight);
48
49 encoding_task_runner_->PostTask(
50 FROM_HERE, base::Bind(&VEAEncoder::ConfigureEncoderOnEncodingTaskRunner,
51 this, size));
52 }
53
54 VEAEncoder::~VEAEncoder() {
55 base::WaitableEvent release_waiter(
56 base::WaitableEvent::ResetPolicy::MANUAL,
57 base::WaitableEvent::InitialState::NOT_SIGNALED);
58 // base::Unretained is safe because the class will be alive until
59 // |release_waiter| is signaled.
60 // TODO(emircan): Consider refactoring media::VideoEncodeAccelerator to avoid
61 // using naked pointers and using DeleteSoon() here, see
62 // http://crbug.com/701627.
63 // It is currently unsafe because |video_encoder_| might be in use on another
64 // function on |encoding_task_runner_|, see http://crbug.com/701030.
65 encoding_task_runner_->PostTask(
66 FROM_HERE, base::Bind(&VEAEncoder::DestroyOnEncodingTaskRunner,
67 base::Unretained(this), &release_waiter));
68 release_waiter.Wait();
69 }
70
71 void VEAEncoder::RequireBitstreamBuffers(unsigned int /*input_count*/,
72 const gfx::Size& input_coded_size,
73 size_t output_buffer_size) {
74 DVLOG(3) << __func__;
75 DCHECK(encoding_task_runner_->BelongsToCurrentThread());
76
77 vea_requested_input_coded_size_ = input_coded_size;
78 output_buffers_.clear();
79 std::queue<std::unique_ptr<base::SharedMemory>>().swap(input_buffers_);
80
81 for (int i = 0; i < kVEAEncoderOutputBufferCount; ++i) {
82 std::unique_ptr<base::SharedMemory> shm =
83 gpu_factories_->CreateSharedMemory(output_buffer_size);
84 if (shm)
85 output_buffers_.push_back(base::WrapUnique(shm.release()));
86 }
87
88 for (size_t i = 0; i < output_buffers_.size(); ++i)
89 UseOutputBitstreamBufferId(i);
90 }
91
92 void VEAEncoder::BitstreamBufferReady(int32_t bitstream_buffer_id,
93 size_t payload_size,
94 bool keyframe,
95 base::TimeDelta timestamp) {
96 DVLOG(3) << __func__;
97 DCHECK(encoding_task_runner_->BelongsToCurrentThread());
98
99 base::SharedMemory* output_buffer =
100 output_buffers_[bitstream_buffer_id].get();
101
102 std::unique_ptr<std::string> data(new std::string);
103 data->append(reinterpret_cast<char*>(output_buffer->memory()), payload_size);
104
105 const auto front_frame = frames_in_encode_.front();
106 frames_in_encode_.pop();
107 origin_task_runner_->PostTask(
108 FROM_HERE, base::Bind(OnFrameEncodeCompleted, on_encoded_video_callback_,
109 front_frame.first, base::Passed(&data), nullptr,
110 front_frame.second, keyframe));
111 UseOutputBitstreamBufferId(bitstream_buffer_id);
112 }
113
114 void VEAEncoder::NotifyError(media::VideoEncodeAccelerator::Error error) {
115 DVLOG(3) << __func__;
116 DCHECK(encoding_task_runner_->BelongsToCurrentThread());
117
118 // TODO(emircan): Notify the owner via a callback.
119 error_notified_ = true;
120 }
121
122 void VEAEncoder::UseOutputBitstreamBufferId(int32_t bitstream_buffer_id) {
123 DVLOG(3) << __func__;
124 DCHECK(encoding_task_runner_->BelongsToCurrentThread());
125
126 video_encoder_->UseOutputBitstreamBuffer(media::BitstreamBuffer(
127 bitstream_buffer_id, output_buffers_[bitstream_buffer_id]->handle(),
128 output_buffers_[bitstream_buffer_id]->mapped_size()));
129 }
130
131 void VEAEncoder::FrameFinished(std::unique_ptr<base::SharedMemory> shm) {
132 DVLOG(3) << __func__;
133 DCHECK(encoding_task_runner_->BelongsToCurrentThread());
134 input_buffers_.push(std::move(shm));
135 }
136
137 void VEAEncoder::EncodeOnEncodingTaskRunner(scoped_refptr<VideoFrame> frame,
138 base::TimeTicks capture_timestamp) {
139 DVLOG(3) << __func__;
140 DCHECK(encoding_task_runner_->BelongsToCurrentThread());
141
142 if (input_visible_size_ != frame->visible_rect().size() && video_encoder_)
143 video_encoder_.reset();
144
145 if (!video_encoder_)
146 ConfigureEncoderOnEncodingTaskRunner(frame->visible_rect().size());
147
148 if (error_notified_) {
149 DVLOG(3) << "An error occurred in VEA encoder";
150 return;
151 }
152
153 // Drop frames if there is no output buffers available.
154 if (output_buffers_.empty()) {
155 // TODO(emircan): Investigate if resetting encoder would help.
156 DVLOG(3) << "Might drop frame.";
157 last_frame_.reset(new std::pair<scoped_refptr<VideoFrame>, base::TimeTicks>(
158 frame, capture_timestamp));
159 return;
160 }
161
162 // If first frame hasn't been encoded, do it first.
163 if (last_frame_) {
164 std::unique_ptr<VideoFrameAndTimestamp> last_frame(last_frame_.release());
165 EncodeOnEncodingTaskRunner(last_frame->first, last_frame->second);
166 }
167
168 // Lower resolutions may fall back to SW encoder in some platforms, i.e. Mac.
169 // In that case, the encoder expects more frames before returning result.
170 // Therefore, a copy is necessary to release the current frame.
171 // Only STORAGE_SHMEM backed frames can be shared with GPU process, therefore
172 // a copy is required for other storage types.
173 scoped_refptr<media::VideoFrame> video_frame = frame;
174 if (video_frame->storage_type() != VideoFrame::STORAGE_SHMEM ||
175 vea_requested_input_coded_size_ != frame->coded_size() ||
176 input_visible_size_.width() < kVEAEncoderMinResolutionWidth ||
177 input_visible_size_.height() < kVEAEncoderMinResolutionHeight) {
178 // Create SharedMemory backed input buffers as necessary. These SharedMemory
179 // instances will be shared with GPU process.
180 std::unique_ptr<base::SharedMemory> input_buffer;
181 const size_t desired_mapped_size = media::VideoFrame::AllocationSize(
182 media::PIXEL_FORMAT_I420, vea_requested_input_coded_size_);
183 if (input_buffers_.empty()) {
184 input_buffer = gpu_factories_->CreateSharedMemory(desired_mapped_size);
185 } else {
186 do {
187 input_buffer = std::move(input_buffers_.front());
188 input_buffers_.pop();
189 } while (!input_buffers_.empty() &&
190 input_buffer->mapped_size() < desired_mapped_size);
191 if (!input_buffer || input_buffer->mapped_size() < desired_mapped_size)
192 return;
193 }
194
195 video_frame = media::VideoFrame::WrapExternalSharedMemory(
196 media::PIXEL_FORMAT_I420, vea_requested_input_coded_size_,
197 gfx::Rect(input_visible_size_), input_visible_size_,
198 reinterpret_cast<uint8_t*>(input_buffer->memory()),
199 input_buffer->mapped_size(), input_buffer->handle(), 0,
200 frame->timestamp());
201 video_frame->AddDestructionObserver(media::BindToCurrentLoop(
202 base::Bind(&VEAEncoder::FrameFinished, this,
203 base::Passed(std::move(input_buffer)))));
204 libyuv::I420Copy(frame->visible_data(media::VideoFrame::kYPlane),
205 frame->stride(media::VideoFrame::kYPlane),
206 frame->visible_data(media::VideoFrame::kUPlane),
207 frame->stride(media::VideoFrame::kUPlane),
208 frame->visible_data(media::VideoFrame::kVPlane),
209 frame->stride(media::VideoFrame::kVPlane),
210 video_frame->visible_data(media::VideoFrame::kYPlane),
211 video_frame->stride(media::VideoFrame::kYPlane),
212 video_frame->visible_data(media::VideoFrame::kUPlane),
213 video_frame->stride(media::VideoFrame::kUPlane),
214 video_frame->visible_data(media::VideoFrame::kVPlane),
215 video_frame->stride(media::VideoFrame::kVPlane),
216 input_visible_size_.width(), input_visible_size_.height());
217 }
218 frames_in_encode_.push(std::make_pair(
219 media::WebmMuxer::VideoParameters(frame), capture_timestamp));
220
221 video_encoder_->Encode(video_frame, false);
222 }
223
224 void VEAEncoder::ConfigureEncoderOnEncodingTaskRunner(const gfx::Size& size) {
225 DVLOG(3) << __func__;
226 DCHECK(encoding_task_runner_->BelongsToCurrentThread());
227 DCHECK(gpu_factories_->GetTaskRunner()->BelongsToCurrentThread());
228 DCHECK_GT(bits_per_second_, 0);
229
230 input_visible_size_ = size;
231 video_encoder_ = gpu_factories_->CreateVideoEncodeAccelerator();
232 if (!video_encoder_ ||
233 !video_encoder_->Initialize(media::PIXEL_FORMAT_I420, input_visible_size_,
234 codec_, bits_per_second_, this)) {
235 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError);
236 }
237 }
238
239 void VEAEncoder::DestroyOnEncodingTaskRunner(
240 base::WaitableEvent* async_waiter) {
241 DCHECK(encoding_task_runner_->BelongsToCurrentThread());
242 video_encoder_.reset();
243 async_waiter->Signal();
244 }
245
246 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698