Introduce a frame dumping encoder wrapper.

Expose new function MaybeCreateFrameDumpingEncoderWrapper that
wraps another passed encoder and dumps its encoded frames out
into a unique IVF file into the directory specified by the
"WebRTC-EncoderDataDumpDirectory" field trial. If the passed
encoder is nullptr, or the field trial is not setup, the function
just returns the passed encoder. The directory specified by the
field trial parameter should be delimited by ';'.

The new function is wired up in VideoStreamEncoder.

Bug: b/296242528
Change-Id: I6143adf899f78fcc03d4239a86c68dcbab483f1c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/317200
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Markus Handell <handellm@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40600}
This commit is contained in:
Markus Handell 2023-08-22 12:58:47 +02:00 committed by WebRTC LUCI CQ
parent 56c34a9ff4
commit 411639ede8
5 changed files with 206 additions and 4 deletions

View file

@ -163,8 +163,8 @@ bool IvfFileWriter::WriteFrame(const EncodedImage& encoded_image,
int64_t timestamp = using_capture_timestamps_ int64_t timestamp = using_capture_timestamps_
? encoded_image.capture_time_ms_ ? encoded_image.capture_time_ms_
: wrap_handler_.Unwrap(encoded_image.Timestamp()); : wrap_handler_.Unwrap(encoded_image.Timestamp());
if (last_timestamp_ != -1 && timestamp <= last_timestamp_) { if (last_timestamp_ != -1 && timestamp < last_timestamp_) {
RTC_LOG(LS_WARNING) << "Timestamp no increasing: " << last_timestamp_ RTC_LOG(LS_WARNING) << "Timestamp not increasing: " << last_timestamp_
<< " -> " << timestamp; << " -> " << timestamp;
} }
last_timestamp_ = timestamp; last_timestamp_ = timestamp;

View file

@ -194,6 +194,32 @@ rtc_library("frame_dumping_decoder") {
] ]
} }
rtc_library("frame_dumping_encoder") {
visibility = [ "*" ]
sources = [
"frame_dumping_encoder.cc",
"frame_dumping_encoder.h",
]
deps = [
"../api:field_trials_view",
"../api:sequence_checker",
"../api/video:encoded_frame",
"../api/video:encoded_image",
"../api/video:video_frame",
"../api/video_codecs:video_codecs_api",
"../modules/video_coding",
"../modules/video_coding:video_codec_interface",
"../modules/video_coding:video_coding_utility",
"../rtc_base:stringutils",
"../rtc_base:timeutils",
"../rtc_base/system:file_wrapper",
]
absl_deps = [ "//third_party/abseil-cpp/absl/algorithm:container" ]
}
rtc_library("frame_cadence_adapter") { rtc_library("frame_cadence_adapter") {
visibility = [ "*" ] visibility = [ "*" ]
sources = [ sources = [
@ -371,6 +397,7 @@ rtc_library("video_stream_encoder_impl") {
deps = [ deps = [
":frame_cadence_adapter", ":frame_cadence_adapter",
":frame_dumping_encoder",
":video_stream_encoder_interface", ":video_stream_encoder_interface",
"../api:field_trials_view", "../api:field_trials_view",
"../api:rtp_parameters", "../api:rtp_parameters",

View file

@ -0,0 +1,138 @@
/*
* Copyright (c) 2023 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 "video/frame_dumping_encoder.h"
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "absl/algorithm/container.h"
#include "api/sequence_checker.h"
#include "api/video/video_codec_type.h"
#include "modules/video_coding/utility/ivf_file_writer.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/system/file_wrapper.h"
#include "rtc_base/time_utils.h"
namespace webrtc {
namespace {
constexpr auto kEncoderDataDumpDirectoryFieldTrial =
"WebRTC-EncoderDataDumpDirectory";
class FrameDumpingEncoder : public VideoEncoder, public EncodedImageCallback {
public:
FrameDumpingEncoder(std::unique_ptr<VideoEncoder> wrapped,
int64_t origin_time_micros,
std::string output_directory)
: wrapped_(std::move(wrapped)),
output_directory_(output_directory),
origin_time_micros_(origin_time_micros) {
sequence_checker_.Detach();
}
// VideoEncoder overloads.
void SetFecControllerOverride(
FecControllerOverride* fec_controller_override) override {
wrapped_->SetFecControllerOverride(fec_controller_override);
}
int InitEncode(const VideoCodec* codec_settings,
const VideoEncoder::Settings& settings) override {
codec_settings_ = *codec_settings;
return wrapped_->InitEncode(codec_settings, settings);
}
int32_t RegisterEncodeCompleteCallback(
EncodedImageCallback* callback) override {
callback_ = callback;
return wrapped_->RegisterEncodeCompleteCallback(this);
}
int32_t Release() override { return wrapped_->Release(); }
int32_t Encode(const VideoFrame& frame,
const std::vector<VideoFrameType>* frame_types) override {
return wrapped_->Encode(frame, frame_types);
}
void SetRates(const RateControlParameters& parameters) override {
wrapped_->SetRates(parameters);
}
void OnPacketLossRateUpdate(float packet_loss_rate) override {
wrapped_->OnPacketLossRateUpdate(packet_loss_rate);
}
void OnRttUpdate(int64_t rtt_ms) override { wrapped_->OnRttUpdate(rtt_ms); }
void OnLossNotification(const LossNotification& loss_notification) override {
wrapped_->OnLossNotification(loss_notification);
}
EncoderInfo GetEncoderInfo() const override {
return wrapped_->GetEncoderInfo();
}
// EncodedImageCallback overrides.
Result OnEncodedImage(const EncodedImage& encoded_image,
const CodecSpecificInfo* codec_specific_info) override {
RTC_DCHECK_RUN_ON(&sequence_checker_);
GetFileWriterForSimulcastIndex(encoded_image.SimulcastIndex().value_or(0))
.WriteFrame(encoded_image, codec_settings_.codecType);
return callback_->OnEncodedImage(encoded_image, codec_specific_info);
}
void OnDroppedFrame(DropReason reason) override {
RTC_DCHECK_RUN_ON(&sequence_checker_);
callback_->OnDroppedFrame(reason);
}
private:
std::string FilenameFromSimulcastIndex(int index) {
char filename_buffer[1024];
rtc::SimpleStringBuilder builder(filename_buffer);
builder << output_directory_ << "/webrtc_encoded_frames"
<< "." << origin_time_micros_ << "." << index << ".ivf";
return builder.str();
}
IvfFileWriter& GetFileWriterForSimulcastIndex(int index) {
const auto& it = writers_by_simulcast_index_.find(index);
if (it != writers_by_simulcast_index_.end()) {
return *it->second;
}
auto writer = IvfFileWriter::Wrap(
FileWrapper::OpenWriteOnly(FilenameFromSimulcastIndex(index)),
/*byte_limit=*/100'000'000);
auto* writer_ptr = writer.get();
writers_by_simulcast_index_.insert(
std::make_pair(index, std::move(writer)));
return *writer_ptr;
}
SequenceChecker sequence_checker_;
std::unique_ptr<VideoEncoder> wrapped_;
std::map<int, std::unique_ptr<IvfFileWriter>> writers_by_simulcast_index_;
std::unique_ptr<IvfFileWriter> writer_;
VideoCodec codec_settings_;
EncodedImageCallback* callback_ = nullptr;
std::string output_directory_;
int64_t origin_time_micros_ = 0;
};
} // namespace
std::unique_ptr<VideoEncoder> MaybeCreateFrameDumpingEncoderWrapper(
std::unique_ptr<VideoEncoder> encoder,
const FieldTrialsView& field_trials) {
auto output_directory =
field_trials.Lookup(kEncoderDataDumpDirectoryFieldTrial);
if (output_directory.empty() || !encoder) {
return encoder;
}
absl::c_replace(output_directory, ';', '/');
return std::make_unique<FrameDumpingEncoder>(
std::move(encoder), rtc::TimeMicros(), output_directory);
}
} // namespace webrtc

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2023 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 VIDEO_FRAME_DUMPING_ENCODER_H_
#define VIDEO_FRAME_DUMPING_ENCODER_H_
#include <memory>
#include "api/field_trials_view.h"
#include "api/video_codecs/video_encoder.h"
namespace webrtc {
// Creates an encoder that wraps another passed encoder and dumps its encoded
// frames out into a unique IVF file into the directory specified by the
// "WebRTC-EncoderDataDumpDirectory" field trial. Each file generated is
// suffixed by the simulcast index of the encoded frames. If the passed encoder
// is nullptr, or the field trial is not setup, the function just returns the
// passed encoder. The directory specified by the field trial parameter should
// be delimited by ';'.
std::unique_ptr<VideoEncoder> MaybeCreateFrameDumpingEncoderWrapper(
std::unique_ptr<VideoEncoder> encoder,
const FieldTrialsView& field_trials);
} // namespace webrtc
#endif // VIDEO_FRAME_DUMPING_ENCODER_H_

View file

@ -55,6 +55,7 @@
#include "video/alignment_adjuster.h" #include "video/alignment_adjuster.h"
#include "video/config/encoder_stream_factory.h" #include "video/config/encoder_stream_factory.h"
#include "video/frame_cadence_adapter.h" #include "video/frame_cadence_adapter.h"
#include "video/frame_dumping_encoder.h"
namespace webrtc { namespace webrtc {
@ -950,8 +951,10 @@ void VideoStreamEncoder::ReconfigureEncoder() {
// supports only single instance of encoder of given type. // supports only single instance of encoder of given type.
encoder_.reset(); encoder_.reset();
encoder_ = settings_.encoder_factory->CreateVideoEncoder( encoder_ = MaybeCreateFrameDumpingEncoderWrapper(
encoder_config_.video_format); settings_.encoder_factory->CreateVideoEncoder(
encoder_config_.video_format),
field_trials_);
if (!encoder_) { if (!encoder_) {
RTC_LOG(LS_ERROR) << "CreateVideoEncoder failed, failing encoder format: " RTC_LOG(LS_ERROR) << "CreateVideoEncoder failed, failing encoder format: "
<< encoder_config_.video_format.ToString(); << encoder_config_.video_format.ToString();