webm2pes: Restore frame fragmentation support.
Cap packet payload at 32 kB. Update test to expect non-zero packet length. Change-Id: Ibb68a4ef8d32c049c492ae546c631ef6376e3ffd
This commit is contained in:
		@@ -72,7 +72,11 @@ TEST_F(Webm2PesTests, CanParseFirstPacket) {
 | 
			
		||||
  ASSERT_TRUE(parser()->ParseNextPacket(&header, &frame));
 | 
			
		||||
  EXPECT_TRUE(VerifyPacketStartCode(header));
 | 
			
		||||
 | 
			
		||||
  const std::size_t kPesPayloadLength = 0;
 | 
			
		||||
  //   9 bytes: PES optional header
 | 
			
		||||
  //  10 bytes: BCMV Header
 | 
			
		||||
  //  83 bytes: frame
 | 
			
		||||
  // 102 bytes total in packet length field:
 | 
			
		||||
  const std::size_t kPesPayloadLength = 102;
 | 
			
		||||
  EXPECT_EQ(kPesPayloadLength, header.packet_length);
 | 
			
		||||
 | 
			
		||||
  EXPECT_GE(header.stream_id, kMinVideoStreamId);
 | 
			
		||||
 
 | 
			
		||||
@@ -172,10 +172,18 @@ bool PesHeader::Write(bool write_pts, PacketDataBuffer* buffer) const {
 | 
			
		||||
  for (int i = 0; i < kStartCodeLength; ++i)
 | 
			
		||||
    buffer->push_back(start_code[i]);
 | 
			
		||||
 | 
			
		||||
  // Write |packet_length| as big endian.
 | 
			
		||||
  std::uint8_t byte = (packet_length >> 8) & 0xff;
 | 
			
		||||
  // The length field here reports number of bytes following the field. The
 | 
			
		||||
  // length of the optional header must be added to the payload length set by
 | 
			
		||||
  // the user.
 | 
			
		||||
  const std::size_t header_length =
 | 
			
		||||
      packet_length + optional_header.size_in_bytes();
 | 
			
		||||
  if (header_length > UINT16_MAX)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  // Write |header_length| as big endian.
 | 
			
		||||
  std::uint8_t byte = (header_length >> 8) & 0xff;
 | 
			
		||||
  buffer->push_back(byte);
 | 
			
		||||
  byte = packet_length & 0xff;
 | 
			
		||||
  byte = header_length & 0xff;
 | 
			
		||||
  buffer->push_back(byte);
 | 
			
		||||
 | 
			
		||||
  // Write the (not really) optional header.
 | 
			
		||||
@@ -341,9 +349,6 @@ bool Webm2Pes::InitWebmParser() {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Store timecode scale.
 | 
			
		||||
  timecode_scale_ = webm_parser_->GetInfo()->GetTimeCodeScale();
 | 
			
		||||
 | 
			
		||||
  // Make sure there's a video track.
 | 
			
		||||
  const mkvparser::Tracks* tracks = webm_parser_->GetTracks();
 | 
			
		||||
  if (tracks == nullptr) {
 | 
			
		||||
@@ -351,6 +356,9 @@ bool Webm2Pes::InitWebmParser() {
 | 
			
		||||
                 input_file_name_.c_str());
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  timecode_scale_ = webm_parser_->GetInfo()->GetTimeCodeScale();
 | 
			
		||||
 | 
			
		||||
  for (int track_index = 0;
 | 
			
		||||
       track_index < static_cast<int>(tracks->GetTracksCount());
 | 
			
		||||
       ++track_index) {
 | 
			
		||||
@@ -418,14 +426,18 @@ bool Webm2Pes::WritePesPacket(const mkvparser::Block::Frame& vpx_frame,
 | 
			
		||||
 | 
			
		||||
  packet_data_.clear();
 | 
			
		||||
 | 
			
		||||
  bool write_pts = true;
 | 
			
		||||
  for (const Range& packet_payload_range : frame_ranges) {
 | 
			
		||||
    header.packet_length = 0;
 | 
			
		||||
    if (header.Write(write_pts, &packet_data_) != true) {
 | 
			
		||||
    std::size_t extra_bytes = 0;
 | 
			
		||||
    if (packet_payload_range.length > kMaxPayloadSize) {
 | 
			
		||||
      extra_bytes = packet_payload_range.length - kMaxPayloadSize;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // First packet of new frame. Always include PTS and BCMV header.
 | 
			
		||||
    header.packet_length = packet_payload_range.length + BCMVHeader::size();
 | 
			
		||||
    if (header.Write(true, &packet_data_) != true) {
 | 
			
		||||
      std::fprintf(stderr, "Webm2Pes: packet header write failed.\n");
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    write_pts = false;
 | 
			
		||||
 | 
			
		||||
    BCMVHeader bcmv_header(static_cast<uint32_t>(packet_payload_range.length));
 | 
			
		||||
    if (bcmv_header.Write(&packet_data_) != true) {
 | 
			
		||||
@@ -437,25 +449,56 @@ bool Webm2Pes::WritePesPacket(const mkvparser::Block::Frame& vpx_frame,
 | 
			
		||||
    const std::uint8_t* payload_start =
 | 
			
		||||
        frame_data.get() + packet_payload_range.offset;
 | 
			
		||||
 | 
			
		||||
    if (CopyAndEscapeStartCodes(payload_start,
 | 
			
		||||
                                static_cast<int>(packet_payload_range.length),
 | 
			
		||||
                                &packet_data_) == false) {
 | 
			
		||||
    const std::size_t bytes_to_copy = packet_payload_range.length - extra_bytes;
 | 
			
		||||
    if (CopyAndEscapeStartCodes(payload_start, bytes_to_copy, &packet_data_) ==
 | 
			
		||||
        false) {
 | 
			
		||||
      fprintf(stderr, "Webm2Pes: Payload write failed.\n");
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    printf("---wrote payload size=%u\n",
 | 
			
		||||
           static_cast<unsigned int>(bytes_to_copy));
 | 
			
		||||
 | 
			
		||||
    std::size_t bytes_copied = bytes_to_copy;
 | 
			
		||||
    while (extra_bytes) {
 | 
			
		||||
      // Write PES packets for the remaining data, but omit the PTS and BCMV
 | 
			
		||||
      // header.
 | 
			
		||||
      const std::size_t extra_bytes_to_copy =
 | 
			
		||||
          std::min(kMaxPayloadSize, extra_bytes);
 | 
			
		||||
      extra_bytes -= extra_bytes_to_copy;
 | 
			
		||||
      header.packet_length = extra_bytes_to_copy;
 | 
			
		||||
      if (header.Write(false, &packet_data_) != true) {
 | 
			
		||||
        fprintf(stderr, "Webm2pes: fragment write failed.\n");
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      payload_start += bytes_copied;
 | 
			
		||||
      if (CopyAndEscapeStartCodes(payload_start, extra_bytes_to_copy,
 | 
			
		||||
                                  &packet_data_) == false) {
 | 
			
		||||
        fprintf(stderr, "Webm2Pes: Payload write failed.\n");
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      bytes_copied += extra_bytes_to_copy;
 | 
			
		||||
 | 
			
		||||
      // TODO: DEBUG/REMOVE
 | 
			
		||||
      printf("----wrote fragment total=%u this_fragment=%u\n",
 | 
			
		||||
             static_cast<unsigned int>(bytes_copied),
 | 
			
		||||
             static_cast<unsigned int>(extra_bytes_to_copy));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool CopyAndEscapeStartCodes(const std::uint8_t* raw_input,
 | 
			
		||||
                             int raw_input_length,
 | 
			
		||||
                             std::size_t raw_input_length,
 | 
			
		||||
                             PacketDataBuffer* packet_buffer) {
 | 
			
		||||
  if (raw_input == nullptr || raw_input_length < 1 || packet_buffer == nullptr)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  int num_zeros = 0;
 | 
			
		||||
  for (int i = 0; i < raw_input_length; ++i) {
 | 
			
		||||
  for (std::size_t i = 0; i < raw_input_length; ++i) {
 | 
			
		||||
    const uint8_t byte = raw_input[i];
 | 
			
		||||
 | 
			
		||||
    if (byte == 0) {
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,30 @@
 | 
			
		||||
#include "mkvparser/mkvparser.h"
 | 
			
		||||
#include "mkvparser/mkvreader.h"
 | 
			
		||||
 | 
			
		||||
// Webm2pes
 | 
			
		||||
//
 | 
			
		||||
// Webm2pes consumes a WebM file containing a VP8 or VP9 video stream and
 | 
			
		||||
// outputs a PES stream suitable for inclusion in a MPEG2 Transport Stream.
 | 
			
		||||
//
 | 
			
		||||
// In the simplest case the PES stream output by Webm2pes consists of a sequence
 | 
			
		||||
// of PES packets with the following structure:
 | 
			
		||||
// | PES Header w/PTS | BCMV Header | Payload (VPx frame) |
 | 
			
		||||
//
 | 
			
		||||
// More typically the output will look like the following due to the PES
 | 
			
		||||
// payload size limitations caused by the format of the PES header.
 | 
			
		||||
// The PES header contains only 2 bytes of storage for expressing payload size.
 | 
			
		||||
// VPx PES streams containing fragmented packets look like this:
 | 
			
		||||
//
 | 
			
		||||
// | PH PTS | BCMV | Payload fragment 1 | PH | Payload fragment 2 | ...
 | 
			
		||||
//
 | 
			
		||||
//   PH = PES Header
 | 
			
		||||
//   PH PTS = PES Header with PTS
 | 
			
		||||
//   BCMV = BCMV Header
 | 
			
		||||
//
 | 
			
		||||
// Note that start codes are properly escaped by Webm2pes, and start code
 | 
			
		||||
// emulation prevention bytes must be stripped from the output stream before
 | 
			
		||||
// it can be parsed.
 | 
			
		||||
 | 
			
		||||
namespace libwebm {
 | 
			
		||||
 | 
			
		||||
// Stores a value and its size in bits for writing into a PES Optional Header.
 | 
			
		||||
@@ -149,7 +173,7 @@ struct PesHeader {
 | 
			
		||||
  std::uint16_t packet_length = 0;  // Number of bytes _after_ this field.
 | 
			
		||||
  PesOptionalHeader optional_header;
 | 
			
		||||
  std::size_t size() const {
 | 
			
		||||
    return optional_header.size_in_bytes() + BCMVHeader::size() +
 | 
			
		||||
    return optional_header.size_in_bytes() +
 | 
			
		||||
           6 /* start_code + packet_length */ + packet_length;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -172,6 +196,7 @@ class PacketReceiverInterface {
 | 
			
		||||
class Webm2Pes {
 | 
			
		||||
 public:
 | 
			
		||||
  enum VideoCodec { VP8, VP9 };
 | 
			
		||||
  const std::size_t kMaxPayloadSize = 32768;
 | 
			
		||||
 | 
			
		||||
  Webm2Pes(const std::string& input_file, const std::string& output_file)
 | 
			
		||||
      : input_file_name_(input_file), output_file_name_(output_file) {}
 | 
			
		||||
@@ -188,9 +213,10 @@ class Webm2Pes {
 | 
			
		||||
  bool ConvertToFile();
 | 
			
		||||
 | 
			
		||||
  // Converts the VPx video stream to a sequence of PES packets, and calls the
 | 
			
		||||
  // PacketReceiverInterface::ReceivePacket() once for each PES packet. Returns
 | 
			
		||||
  // only after full conversion or error. Returns true for success, and false
 | 
			
		||||
  // when an error occurs.
 | 
			
		||||
  // PacketReceiverInterface::ReceivePacket() once for each VPx frame. The
 | 
			
		||||
  // packet sent to the receiver may contain multiple PES packets. Returns only
 | 
			
		||||
  // after full conversion or error. Returns true for success, and false when
 | 
			
		||||
  // an error occurs.
 | 
			
		||||
  bool ConvertToPacketReceiver();
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
@@ -232,7 +258,7 @@ class Webm2Pes {
 | 
			
		||||
//    0x00 0x00 0x01  =>  0x00 0x00 0x03 0x01
 | 
			
		||||
//    0x00 0x00 0x03  =>  0x00 0x00 0x03 0x03
 | 
			
		||||
bool CopyAndEscapeStartCodes(const std::uint8_t* raw_input,
 | 
			
		||||
                             int raw_input_length,
 | 
			
		||||
                             std::size_t raw_input_length,
 | 
			
		||||
                             PacketDataBuffer* packet_buffer);
 | 
			
		||||
}  // namespace libwebm
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user