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