OLD | NEW |
(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 |
OLD | NEW |