1// Copyright (c) 2012 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 9#include "mkvmuxer/mkvmuxer.h" 10 11#include <cfloat> 12#include <climits> 13#include <cstdio> 14#include <cstdlib> 15#include <cstring> 16#include <ctime> 17#include <memory> 18#include <new> 19#include <string> 20#include <vector> 21 22#include "common/webmids.h" 23#include "mkvmuxer/mkvmuxerutil.h" 24#include "mkvmuxer/mkvwriter.h" 25#include "mkvparser/mkvparser.h" 26 27namespace mkvmuxer { 28 29const float PrimaryChromaticity::kChromaticityMin = 0.0f; 30const float PrimaryChromaticity::kChromaticityMax = 1.0f; 31const float MasteringMetadata::kMinLuminance = 0.0f; 32const float MasteringMetadata::kMinLuminanceMax = 999.99f; 33const float MasteringMetadata::kMaxLuminanceMax = 9999.99f; 34const float MasteringMetadata::kValueNotPresent = FLT_MAX; 35const uint64_t Colour::kValueNotPresent = UINT64_MAX; 36 37namespace { 38 39const char kDocTypeWebm[] = "webm"; 40const char kDocTypeMatroska[] = "matroska"; 41 42// Deallocate the string designated by |dst|, and then copy the |src| 43// string to |dst|. The caller owns both the |src| string and the 44// |dst| copy (hence the caller is responsible for eventually 45// deallocating the strings, either directly, or indirectly via 46// StrCpy). Returns true if the source string was successfully copied 47// to the destination. 48bool StrCpy(const char* src, char** dst_ptr) { 49 if (dst_ptr == NULL) 50 return false; 51 52 char*& dst = *dst_ptr; 53 54 delete[] dst; 55 dst = NULL; 56 57 if (src == NULL) 58 return true; 59 60 const size_t size = strlen(src) + 1; 61 62 dst = new (std::nothrow) char[size]; // NOLINT 63 if (dst == NULL) 64 return false; 65 66 strcpy(dst, src); // NOLINT 67 return true; 68} 69 70typedef std::auto_ptr<PrimaryChromaticity> PrimaryChromaticityPtr; 71bool CopyChromaticity(const PrimaryChromaticity* src, 72 PrimaryChromaticityPtr* dst) { 73 if (!dst) 74 return false; 75 76 dst->reset(new (std::nothrow) PrimaryChromaticity(src->x(), src->y())); 77 if (!dst->get()) 78 return false; 79 80 return true; 81} 82 83} // namespace 84 85/////////////////////////////////////////////////////////////// 86// 87// IMkvWriter Class 88 89IMkvWriter::IMkvWriter() {} 90 91IMkvWriter::~IMkvWriter() {} 92 93bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version, 94 const char* const doc_type) { 95 // Level 0 96 uint64_t size = 97 EbmlElementSize(libwebm::kMkvEBMLVersion, static_cast<uint64>(1)); 98 size += EbmlElementSize(libwebm::kMkvEBMLReadVersion, static_cast<uint64>(1)); 99 size += EbmlElementSize(libwebm::kMkvEBMLMaxIDLength, static_cast<uint64>(4)); 100 size += 101 EbmlElementSize(libwebm::kMkvEBMLMaxSizeLength, static_cast<uint64>(8)); 102 size += EbmlElementSize(libwebm::kMkvDocType, doc_type); 103 size += EbmlElementSize(libwebm::kMkvDocTypeVersion, 104 static_cast<uint64>(doc_type_version)); 105 size += 106 EbmlElementSize(libwebm::kMkvDocTypeReadVersion, static_cast<uint64>(2)); 107 108 if (!WriteEbmlMasterElement(writer, libwebm::kMkvEBML, size)) 109 return false; 110 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLVersion, 111 static_cast<uint64>(1))) { 112 return false; 113 } 114 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLReadVersion, 115 static_cast<uint64>(1))) { 116 return false; 117 } 118 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxIDLength, 119 static_cast<uint64>(4))) { 120 return false; 121 } 122 if (!WriteEbmlElement(writer, libwebm::kMkvEBMLMaxSizeLength, 123 static_cast<uint64>(8))) { 124 return false; 125 } 126 if (!WriteEbmlElement(writer, libwebm::kMkvDocType, doc_type)) 127 return false; 128 if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeVersion, 129 static_cast<uint64>(doc_type_version))) { 130 return false; 131 } 132 if (!WriteEbmlElement(writer, libwebm::kMkvDocTypeReadVersion, 133 static_cast<uint64>(2))) { 134 return false; 135 } 136 137 return true; 138} 139 140bool WriteEbmlHeader(IMkvWriter* writer, uint64_t doc_type_version) { 141 return WriteEbmlHeader(writer, doc_type_version, kDocTypeWebm); 142} 143 144bool WriteEbmlHeader(IMkvWriter* writer) { 145 return WriteEbmlHeader(writer, mkvmuxer::Segment::kDefaultDocTypeVersion); 146} 147 148bool ChunkedCopy(mkvparser::IMkvReader* source, mkvmuxer::IMkvWriter* dst, 149 int64_t start, int64_t size) { 150 // TODO(vigneshv): Check if this is a reasonable value. 151 const uint32_t kBufSize = 2048; 152 uint8_t* buf = new uint8_t[kBufSize]; 153 int64_t offset = start; 154 while (size > 0) { 155 const int64_t read_len = (size > kBufSize) ? kBufSize : size; 156 if (source->Read(offset, static_cast<long>(read_len), buf)) 157 return false; 158 dst->Write(buf, static_cast<uint32_t>(read_len)); 159 offset += read_len; 160 size -= read_len; 161 } 162 delete[] buf; 163 return true; 164} 165 166/////////////////////////////////////////////////////////////// 167// 168// Frame Class 169 170Frame::Frame() 171 : add_id_(0), 172 additional_(NULL), 173 additional_length_(0), 174 duration_(0), 175 duration_set_(false), 176 frame_(NULL), 177 is_key_(false), 178 length_(0), 179 track_number_(0), 180 timestamp_(0), 181 discard_padding_(0), 182 reference_block_timestamp_(0), 183 reference_block_timestamp_set_(false) {} 184 185Frame::~Frame() { 186 delete[] frame_; 187 delete[] additional_; 188} 189 190bool Frame::CopyFrom(const Frame& frame) { 191 delete[] frame_; 192 frame_ = NULL; 193 length_ = 0; 194 if (frame.length() > 0 && frame.frame() != NULL && 195 !Init(frame.frame(), frame.length())) { 196 return false; 197 } 198 add_id_ = 0; 199 delete[] additional_; 200 additional_ = NULL; 201 additional_length_ = 0; 202 if (frame.additional_length() > 0 && frame.additional() != NULL && 203 !AddAdditionalData(frame.additional(), frame.additional_length(), 204 frame.add_id())) { 205 return false; 206 } 207 duration_ = frame.duration(); 208 duration_set_ = frame.duration_set(); 209 is_key_ = frame.is_key(); 210 track_number_ = frame.track_number(); 211 timestamp_ = frame.timestamp(); 212 discard_padding_ = frame.discard_padding(); 213 reference_block_timestamp_ = frame.reference_block_timestamp(); 214 reference_block_timestamp_set_ = frame.reference_block_timestamp_set(); 215 return true; 216} 217 218bool Frame::Init(const uint8_t* frame, uint64_t length) { 219 uint8_t* const data = 220 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT 221 if (!data) 222 return false; 223 224 delete[] frame_; 225 frame_ = data; 226 length_ = length; 227 228 memcpy(frame_, frame, static_cast<size_t>(length_)); 229 return true; 230} 231 232bool Frame::AddAdditionalData(const uint8_t* additional, uint64_t length, 233 uint64_t add_id) { 234 uint8_t* const data = 235 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT 236 if (!data) 237 return false; 238 239 delete[] additional_; 240 additional_ = data; 241 additional_length_ = length; 242 add_id_ = add_id; 243 244 memcpy(additional_, additional, static_cast<size_t>(additional_length_)); 245 return true; 246} 247 248bool Frame::IsValid() const { 249 if (length_ == 0 || !frame_) { 250 return false; 251 } 252 if ((additional_length_ != 0 && !additional_) || 253 (additional_ != NULL && additional_length_ == 0)) { 254 return false; 255 } 256 if (track_number_ == 0 || track_number_ > kMaxTrackNumber) { 257 return false; 258 } 259 if (!CanBeSimpleBlock() && !is_key_ && !reference_block_timestamp_set_) { 260 return false; 261 } 262 return true; 263} 264 265bool Frame::CanBeSimpleBlock() const { 266 return additional_ == NULL && discard_padding_ == 0 && duration_ == 0; 267} 268 269void Frame::set_duration(uint64_t duration) { 270 duration_ = duration; 271 duration_set_ = true; 272} 273 274void Frame::set_reference_block_timestamp(int64_t reference_block_timestamp) { 275 reference_block_timestamp_ = reference_block_timestamp; 276 reference_block_timestamp_set_ = true; 277} 278 279/////////////////////////////////////////////////////////////// 280// 281// CuePoint Class 282 283CuePoint::CuePoint() 284 : time_(0), 285 track_(0), 286 cluster_pos_(0), 287 block_number_(1), 288 output_block_number_(true) {} 289 290CuePoint::~CuePoint() {} 291 292bool CuePoint::Write(IMkvWriter* writer) const { 293 if (!writer || track_ < 1 || cluster_pos_ < 1) 294 return false; 295 296 uint64_t size = EbmlElementSize(libwebm::kMkvCueClusterPosition, 297 static_cast<uint64>(cluster_pos_)); 298 size += EbmlElementSize(libwebm::kMkvCueTrack, static_cast<uint64>(track_)); 299 if (output_block_number_ && block_number_ > 1) 300 size += EbmlElementSize(libwebm::kMkvCueBlockNumber, 301 static_cast<uint64>(block_number_)); 302 const uint64_t track_pos_size = 303 EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size; 304 const uint64_t payload_size = 305 EbmlElementSize(libwebm::kMkvCueTime, static_cast<uint64>(time_)) + 306 track_pos_size; 307 308 if (!WriteEbmlMasterElement(writer, libwebm::kMkvCuePoint, payload_size)) 309 return false; 310 311 const int64_t payload_position = writer->Position(); 312 if (payload_position < 0) 313 return false; 314 315 if (!WriteEbmlElement(writer, libwebm::kMkvCueTime, 316 static_cast<uint64>(time_))) { 317 return false; 318 } 319 320 if (!WriteEbmlMasterElement(writer, libwebm::kMkvCueTrackPositions, size)) 321 return false; 322 if (!WriteEbmlElement(writer, libwebm::kMkvCueTrack, 323 static_cast<uint64>(track_))) { 324 return false; 325 } 326 if (!WriteEbmlElement(writer, libwebm::kMkvCueClusterPosition, 327 static_cast<uint64>(cluster_pos_))) { 328 return false; 329 } 330 if (output_block_number_ && block_number_ > 1) { 331 if (!WriteEbmlElement(writer, libwebm::kMkvCueBlockNumber, 332 static_cast<uint64>(block_number_))) { 333 return false; 334 } 335 } 336 337 const int64_t stop_position = writer->Position(); 338 if (stop_position < 0) 339 return false; 340 341 if (stop_position - payload_position != static_cast<int64_t>(payload_size)) 342 return false; 343 344 return true; 345} 346 347uint64_t CuePoint::PayloadSize() const { 348 uint64_t size = EbmlElementSize(libwebm::kMkvCueClusterPosition, 349 static_cast<uint64>(cluster_pos_)); 350 size += EbmlElementSize(libwebm::kMkvCueTrack, static_cast<uint64>(track_)); 351 if (output_block_number_ && block_number_ > 1) 352 size += EbmlElementSize(libwebm::kMkvCueBlockNumber, 353 static_cast<uint64>(block_number_)); 354 const uint64_t track_pos_size = 355 EbmlMasterElementSize(libwebm::kMkvCueTrackPositions, size) + size; 356 const uint64_t payload_size = 357 EbmlElementSize(libwebm::kMkvCueTime, static_cast<uint64>(time_)) + 358 track_pos_size; 359 360 return payload_size; 361} 362 363uint64_t CuePoint::Size() const { 364 const uint64_t payload_size = PayloadSize(); 365 return EbmlMasterElementSize(libwebm::kMkvCuePoint, payload_size) + 366 payload_size; 367} 368 369/////////////////////////////////////////////////////////////// 370// 371// Cues Class 372 373Cues::Cues() 374 : cue_entries_capacity_(0), 375 cue_entries_size_(0), 376 cue_entries_(NULL), 377 output_block_number_(true) {} 378 379Cues::~Cues() { 380 if (cue_entries_) { 381 for (int32_t i = 0; i < cue_entries_size_; ++i) { 382 CuePoint* const cue = cue_entries_[i]; 383 delete cue; 384 } 385 delete[] cue_entries_; 386 } 387} 388 389bool Cues::AddCue(CuePoint* cue) { 390 if (!cue) 391 return false; 392 393 if ((cue_entries_size_ + 1) > cue_entries_capacity_) { 394 // Add more CuePoints. 395 const int32_t new_capacity = 396 (!cue_entries_capacity_) ? 2 : cue_entries_capacity_ * 2; 397 398 if (new_capacity < 1) 399 return false; 400 401 CuePoint** const cues = 402 new (std::nothrow) CuePoint*[new_capacity]; // NOLINT 403 if (!cues) 404 return false; 405 406 for (int32_t i = 0; i < cue_entries_size_; ++i) { 407 cues[i] = cue_entries_[i]; 408 } 409 410 delete[] cue_entries_; 411 412 cue_entries_ = cues; 413 cue_entries_capacity_ = new_capacity; 414 } 415 416 cue->set_output_block_number(output_block_number_); 417 cue_entries_[cue_entries_size_++] = cue; 418 return true; 419} 420 421CuePoint* Cues::GetCueByIndex(int32_t index) const { 422 if (cue_entries_ == NULL) 423 return NULL; 424 425 if (index >= cue_entries_size_) 426 return NULL; 427 428 return cue_entries_[index]; 429} 430 431uint64_t Cues::Size() { 432 uint64_t size = 0; 433 for (int32_t i = 0; i < cue_entries_size_; ++i) 434 size += GetCueByIndex(i)->Size(); 435 size += EbmlMasterElementSize(libwebm::kMkvCues, size); 436 return size; 437} 438 439bool Cues::Write(IMkvWriter* writer) const { 440 if (!writer) 441 return false; 442 443 uint64_t size = 0; 444 for (int32_t i = 0; i < cue_entries_size_; ++i) { 445 const CuePoint* const cue = GetCueByIndex(i); 446 447 if (!cue) 448 return false; 449 450 size += cue->Size(); 451 } 452 453 if (!WriteEbmlMasterElement(writer, libwebm::kMkvCues, size)) 454 return false; 455 456 const int64_t payload_position = writer->Position(); 457 if (payload_position < 0) 458 return false; 459 460 for (int32_t i = 0; i < cue_entries_size_; ++i) { 461 const CuePoint* const cue = GetCueByIndex(i); 462 463 if (!cue->Write(writer)) 464 return false; 465 } 466 467 const int64_t stop_position = writer->Position(); 468 if (stop_position < 0) 469 return false; 470 471 if (stop_position - payload_position != static_cast<int64_t>(size)) 472 return false; 473 474 return true; 475} 476 477/////////////////////////////////////////////////////////////// 478// 479// ContentEncAESSettings Class 480 481ContentEncAESSettings::ContentEncAESSettings() : cipher_mode_(kCTR) {} 482 483uint64_t ContentEncAESSettings::Size() const { 484 const uint64_t payload = PayloadSize(); 485 const uint64_t size = 486 EbmlMasterElementSize(libwebm::kMkvContentEncAESSettings, payload) + 487 payload; 488 return size; 489} 490 491bool ContentEncAESSettings::Write(IMkvWriter* writer) const { 492 const uint64_t payload = PayloadSize(); 493 494 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncAESSettings, 495 payload)) 496 return false; 497 498 const int64_t payload_position = writer->Position(); 499 if (payload_position < 0) 500 return false; 501 502 if (!WriteEbmlElement(writer, libwebm::kMkvAESSettingsCipherMode, 503 static_cast<uint64>(cipher_mode_))) { 504 return false; 505 } 506 507 const int64_t stop_position = writer->Position(); 508 if (stop_position < 0 || 509 stop_position - payload_position != static_cast<int64_t>(payload)) 510 return false; 511 512 return true; 513} 514 515uint64_t ContentEncAESSettings::PayloadSize() const { 516 uint64_t size = EbmlElementSize(libwebm::kMkvAESSettingsCipherMode, 517 static_cast<uint64>(cipher_mode_)); 518 return size; 519} 520 521/////////////////////////////////////////////////////////////// 522// 523// ContentEncoding Class 524 525ContentEncoding::ContentEncoding() 526 : enc_algo_(5), 527 enc_key_id_(NULL), 528 encoding_order_(0), 529 encoding_scope_(1), 530 encoding_type_(1), 531 enc_key_id_length_(0) {} 532 533ContentEncoding::~ContentEncoding() { delete[] enc_key_id_; } 534 535bool ContentEncoding::SetEncryptionID(const uint8_t* id, uint64_t length) { 536 if (!id || length < 1) 537 return false; 538 539 delete[] enc_key_id_; 540 541 enc_key_id_ = 542 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT 543 if (!enc_key_id_) 544 return false; 545 546 memcpy(enc_key_id_, id, static_cast<size_t>(length)); 547 enc_key_id_length_ = length; 548 549 return true; 550} 551 552uint64_t ContentEncoding::Size() const { 553 const uint64_t encryption_size = EncryptionSize(); 554 const uint64_t encoding_size = EncodingSize(0, encryption_size); 555 const uint64_t encodings_size = 556 EbmlMasterElementSize(libwebm::kMkvContentEncoding, encoding_size) + 557 encoding_size; 558 559 return encodings_size; 560} 561 562bool ContentEncoding::Write(IMkvWriter* writer) const { 563 const uint64_t encryption_size = EncryptionSize(); 564 const uint64_t encoding_size = EncodingSize(0, encryption_size); 565 const uint64_t size = 566 EbmlMasterElementSize(libwebm::kMkvContentEncoding, encoding_size) + 567 encoding_size; 568 569 const int64_t payload_position = writer->Position(); 570 if (payload_position < 0) 571 return false; 572 573 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncoding, 574 encoding_size)) 575 return false; 576 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingOrder, 577 static_cast<uint64>(encoding_order_))) 578 return false; 579 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingScope, 580 static_cast<uint64>(encoding_scope_))) 581 return false; 582 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncodingType, 583 static_cast<uint64>(encoding_type_))) 584 return false; 585 586 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncryption, 587 encryption_size)) 588 return false; 589 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncAlgo, 590 static_cast<uint64>(enc_algo_))) { 591 return false; 592 } 593 if (!WriteEbmlElement(writer, libwebm::kMkvContentEncKeyID, enc_key_id_, 594 enc_key_id_length_)) 595 return false; 596 597 if (!enc_aes_settings_.Write(writer)) 598 return false; 599 600 const int64_t stop_position = writer->Position(); 601 if (stop_position < 0 || 602 stop_position - payload_position != static_cast<int64_t>(size)) 603 return false; 604 605 return true; 606} 607 608uint64_t ContentEncoding::EncodingSize(uint64_t compresion_size, 609 uint64_t encryption_size) const { 610 // TODO(fgalligan): Add support for compression settings. 611 if (compresion_size != 0) 612 return 0; 613 614 uint64_t encoding_size = 0; 615 616 if (encryption_size > 0) { 617 encoding_size += 618 EbmlMasterElementSize(libwebm::kMkvContentEncryption, encryption_size) + 619 encryption_size; 620 } 621 encoding_size += EbmlElementSize(libwebm::kMkvContentEncodingType, 622 static_cast<uint64>(encoding_type_)); 623 encoding_size += EbmlElementSize(libwebm::kMkvContentEncodingScope, 624 static_cast<uint64>(encoding_scope_)); 625 encoding_size += EbmlElementSize(libwebm::kMkvContentEncodingOrder, 626 static_cast<uint64>(encoding_order_)); 627 628 return encoding_size; 629} 630 631uint64_t ContentEncoding::EncryptionSize() const { 632 const uint64_t aes_size = enc_aes_settings_.Size(); 633 634 uint64_t encryption_size = EbmlElementSize(libwebm::kMkvContentEncKeyID, 635 enc_key_id_, enc_key_id_length_); 636 encryption_size += EbmlElementSize(libwebm::kMkvContentEncAlgo, 637 static_cast<uint64>(enc_algo_)); 638 639 return encryption_size + aes_size; 640} 641 642/////////////////////////////////////////////////////////////// 643// 644// Track Class 645 646Track::Track(unsigned int* seed) 647 : codec_id_(NULL), 648 codec_private_(NULL), 649 language_(NULL), 650 max_block_additional_id_(0), 651 name_(NULL), 652 number_(0), 653 type_(0), 654 uid_(MakeUID(seed)), 655 codec_delay_(0), 656 seek_pre_roll_(0), 657 default_duration_(0), 658 codec_private_length_(0), 659 content_encoding_entries_(NULL), 660 content_encoding_entries_size_(0) {} 661 662Track::~Track() { 663 delete[] codec_id_; 664 delete[] codec_private_; 665 delete[] language_; 666 delete[] name_; 667 668 if (content_encoding_entries_) { 669 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { 670 ContentEncoding* const encoding = content_encoding_entries_[i]; 671 delete encoding; 672 } 673 delete[] content_encoding_entries_; 674 } 675} 676 677bool Track::AddContentEncoding() { 678 const uint32_t count = content_encoding_entries_size_ + 1; 679 680 ContentEncoding** const content_encoding_entries = 681 new (std::nothrow) ContentEncoding*[count]; // NOLINT 682 if (!content_encoding_entries) 683 return false; 684 685 ContentEncoding* const content_encoding = 686 new (std::nothrow) ContentEncoding(); // NOLINT 687 if (!content_encoding) { 688 delete[] content_encoding_entries; 689 return false; 690 } 691 692 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { 693 content_encoding_entries[i] = content_encoding_entries_[i]; 694 } 695 696 delete[] content_encoding_entries_; 697 698 content_encoding_entries_ = content_encoding_entries; 699 content_encoding_entries_[content_encoding_entries_size_] = content_encoding; 700 content_encoding_entries_size_ = count; 701 return true; 702} 703 704ContentEncoding* Track::GetContentEncodingByIndex(uint32_t index) const { 705 if (content_encoding_entries_ == NULL) 706 return NULL; 707 708 if (index >= content_encoding_entries_size_) 709 return NULL; 710 711 return content_encoding_entries_[index]; 712} 713 714uint64_t Track::PayloadSize() const { 715 uint64_t size = 716 EbmlElementSize(libwebm::kMkvTrackNumber, static_cast<uint64>(number_)); 717 size += EbmlElementSize(libwebm::kMkvTrackUID, static_cast<uint64>(uid_)); 718 size += EbmlElementSize(libwebm::kMkvTrackType, static_cast<uint64>(type_)); 719 if (codec_id_) 720 size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_); 721 if (codec_private_) 722 size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_, 723 codec_private_length_); 724 if (language_) 725 size += EbmlElementSize(libwebm::kMkvLanguage, language_); 726 if (name_) 727 size += EbmlElementSize(libwebm::kMkvName, name_); 728 if (max_block_additional_id_) { 729 size += EbmlElementSize(libwebm::kMkvMaxBlockAdditionID, 730 static_cast<uint64>(max_block_additional_id_)); 731 } 732 if (codec_delay_) { 733 size += EbmlElementSize(libwebm::kMkvCodecDelay, 734 static_cast<uint64>(codec_delay_)); 735 } 736 if (seek_pre_roll_) { 737 size += EbmlElementSize(libwebm::kMkvSeekPreRoll, 738 static_cast<uint64>(seek_pre_roll_)); 739 } 740 if (default_duration_) { 741 size += EbmlElementSize(libwebm::kMkvDefaultDuration, 742 static_cast<uint64>(default_duration_)); 743 } 744 745 if (content_encoding_entries_size_ > 0) { 746 uint64_t content_encodings_size = 0; 747 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { 748 ContentEncoding* const encoding = content_encoding_entries_[i]; 749 content_encodings_size += encoding->Size(); 750 } 751 752 size += EbmlMasterElementSize(libwebm::kMkvContentEncodings, 753 content_encodings_size) + 754 content_encodings_size; 755 } 756 757 return size; 758} 759 760uint64_t Track::Size() const { 761 uint64_t size = PayloadSize(); 762 size += EbmlMasterElementSize(libwebm::kMkvTrackEntry, size); 763 return size; 764} 765 766bool Track::Write(IMkvWriter* writer) const { 767 if (!writer) 768 return false; 769 770 // mandatory elements without a default value. 771 if (!type_ || !codec_id_) 772 return false; 773 774 // |size| may be bigger than what is written out in this function because 775 // derived classes may write out more data in the Track element. 776 const uint64_t payload_size = PayloadSize(); 777 778 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTrackEntry, payload_size)) 779 return false; 780 781 uint64_t size = 782 EbmlElementSize(libwebm::kMkvTrackNumber, static_cast<uint64>(number_)); 783 size += EbmlElementSize(libwebm::kMkvTrackUID, static_cast<uint64>(uid_)); 784 size += EbmlElementSize(libwebm::kMkvTrackType, static_cast<uint64>(type_)); 785 if (codec_id_) 786 size += EbmlElementSize(libwebm::kMkvCodecID, codec_id_); 787 if (codec_private_) 788 size += EbmlElementSize(libwebm::kMkvCodecPrivate, codec_private_, 789 static_cast<uint64>(codec_private_length_)); 790 if (language_) 791 size += EbmlElementSize(libwebm::kMkvLanguage, language_); 792 if (name_) 793 size += EbmlElementSize(libwebm::kMkvName, name_); 794 if (max_block_additional_id_) 795 size += EbmlElementSize(libwebm::kMkvMaxBlockAdditionID, 796 static_cast<uint64>(max_block_additional_id_)); 797 if (codec_delay_) 798 size += EbmlElementSize(libwebm::kMkvCodecDelay, 799 static_cast<uint64>(codec_delay_)); 800 if (seek_pre_roll_) 801 size += EbmlElementSize(libwebm::kMkvSeekPreRoll, 802 static_cast<uint64>(seek_pre_roll_)); 803 if (default_duration_) 804 size += EbmlElementSize(libwebm::kMkvDefaultDuration, 805 static_cast<uint64>(default_duration_)); 806 807 const int64_t payload_position = writer->Position(); 808 if (payload_position < 0) 809 return false; 810 811 if (!WriteEbmlElement(writer, libwebm::kMkvTrackNumber, 812 static_cast<uint64>(number_))) 813 return false; 814 if (!WriteEbmlElement(writer, libwebm::kMkvTrackUID, 815 static_cast<uint64>(uid_))) 816 return false; 817 if (!WriteEbmlElement(writer, libwebm::kMkvTrackType, 818 static_cast<uint64>(type_))) 819 return false; 820 if (max_block_additional_id_) { 821 if (!WriteEbmlElement(writer, libwebm::kMkvMaxBlockAdditionID, 822 static_cast<uint64>(max_block_additional_id_))) { 823 return false; 824 } 825 } 826 if (codec_delay_) { 827 if (!WriteEbmlElement(writer, libwebm::kMkvCodecDelay, 828 static_cast<uint64>(codec_delay_))) 829 return false; 830 } 831 if (seek_pre_roll_) { 832 if (!WriteEbmlElement(writer, libwebm::kMkvSeekPreRoll, 833 static_cast<uint64>(seek_pre_roll_))) 834 return false; 835 } 836 if (default_duration_) { 837 if (!WriteEbmlElement(writer, libwebm::kMkvDefaultDuration, 838 static_cast<uint64>(default_duration_))) 839 return false; 840 } 841 if (codec_id_) { 842 if (!WriteEbmlElement(writer, libwebm::kMkvCodecID, codec_id_)) 843 return false; 844 } 845 if (codec_private_) { 846 if (!WriteEbmlElement(writer, libwebm::kMkvCodecPrivate, codec_private_, 847 static_cast<uint64>(codec_private_length_))) 848 return false; 849 } 850 if (language_) { 851 if (!WriteEbmlElement(writer, libwebm::kMkvLanguage, language_)) 852 return false; 853 } 854 if (name_) { 855 if (!WriteEbmlElement(writer, libwebm::kMkvName, name_)) 856 return false; 857 } 858 859 int64_t stop_position = writer->Position(); 860 if (stop_position < 0 || 861 stop_position - payload_position != static_cast<int64_t>(size)) 862 return false; 863 864 if (content_encoding_entries_size_ > 0) { 865 uint64_t content_encodings_size = 0; 866 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { 867 ContentEncoding* const encoding = content_encoding_entries_[i]; 868 content_encodings_size += encoding->Size(); 869 } 870 871 if (!WriteEbmlMasterElement(writer, libwebm::kMkvContentEncodings, 872 content_encodings_size)) 873 return false; 874 875 for (uint32_t i = 0; i < content_encoding_entries_size_; ++i) { 876 ContentEncoding* const encoding = content_encoding_entries_[i]; 877 if (!encoding->Write(writer)) 878 return false; 879 } 880 } 881 882 stop_position = writer->Position(); 883 if (stop_position < 0) 884 return false; 885 return true; 886} 887 888bool Track::SetCodecPrivate(const uint8_t* codec_private, uint64_t length) { 889 if (!codec_private || length < 1) 890 return false; 891 892 delete[] codec_private_; 893 894 codec_private_ = 895 new (std::nothrow) uint8_t[static_cast<size_t>(length)]; // NOLINT 896 if (!codec_private_) 897 return false; 898 899 memcpy(codec_private_, codec_private, static_cast<size_t>(length)); 900 codec_private_length_ = length; 901 902 return true; 903} 904 905void Track::set_codec_id(const char* codec_id) { 906 if (codec_id) { 907 delete[] codec_id_; 908 909 const size_t length = strlen(codec_id) + 1; 910 codec_id_ = new (std::nothrow) char[length]; // NOLINT 911 if (codec_id_) { 912#ifdef _MSC_VER 913 strcpy_s(codec_id_, length, codec_id); 914#else 915 strcpy(codec_id_, codec_id); 916#endif 917 } 918 } 919} 920 921// TODO(fgalligan): Vet the language parameter. 922void Track::set_language(const char* language) { 923 if (language) { 924 delete[] language_; 925 926 const size_t length = strlen(language) + 1; 927 language_ = new (std::nothrow) char[length]; // NOLINT 928 if (language_) { 929#ifdef _MSC_VER 930 strcpy_s(language_, length, language); 931#else 932 strcpy(language_, language); 933#endif 934 } 935 } 936} 937 938void Track::set_name(const char* name) { 939 if (name) { 940 delete[] name_; 941 942 const size_t length = strlen(name) + 1; 943 name_ = new (std::nothrow) char[length]; // NOLINT 944 if (name_) { 945#ifdef _MSC_VER 946 strcpy_s(name_, length, name); 947#else 948 strcpy(name_, name); 949#endif 950 } 951 } 952} 953 954/////////////////////////////////////////////////////////////// 955// 956// Colour and its child elements 957 958uint64_t PrimaryChromaticity::PrimaryChromaticitySize( 959 libwebm::MkvId x_id, libwebm::MkvId y_id) const { 960 return EbmlElementSize(x_id, x_) + EbmlElementSize(y_id, y_); 961} 962 963bool PrimaryChromaticity::Write(IMkvWriter* writer, libwebm::MkvId x_id, 964 libwebm::MkvId y_id) const { 965 if (!Valid()) { 966 return false; 967 } 968 return WriteEbmlElement(writer, x_id, x_) && 969 WriteEbmlElement(writer, y_id, y_); 970} 971 972bool PrimaryChromaticity::Valid() const { 973 return (x_ >= kChromaticityMin && x_ <= kChromaticityMax && 974 y_ >= kChromaticityMin && y_ <= kChromaticityMax); 975} 976 977uint64_t MasteringMetadata::MasteringMetadataSize() const { 978 uint64_t size = PayloadSize(); 979 980 if (size > 0) 981 size += EbmlMasterElementSize(libwebm::kMkvMasteringMetadata, size); 982 983 return size; 984} 985 986bool MasteringMetadata::Valid() const { 987 if (luminance_min_ != kValueNotPresent) { 988 if (luminance_min_ < kMinLuminance || luminance_min_ > kMinLuminanceMax || 989 luminance_min_ > luminance_max_) { 990 return false; 991 } 992 } 993 if (luminance_max_ != kValueNotPresent) { 994 if (luminance_max_ < kMinLuminance || luminance_max_ > kMaxLuminanceMax || 995 luminance_max_ < luminance_min_) { 996 return false; 997 } 998 } 999 if (r_ && !r_->Valid()) 1000 return false; 1001 if (g_ && !g_->Valid()) 1002 return false; 1003 if (b_ && !b_->Valid()) 1004 return false; 1005 if (white_point_ && !white_point_->Valid()) 1006 return false; 1007 1008 return true; 1009} 1010 1011bool MasteringMetadata::Write(IMkvWriter* writer) const { 1012 const uint64_t size = PayloadSize(); 1013 1014 // Don't write an empty element. 1015 if (size == 0) 1016 return true; 1017 1018 if (!WriteEbmlMasterElement(writer, libwebm::kMkvMasteringMetadata, size)) 1019 return false; 1020 if (luminance_max_ != kValueNotPresent && 1021 !WriteEbmlElement(writer, libwebm::kMkvLuminanceMax, luminance_max_)) { 1022 return false; 1023 } 1024 if (luminance_min_ != kValueNotPresent && 1025 !WriteEbmlElement(writer, libwebm::kMkvLuminanceMin, luminance_min_)) { 1026 return false; 1027 } 1028 if (r_ && 1029 !r_->Write(writer, libwebm::kMkvPrimaryRChromaticityX, 1030 libwebm::kMkvPrimaryRChromaticityY)) { 1031 return false; 1032 } 1033 if (g_ && 1034 !g_->Write(writer, libwebm::kMkvPrimaryGChromaticityX, 1035 libwebm::kMkvPrimaryGChromaticityY)) { 1036 return false; 1037 } 1038 if (b_ && 1039 !b_->Write(writer, libwebm::kMkvPrimaryBChromaticityX, 1040 libwebm::kMkvPrimaryBChromaticityY)) { 1041 return false; 1042 } 1043 if (white_point_ && 1044 !white_point_->Write(writer, libwebm::kMkvWhitePointChromaticityX, 1045 libwebm::kMkvWhitePointChromaticityY)) { 1046 return false; 1047 } 1048 1049 return true; 1050} 1051 1052bool MasteringMetadata::SetChromaticity( 1053 const PrimaryChromaticity* r, const PrimaryChromaticity* g, 1054 const PrimaryChromaticity* b, const PrimaryChromaticity* white_point) { 1055 PrimaryChromaticityPtr r_ptr(NULL); 1056 if (r) { 1057 if (!CopyChromaticity(r, &r_ptr)) 1058 return false; 1059 } 1060 PrimaryChromaticityPtr g_ptr(NULL); 1061 if (g) { 1062 if (!CopyChromaticity(g, &g_ptr)) 1063 return false; 1064 } 1065 PrimaryChromaticityPtr b_ptr(NULL); 1066 if (b) { 1067 if (!CopyChromaticity(b, &b_ptr)) 1068 return false; 1069 } 1070 PrimaryChromaticityPtr wp_ptr(NULL); 1071 if (white_point) { 1072 if (!CopyChromaticity(white_point, &wp_ptr)) 1073 return false; 1074 } 1075 1076 r_ = r_ptr.release(); 1077 g_ = g_ptr.release(); 1078 b_ = b_ptr.release(); 1079 white_point_ = wp_ptr.release(); 1080 return true; 1081} 1082 1083uint64_t MasteringMetadata::PayloadSize() const { 1084 uint64_t size = 0; 1085 1086 if (luminance_max_ != kValueNotPresent) 1087 size += EbmlElementSize(libwebm::kMkvLuminanceMax, luminance_max_); 1088 if (luminance_min_ != kValueNotPresent) 1089 size += EbmlElementSize(libwebm::kMkvLuminanceMin, luminance_min_); 1090 1091 if (r_) { 1092 size += r_->PrimaryChromaticitySize(libwebm::kMkvPrimaryRChromaticityX, 1093 libwebm::kMkvPrimaryRChromaticityY); 1094 } 1095 if (g_) { 1096 size += g_->PrimaryChromaticitySize(libwebm::kMkvPrimaryGChromaticityX, 1097 libwebm::kMkvPrimaryGChromaticityY); 1098 } 1099 if (b_) { 1100 size += b_->PrimaryChromaticitySize(libwebm::kMkvPrimaryBChromaticityX, 1101 libwebm::kMkvPrimaryBChromaticityY); 1102 } 1103 if (white_point_) { 1104 size += white_point_->PrimaryChromaticitySize( 1105 libwebm::kMkvWhitePointChromaticityX, 1106 libwebm::kMkvWhitePointChromaticityY); 1107 } 1108 1109 return size; 1110} 1111 1112uint64_t Colour::ColourSize() const { 1113 uint64_t size = PayloadSize(); 1114 1115 if (size > 0) 1116 size += EbmlMasterElementSize(libwebm::kMkvColour, size); 1117 1118 return size; 1119} 1120 1121bool Colour::Valid() const { 1122 if (mastering_metadata_ && !mastering_metadata_->Valid()) 1123 return false; 1124 if (matrix_coefficients_ != kValueNotPresent && 1125 !IsMatrixCoefficientsValueValid(matrix_coefficients_)) { 1126 return false; 1127 } 1128 if (chroma_siting_horz_ != kValueNotPresent && 1129 !IsChromaSitingHorzValueValid(chroma_siting_horz_)) { 1130 return false; 1131 } 1132 if (chroma_siting_vert_ != kValueNotPresent && 1133 !IsChromaSitingVertValueValid(chroma_siting_vert_)) { 1134 return false; 1135 } 1136 if (range_ != kValueNotPresent && !IsColourRangeValueValid(range_)) 1137 return false; 1138 if (transfer_characteristics_ != kValueNotPresent && 1139 !IsTransferCharacteristicsValueValid(transfer_characteristics_)) { 1140 return false; 1141 } 1142 if (primaries_ != kValueNotPresent && !IsPrimariesValueValid(primaries_)) 1143 return false; 1144 1145 return true; 1146} 1147 1148bool Colour::Write(IMkvWriter* writer) const { 1149 const uint64_t size = PayloadSize(); 1150 1151 // Don't write an empty element. 1152 if (size == 0) 1153 return true; 1154 1155 // Don't write an invalid element. 1156 if (!Valid()) 1157 return false; 1158 1159 if (!WriteEbmlMasterElement(writer, libwebm::kMkvColour, size)) 1160 return false; 1161 1162 if (matrix_coefficients_ != kValueNotPresent && 1163 !WriteEbmlElement(writer, libwebm::kMkvMatrixCoefficients, 1164 static_cast<uint64>(matrix_coefficients_))) { 1165 return false; 1166 } 1167 if (bits_per_channel_ != kValueNotPresent && 1168 !WriteEbmlElement(writer, libwebm::kMkvBitsPerChannel, 1169 static_cast<uint64>(bits_per_channel_))) { 1170 return false; 1171 } 1172 if (chroma_subsampling_horz_ != kValueNotPresent && 1173 !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingHorz, 1174 static_cast<uint64>(chroma_subsampling_horz_))) { 1175 return false; 1176 } 1177 if (chroma_subsampling_vert_ != kValueNotPresent && 1178 !WriteEbmlElement(writer, libwebm::kMkvChromaSubsamplingVert, 1179 static_cast<uint64>(chroma_subsampling_vert_))) { 1180 return false; 1181 } 1182 1183 if (cb_subsampling_horz_ != kValueNotPresent && 1184 !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingHorz, 1185 static_cast<uint64>(cb_subsampling_horz_))) { 1186 return false; 1187 } 1188 if (cb_subsampling_vert_ != kValueNotPresent && 1189 !WriteEbmlElement(writer, libwebm::kMkvCbSubsamplingVert, 1190 static_cast<uint64>(cb_subsampling_vert_))) { 1191 return false; 1192 } 1193 if (chroma_siting_horz_ != kValueNotPresent && 1194 !WriteEbmlElement(writer, libwebm::kMkvChromaSitingHorz, 1195 static_cast<uint64>(chroma_siting_horz_))) { 1196 return false; 1197 } 1198 if (chroma_siting_vert_ != kValueNotPresent && 1199 !WriteEbmlElement(writer, libwebm::kMkvChromaSitingVert, 1200 static_cast<uint64>(chroma_siting_vert_))) { 1201 return false; 1202 } 1203 if (range_ != kValueNotPresent && 1204 !WriteEbmlElement(writer, libwebm::kMkvRange, 1205 static_cast<uint64>(range_))) { 1206 return false; 1207 } 1208 if (transfer_characteristics_ != kValueNotPresent && 1209 !WriteEbmlElement(writer, libwebm::kMkvTransferCharacteristics, 1210 static_cast<uint64>(transfer_characteristics_))) { 1211 return false; 1212 } 1213 if (primaries_ != kValueNotPresent && 1214 !WriteEbmlElement(writer, libwebm::kMkvPrimaries, 1215 static_cast<uint64>(primaries_))) { 1216 return false; 1217 } 1218 if (max_cll_ != kValueNotPresent && 1219 !WriteEbmlElement(writer, libwebm::kMkvMaxCLL, 1220 static_cast<uint64>(max_cll_))) { 1221 return false; 1222 } 1223 if (max_fall_ != kValueNotPresent && 1224 !WriteEbmlElement(writer, libwebm::kMkvMaxFALL, 1225 static_cast<uint64>(max_fall_))) { 1226 return false; 1227 } 1228 1229 if (mastering_metadata_ && !mastering_metadata_->Write(writer)) 1230 return false; 1231 1232 return true; 1233} 1234 1235bool Colour::SetMasteringMetadata(const MasteringMetadata& mastering_metadata) { 1236 std::auto_ptr<MasteringMetadata> mm_ptr(new MasteringMetadata()); 1237 if (!mm_ptr.get()) 1238 return false; 1239 1240 mm_ptr->set_luminance_max(mastering_metadata.luminance_max()); 1241 mm_ptr->set_luminance_min(mastering_metadata.luminance_min()); 1242 1243 if (!mm_ptr->SetChromaticity(mastering_metadata.r(), mastering_metadata.g(), 1244 mastering_metadata.b(), 1245 mastering_metadata.white_point())) { 1246 return false; 1247 } 1248 1249 delete mastering_metadata_; 1250 mastering_metadata_ = mm_ptr.release(); 1251 return true; 1252} 1253 1254uint64_t Colour::PayloadSize() const { 1255 uint64_t size = 0; 1256 1257 if (matrix_coefficients_ != kValueNotPresent) { 1258 size += EbmlElementSize(libwebm::kMkvMatrixCoefficients, 1259 static_cast<uint64>(matrix_coefficients_)); 1260 } 1261 if (bits_per_channel_ != kValueNotPresent) { 1262 size += EbmlElementSize(libwebm::kMkvBitsPerChannel, 1263 static_cast<uint64>(bits_per_channel_)); 1264 } 1265 if (chroma_subsampling_horz_ != kValueNotPresent) { 1266 size += EbmlElementSize(libwebm::kMkvChromaSubsamplingHorz, 1267 static_cast<uint64>(chroma_subsampling_horz_)); 1268 } 1269 if (chroma_subsampling_vert_ != kValueNotPresent) { 1270 size += EbmlElementSize(libwebm::kMkvChromaSubsamplingVert, 1271 static_cast<uint64>(chroma_subsampling_vert_)); 1272 } 1273 if (cb_subsampling_horz_ != kValueNotPresent) { 1274 size += EbmlElementSize(libwebm::kMkvCbSubsamplingHorz, 1275 static_cast<uint64>(cb_subsampling_horz_)); 1276 } 1277 if (cb_subsampling_vert_ != kValueNotPresent) { 1278 size += EbmlElementSize(libwebm::kMkvCbSubsamplingVert, 1279 static_cast<uint64>(cb_subsampling_vert_)); 1280 } 1281 if (chroma_siting_horz_ != kValueNotPresent) { 1282 size += EbmlElementSize(libwebm::kMkvChromaSitingHorz, 1283 static_cast<uint64>(chroma_siting_horz_)); 1284 } 1285 if (chroma_siting_vert_ != kValueNotPresent) { 1286 size += EbmlElementSize(libwebm::kMkvChromaSitingVert, 1287 static_cast<uint64>(chroma_siting_vert_)); 1288 } 1289 if (range_ != kValueNotPresent) { 1290 size += EbmlElementSize(libwebm::kMkvRange, static_cast<uint64>(range_)); 1291 } 1292 if (transfer_characteristics_ != kValueNotPresent) { 1293 size += EbmlElementSize(libwebm::kMkvTransferCharacteristics, 1294 static_cast<uint64>(transfer_characteristics_)); 1295 } 1296 if (primaries_ != kValueNotPresent) { 1297 size += EbmlElementSize(libwebm::kMkvPrimaries, 1298 static_cast<uint64>(primaries_)); 1299 } 1300 if (max_cll_ != kValueNotPresent) { 1301 size += EbmlElementSize(libwebm::kMkvMaxCLL, static_cast<uint64>(max_cll_)); 1302 } 1303 if (max_fall_ != kValueNotPresent) { 1304 size += 1305 EbmlElementSize(libwebm::kMkvMaxFALL, static_cast<uint64>(max_fall_)); 1306 } 1307 1308 if (mastering_metadata_) 1309 size += mastering_metadata_->MasteringMetadataSize(); 1310 1311 return size; 1312} 1313 1314/////////////////////////////////////////////////////////////// 1315// 1316// Projection element 1317 1318uint64_t Projection::ProjectionSize() const { 1319 uint64_t size = PayloadSize(); 1320 1321 if (size > 0) 1322 size += EbmlMasterElementSize(libwebm::kMkvProjection, size); 1323 1324 return size; 1325} 1326 1327bool Projection::Write(IMkvWriter* writer) const { 1328 const uint64_t size = PayloadSize(); 1329 1330 // Don't write an empty element. 1331 if (size == 0) 1332 return true; 1333 1334 if (!WriteEbmlMasterElement(writer, libwebm::kMkvProjection, size)) 1335 return false; 1336 1337 if (!WriteEbmlElement(writer, libwebm::kMkvProjectionType, 1338 static_cast<uint64>(type_))) { 1339 return false; 1340 } 1341 1342 if (private_data_length_ > 0 && private_data_ != NULL && 1343 !WriteEbmlElement(writer, libwebm::kMkvProjectionPrivate, private_data_, 1344 private_data_length_)) { 1345 return false; 1346 } 1347 1348 if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPoseYaw, pose_yaw_)) 1349 return false; 1350 1351 if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPosePitch, 1352 pose_pitch_)) { 1353 return false; 1354 } 1355 1356 if (!WriteEbmlElement(writer, libwebm::kMkvProjectionPoseRoll, pose_roll_)) { 1357 return false; 1358 } 1359 1360 return true; 1361} 1362 1363bool Projection::SetProjectionPrivate(const uint8_t* data, 1364 uint64_t data_length) { 1365 if (data == NULL || data_length == 0) { 1366 return false; 1367 } 1368 1369 if (data_length != static_cast<size_t>(data_length)) { 1370 return false; 1371 } 1372 1373 uint8_t* new_private_data = 1374 new (std::nothrow) uint8_t[static_cast<size_t>(data_length)]; 1375 if (new_private_data == NULL) { 1376 return false; 1377 } 1378 1379 delete[] private_data_; 1380 private_data_ = new_private_data; 1381 private_data_length_ = data_length; 1382 memcpy(private_data_, data, static_cast<size_t>(data_length)); 1383 1384 return true; 1385} 1386 1387uint64_t Projection::PayloadSize() const { 1388 uint64_t size = 1389 EbmlElementSize(libwebm::kMkvProjection, static_cast<uint64>(type_)); 1390 1391 if (private_data_length_ > 0 && private_data_ != NULL) { 1392 size += EbmlElementSize(libwebm::kMkvProjectionPrivate, private_data_, 1393 private_data_length_); 1394 } 1395 1396 size += EbmlElementSize(libwebm::kMkvProjectionPoseYaw, pose_yaw_); 1397 size += EbmlElementSize(libwebm::kMkvProjectionPosePitch, pose_pitch_); 1398 size += EbmlElementSize(libwebm::kMkvProjectionPoseRoll, pose_roll_); 1399 1400 return size; 1401} 1402 1403/////////////////////////////////////////////////////////////// 1404// 1405// VideoTrack Class 1406 1407VideoTrack::VideoTrack(unsigned int* seed) 1408 : Track(seed), 1409 display_height_(0), 1410 display_width_(0), 1411 pixel_height_(0), 1412 pixel_width_(0), 1413 crop_left_(0), 1414 crop_right_(0), 1415 crop_top_(0), 1416 crop_bottom_(0), 1417 frame_rate_(0.0), 1418 height_(0), 1419 stereo_mode_(0), 1420 alpha_mode_(0), 1421 width_(0), 1422 colour_(NULL), 1423 projection_(NULL) {} 1424 1425VideoTrack::~VideoTrack() { 1426 delete colour_; 1427 delete projection_; 1428} 1429 1430bool VideoTrack::SetStereoMode(uint64_t stereo_mode) { 1431 if (stereo_mode != kMono && stereo_mode != kSideBySideLeftIsFirst && 1432 stereo_mode != kTopBottomRightIsFirst && 1433 stereo_mode != kTopBottomLeftIsFirst && 1434 stereo_mode != kSideBySideRightIsFirst) 1435 return false; 1436 1437 stereo_mode_ = stereo_mode; 1438 return true; 1439} 1440 1441bool VideoTrack::SetAlphaMode(uint64_t alpha_mode) { 1442 if (alpha_mode != kNoAlpha && alpha_mode != kAlpha) 1443 return false; 1444 1445 alpha_mode_ = alpha_mode; 1446 return true; 1447} 1448 1449uint64_t VideoTrack::PayloadSize() const { 1450 const uint64_t parent_size = Track::PayloadSize(); 1451 1452 uint64_t size = VideoPayloadSize(); 1453 size += EbmlMasterElementSize(libwebm::kMkvVideo, size); 1454 1455 return parent_size + size; 1456} 1457 1458bool VideoTrack::Write(IMkvWriter* writer) const { 1459 if (!Track::Write(writer)) 1460 return false; 1461 1462 const uint64_t size = VideoPayloadSize(); 1463 1464 if (!WriteEbmlMasterElement(writer, libwebm::kMkvVideo, size)) 1465 return false; 1466 1467 const int64_t payload_position = writer->Position(); 1468 if (payload_position < 0) 1469 return false; 1470 1471 if (!WriteEbmlElement( 1472 writer, libwebm::kMkvPixelWidth, 1473 static_cast<uint64>((pixel_width_ > 0) ? pixel_width_ : width_))) 1474 return false; 1475 if (!WriteEbmlElement( 1476 writer, libwebm::kMkvPixelHeight, 1477 static_cast<uint64>((pixel_height_ > 0) ? pixel_height_ : height_))) 1478 return false; 1479 if (display_width_ > 0) { 1480 if (!WriteEbmlElement(writer, libwebm::kMkvDisplayWidth, 1481 static_cast<uint64>(display_width_))) 1482 return false; 1483 } 1484 if (display_height_ > 0) { 1485 if (!WriteEbmlElement(writer, libwebm::kMkvDisplayHeight, 1486 static_cast<uint64>(display_height_))) 1487 return false; 1488 } 1489 if (crop_left_ > 0) { 1490 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropLeft, 1491 static_cast<uint64>(crop_left_))) 1492 return false; 1493 } 1494 if (crop_right_ > 0) { 1495 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropRight, 1496 static_cast<uint64>(crop_right_))) 1497 return false; 1498 } 1499 if (crop_top_ > 0) { 1500 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropTop, 1501 static_cast<uint64>(crop_top_))) 1502 return false; 1503 } 1504 if (crop_bottom_ > 0) { 1505 if (!WriteEbmlElement(writer, libwebm::kMkvPixelCropBottom, 1506 static_cast<uint64>(crop_bottom_))) 1507 return false; 1508 } 1509 if (stereo_mode_ > kMono) { 1510 if (!WriteEbmlElement(writer, libwebm::kMkvStereoMode, 1511 static_cast<uint64>(stereo_mode_))) 1512 return false; 1513 } 1514 if (alpha_mode_ > kNoAlpha) { 1515 if (!WriteEbmlElement(writer, libwebm::kMkvAlphaMode, 1516 static_cast<uint64>(alpha_mode_))) 1517 return false; 1518 } 1519 if (frame_rate_ > 0.0) { 1520 if (!WriteEbmlElement(writer, libwebm::kMkvFrameRate, 1521 static_cast<float>(frame_rate_))) { 1522 return false; 1523 } 1524 } 1525 if (colour_) { 1526 if (!colour_->Write(writer)) 1527 return false; 1528 } 1529 if (projection_) { 1530 if (!projection_->Write(writer)) 1531 return false; 1532 } 1533 1534 const int64_t stop_position = writer->Position(); 1535 if (stop_position < 0 || 1536 stop_position - payload_position != static_cast<int64_t>(size)) { 1537 return false; 1538 } 1539 1540 return true; 1541} 1542 1543bool VideoTrack::SetColour(const Colour& colour) { 1544 std::auto_ptr<Colour> colour_ptr(new Colour()); 1545 if (!colour_ptr.get()) 1546 return false; 1547 1548 if (colour.mastering_metadata()) { 1549 if (!colour_ptr->SetMasteringMetadata(*colour.mastering_metadata())) 1550 return false; 1551 } 1552 1553 colour_ptr->set_matrix_coefficients(colour.matrix_coefficients()); 1554 colour_ptr->set_bits_per_channel(colour.bits_per_channel()); 1555 colour_ptr->set_chroma_subsampling_horz(colour.chroma_subsampling_horz()); 1556 colour_ptr->set_chroma_subsampling_vert(colour.chroma_subsampling_vert()); 1557 colour_ptr->set_cb_subsampling_horz(colour.cb_subsampling_horz()); 1558 colour_ptr->set_cb_subsampling_vert(colour.cb_subsampling_vert()); 1559 colour_ptr->set_chroma_siting_horz(colour.chroma_siting_horz()); 1560 colour_ptr->set_chroma_siting_vert(colour.chroma_siting_vert()); 1561 colour_ptr->set_range(colour.range()); 1562 colour_ptr->set_transfer_characteristics(colour.transfer_characteristics()); 1563 colour_ptr->set_primaries(colour.primaries()); 1564 colour_ptr->set_max_cll(colour.max_cll()); 1565 colour_ptr->set_max_fall(colour.max_fall()); 1566 delete colour_; 1567 colour_ = colour_ptr.release(); 1568 return true; 1569} 1570 1571bool VideoTrack::SetProjection(const Projection& projection) { 1572 std::auto_ptr<Projection> projection_ptr(new Projection()); 1573 if (!projection_ptr.get()) 1574 return false; 1575 1576 if (projection.private_data()) { 1577 if (!projection_ptr->SetProjectionPrivate( 1578 projection.private_data(), projection.private_data_length())) { 1579 return false; 1580 } 1581 } 1582 1583 projection_ptr->set_type(projection.type()); 1584 projection_ptr->set_pose_yaw(projection.pose_yaw()); 1585 projection_ptr->set_pose_pitch(projection.pose_pitch()); 1586 projection_ptr->set_pose_roll(projection.pose_roll()); 1587 delete projection_; 1588 projection_ = projection_ptr.release(); 1589 return true; 1590} 1591 1592uint64_t VideoTrack::VideoPayloadSize() const { 1593 uint64_t size = EbmlElementSize( 1594 libwebm::kMkvPixelWidth, 1595 static_cast<uint64>((pixel_width_ > 0) ? pixel_width_ : width_)); 1596 size += EbmlElementSize( 1597 libwebm::kMkvPixelHeight, 1598 static_cast<uint64>((pixel_height_ > 0) ? pixel_height_ : height_)); 1599 if (display_width_ > 0) 1600 size += EbmlElementSize(libwebm::kMkvDisplayWidth, 1601 static_cast<uint64>(display_width_)); 1602 if (display_height_ > 0) 1603 size += EbmlElementSize(libwebm::kMkvDisplayHeight, 1604 static_cast<uint64>(display_height_)); 1605 if (crop_left_ > 0) 1606 size += EbmlElementSize(libwebm::kMkvPixelCropLeft, 1607 static_cast<uint64>(crop_left_)); 1608 if (crop_right_ > 0) 1609 size += EbmlElementSize(libwebm::kMkvPixelCropRight, 1610 static_cast<uint64>(crop_right_)); 1611 if (crop_top_ > 0) 1612 size += EbmlElementSize(libwebm::kMkvPixelCropTop, 1613 static_cast<uint64>(crop_top_)); 1614 if (crop_bottom_ > 0) 1615 size += EbmlElementSize(libwebm::kMkvPixelCropBottom, 1616 static_cast<uint64>(crop_bottom_)); 1617 if (stereo_mode_ > kMono) 1618 size += EbmlElementSize(libwebm::kMkvStereoMode, 1619 static_cast<uint64>(stereo_mode_)); 1620 if (alpha_mode_ > kNoAlpha) 1621 size += EbmlElementSize(libwebm::kMkvAlphaMode, 1622 static_cast<uint64>(alpha_mode_)); 1623 if (frame_rate_ > 0.0) 1624 size += EbmlElementSize(libwebm::kMkvFrameRate, 1625 static_cast<float>(frame_rate_)); 1626 if (colour_) 1627 size += colour_->ColourSize(); 1628 if (projection_) 1629 size += projection_->ProjectionSize(); 1630 1631 return size; 1632} 1633 1634/////////////////////////////////////////////////////////////// 1635// 1636// AudioTrack Class 1637 1638AudioTrack::AudioTrack(unsigned int* seed) 1639 : Track(seed), bit_depth_(0), channels_(1), sample_rate_(0.0) {} 1640 1641AudioTrack::~AudioTrack() {} 1642 1643uint64_t AudioTrack::PayloadSize() const { 1644 const uint64_t parent_size = Track::PayloadSize(); 1645 1646 uint64_t size = EbmlElementSize(libwebm::kMkvSamplingFrequency, 1647 static_cast<float>(sample_rate_)); 1648 size += 1649 EbmlElementSize(libwebm::kMkvChannels, static_cast<uint64>(channels_)); 1650 if (bit_depth_ > 0) 1651 size += 1652 EbmlElementSize(libwebm::kMkvBitDepth, static_cast<uint64>(bit_depth_)); 1653 size += EbmlMasterElementSize(libwebm::kMkvAudio, size); 1654 1655 return parent_size + size; 1656} 1657 1658bool AudioTrack::Write(IMkvWriter* writer) const { 1659 if (!Track::Write(writer)) 1660 return false; 1661 1662 // Calculate AudioSettings size. 1663 uint64_t size = EbmlElementSize(libwebm::kMkvSamplingFrequency, 1664 static_cast<float>(sample_rate_)); 1665 size += 1666 EbmlElementSize(libwebm::kMkvChannels, static_cast<uint64>(channels_)); 1667 if (bit_depth_ > 0) 1668 size += 1669 EbmlElementSize(libwebm::kMkvBitDepth, static_cast<uint64>(bit_depth_)); 1670 1671 if (!WriteEbmlMasterElement(writer, libwebm::kMkvAudio, size)) 1672 return false; 1673 1674 const int64_t payload_position = writer->Position(); 1675 if (payload_position < 0) 1676 return false; 1677 1678 if (!WriteEbmlElement(writer, libwebm::kMkvSamplingFrequency, 1679 static_cast<float>(sample_rate_))) 1680 return false; 1681 if (!WriteEbmlElement(writer, libwebm::kMkvChannels, 1682 static_cast<uint64>(channels_))) 1683 return false; 1684 if (bit_depth_ > 0) 1685 if (!WriteEbmlElement(writer, libwebm::kMkvBitDepth, 1686 static_cast<uint64>(bit_depth_))) 1687 return false; 1688 1689 const int64_t stop_position = writer->Position(); 1690 if (stop_position < 0 || 1691 stop_position - payload_position != static_cast<int64_t>(size)) 1692 return false; 1693 1694 return true; 1695} 1696 1697/////////////////////////////////////////////////////////////// 1698// 1699// Tracks Class 1700 1701const char Tracks::kOpusCodecId[] = "A_OPUS"; 1702const char Tracks::kVorbisCodecId[] = "A_VORBIS"; 1703const char Tracks::kVp8CodecId[] = "V_VP8"; 1704const char Tracks::kVp9CodecId[] = "V_VP9"; 1705const char Tracks::kVp10CodecId[] = "V_VP10"; 1706const char Tracks::kWebVttCaptionsId[] = "D_WEBVTT/CAPTIONS"; 1707const char Tracks::kWebVttDescriptionsId[] = "D_WEBVTT/DESCRIPTIONS"; 1708const char Tracks::kWebVttMetadataId[] = "D_WEBVTT/METADATA"; 1709const char Tracks::kWebVttSubtitlesId[] = "D_WEBVTT/SUBTITLES"; 1710 1711Tracks::Tracks() 1712 : track_entries_(NULL), track_entries_size_(0), wrote_tracks_(false) {} 1713 1714Tracks::~Tracks() { 1715 if (track_entries_) { 1716 for (uint32_t i = 0; i < track_entries_size_; ++i) { 1717 Track* const track = track_entries_[i]; 1718 delete track; 1719 } 1720 delete[] track_entries_; 1721 } 1722} 1723 1724bool Tracks::AddTrack(Track* track, int32_t number) { 1725 if (number < 0 || wrote_tracks_) 1726 return false; 1727 1728 // This muxer only supports track numbers in the range [1, 126], in 1729 // order to be able (to use Matroska integer representation) to 1730 // serialize the block header (of which the track number is a part) 1731 // for a frame using exactly 4 bytes. 1732 1733 if (number > 0x7E) 1734 return false; 1735 1736 uint32_t track_num = number; 1737 1738 if (track_num > 0) { 1739 // Check to make sure a track does not already have |track_num|. 1740 for (uint32_t i = 0; i < track_entries_size_; ++i) { 1741 if (track_entries_[i]->number() == track_num) 1742 return false; 1743 } 1744 } 1745 1746 const uint32_t count = track_entries_size_ + 1; 1747 1748 Track** const track_entries = new (std::nothrow) Track*[count]; // NOLINT 1749 if (!track_entries) 1750 return false; 1751 1752 for (uint32_t i = 0; i < track_entries_size_; ++i) { 1753 track_entries[i] = track_entries_[i]; 1754 } 1755 1756 delete[] track_entries_; 1757 1758 // Find the lowest availible track number > 0. 1759 if (track_num == 0) { 1760 track_num = count; 1761 1762 // Check to make sure a track does not already have |track_num|. 1763 bool exit = false; 1764 do { 1765 exit = true; 1766 for (uint32_t i = 0; i < track_entries_size_; ++i) { 1767 if (track_entries[i]->number() == track_num) { 1768 track_num++; 1769 exit = false; 1770 break; 1771 } 1772 } 1773 } while (!exit); 1774 } 1775 track->set_number(track_num); 1776 1777 track_entries_ = track_entries; 1778 track_entries_[track_entries_size_] = track; 1779 track_entries_size_ = count; 1780 return true; 1781} 1782 1783const Track* Tracks::GetTrackByIndex(uint32_t index) const { 1784 if (track_entries_ == NULL) 1785 return NULL; 1786 1787 if (index >= track_entries_size_) 1788 return NULL; 1789 1790 return track_entries_[index]; 1791} 1792 1793Track* Tracks::GetTrackByNumber(uint64_t track_number) const { 1794 const int32_t count = track_entries_size(); 1795 for (int32_t i = 0; i < count; ++i) { 1796 if (track_entries_[i]->number() == track_number) 1797 return track_entries_[i]; 1798 } 1799 1800 return NULL; 1801} 1802 1803bool Tracks::TrackIsAudio(uint64_t track_number) const { 1804 const Track* const track = GetTrackByNumber(track_number); 1805 1806 if (track->type() == kAudio) 1807 return true; 1808 1809 return false; 1810} 1811 1812bool Tracks::TrackIsVideo(uint64_t track_number) const { 1813 const Track* const track = GetTrackByNumber(track_number); 1814 1815 if (track->type() == kVideo) 1816 return true; 1817 1818 return false; 1819} 1820 1821bool Tracks::Write(IMkvWriter* writer) const { 1822 uint64_t size = 0; 1823 const int32_t count = track_entries_size(); 1824 for (int32_t i = 0; i < count; ++i) { 1825 const Track* const track = GetTrackByIndex(i); 1826 1827 if (!track) 1828 return false; 1829 1830 size += track->Size(); 1831 } 1832 1833 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTracks, size)) 1834 return false; 1835 1836 const int64_t payload_position = writer->Position(); 1837 if (payload_position < 0) 1838 return false; 1839 1840 for (int32_t i = 0; i < count; ++i) { 1841 const Track* const track = GetTrackByIndex(i); 1842 if (!track->Write(writer)) 1843 return false; 1844 } 1845 1846 const int64_t stop_position = writer->Position(); 1847 if (stop_position < 0 || 1848 stop_position - payload_position != static_cast<int64_t>(size)) 1849 return false; 1850 1851 wrote_tracks_ = true; 1852 return true; 1853} 1854 1855/////////////////////////////////////////////////////////////// 1856// 1857// Chapter Class 1858 1859bool Chapter::set_id(const char* id) { return StrCpy(id, &id_); } 1860 1861void Chapter::set_time(const Segment& segment, uint64_t start_ns, 1862 uint64_t end_ns) { 1863 const SegmentInfo* const info = segment.GetSegmentInfo(); 1864 const uint64_t timecode_scale = info->timecode_scale(); 1865 start_timecode_ = start_ns / timecode_scale; 1866 end_timecode_ = end_ns / timecode_scale; 1867} 1868 1869bool Chapter::add_string(const char* title, const char* language, 1870 const char* country) { 1871 if (!ExpandDisplaysArray()) 1872 return false; 1873 1874 Display& d = displays_[displays_count_++]; 1875 d.Init(); 1876 1877 if (!d.set_title(title)) 1878 return false; 1879 1880 if (!d.set_language(language)) 1881 return false; 1882 1883 if (!d.set_country(country)) 1884 return false; 1885 1886 return true; 1887} 1888 1889Chapter::Chapter() { 1890 // This ctor only constructs the object. Proper initialization is 1891 // done in Init() (called in Chapters::AddChapter()). The only 1892 // reason we bother implementing this ctor is because we had to 1893 // declare it as private (along with the dtor), in order to prevent 1894 // clients from creating Chapter instances (a privelege we grant 1895 // only to the Chapters class). Doing no initialization here also 1896 // means that creating arrays of chapter objects is more efficient, 1897 // because we only initialize each new chapter object as it becomes 1898 // active on the array. 1899} 1900 1901Chapter::~Chapter() {} 1902 1903void Chapter::Init(unsigned int* seed) { 1904 id_ = NULL; 1905 start_timecode_ = 0; 1906 end_timecode_ = 0; 1907 displays_ = NULL; 1908 displays_size_ = 0; 1909 displays_count_ = 0; 1910 uid_ = MakeUID(seed); 1911} 1912 1913void Chapter::ShallowCopy(Chapter* dst) const { 1914 dst->id_ = id_; 1915 dst->start_timecode_ = start_timecode_; 1916 dst->end_timecode_ = end_timecode_; 1917 dst->uid_ = uid_; 1918 dst->displays_ = displays_; 1919 dst->displays_size_ = displays_size_; 1920 dst->displays_count_ = displays_count_; 1921} 1922 1923void Chapter::Clear() { 1924 StrCpy(NULL, &id_); 1925 1926 while (displays_count_ > 0) { 1927 Display& d = displays_[--displays_count_]; 1928 d.Clear(); 1929 } 1930 1931 delete[] displays_; 1932 displays_ = NULL; 1933 1934 displays_size_ = 0; 1935} 1936 1937bool Chapter::ExpandDisplaysArray() { 1938 if (displays_size_ > displays_count_) 1939 return true; // nothing to do yet 1940 1941 const int size = (displays_size_ == 0) ? 1 : 2 * displays_size_; 1942 1943 Display* const displays = new (std::nothrow) Display[size]; // NOLINT 1944 if (displays == NULL) 1945 return false; 1946 1947 for (int idx = 0; idx < displays_count_; ++idx) { 1948 displays[idx] = displays_[idx]; // shallow copy 1949 } 1950 1951 delete[] displays_; 1952 1953 displays_ = displays; 1954 displays_size_ = size; 1955 1956 return true; 1957} 1958 1959uint64_t Chapter::WriteAtom(IMkvWriter* writer) const { 1960 uint64_t payload_size = 1961 EbmlElementSize(libwebm::kMkvChapterStringUID, id_) + 1962 EbmlElementSize(libwebm::kMkvChapterUID, static_cast<uint64>(uid_)) + 1963 EbmlElementSize(libwebm::kMkvChapterTimeStart, 1964 static_cast<uint64>(start_timecode_)) + 1965 EbmlElementSize(libwebm::kMkvChapterTimeEnd, 1966 static_cast<uint64>(end_timecode_)); 1967 1968 for (int idx = 0; idx < displays_count_; ++idx) { 1969 const Display& d = displays_[idx]; 1970 payload_size += d.WriteDisplay(NULL); 1971 } 1972 1973 const uint64_t atom_size = 1974 EbmlMasterElementSize(libwebm::kMkvChapterAtom, payload_size) + 1975 payload_size; 1976 1977 if (writer == NULL) 1978 return atom_size; 1979 1980 const int64_t start = writer->Position(); 1981 1982 if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapterAtom, payload_size)) 1983 return 0; 1984 1985 if (!WriteEbmlElement(writer, libwebm::kMkvChapterStringUID, id_)) 1986 return 0; 1987 1988 if (!WriteEbmlElement(writer, libwebm::kMkvChapterUID, 1989 static_cast<uint64>(uid_))) 1990 return 0; 1991 1992 if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeStart, 1993 static_cast<uint64>(start_timecode_))) 1994 return 0; 1995 1996 if (!WriteEbmlElement(writer, libwebm::kMkvChapterTimeEnd, 1997 static_cast<uint64>(end_timecode_))) 1998 return 0; 1999 2000 for (int idx = 0; idx < displays_count_; ++idx) { 2001 const Display& d = displays_[idx]; 2002 2003 if (!d.WriteDisplay(writer)) 2004 return 0; 2005 } 2006 2007 const int64_t stop = writer->Position(); 2008 2009 if (stop >= start && uint64_t(stop - start) != atom_size) 2010 return 0; 2011 2012 return atom_size; 2013} 2014 2015void Chapter::Display::Init() { 2016 title_ = NULL; 2017 language_ = NULL; 2018 country_ = NULL; 2019} 2020 2021void Chapter::Display::Clear() { 2022 StrCpy(NULL, &title_); 2023 StrCpy(NULL, &language_); 2024 StrCpy(NULL, &country_); 2025} 2026 2027bool Chapter::Display::set_title(const char* title) { 2028 return StrCpy(title, &title_); 2029} 2030 2031bool Chapter::Display::set_language(const char* language) { 2032 return StrCpy(language, &language_); 2033} 2034 2035bool Chapter::Display::set_country(const char* country) { 2036 return StrCpy(country, &country_); 2037} 2038 2039uint64_t Chapter::Display::WriteDisplay(IMkvWriter* writer) const { 2040 uint64_t payload_size = EbmlElementSize(libwebm::kMkvChapString, title_); 2041 2042 if (language_) 2043 payload_size += EbmlElementSize(libwebm::kMkvChapLanguage, language_); 2044 2045 if (country_) 2046 payload_size += EbmlElementSize(libwebm::kMkvChapCountry, country_); 2047 2048 const uint64_t display_size = 2049 EbmlMasterElementSize(libwebm::kMkvChapterDisplay, payload_size) + 2050 payload_size; 2051 2052 if (writer == NULL) 2053 return display_size; 2054 2055 const int64_t start = writer->Position(); 2056 2057 if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapterDisplay, 2058 payload_size)) 2059 return 0; 2060 2061 if (!WriteEbmlElement(writer, libwebm::kMkvChapString, title_)) 2062 return 0; 2063 2064 if (language_) { 2065 if (!WriteEbmlElement(writer, libwebm::kMkvChapLanguage, language_)) 2066 return 0; 2067 } 2068 2069 if (country_) { 2070 if (!WriteEbmlElement(writer, libwebm::kMkvChapCountry, country_)) 2071 return 0; 2072 } 2073 2074 const int64_t stop = writer->Position(); 2075 2076 if (stop >= start && uint64_t(stop - start) != display_size) 2077 return 0; 2078 2079 return display_size; 2080} 2081 2082/////////////////////////////////////////////////////////////// 2083// 2084// Chapters Class 2085 2086Chapters::Chapters() : chapters_size_(0), chapters_count_(0), chapters_(NULL) {} 2087 2088Chapters::~Chapters() { 2089 while (chapters_count_ > 0) { 2090 Chapter& chapter = chapters_[--chapters_count_]; 2091 chapter.Clear(); 2092 } 2093 2094 delete[] chapters_; 2095 chapters_ = NULL; 2096} 2097 2098int Chapters::Count() const { return chapters_count_; } 2099 2100Chapter* Chapters::AddChapter(unsigned int* seed) { 2101 if (!ExpandChaptersArray()) 2102 return NULL; 2103 2104 Chapter& chapter = chapters_[chapters_count_++]; 2105 chapter.Init(seed); 2106 2107 return &chapter; 2108} 2109 2110bool Chapters::Write(IMkvWriter* writer) const { 2111 if (writer == NULL) 2112 return false; 2113 2114 const uint64_t payload_size = WriteEdition(NULL); // return size only 2115 2116 if (!WriteEbmlMasterElement(writer, libwebm::kMkvChapters, payload_size)) 2117 return false; 2118 2119 const int64_t start = writer->Position(); 2120 2121 if (WriteEdition(writer) == 0) // error 2122 return false; 2123 2124 const int64_t stop = writer->Position(); 2125 2126 if (stop >= start && uint64_t(stop - start) != payload_size) 2127 return false; 2128 2129 return true; 2130} 2131 2132bool Chapters::ExpandChaptersArray() { 2133 if (chapters_size_ > chapters_count_) 2134 return true; // nothing to do yet 2135 2136 const int size = (chapters_size_ == 0) ? 1 : 2 * chapters_size_; 2137 2138 Chapter* const chapters = new (std::nothrow) Chapter[size]; // NOLINT 2139 if (chapters == NULL) 2140 return false; 2141 2142 for (int idx = 0; idx < chapters_count_; ++idx) { 2143 const Chapter& src = chapters_[idx]; 2144 Chapter* const dst = chapters + idx; 2145 src.ShallowCopy(dst); 2146 } 2147 2148 delete[] chapters_; 2149 2150 chapters_ = chapters; 2151 chapters_size_ = size; 2152 2153 return true; 2154} 2155 2156uint64_t Chapters::WriteEdition(IMkvWriter* writer) const { 2157 uint64_t payload_size = 0; 2158 2159 for (int idx = 0; idx < chapters_count_; ++idx) { 2160 const Chapter& chapter = chapters_[idx]; 2161 payload_size += chapter.WriteAtom(NULL); 2162 } 2163 2164 const uint64_t edition_size = 2165 EbmlMasterElementSize(libwebm::kMkvEditionEntry, payload_size) + 2166 payload_size; 2167 2168 if (writer == NULL) // return size only 2169 return edition_size; 2170 2171 const int64_t start = writer->Position(); 2172 2173 if (!WriteEbmlMasterElement(writer, libwebm::kMkvEditionEntry, payload_size)) 2174 return 0; // error 2175 2176 for (int idx = 0; idx < chapters_count_; ++idx) { 2177 const Chapter& chapter = chapters_[idx]; 2178 2179 const uint64_t chapter_size = chapter.WriteAtom(writer); 2180 if (chapter_size == 0) // error 2181 return 0; 2182 } 2183 2184 const int64_t stop = writer->Position(); 2185 2186 if (stop >= start && uint64_t(stop - start) != edition_size) 2187 return 0; 2188 2189 return edition_size; 2190} 2191 2192// Tag Class 2193 2194bool Tag::add_simple_tag(const char* tag_name, const char* tag_string) { 2195 if (!ExpandSimpleTagsArray()) 2196 return false; 2197 2198 SimpleTag& st = simple_tags_[simple_tags_count_++]; 2199 st.Init(); 2200 2201 if (!st.set_tag_name(tag_name)) 2202 return false; 2203 2204 if (!st.set_tag_string(tag_string)) 2205 return false; 2206 2207 return true; 2208} 2209 2210Tag::Tag() { 2211 simple_tags_ = NULL; 2212 simple_tags_size_ = 0; 2213 simple_tags_count_ = 0; 2214} 2215 2216Tag::~Tag() {} 2217 2218void Tag::ShallowCopy(Tag* dst) const { 2219 dst->simple_tags_ = simple_tags_; 2220 dst->simple_tags_size_ = simple_tags_size_; 2221 dst->simple_tags_count_ = simple_tags_count_; 2222} 2223 2224void Tag::Clear() { 2225 while (simple_tags_count_ > 0) { 2226 SimpleTag& st = simple_tags_[--simple_tags_count_]; 2227 st.Clear(); 2228 } 2229 2230 delete[] simple_tags_; 2231 simple_tags_ = NULL; 2232 2233 simple_tags_size_ = 0; 2234} 2235 2236bool Tag::ExpandSimpleTagsArray() { 2237 if (simple_tags_size_ > simple_tags_count_) 2238 return true; // nothing to do yet 2239 2240 const int size = (simple_tags_size_ == 0) ? 1 : 2 * simple_tags_size_; 2241 2242 SimpleTag* const simple_tags = new (std::nothrow) SimpleTag[size]; // NOLINT 2243 if (simple_tags == NULL) 2244 return false; 2245 2246 for (int idx = 0; idx < simple_tags_count_; ++idx) { 2247 simple_tags[idx] = simple_tags_[idx]; // shallow copy 2248 } 2249 2250 delete[] simple_tags_; 2251 2252 simple_tags_ = simple_tags; 2253 simple_tags_size_ = size; 2254 2255 return true; 2256} 2257 2258uint64_t Tag::Write(IMkvWriter* writer) const { 2259 uint64_t payload_size = 0; 2260 2261 for (int idx = 0; idx < simple_tags_count_; ++idx) { 2262 const SimpleTag& st = simple_tags_[idx]; 2263 payload_size += st.Write(NULL); 2264 } 2265 2266 const uint64_t tag_size = 2267 EbmlMasterElementSize(libwebm::kMkvTag, payload_size) + payload_size; 2268 2269 if (writer == NULL) 2270 return tag_size; 2271 2272 const int64_t start = writer->Position(); 2273 2274 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTag, payload_size)) 2275 return 0; 2276 2277 for (int idx = 0; idx < simple_tags_count_; ++idx) { 2278 const SimpleTag& st = simple_tags_[idx]; 2279 2280 if (!st.Write(writer)) 2281 return 0; 2282 } 2283 2284 const int64_t stop = writer->Position(); 2285 2286 if (stop >= start && uint64_t(stop - start) != tag_size) 2287 return 0; 2288 2289 return tag_size; 2290} 2291 2292// Tag::SimpleTag 2293 2294void Tag::SimpleTag::Init() { 2295 tag_name_ = NULL; 2296 tag_string_ = NULL; 2297} 2298 2299void Tag::SimpleTag::Clear() { 2300 StrCpy(NULL, &tag_name_); 2301 StrCpy(NULL, &tag_string_); 2302} 2303 2304bool Tag::SimpleTag::set_tag_name(const char* tag_name) { 2305 return StrCpy(tag_name, &tag_name_); 2306} 2307 2308bool Tag::SimpleTag::set_tag_string(const char* tag_string) { 2309 return StrCpy(tag_string, &tag_string_); 2310} 2311 2312uint64_t Tag::SimpleTag::Write(IMkvWriter* writer) const { 2313 uint64_t payload_size = EbmlElementSize(libwebm::kMkvTagName, tag_name_); 2314 2315 payload_size += EbmlElementSize(libwebm::kMkvTagString, tag_string_); 2316 2317 const uint64_t simple_tag_size = 2318 EbmlMasterElementSize(libwebm::kMkvSimpleTag, payload_size) + 2319 payload_size; 2320 2321 if (writer == NULL) 2322 return simple_tag_size; 2323 2324 const int64_t start = writer->Position(); 2325 2326 if (!WriteEbmlMasterElement(writer, libwebm::kMkvSimpleTag, payload_size)) 2327 return 0; 2328 2329 if (!WriteEbmlElement(writer, libwebm::kMkvTagName, tag_name_)) 2330 return 0; 2331 2332 if (!WriteEbmlElement(writer, libwebm::kMkvTagString, tag_string_)) 2333 return 0; 2334 2335 const int64_t stop = writer->Position(); 2336 2337 if (stop >= start && uint64_t(stop - start) != simple_tag_size) 2338 return 0; 2339 2340 return simple_tag_size; 2341} 2342 2343// Tags Class 2344 2345Tags::Tags() : tags_size_(0), tags_count_(0), tags_(NULL) {} 2346 2347Tags::~Tags() { 2348 while (tags_count_ > 0) { 2349 Tag& tag = tags_[--tags_count_]; 2350 tag.Clear(); 2351 } 2352 2353 delete[] tags_; 2354 tags_ = NULL; 2355} 2356 2357int Tags::Count() const { return tags_count_; } 2358 2359Tag* Tags::AddTag() { 2360 if (!ExpandTagsArray()) 2361 return NULL; 2362 2363 Tag& tag = tags_[tags_count_++]; 2364 2365 return &tag; 2366} 2367 2368bool Tags::Write(IMkvWriter* writer) const { 2369 if (writer == NULL) 2370 return false; 2371 2372 uint64_t payload_size = 0; 2373 2374 for (int idx = 0; idx < tags_count_; ++idx) { 2375 const Tag& tag = tags_[idx]; 2376 payload_size += tag.Write(NULL); 2377 } 2378 2379 if (!WriteEbmlMasterElement(writer, libwebm::kMkvTags, payload_size)) 2380 return false; 2381 2382 const int64_t start = writer->Position(); 2383 2384 for (int idx = 0; idx < tags_count_; ++idx) { 2385 const Tag& tag = tags_[idx]; 2386 2387 const uint64_t tag_size = tag.Write(writer); 2388 if (tag_size == 0) // error 2389 return 0; 2390 } 2391 2392 const int64_t stop = writer->Position(); 2393 2394 if (stop >= start && uint64_t(stop - start) != payload_size) 2395 return false; 2396 2397 return true; 2398} 2399 2400bool Tags::ExpandTagsArray() { 2401 if (tags_size_ > tags_count_) 2402 return true; // nothing to do yet 2403 2404 const int size = (tags_size_ == 0) ? 1 : 2 * tags_size_; 2405 2406 Tag* const tags = new (std::nothrow) Tag[size]; // NOLINT 2407 if (tags == NULL) 2408 return false; 2409 2410 for (int idx = 0; idx < tags_count_; ++idx) { 2411 const Tag& src = tags_[idx]; 2412 Tag* const dst = tags + idx; 2413 src.ShallowCopy(dst); 2414 } 2415 2416 delete[] tags_; 2417 2418 tags_ = tags; 2419 tags_size_ = size; 2420 2421 return true; 2422} 2423 2424/////////////////////////////////////////////////////////////// 2425// 2426// Cluster class 2427 2428Cluster::Cluster(uint64_t timecode, int64_t cues_pos, uint64_t timecode_scale, 2429 bool write_last_frame_with_duration, bool fixed_size_timecode) 2430 : blocks_added_(0), 2431 finalized_(false), 2432 fixed_size_timecode_(fixed_size_timecode), 2433 header_written_(false), 2434 payload_size_(0), 2435 position_for_cues_(cues_pos), 2436 size_position_(-1), 2437 timecode_(timecode), 2438 timecode_scale_(timecode_scale), 2439 write_last_frame_with_duration_(write_last_frame_with_duration), 2440 writer_(NULL) {} 2441 2442Cluster::~Cluster() { 2443 // Delete any stored frames that are left behind. This will happen if the 2444 // Cluster was not Finalized for whatever reason. 2445 while (!stored_frames_.empty()) { 2446 while (!stored_frames_.begin()->second.empty()) { 2447 delete stored_frames_.begin()->second.front(); 2448 stored_frames_.begin()->second.pop_front(); 2449 } 2450 stored_frames_.erase(stored_frames_.begin()->first); 2451 } 2452} 2453 2454bool Cluster::Init(IMkvWriter* ptr_writer) { 2455 if (!ptr_writer) { 2456 return false; 2457 } 2458 writer_ = ptr_writer; 2459 return true; 2460} 2461 2462bool Cluster::AddFrame(const Frame* const frame) { 2463 return QueueOrWriteFrame(frame); 2464} 2465 2466bool Cluster::AddFrame(const uint8_t* data, uint64_t length, 2467 uint64_t track_number, uint64_t abs_timecode, 2468 bool is_key) { 2469 Frame frame; 2470 if (!frame.Init(data, length)) 2471 return false; 2472 frame.set_track_number(track_number); 2473 frame.set_timestamp(abs_timecode); 2474 frame.set_is_key(is_key); 2475 return QueueOrWriteFrame(&frame); 2476} 2477 2478bool Cluster::AddFrameWithAdditional(const uint8_t* data, uint64_t length, 2479 const uint8_t* additional, 2480 uint64_t additional_length, 2481 uint64_t add_id, uint64_t track_number, 2482 uint64_t abs_timecode, bool is_key) { 2483 if (!additional || additional_length == 0) { 2484 return false; 2485 } 2486 Frame frame; 2487 if (!frame.Init(data, length) || 2488 !frame.AddAdditionalData(additional, additional_length, add_id)) { 2489 return false; 2490 } 2491 frame.set_track_number(track_number); 2492 frame.set_timestamp(abs_timecode); 2493 frame.set_is_key(is_key); 2494 return QueueOrWriteFrame(&frame); 2495} 2496 2497bool Cluster::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length, 2498 int64_t discard_padding, 2499 uint64_t track_number, 2500 uint64_t abs_timecode, bool is_key) { 2501 Frame frame; 2502 if (!frame.Init(data, length)) 2503 return false; 2504 frame.set_discard_padding(discard_padding); 2505 frame.set_track_number(track_number); 2506 frame.set_timestamp(abs_timecode); 2507 frame.set_is_key(is_key); 2508 return QueueOrWriteFrame(&frame); 2509} 2510 2511bool Cluster::AddMetadata(const uint8_t* data, uint64_t length, 2512 uint64_t track_number, uint64_t abs_timecode, 2513 uint64_t duration_timecode) { 2514 Frame frame; 2515 if (!frame.Init(data, length)) 2516 return false; 2517 frame.set_track_number(track_number); 2518 frame.set_timestamp(abs_timecode); 2519 frame.set_duration(duration_timecode); 2520 frame.set_is_key(true); // All metadata blocks are keyframes. 2521 return QueueOrWriteFrame(&frame); 2522} 2523 2524void Cluster::AddPayloadSize(uint64_t size) { payload_size_ += size; } 2525 2526bool Cluster::Finalize() { 2527 return !write_last_frame_with_duration_ && Finalize(false, 0); 2528} 2529 2530bool Cluster::Finalize(bool set_last_frame_duration, uint64_t duration) { 2531 if (!writer_ || finalized_) 2532 return false; 2533 2534 if (write_last_frame_with_duration_) { 2535 // Write out held back Frames. This essentially performs a k-way merge 2536 // across all tracks in the increasing order of timestamps. 2537 while (!stored_frames_.empty()) { 2538 Frame* frame = stored_frames_.begin()->second.front(); 2539 2540 // Get the next frame to write (frame with least timestamp across all 2541 // tracks). 2542 for (FrameMapIterator frames_iterator = ++stored_frames_.begin(); 2543 frames_iterator != stored_frames_.end(); ++frames_iterator) { 2544 if (frames_iterator->second.front()->timestamp() < frame->timestamp()) { 2545 frame = frames_iterator->second.front(); 2546 } 2547 } 2548 2549 // Set the duration if it's the last frame for the track. 2550 if (set_last_frame_duration && 2551 stored_frames_[frame->track_number()].size() == 1 && 2552 !frame->duration_set()) { 2553 frame->set_duration(duration - frame->timestamp()); 2554 if (!frame->is_key() && !frame->reference_block_timestamp_set()) { 2555 frame->set_reference_block_timestamp( 2556 last_block_timestamp_[frame->track_number()]); 2557 } 2558 } 2559 2560 // Write the frame and remove it from |stored_frames_|. 2561 const bool wrote_frame = DoWriteFrame(frame); 2562 stored_frames_[frame->track_number()].pop_front(); 2563 if (stored_frames_[frame->track_number()].empty()) { 2564 stored_frames_.erase(frame->track_number()); 2565 } 2566 delete frame; 2567 if (!wrote_frame) 2568 return false; 2569 } 2570 } 2571 2572 if (size_position_ == -1) 2573 return false; 2574 2575 if (writer_->Seekable()) { 2576 const int64_t pos = writer_->Position(); 2577 2578 if (writer_->Position(size_position_)) 2579 return false; 2580 2581 if (WriteUIntSize(writer_, payload_size(), 8)) 2582 return false; 2583 2584 if (writer_->Position(pos)) 2585 return false; 2586 } 2587 2588 finalized_ = true; 2589 2590 return true; 2591} 2592 2593uint64_t Cluster::Size() const { 2594 const uint64_t element_size = 2595 EbmlMasterElementSize(libwebm::kMkvCluster, 0xFFFFFFFFFFFFFFFFULL) + 2596 payload_size_; 2597 return element_size; 2598} 2599 2600bool Cluster::PreWriteBlock() { 2601 if (finalized_) 2602 return false; 2603 2604 if (!header_written_) { 2605 if (!WriteClusterHeader()) 2606 return false; 2607 } 2608 2609 return true; 2610} 2611 2612void Cluster::PostWriteBlock(uint64_t element_size) { 2613 AddPayloadSize(element_size); 2614 ++blocks_added_; 2615} 2616 2617int64_t Cluster::GetRelativeTimecode(int64_t abs_timecode) const { 2618 const int64_t cluster_timecode = this->Cluster::timecode(); 2619 const int64_t rel_timecode = 2620 static_cast<int64_t>(abs_timecode) - cluster_timecode; 2621 2622 if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode) 2623 return -1; 2624 2625 return rel_timecode; 2626} 2627 2628bool Cluster::DoWriteFrame(const Frame* const frame) { 2629 if (!frame || !frame->IsValid()) 2630 return false; 2631 2632 if (!PreWriteBlock()) 2633 return false; 2634 2635 const uint64_t element_size = WriteFrame(writer_, frame, this); 2636 if (element_size == 0) 2637 return false; 2638 2639 PostWriteBlock(element_size); 2640 last_block_timestamp_[frame->track_number()] = frame->timestamp(); 2641 return true; 2642} 2643 2644bool Cluster::QueueOrWriteFrame(const Frame* const frame) { 2645 if (!frame || !frame->IsValid()) 2646 return false; 2647 2648 // If |write_last_frame_with_duration_| is not set, then write the frame right 2649 // away. 2650 if (!write_last_frame_with_duration_) { 2651 return DoWriteFrame(frame); 2652 } 2653 2654 // Queue the current frame. 2655 uint64_t track_number = frame->track_number(); 2656 Frame* const frame_to_store = new Frame(); 2657 frame_to_store->CopyFrom(*frame); 2658 stored_frames_[track_number].push_back(frame_to_store); 2659 2660 // Iterate through all queued frames in the current track except the last one 2661 // and write it if it is okay to do so (i.e.) no other track has an held back 2662 // frame with timestamp <= the timestamp of the frame in question. 2663 std::vector<std::list<Frame*>::iterator> frames_to_erase; 2664 for (std::list<Frame *>::iterator 2665 current_track_iterator = stored_frames_[track_number].begin(), 2666 end = --stored_frames_[track_number].end(); 2667 current_track_iterator != end; ++current_track_iterator) { 2668 const Frame* const frame_to_write = *current_track_iterator; 2669 bool okay_to_write = true; 2670 for (FrameMapIterator track_iterator = stored_frames_.begin(); 2671 track_iterator != stored_frames_.end(); ++track_iterator) { 2672 if (track_iterator->first == track_number) { 2673 continue; 2674 } 2675 if (track_iterator->second.front()->timestamp() < 2676 frame_to_write->timestamp()) { 2677 okay_to_write = false; 2678 break; 2679 } 2680 } 2681 if (okay_to_write) { 2682 const bool wrote_frame = DoWriteFrame(frame_to_write); 2683 delete frame_to_write; 2684 if (!wrote_frame) 2685 return false; 2686 frames_to_erase.push_back(current_track_iterator); 2687 } else { 2688 break; 2689 } 2690 } 2691 for (std::vector<std::list<Frame*>::iterator>::iterator iterator = 2692 frames_to_erase.begin(); 2693 iterator != frames_to_erase.end(); ++iterator) { 2694 stored_frames_[track_number].erase(*iterator); 2695 } 2696 return true; 2697} 2698 2699bool Cluster::WriteClusterHeader() { 2700 if (finalized_) 2701 return false; 2702 2703 if (WriteID(writer_, libwebm::kMkvCluster)) 2704 return false; 2705 2706 // Save for later. 2707 size_position_ = writer_->Position(); 2708 2709 // Write "unknown" (EBML coded -1) as cluster size value. We need to write 8 2710 // bytes because we do not know how big our cluster will be. 2711 if (SerializeInt(writer_, kEbmlUnknownValue, 8)) 2712 return false; 2713 2714 if (!WriteEbmlElement(writer_, libwebm::kMkvTimecode, timecode(), 2715 fixed_size_timecode_ ? 8 : 0)) { 2716 return false; 2717 } 2718 AddPayloadSize(EbmlElementSize(libwebm::kMkvTimecode, timecode(), 2719 fixed_size_timecode_ ? 8 : 0)); 2720 header_written_ = true; 2721 2722 return true; 2723} 2724 2725/////////////////////////////////////////////////////////////// 2726// 2727// SeekHead Class 2728 2729SeekHead::SeekHead() : start_pos_(0ULL) { 2730 for (int32_t i = 0; i < kSeekEntryCount; ++i) { 2731 seek_entry_id_[i] = 0; 2732 seek_entry_pos_[i] = 0; 2733 } 2734} 2735 2736SeekHead::~SeekHead() {} 2737 2738bool SeekHead::Finalize(IMkvWriter* writer) const { 2739 if (writer->Seekable()) { 2740 if (start_pos_ == -1) 2741 return false; 2742 2743 uint64_t payload_size = 0; 2744 uint64_t entry_size[kSeekEntryCount]; 2745 2746 for (int32_t i = 0; i < kSeekEntryCount; ++i) { 2747 if (seek_entry_id_[i] != 0) { 2748 entry_size[i] = EbmlElementSize(libwebm::kMkvSeekID, 2749 static_cast<uint64>(seek_entry_id_[i])); 2750 entry_size[i] += EbmlElementSize( 2751 libwebm::kMkvSeekPosition, static_cast<uint64>(seek_entry_pos_[i])); 2752 2753 payload_size += 2754 EbmlMasterElementSize(libwebm::kMkvSeek, entry_size[i]) + 2755 entry_size[i]; 2756 } 2757 } 2758 2759 // No SeekHead elements 2760 if (payload_size == 0) 2761 return true; 2762 2763 const int64_t pos = writer->Position(); 2764 if (writer->Position(start_pos_)) 2765 return false; 2766 2767 if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeekHead, payload_size)) 2768 return false; 2769 2770 for (int32_t i = 0; i < kSeekEntryCount; ++i) { 2771 if (seek_entry_id_[i] != 0) { 2772 if (!WriteEbmlMasterElement(writer, libwebm::kMkvSeek, entry_size[i])) 2773 return false; 2774 2775 if (!WriteEbmlElement(writer, libwebm::kMkvSeekID, 2776 static_cast<uint64>(seek_entry_id_[i]))) 2777 return false; 2778 2779 if (!WriteEbmlElement(writer, libwebm::kMkvSeekPosition, 2780 static_cast<uint64>(seek_entry_pos_[i]))) 2781 return false; 2782 } 2783 } 2784 2785 const uint64_t total_entry_size = kSeekEntryCount * MaxEntrySize(); 2786 const uint64_t total_size = 2787 EbmlMasterElementSize(libwebm::kMkvSeekHead, total_entry_size) + 2788 total_entry_size; 2789 const int64_t size_left = total_size - (writer->Position() - start_pos_); 2790 2791 const uint64_t bytes_written = WriteVoidElement(writer, size_left); 2792 if (!bytes_written) 2793 return false; 2794 2795 if (writer->Position(pos)) 2796 return false; 2797 } 2798 2799 return true; 2800} 2801 2802bool SeekHead::Write(IMkvWriter* writer) { 2803 const uint64_t entry_size = kSeekEntryCount * MaxEntrySize(); 2804 const uint64_t size = 2805 EbmlMasterElementSize(libwebm::kMkvSeekHead, entry_size); 2806 2807 start_pos_ = writer->Position(); 2808 2809 const uint64_t bytes_written = WriteVoidElement(writer, size + entry_size); 2810 if (!bytes_written) 2811 return false; 2812 2813 return true; 2814} 2815 2816bool SeekHead::AddSeekEntry(uint32_t id, uint64_t pos) { 2817 for (int32_t i = 0; i < kSeekEntryCount; ++i) { 2818 if (seek_entry_id_[i] == 0) { 2819 seek_entry_id_[i] = id; 2820 seek_entry_pos_[i] = pos; 2821 return true; 2822 } 2823 } 2824 return false; 2825} 2826 2827uint32_t SeekHead::GetId(int index) const { 2828 if (index < 0 || index >= kSeekEntryCount) 2829 return UINT_MAX; 2830 return seek_entry_id_[index]; 2831} 2832 2833uint64_t SeekHead::GetPosition(int index) const { 2834 if (index < 0 || index >= kSeekEntryCount) 2835 return ULLONG_MAX; 2836 return seek_entry_pos_[index]; 2837} 2838 2839bool SeekHead::SetSeekEntry(int index, uint32_t id, uint64_t position) { 2840 if (index < 0 || index >= kSeekEntryCount) 2841 return false; 2842 seek_entry_id_[index] = id; 2843 seek_entry_pos_[index] = position; 2844 return true; 2845} 2846 2847uint64_t SeekHead::MaxEntrySize() const { 2848 const uint64_t max_entry_payload_size = 2849 EbmlElementSize(libwebm::kMkvSeekID, 2850 static_cast<uint64>(UINT64_C(0xffffffff))) + 2851 EbmlElementSize(libwebm::kMkvSeekPosition, 2852 static_cast<uint64>(UINT64_C(0xffffffffffffffff))); 2853 const uint64_t max_entry_size = 2854 EbmlMasterElementSize(libwebm::kMkvSeek, max_entry_payload_size) + 2855 max_entry_payload_size; 2856 2857 return max_entry_size; 2858} 2859 2860/////////////////////////////////////////////////////////////// 2861// 2862// SegmentInfo Class 2863 2864SegmentInfo::SegmentInfo() 2865 : duration_(-1.0), 2866 muxing_app_(NULL), 2867 timecode_scale_(1000000ULL), 2868 writing_app_(NULL), 2869 date_utc_(LLONG_MIN), 2870 duration_pos_(-1) {} 2871 2872SegmentInfo::~SegmentInfo() { 2873 delete[] muxing_app_; 2874 delete[] writing_app_; 2875} 2876 2877bool SegmentInfo::Init() { 2878 int32_t major; 2879 int32_t minor; 2880 int32_t build; 2881 int32_t revision; 2882 GetVersion(&major, &minor, &build, &revision); 2883 char temp[256]; 2884#ifdef _MSC_VER 2885 sprintf_s(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major, 2886 minor, build, revision); 2887#else 2888 snprintf(temp, sizeof(temp) / sizeof(temp[0]), "libwebm-%d.%d.%d.%d", major, 2889 minor, build, revision); 2890#endif 2891 2892 const size_t app_len = strlen(temp) + 1; 2893 2894 delete[] muxing_app_; 2895 2896 muxing_app_ = new (std::nothrow) char[app_len]; // NOLINT 2897 if (!muxing_app_) 2898 return false; 2899 2900#ifdef _MSC_VER 2901 strcpy_s(muxing_app_, app_len, temp); 2902#else 2903 strcpy(muxing_app_, temp); 2904#endif 2905 2906 set_writing_app(temp); 2907 if (!writing_app_) 2908 return false; 2909 return true; 2910} 2911 2912bool SegmentInfo::Finalize(IMkvWriter* writer) const { 2913 if (!writer) 2914 return false; 2915 2916 if (duration_ > 0.0) { 2917 if (writer->Seekable()) { 2918 if (duration_pos_ == -1) 2919 return false; 2920 2921 const int64_t pos = writer->Position(); 2922 2923 if (writer->Position(duration_pos_)) 2924 return false; 2925 2926 if (!WriteEbmlElement(writer, libwebm::kMkvDuration, 2927 static_cast<float>(duration_))) 2928 return false; 2929 2930 if (writer->Position(pos)) 2931 return false; 2932 } 2933 } 2934 2935 return true; 2936} 2937 2938bool SegmentInfo::Write(IMkvWriter* writer) { 2939 if (!writer || !muxing_app_ || !writing_app_) 2940 return false; 2941 2942 uint64_t size = EbmlElementSize(libwebm::kMkvTimecodeScale, 2943 static_cast<uint64>(timecode_scale_)); 2944 if (duration_ > 0.0) 2945 size += 2946 EbmlElementSize(libwebm::kMkvDuration, static_cast<float>(duration_)); 2947 if (date_utc_ != LLONG_MIN) 2948 size += EbmlDateElementSize(libwebm::kMkvDateUTC); 2949 size += EbmlElementSize(libwebm::kMkvMuxingApp, muxing_app_); 2950 size += EbmlElementSize(libwebm::kMkvWritingApp, writing_app_); 2951 2952 if (!WriteEbmlMasterElement(writer, libwebm::kMkvInfo, size)) 2953 return false; 2954 2955 const int64_t payload_position = writer->Position(); 2956 if (payload_position < 0) 2957 return false; 2958 2959 if (!WriteEbmlElement(writer, libwebm::kMkvTimecodeScale, 2960 static_cast<uint64>(timecode_scale_))) 2961 return false; 2962 2963 if (duration_ > 0.0) { 2964 // Save for later 2965 duration_pos_ = writer->Position(); 2966 2967 if (!WriteEbmlElement(writer, libwebm::kMkvDuration, 2968 static_cast<float>(duration_))) 2969 return false; 2970 } 2971 2972 if (date_utc_ != LLONG_MIN) 2973 WriteEbmlDateElement(writer, libwebm::kMkvDateUTC, date_utc_); 2974 2975 if (!WriteEbmlElement(writer, libwebm::kMkvMuxingApp, muxing_app_)) 2976 return false; 2977 if (!WriteEbmlElement(writer, libwebm::kMkvWritingApp, writing_app_)) 2978 return false; 2979 2980 const int64_t stop_position = writer->Position(); 2981 if (stop_position < 0 || 2982 stop_position - payload_position != static_cast<int64_t>(size)) 2983 return false; 2984 2985 return true; 2986} 2987 2988void SegmentInfo::set_muxing_app(const char* app) { 2989 if (app) { 2990 const size_t length = strlen(app) + 1; 2991 char* temp_str = new (std::nothrow) char[length]; // NOLINT 2992 if (!temp_str) 2993 return; 2994 2995#ifdef _MSC_VER 2996 strcpy_s(temp_str, length, app); 2997#else 2998 strcpy(temp_str, app); 2999#endif 3000 3001 delete[] muxing_app_; 3002 muxing_app_ = temp_str; 3003 } 3004} 3005 3006void SegmentInfo::set_writing_app(const char* app) { 3007 if (app) { 3008 const size_t length = strlen(app) + 1; 3009 char* temp_str = new (std::nothrow) char[length]; // NOLINT 3010 if (!temp_str) 3011 return; 3012 3013#ifdef _MSC_VER 3014 strcpy_s(temp_str, length, app); 3015#else 3016 strcpy(temp_str, app); 3017#endif 3018 3019 delete[] writing_app_; 3020 writing_app_ = temp_str; 3021 } 3022} 3023 3024/////////////////////////////////////////////////////////////// 3025// 3026// Segment Class 3027 3028Segment::Segment() 3029 : chunk_count_(0), 3030 chunk_name_(NULL), 3031 chunk_writer_cluster_(NULL), 3032 chunk_writer_cues_(NULL), 3033 chunk_writer_header_(NULL), 3034 chunking_(false), 3035 chunking_base_name_(NULL), 3036 cluster_list_(NULL), 3037 cluster_list_capacity_(0), 3038 cluster_list_size_(0), 3039 cues_position_(kAfterClusters), 3040 cues_track_(0), 3041 force_new_cluster_(false), 3042 frames_(NULL), 3043 frames_capacity_(0), 3044 frames_size_(0), 3045 has_video_(false), 3046 header_written_(false), 3047 last_block_duration_(0), 3048 last_timestamp_(0), 3049 max_cluster_duration_(kDefaultMaxClusterDuration), 3050 max_cluster_size_(0), 3051 mode_(kFile), 3052 new_cuepoint_(false), 3053 output_cues_(true), 3054 accurate_cluster_duration_(false), 3055 fixed_size_cluster_timecode_(false), 3056 estimate_file_duration_(true), 3057 payload_pos_(0), 3058 size_position_(0), 3059 doc_type_version_(kDefaultDocTypeVersion), 3060 doc_type_version_written_(0), 3061 duration_(0.0), 3062 writer_cluster_(NULL), 3063 writer_cues_(NULL), 3064 writer_header_(NULL) { 3065 const time_t curr_time = time(NULL); 3066 seed_ = static_cast<unsigned int>(curr_time); 3067#ifdef _WIN32 3068 srand(seed_); 3069#endif 3070} 3071 3072Segment::~Segment() { 3073 if (cluster_list_) { 3074 for (int32_t i = 0; i < cluster_list_size_; ++i) { 3075 Cluster* const cluster = cluster_list_[i]; 3076 delete cluster; 3077 } 3078 delete[] cluster_list_; 3079 } 3080 3081 if (frames_) { 3082 for (int32_t i = 0; i < frames_size_; ++i) { 3083 Frame* const frame = frames_[i]; 3084 delete frame; 3085 } 3086 delete[] frames_; 3087 } 3088 3089 delete[] chunk_name_; 3090 delete[] chunking_base_name_; 3091 3092 if (chunk_writer_cluster_) { 3093 chunk_writer_cluster_->Close(); 3094 delete chunk_writer_cluster_; 3095 } 3096 if (chunk_writer_cues_) { 3097 chunk_writer_cues_->Close(); 3098 delete chunk_writer_cues_; 3099 } 3100 if (chunk_writer_header_) { 3101 chunk_writer_header_->Close(); 3102 delete chunk_writer_header_; 3103 } 3104} 3105 3106void Segment::MoveCuesBeforeClustersHelper(uint64_t diff, int32_t index, 3107 uint64_t* cues_size) { 3108 CuePoint* const cue_point = cues_.GetCueByIndex(index); 3109 if (cue_point == NULL) 3110 return; 3111 const uint64_t old_cue_point_size = cue_point->Size(); 3112 const uint64_t cluster_pos = cue_point->cluster_pos() + diff; 3113 cue_point->set_cluster_pos(cluster_pos); // update the new cluster position 3114 // New size of the cue is computed as follows 3115 // Let a = current sum of size of all CuePoints 3116 // Let b = Increase in Cue Point's size due to this iteration 3117 // Let c = Increase in size of Cues Element's length due to this iteration 3118 // (This is computed as CodedSize(a + b) - CodedSize(a)) 3119 // Let d = b + c. Now d is the |diff| passed to the next recursive call. 3120 // Let e = a + b. Now e is the |cues_size| passed to the next recursive 3121 // call. 3122 const uint64_t cue_point_size_diff = cue_point->Size() - old_cue_point_size; 3123 const uint64_t cue_size_diff = 3124 GetCodedUIntSize(*cues_size + cue_point_size_diff) - 3125 GetCodedUIntSize(*cues_size); 3126 *cues_size += cue_point_size_diff; 3127 diff = cue_size_diff + cue_point_size_diff; 3128 if (diff > 0) { 3129 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) { 3130 MoveCuesBeforeClustersHelper(diff, i, cues_size); 3131 } 3132 } 3133} 3134 3135void Segment::MoveCuesBeforeClusters() { 3136 const uint64_t current_cue_size = cues_.Size(); 3137 uint64_t cue_size = 0; 3138 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) 3139 cue_size += cues_.GetCueByIndex(i)->Size(); 3140 for (int32_t i = 0; i < cues_.cue_entries_size(); ++i) 3141 MoveCuesBeforeClustersHelper(current_cue_size, i, &cue_size); 3142 3143 // Adjust the Seek Entry to reflect the change in position 3144 // of Cluster and Cues 3145 int32_t cluster_index = 0; 3146 int32_t cues_index = 0; 3147 for (int32_t i = 0; i < SeekHead::kSeekEntryCount; ++i) { 3148 if (seek_head_.GetId(i) == libwebm::kMkvCluster) 3149 cluster_index = i; 3150 if (seek_head_.GetId(i) == libwebm::kMkvCues) 3151 cues_index = i; 3152 } 3153 seek_head_.SetSeekEntry(cues_index, libwebm::kMkvCues, 3154 seek_head_.GetPosition(cluster_index)); 3155 seek_head_.SetSeekEntry(cluster_index, libwebm::kMkvCluster, 3156 cues_.Size() + seek_head_.GetPosition(cues_index)); 3157} 3158 3159bool Segment::Init(IMkvWriter* ptr_writer) { 3160 if (!ptr_writer) { 3161 return false; 3162 } 3163 writer_cluster_ = ptr_writer; 3164 writer_cues_ = ptr_writer; 3165 writer_header_ = ptr_writer; 3166 memset(&track_frames_written_, 0, 3167 sizeof(track_frames_written_[0]) * kMaxTrackNumber); 3168 memset(&last_track_timestamp_, 0, 3169 sizeof(last_track_timestamp_[0]) * kMaxTrackNumber); 3170 return segment_info_.Init(); 3171} 3172 3173bool Segment::CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader, 3174 IMkvWriter* writer) { 3175 if (!writer->Seekable() || chunking_) 3176 return false; 3177 const int64_t cluster_offset = 3178 cluster_list_[0]->size_position() - GetUIntSize(libwebm::kMkvCluster); 3179 3180 // Copy the headers. 3181 if (!ChunkedCopy(reader, writer, 0, cluster_offset)) 3182 return false; 3183 3184 // Recompute cue positions and seek entries. 3185 MoveCuesBeforeClusters(); 3186 3187 // Write cues and seek entries. 3188 // TODO(vigneshv): As of now, it's safe to call seek_head_.Finalize() for the 3189 // second time with a different writer object. But the name Finalize() doesn't 3190 // indicate something we want to call more than once. So consider renaming it 3191 // to write() or some such. 3192 if (!cues_.Write(writer) || !seek_head_.Finalize(writer)) 3193 return false; 3194 3195 // Copy the Clusters. 3196 if (!ChunkedCopy(reader, writer, cluster_offset, 3197 cluster_end_offset_ - cluster_offset)) 3198 return false; 3199 3200 // Update the Segment size in case the Cues size has changed. 3201 const int64_t pos = writer->Position(); 3202 const int64_t segment_size = writer->Position() - payload_pos_; 3203 if (writer->Position(size_position_) || 3204 WriteUIntSize(writer, segment_size, 8) || writer->Position(pos)) 3205 return false; 3206 return true; 3207} 3208 3209bool Segment::Finalize() { 3210 if (WriteFramesAll() < 0) 3211 return false; 3212 3213 // In kLive mode, call Cluster::Finalize only if |accurate_cluster_duration_| 3214 // is set. In all other modes, always call Cluster::Finalize. 3215 if ((mode_ == kLive ? accurate_cluster_duration_ : true) && 3216 cluster_list_size_ > 0) { 3217 // Update last cluster's size 3218 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1]; 3219 3220 // For the last frame of the last Cluster, we don't write it as a BlockGroup 3221 // with Duration unless the frame itself has duration set explicitly. 3222 if (!old_cluster || !old_cluster->Finalize(false, 0)) 3223 return false; 3224 } 3225 3226 if (mode_ == kFile) { 3227 if (chunking_ && chunk_writer_cluster_) { 3228 chunk_writer_cluster_->Close(); 3229 chunk_count_++; 3230 } 3231 3232 double duration = 3233 (static_cast<double>(last_timestamp_) + last_block_duration_) / 3234 segment_info_.timecode_scale(); 3235 if (duration_ > 0.0) { 3236 duration = duration_; 3237 } else { 3238 if (last_block_duration_ == 0 && estimate_file_duration_) { 3239 const int num_tracks = static_cast<int>(tracks_.track_entries_size()); 3240 for (int i = 0; i < num_tracks; ++i) { 3241 if (track_frames_written_[i] < 2) 3242 continue; 3243 3244 // Estimate the duration for the last block of a Track. 3245 const double nano_per_frame = 3246 static_cast<double>(last_track_timestamp_[i]) / 3247 (track_frames_written_[i] - 1); 3248 const double track_duration = 3249 (last_track_timestamp_[i] + nano_per_frame) / 3250 segment_info_.timecode_scale(); 3251 if (track_duration > duration) 3252 duration = track_duration; 3253 } 3254 } 3255 } 3256 segment_info_.set_duration(duration); 3257 if (!segment_info_.Finalize(writer_header_)) 3258 return false; 3259 3260 if (output_cues_) 3261 if (!seek_head_.AddSeekEntry(libwebm::kMkvCues, MaxOffset())) 3262 return false; 3263 3264 if (chunking_) { 3265 if (!chunk_writer_cues_) 3266 return false; 3267 3268 char* name = NULL; 3269 if (!UpdateChunkName("cues", &name)) 3270 return false; 3271 3272 const bool cues_open = chunk_writer_cues_->Open(name); 3273 delete[] name; 3274 if (!cues_open) 3275 return false; 3276 } 3277 3278 cluster_end_offset_ = writer_cluster_->Position(); 3279 3280 // Write the seek headers and cues 3281 if (output_cues_) 3282 if (!cues_.Write(writer_cues_)) 3283 return false; 3284 3285 if (!seek_head_.Finalize(writer_header_)) 3286 return false; 3287 3288 if (writer_header_->Seekable()) { 3289 if (size_position_ == -1) 3290 return false; 3291 3292 const int64_t segment_size = MaxOffset(); 3293 if (segment_size < 1) 3294 return false; 3295 3296 const int64_t pos = writer_header_->Position(); 3297 UpdateDocTypeVersion(); 3298 if (doc_type_version_ != doc_type_version_written_) { 3299 if (writer_header_->Position(0)) 3300 return false; 3301 3302 const char* const doc_type = 3303 DocTypeIsWebm() ? kDocTypeWebm : kDocTypeMatroska; 3304 if (!WriteEbmlHeader(writer_header_, doc_type_version_, doc_type)) 3305 return false; 3306 if (writer_header_->Position() != ebml_header_size_) 3307 return false; 3308 3309 doc_type_version_written_ = doc_type_version_; 3310 } 3311 3312 if (writer_header_->Position(size_position_)) 3313 return false; 3314 3315 if (WriteUIntSize(writer_header_, segment_size, 8)) 3316 return false; 3317 3318 if (writer_header_->Position(pos)) 3319 return false; 3320 } 3321 3322 if (chunking_) { 3323 // Do not close any writers until the segment size has been written, 3324 // otherwise the size may be off. 3325 if (!chunk_writer_cues_ || !chunk_writer_header_) 3326 return false; 3327 3328 chunk_writer_cues_->Close(); 3329 chunk_writer_header_->Close(); 3330 } 3331 } 3332 3333 return true; 3334} 3335 3336Track* Segment::AddTrack(int32_t number) { 3337 Track* const track = new (std::nothrow) Track(&seed_); // NOLINT 3338 3339 if (!track) 3340 return NULL; 3341 3342 if (!tracks_.AddTrack(track, number)) { 3343 delete track; 3344 return NULL; 3345 } 3346 3347 return track; 3348} 3349 3350Chapter* Segment::AddChapter() { return chapters_.AddChapter(&seed_); } 3351 3352Tag* Segment::AddTag() { return tags_.AddTag(); } 3353 3354uint64_t Segment::AddVideoTrack(int32_t width, int32_t height, int32_t number) { 3355 VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_); // NOLINT 3356 if (!track) 3357 return 0; 3358 3359 track->set_type(Tracks::kVideo); 3360 track->set_codec_id(Tracks::kVp8CodecId); 3361 track->set_width(width); 3362 track->set_height(height); 3363 3364 tracks_.AddTrack(track, number); 3365 has_video_ = true; 3366 3367 return track->number(); 3368} 3369 3370bool Segment::AddCuePoint(uint64_t timestamp, uint64_t track) { 3371 if (cluster_list_size_ < 1) 3372 return false; 3373 3374 const Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; 3375 if (!cluster) 3376 return false; 3377 3378 CuePoint* const cue = new (std::nothrow) CuePoint(); // NOLINT 3379 if (!cue) 3380 return false; 3381 3382 cue->set_time(timestamp / segment_info_.timecode_scale()); 3383 cue->set_block_number(cluster->blocks_added()); 3384 cue->set_cluster_pos(cluster->position_for_cues()); 3385 cue->set_track(track); 3386 if (!cues_.AddCue(cue)) 3387 return false; 3388 3389 new_cuepoint_ = false; 3390 return true; 3391} 3392 3393uint64_t Segment::AddAudioTrack(int32_t sample_rate, int32_t channels, 3394 int32_t number) { 3395 AudioTrack* const track = new (std::nothrow) AudioTrack(&seed_); // NOLINT 3396 if (!track) 3397 return 0; 3398 3399 track->set_type(Tracks::kAudio); 3400 track->set_codec_id(Tracks::kVorbisCodecId); 3401 track->set_sample_rate(sample_rate); 3402 track->set_channels(channels); 3403 3404 tracks_.AddTrack(track, number); 3405 3406 return track->number(); 3407} 3408 3409bool Segment::AddFrame(const uint8_t* data, uint64_t length, 3410 uint64_t track_number, uint64_t timestamp, bool is_key) { 3411 if (!data) 3412 return false; 3413 3414 Frame frame; 3415 if (!frame.Init(data, length)) 3416 return false; 3417 frame.set_track_number(track_number); 3418 frame.set_timestamp(timestamp); 3419 frame.set_is_key(is_key); 3420 return AddGenericFrame(&frame); 3421} 3422 3423bool Segment::AddFrameWithAdditional(const uint8_t* data, uint64_t length, 3424 const uint8_t* additional, 3425 uint64_t additional_length, 3426 uint64_t add_id, uint64_t track_number, 3427 uint64_t timestamp, bool is_key) { 3428 if (!data || !additional) 3429 return false; 3430 3431 Frame frame; 3432 if (!frame.Init(data, length) || 3433 !frame.AddAdditionalData(additional, additional_length, add_id)) { 3434 return false; 3435 } 3436 frame.set_track_number(track_number); 3437 frame.set_timestamp(timestamp); 3438 frame.set_is_key(is_key); 3439 return AddGenericFrame(&frame); 3440} 3441 3442bool Segment::AddFrameWithDiscardPadding(const uint8_t* data, uint64_t length, 3443 int64_t discard_padding, 3444 uint64_t track_number, 3445 uint64_t timestamp, bool is_key) { 3446 if (!data) 3447 return false; 3448 3449 Frame frame; 3450 if (!frame.Init(data, length)) 3451 return false; 3452 frame.set_discard_padding(discard_padding); 3453 frame.set_track_number(track_number); 3454 frame.set_timestamp(timestamp); 3455 frame.set_is_key(is_key); 3456 return AddGenericFrame(&frame); 3457} 3458 3459bool Segment::AddMetadata(const uint8_t* data, uint64_t length, 3460 uint64_t track_number, uint64_t timestamp_ns, 3461 uint64_t duration_ns) { 3462 if (!data) 3463 return false; 3464 3465 Frame frame; 3466 if (!frame.Init(data, length)) 3467 return false; 3468 frame.set_track_number(track_number); 3469 frame.set_timestamp(timestamp_ns); 3470 frame.set_duration(duration_ns); 3471 frame.set_is_key(true); // All metadata blocks are keyframes. 3472 return AddGenericFrame(&frame); 3473} 3474 3475bool Segment::AddGenericFrame(const Frame* frame) { 3476 if (!frame) 3477 return false; 3478 3479 if (!CheckHeaderInfo()) 3480 return false; 3481 3482 // Check for non-monotonically increasing timestamps. 3483 if (frame->timestamp() < last_timestamp_) 3484 return false; 3485 3486 // Check if the track number is valid. 3487 if (!tracks_.GetTrackByNumber(frame->track_number())) 3488 return false; 3489 3490 if (frame->discard_padding() != 0) 3491 doc_type_version_ = 4; 3492 3493 // If the segment has a video track hold onto audio frames to make sure the 3494 // audio that is associated with the start time of a video key-frame is 3495 // muxed into the same cluster. 3496 if (has_video_ && tracks_.TrackIsAudio(frame->track_number()) && 3497 !force_new_cluster_) { 3498 Frame* const new_frame = new (std::nothrow) Frame(); 3499 if (!new_frame || !new_frame->CopyFrom(*frame)) 3500 return false; 3501 if (!QueueFrame(new_frame)) 3502 return false; 3503 track_frames_written_[frame->track_number() - 1]++; 3504 return true; 3505 } 3506 3507 if (!DoNewClusterProcessing(frame->track_number(), frame->timestamp(), 3508 frame->is_key())) { 3509 return false; 3510 } 3511 3512 if (cluster_list_size_ < 1) 3513 return false; 3514 3515 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; 3516 if (!cluster) 3517 return false; 3518 3519 // If the Frame is not a SimpleBlock, then set the reference_block_timestamp 3520 // if it is not set already. 3521 bool frame_created = false; 3522 if (!frame->CanBeSimpleBlock() && !frame->is_key() && 3523 !frame->reference_block_timestamp_set()) { 3524 Frame* const new_frame = new (std::nothrow) Frame(); 3525 if (!new_frame->CopyFrom(*frame)) 3526 return false; 3527 new_frame->set_reference_block_timestamp( 3528 last_track_timestamp_[frame->track_number() - 1]); 3529 frame = new_frame; 3530 frame_created = true; 3531 } 3532 3533 if (!cluster->AddFrame(frame)) 3534 return false; 3535 3536 if (new_cuepoint_ && cues_track_ == frame->track_number()) { 3537 if (!AddCuePoint(frame->timestamp(), cues_track_)) 3538 return false; 3539 } 3540 3541 last_timestamp_ = frame->timestamp(); 3542 last_track_timestamp_[frame->track_number() - 1] = frame->timestamp(); 3543 last_block_duration_ = frame->duration(); 3544 track_frames_written_[frame->track_number() - 1]++; 3545 3546 if (frame_created) 3547 delete frame; 3548 return true; 3549} 3550 3551void Segment::OutputCues(bool output_cues) { output_cues_ = output_cues; } 3552 3553void Segment::AccurateClusterDuration(bool accurate_cluster_duration) { 3554 accurate_cluster_duration_ = accurate_cluster_duration; 3555} 3556 3557void Segment::UseFixedSizeClusterTimecode(bool fixed_size_cluster_timecode) { 3558 fixed_size_cluster_timecode_ = fixed_size_cluster_timecode; 3559} 3560 3561bool Segment::SetChunking(bool chunking, const char* filename) { 3562 if (chunk_count_ > 0) 3563 return false; 3564 3565 if (chunking) { 3566 if (!filename) 3567 return false; 3568 3569 // Check if we are being set to what is already set. 3570 if (chunking_ && !strcmp(filename, chunking_base_name_)) 3571 return true; 3572 3573 const size_t name_length = strlen(filename) + 1; 3574 char* const temp = new (std::nothrow) char[name_length]; // NOLINT 3575 if (!temp) 3576 return false; 3577 3578#ifdef _MSC_VER 3579 strcpy_s(temp, name_length, filename); 3580#else 3581 strcpy(temp, filename); 3582#endif 3583 3584 delete[] chunking_base_name_; 3585 chunking_base_name_ = temp; 3586 3587 if (!UpdateChunkName("chk", &chunk_name_)) 3588 return false; 3589 3590 if (!chunk_writer_cluster_) { 3591 chunk_writer_cluster_ = new (std::nothrow) MkvWriter(); // NOLINT 3592 if (!chunk_writer_cluster_) 3593 return false; 3594 } 3595 3596 if (!chunk_writer_cues_) { 3597 chunk_writer_cues_ = new (std::nothrow) MkvWriter(); // NOLINT 3598 if (!chunk_writer_cues_) 3599 return false; 3600 } 3601 3602 if (!chunk_writer_header_) { 3603 chunk_writer_header_ = new (std::nothrow) MkvWriter(); // NOLINT 3604 if (!chunk_writer_header_) 3605 return false; 3606 } 3607 3608 if (!chunk_writer_cluster_->Open(chunk_name_)) 3609 return false; 3610 3611 const size_t header_length = strlen(filename) + strlen(".hdr") + 1; 3612 char* const header = new (std::nothrow) char[header_length]; // NOLINT 3613 if (!header) 3614 return false; 3615 3616#ifdef _MSC_VER 3617 strcpy_s(header, header_length - strlen(".hdr"), chunking_base_name_); 3618 strcat_s(header, header_length, ".hdr"); 3619#else 3620 strcpy(header, chunking_base_name_); 3621 strcat(header, ".hdr"); 3622#endif 3623 if (!chunk_writer_header_->Open(header)) { 3624 delete[] header; 3625 return false; 3626 } 3627 3628 writer_cluster_ = chunk_writer_cluster_; 3629 writer_cues_ = chunk_writer_cues_; 3630 writer_header_ = chunk_writer_header_; 3631 3632 delete[] header; 3633 } 3634 3635 chunking_ = chunking; 3636 3637 return true; 3638} 3639 3640bool Segment::CuesTrack(uint64_t track_number) { 3641 const Track* const track = GetTrackByNumber(track_number); 3642 if (!track) 3643 return false; 3644 3645 cues_track_ = track_number; 3646 return true; 3647} 3648 3649void Segment::ForceNewClusterOnNextFrame() { force_new_cluster_ = true; } 3650 3651Track* Segment::GetTrackByNumber(uint64_t track_number) const { 3652 return tracks_.GetTrackByNumber(track_number); 3653} 3654 3655bool Segment::WriteSegmentHeader() { 3656 UpdateDocTypeVersion(); 3657 3658 const char* const doc_type = 3659 DocTypeIsWebm() ? kDocTypeWebm : kDocTypeMatroska; 3660 if (!WriteEbmlHeader(writer_header_, doc_type_version_, doc_type)) 3661 return false; 3662 doc_type_version_written_ = doc_type_version_; 3663 ebml_header_size_ = static_cast<int32_t>(writer_header_->Position()); 3664 3665 // Write "unknown" (-1) as segment size value. If mode is kFile, Segment 3666 // will write over duration when the file is finalized. 3667 if (WriteID(writer_header_, libwebm::kMkvSegment)) 3668 return false; 3669 3670 // Save for later. 3671 size_position_ = writer_header_->Position(); 3672 3673 // Write "unknown" (EBML coded -1) as segment size value. We need to write 8 3674 // bytes because if we are going to overwrite the segment size later we do 3675 // not know how big our segment will be. 3676 if (SerializeInt(writer_header_, kEbmlUnknownValue, 8)) 3677 return false; 3678 3679 payload_pos_ = writer_header_->Position(); 3680 3681 if (mode_ == kFile && writer_header_->Seekable()) { 3682 // Set the duration > 0.0 so SegmentInfo will write out the duration. When 3683 // the muxer is done writing we will set the correct duration and have 3684 // SegmentInfo upadte it. 3685 segment_info_.set_duration(1.0); 3686 3687 if (!seek_head_.Write(writer_header_)) 3688 return false; 3689 } 3690 3691 if (!seek_head_.AddSeekEntry(libwebm::kMkvInfo, MaxOffset())) 3692 return false; 3693 if (!segment_info_.Write(writer_header_)) 3694 return false; 3695 3696 if (!seek_head_.AddSeekEntry(libwebm::kMkvTracks, MaxOffset())) 3697 return false; 3698 if (!tracks_.Write(writer_header_)) 3699 return false; 3700 3701 if (chapters_.Count() > 0) { 3702 if (!seek_head_.AddSeekEntry(libwebm::kMkvChapters, MaxOffset())) 3703 return false; 3704 if (!chapters_.Write(writer_header_)) 3705 return false; 3706 } 3707 3708 if (tags_.Count() > 0) { 3709 if (!seek_head_.AddSeekEntry(libwebm::kMkvTags, MaxOffset())) 3710 return false; 3711 if (!tags_.Write(writer_header_)) 3712 return false; 3713 } 3714 3715 if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) { 3716 if (!chunk_writer_header_) 3717 return false; 3718 3719 chunk_writer_header_->Close(); 3720 } 3721 3722 header_written_ = true; 3723 3724 return true; 3725} 3726 3727// Here we are testing whether to create a new cluster, given a frame 3728// having time frame_timestamp_ns. 3729// 3730int Segment::TestFrame(uint64_t track_number, uint64_t frame_timestamp_ns, 3731 bool is_key) const { 3732 if (force_new_cluster_) 3733 return 1; 3734 3735 // If no clusters have been created yet, then create a new cluster 3736 // and write this frame immediately, in the new cluster. This path 3737 // should only be followed once, the first time we attempt to write 3738 // a frame. 3739 3740 if (cluster_list_size_ <= 0) 3741 return 1; 3742 3743 // There exists at least one cluster. We must compare the frame to 3744 // the last cluster, in order to determine whether the frame is 3745 // written to the existing cluster, or that a new cluster should be 3746 // created. 3747 3748 const uint64_t timecode_scale = segment_info_.timecode_scale(); 3749 const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale; 3750 3751 const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1]; 3752 const uint64_t last_cluster_timecode = last_cluster->timecode(); 3753 3754 // For completeness we test for the case when the frame's timecode 3755 // is less than the cluster's timecode. Although in principle that 3756 // is allowed, this muxer doesn't actually write clusters like that, 3757 // so this indicates a bug somewhere in our algorithm. 3758 3759 if (frame_timecode < last_cluster_timecode) // should never happen 3760 return -1; 3761 3762 // If the frame has a timestamp significantly larger than the last 3763 // cluster (in Matroska, cluster-relative timestamps are serialized 3764 // using a 16-bit signed integer), then we cannot write this frame 3765 // to that cluster, and so we must create a new cluster. 3766 3767 const int64_t delta_timecode = frame_timecode - last_cluster_timecode; 3768 3769 if (delta_timecode > kMaxBlockTimecode) 3770 return 2; 3771 3772 // We decide to create a new cluster when we have a video keyframe. 3773 // This will flush queued (audio) frames, and write the keyframe 3774 // immediately, in the newly-created cluster. 3775 3776 if (is_key && tracks_.TrackIsVideo(track_number)) 3777 return 1; 3778 3779 // Create a new cluster if we have accumulated too many frames 3780 // already, where "too many" is defined as "the total time of frames 3781 // in the cluster exceeds a threshold". 3782 3783 const uint64_t delta_ns = delta_timecode * timecode_scale; 3784 3785 if (max_cluster_duration_ > 0 && delta_ns >= max_cluster_duration_) 3786 return 1; 3787 3788 // This is similar to the case above, with the difference that a new 3789 // cluster is created when the size of the current cluster exceeds a 3790 // threshold. 3791 3792 const uint64_t cluster_size = last_cluster->payload_size(); 3793 3794 if (max_cluster_size_ > 0 && cluster_size >= max_cluster_size_) 3795 return 1; 3796 3797 // There's no need to create a new cluster, so emit this frame now. 3798 3799 return 0; 3800} 3801 3802bool Segment::MakeNewCluster(uint64_t frame_timestamp_ns) { 3803 const int32_t new_size = cluster_list_size_ + 1; 3804 3805 if (new_size > cluster_list_capacity_) { 3806 // Add more clusters. 3807 const int32_t new_capacity = 3808 (cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2; 3809 Cluster** const clusters = 3810 new (std::nothrow) Cluster*[new_capacity]; // NOLINT 3811 if (!clusters) 3812 return false; 3813 3814 for (int32_t i = 0; i < cluster_list_size_; ++i) { 3815 clusters[i] = cluster_list_[i]; 3816 } 3817 3818 delete[] cluster_list_; 3819 3820 cluster_list_ = clusters; 3821 cluster_list_capacity_ = new_capacity; 3822 } 3823 3824 if (!WriteFramesLessThan(frame_timestamp_ns)) 3825 return false; 3826 3827 if (cluster_list_size_ > 0) { 3828 // Update old cluster's size 3829 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1]; 3830 3831 if (!old_cluster || !old_cluster->Finalize(true, frame_timestamp_ns)) 3832 return false; 3833 } 3834 3835 if (output_cues_) 3836 new_cuepoint_ = true; 3837 3838 if (chunking_ && cluster_list_size_ > 0) { 3839 chunk_writer_cluster_->Close(); 3840 chunk_count_++; 3841 3842 if (!UpdateChunkName("chk", &chunk_name_)) 3843 return false; 3844 if (!chunk_writer_cluster_->Open(chunk_name_)) 3845 return false; 3846 } 3847 3848 const uint64_t timecode_scale = segment_info_.timecode_scale(); 3849 const uint64_t frame_timecode = frame_timestamp_ns / timecode_scale; 3850 3851 uint64_t cluster_timecode = frame_timecode; 3852 3853 if (frames_size_ > 0) { 3854 const Frame* const f = frames_[0]; // earliest queued frame 3855 const uint64_t ns = f->timestamp(); 3856 const uint64_t tc = ns / timecode_scale; 3857 3858 if (tc < cluster_timecode) 3859 cluster_timecode = tc; 3860 } 3861 3862 Cluster*& cluster = cluster_list_[cluster_list_size_]; 3863 const int64_t offset = MaxOffset(); 3864 cluster = new (std::nothrow) 3865 Cluster(cluster_timecode, offset, segment_info_.timecode_scale(), 3866 accurate_cluster_duration_, fixed_size_cluster_timecode_); 3867 if (!cluster) 3868 return false; 3869 3870 if (!cluster->Init(writer_cluster_)) 3871 return false; 3872 3873 cluster_list_size_ = new_size; 3874 return true; 3875} 3876 3877bool Segment::DoNewClusterProcessing(uint64_t track_number, 3878 uint64_t frame_timestamp_ns, bool is_key) { 3879 for (;;) { 3880 // Based on the characteristics of the current frame and current 3881 // cluster, decide whether to create a new cluster. 3882 const int result = TestFrame(track_number, frame_timestamp_ns, is_key); 3883 if (result < 0) // error 3884 return false; 3885 3886 // Always set force_new_cluster_ to false after TestFrame. 3887 force_new_cluster_ = false; 3888 3889 // A non-zero result means create a new cluster. 3890 if (result > 0 && !MakeNewCluster(frame_timestamp_ns)) 3891 return false; 3892 3893 // Write queued (audio) frames. 3894 const int frame_count = WriteFramesAll(); 3895 if (frame_count < 0) // error 3896 return false; 3897 3898 // Write the current frame to the current cluster (if TestFrame 3899 // returns 0) or to a newly created cluster (TestFrame returns 1). 3900 if (result <= 1) 3901 return true; 3902 3903 // TestFrame returned 2, which means there was a large time 3904 // difference between the cluster and the frame itself. Do the 3905 // test again, comparing the frame to the new cluster. 3906 } 3907} 3908 3909bool Segment::CheckHeaderInfo() { 3910 if (!header_written_) { 3911 if (!WriteSegmentHeader()) 3912 return false; 3913 3914 if (!seek_head_.AddSeekEntry(libwebm::kMkvCluster, MaxOffset())) 3915 return false; 3916 3917 if (output_cues_ && cues_track_ == 0) { 3918 // Check for a video track 3919 for (uint32_t i = 0; i < tracks_.track_entries_size(); ++i) { 3920 const Track* const track = tracks_.GetTrackByIndex(i); 3921 if (!track) 3922 return false; 3923 3924 if (tracks_.TrackIsVideo(track->number())) { 3925 cues_track_ = track->number(); 3926 break; 3927 } 3928 } 3929 3930 // Set first track found 3931 if (cues_track_ == 0) { 3932 const Track* const track = tracks_.GetTrackByIndex(0); 3933 if (!track) 3934 return false; 3935 3936 cues_track_ = track->number(); 3937 } 3938 } 3939 } 3940 return true; 3941} 3942 3943void Segment::UpdateDocTypeVersion() { 3944 for (uint32_t index = 0; index < tracks_.track_entries_size(); ++index) { 3945 const Track* track = tracks_.GetTrackByIndex(index); 3946 if (track == NULL) 3947 break; 3948 if ((track->codec_delay() || track->seek_pre_roll()) && 3949 doc_type_version_ < 4) { 3950 doc_type_version_ = 4; 3951 break; 3952 } 3953 } 3954} 3955 3956bool Segment::UpdateChunkName(const char* ext, char** name) const { 3957 if (!name || !ext) 3958 return false; 3959 3960 char ext_chk[64]; 3961#ifdef _MSC_VER 3962 sprintf_s(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext); 3963#else 3964 snprintf(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext); 3965#endif 3966 3967 const size_t length = strlen(chunking_base_name_) + strlen(ext_chk) + 1; 3968 char* const str = new (std::nothrow) char[length]; // NOLINT 3969 if (!str) 3970 return false; 3971 3972#ifdef _MSC_VER 3973 strcpy_s(str, length - strlen(ext_chk), chunking_base_name_); 3974 strcat_s(str, length, ext_chk); 3975#else 3976 strcpy(str, chunking_base_name_); 3977 strcat(str, ext_chk); 3978#endif 3979 3980 delete[] * name; 3981 *name = str; 3982 3983 return true; 3984} 3985 3986int64_t Segment::MaxOffset() { 3987 if (!writer_header_) 3988 return -1; 3989 3990 int64_t offset = writer_header_->Position() - payload_pos_; 3991 3992 if (chunking_) { 3993 for (int32_t i = 0; i < cluster_list_size_; ++i) { 3994 Cluster* const cluster = cluster_list_[i]; 3995 offset += cluster->Size(); 3996 } 3997 3998 if (writer_cues_) 3999 offset += writer_cues_->Position(); 4000 } 4001 4002 return offset; 4003} 4004 4005bool Segment::QueueFrame(Frame* frame) { 4006 const int32_t new_size = frames_size_ + 1; 4007 4008 if (new_size > frames_capacity_) { 4009 // Add more frames. 4010 const int32_t new_capacity = (!frames_capacity_) ? 2 : frames_capacity_ * 2; 4011 4012 if (new_capacity < 1) 4013 return false; 4014 4015 Frame** const frames = new (std::nothrow) Frame*[new_capacity]; // NOLINT 4016 if (!frames) 4017 return false; 4018 4019 for (int32_t i = 0; i < frames_size_; ++i) { 4020 frames[i] = frames_[i]; 4021 } 4022 4023 delete[] frames_; 4024 frames_ = frames; 4025 frames_capacity_ = new_capacity; 4026 } 4027 4028 frames_[frames_size_++] = frame; 4029 4030 return true; 4031} 4032 4033int Segment::WriteFramesAll() { 4034 if (frames_ == NULL) 4035 return 0; 4036 4037 if (cluster_list_size_ < 1) 4038 return -1; 4039 4040 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; 4041 4042 if (!cluster) 4043 return -1; 4044 4045 for (int32_t i = 0; i < frames_size_; ++i) { 4046 Frame*& frame = frames_[i]; 4047 // TODO(jzern/vigneshv): using Segment::AddGenericFrame here would limit the 4048 // places where |doc_type_version_| needs to be updated. 4049 if (frame->discard_padding() != 0) 4050 doc_type_version_ = 4; 4051 if (!cluster->AddFrame(frame)) 4052 return -1; 4053 4054 if (new_cuepoint_ && cues_track_ == frame->track_number()) { 4055 if (!AddCuePoint(frame->timestamp(), cues_track_)) 4056 return -1; 4057 } 4058 4059 if (frame->timestamp() > last_timestamp_) { 4060 last_timestamp_ = frame->timestamp(); 4061 last_track_timestamp_[frame->track_number() - 1] = frame->timestamp(); 4062 } 4063 4064 delete frame; 4065 frame = NULL; 4066 } 4067 4068 const int result = frames_size_; 4069 frames_size_ = 0; 4070 4071 return result; 4072} 4073 4074bool Segment::WriteFramesLessThan(uint64_t timestamp) { 4075 // Check |cluster_list_size_| to see if this is the first cluster. If it is 4076 // the first cluster the audio frames that are less than the first video 4077 // timesatmp will be written in a later step. 4078 if (frames_size_ > 0 && cluster_list_size_ > 0) { 4079 if (!frames_) 4080 return false; 4081 4082 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; 4083 if (!cluster) 4084 return false; 4085 4086 int32_t shift_left = 0; 4087 4088 // TODO(fgalligan): Change this to use the durations of frames instead of 4089 // the next frame's start time if the duration is accurate. 4090 for (int32_t i = 1; i < frames_size_; ++i) { 4091 const Frame* const frame_curr = frames_[i]; 4092 4093 if (frame_curr->timestamp() > timestamp) 4094 break; 4095 4096 const Frame* const frame_prev = frames_[i - 1]; 4097 if (frame_prev->discard_padding() != 0) 4098 doc_type_version_ = 4; 4099 if (!cluster->AddFrame(frame_prev)) 4100 return false; 4101 4102 if (new_cuepoint_ && cues_track_ == frame_prev->track_number()) { 4103 if (!AddCuePoint(frame_prev->timestamp(), cues_track_)) 4104 return false; 4105 } 4106 4107 ++shift_left; 4108 if (frame_prev->timestamp() > last_timestamp_) { 4109 last_timestamp_ = frame_prev->timestamp(); 4110 last_track_timestamp_[frame_prev->track_number() - 1] = 4111 frame_prev->timestamp(); 4112 } 4113 4114 delete frame_prev; 4115 } 4116 4117 if (shift_left > 0) { 4118 if (shift_left >= frames_size_) 4119 return false; 4120 4121 const int32_t new_frames_size = frames_size_ - shift_left; 4122 for (int32_t i = 0; i < new_frames_size; ++i) { 4123 frames_[i] = frames_[i + shift_left]; 4124 } 4125 4126 frames_size_ = new_frames_size; 4127 } 4128 } 4129 4130 return true; 4131} 4132 4133bool Segment::DocTypeIsWebm() const { 4134 const int kNumCodecIds = 9; 4135 4136 // TODO(vigneshv): Tweak .clang-format. 4137 const char* kWebmCodecIds[kNumCodecIds] = { 4138 Tracks::kOpusCodecId, Tracks::kVorbisCodecId, 4139 Tracks::kVp8CodecId, Tracks::kVp9CodecId, 4140 Tracks::kVp10CodecId, Tracks::kWebVttCaptionsId, 4141 Tracks::kWebVttDescriptionsId, Tracks::kWebVttMetadataId, 4142 Tracks::kWebVttSubtitlesId}; 4143 4144 const int num_tracks = static_cast<int>(tracks_.track_entries_size()); 4145 for (int track_index = 0; track_index < num_tracks; ++track_index) { 4146 const Track* const track = tracks_.GetTrackByIndex(track_index); 4147 const std::string codec_id = track->codec_id(); 4148 4149 bool id_is_webm = false; 4150 for (int id_index = 0; id_index < kNumCodecIds; ++id_index) { 4151 if (codec_id == kWebmCodecIds[id_index]) { 4152 id_is_webm = true; 4153 break; 4154 } 4155 } 4156 4157 if (!id_is_webm) 4158 return false; 4159 } 4160 4161 return true; 4162} 4163 4164} // namespace mkvmuxer 4165