| Index: media/cast/sender/vp8_encoder.cc
|
| diff --git a/media/cast/sender/vp8_encoder.cc b/media/cast/sender/vp8_encoder.cc
|
| index bf430c1869c082e0d90a5e911ee36af74643ae01..e0f84078acee7053963a8fb6d759ed5c39882fdf 100644
|
| --- a/media/cast/sender/vp8_encoder.cc
|
| +++ b/media/cast/sender/vp8_encoder.cc
|
| @@ -28,8 +28,8 @@ Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config)
|
| use_multiple_video_buffers_(
|
| cast_config_.max_number_of_video_buffers_used ==
|
| kNumberOfVp8VideoBuffers),
|
| - raw_image_(nullptr),
|
| key_frame_requested_(true),
|
| + bitrate_kbit_(cast_config_.start_bitrate / 1000),
|
| last_encoded_frame_id_(kStartFrameId),
|
| last_acked_frame_id_(kStartFrameId),
|
| undroppable_frames_(0) {
|
| @@ -53,18 +53,40 @@ Vp8Encoder::~Vp8Encoder() {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| if (is_initialized())
|
| vpx_codec_destroy(&encoder_);
|
| - vpx_img_free(raw_image_);
|
| }
|
|
|
| void Vp8Encoder::Initialize() {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| DCHECK(!is_initialized());
|
| + // The encoder will be created/configured when the first frame encode is
|
| + // requested.
|
| +}
|
|
|
| - // Creating a wrapper to the image - setting image data to NULL. Actual
|
| - // pointer will be set during encode. Setting align to 1, as it is
|
| - // meaningless (actual memory is not allocated).
|
| - raw_image_ = vpx_img_wrap(
|
| - NULL, VPX_IMG_FMT_I420, cast_config_.width, cast_config_.height, 1, NULL);
|
| +void Vp8Encoder::ConfigureForNewFrameSize(const gfx::Size& frame_size) {
|
| + if (is_initialized()) {
|
| + // Workaround for VP8 bug: If the new size is strictly less-than-or-equal to
|
| + // the old size, in terms of area, the existing encoder instance can
|
| + // continue. Otherwise, completely tear-down and re-create a new encoder to
|
| + // avoid a shutdown crash.
|
| + if (frame_size.GetArea() <= gfx::Size(config_.g_w, config_.g_h).GetArea()) {
|
| + DVLOG(1) << "Continuing to use existing encoder at smaller frame size: "
|
| + << gfx::Size(config_.g_w, config_.g_h).ToString() << " --> "
|
| + << frame_size.ToString();
|
| + config_.g_w = frame_size.width();
|
| + config_.g_h = frame_size.height();
|
| + CHECK_EQ(vpx_codec_enc_config_set(&encoder_, &config_), VPX_CODEC_OK)
|
| + << "Failed to update frame size in encoder config.";
|
| + return;
|
| + }
|
| +
|
| + DVLOG(1) << "Destroying/Re-Creating encoder for larger frame size: "
|
| + << gfx::Size(config_.g_w, config_.g_h).ToString() << " --> "
|
| + << frame_size.ToString();
|
| + vpx_codec_destroy(&encoder_);
|
| + } else {
|
| + DVLOG(1) << "Creating encoder for the first frame; size: "
|
| + << frame_size.ToString();
|
| + }
|
|
|
| for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
|
| buffer_state_[i].frame_id = kStartFrameId;
|
| @@ -72,15 +94,12 @@ void Vp8Encoder::Initialize() {
|
| }
|
|
|
| // Populate encoder configuration with default values.
|
| - if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config_, 0)) {
|
| - NOTREACHED() << "Invalid return value";
|
| - config_.g_timebase.den = 0; // Do not call vpx_codec_destroy() in dtor.
|
| - return;
|
| - }
|
| + CHECK_EQ(vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config_, 0),
|
| + VPX_CODEC_OK);
|
|
|
| config_.g_threads = cast_config_.number_of_encode_threads;
|
| - config_.g_w = cast_config_.width;
|
| - config_.g_h = cast_config_.height;
|
| + config_.g_w = frame_size.width();
|
| + config_.g_h = frame_size.height();
|
| // Set the timebase to match that of base::TimeDelta.
|
| config_.g_timebase.num = 1;
|
| config_.g_timebase.den = base::Time::kMicrosecondsPerSecond;
|
| @@ -89,6 +108,8 @@ void Vp8Encoder::Initialize() {
|
| // codec requirements.
|
| config_.g_error_resilient = 1;
|
| }
|
| + // |g_pass| and |g_lag_in_frames| must be "one pass" and zero, respectively,
|
| + // in order for VP8 to support changing frame sizes during encoding:
|
| config_.g_pass = VPX_RC_ONE_PASS;
|
| config_.g_lag_in_frames = 0; // Immediate data output for each frame.
|
|
|
| @@ -96,7 +117,7 @@ void Vp8Encoder::Initialize() {
|
| config_.rc_dropframe_thresh = 0; // The encoder may not drop any frames.
|
| config_.rc_resize_allowed = 0; // TODO(miu): Why not? Investigate this.
|
| config_.rc_end_usage = VPX_CBR;
|
| - config_.rc_target_bitrate = cast_config_.start_bitrate / 1000; // In kbit/s.
|
| + config_.rc_target_bitrate = bitrate_kbit_;
|
| config_.rc_min_quantizer = cast_config_.min_qp;
|
| config_.rc_max_quantizer = cast_config_.max_qp;
|
| // TODO(miu): Revisit these now that the encoder is being successfully
|
| @@ -113,24 +134,22 @@ void Vp8Encoder::Initialize() {
|
| config_.kf_mode = VPX_KF_DISABLED;
|
|
|
| vpx_codec_flags_t flags = 0;
|
| - if (vpx_codec_enc_init(&encoder_, vpx_codec_vp8_cx(), &config_, flags)) {
|
| - NOTREACHED() << "vpx_codec_enc_init() failed.";
|
| - config_.g_timebase.den = 0; // Do not call vpx_codec_destroy() in dtor.
|
| - return;
|
| - }
|
| + CHECK_EQ(vpx_codec_enc_init(&encoder_, vpx_codec_vp8_cx(), &config_, flags),
|
| + VPX_CODEC_OK);
|
|
|
| // Raise the threshold for considering macroblocks as static. The default is
|
| // zero, so this setting makes the encoder less sensitive to motion. This
|
| // lowers the probability of needing to utilize more CPU to search for motion
|
| // vectors.
|
| - vpx_codec_control(&encoder_, VP8E_SET_STATIC_THRESHOLD, 1);
|
| + CHECK_EQ(vpx_codec_control(&encoder_, VP8E_SET_STATIC_THRESHOLD, 1),
|
| + VPX_CODEC_OK);
|
|
|
| // Improve quality by enabling sets of codec features that utilize more CPU.
|
| // The default is zero, with increasingly more CPU to be used as the value is
|
| // more negative.
|
| // TODO(miu): Document why this value was chosen and expected behaviors.
|
| // Should this be dynamic w.r.t. hardware performance?
|
| - vpx_codec_control(&encoder_, VP8E_SET_CPUUSED, -6);
|
| + CHECK_EQ(vpx_codec_control(&encoder_, VP8E_SET_CPUUSED, -6), VPX_CODEC_OK);
|
| }
|
|
|
| void Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
|
| @@ -139,20 +158,11 @@ void Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| DCHECK(encoded_frame);
|
|
|
| - CHECK(is_initialized()); // No illegal reference to |config_| or |encoder_|.
|
| -
|
| - // Image in vpx_image_t format.
|
| - // Input image is const. VP8's raw image is not defined as const.
|
| - raw_image_->planes[VPX_PLANE_Y] =
|
| - const_cast<uint8*>(video_frame->data(VideoFrame::kYPlane));
|
| - raw_image_->planes[VPX_PLANE_U] =
|
| - const_cast<uint8*>(video_frame->data(VideoFrame::kUPlane));
|
| - raw_image_->planes[VPX_PLANE_V] =
|
| - const_cast<uint8*>(video_frame->data(VideoFrame::kVPlane));
|
| -
|
| - raw_image_->stride[VPX_PLANE_Y] = video_frame->stride(VideoFrame::kYPlane);
|
| - raw_image_->stride[VPX_PLANE_U] = video_frame->stride(VideoFrame::kUPlane);
|
| - raw_image_->stride[VPX_PLANE_V] = video_frame->stride(VideoFrame::kVPlane);
|
| + // Initialize on-demand. Later, if the video frame size has changed, update
|
| + // the encoder configuration.
|
| + const gfx::Size frame_size = video_frame->visible_rect().size();
|
| + if (!is_initialized() || gfx::Size(config_.g_w, config_.g_h) != frame_size)
|
| + ConfigureForNewFrameSize(frame_size);
|
|
|
| uint32 latest_frame_id_to_reference;
|
| Vp8Buffers buffer_to_update;
|
| @@ -171,6 +181,27 @@ void Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
|
| GetCodecUpdateFlags(buffer_to_update, &flags);
|
| }
|
|
|
| + // Wrapper for vpx_codec_encode() to access the YUV data in the |video_frame|.
|
| + // Only the VISIBLE rectangle within |video_frame| is exposed to the codec.
|
| + vpx_image_t vpx_image;
|
| + vpx_image_t* const result = vpx_img_wrap(
|
| + &vpx_image,
|
| + VPX_IMG_FMT_I420,
|
| + frame_size.width(),
|
| + frame_size.height(),
|
| + 1,
|
| + video_frame->data(VideoFrame::kYPlane));
|
| + DCHECK_EQ(result, &vpx_image);
|
| + vpx_image.planes[VPX_PLANE_Y] =
|
| + video_frame->visible_data(VideoFrame::kYPlane);
|
| + vpx_image.planes[VPX_PLANE_U] =
|
| + video_frame->visible_data(VideoFrame::kUPlane);
|
| + vpx_image.planes[VPX_PLANE_V] =
|
| + video_frame->visible_data(VideoFrame::kVPlane);
|
| + vpx_image.stride[VPX_PLANE_Y] = video_frame->stride(VideoFrame::kYPlane);
|
| + vpx_image.stride[VPX_PLANE_U] = video_frame->stride(VideoFrame::kUPlane);
|
| + vpx_image.stride[VPX_PLANE_V] = video_frame->stride(VideoFrame::kVPlane);
|
| +
|
| // The frame duration given to the VP8 codec affects a number of important
|
| // behaviors, including: per-frame bandwidth, CPU time spent encoding,
|
| // temporal quality trade-offs, and key/golden/alt-ref frame generation
|
| @@ -195,7 +226,7 @@ void Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
|
| // entirely on |predicted_frame_duration| and the target bitrate setting being
|
| // micro-managed via calls to UpdateRates().
|
| CHECK_EQ(vpx_codec_encode(&encoder_,
|
| - raw_image_,
|
| + &vpx_image,
|
| 0,
|
| predicted_frame_duration.InMicroseconds(),
|
| flags,
|
| @@ -402,7 +433,7 @@ void Vp8Encoder::UpdateRates(uint32 new_bitrate) {
|
| if (config_.rc_target_bitrate == new_bitrate_kbit)
|
| return;
|
|
|
| - config_.rc_target_bitrate = new_bitrate_kbit;
|
| + config_.rc_target_bitrate = bitrate_kbit_ = new_bitrate_kbit;
|
|
|
| // Update encoder context.
|
| if (vpx_codec_enc_config_set(&encoder_, &config_)) {
|
|
|