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