Add support for writing h264 decoder input to file and parsing interleaved length/packet RTP dumps.

This is useful for debugging h264 input when we don't have an h264 decoder, as the resulting file should be possible to play back using mplayer. It is also often convenient to dump rtp packets in an interleaved format where the size of a packet is inserted before the actual payload.

R=pbos@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/42139004

Cr-Commit-Position: refs/heads/master@{#8558}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8558 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/test/fake_decoder.h b/webrtc/test/fake_decoder.h
index 5a03825..2031c67 100644
--- a/webrtc/test/fake_decoder.h
+++ b/webrtc/test/fake_decoder.h
@@ -24,20 +24,20 @@
   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 @@
  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 c5d245b..34290ac 100644
--- a/webrtc/test/rtp_file_reader.cc
+++ b/webrtc/test/rtp_file_reader.cc
@@ -43,11 +43,78 @@
     }                                                  \
   } 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 @@
     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 @@
     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 @@
   }
 
  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 @@
     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 f309380..c302d4f 100644
--- a/webrtc/test/rtp_file_reader.h
+++ b/webrtc/test/rtp_file_reader.h
@@ -32,10 +32,7 @@
 
 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 2f0fa01..8e9ba81 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 @@
                                    const std::string& string) {
   return string != "";
 }
+
 DEFINE_string(input_file, "", "input file");
 static std::string InputFile() {
   return static_cast<std::string>(FLAGS_input_file);
@@ -120,6 +122,11 @@
   return static_cast<std::string>(FLAGS_out_base);
 }
 
+DEFINE_string(decoder_bitstream_filename, "", "Decoder bitstream output file");
+static std::string DecoderBitstreamFilename() {
+  return static_cast<std::string>(FLAGS_decoder_bitstream_filename);
+}
+
 // Flag for video codec.
 DEFINE_string(codec, "VP8", "Video codec");
 static std::string Codec() { return static_cast<std::string>(FLAGS_codec); }
@@ -184,6 +191,22 @@
   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<test::VideoRenderer> playback_video(
       test::VideoRenderer::Create("Playback Video", 640, 480));
@@ -215,8 +238,20 @@
   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<DecoderBitstreamFileWriter> 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 @@
     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();