Remove VCMEncoderDataBase and put remaining code into VideoStreamEncoder

This is a reland of
https://webrtc-review.googlesource.com/c/src/+/123920
Patch set 1 is identical to the previous CL, additional patch sets fix
the bug that was introduced and adds test coverage.

Since this "data base" only holds a single encoder instance it just
serves to confuse object ownership. Removing it and giving ownership
of generic encoder instance to VideoStreamEncoder.

This CL also removes VideoSender interface from video_coding_impl.h,
which is mostly a leftover from
https://webrtc-review.googlesource.com/c/src/+/123540

Bug: webrtc:10164
Change-Id: Ieaf23457d69af0d6356b70461112892b14760b19
Reviewed-on: https://webrtc-review.googlesource.com/c/124488
Reviewed-by: Rasmus Brandt <brandtr@webrtc.org>
Commit-Queue: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26857}
This commit is contained in:
Erik Språng 2019-02-26 15:52:33 +01:00 committed by Commit Bot
parent 695af944c4
commit b7cb7b5e94
7 changed files with 167 additions and 319 deletions

View file

@ -102,8 +102,6 @@ rtc_static_library("video_coding") {
"decoder_database.h", "decoder_database.h",
"decoding_state.cc", "decoding_state.cc",
"decoding_state.h", "decoding_state.h",
"encoder_database.cc",
"encoder_database.h",
"fec_controller_default.cc", "fec_controller_default.cc",
"fec_controller_default.h", "fec_controller_default.h",
"fec_rate_table.h", "fec_rate_table.h",

View file

@ -1,186 +0,0 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* 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.
*/
#include "modules/video_coding/encoder_database.h"
#include <string.h>
#include "common_types.h" // NOLINT(build/include)
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
namespace webrtc {
namespace {
const size_t kDefaultPayloadSize = 1440;
}
VCMEncoderDataBase::VCMEncoderDataBase(
VCMEncodedFrameCallback* encoded_frame_callback)
: number_of_cores_(0),
max_payload_size_(kDefaultPayloadSize),
pending_encoder_reset_(true),
send_codec_(),
external_encoder_(nullptr),
internal_source_(false),
encoded_frame_callback_(encoded_frame_callback) {}
VCMEncoderDataBase::~VCMEncoderDataBase() {
DeleteEncoder();
}
// Assuming only one registered encoder - since only one used, no need for more.
bool VCMEncoderDataBase::SetSendCodec(const VideoCodec* send_codec,
int number_of_cores,
size_t max_payload_size) {
RTC_DCHECK(send_codec);
if (max_payload_size == 0) {
max_payload_size = kDefaultPayloadSize;
}
RTC_DCHECK_GE(number_of_cores, 1);
// Make sure the start bit rate is sane...
RTC_DCHECK_LE(send_codec->startBitrate, 1000000);
bool reset_required = pending_encoder_reset_;
if (number_of_cores_ != number_of_cores) {
number_of_cores_ = number_of_cores;
reset_required = true;
}
if (max_payload_size_ != max_payload_size) {
max_payload_size_ = max_payload_size;
reset_required = true;
}
VideoCodec new_send_codec;
memcpy(&new_send_codec, send_codec, sizeof(new_send_codec));
if (new_send_codec.maxBitrate == 0) {
// max is one bit per pixel
new_send_codec.maxBitrate = (static_cast<int>(send_codec->height) *
static_cast<int>(send_codec->width) *
static_cast<int>(send_codec->maxFramerate)) /
1000;
if (send_codec->startBitrate > new_send_codec.maxBitrate) {
// But if the user tries to set a higher start bit rate we will
// increase the max accordingly.
new_send_codec.maxBitrate = send_codec->startBitrate;
}
}
if (new_send_codec.startBitrate > new_send_codec.maxBitrate)
new_send_codec.startBitrate = new_send_codec.maxBitrate;
if (!reset_required) {
reset_required = RequiresEncoderReset(new_send_codec);
}
memcpy(&send_codec_, &new_send_codec, sizeof(send_codec_));
if (!reset_required) {
return true;
}
// If encoder exists, will destroy it and create new one.
DeleteEncoder();
ptr_encoder_.reset(new VCMGenericEncoder(
external_encoder_, encoded_frame_callback_, internal_source_));
encoded_frame_callback_->SetInternalSource(internal_source_);
if (ptr_encoder_->InitEncode(&send_codec_, number_of_cores_,
max_payload_size_) < 0) {
RTC_LOG(LS_ERROR) << "Failed to initialize video encoder.";
DeleteEncoder();
return false;
}
pending_encoder_reset_ = false;
return true;
}
void VCMEncoderDataBase::DeregisterExternalEncoder() {
DeleteEncoder();
memset(&send_codec_, 0, sizeof(VideoCodec));
external_encoder_ = nullptr;
internal_source_ = false;
}
void VCMEncoderDataBase::RegisterExternalEncoder(VideoEncoder* external_encoder,
bool internal_source) {
// Since only one encoder can be used at a given time, only one external
// encoder can be registered/used.
RTC_CHECK(external_encoder_ == nullptr);
external_encoder_ = external_encoder;
internal_source_ = internal_source;
pending_encoder_reset_ = true;
}
bool VCMEncoderDataBase::RequiresEncoderReset(
const VideoCodec& new_send_codec) {
if (!ptr_encoder_)
return true;
// Does not check startBitrate, maxFramerate or plType
if (new_send_codec.codecType != send_codec_.codecType ||
new_send_codec.width != send_codec_.width ||
new_send_codec.height != send_codec_.height ||
new_send_codec.maxBitrate != send_codec_.maxBitrate ||
new_send_codec.minBitrate != send_codec_.minBitrate ||
new_send_codec.qpMax != send_codec_.qpMax ||
new_send_codec.numberOfSimulcastStreams !=
send_codec_.numberOfSimulcastStreams ||
new_send_codec.mode != send_codec_.mode) {
return true;
}
switch (new_send_codec.codecType) {
case kVideoCodecVP8:
if (new_send_codec.VP8() != *send_codec_.VP8()) {
return true;
}
break;
case kVideoCodecVP9:
if (new_send_codec.VP9() != *send_codec_.VP9()) {
return true;
}
break;
case kVideoCodecH264:
if (new_send_codec.H264() != *send_codec_.H264()) {
return true;
}
break;
default:
break;
}
for (unsigned char i = 0; i < new_send_codec.numberOfSimulcastStreams; ++i) {
if (new_send_codec.simulcastStream[i] != send_codec_.simulcastStream[i])
return true;
}
return false;
}
VCMGenericEncoder* VCMEncoderDataBase::GetEncoder() {
return ptr_encoder_.get();
}
void VCMEncoderDataBase::DeleteEncoder() {
if (!ptr_encoder_)
return;
ptr_encoder_->Release();
ptr_encoder_.reset();
}
bool VCMEncoderDataBase::MatchesCurrentResolution(int width, int height) const {
return send_codec_.width == width && send_codec_.height == height;
}
} // namespace webrtc

View file

@ -1,68 +0,0 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* 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.
*/
#ifndef MODULES_VIDEO_CODING_ENCODER_DATABASE_H_
#define MODULES_VIDEO_CODING_ENCODER_DATABASE_H_
#include <stddef.h>
#include <memory>
#include "api/video_codecs/video_codec.h"
#include "api/video_codecs/video_encoder.h"
#include "modules/video_coding/generic_encoder.h"
namespace webrtc {
class VCMEncoderDataBase {
public:
explicit VCMEncoderDataBase(VCMEncodedFrameCallback* encoded_frame_callback);
~VCMEncoderDataBase();
// Sets the sender side codec and initiates the desired codec given the
// VideoCodec struct.
// Returns true if the codec was successfully registered, false otherwise.
bool SetSendCodec(const VideoCodec* send_codec,
int number_of_cores,
size_t max_payload_size);
// Registers and initializes an external encoder object.
// |internal_source| should be set to true if the codec has an internal
// video source and doesn't need the user to provide it with frames via
// the Encode() method.
void RegisterExternalEncoder(VideoEncoder* external_encoder,
bool internal_source);
// Deregisters any external encoder.
void DeregisterExternalEncoder();
VCMGenericEncoder* GetEncoder();
bool MatchesCurrentResolution(int width, int height) const;
private:
// Determines whether a new codec has to be created or not.
// Checks every setting apart from maxFramerate and startBitrate.
bool RequiresEncoderReset(const VideoCodec& send_codec);
void DeleteEncoder();
int number_of_cores_;
size_t max_payload_size_;
bool pending_encoder_reset_;
VideoCodec send_codec_;
VideoEncoder* external_encoder_;
bool internal_source_;
VCMEncodedFrameCallback* const encoded_frame_callback_;
std::unique_ptr<VCMGenericEncoder> ptr_encoder_;
};
} // namespace webrtc
#endif // MODULES_VIDEO_CODING_ENCODER_DATABASE_H_

View file

@ -19,7 +19,6 @@
#include "absl/types/optional.h" #include "absl/types/optional.h"
#include "modules/video_coding/decoder_database.h" #include "modules/video_coding/decoder_database.h"
#include "modules/video_coding/encoder_database.h"
#include "modules/video_coding/frame_buffer.h" #include "modules/video_coding/frame_buffer.h"
#include "modules/video_coding/generic_decoder.h" #include "modules/video_coding/generic_decoder.h"
#include "modules/video_coding/generic_encoder.h" #include "modules/video_coding/generic_encoder.h"
@ -57,47 +56,6 @@ class VCMProcessTimer {
int64_t _latestMs; int64_t _latestMs;
}; };
class VideoSender {
public:
typedef VideoCodingModule::SenderNackMode SenderNackMode;
VideoSender(Clock* clock, EncodedImageCallback* post_encode_callback);
~VideoSender();
// Register the send codec to be used.
// This method must be called on the construction thread.
int32_t RegisterSendCodec(const VideoCodec* sendCodec,
uint32_t numberOfCores,
uint32_t maxPayloadSize);
void RegisterExternalEncoder(VideoEncoder* externalEncoder,
bool internalSource);
// Update the the encoder with new bitrate allocation and framerate.
int32_t SetChannelParameters(const VideoBitrateAllocation& bitrate_allocation,
uint32_t framerate_fps);
int32_t AddVideoFrame(const VideoFrame& videoFrame,
const CodecSpecificInfo* codecSpecificInfo,
absl::optional<VideoEncoder::EncoderInfo> encoder_info);
int32_t IntraFrameRequest(size_t stream_index);
private:
rtc::CriticalSection encoder_crit_;
VCMGenericEncoder* _encoder;
VCMEncodedFrameCallback _encodedFrameCallback RTC_GUARDED_BY(encoder_crit_);
VCMEncoderDataBase _codecDataBase RTC_GUARDED_BY(encoder_crit_);
// Must be accessed on the construction thread of VideoSender.
VideoCodec current_codec_;
rtc::SequencedTaskChecker sequenced_checker_;
rtc::CriticalSection params_crit_;
bool encoder_has_internal_source_ RTC_GUARDED_BY(params_crit_);
std::vector<FrameType> next_frame_types_ RTC_GUARDED_BY(params_crit_);
};
class VideoReceiver : public Module { class VideoReceiver : public Module {
public: public:
VideoReceiver(Clock* clock, VideoReceiver(Clock* clock,

View file

@ -61,6 +61,8 @@ const float kFramedropThreshold = 0.3;
// optimization module defaults. // optimization module defaults.
const int64_t kFrameRateAvergingWindowSizeMs = (1000 / 30) * 90; const int64_t kFrameRateAvergingWindowSizeMs = (1000 / 30) * 90;
const size_t kDefaultPayloadSize = 1440;
// Initial limits for BALANCED degradation preference. // Initial limits for BALANCED degradation preference.
int MinFps(int pixels) { int MinFps(int pixels) {
if (pixels <= 320 * 240) { if (pixels <= 320 * 240) {
@ -118,6 +120,51 @@ CpuOveruseOptions GetCpuOveruseOptions(
return options; return options;
} }
bool RequiresEncoderReset(const VideoCodec& previous_send_codec,
const VideoCodec& new_send_codec) {
// Does not check startBitrate or maxFramerate.
if (new_send_codec.codecType != previous_send_codec.codecType ||
new_send_codec.width != previous_send_codec.width ||
new_send_codec.height != previous_send_codec.height ||
new_send_codec.maxBitrate != previous_send_codec.maxBitrate ||
new_send_codec.minBitrate != previous_send_codec.minBitrate ||
new_send_codec.qpMax != previous_send_codec.qpMax ||
new_send_codec.numberOfSimulcastStreams !=
previous_send_codec.numberOfSimulcastStreams ||
new_send_codec.mode != previous_send_codec.mode) {
return true;
}
switch (new_send_codec.codecType) {
case kVideoCodecVP8:
if (new_send_codec.VP8() != previous_send_codec.VP8()) {
return true;
}
break;
case kVideoCodecVP9:
if (new_send_codec.VP9() != previous_send_codec.VP9()) {
return true;
}
break;
case kVideoCodecH264:
if (new_send_codec.H264() != previous_send_codec.H264()) {
return true;
}
break;
default:
break;
}
for (unsigned char i = 0; i < new_send_codec.numberOfSimulcastStreams; ++i) {
if (new_send_codec.simulcastStream[i] !=
previous_send_codec.simulcastStream[i])
return true;
}
return false;
}
} // namespace } // namespace
// VideoSourceProxy is responsible ensuring thread safety between calls to // VideoSourceProxy is responsible ensuring thread safety between calls to
@ -394,7 +441,6 @@ VideoStreamEncoder::VideoStreamEncoder(
pending_frame_drops_(0), pending_frame_drops_(0),
generic_encoder_(nullptr), generic_encoder_(nullptr),
generic_encoder_callback_(this), generic_encoder_callback_(this),
codec_database_(&generic_encoder_callback_),
next_frame_types_(1, kVideoFrameDelta), next_frame_types_(1, kVideoFrameDelta),
encoder_queue_("EncoderQueue") { encoder_queue_("EncoderQueue") {
RTC_DCHECK(encoder_stats_observer); RTC_DCHECK(encoder_stats_observer);
@ -414,9 +460,11 @@ void VideoStreamEncoder::Stop() {
encoder_queue_.PostTask([this] { encoder_queue_.PostTask([this] {
RTC_DCHECK_RUN_ON(&encoder_queue_); RTC_DCHECK_RUN_ON(&encoder_queue_);
overuse_detector_->StopCheckForOveruse(); overuse_detector_->StopCheckForOveruse();
rate_allocator_.reset(); rate_allocator_ = nullptr;
bitrate_observer_ = nullptr; bitrate_observer_ = nullptr;
codec_database_.DeregisterExternalEncoder(); if (encoder_ != nullptr && generic_encoder_ != nullptr) {
encoder_->Release();
}
generic_encoder_ = nullptr; generic_encoder_ = nullptr;
quality_scaler_ = nullptr; quality_scaler_ = nullptr;
shutdown_event_.Set(); shutdown_event_.Set();
@ -593,13 +641,37 @@ void VideoStreamEncoder::ReconfigureEncoder() {
} }
source_proxy_->SetMaxFramerate(max_framerate); source_proxy_->SetMaxFramerate(max_framerate);
if (codec.maxBitrate == 0) {
// max is one bit per pixel
codec.maxBitrate =
(static_cast<int>(codec.height) * static_cast<int>(codec.width) *
static_cast<int>(codec.maxFramerate)) /
1000;
if (codec.startBitrate > codec.maxBitrate) {
// But if the user tries to set a higher start bit rate we will
// increase the max accordingly.
codec.maxBitrate = codec.startBitrate;
}
}
if (codec.startBitrate > codec.maxBitrate) {
codec.startBitrate = codec.maxBitrate;
}
// Reset (release existing encoder) if one exists and anything except
// start bitrate or max framerate has changed. Don't call Release() if
// |pending_encoder_creation_| as that means this is a new encoder
// that has not yet been initialized.
const bool reset_required = RequiresEncoderReset(codec, send_codec_);
send_codec_ = codec;
// Keep the same encoder, as long as the video_format is unchanged. // Keep the same encoder, as long as the video_format is unchanged.
// Encoder creation block is split in two since EncoderInfo needed to start // Encoder creation block is split in two since EncoderInfo needed to start
// CPU adaptation with the correct settings should be polled after // CPU adaptation with the correct settings should be polled after
// encoder_->InitEncode(). // encoder_->InitEncode().
if (pending_encoder_creation_) { if (pending_encoder_creation_) {
if (encoder_) { if (encoder_) {
codec_database_.DeregisterExternalEncoder(); encoder_->Release();
generic_encoder_ = nullptr; generic_encoder_ = nullptr;
} }
@ -611,15 +683,26 @@ void VideoStreamEncoder::ReconfigureEncoder() {
codec_info_ = settings_.encoder_factory->QueryVideoEncoder( codec_info_ = settings_.encoder_factory->QueryVideoEncoder(
encoder_config_.video_format); encoder_config_.video_format);
} else if (reset_required) {
codec_database_.RegisterExternalEncoder(encoder_.get(), RTC_DCHECK(encoder_);
HasInternalSource()); encoder_->Release();
} }
// SetSendCodec implies an unconditional call to encoder_->InitEncode(). bool success = true;
bool success = codec_database_.SetSendCodec(&codec, number_of_cores_, if (pending_encoder_creation_ || reset_required || !generic_encoder_) {
max_data_payload_length_); RTC_DCHECK(encoder_);
generic_encoder_ = codec_database_.GetEncoder(); generic_encoder_ = absl::make_unique<VCMGenericEncoder>(
encoder_.get(), &generic_encoder_callback_, HasInternalSource());
generic_encoder_callback_.SetInternalSource(HasInternalSource());
if (generic_encoder_->InitEncode(&send_codec_, number_of_cores_,
max_data_payload_length_ > 0
? max_data_payload_length_
: kDefaultPayloadSize) < 0) {
encoder_->Release();
generic_encoder_ = nullptr;
success = false;
}
}
if (success) { if (success) {
RTC_DCHECK(generic_encoder_); RTC_DCHECK(generic_encoder_);
@ -633,7 +716,7 @@ void VideoStreamEncoder::ReconfigureEncoder() {
<< " max payload size " << max_data_payload_length_; << " max payload size " << max_data_payload_length_;
} else { } else {
RTC_LOG(LS_ERROR) << "Failed to configure encoder."; RTC_LOG(LS_ERROR) << "Failed to configure encoder.";
rate_allocator_.reset(); rate_allocator_ = nullptr;
} }
if (pending_encoder_creation_) { if (pending_encoder_creation_) {
@ -1117,8 +1200,8 @@ void VideoStreamEncoder::EncodeVideoFrame(const VideoFrame& video_frame,
encoder_info_ = info; encoder_info_ = info;
RTC_DCHECK(generic_encoder_); RTC_DCHECK(generic_encoder_);
RTC_DCHECK(codec_database_.MatchesCurrentResolution(out_frame.width(), RTC_DCHECK_EQ(send_codec_.width, out_frame.width());
out_frame.height())); RTC_DCHECK_EQ(send_codec_.height, out_frame.height());
const VideoFrameBuffer::Type buffer_type = const VideoFrameBuffer::Type buffer_type =
out_frame.video_frame_buffer()->type(); out_frame.video_frame_buffer()->type();
const bool is_buffer_type_supported = const bool is_buffer_type_supported =

View file

@ -304,11 +304,12 @@ class VideoStreamEncoder : public VideoStreamEncoderInterface,
RTC_GUARDED_BY(&encoder_queue_); RTC_GUARDED_BY(&encoder_queue_);
// TODO(webrtc:10164): Refactor/remove these VCM classes. // TODO(webrtc:10164): Refactor/remove these VCM classes.
// |generic_encoder_| instance is owned by |codec_database_|. std::unique_ptr<VCMGenericEncoder> generic_encoder_
VCMGenericEncoder* generic_encoder_ RTC_GUARDED_BY(&encoder_queue_); RTC_GUARDED_BY(&encoder_queue_);
VCMEncodedFrameCallback generic_encoder_callback_ VCMEncodedFrameCallback generic_encoder_callback_
RTC_GUARDED_BY(&encoder_queue_); RTC_GUARDED_BY(&encoder_queue_);
VCMEncoderDataBase codec_database_ RTC_GUARDED_BY(&encoder_queue_); VideoCodec send_codec_ RTC_GUARDED_BY(&encoder_queue_);
// TODO(sprang): Change actually support keyframe per simulcast stream, or // TODO(sprang): Change actually support keyframe per simulcast stream, or
// turn this into a simple bool |pending_keyframe_request_|. // turn this into a simple bool |pending_keyframe_request_|.
std::vector<FrameType> next_frame_types_ RTC_GUARDED_BY(&encoder_queue_); std::vector<FrameType> next_frame_types_ RTC_GUARDED_BY(&encoder_queue_);

View file

@ -547,7 +547,7 @@ class VideoStreamEncoderTest : public ::testing::Test {
VideoEncoder::EncoderInfo GetEncoderInfo() const override { VideoEncoder::EncoderInfo GetEncoderInfo() const override {
rtc::CritScope lock(&local_crit_sect_); rtc::CritScope lock(&local_crit_sect_);
EncoderInfo info; EncoderInfo info;
if (initialized_) { if (initialized_ == EncoderState::kInitialized) {
if (quality_scaling_) { if (quality_scaling_) {
info.scaling_settings = info.scaling_settings =
VideoEncoder::ScalingSettings(1, 2, kMinPixelsPerFrame); VideoEncoder::ScalingSettings(1, 2, kMinPixelsPerFrame);
@ -557,6 +557,13 @@ class VideoStreamEncoderTest : public ::testing::Test {
return info; return info;
} }
int32_t RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) override {
rtc::CritScope lock(&local_crit_sect_);
encoded_image_callback_ = callback;
return FakeEncoder::RegisterEncodeCompleteCallback(callback);
}
void ContinueEncode() { continue_encode_event_.Set(); } void ContinueEncode() { continue_encode_event_.Set(); }
void CheckLastTimeStampsMatch(int64_t ntp_time_ms, void CheckLastTimeStampsMatch(int64_t ntp_time_ms,
@ -611,6 +618,11 @@ class VideoStreamEncoderTest : public ::testing::Test {
FakeEncoder::Encode(input_image, nullptr, &frame_type); FakeEncoder::Encode(input_image, nullptr, &frame_type);
} }
void InjectEncodedImage(const EncodedImage& image) {
rtc::CritScope lock(&local_crit_sect_);
encoded_image_callback_->OnEncodedImage(image, nullptr, nullptr);
}
void ExpectNullFrame() { void ExpectNullFrame() {
rtc::CritScope lock(&local_crit_sect_); rtc::CritScope lock(&local_crit_sect_);
expect_null_frame_ = true; expect_null_frame_ = true;
@ -662,6 +674,7 @@ class VideoStreamEncoderTest : public ::testing::Test {
int res = int res =
FakeEncoder::InitEncode(config, number_of_cores, max_payload_size); FakeEncoder::InitEncode(config, number_of_cores, max_payload_size);
rtc::CritScope lock(&local_crit_sect_); rtc::CritScope lock(&local_crit_sect_);
EXPECT_EQ(initialized_, EncoderState::kUninitialized);
if (config->codecType == kVideoCodecVP8) { if (config->codecType == kVideoCodecVP8) {
// Simulate setting up temporal layers, in order to validate the life // Simulate setting up temporal layers, in order to validate the life
// cycle of these objects. // cycle of these objects.
@ -672,13 +685,22 @@ class VideoStreamEncoderTest : public ::testing::Test {
config->VP8().numberOfTemporalLayers)); config->VP8().numberOfTemporalLayers));
} }
} }
if (force_init_encode_failed_) if (force_init_encode_failed_) {
initialized_ = EncoderState::kInitializationFailed;
return -1; return -1;
}
initialized_ = true; initialized_ = EncoderState::kInitialized;
return res; return res;
} }
int32_t Release() override {
rtc::CritScope lock(&local_crit_sect_);
EXPECT_NE(initialized_, EncoderState::kUninitialized);
initialized_ = EncoderState::kUninitialized;
return FakeEncoder::Release();
}
int32_t SetRateAllocation(const VideoBitrateAllocation& rate_allocation, int32_t SetRateAllocation(const VideoBitrateAllocation& rate_allocation,
uint32_t framerate) { uint32_t framerate) {
rtc::CritScope lock(&local_crit_sect_); rtc::CritScope lock(&local_crit_sect_);
@ -700,7 +722,12 @@ class VideoStreamEncoderTest : public ::testing::Test {
} }
rtc::CriticalSection local_crit_sect_; rtc::CriticalSection local_crit_sect_;
bool initialized_ RTC_GUARDED_BY(local_crit_sect_) = false; enum class EncoderState {
kUninitialized,
kInitializationFailed,
kInitialized
} initialized_ RTC_GUARDED_BY(local_crit_sect_) =
EncoderState::kUninitialized;
bool block_next_encode_ RTC_GUARDED_BY(local_crit_sect_) = false; bool block_next_encode_ RTC_GUARDED_BY(local_crit_sect_) = false;
rtc::Event continue_encode_event_; rtc::Event continue_encode_event_;
uint32_t timestamp_ RTC_GUARDED_BY(local_crit_sect_) = 0; uint32_t timestamp_ RTC_GUARDED_BY(local_crit_sect_) = 0;
@ -719,6 +746,8 @@ class VideoStreamEncoderTest : public ::testing::Test {
RTC_GUARDED_BY(local_crit_sect_) = {0, 0, 0, 0}; RTC_GUARDED_BY(local_crit_sect_) = {0, 0, 0, 0};
std::vector<FrameType> last_frame_types_; std::vector<FrameType> last_frame_types_;
bool expect_null_frame_ = false; bool expect_null_frame_ = false;
EncodedImageCallback* encoded_image_callback_
RTC_GUARDED_BY(local_crit_sect_) = nullptr;
}; };
class TestSink : public VideoStreamEncoder::EncoderSink { class TestSink : public VideoStreamEncoder::EncoderSink {
@ -789,6 +818,11 @@ class VideoStreamEncoderTest : public ::testing::Test {
num_expected_layers_ = num_layers; num_expected_layers_ = num_layers;
} }
int64_t GetLastCaptureTimeMs() const {
rtc::CritScope lock(&crit_);
return last_capture_time_ms_;
}
private: private:
Result OnEncodedImage( Result OnEncodedImage(
const EncodedImage& encoded_image, const EncodedImage& encoded_image,
@ -803,6 +837,7 @@ class VideoStreamEncoderTest : public ::testing::Test {
++num_received_layers_; ++num_received_layers_;
} }
last_timestamp_ = timestamp; last_timestamp_ = timestamp;
last_capture_time_ms_ = encoded_image.capture_time_ms_;
last_width_ = encoded_image._encodedWidth; last_width_ = encoded_image._encodedWidth;
last_height_ = encoded_image._encodedHeight; last_height_ = encoded_image._encodedHeight;
if (num_received_layers_ == num_expected_layers_) { if (num_received_layers_ == num_expected_layers_) {
@ -824,6 +859,7 @@ class VideoStreamEncoderTest : public ::testing::Test {
TestEncoder* test_encoder_; TestEncoder* test_encoder_;
rtc::Event encoded_frame_event_; rtc::Event encoded_frame_event_;
uint32_t last_timestamp_ = 0; uint32_t last_timestamp_ = 0;
int64_t last_capture_time_ms_ = 0;
uint32_t last_height_ = 0; uint32_t last_height_ = 0;
uint32_t last_width_ = 0; uint32_t last_width_ = 0;
size_t num_expected_layers_ = 1; size_t num_expected_layers_ = 1;
@ -3583,4 +3619,30 @@ TEST_F(VideoStreamEncoderTest, RequestKeyframeInternalSource) {
video_stream_encoder_->Stop(); video_stream_encoder_->Stop();
} }
TEST_F(VideoStreamEncoderTest, AdjustsTimestampInternalSource) {
// Configure internal source factory and setup test again.
encoder_factory_.SetHasInternalSource(true);
ResetEncoder("VP8", 1, 1, 1, false);
video_stream_encoder_->OnBitrateUpdated(kTargetBitrateBps, 0, 0);
int64_t timestamp = 1;
EncodedImage image;
image.Allocate(kTargetBitrateBps / kDefaultFramerate / 8);
image.capture_time_ms_ = ++timestamp;
image.SetTimestamp(static_cast<uint32_t>(timestamp * 90));
const int64_t kEncodeFinishDelayMs = 10;
image.timing_.encode_start_ms = timestamp;
image.timing_.encode_finish_ms = timestamp + kEncodeFinishDelayMs;
fake_encoder_.InjectEncodedImage(image);
// Wait for frame without incrementing clock.
EXPECT_TRUE(sink_.WaitForFrame(kDefaultTimeoutMs));
// Frame is captured kEncodeFinishDelayMs before it's encoded, so restored
// capture timestamp should be kEncodeFinishDelayMs in the past.
EXPECT_EQ(sink_.GetLastCaptureTimeMs(),
fake_clock_.TimeNanos() / rtc::kNumNanosecsPerMillisec -
kEncodeFinishDelayMs);
video_stream_encoder_->Stop();
}
} // namespace webrtc } // namespace webrtc