xref: /aosp_15_r20/external/libwebm/m2ts/webm2pes.cc (revision 103e46e4cd4b6efcf6001f23fa8665fb110abf8d)
1 // Copyright (c) 2015 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/webm2pes.h"
9 
10 #include <algorithm>
11 #include <cassert>
12 #include <cstdio>
13 #include <cstring>
14 #include <new>
15 #include <vector>
16 
17 #include "common/libwebm_util.h"
18 
19 namespace libwebm {
20 
21 const std::size_t Webm2Pes::kMaxPayloadSize = 32768;
22 
23 namespace {
24 
ToString(const char * str)25 std::string ToString(const char* str) {
26   return std::string((str == nullptr) ? "" : str);
27 }
28 
29 }  // namespace
30 
31 //
32 // PesOptionalHeader methods.
33 //
34 
SetPtsBits(std::int64_t pts_90khz)35 void PesOptionalHeader::SetPtsBits(std::int64_t pts_90khz) {
36   // PTS is broken up and stored in 40 bits as shown:
37   //
38   //  PES PTS Only flag
39   // /                  Marker              Marker              Marker
40   // |                 /                   /                   /
41   // |                 |                   |                   |
42   // 7654  321         0  765432107654321  0  765432107654321  0
43   // 0010  PTS 32-30   1  PTS 29-15        1  PTS 14-0         1
44   const std::uint32_t pts1 = (pts_90khz >> 30) & 0x7;
45   const std::uint32_t pts2 = (pts_90khz >> 15) & 0x7FFF;
46   const std::uint32_t pts3 = pts_90khz & 0x7FFF;
47 
48   pts.bits = 0;
49 
50   // bottom 7 bits of second PTS chunk.
51   pts.bits |= (pts3 << 1) & 0xff;
52   // Marker.
53   pts.bits |= 1;
54 
55   // Last 15 bits of pts and 1 bit marker.
56   // Top 8 bits of second PTS chunk.
57   pts.bits <<= 8;
58   pts.bits |= (pts3 >> 7) & 0xff;
59 
60   // bottom 7 bits of second PTS chunk.
61   pts.bits <<= 8;
62   pts.bits |= (pts2 << 1);
63   // Marker.
64   pts.bits |= 1;
65 
66   // Next 15 bits of pts and 1 bit marker.
67   // Top 8 bits of second PTS chunk.
68   pts.bits <<= 8;
69   pts.bits |= (pts2 >> 7) & 0xff;
70 
71   // PTS only flag.
72   pts.bits <<= 8;
73   pts.bits |= 1 << 5;
74   // Top 3 bits of PTS and 1 bit marker.
75   pts.bits |= pts1 << 1;
76   // Marker.
77   pts.bits |= 1;
78 }
79 
80 // Writes fields to |buffer| and returns true. Returns false when write or
81 // field value validation fails.
Write(bool write_pts,PacketDataBuffer * buffer) const82 bool PesOptionalHeader::Write(bool write_pts, PacketDataBuffer* buffer) const {
83   if (buffer == nullptr) {
84     std::fprintf(stderr, "Webm2Pes: nullptr in opt header writer.\n");
85     return false;
86   }
87 
88   const int kHeaderSize = 9;
89   std::uint8_t header[kHeaderSize] = {0};
90   std::uint8_t* byte = header;
91 
92   if (marker.Check() != true || scrambling.Check() != true ||
93       priority.Check() != true || data_alignment.Check() != true ||
94       copyright.Check() != true || original.Check() != true ||
95       has_pts.Check() != true || has_dts.Check() != true ||
96       pts.Check() != true || stuffing_byte.Check() != true) {
97     std::fprintf(stderr, "Webm2Pes: Invalid PES Optional Header field.\n");
98     return false;
99   }
100 
101   // TODO(tomfinegan): As noted in above, the PesHeaderFields should be an
102   // array (or some data structure) that can be iterated over.
103 
104   // First byte of header, fields: marker, scrambling, priority, alignment,
105   // copyright, original.
106   *byte = 0;
107   *byte |= marker.bits << marker.shift;
108   *byte |= scrambling.bits << scrambling.shift;
109   *byte |= priority.bits << priority.shift;
110   *byte |= data_alignment.bits << data_alignment.shift;
111   *byte |= copyright.bits << copyright.shift;
112   *byte |= original.bits << original.shift;
113 
114   // Second byte of header, fields: has_pts, has_dts, unused fields.
115   *++byte = 0;
116   if (write_pts == true)
117     *byte |= has_pts.bits << has_pts.shift;
118 
119   *byte |= has_dts.bits << has_dts.shift;
120 
121   // Third byte of header, fields: remaining size of header.
122   *++byte = remaining_size.bits & 0xff;  // Field is 8 bits wide.
123 
124   int num_stuffing_bytes =
125       (pts.num_bits + 7) / 8 + 1 /* always 1 stuffing byte */;
126   if (write_pts == true) {
127     // Write the PTS value as big endian and adjust stuffing byte count
128     // accordingly.
129     *++byte = pts.bits & 0xff;
130     *++byte = (pts.bits >> 8) & 0xff;
131     *++byte = (pts.bits >> 16) & 0xff;
132     *++byte = (pts.bits >> 24) & 0xff;
133     *++byte = (pts.bits >> 32) & 0xff;
134     num_stuffing_bytes = 1;
135   }
136 
137   // Add the stuffing byte(s).
138   for (int i = 0; i < num_stuffing_bytes; ++i)
139     *++byte = stuffing_byte.bits & 0xff;
140 
141   return CopyAndEscapeStartCodes(&header[0], kHeaderSize, buffer);
142 }
143 
144 //
145 // BCMVHeader methods.
146 //
147 
Write(PacketDataBuffer * buffer) const148 bool BCMVHeader::Write(PacketDataBuffer* buffer) const {
149   if (buffer == nullptr) {
150     std::fprintf(stderr, "Webm2Pes: nullptr for buffer in BCMV Write.\n");
151     return false;
152   }
153   const int kBcmvSize = 4;
154   for (int i = 0; i < kBcmvSize; ++i)
155     buffer->push_back(bcmv[i]);
156 
157   // Note: The 4 byte length field must include the size of the BCMV header.
158   const int kRemainingBytes = 6;
159   const uint32_t bcmv_total_length = length + static_cast<uint32_t>(size());
160   const uint8_t bcmv_buffer[kRemainingBytes] = {
161       static_cast<std::uint8_t>((bcmv_total_length >> 24) & 0xff),
162       static_cast<std::uint8_t>((bcmv_total_length >> 16) & 0xff),
163       static_cast<std::uint8_t>((bcmv_total_length >> 8) & 0xff),
164       static_cast<std::uint8_t>(bcmv_total_length & 0xff),
165       0,
166       0 /* 2 bytes 0 padding */};
167 
168   return CopyAndEscapeStartCodes(bcmv_buffer, kRemainingBytes, buffer);
169 }
170 
171 //
172 // PesHeader methods.
173 //
174 
175 // Writes out the header to |buffer|. Calls PesOptionalHeader::Write() to write
176 // |optional_header| contents. Returns true when successful, false otherwise.
Write(bool write_pts,PacketDataBuffer * buffer) const177 bool PesHeader::Write(bool write_pts, PacketDataBuffer* buffer) const {
178   if (buffer == nullptr) {
179     std::fprintf(stderr, "Webm2Pes: nullptr in header writer.\n");
180     return false;
181   }
182 
183   // Write |start_code|.
184   const int kStartCodeLength = 4;
185   for (int i = 0; i < kStartCodeLength; ++i)
186     buffer->push_back(start_code[i]);
187 
188   // The length field here reports number of bytes following the field. The
189   // length of the optional header must be added to the payload length set by
190   // the user.
191   const std::size_t header_length =
192       packet_length + optional_header.size_in_bytes();
193   if (header_length > UINT16_MAX)
194     return false;
195 
196   // Write |header_length| as big endian.
197   std::uint8_t byte = (header_length >> 8) & 0xff;
198   buffer->push_back(byte);
199   byte = header_length & 0xff;
200   buffer->push_back(byte);
201 
202   // Write the (not really) optional header.
203   if (optional_header.Write(write_pts, buffer) != true) {
204     std::fprintf(stderr, "Webm2Pes: PES optional header write failed.");
205     return false;
206   }
207   return true;
208 }
209 
210 //
211 // Webm2Pes methods.
212 //
213 
ConvertToFile()214 bool Webm2Pes::ConvertToFile() {
215   if (input_file_name_.empty() || output_file_name_.empty()) {
216     std::fprintf(stderr, "Webm2Pes: input and/or output file name(s) empty.\n");
217     return false;
218   }
219 
220   output_file_ = FilePtr(fopen(output_file_name_.c_str(), "wb"), FILEDeleter());
221   if (output_file_ == nullptr) {
222     std::fprintf(stderr, "Webm2Pes: Cannot open %s for output.\n",
223                  output_file_name_.c_str());
224     return false;
225   }
226 
227   if (InitWebmParser() != true) {
228     std::fprintf(stderr, "Webm2Pes: Cannot initialize WebM parser.\n");
229     return false;
230   }
231 
232   // Walk clusters in segment.
233   const mkvparser::Cluster* cluster = webm_parser_->GetFirst();
234   while (cluster != nullptr && cluster->EOS() == false) {
235     const mkvparser::BlockEntry* block_entry = nullptr;
236     std::int64_t block_status = cluster->GetFirst(block_entry);
237     if (block_status < 0) {
238       std::fprintf(stderr, "Webm2Pes: Cannot parse first block in %s.\n",
239                    input_file_name_.c_str());
240       return false;
241     }
242 
243     // Walk blocks in cluster.
244     while (block_entry != nullptr && block_entry->EOS() == false) {
245       const mkvparser::Block* block = block_entry->GetBlock();
246       if (block->GetTrackNumber() == video_track_num_) {
247         const int frame_count = block->GetFrameCount();
248 
249         // Walk frames in block.
250         for (int frame_num = 0; frame_num < frame_count; ++frame_num) {
251           const mkvparser::Block::Frame& mkvparser_frame =
252               block->GetFrame(frame_num);
253 
254           // Read the frame.
255           VideoFrame vpx_frame(block->GetTime(cluster), codec_);
256           if (ReadVideoFrame(mkvparser_frame, &vpx_frame) == false) {
257             fprintf(stderr, "Webm2Pes: frame read failed.\n");
258             return false;
259           }
260 
261           // Write frame out as PES packet(s).
262           if (WritePesPacket(vpx_frame, &packet_data_) == false) {
263             std::fprintf(stderr, "Webm2Pes: WritePesPacket failed.\n");
264             return false;
265           }
266 
267           // Write contents of |packet_data_| to |output_file_|.
268           if (std::fwrite(&packet_data_[0], 1, packet_data_.size(),
269                           output_file_.get()) != packet_data_.size()) {
270             std::fprintf(stderr, "Webm2Pes: packet payload write failed.\n");
271             return false;
272           }
273           bytes_written_ += packet_data_.size();
274         }
275       }
276       block_status = cluster->GetNext(block_entry, block_entry);
277       if (block_status < 0) {
278         std::fprintf(stderr, "Webm2Pes: Cannot parse block in %s.\n",
279                      input_file_name_.c_str());
280         return false;
281       }
282     }
283 
284     cluster = webm_parser_->GetNext(cluster);
285   }
286 
287   std::fflush(output_file_.get());
288   return true;
289 }
290 
ConvertToPacketReceiver()291 bool Webm2Pes::ConvertToPacketReceiver() {
292   if (input_file_name_.empty() || packet_sink_ == nullptr) {
293     std::fprintf(stderr, "Webm2Pes: input file name empty or null sink.\n");
294     return false;
295   }
296 
297   if (InitWebmParser() != true) {
298     std::fprintf(stderr, "Webm2Pes: Cannot initialize WebM parser.\n");
299     return false;
300   }
301 
302   // Walk clusters in segment.
303   const mkvparser::Cluster* cluster = webm_parser_->GetFirst();
304   while (cluster != nullptr && cluster->EOS() == false) {
305     const mkvparser::BlockEntry* block_entry = nullptr;
306     std::int64_t block_status = cluster->GetFirst(block_entry);
307     if (block_status < 0) {
308       std::fprintf(stderr, "Webm2Pes: Cannot parse first block in %s.\n",
309                    input_file_name_.c_str());
310       return false;
311     }
312 
313     // Walk blocks in cluster.
314     while (block_entry != nullptr && block_entry->EOS() == false) {
315       const mkvparser::Block* block = block_entry->GetBlock();
316       if (block->GetTrackNumber() == video_track_num_) {
317         const int frame_count = block->GetFrameCount();
318 
319         // Walk frames in block.
320         for (int frame_num = 0; frame_num < frame_count; ++frame_num) {
321           const mkvparser::Block::Frame& mkvparser_frame =
322               block->GetFrame(frame_num);
323 
324           // Read the frame.
325           VideoFrame frame(block->GetTime(cluster), codec_);
326           if (ReadVideoFrame(mkvparser_frame, &frame) == false) {
327             fprintf(stderr, "Webm2Pes: frame read failed.\n");
328             return false;
329           }
330 
331           // Write frame out as PES packet(s).
332           if (WritePesPacket(frame, &packet_data_) == false) {
333             std::fprintf(stderr, "Webm2Pes: WritePesPacket failed.\n");
334             return false;
335           }
336           if (packet_sink_->ReceivePacket(packet_data_) != true) {
337             std::fprintf(stderr, "Webm2Pes: ReceivePacket failed.\n");
338             return false;
339           }
340           bytes_written_ += packet_data_.size();
341         }
342       }
343       block_status = cluster->GetNext(block_entry, block_entry);
344       if (block_status < 0) {
345         std::fprintf(stderr, "Webm2Pes: Cannot parse block in %s.\n",
346                      input_file_name_.c_str());
347         return false;
348       }
349     }
350 
351     cluster = webm_parser_->GetNext(cluster);
352   }
353 
354   return true;
355 }
356 
InitWebmParser()357 bool Webm2Pes::InitWebmParser() {
358   if (webm_reader_.Open(input_file_name_.c_str()) != 0) {
359     std::fprintf(stderr, "Webm2Pes: Cannot open %s as input.\n",
360                  input_file_name_.c_str());
361     return false;
362   }
363 
364   using mkvparser::Segment;
365   Segment* webm_parser = nullptr;
366   if (Segment::CreateInstance(&webm_reader_, 0 /* pos */,
367                               webm_parser /* Segment*& */) != 0) {
368     std::fprintf(stderr, "Webm2Pes: Cannot create WebM parser.\n");
369     return false;
370   }
371   webm_parser_.reset(webm_parser);
372 
373   if (webm_parser_->Load() != 0) {
374     std::fprintf(stderr, "Webm2Pes: Cannot parse %s.\n",
375                  input_file_name_.c_str());
376     return false;
377   }
378 
379   // Make sure there's a video track.
380   const mkvparser::Tracks* tracks = webm_parser_->GetTracks();
381   if (tracks == nullptr) {
382     std::fprintf(stderr, "Webm2Pes: %s has no tracks.\n",
383                  input_file_name_.c_str());
384     return false;
385   }
386 
387   timecode_scale_ = webm_parser_->GetInfo()->GetTimeCodeScale();
388 
389   for (int track_index = 0;
390        track_index < static_cast<int>(tracks->GetTracksCount());
391        ++track_index) {
392     const mkvparser::Track* track = tracks->GetTrackByIndex(track_index);
393     if (track && track->GetType() == mkvparser::Track::kVideo) {
394       const std::string codec_id = ToString(track->GetCodecId());
395       if (codec_id == std::string("V_VP8")) {
396         codec_ = VideoFrame::kVP8;
397       } else if (codec_id == std::string("V_VP9")) {
398         codec_ = VideoFrame::kVP9;
399       } else {
400         fprintf(stderr, "Webm2Pes: Codec must be VP8 or VP9.\n");
401         return false;
402       }
403       video_track_num_ = track_index + 1;
404       break;
405     }
406   }
407   if (video_track_num_ < 1) {
408     std::fprintf(stderr, "Webm2Pes: No video track found in %s.\n",
409                  input_file_name_.c_str());
410     return false;
411   }
412   return true;
413 }
414 
ReadVideoFrame(const mkvparser::Block::Frame & mkvparser_frame,VideoFrame * frame)415 bool Webm2Pes::ReadVideoFrame(const mkvparser::Block::Frame& mkvparser_frame,
416                               VideoFrame* frame) {
417   if (mkvparser_frame.len < 1 || frame == nullptr)
418     return false;
419 
420   const std::size_t mkv_len = static_cast<std::size_t>(mkvparser_frame.len);
421   if (mkv_len > frame->buffer().capacity) {
422     const std::size_t new_size = 2 * mkv_len;
423     if (frame->Init(new_size) == false) {
424       std::fprintf(stderr, "Webm2Pes: Out of memory.\n");
425       return false;
426     }
427   }
428   if (mkvparser_frame.Read(&webm_reader_, frame->buffer().data.get()) != 0) {
429     std::fprintf(stderr, "Webm2Pes: Error reading VPx frame!\n");
430     return false;
431   }
432   return frame->SetBufferLength(mkv_len);
433 }
434 
WritePesPacket(const VideoFrame & frame,PacketDataBuffer * packet_data)435 bool Webm2Pes::WritePesPacket(const VideoFrame& frame,
436                               PacketDataBuffer* packet_data) {
437   if (frame.buffer().data.get() == nullptr || frame.buffer().length < 1)
438     return false;
439 
440   Ranges frame_ranges;
441   if (frame.codec() == VideoFrame::kVP9) {
442     bool error = false;
443     const bool has_superframe_index =
444         ParseVP9SuperFrameIndex(frame.buffer().data.get(),
445                                 frame.buffer().length, &frame_ranges, &error);
446     if (error) {
447       std::fprintf(stderr, "Webm2Pes: Superframe index parse failed.\n");
448       return false;
449     }
450     if (has_superframe_index == false) {
451       frame_ranges.push_back(Range(0, frame.buffer().length));
452     }
453   } else {
454     frame_ranges.push_back(Range(0, frame.buffer().length));
455   }
456 
457   const std::int64_t khz90_pts =
458       NanosecondsTo90KhzTicks(frame.nanosecond_pts());
459   PesHeader header;
460   header.optional_header.SetPtsBits(khz90_pts);
461 
462   packet_data->clear();
463 
464   for (const Range& packet_payload_range : frame_ranges) {
465     std::size_t extra_bytes = 0;
466     if (packet_payload_range.length > kMaxPayloadSize) {
467       extra_bytes = packet_payload_range.length - kMaxPayloadSize;
468     }
469     if (packet_payload_range.length + packet_payload_range.offset >
470         frame.buffer().length) {
471       std::fprintf(stderr, "Webm2Pes: Invalid frame length.\n");
472       return false;
473     }
474 
475     // First packet of new frame. Always include PTS and BCMV header.
476     header.packet_length =
477         packet_payload_range.length - extra_bytes + BCMVHeader::size();
478     if (header.Write(true, packet_data) != true) {
479       std::fprintf(stderr, "Webm2Pes: packet header write failed.\n");
480       return false;
481     }
482 
483     BCMVHeader bcmv_header(static_cast<uint32_t>(packet_payload_range.length));
484     if (bcmv_header.Write(packet_data) != true) {
485       std::fprintf(stderr, "Webm2Pes: BCMV write failed.\n");
486       return false;
487     }
488 
489     // Insert the payload at the end of |packet_data|.
490     const std::uint8_t* const payload_start =
491         frame.buffer().data.get() + packet_payload_range.offset;
492 
493     const std::size_t bytes_to_copy = packet_payload_range.length - extra_bytes;
494     if (CopyAndEscapeStartCodes(payload_start, bytes_to_copy, packet_data) ==
495         false) {
496       fprintf(stderr, "Webm2Pes: Payload write failed.\n");
497       return false;
498     }
499 
500     std::size_t bytes_copied = bytes_to_copy;
501     while (extra_bytes) {
502       // Write PES packets for the remaining data, but omit the PTS and BCMV
503       // header.
504       const std::size_t extra_bytes_to_copy =
505           std::min(kMaxPayloadSize, extra_bytes);
506       extra_bytes -= extra_bytes_to_copy;
507       header.packet_length = extra_bytes_to_copy;
508       if (header.Write(false, packet_data) != true) {
509         fprintf(stderr, "Webm2pes: fragment write failed.\n");
510         return false;
511       }
512 
513       const std::uint8_t* fragment_start = payload_start + bytes_copied;
514       if (CopyAndEscapeStartCodes(fragment_start, extra_bytes_to_copy,
515                                   packet_data) == false) {
516         fprintf(stderr, "Webm2Pes: Payload write failed.\n");
517         return false;
518       }
519 
520       bytes_copied += extra_bytes_to_copy;
521     }
522   }
523 
524   return true;
525 }
526 
CopyAndEscapeStartCodes(const std::uint8_t * raw_input,std::size_t raw_input_length,PacketDataBuffer * packet_buffer)527 bool CopyAndEscapeStartCodes(const std::uint8_t* raw_input,
528                              std::size_t raw_input_length,
529                              PacketDataBuffer* packet_buffer) {
530   if (raw_input == nullptr || raw_input_length < 1 || packet_buffer == nullptr)
531     return false;
532 
533   int num_zeros = 0;
534   for (std::size_t i = 0; i < raw_input_length; ++i) {
535     const uint8_t byte = raw_input[i];
536 
537     if (byte == 0) {
538       ++num_zeros;
539     } else if (num_zeros >= 2 && (byte == 0x1 || byte == 0x3)) {
540       packet_buffer->push_back(0x3);
541       num_zeros = 0;
542     } else {
543       num_zeros = 0;
544     }
545 
546     packet_buffer->push_back(byte);
547   }
548 
549   return true;
550 }
551 
552 }  // namespace libwebm
553