2011-07-07 08:21:25 +00:00
|
|
|
/*
|
2018-06-21 16:16:38 +02:00
|
|
|
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
2011-07-07 08:21:25 +00:00
|
|
|
*
|
|
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
|
|
* tree. An additional intellectual property rights grant can be found
|
|
|
|
|
* in the file PATENTS. All contributing project authors may
|
|
|
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
|
|
|
*/
|
2012-09-20 20:49:12 +00:00
|
|
|
|
2018-11-28 16:47:49 +01:00
|
|
|
#include <assert.h>
|
|
|
|
|
#include <string.h>
|
2014-12-09 10:36:40 +00:00
|
|
|
#include <algorithm>
|
2018-11-28 16:47:49 +01:00
|
|
|
#include <cstdint>
|
2017-03-13 03:43:40 -07:00
|
|
|
#include <string>
|
2018-10-04 17:52:36 +02:00
|
|
|
#include <utility>
|
2018-03-14 17:52:55 +01:00
|
|
|
#include <vector>
|
2011-07-07 08:21:25 +00:00
|
|
|
|
Use absl::make_unique and absl::WrapUnique directly
Instead of going through our wrappers in ptr_util.h.
This CL was generated by the following script:
git grep -l ptr_util | xargs perl -pi -e 's,#include "rtc_base/ptr_util.h",#include "absl/memory/memory.h",'
git grep -l MakeUnique | xargs perl -pi -e 's,\b(rtc::)?MakeUnique\b,absl::make_unique,g'
git grep -l WrapUnique | xargs perl -pi -e 's,\b(rtc::)?WrapUnique\b,absl::WrapUnique,g'
git checkout -- rtc_base/ptr_util{.h,_unittest.cc}
git cl format
Followed by manually adding dependencies on
//third_party/abseil-cpp/absl/memory until `gn check` stopped
complaining.
Bug: webrtc:9473
Change-Id: I89ccd363f070479b8c431eb2c3d404a46eaacc1c
Reviewed-on: https://webrtc-review.googlesource.com/86600
Commit-Queue: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23850}
2018-07-05 11:40:33 +02:00
|
|
|
#include "absl/memory/memory.h"
|
2019-01-25 20:26:48 +01:00
|
|
|
#include "api/scoped_refptr.h"
|
2018-11-28 16:47:49 +01:00
|
|
|
#include "api/video/video_content_type.h"
|
|
|
|
|
#include "api/video/video_frame_buffer.h"
|
|
|
|
|
#include "api/video/video_timing.h"
|
2018-10-12 10:30:31 +02:00
|
|
|
#include "api/video_codecs/create_vp8_temporal_layers.h"
|
|
|
|
|
#include "api/video_codecs/vp8_temporal_layers.h"
|
2017-09-15 06:47:31 +02:00
|
|
|
#include "common_video/libyuv/include/webrtc_libyuv.h"
|
2018-11-28 16:47:49 +01:00
|
|
|
#include "modules/video_coding/codecs/interface/common_constants.h"
|
|
|
|
|
#include "modules/video_coding/codecs/vp8/include/vp8.h"
|
2018-03-14 17:52:55 +01:00
|
|
|
#include "modules/video_coding/codecs/vp8/libvpx_vp8_encoder.h"
|
2018-11-28 16:47:49 +01:00
|
|
|
#include "modules/video_coding/include/video_error_codes.h"
|
2018-06-21 16:16:38 +02:00
|
|
|
#include "modules/video_coding/utility/simulcast_rate_allocator.h"
|
|
|
|
|
#include "modules/video_coding/utility/simulcast_utility.h"
|
2017-09-15 06:47:31 +02:00
|
|
|
#include "rtc_base/checks.h"
|
2018-12-11 14:25:25 -08:00
|
|
|
#include "rtc_base/experiments/field_trial_parser.h"
|
2017-09-15 06:47:31 +02:00
|
|
|
#include "rtc_base/trace_event.h"
|
2018-11-08 16:56:43 +01:00
|
|
|
#include "system_wrappers/include/field_trial.h"
|
2017-12-11 09:32:13 +01:00
|
|
|
#include "third_party/libyuv/include/libyuv/scale.h"
|
2018-11-28 16:47:49 +01:00
|
|
|
#include "vpx/vp8cx.h"
|
2012-09-20 20:49:12 +00:00
|
|
|
|
2014-12-09 10:36:40 +00:00
|
|
|
namespace webrtc {
|
|
|
|
|
namespace {
|
2018-12-11 14:25:25 -08:00
|
|
|
#if defined(WEBRTC_IOS)
|
|
|
|
|
const char kVP8IosMaxNumberOfThreadFieldTrial[] =
|
|
|
|
|
"WebRTC-VP8IosMaxNumberOfThread";
|
|
|
|
|
const char kVP8IosMaxNumberOfThreadFieldTrialParameter[] = "max_thread";
|
|
|
|
|
#endif
|
2018-11-08 16:56:43 +01:00
|
|
|
|
2018-02-22 15:03:53 +01:00
|
|
|
// QP is obtained from VP8-bitstream for HW, so the QP corresponds to the
|
|
|
|
|
// bitstream range of [0, 127] and not the user-level range of [0,63].
|
2018-03-14 17:52:55 +01:00
|
|
|
constexpr int kLowVp8QpThreshold = 29;
|
|
|
|
|
constexpr int kHighVp8QpThreshold = 95;
|
2018-02-22 15:03:53 +01:00
|
|
|
|
2018-03-14 17:52:55 +01:00
|
|
|
constexpr int kTokenPartitions = VP8_ONE_TOKENPARTITION;
|
|
|
|
|
constexpr uint32_t kVp832ByteAlign = 32u;
|
2014-12-09 10:36:40 +00:00
|
|
|
|
|
|
|
|
// VP8 denoiser states.
|
2018-10-04 17:52:36 +02:00
|
|
|
enum denoiserState : uint32_t {
|
2014-12-09 10:36:40 +00:00
|
|
|
kDenoiserOff,
|
|
|
|
|
kDenoiserOnYOnly,
|
|
|
|
|
kDenoiserOnYUV,
|
|
|
|
|
kDenoiserOnYUVAggressive,
|
|
|
|
|
// Adaptive mode defaults to kDenoiserOnYUV on key frame, but may switch
|
|
|
|
|
// to kDenoiserOnYUVAggressive based on a computed noise metric.
|
|
|
|
|
kDenoiserOnAdaptive
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Greatest common divisior
|
2018-06-21 16:16:38 +02:00
|
|
|
static int GCD(int a, int b) {
|
2014-12-09 10:36:40 +00:00
|
|
|
int c = a % b;
|
|
|
|
|
while (c != 0) {
|
|
|
|
|
a = b;
|
|
|
|
|
b = c;
|
|
|
|
|
c = a % b;
|
|
|
|
|
}
|
|
|
|
|
return b;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2018-10-01 18:47:03 +02:00
|
|
|
static_assert(Vp8EncoderConfig::kMaxPeriodicity == VPX_TS_MAX_PERIODICITY,
|
|
|
|
|
"Vp8EncoderConfig::kMaxPeriodicity must be kept in sync with the "
|
|
|
|
|
"constant in libvpx.");
|
|
|
|
|
static_assert(Vp8EncoderConfig::kMaxLayers == VPX_TS_MAX_LAYERS,
|
|
|
|
|
"Vp8EncoderConfig::kMaxLayers must be kept in sync with the "
|
|
|
|
|
"constant in libvpx.");
|
2018-01-24 10:25:15 +01:00
|
|
|
|
|
|
|
|
static Vp8EncoderConfig GetEncoderConfig(vpx_codec_enc_cfg* vpx_config) {
|
|
|
|
|
Vp8EncoderConfig config;
|
|
|
|
|
|
|
|
|
|
config.ts_number_layers = vpx_config->ts_number_layers;
|
|
|
|
|
memcpy(config.ts_target_bitrate, vpx_config->ts_target_bitrate,
|
2018-10-01 18:47:03 +02:00
|
|
|
sizeof(unsigned int) * Vp8EncoderConfig::kMaxLayers);
|
2018-01-24 10:25:15 +01:00
|
|
|
memcpy(config.ts_rate_decimator, vpx_config->ts_rate_decimator,
|
2018-10-01 18:47:03 +02:00
|
|
|
sizeof(unsigned int) * Vp8EncoderConfig::kMaxLayers);
|
2018-01-24 10:25:15 +01:00
|
|
|
config.ts_periodicity = vpx_config->ts_periodicity;
|
|
|
|
|
memcpy(config.ts_layer_id, vpx_config->ts_layer_id,
|
2018-10-01 18:47:03 +02:00
|
|
|
sizeof(unsigned int) * Vp8EncoderConfig::kMaxPeriodicity);
|
2018-01-24 10:25:15 +01:00
|
|
|
config.rc_target_bitrate = vpx_config->rc_target_bitrate;
|
|
|
|
|
config.rc_min_quantizer = vpx_config->rc_min_quantizer;
|
|
|
|
|
config.rc_max_quantizer = vpx_config->rc_max_quantizer;
|
|
|
|
|
|
|
|
|
|
return config;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void FillInEncoderConfig(vpx_codec_enc_cfg* vpx_config,
|
|
|
|
|
const Vp8EncoderConfig& config) {
|
|
|
|
|
vpx_config->ts_number_layers = config.ts_number_layers;
|
|
|
|
|
memcpy(vpx_config->ts_target_bitrate, config.ts_target_bitrate,
|
2018-10-01 18:47:03 +02:00
|
|
|
sizeof(unsigned int) * Vp8EncoderConfig::kMaxLayers);
|
2018-01-24 10:25:15 +01:00
|
|
|
memcpy(vpx_config->ts_rate_decimator, config.ts_rate_decimator,
|
2018-10-01 18:47:03 +02:00
|
|
|
sizeof(unsigned int) * Vp8EncoderConfig::kMaxLayers);
|
2018-01-24 10:25:15 +01:00
|
|
|
vpx_config->ts_periodicity = config.ts_periodicity;
|
|
|
|
|
memcpy(vpx_config->ts_layer_id, config.ts_layer_id,
|
2018-10-01 18:47:03 +02:00
|
|
|
sizeof(unsigned int) * Vp8EncoderConfig::kMaxPeriodicity);
|
2018-01-24 10:25:15 +01:00
|
|
|
vpx_config->rc_target_bitrate = config.rc_target_bitrate;
|
|
|
|
|
vpx_config->rc_min_quantizer = config.rc_min_quantizer;
|
|
|
|
|
vpx_config->rc_max_quantizer = config.rc_max_quantizer;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-12 10:30:31 +02:00
|
|
|
bool UpdateVpxConfiguration(Vp8TemporalLayers* temporal_layers,
|
2018-01-24 10:25:15 +01:00
|
|
|
vpx_codec_enc_cfg_t* cfg) {
|
|
|
|
|
Vp8EncoderConfig config = GetEncoderConfig(cfg);
|
|
|
|
|
const bool res = temporal_layers->UpdateConfiguration(&config);
|
|
|
|
|
if (res)
|
|
|
|
|
FillInEncoderConfig(cfg, config);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2018-10-01 18:47:03 +02:00
|
|
|
|
2014-12-09 10:36:40 +00:00
|
|
|
} // namespace
|
|
|
|
|
|
2018-10-04 17:52:36 +02:00
|
|
|
std::unique_ptr<VideoEncoder> VP8Encoder::Create() {
|
Use absl::make_unique and absl::WrapUnique directly
Instead of going through our wrappers in ptr_util.h.
This CL was generated by the following script:
git grep -l ptr_util | xargs perl -pi -e 's,#include "rtc_base/ptr_util.h",#include "absl/memory/memory.h",'
git grep -l MakeUnique | xargs perl -pi -e 's,\b(rtc::)?MakeUnique\b,absl::make_unique,g'
git grep -l WrapUnique | xargs perl -pi -e 's,\b(rtc::)?WrapUnique\b,absl::WrapUnique,g'
git checkout -- rtc_base/ptr_util{.h,_unittest.cc}
git cl format
Followed by manually adding dependencies on
//third_party/abseil-cpp/absl/memory until `gn check` stopped
complaining.
Bug: webrtc:9473
Change-Id: I89ccd363f070479b8c431eb2c3d404a46eaacc1c
Reviewed-on: https://webrtc-review.googlesource.com/86600
Commit-Queue: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23850}
2018-07-05 11:40:33 +02:00
|
|
|
return absl::make_unique<LibvpxVp8Encoder>();
|
2016-01-25 17:58:00 +01:00
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:52:55 +01:00
|
|
|
vpx_enc_frame_flags_t LibvpxVp8Encoder::EncodeFlags(
|
2019-01-29 14:05:55 +01:00
|
|
|
const Vp8FrameConfig& references) {
|
2017-05-02 02:51:12 -07:00
|
|
|
RTC_DCHECK(!references.drop_frame);
|
|
|
|
|
|
|
|
|
|
vpx_enc_frame_flags_t flags = 0;
|
|
|
|
|
|
2019-01-29 14:05:55 +01:00
|
|
|
if ((references.last_buffer_flags &
|
|
|
|
|
Vp8FrameConfig::BufferFlags::kReference) == 0)
|
2017-05-02 02:51:12 -07:00
|
|
|
flags |= VP8_EFLAG_NO_REF_LAST;
|
2019-01-29 14:05:55 +01:00
|
|
|
if ((references.last_buffer_flags & Vp8FrameConfig::BufferFlags::kUpdate) ==
|
|
|
|
|
0)
|
2017-05-02 02:51:12 -07:00
|
|
|
flags |= VP8_EFLAG_NO_UPD_LAST;
|
2019-01-29 14:05:55 +01:00
|
|
|
if ((references.golden_buffer_flags &
|
|
|
|
|
Vp8FrameConfig::BufferFlags::kReference) == 0)
|
2017-05-02 02:51:12 -07:00
|
|
|
flags |= VP8_EFLAG_NO_REF_GF;
|
2019-01-29 14:05:55 +01:00
|
|
|
if ((references.golden_buffer_flags & Vp8FrameConfig::BufferFlags::kUpdate) ==
|
|
|
|
|
0)
|
2017-05-02 02:51:12 -07:00
|
|
|
flags |= VP8_EFLAG_NO_UPD_GF;
|
2019-01-29 14:05:55 +01:00
|
|
|
if ((references.arf_buffer_flags & Vp8FrameConfig::BufferFlags::kReference) ==
|
|
|
|
|
0)
|
2017-05-02 02:51:12 -07:00
|
|
|
flags |= VP8_EFLAG_NO_REF_ARF;
|
2019-01-29 14:05:55 +01:00
|
|
|
if ((references.arf_buffer_flags & Vp8FrameConfig::BufferFlags::kUpdate) == 0)
|
2017-05-02 02:51:12 -07:00
|
|
|
flags |= VP8_EFLAG_NO_UPD_ARF;
|
|
|
|
|
if (references.freeze_entropy)
|
|
|
|
|
flags |= VP8_EFLAG_NO_UPD_ENTROPY;
|
|
|
|
|
|
|
|
|
|
return flags;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:52:55 +01:00
|
|
|
LibvpxVp8Encoder::LibvpxVp8Encoder()
|
2018-10-04 17:52:36 +02:00
|
|
|
: LibvpxVp8Encoder(LibvpxInterface::CreateEncoder()) {}
|
|
|
|
|
|
|
|
|
|
LibvpxVp8Encoder::LibvpxVp8Encoder(std::unique_ptr<LibvpxInterface> interface)
|
|
|
|
|
: libvpx_(std::move(interface)),
|
2018-11-02 11:38:46 +01:00
|
|
|
experimental_cpu_speed_config_arm_(CpuSpeedExperiment::GetConfigs()),
|
2019-02-13 10:49:37 +01:00
|
|
|
rate_control_settings_(RateControlSettings::ParseFromFieldTrials()),
|
2017-03-21 12:35:51 -04:00
|
|
|
encoded_complete_callback_(nullptr),
|
2012-02-02 12:21:47 +00:00
|
|
|
inited_(false),
|
|
|
|
|
timestamp_(0),
|
2014-12-09 10:36:40 +00:00
|
|
|
qp_max_(56), // Setting for max quantizer.
|
2015-03-04 21:47:06 +00:00
|
|
|
cpu_speed_default_(-6),
|
2016-11-01 04:08:22 -07:00
|
|
|
number_of_cores_(0),
|
2012-02-02 12:21:47 +00:00
|
|
|
rc_max_intra_target_(0),
|
2016-11-29 01:44:11 -08:00
|
|
|
key_frame_request_(kMaxSimulcastStreams, false) {
|
2014-12-09 10:36:40 +00:00
|
|
|
temporal_layers_.reserve(kMaxSimulcastStreams);
|
|
|
|
|
raw_images_.reserve(kMaxSimulcastStreams);
|
|
|
|
|
encoded_images_.reserve(kMaxSimulcastStreams);
|
|
|
|
|
send_stream_.reserve(kMaxSimulcastStreams);
|
2017-03-21 12:35:51 -04:00
|
|
|
cpu_speed_.assign(kMaxSimulcastStreams, cpu_speed_default_);
|
2014-12-09 10:36:40 +00:00
|
|
|
encoders_.reserve(kMaxSimulcastStreams);
|
|
|
|
|
configurations_.reserve(kMaxSimulcastStreams);
|
|
|
|
|
downsampling_factors_.reserve(kMaxSimulcastStreams);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:52:55 +01:00
|
|
|
LibvpxVp8Encoder::~LibvpxVp8Encoder() {
|
2012-02-02 12:21:47 +00:00
|
|
|
Release();
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:52:55 +01:00
|
|
|
int LibvpxVp8Encoder::Release() {
|
2014-12-09 10:36:40 +00:00
|
|
|
int ret_val = WEBRTC_VIDEO_CODEC_OK;
|
|
|
|
|
|
2019-02-07 21:56:12 +01:00
|
|
|
encoded_images_.clear();
|
|
|
|
|
|
2014-12-09 10:36:40 +00:00
|
|
|
while (!encoders_.empty()) {
|
|
|
|
|
vpx_codec_ctx_t& encoder = encoders_.back();
|
2018-03-02 14:44:10 +01:00
|
|
|
if (inited_) {
|
2018-10-04 17:52:36 +02:00
|
|
|
if (libvpx_->codec_destroy(&encoder)) {
|
2018-03-02 14:44:10 +01:00
|
|
|
ret_val = WEBRTC_VIDEO_CODEC_MEMORY;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2014-12-09 10:36:40 +00:00
|
|
|
encoders_.pop_back();
|
2012-02-02 12:21:47 +00:00
|
|
|
}
|
2014-12-09 10:36:40 +00:00
|
|
|
configurations_.clear();
|
|
|
|
|
send_stream_.clear();
|
|
|
|
|
cpu_speed_.clear();
|
|
|
|
|
while (!raw_images_.empty()) {
|
2018-10-04 17:52:36 +02:00
|
|
|
libvpx_->img_free(&raw_images_.back());
|
2014-12-09 10:36:40 +00:00
|
|
|
raw_images_.pop_back();
|
2012-02-02 12:21:47 +00:00
|
|
|
}
|
2017-05-03 03:25:53 -07:00
|
|
|
temporal_layers_.clear();
|
2012-02-02 12:21:47 +00:00
|
|
|
inited_ = false;
|
2014-12-09 10:36:40 +00:00
|
|
|
return ret_val;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2018-04-23 12:32:22 +02:00
|
|
|
int LibvpxVp8Encoder::SetRateAllocation(const VideoBitrateAllocation& bitrate,
|
2018-03-14 17:52:55 +01:00
|
|
|
uint32_t new_framerate) {
|
2016-11-16 16:41:30 +01:00
|
|
|
if (!inited_)
|
2012-02-02 12:21:47 +00:00
|
|
|
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
2016-11-16 16:41:30 +01:00
|
|
|
|
|
|
|
|
if (encoders_[0].err)
|
2012-02-02 12:21:47 +00:00
|
|
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
2016-11-16 16:41:30 +01:00
|
|
|
|
|
|
|
|
if (new_framerate < 1)
|
2012-02-02 12:21:47 +00:00
|
|
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
2016-11-16 16:41:30 +01:00
|
|
|
|
|
|
|
|
if (bitrate.get_sum_bps() == 0) {
|
|
|
|
|
// Encoder paused, turn off all encoding.
|
|
|
|
|
const int num_streams = static_cast<size_t>(encoders_.size());
|
|
|
|
|
for (int i = 0; i < num_streams; ++i)
|
|
|
|
|
SetStreamState(false, i);
|
|
|
|
|
return WEBRTC_VIDEO_CODEC_OK;
|
2012-02-02 12:21:47 +00:00
|
|
|
}
|
2016-11-16 16:41:30 +01:00
|
|
|
|
|
|
|
|
// At this point, bitrate allocation should already match codec settings.
|
|
|
|
|
if (codec_.maxBitrate > 0)
|
|
|
|
|
RTC_DCHECK_LE(bitrate.get_sum_kbps(), codec_.maxBitrate);
|
|
|
|
|
RTC_DCHECK_GE(bitrate.get_sum_kbps(), codec_.minBitrate);
|
|
|
|
|
if (codec_.numberOfSimulcastStreams > 0)
|
|
|
|
|
RTC_DCHECK_GE(bitrate.get_sum_kbps(), codec_.simulcastStream[0].minBitrate);
|
|
|
|
|
|
2012-02-02 12:21:47 +00:00
|
|
|
codec_.maxFramerate = new_framerate;
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2017-03-21 12:35:51 -04:00
|
|
|
if (encoders_.size() > 1) {
|
2014-12-09 10:36:40 +00:00
|
|
|
// If we have more than 1 stream, reduce the qp_max for the low resolution
|
|
|
|
|
// stream if frame rate is not too low. The trade-off with lower qp_max is
|
|
|
|
|
// possibly more dropped frames, so we only do this if the frame rate is
|
|
|
|
|
// above some threshold (base temporal layer is down to 1/4 for 3 layers).
|
|
|
|
|
// We may want to condition this on bitrate later.
|
2019-02-13 10:49:37 +01:00
|
|
|
if (rate_control_settings_.Vp8BoostBaseLayerQuality() &&
|
|
|
|
|
new_framerate > 20) {
|
2014-12-09 10:36:40 +00:00
|
|
|
configurations_[encoders_.size() - 1].rc_max_quantizer = 45;
|
|
|
|
|
} else {
|
|
|
|
|
// Go back to default value set in InitEncode.
|
|
|
|
|
configurations_[encoders_.size() - 1].rc_max_quantizer = qp_max_;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t stream_idx = encoders_.size() - 1;
|
|
|
|
|
for (size_t i = 0; i < encoders_.size(); ++i, --stream_idx) {
|
2016-11-16 16:41:30 +01:00
|
|
|
unsigned int target_bitrate_kbps =
|
|
|
|
|
bitrate.GetSpatialLayerSum(stream_idx) / 1000;
|
|
|
|
|
|
|
|
|
|
bool send_stream = target_bitrate_kbps > 0;
|
|
|
|
|
if (send_stream || encoders_.size() > 1)
|
|
|
|
|
SetStreamState(send_stream, stream_idx);
|
|
|
|
|
|
|
|
|
|
configurations_[i].rc_target_bitrate = target_bitrate_kbps;
|
2018-03-19 18:25:10 +01:00
|
|
|
if (send_stream) {
|
|
|
|
|
temporal_layers_[stream_idx]->OnRatesUpdated(
|
|
|
|
|
bitrate.GetTemporalLayerAllocation(stream_idx), new_framerate);
|
|
|
|
|
}
|
2018-01-24 10:25:15 +01:00
|
|
|
|
|
|
|
|
UpdateVpxConfiguration(temporal_layers_[stream_idx].get(),
|
|
|
|
|
&configurations_[i]);
|
2016-11-16 16:41:30 +01:00
|
|
|
|
2018-10-04 17:52:36 +02:00
|
|
|
if (libvpx_->codec_enc_config_set(&encoders_[i], &configurations_[i])) {
|
2014-12-09 10:36:40 +00:00
|
|
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
|
|
|
|
}
|
2012-02-02 12:21:47 +00:00
|
|
|
}
|
|
|
|
|
return WEBRTC_VIDEO_CODEC_OK;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:52:55 +01:00
|
|
|
void LibvpxVp8Encoder::SetStreamState(bool send_stream, int stream_idx) {
|
2014-12-09 10:36:40 +00:00
|
|
|
if (send_stream && !send_stream_[stream_idx]) {
|
|
|
|
|
// Need a key frame if we have not sent this stream before.
|
|
|
|
|
key_frame_request_[stream_idx] = true;
|
|
|
|
|
}
|
|
|
|
|
send_stream_[stream_idx] = send_stream;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-01 18:47:03 +02:00
|
|
|
void LibvpxVp8Encoder::SetupTemporalLayers(const VideoCodec& codec) {
|
2018-03-19 18:25:10 +01:00
|
|
|
RTC_DCHECK(temporal_layers_.empty());
|
2018-10-01 18:47:03 +02:00
|
|
|
int num_streams = SimulcastUtility::NumberOfSimulcastStreams(codec);
|
2018-03-21 09:57:23 +01:00
|
|
|
for (int i = 0; i < num_streams; ++i) {
|
2018-10-12 10:30:31 +02:00
|
|
|
Vp8TemporalLayersType type;
|
2018-10-01 18:47:03 +02:00
|
|
|
int num_temporal_layers =
|
|
|
|
|
SimulcastUtility::NumberOfTemporalLayers(codec, i);
|
|
|
|
|
if (SimulcastUtility::IsConferenceModeScreenshare(codec) && i == 0) {
|
2018-10-12 10:30:31 +02:00
|
|
|
type = Vp8TemporalLayersType::kBitrateDynamic;
|
2018-10-01 18:47:03 +02:00
|
|
|
// Legacy screenshare layers supports max 2 layers.
|
|
|
|
|
num_temporal_layers = std::max<int>(2, num_temporal_layers);
|
|
|
|
|
} else {
|
2018-10-12 10:30:31 +02:00
|
|
|
type = Vp8TemporalLayersType::kFixedPattern;
|
2018-10-01 18:47:03 +02:00
|
|
|
}
|
2017-05-03 03:25:53 -07:00
|
|
|
temporal_layers_.emplace_back(
|
2018-10-12 10:30:31 +02:00
|
|
|
CreateVp8TemporalLayers(type, num_temporal_layers));
|
2014-12-09 10:36:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:52:55 +01:00
|
|
|
int LibvpxVp8Encoder::InitEncode(const VideoCodec* inst,
|
|
|
|
|
int number_of_cores,
|
|
|
|
|
size_t /*maxPayloadSize */) {
|
2012-02-02 12:21:47 +00:00
|
|
|
if (inst == NULL) {
|
|
|
|
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
|
|
|
|
}
|
|
|
|
|
if (inst->maxFramerate < 1) {
|
|
|
|
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
|
|
|
|
}
|
|
|
|
|
// allow zero to represent an unspecified maxBitRate
|
|
|
|
|
if (inst->maxBitrate > 0 && inst->startBitrate > inst->maxBitrate) {
|
|
|
|
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
|
|
|
|
}
|
2019-01-29 10:37:34 -05:00
|
|
|
if (inst->width < 1 || inst->height < 1) {
|
2012-02-02 12:21:47 +00:00
|
|
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
|
|
|
|
}
|
|
|
|
|
if (number_of_cores < 1) {
|
|
|
|
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
|
|
|
|
}
|
2016-10-25 09:05:06 -07:00
|
|
|
if (inst->VP8().automaticResizeOn && inst->numberOfSimulcastStreams > 1) {
|
2014-12-09 10:36:40 +00:00
|
|
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
|
|
|
|
}
|
2012-02-02 12:21:47 +00:00
|
|
|
int retVal = Release();
|
|
|
|
|
if (retVal < 0) {
|
|
|
|
|
return retVal;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2018-06-21 16:16:38 +02:00
|
|
|
int number_of_streams = SimulcastUtility::NumberOfSimulcastStreams(*inst);
|
2018-10-01 18:47:03 +02:00
|
|
|
if (number_of_streams > 1 &&
|
|
|
|
|
(!SimulcastUtility::ValidSimulcastResolutions(*inst, number_of_streams) ||
|
|
|
|
|
!SimulcastUtility::ValidSimulcastTemporalLayers(*inst,
|
|
|
|
|
number_of_streams))) {
|
2017-10-25 10:04:54 +02:00
|
|
|
return WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED;
|
2013-04-23 13:08:04 +00:00
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2018-10-01 18:47:03 +02:00
|
|
|
SetupTemporalLayers(*inst);
|
2014-12-09 10:36:40 +00:00
|
|
|
|
2016-11-01 04:08:22 -07:00
|
|
|
number_of_cores_ = number_of_cores;
|
2014-12-09 10:36:40 +00:00
|
|
|
timestamp_ = 0;
|
|
|
|
|
codec_ = *inst;
|
|
|
|
|
|
|
|
|
|
// Code expects simulcastStream resolutions to be correct, make sure they are
|
|
|
|
|
// filled even when there are no simulcast layers.
|
|
|
|
|
if (codec_.numberOfSimulcastStreams == 0) {
|
|
|
|
|
codec_.simulcastStream[0].width = codec_.width;
|
|
|
|
|
codec_.simulcastStream[0].height = codec_.height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
encoded_images_.resize(number_of_streams);
|
|
|
|
|
encoders_.resize(number_of_streams);
|
|
|
|
|
configurations_.resize(number_of_streams);
|
|
|
|
|
downsampling_factors_.resize(number_of_streams);
|
|
|
|
|
raw_images_.resize(number_of_streams);
|
|
|
|
|
send_stream_.resize(number_of_streams);
|
|
|
|
|
send_stream_[0] = true; // For non-simulcast case.
|
|
|
|
|
cpu_speed_.resize(number_of_streams);
|
|
|
|
|
std::fill(key_frame_request_.begin(), key_frame_request_.end(), false);
|
|
|
|
|
|
|
|
|
|
int idx = number_of_streams - 1;
|
|
|
|
|
for (int i = 0; i < (number_of_streams - 1); ++i, --idx) {
|
|
|
|
|
int gcd = GCD(inst->simulcastStream[idx].width,
|
2015-12-21 03:04:49 -08:00
|
|
|
inst->simulcastStream[idx - 1].width);
|
2014-12-09 10:36:40 +00:00
|
|
|
downsampling_factors_[i].num = inst->simulcastStream[idx].width / gcd;
|
|
|
|
|
downsampling_factors_[i].den = inst->simulcastStream[idx - 1].width / gcd;
|
|
|
|
|
send_stream_[i] = false;
|
|
|
|
|
}
|
|
|
|
|
if (number_of_streams > 1) {
|
|
|
|
|
send_stream_[number_of_streams - 1] = false;
|
|
|
|
|
downsampling_factors_[number_of_streams - 1].num = 1;
|
|
|
|
|
downsampling_factors_[number_of_streams - 1].den = 1;
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < number_of_streams; ++i) {
|
|
|
|
|
// allocate memory for encoded image
|
2018-12-07 16:21:18 +01:00
|
|
|
size_t frame_capacity =
|
2017-04-28 07:18:05 -07:00
|
|
|
CalcBufferSize(VideoType::kI420, codec_.width, codec_.height);
|
2019-02-07 21:56:12 +01:00
|
|
|
encoded_images_[i].Allocate(frame_capacity);
|
2014-12-09 10:36:40 +00:00
|
|
|
encoded_images_[i]._completeFrame = true;
|
|
|
|
|
}
|
2012-02-02 12:21:47 +00:00
|
|
|
// populate encoder configuration with default values
|
2018-10-04 17:52:36 +02:00
|
|
|
if (libvpx_->codec_enc_config_default(vpx_codec_vp8_cx(), &configurations_[0],
|
|
|
|
|
0)) {
|
2012-02-02 12:21:47 +00:00
|
|
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
|
|
|
|
}
|
|
|
|
|
// setting the time base of the codec
|
2014-12-09 10:36:40 +00:00
|
|
|
configurations_[0].g_timebase.num = 1;
|
|
|
|
|
configurations_[0].g_timebase.den = 90000;
|
|
|
|
|
configurations_[0].g_lag_in_frames = 0; // 0- no frame lagging
|
2012-02-02 12:21:47 +00:00
|
|
|
|
2018-04-25 14:46:06 +02:00
|
|
|
// Set the error resilience mode for temporal layers (but not simulcast).
|
2018-04-20 14:20:01 +02:00
|
|
|
configurations_[0].g_error_resilient =
|
2018-10-01 18:47:03 +02:00
|
|
|
(SimulcastUtility::NumberOfTemporalLayers(*inst, 0) > 1)
|
|
|
|
|
? VPX_ERROR_RESILIENT_DEFAULT
|
|
|
|
|
: 0;
|
2011-12-02 08:34:05 +00:00
|
|
|
|
2012-02-02 12:21:47 +00:00
|
|
|
// rate control settings
|
2018-09-10 10:48:01 +02:00
|
|
|
configurations_[0].rc_dropframe_thresh = FrameDropThreshold(0);
|
2014-12-09 10:36:40 +00:00
|
|
|
configurations_[0].rc_end_usage = VPX_CBR;
|
|
|
|
|
configurations_[0].g_pass = VPX_RC_ONE_PASS;
|
2017-03-21 12:35:51 -04:00
|
|
|
// Handle resizing outside of libvpx.
|
2014-12-09 10:36:40 +00:00
|
|
|
configurations_[0].rc_resize_allowed = 0;
|
2019-02-14 14:44:22 +01:00
|
|
|
configurations_[0].rc_min_quantizer =
|
|
|
|
|
codec_.mode == VideoCodecMode::kScreensharing ? 12 : 2;
|
2014-12-09 10:36:40 +00:00
|
|
|
if (inst->qpMax >= configurations_[0].rc_min_quantizer) {
|
|
|
|
|
qp_max_ = inst->qpMax;
|
|
|
|
|
}
|
|
|
|
|
configurations_[0].rc_max_quantizer = qp_max_;
|
|
|
|
|
configurations_[0].rc_undershoot_pct = 100;
|
|
|
|
|
configurations_[0].rc_overshoot_pct = 15;
|
|
|
|
|
configurations_[0].rc_buf_initial_sz = 500;
|
|
|
|
|
configurations_[0].rc_buf_optimal_sz = 600;
|
|
|
|
|
configurations_[0].rc_buf_sz = 1000;
|
|
|
|
|
|
|
|
|
|
// Set the maximum target size of any key-frame.
|
|
|
|
|
rc_max_intra_target_ = MaxIntraTarget(configurations_[0].rc_buf_optimal_sz);
|
2012-02-02 12:21:47 +00:00
|
|
|
|
2017-03-21 01:54:13 -07:00
|
|
|
if (inst->VP8().keyFrameInterval > 0) {
|
2014-12-09 10:36:40 +00:00
|
|
|
configurations_[0].kf_mode = VPX_KF_AUTO;
|
2016-10-25 09:05:06 -07:00
|
|
|
configurations_[0].kf_max_dist = inst->VP8().keyFrameInterval;
|
2013-01-31 16:37:13 +00:00
|
|
|
} else {
|
2014-12-09 10:36:40 +00:00
|
|
|
configurations_[0].kf_mode = VPX_KF_DISABLED;
|
2012-02-02 12:21:47 +00:00
|
|
|
}
|
2014-12-09 10:36:40 +00:00
|
|
|
|
|
|
|
|
// Allow the user to set the complexity for the base stream.
|
2016-10-25 09:05:06 -07:00
|
|
|
switch (inst->VP8().complexity) {
|
2018-06-13 11:52:16 +02:00
|
|
|
case VideoCodecComplexity::kComplexityHigh:
|
2014-12-09 10:36:40 +00:00
|
|
|
cpu_speed_[0] = -5;
|
2012-02-02 12:21:47 +00:00
|
|
|
break;
|
2018-06-13 11:52:16 +02:00
|
|
|
case VideoCodecComplexity::kComplexityHigher:
|
2014-12-09 10:36:40 +00:00
|
|
|
cpu_speed_[0] = -4;
|
2012-02-02 12:21:47 +00:00
|
|
|
break;
|
2018-06-13 11:52:16 +02:00
|
|
|
case VideoCodecComplexity::kComplexityMax:
|
2014-12-09 10:36:40 +00:00
|
|
|
cpu_speed_[0] = -3;
|
2012-02-02 12:21:47 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
2014-12-09 10:36:40 +00:00
|
|
|
cpu_speed_[0] = -6;
|
2012-02-02 12:21:47 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2015-03-04 21:47:06 +00:00
|
|
|
cpu_speed_default_ = cpu_speed_[0];
|
|
|
|
|
// Set encoding complexity (cpu_speed) based on resolution and/or platform.
|
2018-11-02 11:38:46 +01:00
|
|
|
cpu_speed_[0] = GetCpuSpeed(inst->width, inst->height);
|
2014-12-09 10:36:40 +00:00
|
|
|
for (int i = 1; i < number_of_streams; ++i) {
|
2015-03-04 21:47:06 +00:00
|
|
|
cpu_speed_[i] =
|
2018-11-02 11:38:46 +01:00
|
|
|
GetCpuSpeed(inst->simulcastStream[number_of_streams - 1 - i].width,
|
2015-03-04 21:47:06 +00:00
|
|
|
inst->simulcastStream[number_of_streams - 1 - i].height);
|
2014-12-09 10:36:40 +00:00
|
|
|
}
|
|
|
|
|
configurations_[0].g_w = inst->width;
|
|
|
|
|
configurations_[0].g_h = inst->height;
|
|
|
|
|
|
|
|
|
|
// Determine number of threads based on the image size and #cores.
|
|
|
|
|
// TODO(fbarchard): Consider number of Simulcast layers.
|
2015-12-21 03:04:49 -08:00
|
|
|
configurations_[0].g_threads = NumberOfThreads(
|
|
|
|
|
configurations_[0].g_w, configurations_[0].g_h, number_of_cores);
|
2014-12-09 10:36:40 +00:00
|
|
|
|
|
|
|
|
// Creating a wrapper to the image - setting image data to NULL.
|
|
|
|
|
// Actual pointer will be set in encode. Setting align to 1, as it
|
|
|
|
|
// is meaningless (no memory allocation is done here).
|
2018-10-04 17:52:36 +02:00
|
|
|
libvpx_->img_wrap(&raw_images_[0], VPX_IMG_FMT_I420, inst->width,
|
|
|
|
|
inst->height, 1, NULL);
|
2014-12-09 10:36:40 +00:00
|
|
|
|
2016-11-16 16:41:30 +01:00
|
|
|
// Note the order we use is different from webm, we have lowest resolution
|
|
|
|
|
// at position 0 and they have highest resolution at position 0.
|
|
|
|
|
int stream_idx = encoders_.size() - 1;
|
2018-03-21 09:57:23 +01:00
|
|
|
SimulcastRateAllocator init_allocator(codec_);
|
2018-04-23 12:32:22 +02:00
|
|
|
VideoBitrateAllocation allocation = init_allocator.GetAllocation(
|
2016-11-16 16:41:30 +01:00
|
|
|
inst->startBitrate * 1000, inst->maxFramerate);
|
|
|
|
|
std::vector<uint32_t> stream_bitrates;
|
|
|
|
|
for (int i = 0; i == 0 || i < inst->numberOfSimulcastStreams; ++i) {
|
|
|
|
|
uint32_t bitrate = allocation.GetSpatialLayerSum(i) / 1000;
|
|
|
|
|
stream_bitrates.push_back(bitrate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
configurations_[0].rc_target_bitrate = stream_bitrates[stream_idx];
|
2018-03-19 18:25:10 +01:00
|
|
|
if (stream_bitrates[stream_idx] > 0) {
|
|
|
|
|
temporal_layers_[stream_idx]->OnRatesUpdated(
|
|
|
|
|
allocation.GetTemporalLayerAllocation(stream_idx), inst->maxFramerate);
|
|
|
|
|
}
|
2018-01-24 10:25:15 +01:00
|
|
|
UpdateVpxConfiguration(temporal_layers_[stream_idx].get(),
|
|
|
|
|
&configurations_[0]);
|
2018-09-10 10:48:01 +02:00
|
|
|
configurations_[0].rc_dropframe_thresh = FrameDropThreshold(stream_idx);
|
2018-01-24 10:25:15 +01:00
|
|
|
|
2016-11-16 16:41:30 +01:00
|
|
|
--stream_idx;
|
|
|
|
|
for (size_t i = 1; i < encoders_.size(); ++i, --stream_idx) {
|
|
|
|
|
memcpy(&configurations_[i], &configurations_[0],
|
|
|
|
|
sizeof(configurations_[0]));
|
|
|
|
|
|
|
|
|
|
configurations_[i].g_w = inst->simulcastStream[stream_idx].width;
|
|
|
|
|
configurations_[i].g_h = inst->simulcastStream[stream_idx].height;
|
|
|
|
|
|
|
|
|
|
// Use 1 thread for lower resolutions.
|
|
|
|
|
configurations_[i].g_threads = 1;
|
|
|
|
|
|
2018-09-10 10:48:01 +02:00
|
|
|
configurations_[i].rc_dropframe_thresh = FrameDropThreshold(stream_idx);
|
|
|
|
|
|
2016-11-16 16:41:30 +01:00
|
|
|
// Setting alignment to 32 - as that ensures at least 16 for all
|
|
|
|
|
// planes (32 for Y, 16 for U,V). Libvpx sets the requested stride for
|
|
|
|
|
// the y plane, but only half of it to the u and v planes.
|
2018-10-04 17:52:36 +02:00
|
|
|
libvpx_->img_alloc(&raw_images_[i], VPX_IMG_FMT_I420,
|
|
|
|
|
inst->simulcastStream[stream_idx].width,
|
|
|
|
|
inst->simulcastStream[stream_idx].height,
|
|
|
|
|
kVp832ByteAlign);
|
2015-06-08 22:52:33 +02:00
|
|
|
SetStreamState(stream_bitrates[stream_idx] > 0, stream_idx);
|
2016-11-16 16:41:30 +01:00
|
|
|
configurations_[i].rc_target_bitrate = stream_bitrates[stream_idx];
|
2018-03-19 18:25:10 +01:00
|
|
|
if (stream_bitrates[stream_idx] > 0) {
|
|
|
|
|
temporal_layers_[stream_idx]->OnRatesUpdated(
|
|
|
|
|
allocation.GetTemporalLayerAllocation(stream_idx),
|
|
|
|
|
inst->maxFramerate);
|
|
|
|
|
}
|
2018-01-24 10:25:15 +01:00
|
|
|
UpdateVpxConfiguration(temporal_layers_[stream_idx].get(),
|
|
|
|
|
&configurations_[i]);
|
2014-12-09 10:36:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return InitAndSetControlSettings();
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-02 11:38:46 +01:00
|
|
|
int LibvpxVp8Encoder::GetCpuSpeed(int width, int height) {
|
2018-03-14 17:52:55 +01:00
|
|
|
#if defined(WEBRTC_ARCH_ARM) || defined(WEBRTC_ARCH_ARM64) || \
|
|
|
|
|
defined(WEBRTC_ANDROID)
|
2016-11-01 04:08:22 -07:00
|
|
|
// On mobile platform, use a lower speed setting for lower resolutions for
|
|
|
|
|
// CPUs with 4 or more cores.
|
|
|
|
|
RTC_DCHECK_GT(number_of_cores_, 0);
|
|
|
|
|
if (number_of_cores_ <= 3)
|
|
|
|
|
return -12;
|
|
|
|
|
|
2018-11-02 11:38:46 +01:00
|
|
|
if (experimental_cpu_speed_config_arm_) {
|
|
|
|
|
return CpuSpeedExperiment::GetValue(width * height,
|
|
|
|
|
*experimental_cpu_speed_config_arm_);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-01 04:08:22 -07:00
|
|
|
if (width * height <= 352 * 288)
|
|
|
|
|
return -8;
|
|
|
|
|
else if (width * height <= 640 * 480)
|
|
|
|
|
return -10;
|
|
|
|
|
else
|
|
|
|
|
return -12;
|
2015-03-04 21:47:06 +00:00
|
|
|
#else
|
|
|
|
|
// For non-ARM, increase encoding complexity (i.e., use lower speed setting)
|
|
|
|
|
// if resolution is below CIF. Otherwise, keep the default/user setting
|
2016-10-25 09:05:06 -07:00
|
|
|
// (|cpu_speed_default_|) set on InitEncode via VP8().complexity.
|
2015-03-04 21:47:06 +00:00
|
|
|
if (width * height < 352 * 288)
|
|
|
|
|
return (cpu_speed_default_ < -4) ? -4 : cpu_speed_default_;
|
|
|
|
|
else
|
|
|
|
|
return cpu_speed_default_;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:52:55 +01:00
|
|
|
int LibvpxVp8Encoder::NumberOfThreads(int width, int height, int cpus) {
|
2017-10-16 11:08:54 +02:00
|
|
|
#if defined(WEBRTC_ANDROID)
|
2016-06-14 14:28:29 -07:00
|
|
|
if (width * height >= 320 * 180) {
|
|
|
|
|
if (cpus >= 4) {
|
|
|
|
|
// 3 threads for CPUs with 4 and more cores since most of times only 4
|
|
|
|
|
// cores will be active.
|
|
|
|
|
return 3;
|
|
|
|
|
} else if (cpus == 3 || cpus == 2) {
|
|
|
|
|
return 2;
|
|
|
|
|
} else {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
#else
|
2018-12-11 14:25:25 -08:00
|
|
|
#if defined(WEBRTC_IOS)
|
|
|
|
|
std::string trial_string =
|
|
|
|
|
field_trial::FindFullName(kVP8IosMaxNumberOfThreadFieldTrial);
|
|
|
|
|
FieldTrialParameter<int> max_thread_number(
|
|
|
|
|
kVP8IosMaxNumberOfThreadFieldTrialParameter, 0);
|
|
|
|
|
ParseFieldTrial({&max_thread_number}, trial_string);
|
|
|
|
|
if (max_thread_number.Get() > 0) {
|
|
|
|
|
if (width * height < 320 * 180) {
|
|
|
|
|
return 1; // Use single thread for small screens
|
|
|
|
|
}
|
|
|
|
|
// thread number must be less than or equal to the number of CPUs.
|
|
|
|
|
return std::min(cpus, max_thread_number.Get());
|
|
|
|
|
}
|
|
|
|
|
#endif // defined(WEBRTC_IOS)
|
2014-12-09 10:36:40 +00:00
|
|
|
if (width * height >= 1920 * 1080 && cpus > 8) {
|
|
|
|
|
return 8; // 8 threads for 1080p on high perf machines.
|
|
|
|
|
} else if (width * height > 1280 * 960 && cpus >= 6) {
|
|
|
|
|
// 3 threads for 1080p.
|
|
|
|
|
return 3;
|
|
|
|
|
} else if (width * height > 640 * 480 && cpus >= 3) {
|
2018-05-23 11:01:34 +02:00
|
|
|
// Default 2 threads for qHD/HD, but allow 3 if core count is high enough,
|
|
|
|
|
// as this will allow more margin for high-core/low clock machines or if
|
|
|
|
|
// not built with highest optimization.
|
|
|
|
|
if (cpus >= 6) {
|
|
|
|
|
return 3;
|
|
|
|
|
}
|
2014-12-09 10:36:40 +00:00
|
|
|
return 2;
|
|
|
|
|
} else {
|
|
|
|
|
// 1 thread for VGA or less.
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2016-06-14 14:28:29 -07:00
|
|
|
#endif
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:52:55 +01:00
|
|
|
int LibvpxVp8Encoder::InitAndSetControlSettings() {
|
2012-02-02 12:21:47 +00:00
|
|
|
vpx_codec_flags_t flags = 0;
|
|
|
|
|
flags |= VPX_CODEC_USE_OUTPUT_PARTITION;
|
2014-12-09 10:36:40 +00:00
|
|
|
|
|
|
|
|
if (encoders_.size() > 1) {
|
2018-10-04 17:52:36 +02:00
|
|
|
int error = libvpx_->codec_enc_init_multi(
|
|
|
|
|
&encoders_[0], vpx_codec_vp8_cx(), &configurations_[0],
|
|
|
|
|
encoders_.size(), flags, &downsampling_factors_[0]);
|
2014-12-09 10:36:40 +00:00
|
|
|
if (error) {
|
|
|
|
|
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2018-10-04 17:52:36 +02:00
|
|
|
if (libvpx_->codec_enc_init(&encoders_[0], vpx_codec_vp8_cx(),
|
|
|
|
|
&configurations_[0], flags)) {
|
2014-12-09 10:36:40 +00:00
|
|
|
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
|
|
|
|
}
|
2012-02-02 12:21:47 +00:00
|
|
|
}
|
2014-12-09 10:36:40 +00:00
|
|
|
// Enable denoising for the highest resolution stream, and for
|
|
|
|
|
// the second highest resolution if we are doing more than 2
|
|
|
|
|
// spatial layers/streams.
|
|
|
|
|
// TODO(holmer): Investigate possibility of adding a libvpx API
|
|
|
|
|
// for getting the denoised frame from the encoder and using that
|
|
|
|
|
// when encoding lower resolution streams. Would it work with the
|
|
|
|
|
// multi-res encoding feature?
|
|
|
|
|
denoiserState denoiser_state = kDenoiserOnYOnly;
|
2018-03-14 17:52:55 +01:00
|
|
|
#if defined(WEBRTC_ARCH_ARM) || defined(WEBRTC_ARCH_ARM64) || \
|
|
|
|
|
defined(WEBRTC_ANDROID)
|
2014-12-09 10:36:40 +00:00
|
|
|
denoiser_state = kDenoiserOnYOnly;
|
|
|
|
|
#else
|
|
|
|
|
denoiser_state = kDenoiserOnAdaptive;
|
2012-11-29 09:01:21 +00:00
|
|
|
#endif
|
2018-10-04 17:52:36 +02:00
|
|
|
libvpx_->codec_control(
|
|
|
|
|
&encoders_[0], VP8E_SET_NOISE_SENSITIVITY,
|
|
|
|
|
codec_.VP8()->denoisingOn ? denoiser_state : kDenoiserOff);
|
2014-12-09 10:36:40 +00:00
|
|
|
if (encoders_.size() > 2) {
|
2018-10-04 17:52:36 +02:00
|
|
|
libvpx_->codec_control(
|
2015-12-21 03:04:49 -08:00
|
|
|
&encoders_[1], VP8E_SET_NOISE_SENSITIVITY,
|
2016-10-25 09:05:06 -07:00
|
|
|
codec_.VP8()->denoisingOn ? denoiser_state : kDenoiserOff);
|
2014-12-09 10:36:40 +00:00
|
|
|
}
|
|
|
|
|
for (size_t i = 0; i < encoders_.size(); ++i) {
|
2015-10-01 06:26:10 -07:00
|
|
|
// Allow more screen content to be detected as static.
|
2018-10-04 17:52:36 +02:00
|
|
|
libvpx_->codec_control(
|
|
|
|
|
&(encoders_[i]), VP8E_SET_STATIC_THRESHOLD,
|
2019-02-14 14:44:22 +01:00
|
|
|
codec_.mode == VideoCodecMode::kScreensharing ? 100u : 1u);
|
2018-10-04 17:52:36 +02:00
|
|
|
libvpx_->codec_control(&(encoders_[i]), VP8E_SET_CPUUSED, cpu_speed_[i]);
|
|
|
|
|
libvpx_->codec_control(
|
|
|
|
|
&(encoders_[i]), VP8E_SET_TOKEN_PARTITIONS,
|
|
|
|
|
static_cast<vp8e_token_partitions>(kTokenPartitions));
|
|
|
|
|
libvpx_->codec_control(&(encoders_[i]), VP8E_SET_MAX_INTRA_BITRATE_PCT,
|
|
|
|
|
rc_max_intra_target_);
|
2015-06-24 11:24:44 +02:00
|
|
|
// VP8E_SET_SCREEN_CONTENT_MODE 2 = screen content with more aggressive
|
|
|
|
|
// rate control (drop frames on large target bitrate overshoot)
|
2018-10-04 17:52:36 +02:00
|
|
|
libvpx_->codec_control(
|
|
|
|
|
&(encoders_[i]), VP8E_SET_SCREEN_CONTENT_MODE,
|
|
|
|
|
codec_.mode == VideoCodecMode::kScreensharing ? 2u : 0u);
|
2014-12-09 10:36:40 +00:00
|
|
|
}
|
2012-02-02 12:21:47 +00:00
|
|
|
inited_ = true;
|
|
|
|
|
return WEBRTC_VIDEO_CODEC_OK;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:52:55 +01:00
|
|
|
uint32_t LibvpxVp8Encoder::MaxIntraTarget(uint32_t optimalBuffersize) {
|
2012-02-02 12:21:47 +00:00
|
|
|
// Set max to the optimal buffer level (normalized by target BR),
|
|
|
|
|
// and scaled by a scalePar.
|
|
|
|
|
// Max target size = scalePar * optimalBufferSize * targetBR[Kbps].
|
|
|
|
|
// This values is presented in percentage of perFrameBw:
|
|
|
|
|
// perFrameBw = targetBR[Kbps] * 1000 / frameRate.
|
|
|
|
|
// The target in % is as follows:
|
|
|
|
|
|
|
|
|
|
float scalePar = 0.5;
|
|
|
|
|
uint32_t targetPct = optimalBuffersize * scalePar * codec_.maxFramerate / 10;
|
|
|
|
|
|
|
|
|
|
// Don't go below 3 times the per frame bandwidth.
|
|
|
|
|
const uint32_t minIntraTh = 300;
|
2015-12-21 03:04:49 -08:00
|
|
|
return (targetPct < minIntraTh) ? minIntraTh : targetPct;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2018-09-10 10:48:01 +02:00
|
|
|
uint32_t LibvpxVp8Encoder::FrameDropThreshold(size_t spatial_idx) const {
|
|
|
|
|
bool enable_frame_dropping = codec_.VP8().frameDroppingOn;
|
|
|
|
|
// If temporal layers are used, they get to override the frame dropping
|
|
|
|
|
// setting, as eg. ScreenshareLayers does not work as intended with frame
|
|
|
|
|
// dropping on and DefaultTemporalLayers will have performance issues with
|
|
|
|
|
// frame dropping off.
|
|
|
|
|
if (temporal_layers_.size() <= spatial_idx) {
|
|
|
|
|
enable_frame_dropping =
|
|
|
|
|
temporal_layers_[spatial_idx]->SupportsEncoderFrameDropping();
|
|
|
|
|
}
|
|
|
|
|
return enable_frame_dropping ? 30 : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:52:55 +01:00
|
|
|
int LibvpxVp8Encoder::Encode(const VideoFrame& frame,
|
|
|
|
|
const CodecSpecificInfo* codec_specific_info,
|
|
|
|
|
const std::vector<FrameType>* frame_types) {
|
2016-11-29 01:44:11 -08:00
|
|
|
RTC_DCHECK_EQ(frame.width(), codec_.width);
|
|
|
|
|
RTC_DCHECK_EQ(frame.height(), codec_.height);
|
|
|
|
|
|
2015-06-24 11:24:44 +02:00
|
|
|
if (!inited_)
|
2012-02-02 12:21:47 +00:00
|
|
|
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
2015-06-24 11:24:44 +02:00
|
|
|
if (encoded_complete_callback_ == NULL)
|
2012-02-02 12:21:47 +00:00
|
|
|
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
2012-03-02 16:48:36 +00:00
|
|
|
|
Revert "Revert "Update video_coding/codecs to new VideoFrameBuffer interface""
This reverts commit 88f94fa36aa61f7904d30251205c544ada2c4301.
Chromium code has been updated.
Original change's description:
> Revert "Update video_coding/codecs to new VideoFrameBuffer interface"
>
> This reverts commit 20ebf4ede803cd4f628ef9378700f60b72f2eab0.
>
> Reason for revert:
>
> Suspect of breaking FYI bots.
> See https://build.chromium.org/p/chromium.webrtc.fyi/builders/Win7%20Tester/builds/9036 and others.
>
> Sample logs:
> Backtrace:
> [5024:1036:0607/173649.857:FATAL:webrtc_video_frame_adapter.cc(98)] Check failed: false.
> Backtrace:
> base::debug::StackTrace::StackTrace [0x02D04A37+55]
> base::debug::StackTrace::StackTrace [0x02CCBB8A+10]
> content::WebRtcVideoFrameAdapter::NativeToI420Buffer [0x0508AD71+305]
> webrtc::VideoFrameBuffer::ToI420 [0x0230BF67+39]
> webrtc::H264EncoderImpl::Encode [0x057E8D0B+267]
> webrtc::VCMGenericEncoder::Encode [0x057E0E34+333]
> webrtc::vcm::VideoSender::AddVideoFrame [0x057DED9B+796]
> webrtc::ViEEncoder::EncodeVideoFrame [0x057C00F6+884]
> webrtc::ViEEncoder::EncodeTask::Run [0x057C12D7+215]
> rtc::TaskQueue::PostTask [0x03EE5CFB+194]
> base::internal::Invoker<base::internal::BindState<enum extensions::`anonymous namespace'::VerificationResult (__cdecl*)(std::unique_ptr<extensions::NetworkingCastPrivateDelegate::Credentials,std::default_delete<extensions::NetworkingCastPrivateDelegate::C [0x02DDCAA5+31]
> base::internal::Invoker<base::internal::BindState<enum extensions::`anonymous namespace'::VerificationResult (__cdecl*)(std::unique_ptr<extensions::NetworkingCastPrivateDelegate::Credentials,std::default_delete<extensions::NetworkingCastPrivateDelegate::C [0x02DDEE86+22]
> base::debug::TaskAnnotator::RunTask [0x02D08289+409]
> base::MessageLoop::RunTask [0x02C8CEC1+1233]
> base::MessageLoop::DoWork [0x02C8C1AD+765]
> base::MessagePumpDefault::Run [0x02D0A20B+219]
> base::MessageLoop::Run [0x02C8C9DB+107]
> base::RunLoop::Run [0x02C89583+147]
> base::Thread::Run [0x02CBEFCD+173]
> base::Thread::ThreadMain [0x02CBFADE+622]
> base::PlatformThread::Sleep [0x02C9E1A2+290]
> BaseThreadInitThunk [0x75C3338A+18]
> RtlInitializeExceptionChain [0x773A9902+99]
> RtlInitializeExceptionChain [0x773A98D5+54]
>
> Original change's description:
> > Update video_coding/codecs to new VideoFrameBuffer interface
> >
> > This is a follow-up cleanup for CL
> > https://codereview.webrtc.org/2847383002/.
> >
> > Bug: webrtc:7632
> > Change-Id: I47861d779968f2fee94db9c017102a8e87e67fb7
> > Reviewed-on: https://chromium-review.googlesource.com/524163
> > Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
> > Reviewed-by: Niels Moller <nisse@webrtc.org>
> > Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
> > Cr-Commit-Position: refs/heads/master@{#18477}
>
> TBR=magjed@webrtc.org,nisse@webrtc.org,brandtr@webrtc.org
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: webrtc:7632
>
> Change-Id: I3b73fc7d16ff19ceba196e964dcb36a36510912c
> Reviewed-on: https://chromium-review.googlesource.com/527793
> Reviewed-by: Guido Urdaneta <guidou@chromium.org>
> Commit-Queue: Guido Urdaneta <guidou@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#18489}
TBR=tterriberry@mozilla.com,mflodman@webrtc.org,magjed@webrtc.org,stefan@webrtc.org,guidou@chromium.org,nisse@webrtc.org,brandtr@webrtc.org,webrtc-reviews@webrtc.org
# Not skipping CQ checks because original CL landed > 1 day ago.
No-Presubmit: true
Bug: webrtc:7632
Change-Id: I0962a704e8a9939d4364ce9069c863c9951654c9
Reviewed-on: https://chromium-review.googlesource.com/530684
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#18527}
2017-06-10 17:03:37 +00:00
|
|
|
rtc::scoped_refptr<I420BufferInterface> input_image =
|
|
|
|
|
frame.video_frame_buffer()->ToI420();
|
2015-03-17 12:27:26 +00:00
|
|
|
// Since we are extracting raw pointers from |input_image| to
|
2017-05-02 02:21:17 -07:00
|
|
|
// |raw_images_[0]|, the resolution of these frames must match.
|
2016-11-28 15:58:53 -08:00
|
|
|
RTC_DCHECK_EQ(input_image->width(), raw_images_[0].d_w);
|
|
|
|
|
RTC_DCHECK_EQ(input_image->height(), raw_images_[0].d_h);
|
2015-03-17 12:27:26 +00:00
|
|
|
|
2014-12-09 10:36:40 +00:00
|
|
|
// Image in vpx_image_t format.
|
|
|
|
|
// Input image is const. VP8's raw image is not defined as const.
|
|
|
|
|
raw_images_[0].planes[VPX_PLANE_Y] =
|
2016-06-13 13:06:01 +02:00
|
|
|
const_cast<uint8_t*>(input_image->DataY());
|
2014-12-09 10:36:40 +00:00
|
|
|
raw_images_[0].planes[VPX_PLANE_U] =
|
2016-06-13 13:06:01 +02:00
|
|
|
const_cast<uint8_t*>(input_image->DataU());
|
2014-12-09 10:36:40 +00:00
|
|
|
raw_images_[0].planes[VPX_PLANE_V] =
|
2016-06-13 13:06:01 +02:00
|
|
|
const_cast<uint8_t*>(input_image->DataV());
|
2014-12-09 10:36:40 +00:00
|
|
|
|
2016-06-13 13:06:01 +02:00
|
|
|
raw_images_[0].stride[VPX_PLANE_Y] = input_image->StrideY();
|
|
|
|
|
raw_images_[0].stride[VPX_PLANE_U] = input_image->StrideU();
|
|
|
|
|
raw_images_[0].stride[VPX_PLANE_V] = input_image->StrideV();
|
2014-12-09 10:36:40 +00:00
|
|
|
|
|
|
|
|
for (size_t i = 1; i < encoders_.size(); ++i) {
|
|
|
|
|
// Scale the image down a number of times by downsampling factor
|
|
|
|
|
libyuv::I420Scale(
|
2015-12-21 03:04:49 -08:00
|
|
|
raw_images_[i - 1].planes[VPX_PLANE_Y],
|
|
|
|
|
raw_images_[i - 1].stride[VPX_PLANE_Y],
|
|
|
|
|
raw_images_[i - 1].planes[VPX_PLANE_U],
|
|
|
|
|
raw_images_[i - 1].stride[VPX_PLANE_U],
|
|
|
|
|
raw_images_[i - 1].planes[VPX_PLANE_V],
|
|
|
|
|
raw_images_[i - 1].stride[VPX_PLANE_V], raw_images_[i - 1].d_w,
|
|
|
|
|
raw_images_[i - 1].d_h, raw_images_[i].planes[VPX_PLANE_Y],
|
|
|
|
|
raw_images_[i].stride[VPX_PLANE_Y], raw_images_[i].planes[VPX_PLANE_U],
|
|
|
|
|
raw_images_[i].stride[VPX_PLANE_U], raw_images_[i].planes[VPX_PLANE_V],
|
|
|
|
|
raw_images_[i].stride[VPX_PLANE_V], raw_images_[i].d_w,
|
|
|
|
|
raw_images_[i].d_h, libyuv::kFilterBilinear);
|
2014-12-09 10:36:40 +00:00
|
|
|
}
|
|
|
|
|
bool send_key_frame = false;
|
|
|
|
|
for (size_t i = 0; i < key_frame_request_.size() && i < send_stream_.size();
|
|
|
|
|
++i) {
|
|
|
|
|
if (key_frame_request_[i] && send_stream_[i]) {
|
|
|
|
|
send_key_frame = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!send_key_frame && frame_types) {
|
|
|
|
|
for (size_t i = 0; i < frame_types->size() && i < send_stream_.size();
|
|
|
|
|
++i) {
|
2015-10-23 15:58:18 +02:00
|
|
|
if ((*frame_types)[i] == kVideoFrameKey && send_stream_[i]) {
|
2014-12-09 10:36:40 +00:00
|
|
|
send_key_frame = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-02 10:08:25 +02:00
|
|
|
vpx_enc_frame_flags_t flags[kMaxSimulcastStreams];
|
2019-01-29 14:05:55 +01:00
|
|
|
Vp8FrameConfig tl_configs[kMaxSimulcastStreams];
|
2017-10-02 10:08:25 +02:00
|
|
|
for (size_t i = 0; i < encoders_.size(); ++i) {
|
|
|
|
|
tl_configs[i] = temporal_layers_[i]->UpdateLayerConfig(frame.timestamp());
|
|
|
|
|
if (tl_configs[i].drop_frame) {
|
2018-09-05 10:48:13 +02:00
|
|
|
if (send_key_frame) {
|
2018-08-17 13:21:48 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2017-10-02 10:08:25 +02:00
|
|
|
// Drop this frame.
|
|
|
|
|
return WEBRTC_VIDEO_CODEC_OK;
|
|
|
|
|
}
|
|
|
|
|
flags[i] = EncodeFlags(tl_configs[i]);
|
|
|
|
|
}
|
2014-12-09 10:36:40 +00:00
|
|
|
if (send_key_frame) {
|
|
|
|
|
// Adapt the size of the key frame when in screenshare with 1 temporal
|
|
|
|
|
// layer.
|
2018-06-13 11:52:16 +02:00
|
|
|
if (encoders_.size() == 1 &&
|
|
|
|
|
codec_.mode == VideoCodecMode::kScreensharing &&
|
2016-10-25 09:05:06 -07:00
|
|
|
codec_.VP8()->numberOfTemporalLayers <= 1) {
|
2014-12-09 10:36:40 +00:00
|
|
|
const uint32_t forceKeyFrameIntraTh = 100;
|
2018-10-04 17:52:36 +02:00
|
|
|
libvpx_->codec_control(&(encoders_[0]), VP8E_SET_MAX_INTRA_BITRATE_PCT,
|
|
|
|
|
forceKeyFrameIntraTh);
|
2014-12-09 10:36:40 +00:00
|
|
|
}
|
2012-02-02 12:21:47 +00:00
|
|
|
// Key frame request from caller.
|
|
|
|
|
// Will update both golden and alt-ref.
|
2014-12-09 10:36:40 +00:00
|
|
|
for (size_t i = 0; i < encoders_.size(); ++i) {
|
|
|
|
|
flags[i] = VPX_EFLAG_FORCE_KF;
|
|
|
|
|
}
|
|
|
|
|
std::fill(key_frame_request_.begin(), key_frame_request_.end(), false);
|
2012-02-02 12:21:47 +00:00
|
|
|
}
|
2017-03-21 01:54:13 -07:00
|
|
|
|
2014-12-09 10:36:40 +00:00
|
|
|
// Set the encoder frame flags and temporal layer_id for each spatial stream.
|
|
|
|
|
// Note that |temporal_layers_| are defined starting from lowest resolution at
|
|
|
|
|
// position 0 to highest resolution at position |encoders_.size() - 1|,
|
|
|
|
|
// whereas |encoder_| is from highest to lowest resolution.
|
|
|
|
|
size_t stream_idx = encoders_.size() - 1;
|
|
|
|
|
for (size_t i = 0; i < encoders_.size(); ++i, --stream_idx) {
|
2015-06-24 11:24:44 +02:00
|
|
|
// Allow the layers adapter to temporarily modify the configuration. This
|
|
|
|
|
// change isn't stored in configurations_ so change will be discarded at
|
|
|
|
|
// the next update.
|
|
|
|
|
vpx_codec_enc_cfg_t temp_config;
|
|
|
|
|
memcpy(&temp_config, &configurations_[i], sizeof(vpx_codec_enc_cfg_t));
|
2018-01-24 10:25:15 +01:00
|
|
|
if (UpdateVpxConfiguration(temporal_layers_[stream_idx].get(),
|
|
|
|
|
&temp_config)) {
|
2018-10-04 17:52:36 +02:00
|
|
|
if (libvpx_->codec_enc_config_set(&encoders_[i], &temp_config))
|
2015-06-24 11:24:44 +02:00
|
|
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-04 17:52:36 +02:00
|
|
|
libvpx_->codec_control(&encoders_[i], VP8E_SET_FRAME_FLAGS,
|
|
|
|
|
static_cast<int>(flags[stream_idx]));
|
|
|
|
|
libvpx_->codec_control(&encoders_[i], VP8E_SET_TEMPORAL_LAYER_ID,
|
|
|
|
|
tl_configs[i].encoder_layer_id);
|
2014-12-09 10:36:40 +00:00
|
|
|
}
|
2012-02-02 12:21:47 +00:00
|
|
|
// TODO(holmer): Ideally the duration should be the timestamp diff of this
|
|
|
|
|
// frame and the next frame to be encoded, which we don't have. Instead we
|
|
|
|
|
// would like to use the duration of the previous frame. Unfortunately the
|
|
|
|
|
// rate control seems to be off with that setup. Using the average input
|
|
|
|
|
// frame rate to calculate an average duration for now.
|
|
|
|
|
assert(codec_.maxFramerate > 0);
|
|
|
|
|
uint32_t duration = 90000 / codec_.maxFramerate;
|
2014-12-09 10:36:40 +00:00
|
|
|
|
2017-09-12 10:24:46 -07:00
|
|
|
int error = WEBRTC_VIDEO_CODEC_OK;
|
|
|
|
|
int num_tries = 0;
|
|
|
|
|
// If the first try returns WEBRTC_VIDEO_CODEC_TARGET_BITRATE_OVERSHOOT
|
|
|
|
|
// the frame must be reencoded with the same parameters again because
|
|
|
|
|
// target bitrate is exceeded and encoder state has been reset.
|
|
|
|
|
while (num_tries == 0 ||
|
2017-10-02 10:08:25 +02:00
|
|
|
(num_tries == 1 &&
|
2017-09-12 10:24:46 -07:00
|
|
|
error == WEBRTC_VIDEO_CODEC_TARGET_BITRATE_OVERSHOOT)) {
|
|
|
|
|
++num_tries;
|
|
|
|
|
// Note we must pass 0 for |flags| field in encode call below since they are
|
2018-10-04 17:52:36 +02:00
|
|
|
// set above in |libvpx_interface_->vpx_codec_control_| function for each
|
|
|
|
|
// encoder/spatial layer.
|
|
|
|
|
error = libvpx_->codec_encode(&encoders_[0], &raw_images_[0], timestamp_,
|
|
|
|
|
duration, 0, VPX_DL_REALTIME);
|
2017-09-12 10:24:46 -07:00
|
|
|
// Reset specific intra frame thresholds, following the key frame.
|
|
|
|
|
if (send_key_frame) {
|
2018-10-04 17:52:36 +02:00
|
|
|
libvpx_->codec_control(&(encoders_[0]), VP8E_SET_MAX_INTRA_BITRATE_PCT,
|
|
|
|
|
rc_max_intra_target_);
|
2017-09-12 10:24:46 -07:00
|
|
|
}
|
|
|
|
|
if (error)
|
|
|
|
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
|
|
|
|
// Examines frame timestamps only.
|
2018-10-03 11:05:16 +02:00
|
|
|
error = GetEncodedPartitions(frame);
|
2014-12-09 10:36:40 +00:00
|
|
|
}
|
2018-10-10 14:58:46 +02:00
|
|
|
// TODO(sprang): Shouldn't we use the frame timestamp instead?
|
|
|
|
|
timestamp_ += duration;
|
2017-09-12 10:24:46 -07:00
|
|
|
return error;
|
2011-09-08 06:50:28 +00:00
|
|
|
}
|
|
|
|
|
|
2018-10-03 11:05:16 +02:00
|
|
|
void LibvpxVp8Encoder::PopulateCodecSpecific(CodecSpecificInfo* codec_specific,
|
|
|
|
|
const vpx_codec_cx_pkt_t& pkt,
|
|
|
|
|
int stream_idx,
|
|
|
|
|
int encoder_idx,
|
|
|
|
|
uint32_t timestamp) {
|
2011-09-08 06:50:28 +00:00
|
|
|
assert(codec_specific != NULL);
|
|
|
|
|
codec_specific->codecType = kVideoCodecVP8;
|
2014-12-09 10:36:40 +00:00
|
|
|
CodecSpecificInfoVP8* vp8Info = &(codec_specific->codecSpecific.VP8);
|
2011-11-25 10:17:00 +00:00
|
|
|
vp8Info->keyIdx = kNoKeyIdx; // TODO(hlundin) populate this
|
2017-03-27 15:01:49 -04:00
|
|
|
vp8Info->nonReference = (pkt.data.frame.flags & VPX_FRAME_IS_DROPPABLE) != 0;
|
2018-10-03 11:05:16 +02:00
|
|
|
|
|
|
|
|
int qp = 0;
|
|
|
|
|
vpx_codec_control(&encoders_[encoder_idx], VP8E_GET_LAST_QUANTIZER_64, &qp);
|
|
|
|
|
temporal_layers_[stream_idx]->OnEncodeDone(
|
2019-01-15 08:50:01 +01:00
|
|
|
timestamp, encoded_images_[encoder_idx].size(),
|
2018-10-03 11:05:16 +02:00
|
|
|
(pkt.data.frame.flags & VPX_FRAME_IS_KEY) != 0, qp, vp8Info);
|
2011-09-08 06:50:28 +00:00
|
|
|
}
|
|
|
|
|
|
2018-10-03 11:05:16 +02:00
|
|
|
int LibvpxVp8Encoder::GetEncodedPartitions(const VideoFrame& input_image) {
|
2014-12-09 10:36:40 +00:00
|
|
|
int stream_idx = static_cast<int>(encoders_.size()) - 1;
|
2015-06-24 11:24:44 +02:00
|
|
|
int result = WEBRTC_VIDEO_CODEC_OK;
|
2014-12-09 10:36:40 +00:00
|
|
|
for (size_t encoder_idx = 0; encoder_idx < encoders_.size();
|
2015-12-21 03:04:49 -08:00
|
|
|
++encoder_idx, --stream_idx) {
|
2014-12-09 10:36:40 +00:00
|
|
|
vpx_codec_iter_t iter = NULL;
|
2019-01-15 08:50:01 +01:00
|
|
|
encoded_images_[encoder_idx].set_size(0);
|
2015-10-23 15:58:18 +02:00
|
|
|
encoded_images_[encoder_idx]._frameType = kVideoFrameDelta;
|
2014-12-09 10:36:40 +00:00
|
|
|
CodecSpecificInfo codec_specific;
|
2015-12-21 03:04:49 -08:00
|
|
|
const vpx_codec_cx_pkt_t* pkt = NULL;
|
2018-10-04 17:52:36 +02:00
|
|
|
while ((pkt = libvpx_->codec_get_cx_data(&encoders_[encoder_idx], &iter)) !=
|
2015-12-21 03:04:49 -08:00
|
|
|
NULL) {
|
2014-12-09 10:36:40 +00:00
|
|
|
switch (pkt->kind) {
|
|
|
|
|
case VPX_CODEC_CX_FRAME_PKT: {
|
2019-02-07 21:56:12 +01:00
|
|
|
const size_t size = encoded_images_[encoder_idx].size();
|
|
|
|
|
const size_t new_size = pkt->data.frame.sz + size;
|
|
|
|
|
encoded_images_[encoder_idx].Allocate(new_size);
|
|
|
|
|
memcpy(&encoded_images_[encoder_idx].data()[size],
|
2015-12-21 03:04:49 -08:00
|
|
|
pkt->data.frame.buf, pkt->data.frame.sz);
|
2019-02-07 21:56:12 +01:00
|
|
|
encoded_images_[encoder_idx].set_size(new_size);
|
2014-12-09 10:36:40 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2011-09-08 06:50:28 +00:00
|
|
|
}
|
2014-12-09 10:36:40 +00:00
|
|
|
// 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) {
|
2015-10-23 15:58:18 +02:00
|
|
|
encoded_images_[encoder_idx]._frameType = kVideoFrameKey;
|
2014-12-09 10:36:40 +00:00
|
|
|
}
|
2018-08-27 15:33:42 +02:00
|
|
|
encoded_images_[encoder_idx].SetSpatialIndex(stream_idx);
|
2018-10-03 11:05:16 +02:00
|
|
|
PopulateCodecSpecific(&codec_specific, *pkt, stream_idx, encoder_idx,
|
|
|
|
|
input_image.timestamp());
|
2011-09-08 06:50:28 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-16 10:24:12 +02:00
|
|
|
encoded_images_[encoder_idx].SetTimestamp(input_image.timestamp());
|
2014-12-09 10:36:40 +00:00
|
|
|
encoded_images_[encoder_idx].capture_time_ms_ =
|
|
|
|
|
input_image.render_time_ms();
|
2016-04-19 15:01:23 +02:00
|
|
|
encoded_images_[encoder_idx].rotation_ = input_image.rotation();
|
2017-04-11 10:34:31 -07:00
|
|
|
encoded_images_[encoder_idx].content_type_ =
|
2018-06-13 11:52:16 +02:00
|
|
|
(codec_.mode == VideoCodecMode::kScreensharing)
|
|
|
|
|
? VideoContentType::SCREENSHARE
|
|
|
|
|
: VideoContentType::UNSPECIFIED;
|
2018-06-05 15:21:32 +02:00
|
|
|
encoded_images_[encoder_idx].timing_.flags = VideoSendTiming::kInvalid;
|
2018-12-14 13:55:23 +01:00
|
|
|
encoded_images_[encoder_idx].SetColorSpace(input_image.color_space());
|
2015-06-24 11:24:44 +02:00
|
|
|
|
2014-12-09 10:36:40 +00:00
|
|
|
if (send_stream_[stream_idx]) {
|
2019-01-15 08:50:01 +01:00
|
|
|
if (encoded_images_[encoder_idx].size() > 0) {
|
2014-12-09 10:36:40 +00:00
|
|
|
TRACE_COUNTER_ID1("webrtc", "EncodedFrameSize", encoder_idx,
|
2019-01-15 08:50:01 +01:00
|
|
|
encoded_images_[encoder_idx].size());
|
2014-12-09 10:36:40 +00:00
|
|
|
encoded_images_[encoder_idx]._encodedHeight =
|
|
|
|
|
codec_.simulcastStream[stream_idx].height;
|
|
|
|
|
encoded_images_[encoder_idx]._encodedWidth =
|
|
|
|
|
codec_.simulcastStream[stream_idx].width;
|
2016-03-31 00:00:19 -07:00
|
|
|
int qp_128 = -1;
|
2018-10-04 17:52:36 +02:00
|
|
|
libvpx_->codec_control(&encoders_[encoder_idx], VP8E_GET_LAST_QUANTIZER,
|
|
|
|
|
&qp_128);
|
2016-03-31 00:00:19 -07:00
|
|
|
encoded_images_[encoder_idx].qp_ = qp_128;
|
2016-11-04 11:39:29 -07:00
|
|
|
encoded_complete_callback_->OnEncodedImage(encoded_images_[encoder_idx],
|
2018-10-31 14:11:47 +01:00
|
|
|
&codec_specific, nullptr);
|
2018-10-03 11:05:16 +02:00
|
|
|
} else if (!temporal_layers_[stream_idx]
|
|
|
|
|
->SupportsEncoderFrameDropping()) {
|
2015-06-24 11:24:44 +02:00
|
|
|
result = WEBRTC_VIDEO_CODEC_TARGET_BITRATE_OVERSHOOT;
|
2019-01-15 08:50:01 +01:00
|
|
|
if (encoded_images_[encoder_idx].size() == 0) {
|
2018-10-03 11:05:16 +02:00
|
|
|
// Dropped frame that will be re-encoded.
|
|
|
|
|
temporal_layers_[stream_idx]->OnEncodeDone(input_image.timestamp(), 0,
|
|
|
|
|
false, 0, nullptr);
|
|
|
|
|
}
|
2011-09-08 06:50:28 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-06-24 11:24:44 +02:00
|
|
|
return result;
|
2011-09-08 06:50:28 +00:00
|
|
|
}
|
|
|
|
|
|
2018-11-07 16:47:06 +01:00
|
|
|
VideoEncoder::EncoderInfo LibvpxVp8Encoder::GetEncoderInfo() const {
|
|
|
|
|
EncoderInfo info;
|
|
|
|
|
info.supports_native_handle = false;
|
|
|
|
|
info.implementation_name = "libvpx";
|
2019-02-13 10:49:37 +01:00
|
|
|
info.has_trusted_rate_controller =
|
|
|
|
|
rate_control_settings_.LibvpxVp8TrustedRateController();
|
2018-11-30 13:12:21 +01:00
|
|
|
info.is_hardware_accelerated = false;
|
|
|
|
|
info.has_internal_source = false;
|
2018-11-07 16:47:06 +01:00
|
|
|
|
2016-11-29 01:44:11 -08:00
|
|
|
const bool enable_scaling = encoders_.size() == 1 &&
|
|
|
|
|
configurations_[0].rc_dropframe_thresh > 0 &&
|
|
|
|
|
codec_.VP8().automaticResizeOn;
|
2018-11-07 16:47:06 +01:00
|
|
|
info.scaling_settings = enable_scaling
|
|
|
|
|
? VideoEncoder::ScalingSettings(
|
|
|
|
|
kLowVp8QpThreshold, kHighVp8QpThreshold)
|
|
|
|
|
: VideoEncoder::ScalingSettings::kOff;
|
2019-01-17 15:27:50 +01:00
|
|
|
// |encoder_idx| is libvpx index where 0 is highest resolution.
|
|
|
|
|
// |si| is simulcast index, where 0 is lowest resolution.
|
|
|
|
|
for (size_t si = 0, encoder_idx = encoders_.size() - 1; si < encoders_.size();
|
|
|
|
|
++si, --encoder_idx) {
|
|
|
|
|
info.fps_allocation[si].clear();
|
|
|
|
|
if ((codec_.numberOfSimulcastStreams > si &&
|
|
|
|
|
!codec_.simulcastStream[si].active) ||
|
|
|
|
|
(si == 0 && SimulcastUtility::IsConferenceModeScreenshare(codec_))) {
|
|
|
|
|
// No defined frame rate fractions if not active or if using
|
|
|
|
|
// ScreenshareLayers, leave vector empty and continue;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (configurations_[encoder_idx].ts_number_layers <= 1) {
|
|
|
|
|
info.fps_allocation[si].push_back(EncoderInfo::kMaxFramerateFraction);
|
|
|
|
|
} else {
|
|
|
|
|
for (size_t ti = 0; ti < configurations_[encoder_idx].ts_number_layers;
|
|
|
|
|
++ti) {
|
|
|
|
|
RTC_DCHECK_GT(configurations_[encoder_idx].ts_rate_decimator[ti], 0);
|
|
|
|
|
info.fps_allocation[si].push_back(rtc::saturated_cast<uint8_t>(
|
|
|
|
|
EncoderInfo::kMaxFramerateFraction /
|
|
|
|
|
configurations_[encoder_idx].ts_rate_decimator[ti] +
|
|
|
|
|
0.5));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-07 16:47:06 +01:00
|
|
|
|
|
|
|
|
return info;
|
2016-11-29 01:44:11 -08:00
|
|
|
}
|
|
|
|
|
|
2018-03-14 17:52:55 +01:00
|
|
|
int LibvpxVp8Encoder::RegisterEncodeCompleteCallback(
|
2012-02-02 12:21:47 +00:00
|
|
|
EncodedImageCallback* callback) {
|
|
|
|
|
encoded_complete_callback_ = callback;
|
|
|
|
|
return WEBRTC_VIDEO_CODEC_OK;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
2012-09-20 20:49:12 +00:00
|
|
|
} // namespace webrtc
|