1*103e46e4SHarish Mahendrakar // Copyright (c) 2016 The WebM project authors. All Rights Reserved.
2*103e46e4SHarish Mahendrakar //
3*103e46e4SHarish Mahendrakar // Use of this source code is governed by a BSD-style license
4*103e46e4SHarish Mahendrakar // that can be found in the LICENSE file in the root of the source
5*103e46e4SHarish Mahendrakar // tree. An additional intellectual property rights grant can be found
6*103e46e4SHarish Mahendrakar // in the file PATENTS. All contributing project authors may
7*103e46e4SHarish Mahendrakar // be found in the AUTHORS file in the root of the source tree.
8*103e46e4SHarish Mahendrakar #include "m2ts/vpxpes2ts.h"
9*103e46e4SHarish Mahendrakar
10*103e46e4SHarish Mahendrakar #include <algorithm>
11*103e46e4SHarish Mahendrakar #include <cstdint>
12*103e46e4SHarish Mahendrakar #include <cstdio>
13*103e46e4SHarish Mahendrakar #include <vector>
14*103e46e4SHarish Mahendrakar
15*103e46e4SHarish Mahendrakar namespace libwebm {
16*103e46e4SHarish Mahendrakar // TODO(tomfinegan): Dedupe this and PesHeaderField.
17*103e46e4SHarish Mahendrakar // Stores a value and its size in bits for writing into a MPEG2 TS Header.
18*103e46e4SHarish Mahendrakar // Maximum size is 64 bits. Users may call the Check() method to perform minimal
19*103e46e4SHarish Mahendrakar // validation (size > 0 and <= 64).
20*103e46e4SHarish Mahendrakar struct TsHeaderField {
TsHeaderFieldlibwebm::TsHeaderField21*103e46e4SHarish Mahendrakar TsHeaderField(std::uint64_t value, std::uint32_t size_in_bits,
22*103e46e4SHarish Mahendrakar std::uint8_t byte_index, std::uint8_t bits_to_shift)
23*103e46e4SHarish Mahendrakar : bits(value),
24*103e46e4SHarish Mahendrakar num_bits(size_in_bits),
25*103e46e4SHarish Mahendrakar index(byte_index),
26*103e46e4SHarish Mahendrakar shift(bits_to_shift) {}
27*103e46e4SHarish Mahendrakar TsHeaderField() = delete;
28*103e46e4SHarish Mahendrakar TsHeaderField(const TsHeaderField&) = default;
29*103e46e4SHarish Mahendrakar TsHeaderField(TsHeaderField&&) = default;
30*103e46e4SHarish Mahendrakar ~TsHeaderField() = default;
Checklibwebm::TsHeaderField31*103e46e4SHarish Mahendrakar bool Check() const {
32*103e46e4SHarish Mahendrakar return num_bits > 0 && num_bits <= 64 && shift >= 0 && shift < 64;
33*103e46e4SHarish Mahendrakar }
34*103e46e4SHarish Mahendrakar
35*103e46e4SHarish Mahendrakar // Value to be stored in the field.
36*103e46e4SHarish Mahendrakar std::uint64_t bits;
37*103e46e4SHarish Mahendrakar
38*103e46e4SHarish Mahendrakar // Number of bits in the value.
39*103e46e4SHarish Mahendrakar const int num_bits;
40*103e46e4SHarish Mahendrakar
41*103e46e4SHarish Mahendrakar // Index into the header for the byte in which |bits| will be written.
42*103e46e4SHarish Mahendrakar const std::uint8_t index;
43*103e46e4SHarish Mahendrakar
44*103e46e4SHarish Mahendrakar // Number of bits to left shift value before or'ing. Ignored for whole bytes.
45*103e46e4SHarish Mahendrakar const int shift;
46*103e46e4SHarish Mahendrakar };
47*103e46e4SHarish Mahendrakar
48*103e46e4SHarish Mahendrakar // Data storage for MPEG2 Transport Stream headers.
49*103e46e4SHarish Mahendrakar // https://en.wikipedia.org/wiki/MPEG_transport_stream#Packet
50*103e46e4SHarish Mahendrakar struct TsHeader {
TsHeaderlibwebm::TsHeader51*103e46e4SHarish Mahendrakar TsHeader(bool payload_start, bool adaptation_flag, std::uint8_t counter)
52*103e46e4SHarish Mahendrakar : is_payload_start(payload_start),
53*103e46e4SHarish Mahendrakar has_adaptation(adaptation_flag),
54*103e46e4SHarish Mahendrakar counter_value(counter) {}
55*103e46e4SHarish Mahendrakar TsHeader() = delete;
56*103e46e4SHarish Mahendrakar TsHeader(const TsHeader&) = default;
57*103e46e4SHarish Mahendrakar TsHeader(TsHeader&&) = default;
58*103e46e4SHarish Mahendrakar ~TsHeader() = default;
59*103e46e4SHarish Mahendrakar
60*103e46e4SHarish Mahendrakar void Write(PacketDataBuffer* buffer) const;
61*103e46e4SHarish Mahendrakar
62*103e46e4SHarish Mahendrakar // Indicates the packet is the beginning of a new fragmented payload.
63*103e46e4SHarish Mahendrakar const bool is_payload_start;
64*103e46e4SHarish Mahendrakar
65*103e46e4SHarish Mahendrakar // Indicates the packet contains an adaptation field.
66*103e46e4SHarish Mahendrakar const bool has_adaptation;
67*103e46e4SHarish Mahendrakar
68*103e46e4SHarish Mahendrakar // The sync byte is the bit pattern of 0x47 (ASCII char 'G').
69*103e46e4SHarish Mahendrakar const std::uint8_t kTsHeaderSyncByte = 0x47;
70*103e46e4SHarish Mahendrakar const std::uint8_t sync_byte = kTsHeaderSyncByte;
71*103e46e4SHarish Mahendrakar
72*103e46e4SHarish Mahendrakar // Value for |continuity_counter|. Used to detect gaps when demuxing.
73*103e46e4SHarish Mahendrakar const std::uint8_t counter_value;
74*103e46e4SHarish Mahendrakar
75*103e46e4SHarish Mahendrakar // Set when FEC is impossible. Always 0.
76*103e46e4SHarish Mahendrakar const TsHeaderField transport_error_indicator = TsHeaderField(0, 1, 1, 7);
77*103e46e4SHarish Mahendrakar
78*103e46e4SHarish Mahendrakar // This MPEG2 TS header is the start of a new payload (aka PES packet).
79*103e46e4SHarish Mahendrakar const TsHeaderField payload_unit_start_indicator =
80*103e46e4SHarish Mahendrakar TsHeaderField(is_payload_start ? 1 : 0, 1, 1, 6);
81*103e46e4SHarish Mahendrakar
82*103e46e4SHarish Mahendrakar // Set when the current packet has a higher priority than other packets with
83*103e46e4SHarish Mahendrakar // the same PID. Always 0 for VPX.
84*103e46e4SHarish Mahendrakar const TsHeaderField transport_priority = TsHeaderField(0, 1, 1, 5);
85*103e46e4SHarish Mahendrakar
86*103e46e4SHarish Mahendrakar // https://en.wikipedia.org/wiki/MPEG_transport_stream#Packet_Identifier_.28PID.29
87*103e46e4SHarish Mahendrakar // 0x0020-0x1FFA May be assigned as needed to Program Map Tables, elementary
88*103e46e4SHarish Mahendrakar // streams and other data tables.
89*103e46e4SHarish Mahendrakar // Note: Though we hard code to 0x20, this value is actually 13 bits-- the
90*103e46e4SHarish Mahendrakar // buffer for the header is always set to 0, so it doesn't matter in practice.
91*103e46e4SHarish Mahendrakar const TsHeaderField pid = TsHeaderField(0x20, 8, 2, 0);
92*103e46e4SHarish Mahendrakar
93*103e46e4SHarish Mahendrakar // Indicates scrambling key. Unused; always 0.
94*103e46e4SHarish Mahendrakar const TsHeaderField scrambling_control = TsHeaderField(0, 2, 3, 6);
95*103e46e4SHarish Mahendrakar
96*103e46e4SHarish Mahendrakar // Adaptation field flag. Unused; always 0.
97*103e46e4SHarish Mahendrakar // TODO(tomfinegan): Not sure this is OK. Might need to add support for
98*103e46e4SHarish Mahendrakar // writing the Adaptation Field.
99*103e46e4SHarish Mahendrakar const TsHeaderField adaptation_field_flag =
100*103e46e4SHarish Mahendrakar TsHeaderField(has_adaptation ? 1 : 0, 1, 3, 5);
101*103e46e4SHarish Mahendrakar
102*103e46e4SHarish Mahendrakar // Payload flag. All output packets created here have payloads. Always 1.
103*103e46e4SHarish Mahendrakar const TsHeaderField payload_flag = TsHeaderField(1, 1, 3, 4);
104*103e46e4SHarish Mahendrakar
105*103e46e4SHarish Mahendrakar // Continuity counter. Two bit field that is incremented for every packet.
106*103e46e4SHarish Mahendrakar const TsHeaderField continuity_counter =
107*103e46e4SHarish Mahendrakar TsHeaderField(counter_value, 4, 3, 3);
108*103e46e4SHarish Mahendrakar };
109*103e46e4SHarish Mahendrakar
Write(PacketDataBuffer * buffer) const110*103e46e4SHarish Mahendrakar void TsHeader::Write(PacketDataBuffer* buffer) const {
111*103e46e4SHarish Mahendrakar std::uint8_t* byte = &(*buffer)[0];
112*103e46e4SHarish Mahendrakar *byte = sync_byte;
113*103e46e4SHarish Mahendrakar
114*103e46e4SHarish Mahendrakar *++byte = 0;
115*103e46e4SHarish Mahendrakar *byte |= transport_error_indicator.bits << transport_error_indicator.shift;
116*103e46e4SHarish Mahendrakar *byte |= payload_unit_start_indicator.bits
117*103e46e4SHarish Mahendrakar << payload_unit_start_indicator.shift;
118*103e46e4SHarish Mahendrakar *byte |= transport_priority.bits << transport_priority.shift;
119*103e46e4SHarish Mahendrakar
120*103e46e4SHarish Mahendrakar *++byte = pid.bits & 0xff;
121*103e46e4SHarish Mahendrakar
122*103e46e4SHarish Mahendrakar *++byte = 0;
123*103e46e4SHarish Mahendrakar *byte |= scrambling_control.bits << scrambling_control.shift;
124*103e46e4SHarish Mahendrakar *byte |= adaptation_field_flag.bits << adaptation_field_flag.shift;
125*103e46e4SHarish Mahendrakar *byte |= payload_flag.bits << payload_flag.shift;
126*103e46e4SHarish Mahendrakar *byte |= continuity_counter.bits; // last 4 bits.
127*103e46e4SHarish Mahendrakar }
128*103e46e4SHarish Mahendrakar
ConvertToFile()129*103e46e4SHarish Mahendrakar bool VpxPes2Ts::ConvertToFile() {
130*103e46e4SHarish Mahendrakar output_file_ = FilePtr(fopen(output_file_name_.c_str(), "wb"), FILEDeleter());
131*103e46e4SHarish Mahendrakar if (output_file_ == nullptr) {
132*103e46e4SHarish Mahendrakar std::fprintf(stderr, "VpxPes2Ts: Cannot open %s for output.\n",
133*103e46e4SHarish Mahendrakar output_file_name_.c_str());
134*103e46e4SHarish Mahendrakar return false;
135*103e46e4SHarish Mahendrakar }
136*103e46e4SHarish Mahendrakar pes_converter_.reset(new Webm2Pes(input_file_name_, this));
137*103e46e4SHarish Mahendrakar if (pes_converter_ == nullptr) {
138*103e46e4SHarish Mahendrakar std::fprintf(stderr, "VpxPes2Ts: Out of memory.\n");
139*103e46e4SHarish Mahendrakar return false;
140*103e46e4SHarish Mahendrakar }
141*103e46e4SHarish Mahendrakar return pes_converter_->ConvertToPacketReceiver();
142*103e46e4SHarish Mahendrakar }
143*103e46e4SHarish Mahendrakar
ReceivePacket(const PacketDataBuffer & packet_data)144*103e46e4SHarish Mahendrakar bool VpxPes2Ts::ReceivePacket(const PacketDataBuffer& packet_data) {
145*103e46e4SHarish Mahendrakar const int kTsHeaderSize = 4;
146*103e46e4SHarish Mahendrakar const int kTsPayloadSize = 184;
147*103e46e4SHarish Mahendrakar const int kTsPacketSize = kTsHeaderSize + kTsPayloadSize;
148*103e46e4SHarish Mahendrakar int bytes_to_packetize = static_cast<int>(packet_data.size());
149*103e46e4SHarish Mahendrakar std::uint8_t continuity_counter = 0;
150*103e46e4SHarish Mahendrakar std::size_t read_pos = 0;
151*103e46e4SHarish Mahendrakar
152*103e46e4SHarish Mahendrakar ts_buffer_.reserve(kTsPacketSize);
153*103e46e4SHarish Mahendrakar
154*103e46e4SHarish Mahendrakar while (bytes_to_packetize > 0) {
155*103e46e4SHarish Mahendrakar if (continuity_counter > 0xf)
156*103e46e4SHarish Mahendrakar continuity_counter = 0;
157*103e46e4SHarish Mahendrakar
158*103e46e4SHarish Mahendrakar // Calculate payload size (need to know if we'll have to pad with an empty
159*103e46e4SHarish Mahendrakar // adaptation field).
160*103e46e4SHarish Mahendrakar int payload_size = std::min(bytes_to_packetize, kTsPayloadSize);
161*103e46e4SHarish Mahendrakar
162*103e46e4SHarish Mahendrakar // Write the TS header.
163*103e46e4SHarish Mahendrakar const TsHeader header(
164*103e46e4SHarish Mahendrakar bytes_to_packetize == static_cast<int>(packet_data.size()),
165*103e46e4SHarish Mahendrakar payload_size != kTsPayloadSize, continuity_counter);
166*103e46e4SHarish Mahendrakar header.Write(&ts_buffer_);
167*103e46e4SHarish Mahendrakar int write_pos = kTsHeaderSize;
168*103e46e4SHarish Mahendrakar
169*103e46e4SHarish Mahendrakar // (pre)Pad payload with an empty adaptation field. All packets must be
170*103e46e4SHarish Mahendrakar // |kTsPacketSize| (188).
171*103e46e4SHarish Mahendrakar if (payload_size < kTsPayloadSize) {
172*103e46e4SHarish Mahendrakar // We need at least 2 bytes to write an empty adaptation field.
173*103e46e4SHarish Mahendrakar if (payload_size == (kTsPayloadSize - 1)) {
174*103e46e4SHarish Mahendrakar payload_size--;
175*103e46e4SHarish Mahendrakar }
176*103e46e4SHarish Mahendrakar
177*103e46e4SHarish Mahendrakar // Padding adaptation field:
178*103e46e4SHarish Mahendrakar // 8 bits: number of adaptation field bytes following this byte.
179*103e46e4SHarish Mahendrakar // 8 bits: unused (in this program) flags.
180*103e46e4SHarish Mahendrakar // This is followed by a run of 0xff to reach |kTsPayloadSize| (184)
181*103e46e4SHarish Mahendrakar // bytes.
182*103e46e4SHarish Mahendrakar const int pad_size = kTsPayloadSize - payload_size - 1 - 1;
183*103e46e4SHarish Mahendrakar ts_buffer_[write_pos++] = pad_size + 1;
184*103e46e4SHarish Mahendrakar ts_buffer_[write_pos++] = 0;
185*103e46e4SHarish Mahendrakar
186*103e46e4SHarish Mahendrakar const std::uint8_t kStuffingByte = 0xff;
187*103e46e4SHarish Mahendrakar for (int i = 0; i < pad_size; ++i) {
188*103e46e4SHarish Mahendrakar ts_buffer_[write_pos++] = kStuffingByte;
189*103e46e4SHarish Mahendrakar }
190*103e46e4SHarish Mahendrakar }
191*103e46e4SHarish Mahendrakar
192*103e46e4SHarish Mahendrakar for (int i = 0; i < payload_size; ++i) {
193*103e46e4SHarish Mahendrakar ts_buffer_[write_pos++] = packet_data[read_pos++];
194*103e46e4SHarish Mahendrakar }
195*103e46e4SHarish Mahendrakar
196*103e46e4SHarish Mahendrakar bytes_to_packetize -= payload_size;
197*103e46e4SHarish Mahendrakar continuity_counter++;
198*103e46e4SHarish Mahendrakar
199*103e46e4SHarish Mahendrakar if (write_pos != kTsPacketSize) {
200*103e46e4SHarish Mahendrakar fprintf(stderr, "VpxPes2Ts: Invalid packet length.\n");
201*103e46e4SHarish Mahendrakar return false;
202*103e46e4SHarish Mahendrakar }
203*103e46e4SHarish Mahendrakar
204*103e46e4SHarish Mahendrakar // Write contents of |ts_buffer_| to |output_file_|.
205*103e46e4SHarish Mahendrakar // TODO(tomfinegan): Writing 188 bytes at a time isn't exactly efficient...
206*103e46e4SHarish Mahendrakar // Fix me.
207*103e46e4SHarish Mahendrakar if (static_cast<int>(std::fwrite(&ts_buffer_[0], 1, kTsPacketSize,
208*103e46e4SHarish Mahendrakar output_file_.get())) != kTsPacketSize) {
209*103e46e4SHarish Mahendrakar std::fprintf(stderr, "VpxPes2Ts: TS packet write failed.\n");
210*103e46e4SHarish Mahendrakar return false;
211*103e46e4SHarish Mahendrakar }
212*103e46e4SHarish Mahendrakar }
213*103e46e4SHarish Mahendrakar
214*103e46e4SHarish Mahendrakar return true;
215*103e46e4SHarish Mahendrakar }
216*103e46e4SHarish Mahendrakar
217*103e46e4SHarish Mahendrakar } // namespace libwebm
218