diff --git a/webrtc/test/fake_decoder.h b/webrtc/test/fake_decoder.h index 5a03825b0d..2031c676bf 100644 --- a/webrtc/test/fake_decoder.h +++ b/webrtc/test/fake_decoder.h @@ -24,20 +24,20 @@ class FakeDecoder : public VideoDecoder { FakeDecoder(); virtual ~FakeDecoder() {} - virtual int32_t InitDecode(const VideoCodec* config, - int32_t number_of_cores) OVERRIDE; + int32_t InitDecode(const VideoCodec* config, + int32_t number_of_cores) override; - virtual int32_t Decode(const EncodedImage& input, - bool missing_frames, - const RTPFragmentationHeader* fragmentation, - const CodecSpecificInfo* codec_specific_info, - int64_t render_time_ms) OVERRIDE; + int32_t Decode(const EncodedImage& input, + bool missing_frames, + const RTPFragmentationHeader* fragmentation, + const CodecSpecificInfo* codec_specific_info, + int64_t render_time_ms) override; - virtual int32_t RegisterDecodeCompleteCallback( - DecodedImageCallback* callback) OVERRIDE; + int32_t RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) override; - virtual int32_t Release() OVERRIDE; - virtual int32_t Reset() OVERRIDE; + int32_t Release() override; + int32_t Reset() override; private: VideoCodec config_; @@ -49,11 +49,24 @@ class FakeH264Decoder : public FakeDecoder { public: virtual ~FakeH264Decoder() {} - virtual int32_t Decode(const EncodedImage& input, - bool missing_frames, - const RTPFragmentationHeader* fragmentation, - const CodecSpecificInfo* codec_specific_info, - int64_t render_time_ms) OVERRIDE; + int32_t Decode(const EncodedImage& input, + bool missing_frames, + const RTPFragmentationHeader* fragmentation, + const CodecSpecificInfo* codec_specific_info, + int64_t render_time_ms) override; +}; + +class FakeNullDecoder : public FakeDecoder { + public: + virtual ~FakeNullDecoder() {} + + int32_t Decode(const EncodedImage& input, + bool missing_frames, + const RTPFragmentationHeader* fragmentation, + const CodecSpecificInfo* codec_specific_info, + int64_t render_time_ms) override { + return 0; + } }; } // namespace test } // namespace webrtc diff --git a/webrtc/test/rtp_file_reader.cc b/webrtc/test/rtp_file_reader.cc index c5d245b115..34290ac8d6 100644 --- a/webrtc/test/rtp_file_reader.cc +++ b/webrtc/test/rtp_file_reader.cc @@ -43,11 +43,78 @@ static uint16_t kPacketHeaderSize = 8; } \ } while (0) +bool ReadUint32(uint32_t* out, FILE* file) { + *out = 0; + for (size_t i = 0; i < 4; ++i) { + *out <<= 8; + uint8_t tmp; + if (fread(&tmp, 1, sizeof(uint8_t), file) != sizeof(uint8_t)) + return false; + *out |= tmp; + } + return true; +} + +bool ReadUint16(uint16_t* out, FILE* file) { + *out = 0; + for (size_t i = 0; i < 2; ++i) { + *out <<= 8; + uint8_t tmp; + if (fread(&tmp, 1, sizeof(uint8_t), file) != sizeof(uint8_t)) + return false; + *out |= tmp; + } + return true; +} + class RtpFileReaderImpl : public RtpFileReader { public: virtual bool Init(const std::string& filename) = 0; }; +class InterleavedRtpFileReader : public RtpFileReaderImpl { + public: + virtual ~InterleavedRtpFileReader() { + if (file_ != NULL) { + fclose(file_); + file_ = NULL; + } + } + + virtual bool Init(const std::string& filename) { + file_ = fopen(filename.c_str(), "rb"); + if (file_ == NULL) { + printf("ERROR: Can't open file: %s\n", filename.c_str()); + return false; + } + return true; + } + virtual bool NextPacket(RtpPacket* packet) { + assert(file_ != NULL); + packet->length = RtpPacket::kMaxPacketBufferSize; + uint32_t len = 0; + TRY(ReadUint32(&len, file_)); + if (packet->length < len) { + FATAL() << "Packet is too large to fit: " << len << " bytes vs " + << packet->length + << " bytes allocated. Consider increasing the buffer " + "size"; + } + if (fread(packet->data, 1, len, file_) != len) + return false; + + packet->length = len; + packet->original_length = len; + packet->time_ms = time_ms_; + time_ms_ += 5; + return true; + } + + private: + FILE* file_ = NULL; + int64_t time_ms_ = 0; +}; + // Read RTP packets from file in rtpdump format, as documented at: // http://www.cs.columbia.edu/irt/software/rtptools/ class RtpDumpReader : public RtpFileReaderImpl { @@ -92,11 +159,11 @@ class RtpDumpReader : public RtpFileReaderImpl { uint32_t source; uint16_t port; uint16_t padding; - TRY(ReadUint32(&start_sec)); - TRY(ReadUint32(&start_usec)); - TRY(ReadUint32(&source)); - TRY(ReadUint16(&port)); - TRY(ReadUint16(&padding)); + TRY(ReadUint32(&start_sec, file_)); + TRY(ReadUint32(&start_usec, file_)); + TRY(ReadUint32(&source, file_)); + TRY(ReadUint16(&port, file_)); + TRY(ReadUint16(&padding, file_)); return true; } @@ -108,9 +175,9 @@ class RtpDumpReader : public RtpFileReaderImpl { uint16_t len; uint16_t plen; uint32_t offset; - TRY(ReadUint16(&len)); - TRY(ReadUint16(&plen)); - TRY(ReadUint32(&offset)); + TRY(ReadUint16(&len, file_)); + TRY(ReadUint16(&plen, file_)); + TRY(ReadUint32(&offset, file_)); // Use 'len' here because a 'plen' of 0 specifies rtcp. len -= kPacketHeaderSize; @@ -131,30 +198,6 @@ class RtpDumpReader : public RtpFileReaderImpl { } private: - bool ReadUint32(uint32_t* out) { - *out = 0; - for (size_t i = 0; i < 4; ++i) { - *out <<= 8; - uint8_t tmp; - if (fread(&tmp, 1, sizeof(uint8_t), file_) != sizeof(uint8_t)) - return false; - *out |= tmp; - } - return true; - } - - bool ReadUint16(uint16_t* out) { - *out = 0; - for (size_t i = 0; i < 2; ++i) { - *out <<= 8; - uint8_t tmp; - if (fread(&tmp, 1, sizeof(uint8_t), file_) != sizeof(uint8_t)) - return false; - *out |= tmp; - } - return true; - } - FILE* file_; DISALLOW_COPY_AND_ASSIGN(RtpDumpReader); @@ -598,6 +641,9 @@ RtpFileReader* RtpFileReader::Create(FileFormat format, case kRtpDump: reader = new RtpDumpReader(); break; + case kLengthPacketInterleaved: + reader = new InterleavedRtpFileReader(); + break; } if (!reader->Init(filename)) { delete reader; diff --git a/webrtc/test/rtp_file_reader.h b/webrtc/test/rtp_file_reader.h index f309380fbf..c302d4fbde 100644 --- a/webrtc/test/rtp_file_reader.h +++ b/webrtc/test/rtp_file_reader.h @@ -32,10 +32,7 @@ struct RtpPacket { class RtpFileReader { public: - enum FileFormat { - kPcap, - kRtpDump, - }; + enum FileFormat { kPcap, kRtpDump, kLengthPacketInterleaved }; virtual ~RtpFileReader() {} static RtpFileReader* Create(FileFormat format, diff --git a/webrtc/video/replay.cc b/webrtc/video/replay.cc index 2f0fa01e31..8e9ba81ded 100644 --- a/webrtc/video/replay.cc +++ b/webrtc/video/replay.cc @@ -24,6 +24,7 @@ #include "webrtc/system_wrappers/interface/sleep.h" #include "webrtc/test/encoder_settings.h" #include "webrtc/test/null_transport.h" +#include "webrtc/test/fake_decoder.h" #include "webrtc/test/rtp_file_reader.h" #include "webrtc/test/run_loop.h" #include "webrtc/test/run_test.h" @@ -106,6 +107,7 @@ bool ValidateInputFilenameNotEmpty(const char* flagname, const std::string& string) { return string != ""; } + DEFINE_string(input_file, "", "input file"); static std::string InputFile() { return static_cast(FLAGS_input_file); @@ -120,6 +122,11 @@ static std::string OutBase() { return static_cast(FLAGS_out_base); } +DEFINE_string(decoder_bitstream_filename, "", "Decoder bitstream output file"); +static std::string DecoderBitstreamFilename() { + return static_cast(FLAGS_decoder_bitstream_filename); +} + // Flag for video codec. DEFINE_string(codec, "VP8", "Video codec"); static std::string Codec() { return static_cast(FLAGS_codec); } @@ -184,6 +191,22 @@ class FileRenderPassthrough : public VideoRenderer { int last_height_; }; +class DecoderBitstreamFileWriter : public EncodedFrameObserver { + public: + explicit DecoderBitstreamFileWriter(const char* filename) + : file_(fopen(filename, "wb")) { + assert(file_ != NULL); + } + ~DecoderBitstreamFileWriter() { fclose(file_); } + + virtual void EncodedFrameCallback(const EncodedFrame& encoded_frame) { + fwrite(encoded_frame.data_, 1, encoded_frame.length_, file_); + } + + private: + FILE* file_; +}; + void RtpReplay() { rtc::scoped_ptr playback_video( test::VideoRenderer::Create("Playback Video", 640, 480)); @@ -215,8 +238,20 @@ void RtpReplay() { VideoSendStream::Config::EncoderSettings encoder_settings; encoder_settings.payload_name = flags::Codec(); encoder_settings.payload_type = flags::PayloadType(); - VideoReceiveStream::Decoder decoder = - test::CreateMatchingDecoder(encoder_settings); + VideoReceiveStream::Decoder decoder; + rtc::scoped_ptr bitstream_writer; + if (flags::DecoderBitstreamFilename() != "") { + bitstream_writer.reset(new DecoderBitstreamFileWriter( + flags::DecoderBitstreamFilename().c_str())); + receive_config.pre_decode_callback = bitstream_writer.get(); + } + decoder = test::CreateMatchingDecoder(encoder_settings); + if (flags::DecoderBitstreamFilename() != "") { + // Replace with a null decoder if we're writing the bitstream to a file + // instead. + delete decoder.decoder; + decoder.decoder = new test::FakeNullDecoder(); + } receive_config.decoders.push_back(decoder); VideoReceiveStream* receive_stream = @@ -230,8 +265,15 @@ void RtpReplay() { if (rtp_reader.get() == NULL) { fprintf(stderr, "Couldn't open input file as either a rtpdump or .pcap. Note " - "that .pcapng is not supported.\n"); - return; + "that .pcapng is not supported.\nTrying to interpret the file as " + "length/packet interleaved.\n"); + rtp_reader.reset(test::RtpFileReader::Create( + test::RtpFileReader::kLengthPacketInterleaved, flags::InputFile())); + if (rtp_reader.get() == NULL) { + fprintf(stderr, + "Unable to open input file with any supported format\n"); + return; + } } } receive_stream->Start();