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