OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 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 "media/cast/video_sender/external_video_encoder.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/memory/scoped_vector.h" |
| 10 #include "base/memory/shared_memory.h" |
| 11 #include "base/message_loop/message_loop.h" |
| 12 #include "media/base/video_frame.h" |
| 13 #include "media/base/video_util.h" |
| 14 #include "media/cast/cast_defines.h" |
| 15 #include "media/video/video_encode_accelerator.h" |
| 16 |
| 17 namespace { |
| 18 // We allocate more input buffers than what is asked for by |
| 19 // RequireBitstreamBuffers() due to potential threading timing. |
| 20 static const int kInputBufferExtraCount = 1; |
| 21 static const int kOutputBufferCount = 3; |
| 22 |
| 23 void LogFrameEncodedEvent( |
| 24 const base::TimeTicks& now, |
| 25 media::cast::CastEnvironment* const cast_environment, |
| 26 const base::TimeTicks& capture_time) { |
| 27 cast_environment->Logging()->InsertFrameEvent( |
| 28 now, |
| 29 media::cast::kVideoFrameEncoded, |
| 30 media::cast::GetVideoRtpTimestamp(capture_time), |
| 31 media::cast::kFrameIdUnknown); |
| 32 } |
| 33 } // namespace |
| 34 |
| 35 namespace media { |
| 36 namespace cast { |
| 37 |
| 38 // Container for the associated data of a video frame being processed. |
| 39 struct EncodedFrameReturnData { |
| 40 EncodedFrameReturnData(base::TimeTicks c_time, |
| 41 VideoEncoder::FrameEncodedCallback callback) { |
| 42 capture_time = c_time; |
| 43 frame_encoded_callback = callback; |
| 44 } |
| 45 base::TimeTicks capture_time; |
| 46 VideoEncoder::FrameEncodedCallback frame_encoded_callback; |
| 47 }; |
| 48 |
| 49 // The ExternalVideoEncoder class can be deleted directly by cast, while |
| 50 // LocalVideoEncodeAcceleratorClient stays around long enough to properly shut |
| 51 // down the VideoEncodeAccelerator. |
| 52 class LocalVideoEncodeAcceleratorClient |
| 53 : public VideoEncodeAccelerator::Client, |
| 54 public base::RefCountedThreadSafe<LocalVideoEncodeAcceleratorClient> { |
| 55 public: |
| 56 LocalVideoEncodeAcceleratorClient( |
| 57 scoped_refptr<CastEnvironment> cast_environment, |
| 58 scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories, |
| 59 const base::WeakPtr<ExternalVideoEncoder>& weak_owner) |
| 60 : cast_environment_(cast_environment), |
| 61 gpu_factories_(gpu_factories), |
| 62 encoder_task_runner_(gpu_factories->GetTaskRunner()), |
| 63 weak_owner_(weak_owner), |
| 64 last_encoded_frame_id_(kStartFrameId) { |
| 65 DCHECK(encoder_task_runner_); |
| 66 } |
| 67 |
| 68 // Initialize the real HW encoder. |
| 69 void Initialize(const VideoSenderConfig& video_config) { |
| 70 DCHECK(gpu_factories_); |
| 71 DCHECK(encoder_task_runner_); |
| 72 |
| 73 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread()); |
| 74 |
| 75 video_encode_accelerator_ = |
| 76 gpu_factories_->CreateVideoEncodeAccelerator(this).Pass(); |
| 77 if (!video_encode_accelerator_) return; |
| 78 |
| 79 VideoCodecProfile output_profile = media::VIDEO_CODEC_PROFILE_UNKNOWN; |
| 80 switch (video_config.codec) { |
| 81 case kVp8: |
| 82 output_profile = media::VP8PROFILE_MAIN; |
| 83 break; |
| 84 case kH264: |
| 85 output_profile = media::H264PROFILE_MAIN; |
| 86 break; |
| 87 } |
| 88 codec_ = video_config.codec; |
| 89 max_frame_rate_ = video_config.max_frame_rate; |
| 90 |
| 91 // Asynchronous initialization call; NotifyInitializeDone or NotifyError |
| 92 // will be called once the HW is initialized. |
| 93 video_encode_accelerator_->Initialize( |
| 94 media::VideoFrame::I420, |
| 95 gfx::Size(video_config.width, video_config.height), |
| 96 output_profile, |
| 97 video_config.start_bitrate); |
| 98 } |
| 99 |
| 100 // Free the HW. |
| 101 void Destroy() { |
| 102 DCHECK(encoder_task_runner_); |
| 103 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread()); |
| 104 |
| 105 if (video_encode_accelerator_) { |
| 106 video_encode_accelerator_.release()->Destroy(); |
| 107 } |
| 108 } |
| 109 |
| 110 void SetBitRate(uint32 bit_rate) { |
| 111 DCHECK(encoder_task_runner_); |
| 112 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread()); |
| 113 |
| 114 video_encode_accelerator_->RequestEncodingParametersChange( |
| 115 bit_rate, max_frame_rate_); |
| 116 } |
| 117 |
| 118 void EncodeVideoFrame( |
| 119 const scoped_refptr<media::VideoFrame>& video_frame, |
| 120 const base::TimeTicks& capture_time, |
| 121 bool key_frame_requested, |
| 122 const VideoEncoder::FrameEncodedCallback& frame_encoded_callback) { |
| 123 DCHECK(encoder_task_runner_); |
| 124 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread()); |
| 125 |
| 126 if (input_buffers_free_.empty()) { |
| 127 NOTREACHED(); |
| 128 VLOG(2) << "EncodeVideoFrame(): drop frame due to no hw buffers"; |
| 129 return; |
| 130 } |
| 131 const int index = input_buffers_free_.back(); |
| 132 base::SharedMemory* input_buffer = input_buffers_[index]; |
| 133 |
| 134 // TODO(pwestin): this allocation and copy can be removed once we don't |
| 135 // pass the video frame through liblingle. |
| 136 |
| 137 scoped_refptr<media::VideoFrame> frame = |
| 138 media::VideoFrame::WrapExternalPackedMemory( |
| 139 video_frame->format(), |
| 140 video_frame->coded_size(), |
| 141 video_frame->visible_rect(), |
| 142 video_frame->natural_size(), |
| 143 reinterpret_cast<uint8*>(input_buffer->memory()), |
| 144 input_buffer->mapped_size(), |
| 145 input_buffer->handle(), |
| 146 video_frame->GetTimestamp(), |
| 147 base::Bind(&LocalVideoEncodeAcceleratorClient::FinishedWithInBuffer, |
| 148 this, |
| 149 index)); |
| 150 |
| 151 if (!frame) { |
| 152 VLOG(1) << "EncodeVideoFrame(): failed to create frame"; |
| 153 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError); |
| 154 return; |
| 155 } |
| 156 // Do a stride copy of the input frame to match the input requirements. |
| 157 media::CopyYPlane(video_frame->data(VideoFrame::kYPlane), |
| 158 video_frame->stride(VideoFrame::kYPlane), |
| 159 video_frame->natural_size().height(), |
| 160 frame.get()); |
| 161 media::CopyUPlane(video_frame->data(VideoFrame::kUPlane), |
| 162 video_frame->stride(VideoFrame::kUPlane), |
| 163 video_frame->natural_size().height(), |
| 164 frame.get()); |
| 165 media::CopyVPlane(video_frame->data(VideoFrame::kVPlane), |
| 166 video_frame->stride(VideoFrame::kVPlane), |
| 167 video_frame->natural_size().height(), |
| 168 frame.get()); |
| 169 |
| 170 encoded_frame_data_storage_.push_back( |
| 171 EncodedFrameReturnData(capture_time, frame_encoded_callback)); |
| 172 |
| 173 // BitstreamBufferReady will be called once the encoder is done. |
| 174 video_encode_accelerator_->Encode(frame, key_frame_requested); |
| 175 } |
| 176 |
| 177 protected: |
| 178 virtual void NotifyInitializeDone() OVERRIDE { |
| 179 DCHECK(encoder_task_runner_); |
| 180 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread()); |
| 181 |
| 182 cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, |
| 183 base::Bind(&ExternalVideoEncoder::EncoderInitialized, |
| 184 weak_owner_)); |
| 185 } |
| 186 |
| 187 virtual void NotifyError(VideoEncodeAccelerator::Error error) OVERRIDE { |
| 188 DCHECK(encoder_task_runner_); |
| 189 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread()); |
| 190 VLOG(1) << "ExternalVideoEncoder NotifyError: " << error; |
| 191 |
| 192 if (video_encode_accelerator_) { |
| 193 video_encode_accelerator_.release()->Destroy(); |
| 194 } |
| 195 cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, |
| 196 base::Bind(&ExternalVideoEncoder::EncoderError, |
| 197 weak_owner_)); |
| 198 } |
| 199 |
| 200 // Called to allocate the input and output buffers. |
| 201 virtual void RequireBitstreamBuffers(unsigned int input_count, |
| 202 const gfx::Size& input_coded_size, |
| 203 size_t output_buffer_size) OVERRIDE { |
| 204 DCHECK(encoder_task_runner_); |
| 205 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread()); |
| 206 DCHECK(video_encode_accelerator_); |
| 207 |
| 208 for (unsigned int i = 0; i < input_count + kInputBufferExtraCount; ++i) { |
| 209 base::SharedMemory* shm = gpu_factories_->CreateSharedMemory( |
| 210 media::VideoFrame::AllocationSize(media::VideoFrame::I420, |
| 211 input_coded_size)); |
| 212 if (!shm) { |
| 213 VLOG(1) << "RequireBitstreamBuffers(): failed to create input buffer "; |
| 214 return; |
| 215 } |
| 216 input_buffers_.push_back(shm); |
| 217 input_buffers_free_.push_back(i); |
| 218 } |
| 219 |
| 220 for (int j = 0; j < kOutputBufferCount; ++j) { |
| 221 base::SharedMemory* shm = |
| 222 gpu_factories_->CreateSharedMemory(output_buffer_size); |
| 223 if (!shm) { |
| 224 VLOG(1) << "RequireBitstreamBuffers(): failed to create input buffer "; |
| 225 return; |
| 226 } |
| 227 output_buffers_.push_back(shm); |
| 228 } |
| 229 // Immediately provide all output buffers to the VEA. |
| 230 for (size_t i = 0; i < output_buffers_.size(); ++i) { |
| 231 video_encode_accelerator_->UseOutputBitstreamBuffer( |
| 232 media::BitstreamBuffer(static_cast<int32>(i), |
| 233 output_buffers_[i]->handle(), |
| 234 output_buffers_[i]->mapped_size())); |
| 235 } |
| 236 } |
| 237 |
| 238 // Encoder has encoded a frame and it's available in one of out output |
| 239 // buffers. |
| 240 virtual void BitstreamBufferReady(int32 bitstream_buffer_id, |
| 241 size_t payload_size, |
| 242 bool key_frame) OVERRIDE { |
| 243 DCHECK(encoder_task_runner_); |
| 244 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread()); |
| 245 if (bitstream_buffer_id < 0 || |
| 246 bitstream_buffer_id >= static_cast<int32>(output_buffers_.size())) { |
| 247 NOTREACHED(); |
| 248 VLOG(1) << "BitstreamBufferReady(): invalid bitstream_buffer_id=" |
| 249 << bitstream_buffer_id; |
| 250 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError); |
| 251 return; |
| 252 } |
| 253 base::SharedMemory* output_buffer = output_buffers_[bitstream_buffer_id]; |
| 254 if (payload_size > output_buffer->mapped_size()) { |
| 255 NOTREACHED(); |
| 256 VLOG(1) << "BitstreamBufferReady(): invalid payload_size = " |
| 257 << payload_size; |
| 258 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError); |
| 259 return; |
| 260 } |
| 261 if (encoded_frame_data_storage_.empty()) { |
| 262 NOTREACHED(); |
| 263 NotifyError(media::VideoEncodeAccelerator::kPlatformFailureError); |
| 264 return; |
| 265 } |
| 266 scoped_ptr<EncodedVideoFrame> encoded_frame(new EncodedVideoFrame()); |
| 267 |
| 268 encoded_frame->codec = codec_; |
| 269 encoded_frame->key_frame = key_frame; |
| 270 encoded_frame->last_referenced_frame_id = last_encoded_frame_id_; |
| 271 last_encoded_frame_id_++; |
| 272 encoded_frame->frame_id = last_encoded_frame_id_; |
| 273 if (key_frame) { |
| 274 // Self referenced. |
| 275 encoded_frame->last_referenced_frame_id = encoded_frame->frame_id; |
| 276 } |
| 277 |
| 278 encoded_frame->data.insert( |
| 279 0, static_cast<const char*>(output_buffer->memory()), payload_size); |
| 280 |
| 281 cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, |
| 282 base::Bind(encoded_frame_data_storage_.front().frame_encoded_callback, |
| 283 base::Passed(&encoded_frame), |
| 284 encoded_frame_data_storage_.front().capture_time)); |
| 285 |
| 286 base::TimeTicks now = cast_environment_->Clock()->NowTicks(); |
| 287 cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE, |
| 288 base::Bind(LogFrameEncodedEvent, now, cast_environment_, |
| 289 encoded_frame_data_storage_.front().capture_time)); |
| 290 |
| 291 encoded_frame_data_storage_.pop_front(); |
| 292 |
| 293 // We need to re-add the output buffer to the encoder after we are done |
| 294 // with it. |
| 295 video_encode_accelerator_->UseOutputBitstreamBuffer( |
| 296 media::BitstreamBuffer(bitstream_buffer_id, |
| 297 output_buffers_[bitstream_buffer_id]->handle(), |
| 298 output_buffers_[bitstream_buffer_id]->mapped_size())); |
| 299 } |
| 300 |
| 301 private: |
| 302 // Encoder is done with the provided input buffer. |
| 303 void FinishedWithInBuffer(int input_index) { |
| 304 DCHECK(encoder_task_runner_); |
| 305 DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread()); |
| 306 DCHECK_GE(input_index, 0); |
| 307 DCHECK_LT(input_index, static_cast<int>(input_buffers_.size())); |
| 308 VLOG(2) << "EncodeFrameFinished(): index=" << input_index; |
| 309 input_buffers_free_.push_back(input_index); |
| 310 } |
| 311 |
| 312 friend class base::RefCountedThreadSafe<LocalVideoEncodeAcceleratorClient>; |
| 313 |
| 314 virtual ~LocalVideoEncodeAcceleratorClient() {} |
| 315 |
| 316 const scoped_refptr<CastEnvironment> cast_environment_; |
| 317 scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories_; |
| 318 scoped_refptr<base::SingleThreadTaskRunner> encoder_task_runner_; |
| 319 const base::WeakPtr<ExternalVideoEncoder> weak_owner_; |
| 320 |
| 321 scoped_ptr<media::VideoEncodeAccelerator> video_encode_accelerator_; |
| 322 int max_frame_rate_; |
| 323 VideoCodec codec_; |
| 324 uint32 last_encoded_frame_id_; |
| 325 |
| 326 // Shared memory buffers for input/output with the VideoAccelerator. |
| 327 ScopedVector<base::SharedMemory> input_buffers_; |
| 328 ScopedVector<base::SharedMemory> output_buffers_; |
| 329 |
| 330 // Input buffers ready to be filled with input from Encode(). As a LIFO since |
| 331 // we don't care about ordering. |
| 332 std::vector<int> input_buffers_free_; |
| 333 |
| 334 // FIFO list. |
| 335 std::list<EncodedFrameReturnData> encoded_frame_data_storage_; |
| 336 |
| 337 DISALLOW_COPY_AND_ASSIGN(LocalVideoEncodeAcceleratorClient); |
| 338 }; |
| 339 |
| 340 ExternalVideoEncoder::ExternalVideoEncoder( |
| 341 scoped_refptr<CastEnvironment> cast_environment, |
| 342 const VideoSenderConfig& video_config, |
| 343 scoped_refptr<GpuVideoAcceleratorFactories> gpu_factories) |
| 344 : video_config_(video_config), |
| 345 cast_environment_(cast_environment), |
| 346 encoder_active_(false), |
| 347 key_frame_requested_(false), |
| 348 skip_next_frame_(false), |
| 349 skip_count_(0), |
| 350 encoder_task_runner_(gpu_factories->GetTaskRunner()), |
| 351 weak_factory_(this) { |
| 352 DCHECK(gpu_factories); |
| 353 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| 354 video_accelerator_client_ = new LocalVideoEncodeAcceleratorClient( |
| 355 cast_environment, gpu_factories, weak_factory_.GetWeakPtr()); |
| 356 |
| 357 encoder_task_runner_->PostTask( |
| 358 FROM_HERE, |
| 359 base::Bind(&LocalVideoEncodeAcceleratorClient::Initialize, |
| 360 video_accelerator_client_, video_config)); |
| 361 } |
| 362 |
| 363 ExternalVideoEncoder::~ExternalVideoEncoder() { |
| 364 encoder_task_runner_->PostTask( |
| 365 FROM_HERE, |
| 366 base::Bind(&LocalVideoEncodeAcceleratorClient::Destroy, |
| 367 video_accelerator_client_)); |
| 368 } |
| 369 |
| 370 void ExternalVideoEncoder::EncoderInitialized() { |
| 371 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| 372 encoder_active_ = true; |
| 373 } |
| 374 |
| 375 void ExternalVideoEncoder::EncoderError() { |
| 376 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| 377 encoder_active_ = false; |
| 378 } |
| 379 |
| 380 bool ExternalVideoEncoder::EncodeVideoFrame( |
| 381 const scoped_refptr<media::VideoFrame>& video_frame, |
| 382 const base::TimeTicks& capture_time, |
| 383 const FrameEncodedCallback& frame_encoded_callback) { |
| 384 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| 385 |
| 386 if (!encoder_active_) return false; |
| 387 |
| 388 if (skip_next_frame_) { |
| 389 VLOG(1) << "Skip encoding frame"; |
| 390 ++skip_count_; |
| 391 skip_next_frame_ = false; |
| 392 return false; |
| 393 } |
| 394 base::TimeTicks now = cast_environment_->Clock()->NowTicks(); |
| 395 cast_environment_->Logging()->InsertFrameEvent(now, kVideoFrameSentToEncoder, |
| 396 GetVideoRtpTimestamp(capture_time), kFrameIdUnknown); |
| 397 |
| 398 encoder_task_runner_->PostTask( |
| 399 FROM_HERE, |
| 400 base::Bind(&LocalVideoEncodeAcceleratorClient::EncodeVideoFrame, |
| 401 video_accelerator_client_, video_frame, capture_time, |
| 402 key_frame_requested_, frame_encoded_callback)); |
| 403 |
| 404 key_frame_requested_ = false; |
| 405 return true; |
| 406 } |
| 407 |
| 408 // Inform the encoder about the new target bit rate. |
| 409 void ExternalVideoEncoder::SetBitRate(int new_bit_rate) { |
| 410 encoder_task_runner_->PostTask( |
| 411 FROM_HERE, |
| 412 base::Bind(&LocalVideoEncodeAcceleratorClient::SetBitRate, |
| 413 video_accelerator_client_, new_bit_rate)); |
| 414 } |
| 415 |
| 416 // Inform the encoder to not encode the next frame. |
| 417 void ExternalVideoEncoder::SkipNextFrame(bool skip_next_frame) { |
| 418 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| 419 skip_next_frame_ = skip_next_frame; |
| 420 } |
| 421 |
| 422 // Inform the encoder to encode the next frame as a key frame. |
| 423 void ExternalVideoEncoder::GenerateKeyFrame() { |
| 424 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| 425 key_frame_requested_ = true; |
| 426 } |
| 427 |
| 428 // Inform the encoder to only reference frames older or equal to frame_id; |
| 429 void ExternalVideoEncoder::LatestFrameIdToReference(uint32 /*frame_id*/) { |
| 430 // Do nothing not supported. |
| 431 } |
| 432 |
| 433 int ExternalVideoEncoder::NumberOfSkippedFrames() const { |
| 434 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| 435 return skip_count_; |
| 436 } |
| 437 |
| 438 } // namespace cast |
| 439 } // namespace media |
OLD | NEW |