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