xref: /aosp_15_r20/external/libwebm/m2ts/vpxpes_parser.cc (revision 103e46e4cd4b6efcf6001f23fa8665fb110abf8d)
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 "vpxpes_parser.h"
9 
10 #include <cstdint>
11 #include <cstdio>
12 #include <cstring>
13 #include <limits>
14 #include <vector>
15 
16 #include "common/file_util.h"
17 
18 namespace libwebm {
19 
BcmvHeader(std::uint32_t len)20 VpxPesParser::BcmvHeader::BcmvHeader(std::uint32_t len) : length(len) {
21   id[0] = 'B';
22   id[1] = 'C';
23   id[2] = 'M';
24   id[3] = 'V';
25 }
26 
operator ==(const BcmvHeader & other) const27 bool VpxPesParser::BcmvHeader::operator==(const BcmvHeader& other) const {
28   return (other.length == length && other.id[0] == id[0] &&
29           other.id[1] == id[1] && other.id[2] == id[2] && other.id[3] == id[3]);
30 }
31 
Valid() const32 bool VpxPesParser::BcmvHeader::Valid() const {
33   return (length > 0 && id[0] == 'B' && id[1] == 'C' && id[2] == 'M' &&
34           id[3] == 'V');
35 }
36 
37 // TODO(tomfinegan): Break Open() into separate functions. One that opens the
38 // file, and one that reads one packet at a time. As things are files larger
39 // than the maximum availble memory for the current process cannot be loaded.
Open(const std::string & pes_file)40 bool VpxPesParser::Open(const std::string& pes_file) {
41   pes_file_size_ = static_cast<size_t>(libwebm::GetFileSize(pes_file));
42   if (pes_file_size_ <= 0)
43     return false;
44   pes_file_data_.reserve(static_cast<size_t>(pes_file_size_));
45   libwebm::FilePtr file = libwebm::FilePtr(std::fopen(pes_file.c_str(), "rb"),
46                                            libwebm::FILEDeleter());
47   int byte;
48   while ((byte = fgetc(file.get())) != EOF) {
49     pes_file_data_.push_back(static_cast<std::uint8_t>(byte));
50   }
51 
52   if (!feof(file.get()) || ferror(file.get()) ||
53       pes_file_size_ != pes_file_data_.size()) {
54     return false;
55   }
56 
57   read_pos_ = 0;
58   parse_state_ = kFindStartCode;
59   return true;
60 }
61 
VerifyPacketStartCode() const62 bool VpxPesParser::VerifyPacketStartCode() const {
63   if (read_pos_ + 2 > pes_file_data_.size())
64     return false;
65 
66   // PES packets all start with the byte sequence 0x0 0x0 0x1.
67   if (pes_file_data_[read_pos_] != 0 || pes_file_data_[read_pos_ + 1] != 0 ||
68       pes_file_data_[read_pos_ + 2] != 1) {
69     return false;
70   }
71 
72   return true;
73 }
74 
ReadStreamId(std::uint8_t * stream_id) const75 bool VpxPesParser::ReadStreamId(std::uint8_t* stream_id) const {
76   if (!stream_id || BytesAvailable() < 4)
77     return false;
78 
79   *stream_id = pes_file_data_[read_pos_ + 3];
80   return true;
81 }
82 
ReadPacketLength(std::uint16_t * packet_length) const83 bool VpxPesParser::ReadPacketLength(std::uint16_t* packet_length) const {
84   if (!packet_length || BytesAvailable() < 6)
85     return false;
86 
87   // Read and byte swap 16 bit big endian length.
88   *packet_length =
89       (pes_file_data_[read_pos_ + 4] << 8) | pes_file_data_[read_pos_ + 5];
90 
91   return true;
92 }
93 
ParsePesHeader(PesHeader * header)94 bool VpxPesParser::ParsePesHeader(PesHeader* header) {
95   if (!header || parse_state_ != kParsePesHeader)
96     return false;
97 
98   if (!VerifyPacketStartCode())
99     return false;
100 
101   std::size_t pos = read_pos_;
102   for (auto& a : header->start_code) {
103     a = pes_file_data_[pos++];
104   }
105 
106   // PES Video stream IDs start at E0.
107   if (!ReadStreamId(&header->stream_id))
108     return false;
109 
110   if (header->stream_id < kMinVideoStreamId ||
111       header->stream_id > kMaxVideoStreamId)
112     return false;
113 
114   if (!ReadPacketLength(&header->packet_length))
115     return false;
116 
117   read_pos_ += kPesHeaderSize;
118   parse_state_ = kParsePesOptionalHeader;
119   return true;
120 }
121 
122 // TODO(tomfinegan): Make these masks constants.
ParsePesOptionalHeader(PesOptionalHeader * header)123 bool VpxPesParser::ParsePesOptionalHeader(PesOptionalHeader* header) {
124   if (!header || parse_state_ != kParsePesOptionalHeader ||
125       read_pos_ >= pes_file_size_) {
126     return false;
127   }
128 
129   std::size_t consumed = 0;
130   PacketData poh_buffer;
131   if (!RemoveStartCodeEmulationPreventionBytes(&pes_file_data_[read_pos_],
132                                                kPesOptionalHeaderSize,
133                                                &poh_buffer, &consumed)) {
134     return false;
135   }
136 
137   std::size_t offset = 0;
138   header->marker = (poh_buffer[offset] & 0x80) >> 6;
139   header->scrambling = (poh_buffer[offset] & 0x30) >> 4;
140   header->priority = (poh_buffer[offset] & 0x8) >> 3;
141   header->data_alignment = (poh_buffer[offset] & 0xc) >> 2;
142   header->copyright = (poh_buffer[offset] & 0x2) >> 1;
143   header->original = poh_buffer[offset] & 0x1;
144   offset++;
145 
146   header->has_pts = (poh_buffer[offset] & 0x80) >> 7;
147   header->has_dts = (poh_buffer[offset] & 0x40) >> 6;
148   header->unused_fields = poh_buffer[offset] & 0x3f;
149   offset++;
150 
151   header->remaining_size = poh_buffer[offset];
152   if (header->remaining_size !=
153       static_cast<int>(kWebm2PesOptHeaderRemainingSize))
154     return false;
155 
156   size_t bytes_left = header->remaining_size;
157   offset++;
158 
159   if (header->has_pts) {
160     // Read PTS markers. Format:
161     // PTS: 5 bytes
162     //   4 bits (flag: PTS present, but no DTS): 0x2 ('0010')
163     //   36 bits (90khz PTS):
164     //     top 3 bits
165     //     marker ('1')
166     //     middle 15 bits
167     //     marker ('1')
168     //     bottom 15 bits
169     //     marker ('1')
170     // TODO(tomfinegan): read/store the timestamp.
171     header->pts_dts_flag = (poh_buffer[offset] & 0x20) >> 4;
172     // Check the marker bits.
173     if ((poh_buffer[offset + 0] & 1) != 1 ||
174         (poh_buffer[offset + 2] & 1) != 1 ||
175         (poh_buffer[offset + 4] & 1) != 1) {
176       return false;
177     }
178 
179     header->pts = (poh_buffer[offset] & 0xe) << 29 |
180                   ((ReadUint16(&poh_buffer[offset + 1]) & ~1) << 14) |
181                   (ReadUint16(&poh_buffer[offset + 3]) >> 1);
182     offset += 5;
183     bytes_left -= 5;
184   }
185 
186   // Validate stuffing byte(s).
187   for (size_t i = 0; i < bytes_left; ++i) {
188     if (poh_buffer[offset + i] != 0xff)
189       return false;
190   }
191 
192   read_pos_ += consumed;
193   parse_state_ = kParseBcmvHeader;
194 
195   return true;
196 }
197 
198 // Parses and validates a BCMV header.
ParseBcmvHeader(BcmvHeader * header)199 bool VpxPesParser::ParseBcmvHeader(BcmvHeader* header) {
200   if (!header || parse_state_ != kParseBcmvHeader)
201     return false;
202 
203   PacketData bcmv_buffer;
204   std::size_t consumed = 0;
205   if (!RemoveStartCodeEmulationPreventionBytes(&pes_file_data_[read_pos_],
206                                                kBcmvHeaderSize, &bcmv_buffer,
207                                                &consumed)) {
208     return false;
209   }
210 
211   std::size_t offset = 0;
212   header->id[0] = bcmv_buffer[offset++];
213   header->id[1] = bcmv_buffer[offset++];
214   header->id[2] = bcmv_buffer[offset++];
215   header->id[3] = bcmv_buffer[offset++];
216 
217   header->length = 0;
218   header->length |= bcmv_buffer[offset++] << 24;
219   header->length |= bcmv_buffer[offset++] << 16;
220   header->length |= bcmv_buffer[offset++] << 8;
221   header->length |= bcmv_buffer[offset++];
222 
223   // Length stored in the BCMV header is followed by 2 bytes of 0 padding.
224   if (bcmv_buffer[offset++] != 0 || bcmv_buffer[offset++] != 0)
225     return false;
226 
227   if (!header->Valid())
228     return false;
229 
230   parse_state_ = kFindStartCode;
231   read_pos_ += consumed;
232 
233   return true;
234 }
235 
FindStartCode(std::size_t origin,std::size_t * offset) const236 bool VpxPesParser::FindStartCode(std::size_t origin,
237                                  std::size_t* offset) const {
238   if (read_pos_ + 2 >= pes_file_size_)
239     return false;
240 
241   const std::size_t length = pes_file_size_ - origin;
242   if (length < 3)
243     return false;
244 
245   const uint8_t* const data = &pes_file_data_[origin];
246   for (std::size_t i = 0; i < length - 3; ++i) {
247     if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
248       *offset = origin + i;
249       return true;
250     }
251   }
252 
253   return false;
254 }
255 
IsPayloadFragmented(const PesHeader & header) const256 bool VpxPesParser::IsPayloadFragmented(const PesHeader& header) const {
257   return (header.packet_length != 0 &&
258           (header.packet_length - kPesOptionalHeaderSize) !=
259               header.bcmv_header.length);
260 }
261 
AccumulateFragmentedPayload(std::size_t pes_packet_length,std::size_t payload_length)262 bool VpxPesParser::AccumulateFragmentedPayload(std::size_t pes_packet_length,
263                                                std::size_t payload_length) {
264   const std::size_t first_fragment_length =
265       pes_packet_length - kPesOptionalHeaderSize - kBcmvHeaderSize;
266   for (std::size_t i = 0; i < first_fragment_length; ++i) {
267     payload_.push_back(pes_file_data_[read_pos_ + i]);
268   }
269   read_pos_ += first_fragment_length;
270   parse_state_ = kFindStartCode;
271 
272   while (payload_.size() < payload_length) {
273     PesHeader header;
274     std::size_t packet_start_pos = read_pos_;
275     if (!FindStartCode(read_pos_, &packet_start_pos)) {
276       return false;
277     }
278     parse_state_ = kParsePesHeader;
279     read_pos_ = packet_start_pos;
280 
281     if (!ParsePesHeader(&header)) {
282       return false;
283     }
284     if (!ParsePesOptionalHeader(&header.opt_header)) {
285       return false;
286     }
287 
288     const std::size_t fragment_length =
289         header.packet_length - kPesOptionalHeaderSize;
290     std::size_t consumed = 0;
291     if (!RemoveStartCodeEmulationPreventionBytes(&pes_file_data_[read_pos_],
292                                                  fragment_length, &payload_,
293                                                  &consumed)) {
294       return false;
295     }
296     read_pos_ += consumed;
297   }
298   return true;
299 }
300 
RemoveStartCodeEmulationPreventionBytes(const std::uint8_t * raw_data,std::size_t bytes_required,PacketData * processed_data,std::size_t * bytes_consumed) const301 bool VpxPesParser::RemoveStartCodeEmulationPreventionBytes(
302     const std::uint8_t* raw_data, std::size_t bytes_required,
303     PacketData* processed_data, std::size_t* bytes_consumed) const {
304   if (bytes_required == 0 || !processed_data)
305     return false;
306 
307   std::size_t num_zeros = 0;
308   std::size_t bytes_copied = 0;
309   const std::uint8_t* const end_of_input =
310       &pes_file_data_[0] + pes_file_data_.size();
311   std::size_t i;
312   for (i = 0; bytes_copied < bytes_required; ++i) {
313     if (raw_data + i > end_of_input)
314       return false;
315 
316     bool skip = false;
317 
318     const std::uint8_t byte = raw_data[i];
319     if (byte == 0) {
320       ++num_zeros;
321     } else if (byte == 0x3 && num_zeros == 2) {
322       skip = true;
323       num_zeros = 0;
324     } else {
325       num_zeros = 0;
326     }
327 
328     if (skip == false) {
329       processed_data->push_back(byte);
330       ++bytes_copied;
331     }
332   }
333   *bytes_consumed = i;
334   return true;
335 }
336 
BytesAvailable() const337 int VpxPesParser::BytesAvailable() const {
338   return static_cast<int>(pes_file_data_.size() - read_pos_);
339 }
340 
ParseNextPacket(PesHeader * header,VideoFrame * frame)341 bool VpxPesParser::ParseNextPacket(PesHeader* header, VideoFrame* frame) {
342   if (!header || !frame || parse_state_ != kFindStartCode ||
343       BytesAvailable() == 0) {
344     return false;
345   }
346 
347   std::size_t packet_start_pos = read_pos_;
348   if (!FindStartCode(read_pos_, &packet_start_pos)) {
349     return false;
350   }
351   parse_state_ = kParsePesHeader;
352   read_pos_ = packet_start_pos;
353 
354   if (!ParsePesHeader(header)) {
355     return false;
356   }
357   if (!ParsePesOptionalHeader(&header->opt_header)) {
358     return false;
359   }
360   if (!ParseBcmvHeader(&header->bcmv_header)) {
361     return false;
362   }
363 
364   // BCMV header length includes the length of the BCMVHeader itself. Adjust:
365   const std::size_t payload_length =
366       header->bcmv_header.length - BcmvHeader::size();
367 
368   // Make sure there's enough input data to read the entire frame.
369   if (read_pos_ + payload_length > pes_file_data_.size()) {
370     // Need more data.
371     printf("VpxPesParser: Not enough data. Required: %u Available: %u\n",
372            static_cast<unsigned int>(payload_length),
373            static_cast<unsigned int>(pes_file_data_.size() - read_pos_));
374     parse_state_ = kFindStartCode;
375     read_pos_ = packet_start_pos;
376     return false;
377   }
378 
379   if (IsPayloadFragmented(*header)) {
380     if (!AccumulateFragmentedPayload(header->packet_length, payload_length)) {
381       fprintf(stderr, "VpxPesParser: Failed parsing fragmented payload!\n");
382       return false;
383     }
384   } else {
385     std::size_t consumed = 0;
386     if (!RemoveStartCodeEmulationPreventionBytes(
387             &pes_file_data_[read_pos_], payload_length, &payload_, &consumed)) {
388       return false;
389     }
390     read_pos_ += consumed;
391   }
392 
393   if (frame->buffer().capacity < payload_.size()) {
394     if (frame->Init(payload_.size()) == false) {
395       fprintf(stderr, "VpxPesParser: Out of memory.\n");
396       return false;
397     }
398   }
399   frame->set_nanosecond_pts(Khz90TicksToNanoseconds(header->opt_header.pts));
400   std::memcpy(frame->buffer().data.get(), &payload_[0], payload_.size());
401   frame->SetBufferLength(payload_.size());
402 
403   payload_.clear();
404   parse_state_ = kFindStartCode;
405 
406   return true;
407 }
408 
409 }  // namespace libwebm
410