Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(86)

Unified Diff: media/cast/sender/vp8_encoder.cc

Issue 892383002: RELAND: [Cast] Software encoder support for varying video frame sizes. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix for 'access to uninitialized memory' error. Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/cast/sender/vp8_encoder.h ('k') | media/cast/test/fake_media_source.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..fae960e1b4fac2e77b2aa654ebeac7716f30429f 100644
--- a/media/cast/sender/vp8_encoder.cc
+++ b/media/cast/sender/vp8_encoder.cc
@@ -28,13 +28,18 @@ 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) {
config_.g_timebase.den = 0; // Not initialized.
+ for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
+ buffer_state_[i].frame_id = last_encoded_frame_id_;
+ buffer_state_[i].state = kBufferStartState;
+ }
+
// VP8 have 3 buffers available for prediction, with
// max_number_of_video_buffers_used set to 1 we maximize the coding efficiency
// however in this mode we can not skip frames in the receiver to catch up
@@ -53,34 +58,57 @@ 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() &&
+ !use_multiple_video_buffers_) {
+ 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();
+ }
+ // Reset multi-buffer mode state.
+ last_acked_frame_id_ = last_encoded_frame_id_;
+ undroppable_frames_ = 0;
for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
- buffer_state_[i].frame_id = kStartFrameId;
+ buffer_state_[i].frame_id = last_encoded_frame_id_;
buffer_state_[i].state = kBufferStartState;
}
// 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 +117,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 +126,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 +143,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 +167,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 +190,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 +235,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 +442,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_)) {
« no previous file with comments | « media/cast/sender/vp8_encoder.h ('k') | media/cast/test/fake_media_source.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698