blob: 5faa193f9fa0acae393d5e7c3c02bb3ba8b796b8 [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
sprang3911c262016-04-15 01:24:14 -07002* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3*
4* Use of this source code is governed by a BSD-style license
5* that can be found in the LICENSE file in the root of the source
6* tree. An additional intellectual property rights grant can be found
7* in the file PATENTS. All contributing project authors may
8* be found in the AUTHORS file in the root of the source tree.
9*/
niklase@google.com470e71d2011-07-07 08:21:25 +000010
philipel9d3ab612015-12-21 04:12:39 -080011#include "webrtc/modules/video_coding/generic_encoder.h"
12
13#include <vector>
14
nisseaf916892017-01-10 07:44:26 -080015#include "webrtc/api/video/i420_buffer.h"
Guo-wei Shieh2c370782015-04-08 13:00:10 -070016#include "webrtc/base/checks.h"
pbos854e84c2015-11-16 16:39:06 -080017#include "webrtc/base/logging.h"
ilnik04f4d122017-06-19 07:18:55 -070018#include "webrtc/base/timeutils.h"
pbosd9eec762015-11-17 06:03:43 -080019#include "webrtc/base/trace_event.h"
Henrik Kjellander2557b862015-11-18 22:00:21 +010020#include "webrtc/modules/video_coding/encoded_frame.h"
Henrik Kjellander2557b862015-11-18 22:00:21 +010021#include "webrtc/modules/video_coding/media_optimization.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000022
23namespace webrtc {
Sergey Ulanov525df3f2016-08-02 17:46:41 -070024
Peter Boström4f5db112015-10-29 16:53:59 +010025VCMGenericEncoder::VCMGenericEncoder(
26 VideoEncoder* encoder,
Peter Boström4f5db112015-10-29 16:53:59 +010027 VCMEncodedFrameCallback* encoded_frame_callback,
sprang3911c262016-04-15 01:24:14 -070028 bool internal_source)
pbos@webrtc.org891d4832015-02-26 13:15:22 +000029 : encoder_(encoder),
Peter Boström4f5db112015-10-29 16:53:59 +010030 vcm_encoded_frame_callback_(encoded_frame_callback),
sprang3911c262016-04-15 01:24:14 -070031 internal_source_(internal_source),
Erik Språng08127a92016-11-16 16:41:30 +010032 encoder_params_({BitrateAllocation(), 0, 0, 0}),
ilnik04f4d122017-06-19 07:18:55 -070033 is_screenshare_(false),
34 streams_or_svc_num_(0) {}
niklase@google.com470e71d2011-07-07 08:21:25 +000035
Peter Boström4f5db112015-10-29 16:53:59 +010036VCMGenericEncoder::~VCMGenericEncoder() {}
37
38int32_t VCMGenericEncoder::Release() {
Peter Boström02bafc62016-07-01 12:45:15 +020039 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
Peter Boström4fd6cda2016-01-26 10:19:53 +010040 TRACE_EVENT0("webrtc", "VCMGenericEncoder::Release");
Peter Boström4f5db112015-10-29 16:53:59 +010041 return encoder_->Release();
niklase@google.com470e71d2011-07-07 08:21:25 +000042}
43
Peter Boström4f5db112015-10-29 16:53:59 +010044int32_t VCMGenericEncoder::InitEncode(const VideoCodec* settings,
sprang3911c262016-04-15 01:24:14 -070045 int32_t number_of_cores,
46 size_t max_payload_size) {
Peter Boström02bafc62016-07-01 12:45:15 +020047 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
pbosd9eec762015-11-17 06:03:43 -080048 TRACE_EVENT0("webrtc", "VCMGenericEncoder::InitEncode");
Peter Boström4f5db112015-10-29 16:53:59 +010049 is_screenshare_ = settings->mode == VideoCodecMode::kScreensharing;
ilnik04f4d122017-06-19 07:18:55 -070050 streams_or_svc_num_ = settings->numberOfSimulcastStreams;
51 if (settings->codecType == kVideoCodecVP9) {
52 streams_or_svc_num_ = settings->VP9().numberOfSpatialLayers;
53 }
54 if (streams_or_svc_num_ == 0)
55 streams_or_svc_num_ = 1;
56
57 vcm_encoded_frame_callback_->SetTimingFramesThresholds(
58 settings->timing_frame_thresholds);
59 vcm_encoded_frame_callback_->OnFrameRateChanged(settings->maxFramerate);
60
sprang3911c262016-04-15 01:24:14 -070061 if (encoder_->InitEncode(settings, number_of_cores, max_payload_size) != 0) {
Peter Boström4f5db112015-10-29 16:53:59 +010062 LOG(LS_ERROR) << "Failed to initialize the encoder associated with "
63 "payload name: "
64 << settings->plName;
65 return -1;
66 }
67 encoder_->RegisterEncodeCompleteCallback(vcm_encoded_frame_callback_);
68 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +000069}
70
sprang3911c262016-04-15 01:24:14 -070071int32_t VCMGenericEncoder::Encode(const VideoFrame& frame,
72 const CodecSpecificInfo* codec_specific,
73 const std::vector<FrameType>& frame_types) {
Peter Boström02bafc62016-07-01 12:45:15 +020074 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
pbosd9eec762015-11-17 06:03:43 -080075 TRACE_EVENT1("webrtc", "VCMGenericEncoder::Encode", "timestamp",
sprang3911c262016-04-15 01:24:14 -070076 frame.timestamp());
pbosd9eec762015-11-17 06:03:43 -080077
sprang3911c262016-04-15 01:24:14 -070078 for (FrameType frame_type : frame_types)
pbos22993e12015-10-19 02:39:06 -070079 RTC_DCHECK(frame_type == kVideoFrameKey || frame_type == kVideoFrameDelta);
guoweis@webrtc.org54d072e2015-03-17 21:54:50 +000080
ilnik04f4d122017-06-19 07:18:55 -070081 for (size_t i = 0; i < streams_or_svc_num_; ++i)
82 vcm_encoded_frame_callback_->OnEncodeStarted(frame.render_time_ms(), i);
sprang3911c262016-04-15 01:24:14 -070083 int32_t result = encoder_->Encode(frame, codec_specific, &frame_types);
Peter Boströmb7d9a972015-12-18 16:01:11 +010084
Erik Språng2c4c9142015-06-24 11:24:44 +020085 if (is_screenshare_ &&
86 result == WEBRTC_VIDEO_CODEC_TARGET_BITRATE_OVERSHOOT) {
87 // Target bitrate exceeded, encoder state has been reset - try again.
sprang3911c262016-04-15 01:24:14 -070088 return encoder_->Encode(frame, codec_specific, &frame_types);
Erik Språng2c4c9142015-06-24 11:24:44 +020089 }
90
91 return result;
niklase@google.com470e71d2011-07-07 08:21:25 +000092}
93
Peter Boström69ccb332015-10-29 16:30:23 +010094void VCMGenericEncoder::SetEncoderParameters(const EncoderParameters& params) {
Peter Boström02bafc62016-07-01 12:45:15 +020095 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
Peter Boström69ccb332015-10-29 16:30:23 +010096 bool channel_parameters_have_changed;
97 bool rates_have_changed;
98 {
99 rtc::CritScope lock(&params_lock_);
100 channel_parameters_have_changed =
101 params.loss_rate != encoder_params_.loss_rate ||
102 params.rtt != encoder_params_.rtt;
103 rates_have_changed =
104 params.target_bitrate != encoder_params_.target_bitrate ||
105 params.input_frame_rate != encoder_params_.input_frame_rate;
106 encoder_params_ = params;
107 }
Erik Språng08127a92016-11-16 16:41:30 +0100108 if (channel_parameters_have_changed) {
109 int res = encoder_->SetChannelParameters(params.loss_rate, params.rtt);
110 if (res != 0) {
111 LOG(LS_WARNING) << "Error set encoder parameters (loss = "
112 << params.loss_rate << ", rtt = " << params.rtt
113 << "): " << res;
114 }
115 }
Peter Boström69ccb332015-10-29 16:30:23 +0100116 if (rates_have_changed) {
Erik Språng08127a92016-11-16 16:41:30 +0100117 int res = encoder_->SetRateAllocation(params.target_bitrate,
118 params.input_frame_rate);
119 if (res != 0) {
120 LOG(LS_WARNING) << "Error set encoder rate (total bitrate bps = "
121 << params.target_bitrate.get_sum_bps()
122 << ", framerate = " << params.input_frame_rate
123 << "): " << res;
124 }
ilnik04f4d122017-06-19 07:18:55 -0700125 vcm_encoded_frame_callback_->OnFrameRateChanged(params.input_frame_rate);
126 for (size_t i = 0; i < streams_or_svc_num_; ++i) {
127 size_t layer_bitrate_bytes_per_sec =
128 params.target_bitrate.GetSpatialLayerSum(i) / 8;
129 // VP9 rate control is not yet moved out of VP9Impl. Due to that rates
130 // are not split among spatial layers.
131 if (layer_bitrate_bytes_per_sec == 0)
132 layer_bitrate_bytes_per_sec = params.target_bitrate.get_sum_bps() / 8;
133 vcm_encoded_frame_callback_->OnTargetBitrateChanged(
134 layer_bitrate_bytes_per_sec, i);
135 }
Peter Boström69ccb332015-10-29 16:30:23 +0100136 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000137}
138
Peter Boström69ccb332015-10-29 16:30:23 +0100139EncoderParameters VCMGenericEncoder::GetEncoderParameters() const {
140 rtc::CritScope lock(&params_lock_);
141 return encoder_params_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000142}
143
philipel9d3ab612015-12-21 04:12:39 -0800144int32_t VCMGenericEncoder::SetPeriodicKeyFrames(bool enable) {
Peter Boström02bafc62016-07-01 12:45:15 +0200145 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
philipel9d3ab612015-12-21 04:12:39 -0800146 return encoder_->SetPeriodicKeyFrames(enable);
niklase@google.com470e71d2011-07-07 08:21:25 +0000147}
148
pbos@webrtc.org7b859cc2013-04-02 15:54:38 +0000149int32_t VCMGenericEncoder::RequestFrame(
stefan@webrtc.orgcf216862012-10-25 11:29:51 +0000150 const std::vector<FrameType>& frame_types) {
Peter Boström02bafc62016-07-01 12:45:15 +0200151 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
nissedf2ceb82016-12-15 06:29:53 -0800152
ilnik04f4d122017-06-19 07:18:55 -0700153 for (size_t i = 0; i < streams_or_svc_num_; ++i)
154 vcm_encoded_frame_callback_->OnEncodeStarted(0, i);
nissedf2ceb82016-12-15 06:29:53 -0800155 // TODO(nisse): Used only with internal source. Delete as soon as
156 // that feature is removed. The only implementation I've been able
nisse01d5a0b2017-05-31 06:33:21 -0700157 // to find ignores what's in the frame. With one exception: It seems
158 // a few test cases, e.g.,
159 // VideoSendStreamTest.VideoSendStreamStopSetEncoderRateToZero, set
160 // internal_source to true and use FakeEncoder. And the latter will
161 // happily encode this 1x1 frame and pass it on down the pipeline.
nissedf2ceb82016-12-15 06:29:53 -0800162 return encoder_->Encode(VideoFrame(I420Buffer::Create(1, 1),
163 kVideoRotation_0, 0),
164 NULL, &frame_types);
165 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000166}
167
philipel9d3ab612015-12-21 04:12:39 -0800168bool VCMGenericEncoder::InternalSource() const {
169 return internal_source_;
niklase@google.com470e71d2011-07-07 08:21:25 +0000170}
171
Peter Boströmeb66e802015-06-05 11:08:03 +0200172bool VCMGenericEncoder::SupportsNativeHandle() const {
Peter Boström02bafc62016-07-01 12:45:15 +0200173 RTC_DCHECK_RUNS_SERIALIZED(&race_checker_);
Peter Boströmeb66e802015-06-05 11:08:03 +0200174 return encoder_->SupportsNativeHandle();
175}
176
andresp@webrtc.org1df9dc32014-01-09 08:01:57 +0000177VCMEncodedFrameCallback::VCMEncodedFrameCallback(
perkj376b1922016-05-02 11:35:24 -0700178 EncodedImageCallback* post_encode_callback,
179 media_optimization::MediaOptimization* media_opt)
180 : internal_source_(false),
181 post_encode_callback_(post_encode_callback),
ilnik04f4d122017-06-19 07:18:55 -0700182 media_opt_(media_opt),
183 framerate_(1),
184 last_timing_frame_time_ms_(-1),
185 timing_frames_thresholds_({-1, 0}) {}
niklase@google.com470e71d2011-07-07 08:21:25 +0000186
sprang3911c262016-04-15 01:24:14 -0700187VCMEncodedFrameCallback::~VCMEncodedFrameCallback() {}
niklase@google.com470e71d2011-07-07 08:21:25 +0000188
ilnik04f4d122017-06-19 07:18:55 -0700189void VCMEncodedFrameCallback::OnTargetBitrateChanged(
190 size_t bitrate_bytes_per_second,
191 size_t simulcast_svc_idx) {
192 rtc::CritScope crit(&timing_params_lock_);
193 if (timing_frames_info_.size() < simulcast_svc_idx + 1)
194 timing_frames_info_.resize(simulcast_svc_idx + 1);
195 timing_frames_info_[simulcast_svc_idx].target_bitrate_bytes_per_sec =
196 bitrate_bytes_per_second;
197}
198
199void VCMEncodedFrameCallback::OnFrameRateChanged(size_t framerate) {
200 rtc::CritScope crit(&timing_params_lock_);
201 framerate_ = framerate;
202}
203
204void VCMEncodedFrameCallback::OnEncodeStarted(int64_t capture_time_ms,
205 size_t simulcast_svc_idx) {
206 rtc::CritScope crit(&timing_params_lock_);
207 if (timing_frames_info_.size() < simulcast_svc_idx + 1)
208 timing_frames_info_.resize(simulcast_svc_idx + 1);
209 timing_frames_info_[simulcast_svc_idx].encode_start_time_ms[capture_time_ms] =
210 rtc::TimeMillis();
211}
212
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700213EncodedImageCallback::Result VCMEncodedFrameCallback::OnEncodedImage(
Peter Boströmb7d9a972015-12-18 16:01:11 +0100214 const EncodedImage& encoded_image,
sprang3911c262016-04-15 01:24:14 -0700215 const CodecSpecificInfo* codec_specific,
216 const RTPFragmentationHeader* fragmentation_header) {
pbosd9eec762015-11-17 06:03:43 -0800217 TRACE_EVENT_INSTANT1("webrtc", "VCMEncodedFrameCallback::Encoded",
Peter Boströmb7d9a972015-12-18 16:01:11 +0100218 "timestamp", encoded_image._timeStamp);
ilnik04f4d122017-06-19 07:18:55 -0700219 bool is_timing_frame = false;
220 size_t outlier_frame_size = 0;
221 int64_t encode_start_ms = -1;
222 size_t simulcast_svc_idx = 0;
223 if (codec_specific->codecType == kVideoCodecVP9) {
224 if (codec_specific->codecSpecific.VP9.num_spatial_layers > 1)
225 simulcast_svc_idx = codec_specific->codecSpecific.VP9.spatial_idx;
226 } else if (codec_specific->codecType == kVideoCodecVP8) {
227 simulcast_svc_idx = codec_specific->codecSpecific.VP8.simulcastIdx;
228 } else if (codec_specific->codecType == kVideoCodecGeneric) {
229 simulcast_svc_idx = codec_specific->codecSpecific.generic.simulcast_idx;
230 } else if (codec_specific->codecType == kVideoCodecH264) {
231 // TODO(ilnik): When h264 simulcast is landed, extract simulcast idx here.
232 }
233
234 {
235 rtc::CritScope crit(&timing_params_lock_);
236 RTC_CHECK_LT(simulcast_svc_idx, timing_frames_info_.size());
237
238 auto encode_start_map =
239 &timing_frames_info_[simulcast_svc_idx].encode_start_time_ms;
240 auto it = encode_start_map->find(encoded_image.capture_time_ms_);
241 if (it != encode_start_map->end()) {
242 encode_start_ms = it->second;
243 // Assuming all encoders do not reorder frames within single stream,
244 // there may be some dropped frames with smaller timestamps. These should
245 // be purged.
246 encode_start_map->erase(encode_start_map->begin(), it);
247 encode_start_map->erase(it);
248 } else {
249 // Some chromium remoting unittests use generic encoder incorrectly
250 // If timestamps do not match, purge them all.
251 encode_start_map->erase(encode_start_map->begin(),
252 encode_start_map->end());
253 }
254
255 int64_t timing_frame_delay_ms =
256 encoded_image.capture_time_ms_ - last_timing_frame_time_ms_;
257 if (last_timing_frame_time_ms_ == -1 ||
258 timing_frame_delay_ms >= timing_frames_thresholds_.delay_ms ||
259 timing_frame_delay_ms == 0) {
260 is_timing_frame = true;
261 last_timing_frame_time_ms_ = encoded_image.capture_time_ms_;
262 }
263 RTC_CHECK_GT(framerate_, 0);
264 size_t average_frame_size =
265 timing_frames_info_[simulcast_svc_idx].target_bitrate_bytes_per_sec /
266 framerate_;
267 outlier_frame_size = average_frame_size *
268 timing_frames_thresholds_.outlier_ratio_percent / 100;
269 }
270
271 if (encoded_image._length >= outlier_frame_size) {
272 is_timing_frame = true;
273 }
274 if (encode_start_ms >= 0 && is_timing_frame) {
275 encoded_image.SetEncodeTime(encode_start_ms, rtc::TimeMillis());
276 }
277
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700278 Result result = post_encode_callback_->OnEncodedImage(
279 encoded_image, codec_specific, fragmentation_header);
280 if (result.error != Result::OK)
281 return result;
niklase@google.com470e71d2011-07-07 08:21:25 +0000282
sprang3911c262016-04-15 01:24:14 -0700283 if (media_opt_) {
284 media_opt_->UpdateWithEncodedData(encoded_image);
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700285 if (internal_source_) {
286 // Signal to encoder to drop next frame.
287 result.drop_next_frame = media_opt_->DropFrame();
288 }
changbin.shao@webrtc.orgf31f56d2015-02-09 09:14:03 +0000289 }
Sergey Ulanov525df3f2016-08-02 17:46:41 -0700290 return result;
niklase@google.com470e71d2011-07-07 08:21:25 +0000291}
292
pbos@webrtc.orgd900e8b2013-07-03 15:12:26 +0000293} // namespace webrtc