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}
diff --git a/modules/video_coding/BUILD.gn b/modules/video_coding/BUILD.gn
index 8c56078..f3807fa 100644
--- a/modules/video_coding/BUILD.gn
+++ b/modules/video_coding/BUILD.gn
@@ -102,8 +102,6 @@
"decoder_database.h",
"decoding_state.cc",
"decoding_state.h",
- "encoder_database.cc",
- "encoder_database.h",
"fec_controller_default.cc",
"fec_controller_default.h",
"fec_rate_table.h",
diff --git a/modules/video_coding/encoder_database.cc b/modules/video_coding/encoder_database.cc
deleted file mode 100644
index 0497e3a..0000000
--- a/modules/video_coding/encoder_database.cc
+++ /dev/null
@@ -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
diff --git a/modules/video_coding/encoder_database.h b/modules/video_coding/encoder_database.h
deleted file mode 100644
index 09baff0..0000000
--- a/modules/video_coding/encoder_database.h
+++ /dev/null
@@ -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_
diff --git a/modules/video_coding/video_coding_impl.h b/modules/video_coding/video_coding_impl.h
index 87fc5f6..0681eec 100644
--- a/modules/video_coding/video_coding_impl.h
+++ b/modules/video_coding/video_coding_impl.h
@@ -19,7 +19,6 @@
#include "absl/types/optional.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/generic_decoder.h"
#include "modules/video_coding/generic_encoder.h"
@@ -57,47 +56,6 @@
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 {
public:
VideoReceiver(Clock* clock,
diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc
index 8dcb8d9..4be8a7f 100644
--- a/video/video_stream_encoder.cc
+++ b/video/video_stream_encoder.cc
@@ -61,6 +61,8 @@
// optimization module defaults.
const int64_t kFrameRateAvergingWindowSizeMs = (1000 / 30) * 90;
+const size_t kDefaultPayloadSize = 1440;
+
// Initial limits for BALANCED degradation preference.
int MinFps(int pixels) {
if (pixels <= 320 * 240) {
@@ -118,6 +120,51 @@
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
// VideoSourceProxy is responsible ensuring thread safety between calls to
@@ -394,7 +441,6 @@
pending_frame_drops_(0),
generic_encoder_(nullptr),
generic_encoder_callback_(this),
- codec_database_(&generic_encoder_callback_),
next_frame_types_(1, kVideoFrameDelta),
encoder_queue_("EncoderQueue") {
RTC_DCHECK(encoder_stats_observer);
@@ -414,9 +460,11 @@
encoder_queue_.PostTask([this] {
RTC_DCHECK_RUN_ON(&encoder_queue_);
overuse_detector_->StopCheckForOveruse();
- rate_allocator_.reset();
+ rate_allocator_ = nullptr;
bitrate_observer_ = nullptr;
- codec_database_.DeregisterExternalEncoder();
+ if (encoder_ != nullptr && generic_encoder_ != nullptr) {
+ encoder_->Release();
+ }
generic_encoder_ = nullptr;
quality_scaler_ = nullptr;
shutdown_event_.Set();
@@ -593,13 +641,37 @@
}
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.
// Encoder creation block is split in two since EncoderInfo needed to start
// CPU adaptation with the correct settings should be polled after
// encoder_->InitEncode().
if (pending_encoder_creation_) {
if (encoder_) {
- codec_database_.DeregisterExternalEncoder();
+ encoder_->Release();
generic_encoder_ = nullptr;
}
@@ -611,15 +683,26 @@
codec_info_ = settings_.encoder_factory->QueryVideoEncoder(
encoder_config_.video_format);
-
- codec_database_.RegisterExternalEncoder(encoder_.get(),
- HasInternalSource());
+ } else if (reset_required) {
+ RTC_DCHECK(encoder_);
+ encoder_->Release();
}
- // SetSendCodec implies an unconditional call to encoder_->InitEncode().
- bool success = codec_database_.SetSendCodec(&codec, number_of_cores_,
- max_data_payload_length_);
- generic_encoder_ = codec_database_.GetEncoder();
+ bool success = true;
+ if (pending_encoder_creation_ || reset_required || !generic_encoder_) {
+ RTC_DCHECK(encoder_);
+ 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) {
RTC_DCHECK(generic_encoder_);
@@ -633,7 +716,7 @@
<< " max payload size " << max_data_payload_length_;
} else {
RTC_LOG(LS_ERROR) << "Failed to configure encoder.";
- rate_allocator_.reset();
+ rate_allocator_ = nullptr;
}
if (pending_encoder_creation_) {
@@ -1117,8 +1200,8 @@
encoder_info_ = info;
RTC_DCHECK(generic_encoder_);
- RTC_DCHECK(codec_database_.MatchesCurrentResolution(out_frame.width(),
- out_frame.height()));
+ RTC_DCHECK_EQ(send_codec_.width, out_frame.width());
+ RTC_DCHECK_EQ(send_codec_.height, out_frame.height());
const VideoFrameBuffer::Type buffer_type =
out_frame.video_frame_buffer()->type();
const bool is_buffer_type_supported =
diff --git a/video/video_stream_encoder.h b/video/video_stream_encoder.h
index 11aa570..122c9fb 100644
--- a/video/video_stream_encoder.h
+++ b/video/video_stream_encoder.h
@@ -304,11 +304,12 @@
RTC_GUARDED_BY(&encoder_queue_);
// TODO(webrtc:10164): Refactor/remove these VCM classes.
- // |generic_encoder_| instance is owned by |codec_database_|.
- VCMGenericEncoder* generic_encoder_ RTC_GUARDED_BY(&encoder_queue_);
+ std::unique_ptr<VCMGenericEncoder> generic_encoder_
+ RTC_GUARDED_BY(&encoder_queue_);
VCMEncodedFrameCallback generic_encoder_callback_
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
// turn this into a simple bool |pending_keyframe_request_|.
std::vector<FrameType> next_frame_types_ RTC_GUARDED_BY(&encoder_queue_);
diff --git a/video/video_stream_encoder_unittest.cc b/video/video_stream_encoder_unittest.cc
index 9bf9cfd..b247004 100644
--- a/video/video_stream_encoder_unittest.cc
+++ b/video/video_stream_encoder_unittest.cc
@@ -547,7 +547,7 @@
VideoEncoder::EncoderInfo GetEncoderInfo() const override {
rtc::CritScope lock(&local_crit_sect_);
EncoderInfo info;
- if (initialized_) {
+ if (initialized_ == EncoderState::kInitialized) {
if (quality_scaling_) {
info.scaling_settings =
VideoEncoder::ScalingSettings(1, 2, kMinPixelsPerFrame);
@@ -557,6 +557,13 @@
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 CheckLastTimeStampsMatch(int64_t ntp_time_ms,
@@ -611,6 +618,11 @@
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() {
rtc::CritScope lock(&local_crit_sect_);
expect_null_frame_ = true;
@@ -662,6 +674,7 @@
int res =
FakeEncoder::InitEncode(config, number_of_cores, max_payload_size);
rtc::CritScope lock(&local_crit_sect_);
+ EXPECT_EQ(initialized_, EncoderState::kUninitialized);
if (config->codecType == kVideoCodecVP8) {
// Simulate setting up temporal layers, in order to validate the life
// cycle of these objects.
@@ -672,13 +685,22 @@
config->VP8().numberOfTemporalLayers));
}
}
- if (force_init_encode_failed_)
+ if (force_init_encode_failed_) {
+ initialized_ = EncoderState::kInitializationFailed;
return -1;
+ }
- initialized_ = true;
+ initialized_ = EncoderState::kInitialized;
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,
uint32_t framerate) {
rtc::CritScope lock(&local_crit_sect_);
@@ -700,7 +722,12 @@
}
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;
rtc::Event continue_encode_event_;
uint32_t timestamp_ RTC_GUARDED_BY(local_crit_sect_) = 0;
@@ -719,6 +746,8 @@
RTC_GUARDED_BY(local_crit_sect_) = {0, 0, 0, 0};
std::vector<FrameType> last_frame_types_;
bool expect_null_frame_ = false;
+ EncodedImageCallback* encoded_image_callback_
+ RTC_GUARDED_BY(local_crit_sect_) = nullptr;
};
class TestSink : public VideoStreamEncoder::EncoderSink {
@@ -789,6 +818,11 @@
num_expected_layers_ = num_layers;
}
+ int64_t GetLastCaptureTimeMs() const {
+ rtc::CritScope lock(&crit_);
+ return last_capture_time_ms_;
+ }
+
private:
Result OnEncodedImage(
const EncodedImage& encoded_image,
@@ -803,6 +837,7 @@
++num_received_layers_;
}
last_timestamp_ = timestamp;
+ last_capture_time_ms_ = encoded_image.capture_time_ms_;
last_width_ = encoded_image._encodedWidth;
last_height_ = encoded_image._encodedHeight;
if (num_received_layers_ == num_expected_layers_) {
@@ -824,6 +859,7 @@
TestEncoder* test_encoder_;
rtc::Event encoded_frame_event_;
uint32_t last_timestamp_ = 0;
+ int64_t last_capture_time_ms_ = 0;
uint32_t last_height_ = 0;
uint32_t last_width_ = 0;
size_t num_expected_layers_ = 1;
@@ -3583,4 +3619,30 @@
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