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