diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn index 1f0892b8ae..bd90474689 100644 --- a/rtc_tools/BUILD.gn +++ b/rtc_tools/BUILD.gn @@ -210,11 +210,13 @@ if (!is_component_build) { "../api/task_queue:default_task_queue_factory", "../api/test/video:function_video_factory", "../api/transport:field_trial_based_config", + "../api/video:video_frame", "../api/video_codecs:video_codecs_api", "../call", "../call:call_interfaces", "../common_video", "../media:rtc_internal_video_codecs", + "../modules/video_coding:video_coding_utility", "../rtc_base:checks", "../rtc_base:rtc_json", "../rtc_base:stringutils", diff --git a/rtc_tools/DEPS b/rtc_tools/DEPS index 5ccd86b63b..3cf6080c93 100644 --- a/rtc_tools/DEPS +++ b/rtc_tools/DEPS @@ -29,4 +29,7 @@ specific_include_rules = { "+modules/video_coding/utility/ivf_file_writer.h", "+modules/video_coding/codecs/h264/include/h264.h", ], + ".*video_replay\.cc": [ + "+modules/video_coding/utility/ivf_file_writer.h", + ], } diff --git a/rtc_tools/video_replay.cc b/rtc_tools/video_replay.cc index d17a57e826..1faad28ff1 100644 --- a/rtc_tools/video_replay.cc +++ b/rtc_tools/video_replay.cc @@ -20,10 +20,12 @@ #include "api/task_queue/default_task_queue_factory.h" #include "api/test/video/function_video_decoder_factory.h" #include "api/transport/field_trial_based_config.h" +#include "api/video/video_codec_type.h" #include "api/video_codecs/video_decoder.h" #include "call/call.h" #include "common_video/libyuv/include/webrtc_libyuv.h" #include "media/engine/internal_decoder_factory.h" +#include "modules/video_coding/utility/ivf_file_writer.h" #include "rtc_base/checks.h" #include "rtc_base/string_to_number.h" #include "rtc_base/strings/json.h" @@ -112,6 +114,8 @@ ABSL_FLAG(std::string, "", "Decoder bitstream output file"); +ABSL_FLAG(std::string, decoder_ivf_filename, "", "Decoder ivf output file"); + // Flag for video codec. ABSL_FLAG(std::string, codec, "VP8", "Video codec"); @@ -189,6 +193,10 @@ static std::string DecoderBitstreamFilename() { return absl::GetFlag(FLAGS_decoder_bitstream_filename); } +static std::string IVFFilename() { + return absl::GetFlag(FLAGS_decoder_ivf_filename); +} + static std::string Codec() { return absl::GetFlag(FLAGS_codec); } @@ -255,6 +263,39 @@ class DecoderBitstreamFileWriter : public test::FakeDecoder { FILE* file_; }; +class DecoderIvfFileWriter : public test::FakeDecoder { + public: + explicit DecoderIvfFileWriter(const char* filename, const std::string& codec) + : file_writer_( + IvfFileWriter::Wrap(FileWrapper::OpenWriteOnly(filename), 0)) { + RTC_DCHECK(file_writer_.get()); + if (codec == "VP8") { + video_codec_type_ = VideoCodecType::kVideoCodecVP8; + } else if (codec == "VP9") { + video_codec_type_ = VideoCodecType::kVideoCodecVP9; + } else if (codec == "H264") { + video_codec_type_ = VideoCodecType::kVideoCodecH264; + } else { + RTC_LOG(LS_ERROR) << "Unsupported video codec " << codec; + RTC_DCHECK(false); + } + } + ~DecoderIvfFileWriter() override { file_writer_->Close(); } + + int32_t Decode(const EncodedImage& encoded_frame, + bool /* missing_frames */, + int64_t render_time_ms) override { + if (!file_writer_->WriteFrame(encoded_frame, video_codec_type_)) { + return WEBRTC_VIDEO_CODEC_ERROR; + } + return WEBRTC_VIDEO_CODEC_OK; + } + + private: + std::unique_ptr file_writer_; + VideoCodecType video_codec_type_; +}; + // The RtpReplayer is responsible for parsing the configuration provided by the // user, setting up the windows, recieve streams and decoders and then replaying // the provided RTP dump. @@ -419,10 +460,7 @@ class RtpReplayer final { // Setup the receiving stream VideoReceiveStream::Decoder decoder; decoder = test::CreateMatchingDecoder(MediaPayloadType(), Codec()); - if (DecoderBitstreamFilename().empty()) { - stream_state->decoder_factory = - std::make_unique(); - } else { + if (!DecoderBitstreamFilename().empty()) { // Replace decoder with file writer if we're writing the bitstream to a // file instead. stream_state->decoder_factory = @@ -430,6 +468,17 @@ class RtpReplayer final { return std::make_unique( DecoderBitstreamFilename().c_str()); }); + } else if (!IVFFilename().empty()) { + // Replace decoder with file writer if we're writing the ivf to a + // file instead. + stream_state->decoder_factory = + std::make_unique([]() { + return std::make_unique(IVFFilename().c_str(), + Codec()); + }); + } else { + stream_state->decoder_factory = + std::make_unique(); } receive_config.decoder_factory = stream_state->decoder_factory.get(); receive_config.decoders.push_back(decoder);