Index: webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc |
diff --git a/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc b/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc |
index 73beab37b0627c44f065a784afd7e8f601281894..e59ddc302389ed375b73a6fde6830030567b28a4 100644 |
--- a/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc |
+++ b/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc |
@@ -76,9 +76,10 @@ VP9EncoderImpl::VP9EncoderImpl() |
raw_(NULL), |
input_image_(NULL), |
tl0_pic_idx_(0), |
- gof_idx_(0), |
+ frames_since_kf_(0), |
num_temporal_layers_(0), |
- num_spatial_layers_(0) { |
+ num_spatial_layers_(0), |
+ frames_encoded_(0) { |
memset(&codec_, 0, sizeof(codec_)); |
uint32_t seed = static_cast<uint32_t>(TickTime::MillisecondTimestamp()); |
srand(seed); |
@@ -292,7 +293,26 @@ int VP9EncoderImpl::InitEncode(const VideoCodec* inst, |
// TODO(asapersson): Check configuration of temporal switch up and increase |
// pattern length. |
- if (num_temporal_layers_ == 1) { |
+ is_flexible_mode_ = inst->codecSpecific.VP9.flexibleMode; |
+ if (is_flexible_mode_) { |
+ config_->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_BYPASS; |
+ config_->ts_number_layers = num_temporal_layers_; |
+ |
+ if (codec_.mode == kScreensharing) { |
+ // TODO(philipel): Using VP8 style screen sharing. Change conditions |
+ // when flexible mode + spatial layers become available. |
+ assert(inst->codecSpecific.VP9.numberOfTemporalLayers == 2); |
sprang_webrtc
2015/09/07 14:39:05
CHECK_EQ(2, ...)
philipel
2015/09/10 14:47:02
Code removed, now use spatial layers instead. Hurr
|
+ config_->ts_number_layers = 2; |
+ config_->ts_rate_decimator[0] = 2; |
+ config_->ts_rate_decimator[1] = 1; |
+ config_->ts_periodicity = 2; |
+ config_->ts_layer_id[0] = 0; |
+ config_->ts_layer_id[1] = 1; |
+ |
+ // TODO(philipel): The following should be configurable, not fixed: |
+ temporal_layer_.ConfigureBitrate(1000); |
sprang_webrtc
2015/09/07 14:39:05
Why not set to start bitrate from VideoCodec param
philipel
2015/09/10 14:47:02
Done.
|
+ } |
+ } else if (num_temporal_layers_ == 1) { |
gof_.SetGofInfoVP9(kTemporalStructureMode1); |
config_->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_NOLAYERING; |
config_->ts_number_layers = 1; |
@@ -458,12 +478,64 @@ int VP9EncoderImpl::Encode(const VideoFrame& input_image, |
raw_->stride[VPX_PLANE_U] = input_image.stride(kUPlane); |
raw_->stride[VPX_PLANE_V] = input_image.stride(kVPlane); |
- int flags = 0; |
+ vpx_enc_frame_flags_t flags = 0; |
bool send_keyframe = (frame_type == kKeyFrame); |
if (send_keyframe) { |
// Key frame request from caller. |
flags = VPX_EFLAG_FORCE_KF; |
} |
+ |
+ if (is_flexible_mode_) { |
+ vpx_svc_layer_id_t svc_layer; |
+ svc_layer.spatial_layer_id = 0; |
+ if (send_keyframe) { |
+ flags = GenerateRefsAndFlags(); |
+ } else if (codec_.mode == kRealtimeVideo) { |
+ // TODO(philipel): For now produce the 0-2-1-2 mod 8 pattern |
+ switch ((frames_since_kf_ - 1) % 8) { |
+ case 0: { |
+ flags = GenerateRefsAndFlags(0, 0); |
+ break; |
+ } |
+ case 1: { |
+ flags = GenerateRefsAndFlags(2, 0); |
+ break; |
+ } |
+ case 2: { |
+ flags = GenerateRefsAndFlags(1, 0); |
+ break; |
+ } |
+ case 3: { |
+ flags = GenerateRefsAndFlags(2, 0, 1, 2); |
+ break; |
+ } |
+ case 4: { |
+ flags = GenerateRefsAndFlags(0, 0); |
+ break; |
+ } |
+ case 5: { |
+ flags = GenerateRefsAndFlags(2, 0, 1, 2); |
+ break; |
+ } |
+ case 6: { |
+ flags = GenerateRefsAndFlags(1, 0, 1); |
+ break; |
+ } |
+ case 7: { |
+ flags = GenerateRefsAndFlags(2, 0, 1, 2); |
+ break; |
+ } |
+ } |
+ static const int temporal_layers[4] = {0, 2, 1, 2}; |
+ svc_layer.temporal_layer_id = temporal_layers[(frames_since_kf_ - 1) % 4]; |
+ } else { |
+ flags = GenerateRefsAndFlags( |
+ temporal_layer_.BufferArguments(input_image.timestamp())); |
+ svc_layer.temporal_layer_id = temporal_layer_.CurrentLayer(); |
+ } |
+ vpx_codec_control(encoder_, VP9E_SET_SVC_LAYER_ID, &svc_layer); |
+ } |
+ |
assert(codec_.maxFramerate > 0); |
uint32_t duration = 90000 / codec_.maxFramerate; |
if (vpx_codec_encode(encoder_, raw_, timestamp_, duration, flags, |
@@ -489,9 +561,8 @@ void VP9EncoderImpl::PopulateCodecSpecific(CodecSpecificInfo* codec_specific, |
!codec_.codecSpecific.VP9.flexibleMode) |
? true |
: false; |
- if (pkt.data.frame.flags & VPX_FRAME_IS_KEY) { |
- gof_idx_ = 0; |
- } |
+ if (pkt.data.frame.flags & VPX_FRAME_IS_KEY) |
+ frames_since_kf_ = 0; |
vpx_svc_layer_id_t layer_id = {0}; |
vpx_codec_control(encoder_, VP9E_GET_SVC_LAYER_ID, &layer_id); |
@@ -514,13 +585,6 @@ void VP9EncoderImpl::PopulateCodecSpecific(CodecSpecificInfo* codec_specific, |
vp9_info->ss_data_available = false; |
} |
- if (vp9_info->flexible_mode) { |
- vp9_info->gof_idx = kNoGofIdx; |
- } else { |
- vp9_info->gof_idx = |
- static_cast<uint8_t>(gof_idx_++ % gof_.num_frames_in_gof); |
- } |
- |
// TODO(asapersson): this info has to be obtained from the encoder. |
vp9_info->temporal_up_switch = true; |
@@ -542,6 +606,21 @@ void VP9EncoderImpl::PopulateCodecSpecific(CodecSpecificInfo* codec_specific, |
vp9_info->tl0_pic_idx = tl0_pic_idx_; |
} |
+ vp9_info->num_ref_pics = 0; |
+ if (vp9_info->flexible_mode) { |
+ vp9_info->gof_idx = kNoGofIdx; |
+ if (!(pkt.data.frame.flags & VPX_FRAME_IS_KEY)) { |
+ vp9_info->num_ref_pics = num_refs_pics_; |
+ for (int i = 0; i < num_refs_pics_; ++i) { |
+ vp9_info->p_diff[i] = p_diff_[i]; |
+ } |
+ } |
+ } else { |
+ vp9_info->gof_idx = |
+ static_cast<uint8_t>(frames_since_kf_ % gof_.num_frames_in_gof); |
+ } |
+ ++frames_since_kf_; |
+ |
if (vp9_info->ss_data_available) { |
vp9_info->num_spatial_layers = num_spatial_layers_; |
vp9_info->spatial_layer_resolution_present = true; |
@@ -578,6 +657,9 @@ int VP9EncoderImpl::GetEncodedLayerFrame(const vpx_codec_cx_pkt* pkt) { |
frag_info.fragmentationPlType[part_idx] = 0; |
frag_info.fragmentationTimeDiff[part_idx] = 0; |
encoded_image_._length += static_cast<uint32_t>(pkt->data.frame.sz); |
+ if (is_flexible_mode_ && codec_.mode == kScreensharing) { |
+ temporal_layer_.FrameEncoded(encoded_image_._length); |
+ } |
sprang_webrtc
2015/09/07 14:39:05
Remove {}
philipel
2015/09/10 14:47:02
Done.
|
assert(encoded_image_._length <= encoded_image_._size); |
// End of frame. |
@@ -599,6 +681,94 @@ int VP9EncoderImpl::GetEncodedLayerFrame(const vpx_codec_cx_pkt* pkt) { |
return WEBRTC_VIDEO_CODEC_OK; |
} |
+vpx_enc_frame_flags_t VP9EncoderImpl::GenerateRefsAndFlags(int8_t upd_buffer, |
+ int8_t ref_buf1, |
+ int8_t ref_buf2, |
+ int8_t ref_buf3) { |
+ // For now we only use 3 out of the 8 buffers available |
+ DCHECK(upd_buffer < 3); |
sprang_webrtc
2015/09/07 14:39:05
DCHECK_LT
philipel
2015/09/10 14:47:02
Code removed.
|
+ |
+ // BUF0 = LAST |
+ // BUF1 = GF |
+ // BUF2 = ARF |
+ num_refs_pics_ = 0; |
+ vpx_enc_frame_flags_t flags = VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_LAST | |
+ VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_ARF | |
+ VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_LAST; |
+ if (upd_buffer == -2) { |
sprang_webrtc
2015/09/07 14:39:05
Have a named constant for -2?
philipel
2015/09/10 14:47:02
Code removed.
|
+#ifdef NDEBUG |
sprang_webrtc
2015/09/07 14:39:05
Can't just always do this? memset it?
philipel
2015/09/10 14:47:02
Done.
|
+ // Used later on to make sure we don't make any invalid references. |
+ for (int i = 0; i < 8; ++i) |
+ buf_upd_at_frame_[i] = -1; |
+#endif |
+ |
+ // Keyframe, always stored in BUF0. Why? Because of line 161 in |
sprang_webrtc
2015/09/07 14:39:05
This comment may grow stale quickly :)
"Keyframe a
philipel
2015/09/10 14:47:02
Changed so that the keyframes are stored in the bu
|
+ // vpx_temporal_svc_encoder.c. I'm not sure if that comment applies here |
+ buf_upd_at_frame_[0] = frames_encoded_; |
+ flags = VPX_EFLAG_FORCE_KF | VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF; |
+ } else { |
+ int8_t refs[3] = {ref_buf1, ref_buf2, ref_buf3}; |
sprang_webrtc
2015/09/07 14:39:05
I'd probably add a named constant for 3 and use he
philipel
2015/09/10 14:47:01
Now use the kMaxVp9RefPics instead of '3'.
|
+ for (int i = 0; i < 3; ++i) { |
+ switch (refs[i]) { |
+ case -1: |
+ goto done; |
sprang_webrtc
2015/09/07 14:39:05
Ugh! How bout:
i = kNumReferences;
continue;
Per
philipel
2015/09/10 14:47:02
Code removed.
|
+ case 0: { |
+ flags ^= VP8_EFLAG_NO_REF_LAST; |
+ break; |
+ } |
+ case 1: { |
+ flags ^= VP8_EFLAG_NO_REF_GF; |
+ break; |
+ } |
+ case 2: { |
+ flags ^= VP8_EFLAG_NO_REF_ARF; |
+ break; |
+ } |
+ default: |
+ DCHECK(false); |
sprang_webrtc
2015/09/07 14:39:05
RTC_NOTREACHED();
philipel
2015/09/10 14:47:02
Code removed.
|
+ } |
+ |
+ // Make sure this frame doesn't reference to an unavailable |
+ // buffer, either because it has not yet been used or |
+ // because a KF has occurred since it was used. |
+ DCHECK(buf_upd_at_frame_[refs[i]] != -1); |
+ |
+ p_diff_[i] = frames_encoded_ - buf_upd_at_frame_[refs[i]]; |
+ num_refs_pics_++; |
+ } |
+ // Everybody love gotos! |
sprang_webrtc
2015/09/07 14:39:05
No. :)
philipel
2015/09/10 14:47:02
Yes!
|
+ done: |
+ |
+ buf_upd_at_frame_[upd_buffer] = frames_encoded_; |
+ switch (upd_buffer) { |
+ case -1: |
+ break; |
+ case 0: { |
+ flags ^= VP8_EFLAG_NO_UPD_LAST; |
+ break; |
+ } |
+ case 1: { |
+ flags ^= VP8_EFLAG_NO_UPD_GF; |
+ break; |
+ } |
+ case 2: { |
+ flags ^= VP8_EFLAG_NO_UPD_ARF; |
+ break; |
+ } |
+ default: |
+ DCHECK(false); |
sprang_webrtc
2015/09/07 14:39:05
RTC_NOTREACHED();
philipel
2015/09/10 14:47:01
Code removed.
|
+ } |
+ } |
+ |
+ frames_encoded_++; |
+ return flags; |
+} |
+ |
+vpx_enc_frame_flags_t VP9EncoderImpl::GenerateRefsAndFlags( |
+ std::array<int8_t, 4> args) { |
+ return GenerateRefsAndFlags(args[0], args[1], args[2], args[3]); |
+} |
+ |
int VP9EncoderImpl::SetChannelParameters(uint32_t packet_loss, int64_t rtt) { |
return WEBRTC_VIDEO_CODEC_OK; |
} |