• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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