Chromium Code Reviews| 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 a44d0f66bfc69869f926ffc4757dd3dbed599654..f7ff3545a0c9e4ea8d89e1193e9d25b754b96255 100644 |
| --- a/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc |
| +++ b/webrtc/modules/video_coding/codecs/vp9/vp9_impl.cc |
| @@ -56,7 +56,11 @@ VP9EncoderImpl::VP9EncoderImpl() |
| rc_max_intra_target_(0), |
| encoder_(NULL), |
| config_(NULL), |
| - raw_(NULL) { |
| + raw_(NULL), |
| + input_image_(NULL), |
| + tl0_pic_idx_(0), |
| + number_of_temporal_layers_(0), |
| + number_of_spatial_layers_(0) { |
| memset(&codec_, 0, sizeof(codec_)); |
| uint32_t seed = static_cast<uint32_t>(TickTime::MillisecondTimestamp()); |
| srand(seed); |
| @@ -90,6 +94,59 @@ int VP9EncoderImpl::Release() { |
| return WEBRTC_VIDEO_CODEC_OK; |
| } |
| +int VP9EncoderImpl::SetSvcRates() { |
| + float sRateRatio[VPX_MAX_LAYERS] = {0}; |
|
stefan-webrtc
2015/07/09 14:48:59
No camelCase.
åsapersson
2015/07/29 12:10:12
Done.
|
| + float total = 0; |
| + uint8_t i = 0; |
| + |
| + if (number_of_spatial_layers_ > 1) { |
| + for (i = 0; i < number_of_spatial_layers_; ++i) { |
| + if (svc_internal_.svc_params.scaling_factor_num[i] <= 0 || |
| + !svc_internal_.svc_params.scaling_factor_den[i]) { |
|
stefan-webrtc
2015/07/09 14:48:59
svc_internal_.svc_params.scaling_factor_den[i] <=
åsapersson
2015/07/29 12:10:12
Done.
|
| + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| + } |
| + sRateRatio[i] = |
| + (float)(svc_internal_.svc_params.scaling_factor_num[i] * 1.0 / |
|
stefan-webrtc
2015/07/09 14:48:59
static_cast. I don't think the * 1.0 matters.
åsapersson
2015/07/29 12:10:12
Changed parenthesis -> removed 1.0.
|
| + svc_internal_.svc_params.scaling_factor_den[i]); |
| + total += sRateRatio[i]; |
| + } |
| + } else { |
| + sRateRatio[0] = total = 1; |
| + } |
| + |
|
stefan-webrtc
2015/07/09 14:48:59
Assert that total = 1?
åsapersson
2015/07/29 12:10:12
added assert >=1?
|
| + for (i = 0; i < number_of_spatial_layers_; ++i) { |
| + config_->ss_target_bitrate[i] = |
| + (unsigned int)(config_->rc_target_bitrate * sRateRatio[i] / total); |
|
stefan-webrtc
2015/07/09 14:48:59
static_cast
åsapersson
2015/07/29 12:10:12
Done.
|
| + if (number_of_temporal_layers_ == 1) { |
| + config_->layer_target_bitrate[0] = config_->ss_target_bitrate[i]; |
| + } else if (number_of_temporal_layers_ == 2) { |
| + config_->layer_target_bitrate[i * number_of_temporal_layers_] = |
| + config_->ss_target_bitrate[i] * 2 / 3; |
| + config_->layer_target_bitrate[i * number_of_temporal_layers_ + 1] = |
| + config_->ss_target_bitrate[i]; |
| + } else { |
| + if (number_of_temporal_layers_ != 3) { |
| + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| + } |
| + config_->layer_target_bitrate[i * number_of_temporal_layers_] = |
| + config_->ss_target_bitrate[i] / 2; |
| + config_->layer_target_bitrate[i * number_of_temporal_layers_ + 1] = |
| + config_->layer_target_bitrate[i * number_of_temporal_layers_] + |
| + (config_->ss_target_bitrate[i] / 4); |
| + config_->layer_target_bitrate[i * number_of_temporal_layers_ + 2] = |
| + config_->ss_target_bitrate[i]; |
| + } |
| + } |
| + |
| + if (number_of_spatial_layers_ == 1) { |
| + for (i = 0; i < VPX_TS_MAX_LAYERS; ++i) { |
| + config_->ts_target_bitrate[i] = config_->layer_target_bitrate[i]; |
|
stefan-webrtc
2015/07/09 14:48:59
Are all VPX_TS_MAX_LAYERS elements of layer_target
åsapersson
2015/07/29 12:10:12
Changed to number of temporal layers.
|
| + } |
| + } |
| + |
| + return WEBRTC_VIDEO_CODEC_OK; |
|
stefan-webrtc
2015/07/09 14:48:59
Make this method return bool instead.
åsapersson
2015/07/29 12:10:12
Done.
|
| +} |
| + |
| int VP9EncoderImpl::SetRates(uint32_t new_bitrate_kbit, |
| uint32_t new_framerate) { |
| if (!inited_) { |
| @@ -107,6 +164,11 @@ int VP9EncoderImpl::SetRates(uint32_t new_bitrate_kbit, |
| } |
| config_->rc_target_bitrate = new_bitrate_kbit; |
| codec_.maxFramerate = new_framerate; |
| + |
| + int ret = SetSvcRates(); |
| + if (ret != WEBRTC_VIDEO_CODEC_OK) |
| + return ret; |
| + |
| // Update encoder context |
| if (vpx_codec_enc_config_set(encoder_, config_)) { |
| return WEBRTC_VIDEO_CODEC_ERROR; |
| @@ -147,6 +209,16 @@ int VP9EncoderImpl::InitEncode(const VideoCodec* inst, |
| if (&codec_ != inst) { |
| codec_ = *inst; |
| } |
| + |
| + // Member codec_ has access to VideoCodecVP9 (common_types.h), which should |
| + // have number of spatial layers and temporal structure mode set. |
|
stefan-webrtc
2015/07/09 14:48:59
Not sure if this comment is useful?
åsapersson
2015/07/29 12:10:12
Removed.
|
| + number_of_temporal_layers_ = inst->codecSpecific.VP9.numberOfTemporalLayers; |
| + number_of_spatial_layers_ = inst->codecSpecific.VP9.numberOfSpatialLayers; |
| + // For now, only support 1 spatial layer. |
| + if (number_of_spatial_layers_ != 1) { |
| + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| + } |
| + |
| // Random start 16 bits is enough. |
| picture_id_ = static_cast<uint16_t>(rand()) & 0x7FFF; |
| // Allocate memory for encoded image |
| @@ -199,12 +271,53 @@ int VP9EncoderImpl::InitEncode(const VideoCodec* inst, |
| config_->g_threads = NumberOfThreads(config_->g_w, |
| config_->g_h, |
| number_of_cores); |
| + |
| + if (number_of_temporal_layers_ == 1) { |
| + gof_.SetGofInfoVP9(kTemporalStructureMode1); |
| + config_->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_NOLAYERING; |
| + config_->ts_number_layers = 1; |
| + config_->ts_rate_decimator[0] = 1; |
| + config_->ts_periodicity = 1; |
| + config_->ts_layer_id[0] = 0; |
| + } else if (number_of_temporal_layers_ == 2) { |
| + gof_.SetGofInfoVP9(kTemporalStructureMode2); |
| + config_->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_0101; |
| + 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; |
| + } else if (number_of_temporal_layers_ == 3) { |
| + gof_.SetGofInfoVP9(kTemporalStructureMode3); |
| + config_->temporal_layering_mode = VP9E_TEMPORAL_LAYERING_MODE_0212; |
| + config_->ts_number_layers = 3; |
| + config_->ts_rate_decimator[0] = 4; |
| + config_->ts_rate_decimator[1] = 2; |
| + config_->ts_rate_decimator[2] = 1; |
| + config_->ts_periodicity = 4; |
| + config_->ts_layer_id[0] = 0; |
| + config_->ts_layer_id[1] = 2; |
| + config_->ts_layer_id[2] = 1; |
| + config_->ts_layer_id[3] = 2; |
| + } else { |
| + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
| + } |
| + |
| + tl0_pic_idx_ = static_cast<uint8_t>(rand()); |
| + |
| return InitAndSetControlSettings(inst); |
| } |
| int VP9EncoderImpl::NumberOfThreads(int width, |
| int height, |
| int number_of_cores) { |
| + // For the current libvpx library, only 1 thread is supported when SVC is |
| + // turned on. |
| + if (number_of_temporal_layers_ > 1 || number_of_spatial_layers_ > 1) { |
| + return 1; |
| + } |
| + |
| // Keep the number of encoder threads equal to the possible number of column |
| // tiles, which is (1, 2, 4, 8). See comments below for VP9E_SET_TILE_COLUMNS. |
| if (width * height >= 1280 * 720 && number_of_cores > 4) { |
| @@ -218,6 +331,26 @@ int VP9EncoderImpl::NumberOfThreads(int width, |
| } |
| int VP9EncoderImpl::InitAndSetControlSettings(const VideoCodec* inst) { |
| + |
| + config_->ss_number_layers = number_of_spatial_layers_; |
| + |
| + if (number_of_spatial_layers_ > 1) { |
| + config_->rc_min_quantizer = 0; |
| + config_->rc_max_quantizer = 63; |
| + } |
| + for (uint8_t i = 0; i < number_of_spatial_layers_; i++) { |
| + svc_internal_.svc_params.max_quantizers[i] = config_->rc_max_quantizer; |
| + svc_internal_.svc_params.min_quantizers[i] = config_->rc_min_quantizer; |
| + /* 1:2 scaling in each dimension */ |
|
stefan-webrtc
2015/07/09 14:48:59
Fix this comment.
åsapersson
2015/07/29 12:10:12
Done.
|
| + svc_internal_.svc_params.scaling_factor_num[i] = |
| + 256 >> (number_of_spatial_layers_ - i - 1); |
|
stefan-webrtc
2015/07/09 14:48:59
Can't we divide instead of shifting? I think it ma
åsapersson
2015/07/29 12:10:12
Done.
|
| + svc_internal_.svc_params.scaling_factor_den[i] = 256; |
| + } |
| + |
| + int ret = SetSvcRates(); |
| + if (ret != WEBRTC_VIDEO_CODEC_OK) |
| + return ret; |
| + |
| if (vpx_codec_enc_init(encoder_, vpx_codec_vp9_cx(), config_, 0)) { |
| return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
| } |
| @@ -231,6 +364,20 @@ int VP9EncoderImpl::InitAndSetControlSettings(const VideoCodec* inst) { |
| rc_max_intra_target_); |
| vpx_codec_control(encoder_, VP9E_SET_AQ_MODE, |
| inst->codecSpecific.VP9.adaptiveQpMode ? 3 : 0); |
| + |
| + vpx_codec_control( |
| + encoder_, VP9E_SET_SVC, |
| + (number_of_temporal_layers_ > 1 || number_of_spatial_layers_ > 1) ? 1 |
| + : 0); |
| + if (number_of_spatial_layers_ > 1) { |
| + vpx_codec_control(encoder_, VP9E_SET_SVC_PARAMETERS, |
| + &svc_internal_.svc_params); |
| + } |
| + // Register callback for getting each spatial layer |
| + vpx_codec_priv_output_cx_pkt_cb_pair_t cbp = { |
| + VP9EncoderImpl::EncocderOutputCodedPacketCb, (void*)(this)}; |
| + vpx_codec_control(encoder_, VP9E_REGISTER_CX_CALLBACK, (void*)(&cbp)); |
| + |
| // Control function to set the number of column tiles in encoding a frame, in |
| // log2 unit: e.g., 0 = 1 tile column, 1 = 2 tile columns, 2 = 4 tile columns. |
| // The number tile columns will be capped by the encoder based on image size |
| @@ -242,6 +389,7 @@ int VP9EncoderImpl::InitAndSetControlSettings(const VideoCodec* inst) { |
| vpx_codec_control(encoder_, VP9E_SET_NOISE_SENSITIVITY, |
| inst->codecSpecific.VP9.denoisingOn ? 1 : 0); |
| #endif |
| + |
| inited_ = true; |
| return WEBRTC_VIDEO_CODEC_OK; |
| } |
| @@ -280,6 +428,13 @@ int VP9EncoderImpl::Encode(const VideoFrame& input_image, |
| } |
| DCHECK_EQ(input_image.width(), static_cast<int>(raw_->d_w)); |
| DCHECK_EQ(input_image.height(), static_cast<int>(raw_->d_h)); |
| + |
| + // Set input image for use in the callback. |
| + // This was necessary since you need some information from input_image. |
| + // You can save only the necessary information (such as timestamp) instead of |
| + // doing this. |
| + input_image_ = &input_image; |
| + |
| // Image in vpx_image_t format. |
| // Input image is const. VPX's raw image is not defined as const. |
| raw_->planes[VPX_PLANE_Y] = const_cast<uint8_t*>(input_image.buffer(kYPlane)); |
| @@ -302,7 +457,8 @@ int VP9EncoderImpl::Encode(const VideoFrame& input_image, |
| return WEBRTC_VIDEO_CODEC_ERROR; |
| } |
| timestamp_ += duration; |
| - return GetEncodedPartitions(input_image); |
| + |
| + return WEBRTC_VIDEO_CODEC_OK; |
| } |
| void VP9EncoderImpl::PopulateCodecSpecific(CodecSpecificInfo* codec_specific, |
| @@ -311,20 +467,81 @@ void VP9EncoderImpl::PopulateCodecSpecific(CodecSpecificInfo* codec_specific, |
| assert(codec_specific != NULL); |
| codec_specific->codecType = kVideoCodecVP9; |
| CodecSpecificInfoVP9 *vp9_info = &(codec_specific->codecSpecific.VP9); |
| + // TODO(asapersson): Set correct values. |
| + vp9_info->interPicPredicted = |
| + (pkt.data.frame.flags & VPX_FRAME_IS_KEY) ? false : true; |
| + vp9_info->flexibleMode = codec_.codecSpecific.VP9.flexibleMode; |
| + vp9_info->beginningOfFrame = true; |
| + vp9_info->endOfFrame = true; |
| + vp9_info->ssDataAvailable = |
| + (pkt.data.frame.flags & VPX_FRAME_IS_KEY) ? true : false; |
| + |
| + vpx_svc_layer_id_t lyr = {0}; |
|
stefan-webrtc
2015/07/09 14:48:59
layer_id
åsapersson
2015/07/29 12:10:12
Done.
|
| + vpx_codec_control(encoder_, VP9E_GET_SVC_LAYER_ID, &lyr); |
| + |
| + assert(number_of_temporal_layers_ > 0); |
| + assert(number_of_spatial_layers_ > 0); |
| + if (number_of_temporal_layers_ == 1) { |
| + assert(lyr.temporal_layer_id == 0); |
| + vp9_info->temporalIdx = kNoTemporalIdx; |
| + } else { |
| + vp9_info->temporalIdx = lyr.temporal_layer_id; |
| + } |
| + if (number_of_spatial_layers_ == 1) { |
| + assert(lyr.spatial_layer_id == 0); |
| + vp9_info->spatialIdx = kNoSpatialIdx; |
| + } else { |
| + vp9_info->spatialIdx = lyr.spatial_layer_id; |
| + } |
| + if (lyr.spatial_layer_id != 0) { |
| + vp9_info->ssDataAvailable = false; |
| + } |
| + |
| + if (vp9_info->flexibleMode) { |
| + vp9_info->gofIdx = kNoGofIdx; |
| + } else { |
| + vp9_info->gofIdx = lyr.temporal_layer_id; |
| + } |
| + |
| + // TODO(asapersson): this info has to be obtained from the encoder. |
| + vp9_info->temporalUpSwitch = true; |
| + |
| + if (lyr.spatial_layer_id == 0) { |
| + picture_id_ = (picture_id_ + 1) & 0x7FFF; |
| + // TODO(asapersson): this info has to be obtained from the encoder. |
| + vp9_info->interLayerPredicted = false; |
| + } else { |
| + // TODO(asapersson): this info has to be obtained from the encoder. |
| + vp9_info->interLayerPredicted = true; |
| + } |
| + |
| vp9_info->pictureId = picture_id_; |
| - vp9_info->keyIdx = kNoKeyIdx; |
| - vp9_info->nonReference = (pkt.data.frame.flags & VPX_FRAME_IS_DROPPABLE) != 0; |
| - // TODO(marpan): Temporal layers are supported in the current VP9 version, |
| - // but for now use 1 temporal layer encoding. Will update this when temporal |
| - // layer support for VP9 is added in webrtc. |
| - vp9_info->temporalIdx = kNoTemporalIdx; |
| - vp9_info->layerSync = false; |
| - vp9_info->tl0PicIdx = kNoTl0PicIdx; |
| - picture_id_ = (picture_id_ + 1) & 0x7FFF; |
| + |
| + if (!vp9_info->flexibleMode) { |
| + if (lyr.temporal_layer_id == 0 && lyr.spatial_layer_id == 0) { |
| + tl0_pic_idx_++; |
| + } |
| + vp9_info->tl0PicIdx = tl0_pic_idx_; |
| + } |
| + |
| + if (vp9_info->ssDataAvailable) { |
| + vp9_info->numSpatialLayers = number_of_spatial_layers_; |
| + vp9_info->spatialLayerResolutionPresent = true; |
| + for (uint8_t i = 0; i < vp9_info->numSpatialLayers; i++) { |
| + vp9_info->width[i] = codec_.width * |
| + svc_internal_.svc_params.scaling_factor_num[i] / |
| + svc_internal_.svc_params.scaling_factor_den[i]; |
| + vp9_info->height[i] = codec_.height * |
| + svc_internal_.svc_params.scaling_factor_num[i] / |
| + svc_internal_.svc_params.scaling_factor_den[i]; |
| + } |
| + if (!vp9_info->flexibleMode) { |
| + vp9_info->gof.CopyGofInfoVP9(gof_); |
| + } |
| + } |
| } |
| -int VP9EncoderImpl::GetEncodedPartitions(const VideoFrame& input_image) { |
| - vpx_codec_iter_t iter = NULL; |
| +int VP9EncoderImpl::GetEncodedLayerFrame(const vpx_codec_cx_pkt* pkt) { |
| encoded_image_._length = 0; |
| encoded_image_._frameType = kDeltaFrame; |
| RTPFragmentationHeader frag_info; |
| @@ -333,44 +550,34 @@ int VP9EncoderImpl::GetEncodedPartitions(const VideoFrame& input_image) { |
| frag_info.VerifyAndAllocateFragmentationHeader(1); |
| int part_idx = 0; |
| CodecSpecificInfo codec_specific; |
| - const vpx_codec_cx_pkt_t *pkt = NULL; |
| - while ((pkt = vpx_codec_get_cx_data(encoder_, &iter)) != NULL) { |
| - switch (pkt->kind) { |
| - case VPX_CODEC_CX_FRAME_PKT: { |
| - memcpy(&encoded_image_._buffer[encoded_image_._length], |
| - pkt->data.frame.buf, |
| - pkt->data.frame.sz); |
| - frag_info.fragmentationOffset[part_idx] = encoded_image_._length; |
| - frag_info.fragmentationLength[part_idx] = |
| - static_cast<uint32_t>(pkt->data.frame.sz); |
| - frag_info.fragmentationPlType[part_idx] = 0; |
| - frag_info.fragmentationTimeDiff[part_idx] = 0; |
| - encoded_image_._length += static_cast<uint32_t>(pkt->data.frame.sz); |
| - assert(encoded_image_._length <= encoded_image_._size); |
| - break; |
| - } |
| - default: { |
| - break; |
| - } |
| - } |
| - // End of frame. |
| - if ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0) { |
| - // Check if encoded frame is a key frame. |
| - if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) { |
| - encoded_image_._frameType = kKeyFrame; |
| - } |
| - PopulateCodecSpecific(&codec_specific, *pkt, input_image.timestamp()); |
| - break; |
| + |
| + assert(pkt->kind == VPX_CODEC_CX_FRAME_PKT); |
| + memcpy(&encoded_image_._buffer[encoded_image_._length], pkt->data.frame.buf, |
| + pkt->data.frame.sz); |
| + frag_info.fragmentationOffset[part_idx] = encoded_image_._length; |
| + frag_info.fragmentationLength[part_idx] = |
| + static_cast<uint32_t>(pkt->data.frame.sz); |
| + frag_info.fragmentationPlType[part_idx] = 0; |
| + frag_info.fragmentationTimeDiff[part_idx] = 0; |
| + encoded_image_._length += static_cast<uint32_t>(pkt->data.frame.sz); |
| + assert(encoded_image_._length <= encoded_image_._size); |
| + |
| + // End of frame. |
| + if ((pkt->data.frame.flags & VPX_FRAME_IS_FRAGMENT) == 0) { |
| + // Check if encoded frame is a key frame. |
| + if (pkt->data.frame.flags & VPX_FRAME_IS_KEY) { |
| + encoded_image_._frameType = kKeyFrame; |
| } |
| + PopulateCodecSpecific(&codec_specific, *pkt, input_image_->timestamp()); |
| } |
| if (encoded_image_._length > 0) { |
| TRACE_COUNTER1("webrtc", "EncodedFrameSize", encoded_image_._length); |
| - encoded_image_._timeStamp = input_image.timestamp(); |
| - encoded_image_.capture_time_ms_ = input_image.render_time_ms(); |
| + encoded_image_._timeStamp = input_image_->timestamp(); |
| + encoded_image_.capture_time_ms_ = input_image_->render_time_ms(); |
| encoded_image_._encodedHeight = raw_->d_h; |
| encoded_image_._encodedWidth = raw_->d_w; |
| encoded_complete_callback_->Encoded(encoded_image_, &codec_specific, |
| - &frag_info); |
| + &frag_info); |
| } |
| return WEBRTC_VIDEO_CODEC_OK; |
| } |