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