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:
parent
f8bb7149f5
commit
448af971d2
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user