diff --git a/modules/video_coding/utility/ivf_file_writer.cc b/modules/video_coding/utility/ivf_file_writer.cc index cabfd497c6..01025986d6 100644 --- a/modules/video_coding/utility/ivf_file_writer.cc +++ b/modules/video_coding/utility/ivf_file_writer.cc @@ -163,8 +163,8 @@ bool IvfFileWriter::WriteFrame(const EncodedImage& encoded_image, int64_t timestamp = using_capture_timestamps_ ? encoded_image.capture_time_ms_ : wrap_handler_.Unwrap(encoded_image.Timestamp()); - if (last_timestamp_ != -1 && timestamp <= last_timestamp_) { - RTC_LOG(LS_WARNING) << "Timestamp no increasing: " << last_timestamp_ + if (last_timestamp_ != -1 && timestamp < last_timestamp_) { + RTC_LOG(LS_WARNING) << "Timestamp not increasing: " << last_timestamp_ << " -> " << timestamp; } last_timestamp_ = timestamp; diff --git a/video/BUILD.gn b/video/BUILD.gn index e0ef6fab8f..3de58b6615 100644 --- a/video/BUILD.gn +++ b/video/BUILD.gn @@ -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") { visibility = [ "*" ] sources = [ @@ -371,6 +397,7 @@ rtc_library("video_stream_encoder_impl") { deps = [ ":frame_cadence_adapter", + ":frame_dumping_encoder", ":video_stream_encoder_interface", "../api:field_trials_view", "../api:rtp_parameters", diff --git a/video/frame_dumping_encoder.cc b/video/frame_dumping_encoder.cc new file mode 100644 index 0000000000..5c9e601a49 --- /dev/null +++ b/video/frame_dumping_encoder.cc @@ -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 +#include +#include +#include + +#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 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* 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 wrapped_; + std::map> writers_by_simulcast_index_; + std::unique_ptr writer_; + VideoCodec codec_settings_; + EncodedImageCallback* callback_ = nullptr; + std::string output_directory_; + int64_t origin_time_micros_ = 0; +}; + +} // namespace + +std::unique_ptr MaybeCreateFrameDumpingEncoderWrapper( + std::unique_ptr 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( + std::move(encoder), rtc::TimeMicros(), output_directory); +} + +} // namespace webrtc diff --git a/video/frame_dumping_encoder.h b/video/frame_dumping_encoder.h new file mode 100644 index 0000000000..2fd543a632 --- /dev/null +++ b/video/frame_dumping_encoder.h @@ -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 + +#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 MaybeCreateFrameDumpingEncoderWrapper( + std::unique_ptr encoder, + const FieldTrialsView& field_trials); + +} // namespace webrtc + +#endif // VIDEO_FRAME_DUMPING_ENCODER_H_ diff --git a/video/video_stream_encoder.cc b/video/video_stream_encoder.cc index 98d163045d..c367510b21 100644 --- a/video/video_stream_encoder.cc +++ b/video/video_stream_encoder.cc @@ -55,6 +55,7 @@ #include "video/alignment_adjuster.h" #include "video/config/encoder_stream_factory.h" #include "video/frame_cadence_adapter.h" +#include "video/frame_dumping_encoder.h" namespace webrtc { @@ -950,8 +951,10 @@ void VideoStreamEncoder::ReconfigureEncoder() { // supports only single instance of encoder of given type. encoder_.reset(); - encoder_ = settings_.encoder_factory->CreateVideoEncoder( - encoder_config_.video_format); + encoder_ = MaybeCreateFrameDumpingEncoderWrapper( + settings_.encoder_factory->CreateVideoEncoder( + encoder_config_.video_format), + field_trials_); if (!encoder_) { RTC_LOG(LS_ERROR) << "CreateVideoEncoder failed, failing encoder format: " << encoder_config_.video_format.ToString();