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 |