webm2ts: Converts WebM VPx video to a MPEG TS.

Built atop webm2pes, takes the output from webm2pes and packetizes it
into MPEG TS.

Change-Id: Ia122479ee91a112ad7fe223a571ca6d7ba66d406
This commit is contained in:
Tom Finegan 2016-01-22 11:24:55 -08:00
parent 453bf44d32
commit bb8cefd516
6 changed files with 451 additions and 58 deletions

View File

@ -11,6 +11,13 @@ include("${CMAKE_CURRENT_SOURCE_DIR}/build/msvc_runtime.cmake")
set(LIBWEBM_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
# Turn up the warning levels.
if(MSVC)
set(CMAKE_CXX_FLAGS "/W4 ${CMAKE_CXX_FLAGS}")
else ()
set(CMAKE_CXX_FLAGS "-Wall -Wextra ${CMAKE_CXX_FLAGS}")
endif()
# Test configuration flags. Defined here for visibility.
option(ENABLE_TESTS "Enables tests." OFF)
set(GTEST_SRC_DIR "${LIBWEBM_SRC_DIR}/../googletest" CACHE PATH
@ -74,7 +81,17 @@ add_executable(webm2pes
"${LIBWEBM_SRC_DIR}/webm2pes_main.cc")
target_link_libraries(webm2pes LINK_PUBLIC webm)
# tests section.
# webm2ts section.
add_executable(webm2ts
"${LIBWEBM_SRC_DIR}/common/libwebm_utils.cc"
"${LIBWEBM_SRC_DIR}/common/libwebm_utils.h"
"${LIBWEBM_SRC_DIR}/vpxpes2ts.cc"
"${LIBWEBM_SRC_DIR}/vpxpes2ts.h"
"${LIBWEBM_SRC_DIR}/webm2pes.cc"
"${LIBWEBM_SRC_DIR}/webm2pes.h"
"${LIBWEBM_SRC_DIR}/vpxpes2ts_main.cc")
target_link_libraries(webm2ts LINK_PUBLIC webm)
if (ENABLE_TESTS)
add_subdirectory("${GTEST_SRC_DIR}" "${GTEST_BUILD_DIR}")
include_directories("${GTEST_SRC_DIR}/googletest/include")
@ -101,3 +118,4 @@ if (ENABLE_TESTS)
"${LIBWEBM_SRC_DIR}/testing/webm2pes_tests.cc")
target_link_libraries(webm2pes_tests LINK_PUBLIC webm gtest)
endif (ENABLE_TESTS)

217
vpxpes2ts.cc Normal file
View File

@ -0,0 +1,217 @@
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#include "vpxpes2ts.h"
#include <algorithm>
#include <cstdio>
namespace libwebm {
// TODO(tomfinegan): Dedupe this and PesHeaderField.
// Stores a value and its size in bits for writing into a MPEG2 TS Header.
// Maximum size is 64 bits. Users may call the Check() method to perform minimal
// validation (size > 0 and <= 64).
struct TsHeaderField {
TsHeaderField(std::uint64_t value,
std::uint32_t size_in_bits,
std::uint8_t byte_index,
std::uint8_t bits_to_shift)
: bits(value),
num_bits(size_in_bits),
index(byte_index),
shift(bits_to_shift) {}
TsHeaderField() = delete;
TsHeaderField(const TsHeaderField&) = default;
TsHeaderField(TsHeaderField&&) = default;
~TsHeaderField() = default;
bool Check() const {
return num_bits > 0 && num_bits <= 64 && shift >= 0 && shift < 64;
}
// Value to be stored in the field.
std::uint64_t bits;
// Number of bits in the value.
const std::uint32_t num_bits;
// Index into the header for the byte in which |bits| will be written.
const std::uint8_t index;
// Number of bits to left shift value before or'ing. Ignored for whole bytes.
const std::uint8_t shift;
};
// Data storage for MPEG2 Transport Stream headers.
// https://en.wikipedia.org/wiki/MPEG_transport_stream#Packet
struct TsHeader {
TsHeader(bool payload_start, bool adaptation_flag, std::uint8_t counter)
: is_payload_start(payload_start),
has_adaptation(adaptation_flag),
counter_value(counter) {}
TsHeader() = delete;
TsHeader(const TsHeader&) = default;
TsHeader(TsHeader&&) = default;
~TsHeader() = default;
void Write(PacketDataBuffer* buffer) const;
// Indicates the packet is the beginning of a new fragmented payload.
const bool is_payload_start;
// Indicates the packet contains an adaptation field.
const bool has_adaptation;
// The sync byte is the bit pattern of 0x47 (ASCII char 'G').
const std::uint8_t kTsHeaderSyncByte = 0x47;
const std::uint8_t sync_byte = kTsHeaderSyncByte;
// Value for |continuity_counter|. Used to detect gaps when demuxing.
const std::uint8_t counter_value;
// Set when FEC is impossible. Always 0.
const TsHeaderField transport_error_indicator = TsHeaderField(0, 1, 1, 7);
// This MPEG2 TS header is the start of a new payload (aka PES packet).
const TsHeaderField payload_unit_start_indicator =
TsHeaderField(is_payload_start ? 1 : 0, 1, 1, 6);
// Set when the current packet has a higher priority than other packets with
// the same PID. Always 0 for VPX.
const TsHeaderField transport_priority = TsHeaderField(0, 1, 1, 5);
// https://en.wikipedia.org/wiki/MPEG_transport_stream#Packet_Identifier_.28PID.29
// 0x0020-0x1FFA May be assigned as needed to Program Map Tables, elementary
// streams and other data tables.
// Note: Though we hard code to 0x20, this value is actually 13 bits-- the
// buffer for the header is always set to 0, so it doesn't matter in practice.
const TsHeaderField pid = TsHeaderField(0x20, 8, 2, 0);
// Indicates scrambling key. Unused; always 0.
const TsHeaderField scrambling_control = TsHeaderField(0, 2, 3, 6);
// Adaptation field flag. Unused; always 0.
// TODO(tomfinegan): Not sure this is OK. Might need to add support for
// writing the Adaptation Field.
const TsHeaderField adaptation_field_flag =
TsHeaderField(has_adaptation ? 1 : 0, 1, 3, 5);
// Payload flag. All output packets created here have payloads. Always 1.
const TsHeaderField payload_flag = TsHeaderField(1, 1, 3, 4);
// Continuity counter. Two bit field that is incremented for every packet.
const TsHeaderField continuity_counter =
TsHeaderField(counter_value, 4, 3, 3);
};
void TsHeader::Write(PacketDataBuffer* buffer) const {
std::uint8_t* byte = &(*buffer)[0];
*byte = sync_byte;
*++byte = 0;
*byte |= transport_error_indicator.bits << transport_error_indicator.shift;
*byte |= payload_unit_start_indicator.bits
<< payload_unit_start_indicator.shift;
*byte |= transport_priority.bits << transport_priority.shift;
*++byte = pid.bits;
*++byte = 0;
*byte |= scrambling_control.bits << scrambling_control.shift;
*byte |= adaptation_field_flag.bits << adaptation_field_flag.shift;
*byte |= payload_flag.bits << payload_flag.shift;
*byte |= continuity_counter.bits; // last 4 bits.
}
bool VpxPes2Ts::ConvertToFile() {
output_file_ = FilePtr(fopen(output_file_name_.c_str(), "wb"), FILEDeleter());
if (output_file_ == nullptr) {
std::fprintf(stderr, "VpxPes2Ts: Cannot open %s for output.\n",
output_file_name_.c_str());
return false;
}
pes_converter_.reset(new Webm2Pes(input_file_name_, this));
if (pes_converter_ == nullptr) {
std::fprintf(stderr, "VpxPes2Ts: Out of memory.\n");
return false;
}
return pes_converter_->ConvertToPacketReceiver();
}
bool VpxPes2Ts::ReceivePacket(const PacketDataBuffer& packet_data) {
const int kTsHeaderSize = 4;
const int kTsPayloadSize = 184;
const int kTsPacketSize = kTsHeaderSize + kTsPayloadSize;
int bytes_to_packetize = packet_data.size();
std::uint8_t continuity_counter = 0;
std::size_t read_pos = 0;
ts_buffer_.reserve(kTsPacketSize);
while (bytes_to_packetize > 0) {
if (continuity_counter > 0xf)
continuity_counter = 0;
// Calculate payload size (need to know if we'll have to pad with an empty
// adaptation field).
int payload_size = std::min(bytes_to_packetize, kTsPayloadSize);
// Write the TS header.
const TsHeader header(
bytes_to_packetize == static_cast<int>(packet_data.size()),
payload_size != kTsPayloadSize, continuity_counter);
header.Write(&ts_buffer_);
std::size_t write_pos = kTsHeaderSize;
// (pre)Pad payload with an empty adaptation field. All packets must be
// |kTsPacketSize| (188).
if (payload_size < kTsPayloadSize) {
// We need at least 2 bytes to write an empty adaptation field.
if (payload_size == (kTsPayloadSize - 1)) {
payload_size--;
}
// Padding adaptation field:
// 8 bits: number of adaptation field bytes following this byte.
// 8 bits: unused (in this program) flags.
// This is followed by a run of 0xff to reach |kTsPayloadSize| (184)
// bytes.
const int pad_size = kTsPayloadSize - payload_size - 1 - 1;
ts_buffer_[write_pos++] = pad_size + 1;
ts_buffer_[write_pos++] = 0;
const std::uint8_t kStuffingByte = 0xff;
for (int i = 0; i < pad_size; ++i) {
ts_buffer_[write_pos++] = kStuffingByte;
}
}
for (int i = 0; i < payload_size; ++i) {
ts_buffer_[write_pos++] = packet_data[read_pos++];
}
bytes_to_packetize -= payload_size;
continuity_counter++;
if (write_pos != kTsPacketSize) {
fprintf(stderr, "VpxPes2Ts: Invalid packet length.\n");
return false;
}
// Write contents of |ts_buffer_| to |output_file_|.
// TODO(tomfinegan): Writing 188 bytes at a time isn't exactly efficient...
// Fix me.
if (std::fwrite(&ts_buffer_[0], 1, kTsPacketSize, output_file_.get()) !=
kTsPacketSize) {
std::fprintf(stderr, "VpxPes2Ts: TS packet write failed.\n");
return false;
}
}
return true;
}
} // namespace libwebm

48
vpxpes2ts.h Normal file
View File

@ -0,0 +1,48 @@
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#ifndef LIBWEBM_VPXPES2TS_H_
#define LIBWEBM_VPXPES2TS_H_
#include <cstdio>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include "common/libwebm_utils.h"
#include "webm2pes.h"
namespace libwebm {
class VpxPes2Ts : public PacketReceiverInterface {
public:
VpxPes2Ts(const std::string& input_file_name,
const std::string& output_file_name)
: input_file_name_(input_file_name),
output_file_name_(output_file_name) {}
virtual ~VpxPes2Ts() = default;
VpxPes2Ts() = delete;
VpxPes2Ts(const VpxPes2Ts&) = delete;
VpxPes2Ts(VpxPes2Ts&&) = delete;
bool ConvertToFile();
private:
bool ReceivePacket(const PacketDataBuffer& packet_data) override;
const std::string input_file_name_;
const std::string output_file_name_;
FilePtr output_file_;
std::unique_ptr<Webm2Pes> pes_converter_;
PacketDataBuffer ts_buffer_;
};
} // namespace libwebm
#endif // LIBWEBM_VPXPES2TS_H_

32
vpxpes2ts_main.cc Normal file
View File

@ -0,0 +1,32 @@
// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#include "vpxpes2ts.h"
#include <cstdio>
#include <string>
namespace {
void Usage(const char* argv[]) {
printf("Usage: %s <WebM file> <output file>", argv[0]);
}
} // namespace
int main(int argc, const char* argv[]) {
if (argc < 3) {
Usage(argv);
return EXIT_FAILURE;
}
const std::string input_path = argv[1];
const std::string output_path = argv[2];
libwebm::VpxPes2Ts converter(input_path, output_path);
return converter.ConvertToFile() == true ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -106,13 +106,14 @@ void PesOptionalHeader::SetPtsBits(std::int64_t pts_90khz) {
// Writes fields to |file| and returns true. Returns false when write or
// field value validation fails.
bool PesOptionalHeader::Write(std::FILE* file, bool write_pts) const {
if (file == nullptr) {
bool PesOptionalHeader::Write(bool write_pts, PacketDataBuffer* buffer) const {
if (buffer == nullptr) {
std::fprintf(stderr, "Webm2Pes: nullptr in opt header writer.\n");
return false;
}
std::uint8_t header[9] = {0};
const std::size_t kHeaderSize = 9;
std::uint8_t header[kHeaderSize] = {0};
std::uint8_t* byte = header;
if (marker.Check() != true || scrambling.Check() != true ||
@ -163,11 +164,8 @@ bool PesOptionalHeader::Write(std::FILE* file, bool write_pts) const {
for (int i = 0; i < num_stuffing_bytes; ++i)
*++byte = stuffing_byte.bits;
if (std::fwrite(reinterpret_cast<void*>(header), 1, size_in_bytes(), file) !=
size_in_bytes()) {
std::fprintf(stderr, "Webm2Pes: unable to write PES opt header to file.\n");
return false;
}
for (int i = 0; i < kHeaderSize; ++i)
buffer->push_back(header[i]);
return true;
}
@ -176,28 +174,26 @@ bool PesOptionalHeader::Write(std::FILE* file, bool write_pts) const {
// BCMVHeader methods.
//
bool BCMVHeader::Write(std::FILE* fileptr) const {
if (fileptr == nullptr) {
std::fprintf(stderr, "Webm2Pes: nullptr for file in BCMV Write.\n");
bool BCMVHeader::Write(PacketDataBuffer* buffer) const {
if (buffer == nullptr) {
std::fprintf(stderr, "Webm2Pes: nullptr for buffer in BCMV Write.\n");
return false;
}
if (std::fwrite(bcmv, 1, 4, fileptr) != 4) {
std::fprintf(stderr, "Webm2Pes: BCMV write failed.\n");
}
const std::size_t kBcmvSize = 4;
for (int i = 0; i < kBcmvSize; ++i)
buffer->push_back(bcmv[i]);
const std::size_t kRemainingBytes = 6;
const uint8_t buffer[kRemainingBytes] = {
const uint8_t bcmv_buffer[kRemainingBytes] = {
static_cast<std::uint8_t>((length >> 24) & 0xff),
static_cast<std::uint8_t>((length >> 16) & 0xff),
static_cast<std::uint8_t>((length >> 8) & 0xff),
static_cast<std::uint8_t>(length & 0xff),
0,
0 /* 2 bytes 0 padding */};
for (std::int8_t i = 0; i < kRemainingBytes; ++i) {
if (WriteUint8(buffer[i], fileptr) != true) {
std::fprintf(stderr, "Webm2Pes: BCMV remainder write failed.\n");
return false;
}
}
for (std::int8_t i = 0; i < kRemainingBytes; ++i)
buffer->push_back(bcmv_buffer[i]);
return true;
}
@ -205,34 +201,27 @@ bool BCMVHeader::Write(std::FILE* fileptr) const {
// PesHeader methods.
//
// Writes out the header to |file|. Calls PesOptionalHeader::Write() to write
// Writes out the header to |buffer|. Calls PesOptionalHeader::Write() to write
// |optional_header| contents. Returns true when successful, false otherwise.
bool PesHeader::Write(std::FILE* file, bool write_pts) const {
if (file == nullptr) {
bool PesHeader::Write(bool write_pts, PacketDataBuffer* buffer) const {
if (buffer == nullptr) {
std::fprintf(stderr, "Webm2Pes: nullptr in header writer.\n");
return false;
}
// Write |start_code|.
if (std::fwrite(reinterpret_cast<const void*>(start_code), 1, 4, file) != 4) {
std::fprintf(stderr, "Webm2Pes: cannot write packet start code.\n");
return false;
}
const std::size_t kStartCodeLength = 4;
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;
if (WriteUint8(byte, file) != true) {
std::fprintf(stderr, "Webm2Pes: cannot write packet length (byte 0).\n");
return false;
}
buffer->push_back(byte);
byte = packet_length & 0xff;
if (WriteUint8(byte, file) != true) {
std::fprintf(stderr, "Webm2Pes: cannot write packet length (byte 1).\n");
return false;
}
buffer->push_back(byte);
// Write the (not really) optional header.
if (optional_header.Write(file, write_pts) != true) {
if (optional_header.Write(write_pts, buffer) != true) {
std::fprintf(stderr, "Webm2Pes: PES optional header write failed.");
return false;
}
@ -261,6 +250,68 @@ bool Webm2Pes::ConvertToFile() {
return false;
}
// Walk clusters in segment.
const mkvparser::Cluster* cluster = webm_parser_->GetFirst();
while (cluster != nullptr && cluster->EOS() == false) {
const mkvparser::BlockEntry* block_entry = nullptr;
std::int64_t block_status = cluster->GetFirst(block_entry);
if (block_status < 0) {
std::fprintf(stderr, "Webm2Pes: Cannot parse first block in %s.\n",
input_file_name_.c_str());
return false;
}
// Walk blocks in cluster.
while (block_entry != nullptr && block_entry->EOS() == false) {
const mkvparser::Block* block = block_entry->GetBlock();
if (block->GetTrackNumber() == video_track_num_) {
const int frame_count = block->GetFrameCount();
// Walk frames in block.
for (int frame_num = 0; frame_num < frame_count; ++frame_num) {
const mkvparser::Block::Frame& frame = block->GetFrame(frame_num);
// Write frame out as PES packet(s), storing them in |packet_data_|.
const bool pes_status =
WritePesPacket(frame, block->GetTime(cluster));
if (pes_status != true) {
std::fprintf(stderr, "Webm2Pes: WritePesPacket failed.\n");
return false;
}
// Write contents of |packet_data_| to |output_file_|.
if (std::fwrite(&packet_data_[0], 1, packet_data_.size(),
output_file_.get()) != packet_data_.size()) {
std::fprintf(stderr, "Webm2Pes: packet payload write failed.\n");
return false;
}
}
}
block_status = cluster->GetNext(block_entry, block_entry);
if (block_status < 0) {
std::fprintf(stderr, "Webm2Pes: Cannot parse block in %s.\n",
input_file_name_.c_str());
return false;
}
}
cluster = webm_parser_->GetNext(cluster);
}
return true;
}
bool Webm2Pes::ConvertToPacketReceiver() {
if (input_file_name_.empty() || packet_sink_ == nullptr) {
std::fprintf(stderr, "Webm2Pes: input file name empty or null sink.\n");
return false;
}
if (InitWebmParser() != true) {
std::fprintf(stderr, "Webm2Pes: Cannot initialize WebM parser.\n");
return false;
}
// Walk clusters in segment.
const mkvparser::Cluster* cluster = webm_parser_->GetFirst();
while (cluster != nullptr && cluster->EOS() == false) {
@ -289,6 +340,10 @@ bool Webm2Pes::ConvertToFile() {
std::fprintf(stderr, "Webm2Pes: WritePesPacket failed.\n");
return false;
}
if (packet_sink_->ReceivePacket(packet_data_) != true) {
std::fprintf(stderr, "Webm2Pes: ReceivePacket failed.\n");
return false;
}
}
}
block_status = cluster->GetNext(block_entry, block_entry);
@ -411,29 +466,29 @@ bool Webm2Pes::WritePesPacket(const mkvparser::Block::Frame& vpx_frame,
const std::int64_t khz90_pts = NanosecondsTo90KhzTicks(nanosecond_pts);
header.optional_header.SetPtsBits(khz90_pts);
packet_data_.clear();
bool write_pts = true;
for (const Range& packet_payload_range : packet_payload_ranges) {
header.packet_length =
header.optional_header.size_in_bytes() + packet_payload_range.length;
if (header.Write(output_file_.get(), write_pts) != true) {
if (header.Write(write_pts, &packet_data_) != true) {
std::fprintf(stderr, "Webm2Pes: packet header write failed.\n");
return false;
}
write_pts = false;
BCMVHeader bcmv_header(packet_payload_range.length);
if (bcmv_header.Write(output_file_.get()) != true) {
if (bcmv_header.Write(&packet_data_) != true) {
std::fprintf(stderr, "Webm2Pes: BCMV write failed.\n");
return false;
}
// Write the payload.
if (std::fwrite(frame_data.get() + packet_payload_range.offset, 1,
packet_payload_range.length,
output_file_.get()) != packet_payload_range.length) {
std::fprintf(stderr, "Webm2Pes: packet payload write failed.\n");
return false;
}
// Insert the payload at the end of |packet_data_|.
const std::uint8_t* payload_start =
frame_data.get() + packet_payload_range.offset;
packet_data_.insert(packet_data_.end(), payload_start,
payload_start + packet_payload_range.length);
}
return true;

View File

@ -11,6 +11,7 @@
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include "mkvparser.hpp"
#include "mkvreader.hpp"
@ -52,6 +53,9 @@ struct PesHeaderField {
const std::uint8_t shift;
};
// Data is stored in buffers before being written to output files.
typedef std::vector<std::uint8_t> PacketDataBuffer;
// Storage for PES Optional Header values. Fields written in order using sizes
// specified.
struct PesOptionalHeader {
@ -112,9 +116,9 @@ struct PesOptionalHeader {
// Writes |pts_90khz| to |pts| per format described at its declaration above.
void SetPtsBits(std::int64_t pts_90khz);
// Writes fields to |file| and returns true. Returns false when write or
// Writes fields to |buffer| and returns true. Returns false when write or
// field value validation fails.
bool Write(std::FILE* file, bool write_pts) const;
bool Write(bool write_pts, PacketDataBuffer* buffer) const;
};
// Describes custom 10 byte header that immediately follows the PES Optional
@ -133,14 +137,13 @@ struct BCMVHeader {
static std::size_t size() { return 10; }
// Write the BCMV Header into the FILE stream.
bool Write(std::FILE* fileptr) const;
// Write the BCMV Header into |buffer|.
bool Write(PacketDataBuffer* buffer) const;
};
struct PesHeader {
const std::uint8_t start_code[4] = {
0x00,
0x00,
0x00, 0x00,
0x01, // 0x000001 is the PES packet start code prefix.
0xE0}; // 0xE0 is the minimum video stream ID.
std::uint16_t packet_length = 0; // Number of bytes _after_ this field.
@ -150,9 +153,16 @@ struct PesHeader {
6 /* start_code + packet_length */ + packet_length;
}
// Writes out the header to |file|. Calls PesOptionalHeader::Write() to write
// |optional_header| contents. Returns true when successful, false otherwise.
bool Write(std::FILE* file, bool write_pts) const;
// Writes out the header to |buffer|. Calls PesOptionalHeader::Write() to
// write |optional_header| contents. Returns true when successful, false
// otherwise.
bool Write(bool write_pts, PacketDataBuffer* buffer) const;
};
class PacketReceiverInterface {
public:
virtual ~PacketReceiverInterface() {}
virtual bool ReceivePacket(const PacketDataBuffer& packet) = 0;
};
// Converts the VP9 track of a WebM file to a Packetized Elementary Stream
@ -165,6 +175,8 @@ class Webm2Pes {
Webm2Pes(const std::string& input_file, const std::string& output_file)
: input_file_name_(input_file), output_file_name_(output_file) {}
Webm2Pes(const std::string& input_file, PacketReceiverInterface* packet_sink)
: input_file_name_(input_file), packet_sink_(packet_sink) {}
Webm2Pes() = delete;
Webm2Pes(const Webm2Pes&) = delete;
@ -175,6 +187,12 @@ class Webm2Pes {
// to report failure.
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.
bool ConvertToPacketReceiver();
private:
bool InitWebmParser();
bool WritePesPacket(const mkvparser::Block::Frame& vpx_frame,
@ -194,10 +212,15 @@ class Webm2Pes {
// Input timecode scale.
std::int64_t timecode_scale_ = 1000000;
// Packet sink; when constructed with a PacketReceiverInterface*, packet and
// type of packet are sent to |packet_sink_| instead of written to an output
// file.
PacketReceiverInterface* packet_sink_ = nullptr;
PacketDataBuffer packet_data_;
};
} // namespace libwebm
#endif // LIBWEBM_WEBM2PES_H_