| Index: media/cast/sender/vp8_encoder.cc
|
| diff --git a/media/cast/sender/vp8_encoder.cc b/media/cast/sender/vp8_encoder.cc
|
| index f4ecb3ec1af08d871aebbf3ac006c7e1ae5dc220..82906bfbd7ab7ac619141d3674e2defa23dad0d4 100644
|
| --- a/media/cast/sender/vp8_encoder.cc
|
| +++ b/media/cast/sender/vp8_encoder.cc
|
| @@ -17,32 +17,18 @@ namespace cast {
|
|
|
| static const uint32 kMinIntra = 300;
|
|
|
| -static int ComputeMaxNumOfRepeatedBuffers(int max_unacked_frames) {
|
| - if (max_unacked_frames > kNumberOfVp8VideoBuffers)
|
| - return (max_unacked_frames - 1) / kNumberOfVp8VideoBuffers;
|
| -
|
| - return 0;
|
| -}
|
| -
|
| Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config,
|
| int max_unacked_frames)
|
| : cast_config_(video_config),
|
| use_multiple_video_buffers_(
|
| cast_config_.max_number_of_video_buffers_used ==
|
| kNumberOfVp8VideoBuffers),
|
| - max_number_of_repeated_buffers_in_a_row_(
|
| - ComputeMaxNumOfRepeatedBuffers(max_unacked_frames)),
|
| key_frame_requested_(true),
|
| first_frame_received_(false),
|
| last_encoded_frame_id_(kStartFrameId),
|
| - number_of_repeated_buffers_(0) {
|
| - // TODO(pwestin): we need to figure out how to synchronize the acking with the
|
| - // internal state of the encoder, ideally the encoder will tell if we can
|
| - // send another frame.
|
| - DCHECK(!use_multiple_video_buffers_ ||
|
| - max_number_of_repeated_buffers_in_a_row_ == 0)
|
| - << "Invalid config";
|
| -
|
| + last_acked_frame_id_(kStartFrameId),
|
| + frame_id_to_reference_(kStartFrameId - 1),
|
| + undroppable_frames_(0) {
|
| // 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
|
| @@ -74,8 +60,8 @@ void Vp8Encoder::Initialize() {
|
| NULL, IMG_FMT_I420, cast_config_.width, cast_config_.height, 1, NULL);
|
|
|
| for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
|
| - acked_frame_buffers_[i] = true;
|
| - used_buffers_frame_id_[i] = kStartFrameId;
|
| + buffer_state_[i].frame_id = kStartFrameId;
|
| + buffer_state_[i].state = kBufferStartState;
|
| }
|
| InitEncode(cast_config_.number_of_encode_threads);
|
| }
|
| @@ -160,8 +146,7 @@ bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
|
| buffer_to_update = kLastBuffer;
|
| } else {
|
| // Reference all acked frames (buffers).
|
| - latest_frame_id_to_reference = GetLatestFrameIdToReference();
|
| - GetCodecReferenceFlags(&flags);
|
| + latest_frame_id_to_reference = GetCodecReferenceFlags(&flags);
|
| buffer_to_update = GetNextBufferToUpdate();
|
| GetCodecUpdateFlags(buffer_to_update, &flags);
|
| }
|
| @@ -214,6 +199,7 @@ bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
|
| // Populate the encoded frame.
|
| encoded_image->frame_id = ++last_encoded_frame_id_;
|
| if (is_key_frame) {
|
| + // TODO(Hubbe): Replace "dependency" with a "bool is_key_frame".
|
| encoded_image->dependency = EncodedFrame::KEY;
|
| encoded_image->referenced_frame_id = encoded_image->frame_id;
|
| } else {
|
| @@ -228,108 +214,130 @@ bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
|
| key_frame_requested_ = false;
|
|
|
| for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
|
| - used_buffers_frame_id_[i] = encoded_image->frame_id;
|
| + buffer_state_[i].state = kBufferSent;
|
| + buffer_state_[i].frame_id = encoded_image->frame_id;
|
| }
|
| - // We can pick any buffer as last_used_vp8_buffer_ since we update
|
| - // them all.
|
| - last_used_vp8_buffer_ = buffer_to_update;
|
| } else {
|
| if (buffer_to_update != kNoBuffer) {
|
| - acked_frame_buffers_[buffer_to_update] = false;
|
| - used_buffers_frame_id_[buffer_to_update] = encoded_image->frame_id;
|
| - last_used_vp8_buffer_ = buffer_to_update;
|
| + buffer_state_[buffer_to_update].state = kBufferSent;
|
| + buffer_state_[buffer_to_update].frame_id = encoded_image->frame_id;
|
| }
|
| }
|
| return true;
|
| }
|
|
|
| -void Vp8Encoder::GetCodecReferenceFlags(vpx_codec_flags_t* flags) {
|
| +uint32 Vp8Encoder::GetCodecReferenceFlags(vpx_codec_flags_t* flags) {
|
| if (!use_multiple_video_buffers_)
|
| - return;
|
| + return last_encoded_frame_id_ + 1;
|
|
|
| - // We need to reference something.
|
| - DCHECK(acked_frame_buffers_[kAltRefBuffer] ||
|
| - acked_frame_buffers_[kGoldenBuffer] ||
|
| - acked_frame_buffers_[kLastBuffer])
|
| - << "Invalid state";
|
| + const uint32 kMagicFrameOffset = 512;
|
| + // We set latest_frame_to_reference to an old frame so that
|
| + // IsNewerFrameId will work correctly.
|
| + uint32 latest_frame_to_reference =
|
| + last_encoded_frame_id_ - kMagicFrameOffset;
|
|
|
| - if (!acked_frame_buffers_[kAltRefBuffer]) {
|
| - *flags |= VP8_EFLAG_NO_REF_ARF;
|
| - }
|
| - if (!acked_frame_buffers_[kGoldenBuffer]) {
|
| - *flags |= VP8_EFLAG_NO_REF_GF;
|
| + // Reference all acked frames.
|
| + // TODO(hubbe): We may also want to allow references to the
|
| + // last encoded frame, if that frame was assigned to a buffer.
|
| + for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
|
| + if (buffer_state_[i].state == kBufferAcked) {
|
| + if (IsNewerFrameId(buffer_state_[i].frame_id,
|
| + latest_frame_to_reference)) {
|
| + latest_frame_to_reference = buffer_state_[i].frame_id;
|
| + }
|
| + } else {
|
| + switch (i) {
|
| + case kAltRefBuffer:
|
| + *flags |= VP8_EFLAG_NO_REF_ARF;
|
| + break;
|
| + case kGoldenBuffer:
|
| + *flags |= VP8_EFLAG_NO_REF_GF;
|
| + break;
|
| + case kLastBuffer:
|
| + *flags |= VP8_EFLAG_NO_REF_LAST;
|
| + break;
|
| + }
|
| + }
|
| }
|
| - if (!acked_frame_buffers_[kLastBuffer]) {
|
| - *flags |= VP8_EFLAG_NO_REF_LAST;
|
| +
|
| + if (latest_frame_to_reference ==
|
| + last_encoded_frame_id_ - kMagicFrameOffset) {
|
| + // We have nothing to reference, it's kind of like a key frame,
|
| + // but doesn't reset buffers.
|
| + latest_frame_to_reference = last_encoded_frame_id_ + 1;
|
| }
|
| +
|
| + return latest_frame_to_reference;
|
| }
|
|
|
| -uint32 Vp8Encoder::GetLatestFrameIdToReference() {
|
| +Vp8Encoder::Vp8Buffers Vp8Encoder::GetNextBufferToUpdate() {
|
| if (!use_multiple_video_buffers_)
|
| - return last_encoded_frame_id_;
|
| + return kNoBuffer;
|
|
|
| - int latest_frame_id_to_reference = -1;
|
| - if (acked_frame_buffers_[kAltRefBuffer]) {
|
| - latest_frame_id_to_reference = used_buffers_frame_id_[kAltRefBuffer];
|
| - }
|
| - if (acked_frame_buffers_[kGoldenBuffer]) {
|
| - if (latest_frame_id_to_reference == -1) {
|
| - latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer];
|
| - } else {
|
| - if (IsNewerFrameId(used_buffers_frame_id_[kGoldenBuffer],
|
| - latest_frame_id_to_reference)) {
|
| - latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer];
|
| - }
|
| + // The goal here is to make sure that we always keep one ACKed
|
| + // buffer while trying to get an ACK for a newer buffer as we go.
|
| + // Here are the rules for which buffer to select for update:
|
| + // 1. If there is a buffer in state kStartState, use it.
|
| + // 2. If there is a buffer other than the oldest buffer
|
| + // which is Acked, use the oldest buffer.
|
| + // 3. If there are Sent buffers which are older than
|
| + // latest_acked_frame_, use the oldest one.
|
| + // 4. If all else fails, just overwrite the newest buffer,
|
| + // but no more than 3 times in a row.
|
| + // TODO(hubbe): Figure out if 3 is optimal.
|
| + // Note, rule 1-3 describe cases where there is a "free" buffer
|
| + // that we can use. Rule 4 describes what happens when there is
|
| + // no free buffer available.
|
| +
|
| + // Buffers, sorted from oldest frame to newest.
|
| + Vp8Encoder::Vp8Buffers buffers[kNumberOfVp8VideoBuffers];
|
| +
|
| + for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
|
| + Vp8Encoder::Vp8Buffers buffer = static_cast<Vp8Encoder::Vp8Buffers>(i);
|
| +
|
| + // Rule 1
|
| + if (buffer_state_[buffer].state == kBufferStartState) {
|
| + undroppable_frames_ = 0;
|
| + return buffer;
|
| }
|
| + buffers[buffer] = buffer;
|
| }
|
| - if (acked_frame_buffers_[kLastBuffer]) {
|
| - if (latest_frame_id_to_reference == -1) {
|
| - latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer];
|
| - } else {
|
| - if (IsNewerFrameId(used_buffers_frame_id_[kLastBuffer],
|
| - latest_frame_id_to_reference)) {
|
| - latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer];
|
| +
|
| + // Sorting three elements with selection sort.
|
| + for (int i = 0; i < kNumberOfVp8VideoBuffers - 1; i++) {
|
| + for (int j = i + 1; j < kNumberOfVp8VideoBuffers; j++) {
|
| + if (IsOlderFrameId(buffer_state_[buffers[j]].frame_id,
|
| + buffer_state_[buffers[i]].frame_id)) {
|
| + std::swap(buffers[i], buffers[j]);
|
| }
|
| }
|
| }
|
| - DCHECK(latest_frame_id_to_reference != -1) << "Invalid state";
|
| - return static_cast<uint32>(latest_frame_id_to_reference);
|
| -}
|
|
|
| -Vp8Encoder::Vp8Buffers Vp8Encoder::GetNextBufferToUpdate() {
|
| - if (!use_multiple_video_buffers_)
|
| - return kNoBuffer;
|
| + // Rule 2
|
| + if (buffer_state_[buffers[1]].state == kBufferAcked ||
|
| + buffer_state_[buffers[2]].state == kBufferAcked) {
|
| + undroppable_frames_ = 0;
|
| + return buffers[0];
|
| + }
|
|
|
| - // Update at most one buffer, except for key-frames.
|
| + // Rule 3
|
| + for (int i = 0; i < kNumberOfVp8VideoBuffers; i++) {
|
| + if (buffer_state_[buffers[i]].state == kBufferSent &&
|
| + IsOlderFrameId(buffer_state_[buffers[i]].frame_id,
|
| + last_acked_frame_id_)) {
|
| + undroppable_frames_ = 0;
|
| + return buffers[i];
|
| + }
|
| + }
|
|
|
| - Vp8Buffers buffer_to_update = kNoBuffer;
|
| - if (number_of_repeated_buffers_ < max_number_of_repeated_buffers_in_a_row_) {
|
| - // TODO(pwestin): experiment with this. The issue with only this change is
|
| - // that we can end up with only 4 frames in flight when we expect 6.
|
| - // buffer_to_update = last_used_vp8_buffer_;
|
| - buffer_to_update = kNoBuffer;
|
| - ++number_of_repeated_buffers_;
|
| + // Rule 4
|
| + if (undroppable_frames_ >= 3) {
|
| + undroppable_frames_ = 0;
|
| + return kNoBuffer;
|
| } else {
|
| - number_of_repeated_buffers_ = 0;
|
| - switch (last_used_vp8_buffer_) {
|
| - case kAltRefBuffer:
|
| - buffer_to_update = kLastBuffer;
|
| - VLOG(1) << "VP8 update last buffer";
|
| - break;
|
| - case kLastBuffer:
|
| - buffer_to_update = kGoldenBuffer;
|
| - VLOG(1) << "VP8 update golden buffer";
|
| - break;
|
| - case kGoldenBuffer:
|
| - buffer_to_update = kAltRefBuffer;
|
| - VLOG(1) << "VP8 update alt-ref buffer";
|
| - break;
|
| - case kNoBuffer:
|
| - DCHECK(false) << "Invalid state";
|
| - break;
|
| - }
|
| + undroppable_frames_++;
|
| + return buffers[kNumberOfVp8VideoBuffers - 1];
|
| }
|
| - return buffer_to_update;
|
| }
|
|
|
| void Vp8Encoder::GetCodecUpdateFlags(Vp8Buffers buffer_to_update,
|
| @@ -381,10 +389,14 @@ void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) {
|
|
|
| VLOG(1) << "VP8 ok to reference frame:" << static_cast<int>(frame_id);
|
| for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
|
| - if (frame_id == used_buffers_frame_id_[i]) {
|
| - acked_frame_buffers_[i] = true;
|
| + if (frame_id == buffer_state_[i].frame_id) {
|
| + buffer_state_[i].state = kBufferAcked;
|
| + break;
|
| }
|
| }
|
| + if (IsOlderFrameId(last_acked_frame_id_, frame_id)) {
|
| + last_acked_frame_id_ = frame_id;
|
| + }
|
| }
|
|
|
| void Vp8Encoder::GenerateKeyFrame() {
|
|
|