1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
|
// Copyright (c) 2015 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_M2TS_WEBM2PES_H_
#define LIBWEBM_M2TS_WEBM2PES_H_
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include "common/libwebm_util.h"
#include "common/video_frame.h"
#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.
// Maximum size is 64 bits. Users may call the Check() method to perform minimal
// validation (size > 0 and <= 64).
struct PesHeaderField {
PesHeaderField(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) {}
PesHeaderField() = delete;
PesHeaderField(const PesHeaderField&) = default;
PesHeaderField(PesHeaderField&&) = default;
~PesHeaderField() = 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 int num_bits;
// Index into the header for the byte in which |bits| will be written.
const std::uint8_t index;
// Number of bits to shift value before or'ing.
const int 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 {
// TODO(tomfinegan): The fields could be in an array, which would allow the
// code writing the optional header to iterate over the fields instead of
// having code for dealing with each one.
// 2 bits (marker): 2 ('10')
const PesHeaderField marker = PesHeaderField(2, 2, 0, 6);
// 2 bits (no scrambling): 0x0 ('00')
const PesHeaderField scrambling = PesHeaderField(0, 2, 0, 4);
// 1 bit (priority): 0x0 ('0')
const PesHeaderField priority = PesHeaderField(0, 1, 0, 3);
// TODO(tomfinegan): The BCMV header could be considered a sync word, and this
// field should be 1 when a sync word follows the packet. Clarify.
// 1 bit (data alignment): 0x0 ('0')
const PesHeaderField data_alignment = PesHeaderField(0, 1, 0, 2);
// 1 bit (copyright): 0x0 ('0')
const PesHeaderField copyright = PesHeaderField(0, 1, 0, 1);
// 1 bit (original/copy): 0x0 ('0')
const PesHeaderField original = PesHeaderField(0, 1, 0, 0);
// 1 bit (has_pts): 0x1 ('1')
const PesHeaderField has_pts = PesHeaderField(1, 1, 1, 7);
// 1 bit (has_dts): 0x0 ('0')
const PesHeaderField has_dts = PesHeaderField(0, 1, 1, 6);
// 6 bits (unused fields): 0x0 ('000000')
const PesHeaderField unused = PesHeaderField(0, 6, 1, 0);
// 8 bits (size of remaining data in the Header).
const PesHeaderField remaining_size = PesHeaderField(6, 8, 2, 0);
// PTS: 5 bytes
// 4 bits (flag: PTS present, but no DTS): 0x2 ('0010')
// 36 bits (90khz PTS):
// top 3 bits
// marker ('1')
// middle 15 bits
// marker ('1')
// bottom 15 bits
// marker ('1')
PesHeaderField pts = PesHeaderField(0, 40, 3, 0);
PesHeaderField stuffing_byte = PesHeaderField(0xFF, 8, 8, 0);
// PTS omitted in fragments. Size remains unchanged: More stuffing bytes.
bool fragment = false;
static std::size_t size_in_bytes() { return 9; }
// Writes |pts_90khz| to |pts| per format described at its declaration above.
void SetPtsBits(std::int64_t pts_90khz);
// Writes fields to |buffer| and returns true. Returns false when write or
// field value validation fails.
bool Write(bool write_pts, PacketDataBuffer* buffer) const;
};
// Describes custom 10 byte header that immediately follows the PES Optional
// Header in each PES packet output by Webm2Pes:
// 4 byte 'B' 'C' 'M' 'V'
// 4 byte big-endian length of frame
// 2 bytes 0 padding
struct BCMVHeader {
explicit BCMVHeader(std::uint32_t frame_length) : length(frame_length) {}
BCMVHeader() = delete;
BCMVHeader(const BCMVHeader&) = delete;
BCMVHeader(BCMVHeader&&) = delete;
~BCMVHeader() = default;
const std::uint8_t bcmv[4] = {'B', 'C', 'M', 'V'};
const std::uint32_t length;
static std::size_t size() { return 10; }
// Write the BCMV Header into |buffer|. Caller responsible for ensuring
// destination buffer is of size >= BCMVHeader::size().
bool Write(PacketDataBuffer* buffer) const;
bool Write(uint8_t* buffer);
};
struct PesHeader {
const std::uint8_t start_code[4] = {
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.
PesOptionalHeader optional_header;
std::size_t size() const {
return optional_header.size_in_bytes() +
6 /* start_code + packet_length */ + packet_length;
}
// 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
// suitable for use in a MPEG2TS.
// https://en.wikipedia.org/wiki/Packetized_elementary_stream
// https://en.wikipedia.org/wiki/MPEG_transport_stream
class Webm2Pes {
public:
static const std::size_t kMaxPayloadSize;
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;
Webm2Pes(Webm2Pes&&) = delete;
~Webm2Pes() = default;
// Converts the VPx video stream to a PES file and returns true. Returns false
// to report failure.
bool ConvertToFile();
// Converts the VPx video stream to a sequence of PES packets, and calls the
// 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();
// Writes |vpx_frame| out as PES packet[s] and stores output in |packet_data|.
// Returns true for success, false for failure.
static bool WritePesPacket(const VideoFrame& frame,
PacketDataBuffer* packet_data);
uint64_t bytes_written() const { return bytes_written_; }
private:
bool InitWebmParser();
bool ReadVideoFrame(const mkvparser::Block::Frame& mkvparser_frame,
VideoFrame* frame);
const std::string input_file_name_;
const std::string output_file_name_;
std::unique_ptr<mkvparser::Segment> webm_parser_;
mkvparser::MkvReader webm_reader_;
FilePtr output_file_;
// Video track num in the WebM file.
int video_track_num_ = 0;
// Video codec reported by CodecName from Video TrackEntry.
VideoFrame::Codec codec_;
// 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_;
std::uint64_t bytes_written_ = 0;
};
// Copies |raw_input_length| bytes from |raw_input| to |packet_buffer| while
// escaping start codes. Returns true when bytes are successfully copied.
// A start code is the 3 byte sequence 0x00 0x00 0x01. When
// the sequence is encountered, the value 0x03 is inserted. To avoid
// any ambiguity at reassembly time, the same is done for the sequence
// 0x00 0x00 0x03. So, the following transformation occurs for when either
// of the noted sequences is encountered:
//
// 0x00 0x00 0x01 => 0x00 0x00 0x03 0x01
// 0x00 0x00 0x03 => 0x00 0x00 0x03 0x03
bool CopyAndEscapeStartCodes(const std::uint8_t* raw_input,
std::size_t raw_input_length,
PacketDataBuffer* packet_buffer);
} // namespace libwebm
#endif // LIBWEBM_M2TS_WEBM2PES_H_
|