Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/cast/sender/external_video_encoder.h" | 5 #include "media/cast/sender/external_video_encoder.h" |
| 6 | 6 |
| 7 #include <cmath> | 7 #include <cmath> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 28 #include "media/cast/logging/logging_defines.h" | 28 #include "media/cast/logging/logging_defines.h" |
| 29 #include "media/cast/net/cast_transport_config.h" | 29 #include "media/cast/net/cast_transport_config.h" |
| 30 #include "media/cast/sender/vp8_quantizer_parser.h" | 30 #include "media/cast/sender/vp8_quantizer_parser.h" |
| 31 #include "media/filters/h264_parser.h" | 31 #include "media/filters/h264_parser.h" |
| 32 | 32 |
| 33 namespace { | 33 namespace { |
| 34 | 34 |
| 35 enum { MAX_H264_QUANTIZER = 51 }; | 35 enum { MAX_H264_QUANTIZER = 51 }; |
| 36 static const size_t kOutputBufferCount = 3; | 36 static const size_t kOutputBufferCount = 3; |
| 37 | 37 |
| 38 // Copy buffer to match the required coded size. | |
| 39 // Cropping or padding is done as required. | |
| 40 bool StridedBufferCopy(const uint8_t* src, | |
| 41 const gfx::Size& visible_rect, | |
| 42 int src_stride, | |
| 43 uint8_t* dst, | |
| 44 const gfx::Size& dst_coded_size) { | |
| 45 if (!src || !dst) | |
| 46 return false; | |
| 47 | |
| 48 const int src_width = visible_rect.width(); | |
| 49 const int src_height = visible_rect.height(); | |
| 50 const int dst_stride = dst_coded_size.width(); | |
| 51 const int dst_height = dst_coded_size.height(); | |
| 52 const int copy_width = src_width < dst_stride ? src_width : dst_stride; | |
| 53 const int copy_height = src_height < dst_height ? src_height : dst_height; | |
| 54 | |
| 55 for (int i = 0; i < copy_height; ++i) { | |
| 56 std::memcpy(dst, src, copy_width); | |
| 57 // Padding the remaining columns by repeating the last column of the source. | |
| 58 if (copy_width < dst_stride) | |
| 59 std::memset(dst + copy_width, src[copy_width - 1], | |
| 60 dst_stride - copy_width); | |
| 61 src += src_stride; | |
| 62 dst += dst_stride; | |
| 63 } | |
| 64 | |
| 65 // Padding the remaining rows by repeating the last row of the source. | |
| 66 src = dst - dst_stride; | |
| 67 for (int i = copy_height; i < dst_height; ++i) { | |
| 68 memcpy(dst, src, dst_stride); | |
| 69 dst += dst_stride; | |
| 70 } | |
| 71 | |
| 72 return true; | |
| 73 } | |
| 74 | |
| 75 // Copy video frame to match the required coded size. | |
| 76 bool StridedFrameCopy(const scoped_refptr<media::VideoFrame> src_frame, | |
|
miu
2016/04/25 23:39:39
Oh! Oops, I forgot to mention these routines alre
xjz
2016/04/26 17:42:30
Done. Added dependency on libyuv. This copy does n
miu
2016/04/26 19:20:17
Oh. IMO, the padding was a nice thing to have. A
xjz
2016/04/27 22:13:54
Done. Padding is nice to improve coding efficiency
| |
| 77 scoped_refptr<media::VideoFrame> dst_frame) { | |
| 78 if (!StridedBufferCopy(src_frame->data(media::VideoFrame::kYPlane), | |
| 79 src_frame->visible_rect().size(), | |
| 80 src_frame->stride(media::VideoFrame::kYPlane), | |
| 81 dst_frame->data(media::VideoFrame::kYPlane), | |
| 82 dst_frame->coded_size())) | |
| 83 return false; | |
| 84 | |
| 85 const gfx::Size uv_visible_size(src_frame->visible_rect().width() / 2, | |
| 86 src_frame->visible_rect().height() / 2); | |
| 87 const gfx::Size uv_coded_size(dst_frame->coded_size().width() / 2, | |
| 88 dst_frame->coded_size().height() / 2); | |
| 89 if (!StridedBufferCopy( | |
| 90 src_frame->data(media::VideoFrame::kUPlane), uv_visible_size, | |
| 91 src_frame->stride(media::VideoFrame::kUPlane), | |
| 92 dst_frame->data(media::VideoFrame::kUPlane), uv_coded_size)) | |
| 93 return false; | |
| 94 | |
| 95 if (!StridedBufferCopy( | |
| 96 src_frame->data(media::VideoFrame::kVPlane), uv_visible_size, | |
| 97 src_frame->stride(media::VideoFrame::kVPlane), | |
| 98 dst_frame->data(media::VideoFrame::kVPlane), uv_coded_size)) | |
| 99 return false; | |
| 100 | |
| 101 return true; | |
| 102 } | |
| 103 | |
| 38 } // namespace | 104 } // namespace |
| 39 | 105 |
| 40 namespace media { | 106 namespace media { |
| 41 namespace cast { | 107 namespace cast { |
| 42 | 108 |
| 43 // Container for the associated data of a video frame being processed. | 109 // Container for the associated data of a video frame being processed. |
| 44 struct InProgressFrameEncode { | 110 struct InProgressFrameEncode { |
| 45 // The source content to encode. | 111 // The source content to encode. |
| 46 const scoped_refptr<VideoFrame> video_frame; | 112 const scoped_refptr<VideoFrame> video_frame; |
| 47 | 113 |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 142 const VideoEncoder::FrameEncodedCallback& frame_encoded_callback) { | 208 const VideoEncoder::FrameEncodedCallback& frame_encoded_callback) { |
| 143 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 209 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 144 | 210 |
| 145 if (!encoder_active_) | 211 if (!encoder_active_) |
| 146 return; | 212 return; |
| 147 | 213 |
| 148 in_progress_frame_encodes_.push_back(InProgressFrameEncode( | 214 in_progress_frame_encodes_.push_back(InProgressFrameEncode( |
| 149 video_frame, reference_time, frame_encoded_callback, | 215 video_frame, reference_time, frame_encoded_callback, |
| 150 requested_bit_rate_)); | 216 requested_bit_rate_)); |
| 151 | 217 |
| 218 scoped_refptr<media::VideoFrame> frame = video_frame; | |
| 219 if (video_frame->coded_size() != frame_coded_size_) { | |
| 220 VLOG(1) << "ExternalVideoEncoder copy required. " | |
|
miu
2016/04/25 23:39:39
nit: Maybe make these logging statements VLOG(2) s
xjz
2016/04/26 17:42:30
Removed this log.
| |
| 221 << "Source coded size: " << video_frame->coded_size().ToString() | |
| 222 << " Required coded size: " << frame_coded_size_.ToString(); | |
| 223 | |
| 224 frame = VideoFrame::CreateFrame( | |
| 225 video_frame->format(), frame_coded_size_, video_frame->visible_rect(), | |
| 226 video_frame->natural_size(), video_frame->timestamp()); | |
| 227 if (!frame.get()) { | |
| 228 VLOG(1) << "Error: ExternalVideoEncoder: CreateFrame failed."; | |
| 229 return; | |
| 230 } | |
| 231 | |
| 232 // Copy the input frame to match the input requirements for the encoder. | |
| 233 if (!StridedFrameCopy(video_frame, frame)) { | |
| 234 VLOG(1) << "ERROR: ExternalVideoEncoder: Copy failed."; | |
| 235 return; | |
| 236 } | |
| 237 } | |
| 238 | |
| 152 // BitstreamBufferReady will be called once the encoder is done. | 239 // BitstreamBufferReady will be called once the encoder is done. |
| 153 video_encode_accelerator_->Encode(video_frame, key_frame_requested); | 240 video_encode_accelerator_->Encode(frame, key_frame_requested); |
| 154 } | 241 } |
| 155 | 242 |
| 156 protected: | 243 protected: |
| 157 void NotifyError(VideoEncodeAccelerator::Error error) final { | 244 void NotifyError(VideoEncodeAccelerator::Error error) final { |
| 158 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 245 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 159 | 246 |
| 160 DCHECK(error != VideoEncodeAccelerator::kInvalidArgumentError && | 247 DCHECK(error != VideoEncodeAccelerator::kInvalidArgumentError && |
| 161 error != VideoEncodeAccelerator::kIllegalStateError); | 248 error != VideoEncodeAccelerator::kIllegalStateError); |
| 162 | 249 |
| 163 encoder_active_ = false; | 250 encoder_active_ = false; |
| 164 | 251 |
| 165 cast_environment_->PostTask( | 252 cast_environment_->PostTask( |
| 166 CastEnvironment::MAIN, | 253 CastEnvironment::MAIN, |
| 167 FROM_HERE, | 254 FROM_HERE, |
| 168 base::Bind(status_change_cb_, STATUS_CODEC_RUNTIME_ERROR)); | 255 base::Bind(status_change_cb_, STATUS_CODEC_RUNTIME_ERROR)); |
| 169 | 256 |
| 170 // TODO(miu): Force-flush all |in_progress_frame_encodes_| immediately so | 257 // TODO(miu): Force-flush all |in_progress_frame_encodes_| immediately so |
| 171 // pending frames do not become stuck, freezing VideoSender. | 258 // pending frames do not become stuck, freezing VideoSender. |
| 172 } | 259 } |
| 173 | 260 |
| 174 // Called to allocate the input and output buffers. | 261 // Called to allocate the input and output buffers. |
| 175 void RequireBitstreamBuffers(unsigned int input_count, | 262 void RequireBitstreamBuffers(unsigned int input_count, |
| 176 const gfx::Size& input_coded_size, | 263 const gfx::Size& input_coded_size, |
| 177 size_t output_buffer_size) final { | 264 size_t output_buffer_size) final { |
| 178 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | 265 DCHECK(task_runner_->RunsTasksOnCurrentThread()); |
| 179 | 266 |
| 267 frame_coded_size_ = input_coded_size; | |
| 268 | |
| 180 // TODO(miu): Investigate why we are ignoring |input_count| (4) and instead | 269 // TODO(miu): Investigate why we are ignoring |input_count| (4) and instead |
| 181 // using |kOutputBufferCount| (3) here. | 270 // using |kOutputBufferCount| (3) here. |
| 182 for (size_t j = 0; j < kOutputBufferCount; ++j) { | 271 for (size_t j = 0; j < kOutputBufferCount; ++j) { |
|
miu
2016/04/25 23:39:39
BTW--In a follow-up change, perhaps you could take
xjz
2016/04/26 17:42:30
|input_count| is how many frames the encoder will
| |
| 183 create_video_encode_memory_cb_.Run( | 272 create_video_encode_memory_cb_.Run( |
| 184 output_buffer_size, | 273 output_buffer_size, |
| 185 base::Bind(&VEAClientImpl::OnCreateSharedMemory, this)); | 274 base::Bind(&VEAClientImpl::OnCreateSharedMemory, this)); |
| 186 } | 275 } |
| 187 } | 276 } |
| 188 | 277 |
| 189 // Encoder has encoded a frame and it's available in one of the output | 278 // Encoder has encoded a frame and it's available in one of the output |
| 190 // buffers. Package the result in a media::cast::EncodedFrame and post it | 279 // buffers. Package the result in a media::cast::EncodedFrame and post it |
| 191 // to the Cast MAIN thread via the supplied callback. | 280 // to the Cast MAIN thread via the supplied callback. |
| 192 void BitstreamBufferReady(int32_t bitstream_buffer_id, | 281 void BitstreamBufferReady(int32_t bitstream_buffer_id, |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 469 int requested_bit_rate_; | 558 int requested_bit_rate_; |
| 470 | 559 |
| 471 // Used to compute utilization metrics for each frame. | 560 // Used to compute utilization metrics for each frame. |
| 472 QuantizerEstimator quantizer_estimator_; | 561 QuantizerEstimator quantizer_estimator_; |
| 473 | 562 |
| 474 // Set to true once a frame with zero-length encoded data has been | 563 // Set to true once a frame with zero-length encoded data has been |
| 475 // encountered. | 564 // encountered. |
| 476 // TODO(miu): Remove after discovering cause. http://crbug.com/519022 | 565 // TODO(miu): Remove after discovering cause. http://crbug.com/519022 |
| 477 bool has_seen_zero_length_encoded_frame_; | 566 bool has_seen_zero_length_encoded_frame_; |
| 478 | 567 |
| 568 // The coded size of the video frame required by Encoder. This size is | |
| 569 // obtained from VEA through |RequireBitstreamBuffers()|. | |
| 570 gfx::Size frame_coded_size_; | |
| 571 | |
| 479 DISALLOW_COPY_AND_ASSIGN(VEAClientImpl); | 572 DISALLOW_COPY_AND_ASSIGN(VEAClientImpl); |
| 480 }; | 573 }; |
| 481 | 574 |
| 482 // static | 575 // static |
| 483 bool ExternalVideoEncoder::IsSupported(const VideoSenderConfig& video_config) { | 576 bool ExternalVideoEncoder::IsSupported(const VideoSenderConfig& video_config) { |
| 484 if (video_config.codec != CODEC_VIDEO_VP8 && | 577 if (video_config.codec != CODEC_VIDEO_VP8 && |
| 485 video_config.codec != CODEC_VIDEO_H264) | 578 video_config.codec != CODEC_VIDEO_H264) |
| 486 return false; | 579 return false; |
| 487 | 580 |
| 488 // TODO(miu): "Layering hooks" are needed to be able to query outside of | 581 // TODO(miu): "Layering hooks" are needed to be able to query outside of |
| (...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 787 const double kEntropyAtMaxQuantizer = 7.5; | 880 const double kEntropyAtMaxQuantizer = 7.5; |
| 788 const double slope = | 881 const double slope = |
| 789 (MAX_VP8_QUANTIZER - MIN_VP8_QUANTIZER) / kEntropyAtMaxQuantizer; | 882 (MAX_VP8_QUANTIZER - MIN_VP8_QUANTIZER) / kEntropyAtMaxQuantizer; |
| 790 const double quantizer = std::min<double>( | 883 const double quantizer = std::min<double>( |
| 791 MAX_VP8_QUANTIZER, MIN_VP8_QUANTIZER + slope * shannon_entropy); | 884 MAX_VP8_QUANTIZER, MIN_VP8_QUANTIZER + slope * shannon_entropy); |
| 792 return quantizer; | 885 return quantizer; |
| 793 } | 886 } |
| 794 | 887 |
| 795 } // namespace cast | 888 } // namespace cast |
| 796 } // namespace media | 889 } // namespace media |
| OLD | NEW |