OLD | NEW |
(Empty) | |
| 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.hpp" |
| 10 |
| 11 #include <climits> |
| 12 #include <cstdio> |
| 13 #include <cstdlib> |
| 14 #include <cstring> |
| 15 #include <ctime> |
| 16 #include <new> |
| 17 |
| 18 #include "mkvmuxerutil.hpp" |
| 19 #include "mkvparser.hpp" |
| 20 #include "mkvwriter.hpp" |
| 21 #include "webmids.hpp" |
| 22 |
| 23 #ifdef _MSC_VER |
| 24 // Disable MSVC warnings that suggest making code non-portable. |
| 25 #pragma warning(disable:4996) |
| 26 #endif |
| 27 |
| 28 namespace mkvmuxer { |
| 29 |
| 30 namespace { |
| 31 // Deallocate the string designated by |dst|, and then copy the |src| |
| 32 // string to |dst|. The caller owns both the |src| string and the |
| 33 // |dst| copy (hence the caller is responsible for eventually |
| 34 // deallocating the strings, either directly, or indirectly via |
| 35 // StrCpy). Returns true if the source string was successfully copied |
| 36 // to the destination. |
| 37 bool StrCpy(const char* src, char** dst_ptr) { |
| 38 if (dst_ptr == NULL) |
| 39 return false; |
| 40 |
| 41 char*& dst = *dst_ptr; |
| 42 |
| 43 delete [] dst; |
| 44 dst = NULL; |
| 45 |
| 46 if (src == NULL) |
| 47 return true; |
| 48 |
| 49 const size_t size = strlen(src) + 1; |
| 50 |
| 51 dst = new (std::nothrow) char[size]; // NOLINT |
| 52 if (dst == NULL) |
| 53 return false; |
| 54 |
| 55 strcpy(dst, src); // NOLINT |
| 56 return true; |
| 57 } |
| 58 } // namespace |
| 59 |
| 60 /////////////////////////////////////////////////////////////// |
| 61 // |
| 62 // IMkvWriter Class |
| 63 |
| 64 IMkvWriter::IMkvWriter() { |
| 65 } |
| 66 |
| 67 IMkvWriter::~IMkvWriter() { |
| 68 } |
| 69 |
| 70 bool WriteEbmlHeader(IMkvWriter* writer) { |
| 71 // Level 0 |
| 72 uint64 size = EbmlElementSize(kMkvEBMLVersion, 1ULL); |
| 73 size += EbmlElementSize(kMkvEBMLReadVersion, 1ULL); |
| 74 size += EbmlElementSize(kMkvEBMLMaxIDLength, 4ULL); |
| 75 size += EbmlElementSize(kMkvEBMLMaxSizeLength, 8ULL); |
| 76 size += EbmlElementSize(kMkvDocType, "webm"); |
| 77 size += EbmlElementSize(kMkvDocTypeVersion, 2ULL); |
| 78 size += EbmlElementSize(kMkvDocTypeReadVersion, 2ULL); |
| 79 |
| 80 if (!WriteEbmlMasterElement(writer, kMkvEBML, size)) |
| 81 return false; |
| 82 if (!WriteEbmlElement(writer, kMkvEBMLVersion, 1ULL)) |
| 83 return false; |
| 84 if (!WriteEbmlElement(writer, kMkvEBMLReadVersion, 1ULL)) |
| 85 return false; |
| 86 if (!WriteEbmlElement(writer, kMkvEBMLMaxIDLength, 4ULL)) |
| 87 return false; |
| 88 if (!WriteEbmlElement(writer, kMkvEBMLMaxSizeLength, 8ULL)) |
| 89 return false; |
| 90 if (!WriteEbmlElement(writer, kMkvDocType, "webm")) |
| 91 return false; |
| 92 if (!WriteEbmlElement(writer, kMkvDocTypeVersion, 2ULL)) |
| 93 return false; |
| 94 if (!WriteEbmlElement(writer, kMkvDocTypeReadVersion, 2ULL)) |
| 95 return false; |
| 96 |
| 97 return true; |
| 98 } |
| 99 |
| 100 bool ChunkedCopy(mkvparser::IMkvReader* source, |
| 101 mkvmuxer::IMkvWriter* dst, |
| 102 mkvmuxer::int64 start, int64 size) { |
| 103 // TODO(vigneshv): Check if this is a reasonable value. |
| 104 const uint32 kBufSize = 2048; |
| 105 uint8* buf = new uint8[kBufSize]; |
| 106 int64 offset = start; |
| 107 while (size > 0) { |
| 108 const int64 read_len = (size > kBufSize) ? kBufSize : size; |
| 109 if (source->Read(offset, static_cast<long>(read_len), buf)) |
| 110 return false; |
| 111 dst->Write(buf, static_cast<uint32>(read_len)); |
| 112 offset += read_len; |
| 113 size -= read_len; |
| 114 } |
| 115 delete[] buf; |
| 116 return true; |
| 117 } |
| 118 |
| 119 /////////////////////////////////////////////////////////////// |
| 120 // |
| 121 // Frame Class |
| 122 |
| 123 Frame::Frame() |
| 124 : add_id_(0), |
| 125 additional_(NULL), |
| 126 additional_length_(0), |
| 127 duration_(0), |
| 128 frame_(NULL), |
| 129 is_key_(false), |
| 130 length_(0), |
| 131 track_number_(0), |
| 132 timestamp_(0), |
| 133 discard_padding_(0) { |
| 134 } |
| 135 |
| 136 Frame::~Frame() { |
| 137 delete [] frame_; |
| 138 delete [] additional_; |
| 139 } |
| 140 |
| 141 bool Frame::Init(const uint8* frame, uint64 length) { |
| 142 uint8* const data = |
| 143 new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT |
| 144 if (!data) |
| 145 return false; |
| 146 |
| 147 delete [] frame_; |
| 148 frame_ = data; |
| 149 length_ = length; |
| 150 |
| 151 memcpy(frame_, frame, static_cast<size_t>(length_)); |
| 152 return true; |
| 153 } |
| 154 |
| 155 bool Frame::AddAdditionalData(const uint8* additional, uint64 length, |
| 156 uint64 add_id) { |
| 157 uint8* const data = |
| 158 new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT |
| 159 if (!data) |
| 160 return false; |
| 161 |
| 162 delete [] additional_; |
| 163 additional_ = data; |
| 164 additional_length_ = length; |
| 165 add_id_ = add_id; |
| 166 |
| 167 memcpy(additional_, additional, static_cast<size_t>(additional_length_)); |
| 168 return true; |
| 169 } |
| 170 |
| 171 /////////////////////////////////////////////////////////////// |
| 172 // |
| 173 // CuePoint Class |
| 174 |
| 175 CuePoint::CuePoint() |
| 176 : time_(0), |
| 177 track_(0), |
| 178 cluster_pos_(0), |
| 179 block_number_(1), |
| 180 output_block_number_(true) { |
| 181 } |
| 182 |
| 183 CuePoint::~CuePoint() { |
| 184 } |
| 185 |
| 186 bool CuePoint::Write(IMkvWriter* writer) const { |
| 187 if (!writer || track_ < 1 || cluster_pos_ < 1) |
| 188 return false; |
| 189 |
| 190 uint64 size = EbmlElementSize(kMkvCueClusterPosition, cluster_pos_); |
| 191 size += EbmlElementSize(kMkvCueTrack, track_); |
| 192 if (output_block_number_ && block_number_ > 1) |
| 193 size += EbmlElementSize(kMkvCueBlockNumber, block_number_); |
| 194 const uint64 track_pos_size = EbmlMasterElementSize(kMkvCueTrackPositions, |
| 195 size) + size; |
| 196 const uint64 payload_size = EbmlElementSize(kMkvCueTime, time_) + |
| 197 track_pos_size; |
| 198 |
| 199 if (!WriteEbmlMasterElement(writer, kMkvCuePoint, payload_size)) |
| 200 return false; |
| 201 |
| 202 const int64 payload_position = writer->Position(); |
| 203 if (payload_position < 0) |
| 204 return false; |
| 205 |
| 206 if (!WriteEbmlElement(writer, kMkvCueTime, time_)) |
| 207 return false; |
| 208 |
| 209 if (!WriteEbmlMasterElement(writer, kMkvCueTrackPositions, size)) |
| 210 return false; |
| 211 if (!WriteEbmlElement(writer, kMkvCueTrack, track_)) |
| 212 return false; |
| 213 if (!WriteEbmlElement(writer, kMkvCueClusterPosition, cluster_pos_)) |
| 214 return false; |
| 215 if (output_block_number_ && block_number_ > 1) |
| 216 if (!WriteEbmlElement(writer, kMkvCueBlockNumber, block_number_)) |
| 217 return false; |
| 218 |
| 219 const int64 stop_position = writer->Position(); |
| 220 if (stop_position < 0) |
| 221 return false; |
| 222 |
| 223 if (stop_position - payload_position != static_cast<int64>(payload_size)) |
| 224 return false; |
| 225 |
| 226 return true; |
| 227 } |
| 228 |
| 229 uint64 CuePoint::PayloadSize() const { |
| 230 uint64 size = EbmlElementSize(kMkvCueClusterPosition, cluster_pos_); |
| 231 size += EbmlElementSize(kMkvCueTrack, track_); |
| 232 if (output_block_number_ && block_number_ > 1) |
| 233 size += EbmlElementSize(kMkvCueBlockNumber, block_number_); |
| 234 const uint64 track_pos_size = EbmlMasterElementSize(kMkvCueTrackPositions, |
| 235 size) + size; |
| 236 const uint64 payload_size = EbmlElementSize(kMkvCueTime, time_) + |
| 237 track_pos_size; |
| 238 |
| 239 return payload_size; |
| 240 } |
| 241 |
| 242 uint64 CuePoint::Size() const { |
| 243 const uint64 payload_size = PayloadSize(); |
| 244 return EbmlMasterElementSize(kMkvCuePoint, payload_size) + payload_size; |
| 245 } |
| 246 |
| 247 /////////////////////////////////////////////////////////////// |
| 248 // |
| 249 // Cues Class |
| 250 |
| 251 Cues::Cues() |
| 252 : cue_entries_capacity_(0), |
| 253 cue_entries_size_(0), |
| 254 cue_entries_(NULL), |
| 255 output_block_number_(true) { |
| 256 } |
| 257 |
| 258 Cues::~Cues() { |
| 259 if (cue_entries_) { |
| 260 for (int32 i = 0; i < cue_entries_size_; ++i) { |
| 261 CuePoint* const cue = cue_entries_[i]; |
| 262 delete cue; |
| 263 } |
| 264 delete [] cue_entries_; |
| 265 } |
| 266 } |
| 267 |
| 268 bool Cues::AddCue(CuePoint* cue) { |
| 269 if (!cue) |
| 270 return false; |
| 271 |
| 272 if ((cue_entries_size_ + 1) > cue_entries_capacity_) { |
| 273 // Add more CuePoints. |
| 274 const int32 new_capacity = |
| 275 (!cue_entries_capacity_) ? 2 : cue_entries_capacity_ * 2; |
| 276 |
| 277 if (new_capacity < 1) |
| 278 return false; |
| 279 |
| 280 CuePoint** const cues = |
| 281 new (std::nothrow) CuePoint*[new_capacity]; // NOLINT |
| 282 if (!cues) |
| 283 return false; |
| 284 |
| 285 for (int32 i = 0; i < cue_entries_size_; ++i) { |
| 286 cues[i] = cue_entries_[i]; |
| 287 } |
| 288 |
| 289 delete [] cue_entries_; |
| 290 |
| 291 cue_entries_ = cues; |
| 292 cue_entries_capacity_ = new_capacity; |
| 293 } |
| 294 |
| 295 cue->set_output_block_number(output_block_number_); |
| 296 cue_entries_[cue_entries_size_++] = cue; |
| 297 return true; |
| 298 } |
| 299 |
| 300 CuePoint* Cues::GetCueByIndex(int32 index) const { |
| 301 if (cue_entries_ == NULL) |
| 302 return NULL; |
| 303 |
| 304 if (index >= cue_entries_size_) |
| 305 return NULL; |
| 306 |
| 307 return cue_entries_[index]; |
| 308 } |
| 309 |
| 310 uint64 Cues::Size() { |
| 311 uint64 size = 0; |
| 312 for (int32 i = 0; i < cue_entries_size_; ++i) |
| 313 size += GetCueByIndex(i)->Size(); |
| 314 size += EbmlMasterElementSize(kMkvCues, size); |
| 315 return size; |
| 316 } |
| 317 |
| 318 bool Cues::Write(IMkvWriter* writer) const { |
| 319 if (!writer) |
| 320 return false; |
| 321 |
| 322 uint64 size = 0; |
| 323 for (int32 i = 0; i < cue_entries_size_; ++i) { |
| 324 const CuePoint* const cue = GetCueByIndex(i); |
| 325 |
| 326 if (!cue) |
| 327 return false; |
| 328 |
| 329 size += cue->Size(); |
| 330 } |
| 331 |
| 332 if (!WriteEbmlMasterElement(writer, kMkvCues, size)) |
| 333 return false; |
| 334 |
| 335 const int64 payload_position = writer->Position(); |
| 336 if (payload_position < 0) |
| 337 return false; |
| 338 |
| 339 for (int32 i = 0; i < cue_entries_size_; ++i) { |
| 340 const CuePoint* const cue = GetCueByIndex(i); |
| 341 |
| 342 if (!cue->Write(writer)) |
| 343 return false; |
| 344 } |
| 345 |
| 346 const int64 stop_position = writer->Position(); |
| 347 if (stop_position < 0) |
| 348 return false; |
| 349 |
| 350 if (stop_position - payload_position != static_cast<int64>(size)) |
| 351 return false; |
| 352 |
| 353 return true; |
| 354 } |
| 355 |
| 356 /////////////////////////////////////////////////////////////// |
| 357 // |
| 358 // ContentEncAESSettings Class |
| 359 |
| 360 ContentEncAESSettings::ContentEncAESSettings() : cipher_mode_(kCTR) {} |
| 361 |
| 362 uint64 ContentEncAESSettings::Size() const { |
| 363 const uint64 payload = PayloadSize(); |
| 364 const uint64 size = |
| 365 EbmlMasterElementSize(kMkvContentEncAESSettings, payload) + payload; |
| 366 return size; |
| 367 } |
| 368 |
| 369 bool ContentEncAESSettings::Write(IMkvWriter* writer) const { |
| 370 const uint64 payload = PayloadSize(); |
| 371 |
| 372 if (!WriteEbmlMasterElement(writer, kMkvContentEncAESSettings, payload)) |
| 373 return false; |
| 374 |
| 375 const int64 payload_position = writer->Position(); |
| 376 if (payload_position < 0) |
| 377 return false; |
| 378 |
| 379 if (!WriteEbmlElement(writer, kMkvAESSettingsCipherMode, cipher_mode_)) |
| 380 return false; |
| 381 |
| 382 const int64 stop_position = writer->Position(); |
| 383 if (stop_position < 0 || |
| 384 stop_position - payload_position != static_cast<int64>(payload)) |
| 385 return false; |
| 386 |
| 387 return true; |
| 388 } |
| 389 |
| 390 uint64 ContentEncAESSettings::PayloadSize() const { |
| 391 uint64 size = EbmlElementSize(kMkvAESSettingsCipherMode, cipher_mode_); |
| 392 return size; |
| 393 } |
| 394 |
| 395 /////////////////////////////////////////////////////////////// |
| 396 // |
| 397 // ContentEncoding Class |
| 398 |
| 399 ContentEncoding::ContentEncoding() |
| 400 : enc_algo_(5), |
| 401 enc_key_id_(NULL), |
| 402 encoding_order_(0), |
| 403 encoding_scope_(1), |
| 404 encoding_type_(1), |
| 405 enc_key_id_length_(0) { |
| 406 } |
| 407 |
| 408 ContentEncoding::~ContentEncoding() { |
| 409 delete [] enc_key_id_; |
| 410 } |
| 411 |
| 412 bool ContentEncoding::SetEncryptionID(const uint8* id, uint64 length) { |
| 413 if (!id || length < 1) |
| 414 return false; |
| 415 |
| 416 delete [] enc_key_id_; |
| 417 |
| 418 enc_key_id_ = |
| 419 new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT |
| 420 if (!enc_key_id_) |
| 421 return false; |
| 422 |
| 423 memcpy(enc_key_id_, id, static_cast<size_t>(length)); |
| 424 enc_key_id_length_ = length; |
| 425 |
| 426 return true; |
| 427 } |
| 428 |
| 429 uint64 ContentEncoding::Size() const { |
| 430 const uint64 encryption_size = EncryptionSize(); |
| 431 const uint64 encoding_size = EncodingSize(0, encryption_size); |
| 432 const uint64 encodings_size = EbmlMasterElementSize(kMkvContentEncoding, |
| 433 encoding_size) + |
| 434 encoding_size; |
| 435 |
| 436 return encodings_size; |
| 437 } |
| 438 |
| 439 bool ContentEncoding::Write(IMkvWriter* writer) const { |
| 440 const uint64 encryption_size = EncryptionSize(); |
| 441 const uint64 encoding_size = EncodingSize(0, encryption_size); |
| 442 const uint64 size = EbmlMasterElementSize(kMkvContentEncoding, |
| 443 encoding_size) + |
| 444 encoding_size; |
| 445 |
| 446 const int64 payload_position = writer->Position(); |
| 447 if (payload_position < 0) |
| 448 return false; |
| 449 |
| 450 if (!WriteEbmlMasterElement(writer, kMkvContentEncoding, encoding_size)) |
| 451 return false; |
| 452 if (!WriteEbmlElement(writer, kMkvContentEncodingOrder, encoding_order_)) |
| 453 return false; |
| 454 if (!WriteEbmlElement(writer, kMkvContentEncodingScope, encoding_scope_)) |
| 455 return false; |
| 456 if (!WriteEbmlElement(writer, kMkvContentEncodingType, encoding_type_)) |
| 457 return false; |
| 458 |
| 459 if (!WriteEbmlMasterElement(writer, kMkvContentEncryption, encryption_size)) |
| 460 return false; |
| 461 if (!WriteEbmlElement(writer, kMkvContentEncAlgo, enc_algo_)) |
| 462 return false; |
| 463 if (!WriteEbmlElement(writer, |
| 464 kMkvContentEncKeyID, |
| 465 enc_key_id_, |
| 466 enc_key_id_length_)) |
| 467 return false; |
| 468 |
| 469 if (!enc_aes_settings_.Write(writer)) |
| 470 return false; |
| 471 |
| 472 const int64 stop_position = writer->Position(); |
| 473 if (stop_position < 0 || |
| 474 stop_position - payload_position != static_cast<int64>(size)) |
| 475 return false; |
| 476 |
| 477 return true; |
| 478 } |
| 479 |
| 480 uint64 ContentEncoding::EncodingSize(uint64 compresion_size, |
| 481 uint64 encryption_size) const { |
| 482 // TODO(fgalligan): Add support for compression settings. |
| 483 if (compresion_size != 0) |
| 484 return 0; |
| 485 |
| 486 uint64 encoding_size = 0; |
| 487 |
| 488 if (encryption_size > 0) { |
| 489 encoding_size += EbmlMasterElementSize(kMkvContentEncryption, |
| 490 encryption_size) + |
| 491 encryption_size; |
| 492 } |
| 493 encoding_size += EbmlElementSize(kMkvContentEncodingType, encoding_type_); |
| 494 encoding_size += EbmlElementSize(kMkvContentEncodingScope, encoding_scope_); |
| 495 encoding_size += EbmlElementSize(kMkvContentEncodingOrder, encoding_order_); |
| 496 |
| 497 return encoding_size; |
| 498 } |
| 499 |
| 500 uint64 ContentEncoding::EncryptionSize() const { |
| 501 const uint64 aes_size = enc_aes_settings_.Size(); |
| 502 |
| 503 uint64 encryption_size = EbmlElementSize(kMkvContentEncKeyID, |
| 504 enc_key_id_, |
| 505 enc_key_id_length_); |
| 506 encryption_size += EbmlElementSize(kMkvContentEncAlgo, enc_algo_); |
| 507 |
| 508 return encryption_size + aes_size; |
| 509 } |
| 510 |
| 511 /////////////////////////////////////////////////////////////// |
| 512 // |
| 513 // Track Class |
| 514 |
| 515 Track::Track(unsigned int* seed) |
| 516 : codec_id_(NULL), |
| 517 codec_private_(NULL), |
| 518 language_(NULL), |
| 519 max_block_additional_id_(0), |
| 520 name_(NULL), |
| 521 number_(0), |
| 522 type_(0), |
| 523 uid_(MakeUID(seed)), |
| 524 codec_delay_(0), |
| 525 seek_pre_roll_(0), |
| 526 codec_private_length_(0), |
| 527 content_encoding_entries_(NULL), |
| 528 content_encoding_entries_size_(0) { |
| 529 } |
| 530 |
| 531 Track::~Track() { |
| 532 delete [] codec_id_; |
| 533 delete [] codec_private_; |
| 534 delete [] language_; |
| 535 delete [] name_; |
| 536 |
| 537 if (content_encoding_entries_) { |
| 538 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) { |
| 539 ContentEncoding* const encoding = content_encoding_entries_[i]; |
| 540 delete encoding; |
| 541 } |
| 542 delete [] content_encoding_entries_; |
| 543 } |
| 544 } |
| 545 |
| 546 bool Track::AddContentEncoding() { |
| 547 const uint32 count = content_encoding_entries_size_ + 1; |
| 548 |
| 549 ContentEncoding** const content_encoding_entries = |
| 550 new (std::nothrow) ContentEncoding*[count]; // NOLINT |
| 551 if (!content_encoding_entries) |
| 552 return false; |
| 553 |
| 554 ContentEncoding* const content_encoding = |
| 555 new (std::nothrow) ContentEncoding(); // NOLINT |
| 556 if (!content_encoding) { |
| 557 delete [] content_encoding_entries; |
| 558 return false; |
| 559 } |
| 560 |
| 561 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) { |
| 562 content_encoding_entries[i] = content_encoding_entries_[i]; |
| 563 } |
| 564 |
| 565 delete [] content_encoding_entries_; |
| 566 |
| 567 content_encoding_entries_ = content_encoding_entries; |
| 568 content_encoding_entries_[content_encoding_entries_size_] = content_encoding; |
| 569 content_encoding_entries_size_ = count; |
| 570 return true; |
| 571 } |
| 572 |
| 573 ContentEncoding* Track::GetContentEncodingByIndex(uint32 index) const { |
| 574 if (content_encoding_entries_ == NULL) |
| 575 return NULL; |
| 576 |
| 577 if (index >= content_encoding_entries_size_) |
| 578 return NULL; |
| 579 |
| 580 return content_encoding_entries_[index]; |
| 581 } |
| 582 |
| 583 uint64 Track::PayloadSize() const { |
| 584 uint64 size = EbmlElementSize(kMkvTrackNumber, number_); |
| 585 size += EbmlElementSize(kMkvTrackUID, uid_); |
| 586 size += EbmlElementSize(kMkvTrackType, type_); |
| 587 if (codec_id_) |
| 588 size += EbmlElementSize(kMkvCodecID, codec_id_); |
| 589 if (codec_private_) |
| 590 size += EbmlElementSize(kMkvCodecPrivate, |
| 591 codec_private_, |
| 592 codec_private_length_); |
| 593 if (language_) |
| 594 size += EbmlElementSize(kMkvLanguage, language_); |
| 595 if (name_) |
| 596 size += EbmlElementSize(kMkvName, name_); |
| 597 if (max_block_additional_id_) |
| 598 size += EbmlElementSize(kMkvMaxBlockAdditionID, max_block_additional_id_); |
| 599 if (codec_delay_) |
| 600 size += EbmlElementSize(kMkvCodecDelay, codec_delay_); |
| 601 if (seek_pre_roll_) |
| 602 size += EbmlElementSize(kMkvSeekPreRoll, seek_pre_roll_); |
| 603 |
| 604 if (content_encoding_entries_size_ > 0) { |
| 605 uint64 content_encodings_size = 0; |
| 606 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) { |
| 607 ContentEncoding* const encoding = content_encoding_entries_[i]; |
| 608 content_encodings_size += encoding->Size(); |
| 609 } |
| 610 |
| 611 size += EbmlMasterElementSize(kMkvContentEncodings, |
| 612 content_encodings_size) + |
| 613 content_encodings_size; |
| 614 } |
| 615 |
| 616 return size; |
| 617 } |
| 618 |
| 619 uint64 Track::Size() const { |
| 620 uint64 size = PayloadSize(); |
| 621 size += EbmlMasterElementSize(kMkvTrackEntry, size); |
| 622 return size; |
| 623 } |
| 624 |
| 625 bool Track::Write(IMkvWriter* writer) const { |
| 626 if (!writer) |
| 627 return false; |
| 628 |
| 629 // |size| may be bigger than what is written out in this function because |
| 630 // derived classes may write out more data in the Track element. |
| 631 const uint64 payload_size = PayloadSize(); |
| 632 |
| 633 if (!WriteEbmlMasterElement(writer, kMkvTrackEntry, payload_size)) |
| 634 return false; |
| 635 |
| 636 uint64 size = EbmlElementSize(kMkvTrackNumber, number_); |
| 637 size += EbmlElementSize(kMkvTrackUID, uid_); |
| 638 size += EbmlElementSize(kMkvTrackType, type_); |
| 639 if (codec_id_) |
| 640 size += EbmlElementSize(kMkvCodecID, codec_id_); |
| 641 if (codec_private_) |
| 642 size += EbmlElementSize(kMkvCodecPrivate, |
| 643 codec_private_, |
| 644 codec_private_length_); |
| 645 if (language_) |
| 646 size += EbmlElementSize(kMkvLanguage, language_); |
| 647 if (name_) |
| 648 size += EbmlElementSize(kMkvName, name_); |
| 649 if (max_block_additional_id_) |
| 650 size += EbmlElementSize(kMkvMaxBlockAdditionID, max_block_additional_id_); |
| 651 if (codec_delay_) |
| 652 size += EbmlElementSize(kMkvCodecDelay, codec_delay_); |
| 653 if (seek_pre_roll_) |
| 654 size += EbmlElementSize(kMkvSeekPreRoll, seek_pre_roll_); |
| 655 |
| 656 |
| 657 const int64 payload_position = writer->Position(); |
| 658 if (payload_position < 0) |
| 659 return false; |
| 660 |
| 661 if (!WriteEbmlElement(writer, kMkvTrackNumber, number_)) |
| 662 return false; |
| 663 if (!WriteEbmlElement(writer, kMkvTrackUID, uid_)) |
| 664 return false; |
| 665 if (!WriteEbmlElement(writer, kMkvTrackType, type_)) |
| 666 return false; |
| 667 if (max_block_additional_id_) { |
| 668 if (!WriteEbmlElement(writer, |
| 669 kMkvMaxBlockAdditionID, |
| 670 max_block_additional_id_)) { |
| 671 return false; |
| 672 } |
| 673 } |
| 674 if (codec_delay_) { |
| 675 if (!WriteEbmlElement(writer, kMkvCodecDelay, codec_delay_)) |
| 676 return false; |
| 677 } |
| 678 if (seek_pre_roll_) { |
| 679 if (!WriteEbmlElement(writer, kMkvSeekPreRoll, seek_pre_roll_)) |
| 680 return false; |
| 681 } |
| 682 if (codec_id_) { |
| 683 if (!WriteEbmlElement(writer, kMkvCodecID, codec_id_)) |
| 684 return false; |
| 685 } |
| 686 if (codec_private_) { |
| 687 if (!WriteEbmlElement(writer, |
| 688 kMkvCodecPrivate, |
| 689 codec_private_, |
| 690 codec_private_length_)) |
| 691 return false; |
| 692 } |
| 693 if (language_) { |
| 694 if (!WriteEbmlElement(writer, kMkvLanguage, language_)) |
| 695 return false; |
| 696 } |
| 697 if (name_) { |
| 698 if (!WriteEbmlElement(writer, kMkvName, name_)) |
| 699 return false; |
| 700 } |
| 701 |
| 702 int64 stop_position = writer->Position(); |
| 703 if (stop_position < 0 || |
| 704 stop_position - payload_position != static_cast<int64>(size)) |
| 705 return false; |
| 706 |
| 707 if (content_encoding_entries_size_ > 0) { |
| 708 uint64 content_encodings_size = 0; |
| 709 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) { |
| 710 ContentEncoding* const encoding = content_encoding_entries_[i]; |
| 711 content_encodings_size += encoding->Size(); |
| 712 } |
| 713 |
| 714 if (!WriteEbmlMasterElement(writer, |
| 715 kMkvContentEncodings, |
| 716 content_encodings_size)) |
| 717 return false; |
| 718 |
| 719 for (uint32 i = 0; i < content_encoding_entries_size_; ++i) { |
| 720 ContentEncoding* const encoding = content_encoding_entries_[i]; |
| 721 if (!encoding->Write(writer)) |
| 722 return false; |
| 723 } |
| 724 } |
| 725 |
| 726 stop_position = writer->Position(); |
| 727 if (stop_position < 0) |
| 728 return false; |
| 729 return true; |
| 730 } |
| 731 |
| 732 bool Track::SetCodecPrivate(const uint8* codec_private, uint64 length) { |
| 733 if (!codec_private || length < 1) |
| 734 return false; |
| 735 |
| 736 delete [] codec_private_; |
| 737 |
| 738 codec_private_ = |
| 739 new (std::nothrow) uint8[static_cast<size_t>(length)]; // NOLINT |
| 740 if (!codec_private_) |
| 741 return false; |
| 742 |
| 743 memcpy(codec_private_, codec_private, static_cast<size_t>(length)); |
| 744 codec_private_length_ = length; |
| 745 |
| 746 return true; |
| 747 } |
| 748 |
| 749 void Track::set_codec_id(const char* codec_id) { |
| 750 if (codec_id) { |
| 751 delete [] codec_id_; |
| 752 |
| 753 const size_t length = strlen(codec_id) + 1; |
| 754 codec_id_ = new (std::nothrow) char[length]; // NOLINT |
| 755 if (codec_id_) { |
| 756 #ifdef _MSC_VER |
| 757 strcpy_s(codec_id_, length, codec_id); |
| 758 #else |
| 759 strcpy(codec_id_, codec_id); |
| 760 #endif |
| 761 } |
| 762 } |
| 763 } |
| 764 |
| 765 // TODO(fgalligan): Vet the language parameter. |
| 766 void Track::set_language(const char* language) { |
| 767 if (language) { |
| 768 delete [] language_; |
| 769 |
| 770 const size_t length = strlen(language) + 1; |
| 771 language_ = new (std::nothrow) char[length]; // NOLINT |
| 772 if (language_) { |
| 773 #ifdef _MSC_VER |
| 774 strcpy_s(language_, length, language); |
| 775 #else |
| 776 strcpy(language_, language); |
| 777 #endif |
| 778 } |
| 779 } |
| 780 } |
| 781 |
| 782 void Track::set_name(const char* name) { |
| 783 if (name) { |
| 784 delete [] name_; |
| 785 |
| 786 const size_t length = strlen(name) + 1; |
| 787 name_ = new (std::nothrow) char[length]; // NOLINT |
| 788 if (name_) { |
| 789 #ifdef _MSC_VER |
| 790 strcpy_s(name_, length, name); |
| 791 #else |
| 792 strcpy(name_, name); |
| 793 #endif |
| 794 } |
| 795 } |
| 796 } |
| 797 |
| 798 /////////////////////////////////////////////////////////////// |
| 799 // |
| 800 // VideoTrack Class |
| 801 |
| 802 VideoTrack::VideoTrack(unsigned int* seed) |
| 803 : Track(seed), |
| 804 display_height_(0), |
| 805 display_width_(0), |
| 806 frame_rate_(0.0), |
| 807 height_(0), |
| 808 stereo_mode_(0), |
| 809 alpha_mode_(0), |
| 810 width_(0) { |
| 811 } |
| 812 |
| 813 VideoTrack::~VideoTrack() { |
| 814 } |
| 815 |
| 816 bool VideoTrack::SetStereoMode(uint64 stereo_mode) { |
| 817 if (stereo_mode != kMono && |
| 818 stereo_mode != kSideBySideLeftIsFirst && |
| 819 stereo_mode != kTopBottomRightIsFirst && |
| 820 stereo_mode != kTopBottomLeftIsFirst && |
| 821 stereo_mode != kSideBySideRightIsFirst) |
| 822 return false; |
| 823 |
| 824 stereo_mode_ = stereo_mode; |
| 825 return true; |
| 826 } |
| 827 |
| 828 bool VideoTrack::SetAlphaMode(uint64 alpha_mode) { |
| 829 if (alpha_mode != kNoAlpha && |
| 830 alpha_mode != kAlpha) |
| 831 return false; |
| 832 |
| 833 alpha_mode_ = alpha_mode; |
| 834 return true; |
| 835 } |
| 836 |
| 837 uint64 VideoTrack::PayloadSize() const { |
| 838 const uint64 parent_size = Track::PayloadSize(); |
| 839 |
| 840 uint64 size = VideoPayloadSize(); |
| 841 size += EbmlMasterElementSize(kMkvVideo, size); |
| 842 |
| 843 return parent_size + size; |
| 844 } |
| 845 |
| 846 bool VideoTrack::Write(IMkvWriter* writer) const { |
| 847 if (!Track::Write(writer)) |
| 848 return false; |
| 849 |
| 850 const uint64 size = VideoPayloadSize(); |
| 851 |
| 852 if (!WriteEbmlMasterElement(writer, kMkvVideo, size)) |
| 853 return false; |
| 854 |
| 855 const int64 payload_position = writer->Position(); |
| 856 if (payload_position < 0) |
| 857 return false; |
| 858 |
| 859 if (!WriteEbmlElement(writer, kMkvPixelWidth, width_)) |
| 860 return false; |
| 861 if (!WriteEbmlElement(writer, kMkvPixelHeight, height_)) |
| 862 return false; |
| 863 if (display_width_ > 0) |
| 864 if (!WriteEbmlElement(writer, kMkvDisplayWidth, display_width_)) |
| 865 return false; |
| 866 if (display_height_ > 0) |
| 867 if (!WriteEbmlElement(writer, kMkvDisplayHeight, display_height_)) |
| 868 return false; |
| 869 if (stereo_mode_ > kMono) |
| 870 if (!WriteEbmlElement(writer, kMkvStereoMode, stereo_mode_)) |
| 871 return false; |
| 872 if (alpha_mode_ > kNoAlpha) |
| 873 if (!WriteEbmlElement(writer, kMkvAlphaMode, alpha_mode_)) |
| 874 return false; |
| 875 if (frame_rate_ > 0.0) |
| 876 if (!WriteEbmlElement(writer, |
| 877 kMkvFrameRate, |
| 878 static_cast<float>(frame_rate_))) |
| 879 return false; |
| 880 |
| 881 const int64 stop_position = writer->Position(); |
| 882 if (stop_position < 0 || |
| 883 stop_position - payload_position != static_cast<int64>(size)) |
| 884 return false; |
| 885 |
| 886 return true; |
| 887 } |
| 888 |
| 889 uint64 VideoTrack::VideoPayloadSize() const { |
| 890 uint64 size = EbmlElementSize(kMkvPixelWidth, width_); |
| 891 size += EbmlElementSize(kMkvPixelHeight, height_); |
| 892 if (display_width_ > 0) |
| 893 size += EbmlElementSize(kMkvDisplayWidth, display_width_); |
| 894 if (display_height_ > 0) |
| 895 size += EbmlElementSize(kMkvDisplayHeight, display_height_); |
| 896 if (stereo_mode_ > kMono) |
| 897 size += EbmlElementSize(kMkvStereoMode, stereo_mode_); |
| 898 if (alpha_mode_ > kNoAlpha) |
| 899 size += EbmlElementSize(kMkvAlphaMode, alpha_mode_); |
| 900 if (frame_rate_ > 0.0) |
| 901 size += EbmlElementSize(kMkvFrameRate, static_cast<float>(frame_rate_)); |
| 902 |
| 903 return size; |
| 904 } |
| 905 |
| 906 /////////////////////////////////////////////////////////////// |
| 907 // |
| 908 // AudioTrack Class |
| 909 |
| 910 AudioTrack::AudioTrack(unsigned int* seed) |
| 911 : Track(seed), |
| 912 bit_depth_(0), |
| 913 channels_(1), |
| 914 sample_rate_(0.0) { |
| 915 } |
| 916 |
| 917 AudioTrack::~AudioTrack() { |
| 918 } |
| 919 |
| 920 uint64 AudioTrack::PayloadSize() const { |
| 921 const uint64 parent_size = Track::PayloadSize(); |
| 922 |
| 923 uint64 size = EbmlElementSize(kMkvSamplingFrequency, |
| 924 static_cast<float>(sample_rate_)); |
| 925 size += EbmlElementSize(kMkvChannels, channels_); |
| 926 if (bit_depth_ > 0) |
| 927 size += EbmlElementSize(kMkvBitDepth, bit_depth_); |
| 928 size += EbmlMasterElementSize(kMkvAudio, size); |
| 929 |
| 930 return parent_size + size; |
| 931 } |
| 932 |
| 933 bool AudioTrack::Write(IMkvWriter* writer) const { |
| 934 if (!Track::Write(writer)) |
| 935 return false; |
| 936 |
| 937 // Calculate AudioSettings size. |
| 938 uint64 size = EbmlElementSize(kMkvSamplingFrequency, |
| 939 static_cast<float>(sample_rate_)); |
| 940 size += EbmlElementSize(kMkvChannels, channels_); |
| 941 if (bit_depth_ > 0) |
| 942 size += EbmlElementSize(kMkvBitDepth, bit_depth_); |
| 943 |
| 944 if (!WriteEbmlMasterElement(writer, kMkvAudio, size)) |
| 945 return false; |
| 946 |
| 947 const int64 payload_position = writer->Position(); |
| 948 if (payload_position < 0) |
| 949 return false; |
| 950 |
| 951 if (!WriteEbmlElement(writer, |
| 952 kMkvSamplingFrequency, |
| 953 static_cast<float>(sample_rate_))) |
| 954 return false; |
| 955 if (!WriteEbmlElement(writer, kMkvChannels, channels_)) |
| 956 return false; |
| 957 if (bit_depth_ > 0) |
| 958 if (!WriteEbmlElement(writer, kMkvBitDepth, bit_depth_)) |
| 959 return false; |
| 960 |
| 961 const int64 stop_position = writer->Position(); |
| 962 if (stop_position < 0 || |
| 963 stop_position - payload_position != static_cast<int64>(size)) |
| 964 return false; |
| 965 |
| 966 return true; |
| 967 } |
| 968 |
| 969 /////////////////////////////////////////////////////////////// |
| 970 // |
| 971 // Tracks Class |
| 972 |
| 973 const char Tracks::kOpusCodecId[] = "A_OPUS"; |
| 974 const char Tracks::kVorbisCodecId[] = "A_VORBIS"; |
| 975 const char Tracks::kVp8CodecId[] = "V_VP8"; |
| 976 const char Tracks::kVp9CodecId[] = "V_VP9"; |
| 977 |
| 978 |
| 979 Tracks::Tracks() |
| 980 : track_entries_(NULL), |
| 981 track_entries_size_(0) { |
| 982 } |
| 983 |
| 984 Tracks::~Tracks() { |
| 985 if (track_entries_) { |
| 986 for (uint32 i = 0; i < track_entries_size_; ++i) { |
| 987 Track* const track = track_entries_[i]; |
| 988 delete track; |
| 989 } |
| 990 delete [] track_entries_; |
| 991 } |
| 992 } |
| 993 |
| 994 bool Tracks::AddTrack(Track* track, int32 number) { |
| 995 if (number < 0) |
| 996 return false; |
| 997 |
| 998 // This muxer only supports track numbers in the range [1, 126], in |
| 999 // order to be able (to use Matroska integer representation) to |
| 1000 // serialize the block header (of which the track number is a part) |
| 1001 // for a frame using exactly 4 bytes. |
| 1002 |
| 1003 if (number > 0x7E) |
| 1004 return false; |
| 1005 |
| 1006 uint32 track_num = number; |
| 1007 |
| 1008 if (track_num > 0) { |
| 1009 // Check to make sure a track does not already have |track_num|. |
| 1010 for (uint32 i = 0; i < track_entries_size_; ++i) { |
| 1011 if (track_entries_[i]->number() == track_num) |
| 1012 return false; |
| 1013 } |
| 1014 } |
| 1015 |
| 1016 const uint32 count = track_entries_size_ + 1; |
| 1017 |
| 1018 Track** const track_entries = new (std::nothrow) Track*[count]; // NOLINT |
| 1019 if (!track_entries) |
| 1020 return false; |
| 1021 |
| 1022 for (uint32 i = 0; i < track_entries_size_; ++i) { |
| 1023 track_entries[i] = track_entries_[i]; |
| 1024 } |
| 1025 |
| 1026 delete [] track_entries_; |
| 1027 |
| 1028 // Find the lowest availible track number > 0. |
| 1029 if (track_num == 0) { |
| 1030 track_num = count; |
| 1031 |
| 1032 // Check to make sure a track does not already have |track_num|. |
| 1033 bool exit = false; |
| 1034 do { |
| 1035 exit = true; |
| 1036 for (uint32 i = 0; i < track_entries_size_; ++i) { |
| 1037 if (track_entries[i]->number() == track_num) { |
| 1038 track_num++; |
| 1039 exit = false; |
| 1040 break; |
| 1041 } |
| 1042 } |
| 1043 } while (!exit); |
| 1044 } |
| 1045 track->set_number(track_num); |
| 1046 |
| 1047 track_entries_ = track_entries; |
| 1048 track_entries_[track_entries_size_] = track; |
| 1049 track_entries_size_ = count; |
| 1050 return true; |
| 1051 } |
| 1052 |
| 1053 const Track* Tracks::GetTrackByIndex(uint32 index) const { |
| 1054 if (track_entries_ == NULL) |
| 1055 return NULL; |
| 1056 |
| 1057 if (index >= track_entries_size_) |
| 1058 return NULL; |
| 1059 |
| 1060 return track_entries_[index]; |
| 1061 } |
| 1062 |
| 1063 Track* Tracks::GetTrackByNumber(uint64 track_number) const { |
| 1064 const int32 count = track_entries_size(); |
| 1065 for (int32 i = 0; i < count; ++i) { |
| 1066 if (track_entries_[i]->number() == track_number) |
| 1067 return track_entries_[i]; |
| 1068 } |
| 1069 |
| 1070 return NULL; |
| 1071 } |
| 1072 |
| 1073 bool Tracks::TrackIsAudio(uint64 track_number) const { |
| 1074 const Track* const track = GetTrackByNumber(track_number); |
| 1075 |
| 1076 if (track->type() == kAudio) |
| 1077 return true; |
| 1078 |
| 1079 return false; |
| 1080 } |
| 1081 |
| 1082 bool Tracks::TrackIsVideo(uint64 track_number) const { |
| 1083 const Track* const track = GetTrackByNumber(track_number); |
| 1084 |
| 1085 if (track->type() == kVideo) |
| 1086 return true; |
| 1087 |
| 1088 return false; |
| 1089 } |
| 1090 |
| 1091 bool Tracks::Write(IMkvWriter* writer) const { |
| 1092 uint64 size = 0; |
| 1093 const int32 count = track_entries_size(); |
| 1094 for (int32 i = 0; i < count; ++i) { |
| 1095 const Track* const track = GetTrackByIndex(i); |
| 1096 |
| 1097 if (!track) |
| 1098 return false; |
| 1099 |
| 1100 size += track->Size(); |
| 1101 } |
| 1102 |
| 1103 if (!WriteEbmlMasterElement(writer, kMkvTracks, size)) |
| 1104 return false; |
| 1105 |
| 1106 const int64 payload_position = writer->Position(); |
| 1107 if (payload_position < 0) |
| 1108 return false; |
| 1109 |
| 1110 for (int32 i = 0; i < count; ++i) { |
| 1111 const Track* const track = GetTrackByIndex(i); |
| 1112 if (!track->Write(writer)) |
| 1113 return false; |
| 1114 } |
| 1115 |
| 1116 const int64 stop_position = writer->Position(); |
| 1117 if (stop_position < 0 || |
| 1118 stop_position - payload_position != static_cast<int64>(size)) |
| 1119 return false; |
| 1120 |
| 1121 return true; |
| 1122 } |
| 1123 |
| 1124 /////////////////////////////////////////////////////////////// |
| 1125 // |
| 1126 // Chapter Class |
| 1127 |
| 1128 bool Chapter::set_id(const char* id) { |
| 1129 return StrCpy(id, &id_); |
| 1130 } |
| 1131 |
| 1132 void Chapter::set_time(const Segment& segment, |
| 1133 uint64 start_ns, |
| 1134 uint64 end_ns) { |
| 1135 const SegmentInfo* const info = segment.GetSegmentInfo(); |
| 1136 const uint64 timecode_scale = info->timecode_scale(); |
| 1137 start_timecode_ = start_ns / timecode_scale; |
| 1138 end_timecode_ = end_ns / timecode_scale; |
| 1139 } |
| 1140 |
| 1141 bool Chapter::add_string(const char* title, |
| 1142 const char* language, |
| 1143 const char* country) { |
| 1144 if (!ExpandDisplaysArray()) |
| 1145 return false; |
| 1146 |
| 1147 Display& d = displays_[displays_count_++]; |
| 1148 d.Init(); |
| 1149 |
| 1150 if (!d.set_title(title)) |
| 1151 return false; |
| 1152 |
| 1153 if (!d.set_language(language)) |
| 1154 return false; |
| 1155 |
| 1156 if (!d.set_country(country)) |
| 1157 return false; |
| 1158 |
| 1159 return true; |
| 1160 } |
| 1161 |
| 1162 Chapter::Chapter() { |
| 1163 // This ctor only constructs the object. Proper initialization is |
| 1164 // done in Init() (called in Chapters::AddChapter()). The only |
| 1165 // reason we bother implementing this ctor is because we had to |
| 1166 // declare it as private (along with the dtor), in order to prevent |
| 1167 // clients from creating Chapter instances (a privelege we grant |
| 1168 // only to the Chapters class). Doing no initialization here also |
| 1169 // means that creating arrays of chapter objects is more efficient, |
| 1170 // because we only initialize each new chapter object as it becomes |
| 1171 // active on the array. |
| 1172 } |
| 1173 |
| 1174 Chapter::~Chapter() { |
| 1175 } |
| 1176 |
| 1177 void Chapter::Init(unsigned int* seed) { |
| 1178 id_ = NULL; |
| 1179 displays_ = NULL; |
| 1180 displays_size_ = 0; |
| 1181 displays_count_ = 0; |
| 1182 uid_ = MakeUID(seed); |
| 1183 } |
| 1184 |
| 1185 void Chapter::ShallowCopy(Chapter* dst) const { |
| 1186 dst->id_ = id_; |
| 1187 dst->start_timecode_ = start_timecode_; |
| 1188 dst->end_timecode_ = end_timecode_; |
| 1189 dst->uid_ = uid_; |
| 1190 dst->displays_ = displays_; |
| 1191 dst->displays_size_ = displays_size_; |
| 1192 dst->displays_count_ = displays_count_; |
| 1193 } |
| 1194 |
| 1195 void Chapter::Clear() { |
| 1196 StrCpy(NULL, &id_); |
| 1197 |
| 1198 while (displays_count_ > 0) { |
| 1199 Display& d = displays_[--displays_count_]; |
| 1200 d.Clear(); |
| 1201 } |
| 1202 |
| 1203 delete [] displays_; |
| 1204 displays_ = NULL; |
| 1205 |
| 1206 displays_size_ = 0; |
| 1207 } |
| 1208 |
| 1209 bool Chapter::ExpandDisplaysArray() { |
| 1210 if (displays_size_ > displays_count_) |
| 1211 return true; // nothing to do yet |
| 1212 |
| 1213 const int size = (displays_size_ == 0) ? 1 : 2 * displays_size_; |
| 1214 |
| 1215 Display* const displays = new (std::nothrow) Display[size]; // NOLINT |
| 1216 if (displays == NULL) |
| 1217 return false; |
| 1218 |
| 1219 for (int idx = 0; idx < displays_count_; ++idx) { |
| 1220 displays[idx] = displays_[idx]; // shallow copy |
| 1221 } |
| 1222 |
| 1223 delete [] displays_; |
| 1224 |
| 1225 displays_ = displays; |
| 1226 displays_size_ = size; |
| 1227 |
| 1228 return true; |
| 1229 } |
| 1230 |
| 1231 uint64 Chapter::WriteAtom(IMkvWriter* writer) const { |
| 1232 uint64 payload_size = |
| 1233 EbmlElementSize(kMkvChapterStringUID, id_) + |
| 1234 EbmlElementSize(kMkvChapterUID, uid_) + |
| 1235 EbmlElementSize(kMkvChapterTimeStart, start_timecode_) + |
| 1236 EbmlElementSize(kMkvChapterTimeEnd, end_timecode_); |
| 1237 |
| 1238 for (int idx = 0; idx < displays_count_; ++idx) { |
| 1239 const Display& d = displays_[idx]; |
| 1240 payload_size += d.WriteDisplay(NULL); |
| 1241 } |
| 1242 |
| 1243 const uint64 atom_size = |
| 1244 EbmlMasterElementSize(kMkvChapterAtom, payload_size) + |
| 1245 payload_size; |
| 1246 |
| 1247 if (writer == NULL) |
| 1248 return atom_size; |
| 1249 |
| 1250 const int64 start = writer->Position(); |
| 1251 |
| 1252 if (!WriteEbmlMasterElement(writer, kMkvChapterAtom, payload_size)) |
| 1253 return 0; |
| 1254 |
| 1255 if (!WriteEbmlElement(writer, kMkvChapterStringUID, id_)) |
| 1256 return 0; |
| 1257 |
| 1258 if (!WriteEbmlElement(writer, kMkvChapterUID, uid_)) |
| 1259 return 0; |
| 1260 |
| 1261 if (!WriteEbmlElement(writer, kMkvChapterTimeStart, start_timecode_)) |
| 1262 return 0; |
| 1263 |
| 1264 if (!WriteEbmlElement(writer, kMkvChapterTimeEnd, end_timecode_)) |
| 1265 return 0; |
| 1266 |
| 1267 for (int idx = 0; idx < displays_count_; ++idx) { |
| 1268 const Display& d = displays_[idx]; |
| 1269 |
| 1270 if (!d.WriteDisplay(writer)) |
| 1271 return 0; |
| 1272 } |
| 1273 |
| 1274 const int64 stop = writer->Position(); |
| 1275 |
| 1276 if (stop >= start && uint64(stop - start) != atom_size) |
| 1277 return 0; |
| 1278 |
| 1279 return atom_size; |
| 1280 } |
| 1281 |
| 1282 void Chapter::Display::Init() { |
| 1283 title_ = NULL; |
| 1284 language_ = NULL; |
| 1285 country_ = NULL; |
| 1286 } |
| 1287 |
| 1288 void Chapter::Display::Clear() { |
| 1289 StrCpy(NULL, &title_); |
| 1290 StrCpy(NULL, &language_); |
| 1291 StrCpy(NULL, &country_); |
| 1292 } |
| 1293 |
| 1294 bool Chapter::Display::set_title(const char* title) { |
| 1295 return StrCpy(title, &title_); |
| 1296 } |
| 1297 |
| 1298 bool Chapter::Display::set_language(const char* language) { |
| 1299 return StrCpy(language, &language_); |
| 1300 } |
| 1301 |
| 1302 bool Chapter::Display::set_country(const char* country) { |
| 1303 return StrCpy(country, &country_); |
| 1304 } |
| 1305 |
| 1306 uint64 Chapter::Display::WriteDisplay(IMkvWriter* writer) const { |
| 1307 uint64 payload_size = EbmlElementSize(kMkvChapString, title_); |
| 1308 |
| 1309 if (language_) |
| 1310 payload_size += EbmlElementSize(kMkvChapLanguage, language_); |
| 1311 |
| 1312 if (country_) |
| 1313 payload_size += EbmlElementSize(kMkvChapCountry, country_); |
| 1314 |
| 1315 const uint64 display_size = |
| 1316 EbmlMasterElementSize(kMkvChapterDisplay, payload_size) + |
| 1317 payload_size; |
| 1318 |
| 1319 if (writer == NULL) |
| 1320 return display_size; |
| 1321 |
| 1322 const int64 start = writer->Position(); |
| 1323 |
| 1324 if (!WriteEbmlMasterElement(writer, kMkvChapterDisplay, payload_size)) |
| 1325 return 0; |
| 1326 |
| 1327 if (!WriteEbmlElement(writer, kMkvChapString, title_)) |
| 1328 return 0; |
| 1329 |
| 1330 if (language_) { |
| 1331 if (!WriteEbmlElement(writer, kMkvChapLanguage, language_)) |
| 1332 return 0; |
| 1333 } |
| 1334 |
| 1335 if (country_) { |
| 1336 if (!WriteEbmlElement(writer, kMkvChapCountry, country_)) |
| 1337 return 0; |
| 1338 } |
| 1339 |
| 1340 const int64 stop = writer->Position(); |
| 1341 |
| 1342 if (stop >= start && uint64(stop - start) != display_size) |
| 1343 return 0; |
| 1344 |
| 1345 return display_size; |
| 1346 } |
| 1347 |
| 1348 /////////////////////////////////////////////////////////////// |
| 1349 // |
| 1350 // Chapters Class |
| 1351 |
| 1352 Chapters::Chapters() |
| 1353 : chapters_size_(0), |
| 1354 chapters_count_(0), |
| 1355 chapters_(NULL) { |
| 1356 } |
| 1357 |
| 1358 Chapters::~Chapters() { |
| 1359 while (chapters_count_ > 0) { |
| 1360 Chapter& chapter = chapters_[--chapters_count_]; |
| 1361 chapter.Clear(); |
| 1362 } |
| 1363 |
| 1364 delete [] chapters_; |
| 1365 chapters_ = NULL; |
| 1366 } |
| 1367 |
| 1368 int Chapters::Count() const { |
| 1369 return chapters_count_; |
| 1370 } |
| 1371 |
| 1372 Chapter* Chapters::AddChapter(unsigned int* seed) { |
| 1373 if (!ExpandChaptersArray()) |
| 1374 return NULL; |
| 1375 |
| 1376 Chapter& chapter = chapters_[chapters_count_++]; |
| 1377 chapter.Init(seed); |
| 1378 |
| 1379 return &chapter; |
| 1380 } |
| 1381 |
| 1382 bool Chapters::Write(IMkvWriter* writer) const { |
| 1383 if (writer == NULL) |
| 1384 return false; |
| 1385 |
| 1386 const uint64 payload_size = WriteEdition(NULL); // return size only |
| 1387 |
| 1388 if (!WriteEbmlMasterElement(writer, kMkvChapters, payload_size)) |
| 1389 return false; |
| 1390 |
| 1391 const int64 start = writer->Position(); |
| 1392 |
| 1393 if (WriteEdition(writer) == 0) // error |
| 1394 return false; |
| 1395 |
| 1396 const int64 stop = writer->Position(); |
| 1397 |
| 1398 if (stop >= start && uint64(stop - start) != payload_size) |
| 1399 return false; |
| 1400 |
| 1401 return true; |
| 1402 } |
| 1403 |
| 1404 bool Chapters::ExpandChaptersArray() { |
| 1405 if (chapters_size_ > chapters_count_) |
| 1406 return true; // nothing to do yet |
| 1407 |
| 1408 const int size = (chapters_size_ == 0) ? 1 : 2 * chapters_size_; |
| 1409 |
| 1410 Chapter* const chapters = new (std::nothrow) Chapter[size]; // NOLINT |
| 1411 if (chapters == NULL) |
| 1412 return false; |
| 1413 |
| 1414 for (int idx = 0; idx < chapters_count_; ++idx) { |
| 1415 const Chapter& src = chapters_[idx]; |
| 1416 Chapter* const dst = chapters + idx; |
| 1417 src.ShallowCopy(dst); |
| 1418 } |
| 1419 |
| 1420 delete [] chapters_; |
| 1421 |
| 1422 chapters_ = chapters; |
| 1423 chapters_size_ = size; |
| 1424 |
| 1425 return true; |
| 1426 } |
| 1427 |
| 1428 uint64 Chapters::WriteEdition(IMkvWriter* writer) const { |
| 1429 uint64 payload_size = 0; |
| 1430 |
| 1431 for (int idx = 0; idx < chapters_count_; ++idx) { |
| 1432 const Chapter& chapter = chapters_[idx]; |
| 1433 payload_size += chapter.WriteAtom(NULL); |
| 1434 } |
| 1435 |
| 1436 const uint64 edition_size = |
| 1437 EbmlMasterElementSize(kMkvEditionEntry, payload_size) + |
| 1438 payload_size; |
| 1439 |
| 1440 if (writer == NULL) // return size only |
| 1441 return edition_size; |
| 1442 |
| 1443 const int64 start = writer->Position(); |
| 1444 |
| 1445 if (!WriteEbmlMasterElement(writer, kMkvEditionEntry, payload_size)) |
| 1446 return 0; // error |
| 1447 |
| 1448 for (int idx = 0; idx < chapters_count_; ++idx) { |
| 1449 const Chapter& chapter = chapters_[idx]; |
| 1450 |
| 1451 const uint64 chapter_size = chapter.WriteAtom(writer); |
| 1452 if (chapter_size == 0) // error |
| 1453 return 0; |
| 1454 } |
| 1455 |
| 1456 const int64 stop = writer->Position(); |
| 1457 |
| 1458 if (stop >= start && uint64(stop - start) != edition_size) |
| 1459 return 0; |
| 1460 |
| 1461 return edition_size; |
| 1462 } |
| 1463 |
| 1464 /////////////////////////////////////////////////////////////// |
| 1465 // |
| 1466 // Cluster class |
| 1467 |
| 1468 Cluster::Cluster(uint64 timecode, int64 cues_pos) |
| 1469 : blocks_added_(0), |
| 1470 finalized_(false), |
| 1471 header_written_(false), |
| 1472 payload_size_(0), |
| 1473 position_for_cues_(cues_pos), |
| 1474 size_position_(-1), |
| 1475 timecode_(timecode), |
| 1476 writer_(NULL) { |
| 1477 } |
| 1478 |
| 1479 Cluster::~Cluster() { |
| 1480 } |
| 1481 |
| 1482 bool Cluster::Init(IMkvWriter* ptr_writer) { |
| 1483 if (!ptr_writer) { |
| 1484 return false; |
| 1485 } |
| 1486 writer_ = ptr_writer; |
| 1487 return true; |
| 1488 } |
| 1489 |
| 1490 bool Cluster::AddFrame(const uint8* frame, |
| 1491 uint64 length, |
| 1492 uint64 track_number, |
| 1493 uint64 abs_timecode, |
| 1494 bool is_key) { |
| 1495 return DoWriteBlock(frame, |
| 1496 length, |
| 1497 track_number, |
| 1498 abs_timecode, |
| 1499 is_key ? 1 : 0, |
| 1500 &WriteSimpleBlock); |
| 1501 } |
| 1502 |
| 1503 bool Cluster::AddFrameWithAdditional(const uint8* frame, |
| 1504 uint64 length, |
| 1505 const uint8* additional, |
| 1506 uint64 additional_length, |
| 1507 uint64 add_id, |
| 1508 uint64 track_number, |
| 1509 uint64 abs_timecode, |
| 1510 bool is_key) { |
| 1511 return DoWriteBlockWithAdditional(frame, |
| 1512 length, |
| 1513 additional, |
| 1514 additional_length, |
| 1515 add_id, |
| 1516 track_number, |
| 1517 abs_timecode, |
| 1518 is_key ? 1 : 0, |
| 1519 &WriteBlockWithAdditional); |
| 1520 } |
| 1521 |
| 1522 bool Cluster::AddFrameWithDiscardPadding(const uint8* frame, |
| 1523 uint64 length, |
| 1524 int64 discard_padding, |
| 1525 uint64 track_number, |
| 1526 uint64 abs_timecode, |
| 1527 bool is_key) { |
| 1528 return DoWriteBlockWithDiscardPadding(frame, |
| 1529 length, |
| 1530 discard_padding, |
| 1531 track_number, |
| 1532 abs_timecode, |
| 1533 is_key ? 1 : 0, |
| 1534 &WriteBlockWithDiscardPadding); |
| 1535 } |
| 1536 |
| 1537 bool Cluster::AddMetadata(const uint8* frame, |
| 1538 uint64 length, |
| 1539 uint64 track_number, |
| 1540 uint64 abs_timecode, |
| 1541 uint64 duration_timecode) { |
| 1542 return DoWriteBlock(frame, |
| 1543 length, |
| 1544 track_number, |
| 1545 abs_timecode, |
| 1546 duration_timecode, |
| 1547 &WriteMetadataBlock); |
| 1548 } |
| 1549 |
| 1550 void Cluster::AddPayloadSize(uint64 size) { |
| 1551 payload_size_ += size; |
| 1552 } |
| 1553 |
| 1554 bool Cluster::Finalize() { |
| 1555 if (!writer_ || finalized_ || size_position_ == -1) |
| 1556 return false; |
| 1557 |
| 1558 if (writer_->Seekable()) { |
| 1559 const int64 pos = writer_->Position(); |
| 1560 |
| 1561 if (writer_->Position(size_position_)) |
| 1562 return false; |
| 1563 |
| 1564 if (WriteUIntSize(writer_, payload_size(), 8)) |
| 1565 return false; |
| 1566 |
| 1567 if (writer_->Position(pos)) |
| 1568 return false; |
| 1569 } |
| 1570 |
| 1571 finalized_ = true; |
| 1572 |
| 1573 return true; |
| 1574 } |
| 1575 |
| 1576 uint64 Cluster::Size() const { |
| 1577 const uint64 element_size = |
| 1578 EbmlMasterElementSize(kMkvCluster, |
| 1579 0xFFFFFFFFFFFFFFFFULL) + payload_size_; |
| 1580 return element_size; |
| 1581 } |
| 1582 |
| 1583 template <typename Type> |
| 1584 bool Cluster::PreWriteBlock(Type* write_function) { |
| 1585 if (write_function == NULL) |
| 1586 return false; |
| 1587 |
| 1588 if (finalized_) |
| 1589 return false; |
| 1590 |
| 1591 if (!header_written_) { |
| 1592 if (!WriteClusterHeader()) |
| 1593 return false; |
| 1594 } |
| 1595 |
| 1596 return true; |
| 1597 } |
| 1598 |
| 1599 void Cluster::PostWriteBlock(uint64 element_size) { |
| 1600 AddPayloadSize(element_size); |
| 1601 ++blocks_added_; |
| 1602 } |
| 1603 |
| 1604 bool Cluster::IsValidTrackNumber(uint64 track_number) const { |
| 1605 return (track_number > 0 && track_number <= 0x7E); |
| 1606 } |
| 1607 |
| 1608 int64 Cluster::GetRelativeTimecode(int64 abs_timecode) const { |
| 1609 const int64 cluster_timecode = this->Cluster::timecode(); |
| 1610 const int64 rel_timecode = |
| 1611 static_cast<int64>(abs_timecode) - cluster_timecode; |
| 1612 |
| 1613 if (rel_timecode < 0 || rel_timecode > kMaxBlockTimecode) |
| 1614 return -1; |
| 1615 |
| 1616 return rel_timecode; |
| 1617 } |
| 1618 |
| 1619 bool Cluster::DoWriteBlock( |
| 1620 const uint8* frame, |
| 1621 uint64 length, |
| 1622 uint64 track_number, |
| 1623 uint64 abs_timecode, |
| 1624 uint64 generic_arg, |
| 1625 WriteBlock write_block) { |
| 1626 if (frame == NULL || length == 0) |
| 1627 return false; |
| 1628 |
| 1629 if (!IsValidTrackNumber(track_number)) |
| 1630 return false; |
| 1631 |
| 1632 const int64 rel_timecode = GetRelativeTimecode(abs_timecode); |
| 1633 if (rel_timecode < 0) |
| 1634 return false; |
| 1635 |
| 1636 if (!PreWriteBlock(write_block)) |
| 1637 return false; |
| 1638 |
| 1639 const uint64 element_size = (*write_block)(writer_, |
| 1640 frame, |
| 1641 length, |
| 1642 track_number, |
| 1643 rel_timecode, |
| 1644 generic_arg); |
| 1645 if (element_size == 0) |
| 1646 return false; |
| 1647 |
| 1648 PostWriteBlock(element_size); |
| 1649 return true; |
| 1650 } |
| 1651 |
| 1652 bool Cluster::DoWriteBlockWithAdditional( |
| 1653 const uint8* frame, |
| 1654 uint64 length, |
| 1655 const uint8* additional, |
| 1656 uint64 additional_length, |
| 1657 uint64 add_id, |
| 1658 uint64 track_number, |
| 1659 uint64 abs_timecode, |
| 1660 uint64 generic_arg, |
| 1661 WriteBlockAdditional write_block) { |
| 1662 if (frame == NULL || length == 0 || |
| 1663 additional == NULL || additional_length == 0) |
| 1664 return false; |
| 1665 |
| 1666 if (!IsValidTrackNumber(track_number)) |
| 1667 return false; |
| 1668 |
| 1669 const int64 rel_timecode = GetRelativeTimecode(abs_timecode); |
| 1670 if (rel_timecode < 0) |
| 1671 return false; |
| 1672 |
| 1673 if (!PreWriteBlock(write_block)) |
| 1674 return false; |
| 1675 |
| 1676 const uint64 element_size = (*write_block)(writer_, |
| 1677 frame, |
| 1678 length, |
| 1679 additional, |
| 1680 additional_length, |
| 1681 add_id, |
| 1682 track_number, |
| 1683 rel_timecode, |
| 1684 generic_arg); |
| 1685 if (element_size == 0) |
| 1686 return false; |
| 1687 |
| 1688 PostWriteBlock(element_size); |
| 1689 return true; |
| 1690 } |
| 1691 |
| 1692 bool Cluster::DoWriteBlockWithDiscardPadding( |
| 1693 const uint8* frame, |
| 1694 uint64 length, |
| 1695 int64 discard_padding, |
| 1696 uint64 track_number, |
| 1697 uint64 abs_timecode, |
| 1698 uint64 generic_arg, |
| 1699 WriteBlockDiscardPadding write_block) { |
| 1700 if (frame == NULL || length == 0 || discard_padding <= 0) |
| 1701 return false; |
| 1702 |
| 1703 if (!IsValidTrackNumber(track_number)) |
| 1704 return false; |
| 1705 |
| 1706 const int64 rel_timecode = GetRelativeTimecode(abs_timecode); |
| 1707 if (rel_timecode < 0) |
| 1708 return false; |
| 1709 |
| 1710 if (!PreWriteBlock(write_block)) |
| 1711 return false; |
| 1712 |
| 1713 const uint64 element_size = (*write_block)(writer_, |
| 1714 frame, |
| 1715 length, |
| 1716 discard_padding, |
| 1717 track_number, |
| 1718 rel_timecode, |
| 1719 generic_arg); |
| 1720 if (element_size == 0) |
| 1721 return false; |
| 1722 |
| 1723 PostWriteBlock(element_size); |
| 1724 return true; |
| 1725 } |
| 1726 |
| 1727 bool Cluster::WriteClusterHeader() { |
| 1728 if (finalized_) |
| 1729 return false; |
| 1730 |
| 1731 if (WriteID(writer_, kMkvCluster)) |
| 1732 return false; |
| 1733 |
| 1734 // Save for later. |
| 1735 size_position_ = writer_->Position(); |
| 1736 |
| 1737 // Write "unknown" (EBML coded -1) as cluster size value. We need to write 8 |
| 1738 // bytes because we do not know how big our cluster will be. |
| 1739 if (SerializeInt(writer_, kEbmlUnknownValue, 8)) |
| 1740 return false; |
| 1741 |
| 1742 if (!WriteEbmlElement(writer_, kMkvTimecode, timecode())) |
| 1743 return false; |
| 1744 AddPayloadSize(EbmlElementSize(kMkvTimecode, timecode())); |
| 1745 header_written_ = true; |
| 1746 |
| 1747 return true; |
| 1748 } |
| 1749 |
| 1750 /////////////////////////////////////////////////////////////// |
| 1751 // |
| 1752 // SeekHead Class |
| 1753 |
| 1754 SeekHead::SeekHead() : start_pos_(0ULL) { |
| 1755 for (int32 i = 0; i < kSeekEntryCount; ++i) { |
| 1756 seek_entry_id_[i] = 0; |
| 1757 seek_entry_pos_[i] = 0; |
| 1758 } |
| 1759 } |
| 1760 |
| 1761 SeekHead::~SeekHead() { |
| 1762 } |
| 1763 |
| 1764 bool SeekHead::Finalize(IMkvWriter* writer) const { |
| 1765 if (writer->Seekable()) { |
| 1766 if (start_pos_ == -1) |
| 1767 return false; |
| 1768 |
| 1769 uint64 payload_size = 0; |
| 1770 uint64 entry_size[kSeekEntryCount]; |
| 1771 |
| 1772 for (int32 i = 0; i < kSeekEntryCount; ++i) { |
| 1773 if (seek_entry_id_[i] != 0) { |
| 1774 entry_size[i] = EbmlElementSize( |
| 1775 kMkvSeekID, |
| 1776 static_cast<uint64>(seek_entry_id_[i])); |
| 1777 entry_size[i] += EbmlElementSize(kMkvSeekPosition, seek_entry_pos_[i]); |
| 1778 |
| 1779 payload_size += EbmlMasterElementSize(kMkvSeek, entry_size[i]) + |
| 1780 entry_size[i]; |
| 1781 } |
| 1782 } |
| 1783 |
| 1784 // No SeekHead elements |
| 1785 if (payload_size == 0) |
| 1786 return true; |
| 1787 |
| 1788 const int64 pos = writer->Position(); |
| 1789 if (writer->Position(start_pos_)) |
| 1790 return false; |
| 1791 |
| 1792 if (!WriteEbmlMasterElement(writer, kMkvSeekHead, payload_size)) |
| 1793 return false; |
| 1794 |
| 1795 for (int32 i = 0; i < kSeekEntryCount; ++i) { |
| 1796 if (seek_entry_id_[i] != 0) { |
| 1797 if (!WriteEbmlMasterElement(writer, kMkvSeek, entry_size[i])) |
| 1798 return false; |
| 1799 |
| 1800 if (!WriteEbmlElement(writer, |
| 1801 kMkvSeekID, |
| 1802 static_cast<uint64>(seek_entry_id_[i]))) |
| 1803 return false; |
| 1804 |
| 1805 if (!WriteEbmlElement(writer, kMkvSeekPosition, seek_entry_pos_[i])) |
| 1806 return false; |
| 1807 } |
| 1808 } |
| 1809 |
| 1810 const uint64 total_entry_size = kSeekEntryCount * MaxEntrySize(); |
| 1811 const uint64 total_size = |
| 1812 EbmlMasterElementSize(kMkvSeekHead, |
| 1813 total_entry_size) + total_entry_size; |
| 1814 const int64 size_left = total_size - (writer->Position() - start_pos_); |
| 1815 |
| 1816 const uint64 bytes_written = WriteVoidElement(writer, size_left); |
| 1817 if (!bytes_written) |
| 1818 return false; |
| 1819 |
| 1820 if (writer->Position(pos)) |
| 1821 return false; |
| 1822 } |
| 1823 |
| 1824 return true; |
| 1825 } |
| 1826 |
| 1827 bool SeekHead::Write(IMkvWriter* writer) { |
| 1828 const uint64 entry_size = kSeekEntryCount * MaxEntrySize(); |
| 1829 const uint64 size = EbmlMasterElementSize(kMkvSeekHead, entry_size); |
| 1830 |
| 1831 start_pos_ = writer->Position(); |
| 1832 |
| 1833 const uint64 bytes_written = WriteVoidElement(writer, size + entry_size); |
| 1834 if (!bytes_written) |
| 1835 return false; |
| 1836 |
| 1837 return true; |
| 1838 } |
| 1839 |
| 1840 bool SeekHead::AddSeekEntry(uint32 id, uint64 pos) { |
| 1841 for (int32 i = 0; i < kSeekEntryCount; ++i) { |
| 1842 if (seek_entry_id_[i] == 0) { |
| 1843 seek_entry_id_[i] = id; |
| 1844 seek_entry_pos_[i] = pos; |
| 1845 return true; |
| 1846 } |
| 1847 } |
| 1848 return false; |
| 1849 } |
| 1850 |
| 1851 uint32 SeekHead::GetId(int index) const { |
| 1852 if (index < 0 || index >= kSeekEntryCount) |
| 1853 return UINT_MAX; |
| 1854 return seek_entry_id_[index]; |
| 1855 } |
| 1856 |
| 1857 uint64 SeekHead::GetPosition(int index) const { |
| 1858 if (index < 0 || index >= kSeekEntryCount) |
| 1859 return ULLONG_MAX; |
| 1860 return seek_entry_pos_[index]; |
| 1861 } |
| 1862 |
| 1863 bool SeekHead::SetSeekEntry(int index, uint32 id, uint64 position) { |
| 1864 if (index < 0 || index >= kSeekEntryCount) |
| 1865 return false; |
| 1866 seek_entry_id_[index] = id; |
| 1867 seek_entry_pos_[index] = position; |
| 1868 return true; |
| 1869 } |
| 1870 |
| 1871 uint64 SeekHead::MaxEntrySize() const { |
| 1872 const uint64 max_entry_payload_size = |
| 1873 EbmlElementSize(kMkvSeekID, 0xffffffffULL) + |
| 1874 EbmlElementSize(kMkvSeekPosition, 0xffffffffffffffffULL); |
| 1875 const uint64 max_entry_size = |
| 1876 EbmlMasterElementSize(kMkvSeek, max_entry_payload_size) + |
| 1877 max_entry_payload_size; |
| 1878 |
| 1879 return max_entry_size; |
| 1880 } |
| 1881 |
| 1882 /////////////////////////////////////////////////////////////// |
| 1883 // |
| 1884 // SegmentInfo Class |
| 1885 |
| 1886 SegmentInfo::SegmentInfo() |
| 1887 : duration_(-1.0), |
| 1888 muxing_app_(NULL), |
| 1889 timecode_scale_(1000000ULL), |
| 1890 writing_app_(NULL), |
| 1891 duration_pos_(-1) { |
| 1892 } |
| 1893 |
| 1894 SegmentInfo::~SegmentInfo() { |
| 1895 delete [] muxing_app_; |
| 1896 delete [] writing_app_; |
| 1897 } |
| 1898 |
| 1899 bool SegmentInfo::Init() { |
| 1900 int32 major; |
| 1901 int32 minor; |
| 1902 int32 build; |
| 1903 int32 revision; |
| 1904 GetVersion(&major, &minor, &build, &revision); |
| 1905 char temp[256]; |
| 1906 #ifdef _MSC_VER |
| 1907 sprintf_s(temp, |
| 1908 sizeof(temp)/sizeof(temp[0]), |
| 1909 "libwebm-%d.%d.%d.%d", |
| 1910 major, |
| 1911 minor, |
| 1912 build, |
| 1913 revision); |
| 1914 #else |
| 1915 snprintf(temp, |
| 1916 sizeof(temp)/sizeof(temp[0]), |
| 1917 "libwebm-%d.%d.%d.%d", |
| 1918 major, |
| 1919 minor, |
| 1920 build, |
| 1921 revision); |
| 1922 #endif |
| 1923 |
| 1924 const size_t app_len = strlen(temp) + 1; |
| 1925 |
| 1926 delete [] muxing_app_; |
| 1927 |
| 1928 muxing_app_ = new (std::nothrow) char[app_len]; // NOLINT |
| 1929 if (!muxing_app_) |
| 1930 return false; |
| 1931 |
| 1932 #ifdef _MSC_VER |
| 1933 strcpy_s(muxing_app_, app_len, temp); |
| 1934 #else |
| 1935 strcpy(muxing_app_, temp); |
| 1936 #endif |
| 1937 |
| 1938 set_writing_app(temp); |
| 1939 if (!writing_app_) |
| 1940 return false; |
| 1941 return true; |
| 1942 } |
| 1943 |
| 1944 bool SegmentInfo::Finalize(IMkvWriter* writer) const { |
| 1945 if (!writer) |
| 1946 return false; |
| 1947 |
| 1948 if (duration_ > 0.0) { |
| 1949 if (writer->Seekable()) { |
| 1950 if (duration_pos_ == -1) |
| 1951 return false; |
| 1952 |
| 1953 const int64 pos = writer->Position(); |
| 1954 |
| 1955 if (writer->Position(duration_pos_)) |
| 1956 return false; |
| 1957 |
| 1958 if (!WriteEbmlElement(writer, |
| 1959 kMkvDuration, |
| 1960 static_cast<float>(duration_))) |
| 1961 return false; |
| 1962 |
| 1963 if (writer->Position(pos)) |
| 1964 return false; |
| 1965 } |
| 1966 } |
| 1967 |
| 1968 return true; |
| 1969 } |
| 1970 |
| 1971 bool SegmentInfo::Write(IMkvWriter* writer) { |
| 1972 if (!writer || !muxing_app_ || !writing_app_) |
| 1973 return false; |
| 1974 |
| 1975 uint64 size = EbmlElementSize(kMkvTimecodeScale, timecode_scale_); |
| 1976 if (duration_ > 0.0) |
| 1977 size += EbmlElementSize(kMkvDuration, static_cast<float>(duration_)); |
| 1978 size += EbmlElementSize(kMkvMuxingApp, muxing_app_); |
| 1979 size += EbmlElementSize(kMkvWritingApp, writing_app_); |
| 1980 |
| 1981 if (!WriteEbmlMasterElement(writer, kMkvInfo, size)) |
| 1982 return false; |
| 1983 |
| 1984 const int64 payload_position = writer->Position(); |
| 1985 if (payload_position < 0) |
| 1986 return false; |
| 1987 |
| 1988 if (!WriteEbmlElement(writer, kMkvTimecodeScale, timecode_scale_)) |
| 1989 return false; |
| 1990 |
| 1991 if (duration_ > 0.0) { |
| 1992 // Save for later |
| 1993 duration_pos_ = writer->Position(); |
| 1994 |
| 1995 if (!WriteEbmlElement(writer, kMkvDuration, static_cast<float>(duration_))) |
| 1996 return false; |
| 1997 } |
| 1998 |
| 1999 if (!WriteEbmlElement(writer, kMkvMuxingApp, muxing_app_)) |
| 2000 return false; |
| 2001 if (!WriteEbmlElement(writer, kMkvWritingApp, writing_app_)) |
| 2002 return false; |
| 2003 |
| 2004 const int64 stop_position = writer->Position(); |
| 2005 if (stop_position < 0 || |
| 2006 stop_position - payload_position != static_cast<int64>(size)) |
| 2007 return false; |
| 2008 |
| 2009 return true; |
| 2010 } |
| 2011 |
| 2012 void SegmentInfo::set_muxing_app(const char* app) { |
| 2013 if (app) { |
| 2014 const size_t length = strlen(app) + 1; |
| 2015 char* temp_str = new (std::nothrow) char[length]; // NOLINT |
| 2016 if (!temp_str) |
| 2017 return; |
| 2018 |
| 2019 #ifdef _MSC_VER |
| 2020 strcpy_s(temp_str, length, app); |
| 2021 #else |
| 2022 strcpy(temp_str, app); |
| 2023 #endif |
| 2024 |
| 2025 delete [] muxing_app_; |
| 2026 muxing_app_ = temp_str; |
| 2027 } |
| 2028 } |
| 2029 |
| 2030 void SegmentInfo::set_writing_app(const char* app) { |
| 2031 if (app) { |
| 2032 const size_t length = strlen(app) + 1; |
| 2033 char* temp_str = new (std::nothrow) char[length]; // NOLINT |
| 2034 if (!temp_str) |
| 2035 return; |
| 2036 |
| 2037 #ifdef _MSC_VER |
| 2038 strcpy_s(temp_str, length, app); |
| 2039 #else |
| 2040 strcpy(temp_str, app); |
| 2041 #endif |
| 2042 |
| 2043 delete [] writing_app_; |
| 2044 writing_app_ = temp_str; |
| 2045 } |
| 2046 } |
| 2047 |
| 2048 /////////////////////////////////////////////////////////////// |
| 2049 // |
| 2050 // Segment Class |
| 2051 |
| 2052 Segment::Segment() |
| 2053 : chunk_count_(0), |
| 2054 chunk_name_(NULL), |
| 2055 chunk_writer_cluster_(NULL), |
| 2056 chunk_writer_cues_(NULL), |
| 2057 chunk_writer_header_(NULL), |
| 2058 chunking_(false), |
| 2059 chunking_base_name_(NULL), |
| 2060 cluster_list_(NULL), |
| 2061 cluster_list_capacity_(0), |
| 2062 cluster_list_size_(0), |
| 2063 cues_position_(kAfterClusters), |
| 2064 cues_track_(0), |
| 2065 force_new_cluster_(false), |
| 2066 frames_(NULL), |
| 2067 frames_capacity_(0), |
| 2068 frames_size_(0), |
| 2069 has_video_(false), |
| 2070 header_written_(false), |
| 2071 last_block_duration_(0), |
| 2072 last_timestamp_(0), |
| 2073 max_cluster_duration_(kDefaultMaxClusterDuration), |
| 2074 max_cluster_size_(0), |
| 2075 mode_(kFile), |
| 2076 new_cuepoint_(false), |
| 2077 output_cues_(true), |
| 2078 payload_pos_(0), |
| 2079 size_position_(0), |
| 2080 writer_cluster_(NULL), |
| 2081 writer_cues_(NULL), |
| 2082 writer_header_(NULL) { |
| 2083 const time_t curr_time = time(NULL); |
| 2084 seed_ = static_cast<unsigned int>(curr_time); |
| 2085 #ifdef _WIN32 |
| 2086 srand(seed_); |
| 2087 #endif |
| 2088 } |
| 2089 |
| 2090 Segment::~Segment() { |
| 2091 if (cluster_list_) { |
| 2092 for (int32 i = 0; i < cluster_list_size_; ++i) { |
| 2093 Cluster* const cluster = cluster_list_[i]; |
| 2094 delete cluster; |
| 2095 } |
| 2096 delete [] cluster_list_; |
| 2097 } |
| 2098 |
| 2099 if (frames_) { |
| 2100 for (int32 i = 0; i < frames_size_; ++i) { |
| 2101 Frame* const frame = frames_[i]; |
| 2102 delete frame; |
| 2103 } |
| 2104 delete [] frames_; |
| 2105 } |
| 2106 |
| 2107 delete [] chunk_name_; |
| 2108 delete [] chunking_base_name_; |
| 2109 |
| 2110 if (chunk_writer_cluster_) { |
| 2111 chunk_writer_cluster_->Close(); |
| 2112 delete chunk_writer_cluster_; |
| 2113 } |
| 2114 if (chunk_writer_cues_) { |
| 2115 chunk_writer_cues_->Close(); |
| 2116 delete chunk_writer_cues_; |
| 2117 } |
| 2118 if (chunk_writer_header_) { |
| 2119 chunk_writer_header_->Close(); |
| 2120 delete chunk_writer_header_; |
| 2121 } |
| 2122 } |
| 2123 |
| 2124 void Segment::MoveCuesBeforeClustersHelper(uint64 diff, |
| 2125 int32 index, |
| 2126 uint64* cues_size) { |
| 2127 const uint64 old_cues_size = *cues_size; |
| 2128 CuePoint* const cue_point = cues_.GetCueByIndex(index); |
| 2129 if (cue_point == NULL) |
| 2130 return; |
| 2131 const uint64 old_cue_point_size = cue_point->Size(); |
| 2132 const uint64 cluster_pos = cue_point->cluster_pos() + diff; |
| 2133 cue_point->set_cluster_pos(cluster_pos); // update the new cluster position |
| 2134 // New size of the cue is computed as follows |
| 2135 // Let a = current size of Cues Element |
| 2136 // Let b = Difference in Cue Point's size after this pass |
| 2137 // Let c = Difference in length of Cues Element's size |
| 2138 // (This is computed as CodedSize(a + b) - CodedSize(a) |
| 2139 // Let d = a + b + c. Now d is the new size of the Cues element which is |
| 2140 // passed on to the next recursive call. |
| 2141 const uint64 cue_point_size_diff = cue_point->Size() - old_cue_point_size; |
| 2142 const uint64 cue_size_diff = GetCodedUIntSize(*cues_size + |
| 2143 cue_point_size_diff) - |
| 2144 GetCodedUIntSize(*cues_size); |
| 2145 *cues_size += cue_point_size_diff + cue_size_diff; |
| 2146 diff = *cues_size - old_cues_size; |
| 2147 if (diff > 0) { |
| 2148 for (int32 i = 0; i < cues_.cue_entries_size(); ++i) { |
| 2149 MoveCuesBeforeClustersHelper(diff, i, cues_size); |
| 2150 } |
| 2151 } |
| 2152 } |
| 2153 |
| 2154 void Segment::MoveCuesBeforeClusters() { |
| 2155 const uint64 current_cue_size = cues_.Size(); |
| 2156 uint64 cue_size = current_cue_size; |
| 2157 for (int32 i = 0; i < cues_.cue_entries_size(); i++) |
| 2158 MoveCuesBeforeClustersHelper(current_cue_size, i, &cue_size); |
| 2159 |
| 2160 // Adjust the Seek Entry to reflect the change in position |
| 2161 // of Cluster and Cues |
| 2162 int32 cluster_index = 0; |
| 2163 int32 cues_index = 0; |
| 2164 for (int32 i = 0; i < SeekHead::kSeekEntryCount; ++i) { |
| 2165 if (seek_head_.GetId(i) == kMkvCluster) |
| 2166 cluster_index = i; |
| 2167 if (seek_head_.GetId(i) == kMkvCues) |
| 2168 cues_index = i; |
| 2169 } |
| 2170 seek_head_.SetSeekEntry(cues_index, kMkvCues, |
| 2171 seek_head_.GetPosition(cluster_index)); |
| 2172 seek_head_.SetSeekEntry(cluster_index, kMkvCluster, |
| 2173 cues_.Size() + seek_head_.GetPosition(cues_index)); |
| 2174 } |
| 2175 |
| 2176 bool Segment::Init(IMkvWriter* ptr_writer) { |
| 2177 if (!ptr_writer) { |
| 2178 return false; |
| 2179 } |
| 2180 writer_cluster_ = ptr_writer; |
| 2181 writer_cues_ = ptr_writer; |
| 2182 writer_header_ = ptr_writer; |
| 2183 return segment_info_.Init(); |
| 2184 } |
| 2185 |
| 2186 bool Segment::CopyAndMoveCuesBeforeClusters(mkvparser::IMkvReader* reader, |
| 2187 IMkvWriter* writer) { |
| 2188 if (!writer->Seekable() || chunking_) |
| 2189 return false; |
| 2190 const int64 cluster_offset = cluster_list_[0]->size_position() - |
| 2191 GetUIntSize(kMkvCluster); |
| 2192 |
| 2193 // Copy the headers. |
| 2194 if (!ChunkedCopy(reader, writer, 0, cluster_offset)) |
| 2195 return false; |
| 2196 |
| 2197 // Recompute cue positions and seek entries. |
| 2198 MoveCuesBeforeClusters(); |
| 2199 |
| 2200 // Write cues and seek entries. |
| 2201 // TODO(vigneshv): As of now, it's safe to call seek_head_.Finalize() for the |
| 2202 // second time with a different writer object. But the name Finalize() doesn't |
| 2203 // indicate something we want to call more than once. So consider renaming it |
| 2204 // to write() or some such. |
| 2205 if (!cues_.Write(writer) || !seek_head_.Finalize(writer)) |
| 2206 return false; |
| 2207 |
| 2208 // Copy the Clusters. |
| 2209 if (!ChunkedCopy(reader, writer, cluster_offset, |
| 2210 cluster_end_offset_ - cluster_offset)) |
| 2211 return false; |
| 2212 |
| 2213 // Update the Segment size in case the Cues size has changed. |
| 2214 const int64 pos = writer->Position(); |
| 2215 const int64 segment_size = writer->Position() - payload_pos_; |
| 2216 if (writer->Position(size_position_) || |
| 2217 WriteUIntSize(writer, segment_size, 8) || |
| 2218 writer->Position(pos)) |
| 2219 return false; |
| 2220 return true; |
| 2221 } |
| 2222 |
| 2223 bool Segment::Finalize() { |
| 2224 if (WriteFramesAll() < 0) |
| 2225 return false; |
| 2226 |
| 2227 if (mode_ == kFile) { |
| 2228 if (cluster_list_size_ > 0) { |
| 2229 // Update last cluster's size |
| 2230 Cluster* const old_cluster = cluster_list_[cluster_list_size_-1]; |
| 2231 |
| 2232 if (!old_cluster || !old_cluster->Finalize()) |
| 2233 return false; |
| 2234 } |
| 2235 |
| 2236 if (chunking_ && chunk_writer_cluster_) { |
| 2237 chunk_writer_cluster_->Close(); |
| 2238 chunk_count_++; |
| 2239 } |
| 2240 |
| 2241 const double duration = |
| 2242 (static_cast<double>(last_timestamp_) + last_block_duration_) / |
| 2243 segment_info_.timecode_scale(); |
| 2244 segment_info_.set_duration(duration); |
| 2245 if (!segment_info_.Finalize(writer_header_)) |
| 2246 return false; |
| 2247 |
| 2248 if (output_cues_) |
| 2249 if (!seek_head_.AddSeekEntry(kMkvCues, MaxOffset())) |
| 2250 return false; |
| 2251 |
| 2252 if (chunking_) { |
| 2253 if (!chunk_writer_cues_) |
| 2254 return false; |
| 2255 |
| 2256 char* name = NULL; |
| 2257 if (!UpdateChunkName("cues", &name)) |
| 2258 return false; |
| 2259 |
| 2260 const bool cues_open = chunk_writer_cues_->Open(name); |
| 2261 delete [] name; |
| 2262 if (!cues_open) |
| 2263 return false; |
| 2264 } |
| 2265 |
| 2266 cluster_end_offset_ = writer_cluster_->Position(); |
| 2267 |
| 2268 // Write the seek headers and cues |
| 2269 if (output_cues_) |
| 2270 if (!cues_.Write(writer_cues_)) |
| 2271 return false; |
| 2272 |
| 2273 if (!seek_head_.Finalize(writer_header_)) |
| 2274 return false; |
| 2275 |
| 2276 if (writer_header_->Seekable()) { |
| 2277 if (size_position_ == -1) |
| 2278 return false; |
| 2279 |
| 2280 const int64 pos = writer_header_->Position(); |
| 2281 const int64 segment_size = MaxOffset(); |
| 2282 |
| 2283 if (segment_size < 1) |
| 2284 return false; |
| 2285 |
| 2286 if (writer_header_->Position(size_position_)) |
| 2287 return false; |
| 2288 |
| 2289 if (WriteUIntSize(writer_header_, segment_size, 8)) |
| 2290 return false; |
| 2291 |
| 2292 if (writer_header_->Position(pos)) |
| 2293 return false; |
| 2294 } |
| 2295 |
| 2296 if (chunking_) { |
| 2297 // Do not close any writers until the segment size has been written, |
| 2298 // otherwise the size may be off. |
| 2299 if (!chunk_writer_cues_ || !chunk_writer_header_) |
| 2300 return false; |
| 2301 |
| 2302 chunk_writer_cues_->Close(); |
| 2303 chunk_writer_header_->Close(); |
| 2304 } |
| 2305 } |
| 2306 |
| 2307 return true; |
| 2308 } |
| 2309 |
| 2310 Track* Segment::AddTrack(int32 number) { |
| 2311 Track* const track = new (std::nothrow) Track(&seed_); // NOLINT |
| 2312 |
| 2313 if (!track) |
| 2314 return NULL; |
| 2315 |
| 2316 if (!tracks_.AddTrack(track, number)) { |
| 2317 delete track; |
| 2318 return NULL; |
| 2319 } |
| 2320 |
| 2321 return track; |
| 2322 } |
| 2323 |
| 2324 Chapter* Segment::AddChapter() { |
| 2325 return chapters_.AddChapter(&seed_); |
| 2326 } |
| 2327 |
| 2328 uint64 Segment::AddVideoTrack(int32 width, int32 height, int32 number) { |
| 2329 VideoTrack* const track = new (std::nothrow) VideoTrack(&seed_); // NOLINT |
| 2330 if (!track) |
| 2331 return 0; |
| 2332 |
| 2333 track->set_type(Tracks::kVideo); |
| 2334 track->set_codec_id(Tracks::kVp8CodecId); |
| 2335 track->set_width(width); |
| 2336 track->set_height(height); |
| 2337 |
| 2338 tracks_.AddTrack(track, number); |
| 2339 has_video_ = true; |
| 2340 |
| 2341 return track->number(); |
| 2342 } |
| 2343 |
| 2344 bool Segment::AddCuePoint(uint64 timestamp, uint64 track) { |
| 2345 if (cluster_list_size_ < 1) |
| 2346 return false; |
| 2347 |
| 2348 const Cluster* const cluster = cluster_list_[cluster_list_size_-1]; |
| 2349 if (!cluster) |
| 2350 return false; |
| 2351 |
| 2352 CuePoint* const cue = new (std::nothrow) CuePoint(); // NOLINT |
| 2353 if (!cue) |
| 2354 return false; |
| 2355 |
| 2356 cue->set_time(timestamp / segment_info_.timecode_scale()); |
| 2357 cue->set_block_number(cluster->blocks_added()); |
| 2358 cue->set_cluster_pos(cluster->position_for_cues()); |
| 2359 cue->set_track(track); |
| 2360 if (!cues_.AddCue(cue)) |
| 2361 return false; |
| 2362 |
| 2363 new_cuepoint_ = false; |
| 2364 return true; |
| 2365 } |
| 2366 |
| 2367 uint64 Segment::AddAudioTrack(int32 sample_rate, |
| 2368 int32 channels, |
| 2369 int32 number) { |
| 2370 AudioTrack* const track = new (std::nothrow) AudioTrack(&seed_); // NOLINT |
| 2371 if (!track) |
| 2372 return 0; |
| 2373 |
| 2374 track->set_type(Tracks::kAudio); |
| 2375 track->set_codec_id(Tracks::kVorbisCodecId); |
| 2376 track->set_sample_rate(sample_rate); |
| 2377 track->set_channels(channels); |
| 2378 |
| 2379 tracks_.AddTrack(track, number); |
| 2380 |
| 2381 return track->number(); |
| 2382 } |
| 2383 |
| 2384 bool Segment::AddFrame(const uint8* frame, |
| 2385 uint64 length, |
| 2386 uint64 track_number, |
| 2387 uint64 timestamp, |
| 2388 bool is_key) { |
| 2389 if (!frame) |
| 2390 return false; |
| 2391 |
| 2392 if (!CheckHeaderInfo()) |
| 2393 return false; |
| 2394 |
| 2395 // Check for non-monotonically increasing timestamps. |
| 2396 if (timestamp < last_timestamp_) |
| 2397 return false; |
| 2398 |
| 2399 // If the segment has a video track hold onto audio frames to make sure the |
| 2400 // audio that is associated with the start time of a video key-frame is |
| 2401 // muxed into the same cluster. |
| 2402 if (has_video_ && tracks_.TrackIsAudio(track_number) && !force_new_cluster_) { |
| 2403 Frame* const new_frame = new (std::nothrow) Frame(); |
| 2404 if (new_frame == NULL || !new_frame->Init(frame, length)) |
| 2405 return false; |
| 2406 new_frame->set_track_number(track_number); |
| 2407 new_frame->set_timestamp(timestamp); |
| 2408 new_frame->set_is_key(is_key); |
| 2409 |
| 2410 if (!QueueFrame(new_frame)) |
| 2411 return false; |
| 2412 |
| 2413 return true; |
| 2414 } |
| 2415 |
| 2416 if (!DoNewClusterProcessing(track_number, timestamp, is_key)) |
| 2417 return false; |
| 2418 |
| 2419 if (cluster_list_size_ < 1) |
| 2420 return false; |
| 2421 |
| 2422 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; |
| 2423 if (!cluster) |
| 2424 return false; |
| 2425 |
| 2426 const uint64 timecode_scale = segment_info_.timecode_scale(); |
| 2427 const uint64 abs_timecode = timestamp / timecode_scale; |
| 2428 |
| 2429 if (!cluster->AddFrame(frame, |
| 2430 length, |
| 2431 track_number, |
| 2432 abs_timecode, |
| 2433 is_key)) |
| 2434 return false; |
| 2435 |
| 2436 if (new_cuepoint_ && cues_track_ == track_number) { |
| 2437 if (!AddCuePoint(timestamp, cues_track_)) |
| 2438 return false; |
| 2439 } |
| 2440 |
| 2441 if (timestamp > last_timestamp_) |
| 2442 last_timestamp_ = timestamp; |
| 2443 |
| 2444 return true; |
| 2445 } |
| 2446 |
| 2447 bool Segment::AddFrameWithAdditional(const uint8* frame, |
| 2448 uint64 length, |
| 2449 const uint8* additional, |
| 2450 uint64 additional_length, |
| 2451 uint64 add_id, |
| 2452 uint64 track_number, |
| 2453 uint64 timestamp, |
| 2454 bool is_key) { |
| 2455 if (frame == NULL || additional == NULL) |
| 2456 return false; |
| 2457 |
| 2458 if (!CheckHeaderInfo()) |
| 2459 return false; |
| 2460 |
| 2461 // Check for non-monotonically increasing timestamps. |
| 2462 if (timestamp < last_timestamp_) |
| 2463 return false; |
| 2464 |
| 2465 // If the segment has a video track hold onto audio frames to make sure the |
| 2466 // audio that is associated with the start time of a video key-frame is |
| 2467 // muxed into the same cluster. |
| 2468 if (has_video_ && tracks_.TrackIsAudio(track_number) && !force_new_cluster_) { |
| 2469 Frame* const new_frame = new (std::nothrow) Frame(); |
| 2470 if (new_frame == NULL || !new_frame->Init(frame, length)) |
| 2471 return false; |
| 2472 new_frame->set_track_number(track_number); |
| 2473 new_frame->set_timestamp(timestamp); |
| 2474 new_frame->set_is_key(is_key); |
| 2475 |
| 2476 if (!QueueFrame(new_frame)) |
| 2477 return false; |
| 2478 |
| 2479 return true; |
| 2480 } |
| 2481 |
| 2482 if (!DoNewClusterProcessing(track_number, timestamp, is_key)) |
| 2483 return false; |
| 2484 |
| 2485 if (cluster_list_size_ < 1) |
| 2486 return false; |
| 2487 |
| 2488 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; |
| 2489 if (cluster == NULL) |
| 2490 return false; |
| 2491 |
| 2492 const uint64 timecode_scale = segment_info_.timecode_scale(); |
| 2493 const uint64 abs_timecode = timestamp / timecode_scale; |
| 2494 |
| 2495 if (!cluster->AddFrameWithAdditional(frame, |
| 2496 length, |
| 2497 additional, |
| 2498 additional_length, |
| 2499 add_id, |
| 2500 track_number, |
| 2501 abs_timecode, |
| 2502 is_key)) |
| 2503 return false; |
| 2504 |
| 2505 if (new_cuepoint_ && cues_track_ == track_number) { |
| 2506 if (!AddCuePoint(timestamp, cues_track_)) |
| 2507 return false; |
| 2508 } |
| 2509 |
| 2510 if (timestamp > last_timestamp_) |
| 2511 last_timestamp_ = timestamp; |
| 2512 |
| 2513 return true; |
| 2514 } |
| 2515 |
| 2516 bool Segment::AddFrameWithDiscardPadding(const uint8* frame, |
| 2517 uint64 length, |
| 2518 int64 discard_padding, |
| 2519 uint64 track_number, |
| 2520 uint64 timestamp, |
| 2521 bool is_key) { |
| 2522 if (frame == NULL || discard_padding <= 0) |
| 2523 return false; |
| 2524 |
| 2525 if (!CheckHeaderInfo()) |
| 2526 return false; |
| 2527 |
| 2528 // Check for non-monotonically increasing timestamps. |
| 2529 if (timestamp < last_timestamp_) |
| 2530 return false; |
| 2531 |
| 2532 // If the segment has a video track hold onto audio frames to make sure the |
| 2533 // audio that is associated with the start time of a video key-frame is |
| 2534 // muxed into the same cluster. |
| 2535 if (has_video_ && tracks_.TrackIsAudio(track_number) && !force_new_cluster_) { |
| 2536 Frame* const new_frame = new (std::nothrow) Frame(); |
| 2537 if (new_frame == NULL || !new_frame->Init(frame, length)) |
| 2538 return false; |
| 2539 new_frame->set_track_number(track_number); |
| 2540 new_frame->set_timestamp(timestamp); |
| 2541 new_frame->set_is_key(is_key); |
| 2542 new_frame->set_discard_padding(discard_padding); |
| 2543 |
| 2544 if (!QueueFrame(new_frame)) |
| 2545 return false; |
| 2546 |
| 2547 return true; |
| 2548 } |
| 2549 |
| 2550 if (!DoNewClusterProcessing(track_number, timestamp, is_key)) |
| 2551 return false; |
| 2552 |
| 2553 if (cluster_list_size_ < 1) |
| 2554 return false; |
| 2555 |
| 2556 Cluster* const cluster = cluster_list_[cluster_list_size_ - 1]; |
| 2557 if (!cluster) |
| 2558 return false; |
| 2559 |
| 2560 const uint64 timecode_scale = segment_info_.timecode_scale(); |
| 2561 const uint64 abs_timecode = timestamp / timecode_scale; |
| 2562 |
| 2563 if (!cluster->AddFrameWithDiscardPadding(frame, length, |
| 2564 discard_padding, |
| 2565 track_number, |
| 2566 abs_timecode, |
| 2567 is_key)) { |
| 2568 return false; |
| 2569 } |
| 2570 |
| 2571 if (new_cuepoint_ && cues_track_ == track_number) { |
| 2572 if (!AddCuePoint(timestamp, cues_track_)) |
| 2573 return false; |
| 2574 } |
| 2575 |
| 2576 if (timestamp > last_timestamp_) |
| 2577 last_timestamp_ = timestamp; |
| 2578 |
| 2579 return true; |
| 2580 } |
| 2581 |
| 2582 bool Segment::AddMetadata(const uint8* frame, |
| 2583 uint64 length, |
| 2584 uint64 track_number, |
| 2585 uint64 timestamp_ns, |
| 2586 uint64 duration_ns) { |
| 2587 if (!frame) |
| 2588 return false; |
| 2589 |
| 2590 if (!CheckHeaderInfo()) |
| 2591 return false; |
| 2592 |
| 2593 // Check for non-monotonically increasing timestamps. |
| 2594 if (timestamp_ns < last_timestamp_) |
| 2595 return false; |
| 2596 |
| 2597 if (!DoNewClusterProcessing(track_number, timestamp_ns, true)) |
| 2598 return false; |
| 2599 |
| 2600 if (cluster_list_size_ < 1) |
| 2601 return false; |
| 2602 |
| 2603 Cluster* const cluster = cluster_list_[cluster_list_size_-1]; |
| 2604 |
| 2605 if (!cluster) |
| 2606 return false; |
| 2607 |
| 2608 const uint64 timecode_scale = segment_info_.timecode_scale(); |
| 2609 const uint64 abs_timecode = timestamp_ns / timecode_scale; |
| 2610 const uint64 duration_timecode = duration_ns / timecode_scale; |
| 2611 |
| 2612 if (!cluster->AddMetadata(frame, |
| 2613 length, |
| 2614 track_number, |
| 2615 abs_timecode, |
| 2616 duration_timecode)) |
| 2617 return false; |
| 2618 |
| 2619 if (timestamp_ns > last_timestamp_) |
| 2620 last_timestamp_ = timestamp_ns; |
| 2621 |
| 2622 return true; |
| 2623 } |
| 2624 |
| 2625 bool Segment::AddGenericFrame(const Frame* frame) { |
| 2626 last_block_duration_ = frame->duration(); |
| 2627 if (!tracks_.TrackIsAudio(frame->track_number()) && |
| 2628 !tracks_.TrackIsVideo(frame->track_number()) && |
| 2629 frame->duration() > 0) { |
| 2630 return AddMetadata(frame->frame(), |
| 2631 frame->length(), |
| 2632 frame->track_number(), |
| 2633 frame->timestamp(), |
| 2634 frame->duration()); |
| 2635 } else if (frame->additional() && frame->additional_length() > 0) { |
| 2636 return AddFrameWithAdditional(frame->frame(), |
| 2637 frame->length(), |
| 2638 frame->additional(), |
| 2639 frame->additional_length(), |
| 2640 frame->add_id(), |
| 2641 frame->track_number(), |
| 2642 frame->timestamp(), |
| 2643 frame->is_key()); |
| 2644 } else if (frame->discard_padding() > 0) { |
| 2645 return AddFrameWithDiscardPadding(frame->frame(), frame->length(), |
| 2646 frame->discard_padding(), |
| 2647 frame->track_number(), |
| 2648 frame->timestamp(), |
| 2649 frame->is_key()); |
| 2650 } else { |
| 2651 return AddFrame(frame->frame(), |
| 2652 frame->length(), |
| 2653 frame->track_number(), |
| 2654 frame->timestamp(), |
| 2655 frame->is_key()); |
| 2656 } |
| 2657 } |
| 2658 |
| 2659 void Segment::OutputCues(bool output_cues) { |
| 2660 output_cues_ = output_cues; |
| 2661 } |
| 2662 |
| 2663 bool Segment::SetChunking(bool chunking, const char* filename) { |
| 2664 if (chunk_count_ > 0) |
| 2665 return false; |
| 2666 |
| 2667 if (chunking) { |
| 2668 if (!filename) |
| 2669 return false; |
| 2670 |
| 2671 // Check if we are being set to what is already set. |
| 2672 if (chunking_ && !strcmp(filename, chunking_base_name_)) |
| 2673 return true; |
| 2674 |
| 2675 const size_t name_length = strlen(filename) + 1; |
| 2676 char* const temp = new (std::nothrow) char[name_length]; // NOLINT |
| 2677 if (!temp) |
| 2678 return false; |
| 2679 |
| 2680 #ifdef _MSC_VER |
| 2681 strcpy_s(temp, name_length, filename); |
| 2682 #else |
| 2683 strcpy(temp, filename); |
| 2684 #endif |
| 2685 |
| 2686 delete [] chunking_base_name_; |
| 2687 chunking_base_name_ = temp; |
| 2688 |
| 2689 if (!UpdateChunkName("chk", &chunk_name_)) |
| 2690 return false; |
| 2691 |
| 2692 if (!chunk_writer_cluster_) { |
| 2693 chunk_writer_cluster_ = new (std::nothrow) MkvWriter(); // NOLINT |
| 2694 if (!chunk_writer_cluster_) |
| 2695 return false; |
| 2696 } |
| 2697 |
| 2698 if (!chunk_writer_cues_) { |
| 2699 chunk_writer_cues_ = new (std::nothrow) MkvWriter(); // NOLINT |
| 2700 if (!chunk_writer_cues_) |
| 2701 return false; |
| 2702 } |
| 2703 |
| 2704 if (!chunk_writer_header_) { |
| 2705 chunk_writer_header_ = new (std::nothrow) MkvWriter(); // NOLINT |
| 2706 if (!chunk_writer_header_) |
| 2707 return false; |
| 2708 } |
| 2709 |
| 2710 if (!chunk_writer_cluster_->Open(chunk_name_)) |
| 2711 return false; |
| 2712 |
| 2713 const size_t header_length = strlen(filename) + strlen(".hdr") + 1; |
| 2714 char* const header = new (std::nothrow) char[header_length]; // NOLINT |
| 2715 if (!header) |
| 2716 return false; |
| 2717 |
| 2718 #ifdef _MSC_VER |
| 2719 strcpy_s(header, header_length - strlen(".hdr"), chunking_base_name_); |
| 2720 strcat_s(header, header_length, ".hdr"); |
| 2721 #else |
| 2722 strcpy(header, chunking_base_name_); |
| 2723 strcat(header, ".hdr"); |
| 2724 #endif |
| 2725 if (!chunk_writer_header_->Open(header)) { |
| 2726 delete [] header; |
| 2727 return false; |
| 2728 } |
| 2729 |
| 2730 writer_cluster_ = chunk_writer_cluster_; |
| 2731 writer_cues_ = chunk_writer_cues_; |
| 2732 writer_header_ = chunk_writer_header_; |
| 2733 |
| 2734 delete [] header; |
| 2735 } |
| 2736 |
| 2737 chunking_ = chunking; |
| 2738 |
| 2739 return true; |
| 2740 } |
| 2741 |
| 2742 bool Segment::CuesTrack(uint64 track_number) { |
| 2743 const Track* const track = GetTrackByNumber(track_number); |
| 2744 if (!track) |
| 2745 return false; |
| 2746 |
| 2747 cues_track_ = track_number; |
| 2748 return true; |
| 2749 } |
| 2750 |
| 2751 void Segment::ForceNewClusterOnNextFrame() { |
| 2752 force_new_cluster_ = true; |
| 2753 } |
| 2754 |
| 2755 Track* Segment::GetTrackByNumber(uint64 track_number) const { |
| 2756 return tracks_.GetTrackByNumber(track_number); |
| 2757 } |
| 2758 |
| 2759 bool Segment::WriteSegmentHeader() { |
| 2760 // TODO(fgalligan): Support more than one segment. |
| 2761 if (!WriteEbmlHeader(writer_header_)) |
| 2762 return false; |
| 2763 |
| 2764 // Write "unknown" (-1) as segment size value. If mode is kFile, Segment |
| 2765 // will write over duration when the file is finalized. |
| 2766 if (WriteID(writer_header_, kMkvSegment)) |
| 2767 return false; |
| 2768 |
| 2769 // Save for later. |
| 2770 size_position_ = writer_header_->Position(); |
| 2771 |
| 2772 // Write "unknown" (EBML coded -1) as segment size value. We need to write 8 |
| 2773 // bytes because if we are going to overwrite the segment size later we do |
| 2774 // not know how big our segment will be. |
| 2775 if (SerializeInt(writer_header_, kEbmlUnknownValue, 8)) |
| 2776 return false; |
| 2777 |
| 2778 payload_pos_ = writer_header_->Position(); |
| 2779 |
| 2780 if (mode_ == kFile && writer_header_->Seekable()) { |
| 2781 // Set the duration > 0.0 so SegmentInfo will write out the duration. When |
| 2782 // the muxer is done writing we will set the correct duration and have |
| 2783 // SegmentInfo upadte it. |
| 2784 segment_info_.set_duration(1.0); |
| 2785 |
| 2786 if (!seek_head_.Write(writer_header_)) |
| 2787 return false; |
| 2788 } |
| 2789 |
| 2790 if (!seek_head_.AddSeekEntry(kMkvInfo, MaxOffset())) |
| 2791 return false; |
| 2792 if (!segment_info_.Write(writer_header_)) |
| 2793 return false; |
| 2794 |
| 2795 if (!seek_head_.AddSeekEntry(kMkvTracks, MaxOffset())) |
| 2796 return false; |
| 2797 if (!tracks_.Write(writer_header_)) |
| 2798 return false; |
| 2799 |
| 2800 if (chapters_.Count() > 0) { |
| 2801 if (!seek_head_.AddSeekEntry(kMkvChapters, MaxOffset())) |
| 2802 return false; |
| 2803 if (!chapters_.Write(writer_header_)) |
| 2804 return false; |
| 2805 } |
| 2806 |
| 2807 if (chunking_ && (mode_ == kLive || !writer_header_->Seekable())) { |
| 2808 if (!chunk_writer_header_) |
| 2809 return false; |
| 2810 |
| 2811 chunk_writer_header_->Close(); |
| 2812 } |
| 2813 |
| 2814 header_written_ = true; |
| 2815 |
| 2816 return true; |
| 2817 } |
| 2818 |
| 2819 // Here we are testing whether to create a new cluster, given a frame |
| 2820 // having time frame_timestamp_ns. |
| 2821 // |
| 2822 int Segment::TestFrame(uint64 track_number, |
| 2823 uint64 frame_timestamp_ns, |
| 2824 bool is_key) const { |
| 2825 if (force_new_cluster_) |
| 2826 return 1; |
| 2827 |
| 2828 // If no clusters have been created yet, then create a new cluster |
| 2829 // and write this frame immediately, in the new cluster. This path |
| 2830 // should only be followed once, the first time we attempt to write |
| 2831 // a frame. |
| 2832 |
| 2833 if (cluster_list_size_ <= 0) |
| 2834 return 1; |
| 2835 |
| 2836 // There exists at least one cluster. We must compare the frame to |
| 2837 // the last cluster, in order to determine whether the frame is |
| 2838 // written to the existing cluster, or that a new cluster should be |
| 2839 // created. |
| 2840 |
| 2841 const uint64 timecode_scale = segment_info_.timecode_scale(); |
| 2842 const uint64 frame_timecode = frame_timestamp_ns / timecode_scale; |
| 2843 |
| 2844 const Cluster* const last_cluster = cluster_list_[cluster_list_size_ - 1]; |
| 2845 const uint64 last_cluster_timecode = last_cluster->timecode(); |
| 2846 |
| 2847 // For completeness we test for the case when the frame's timecode |
| 2848 // is less than the cluster's timecode. Although in principle that |
| 2849 // is allowed, this muxer doesn't actually write clusters like that, |
| 2850 // so this indicates a bug somewhere in our algorithm. |
| 2851 |
| 2852 if (frame_timecode < last_cluster_timecode) // should never happen |
| 2853 return -1; // error |
| 2854 |
| 2855 // If the frame has a timestamp significantly larger than the last |
| 2856 // cluster (in Matroska, cluster-relative timestamps are serialized |
| 2857 // using a 16-bit signed integer), then we cannot write this frame |
| 2858 // to that cluster, and so we must create a new cluster. |
| 2859 |
| 2860 const int64 delta_timecode = frame_timecode - last_cluster_timecode; |
| 2861 |
| 2862 if (delta_timecode > kMaxBlockTimecode) |
| 2863 return 2; |
| 2864 |
| 2865 // We decide to create a new cluster when we have a video keyframe. |
| 2866 // This will flush queued (audio) frames, and write the keyframe |
| 2867 // immediately, in the newly-created cluster. |
| 2868 |
| 2869 if (is_key && tracks_.TrackIsVideo(track_number)) |
| 2870 return 1; |
| 2871 |
| 2872 // Create a new cluster if we have accumulated too many frames |
| 2873 // already, where "too many" is defined as "the total time of frames |
| 2874 // in the cluster exceeds a threshold". |
| 2875 |
| 2876 const uint64 delta_ns = delta_timecode * timecode_scale; |
| 2877 |
| 2878 if (max_cluster_duration_ > 0 && delta_ns >= max_cluster_duration_) |
| 2879 return 1; |
| 2880 |
| 2881 // This is similar to the case above, with the difference that a new |
| 2882 // cluster is created when the size of the current cluster exceeds a |
| 2883 // threshold. |
| 2884 |
| 2885 const uint64 cluster_size = last_cluster->payload_size(); |
| 2886 |
| 2887 if (max_cluster_size_ > 0 && cluster_size >= max_cluster_size_) |
| 2888 return 1; |
| 2889 |
| 2890 // There's no need to create a new cluster, so emit this frame now. |
| 2891 |
| 2892 return 0; |
| 2893 } |
| 2894 |
| 2895 bool Segment::MakeNewCluster(uint64 frame_timestamp_ns) { |
| 2896 const int32 new_size = cluster_list_size_ + 1; |
| 2897 |
| 2898 if (new_size > cluster_list_capacity_) { |
| 2899 // Add more clusters. |
| 2900 const int32 new_capacity = |
| 2901 (cluster_list_capacity_ <= 0) ? 1 : cluster_list_capacity_ * 2; |
| 2902 Cluster** const clusters = |
| 2903 new (std::nothrow) Cluster*[new_capacity]; // NOLINT |
| 2904 if (!clusters) |
| 2905 return false; |
| 2906 |
| 2907 for (int32 i = 0; i < cluster_list_size_; ++i) { |
| 2908 clusters[i] = cluster_list_[i]; |
| 2909 } |
| 2910 |
| 2911 delete [] cluster_list_; |
| 2912 |
| 2913 cluster_list_ = clusters; |
| 2914 cluster_list_capacity_ = new_capacity; |
| 2915 } |
| 2916 |
| 2917 if (!WriteFramesLessThan(frame_timestamp_ns)) |
| 2918 return false; |
| 2919 |
| 2920 if (mode_ == kFile) { |
| 2921 if (cluster_list_size_ > 0) { |
| 2922 // Update old cluster's size |
| 2923 Cluster* const old_cluster = cluster_list_[cluster_list_size_ - 1]; |
| 2924 |
| 2925 if (!old_cluster || !old_cluster->Finalize()) |
| 2926 return false; |
| 2927 } |
| 2928 |
| 2929 if (output_cues_) |
| 2930 new_cuepoint_ = true; |
| 2931 } |
| 2932 |
| 2933 if (chunking_ && cluster_list_size_ > 0) { |
| 2934 chunk_writer_cluster_->Close(); |
| 2935 chunk_count_++; |
| 2936 |
| 2937 if (!UpdateChunkName("chk", &chunk_name_)) |
| 2938 return false; |
| 2939 if (!chunk_writer_cluster_->Open(chunk_name_)) |
| 2940 return false; |
| 2941 } |
| 2942 |
| 2943 const uint64 timecode_scale = segment_info_.timecode_scale(); |
| 2944 const uint64 frame_timecode = frame_timestamp_ns / timecode_scale; |
| 2945 |
| 2946 uint64 cluster_timecode = frame_timecode; |
| 2947 |
| 2948 if (frames_size_ > 0) { |
| 2949 const Frame* const f = frames_[0]; // earliest queued frame |
| 2950 const uint64 ns = f->timestamp(); |
| 2951 const uint64 tc = ns / timecode_scale; |
| 2952 |
| 2953 if (tc < cluster_timecode) |
| 2954 cluster_timecode = tc; |
| 2955 } |
| 2956 |
| 2957 Cluster*& cluster = cluster_list_[cluster_list_size_]; |
| 2958 const int64 offset = MaxOffset(); |
| 2959 cluster = new (std::nothrow) Cluster(cluster_timecode, offset); // NOLINT |
| 2960 if (!cluster) |
| 2961 return false; |
| 2962 |
| 2963 if (!cluster->Init(writer_cluster_)) |
| 2964 return false; |
| 2965 |
| 2966 cluster_list_size_ = new_size; |
| 2967 return true; |
| 2968 } |
| 2969 |
| 2970 bool Segment::DoNewClusterProcessing(uint64 track_number, |
| 2971 uint64 frame_timestamp_ns, |
| 2972 bool is_key) { |
| 2973 for (;;) { |
| 2974 // Based on the characteristics of the current frame and current |
| 2975 // cluster, decide whether to create a new cluster. |
| 2976 const int result = TestFrame(track_number, frame_timestamp_ns, is_key); |
| 2977 if (result < 0) // error |
| 2978 return false; |
| 2979 |
| 2980 // Always set force_new_cluster_ to false after TestFrame. |
| 2981 force_new_cluster_ = false; |
| 2982 |
| 2983 // A non-zero result means create a new cluster. |
| 2984 if (result > 0 && !MakeNewCluster(frame_timestamp_ns)) |
| 2985 return false; |
| 2986 |
| 2987 // Write queued (audio) frames. |
| 2988 const int frame_count = WriteFramesAll(); |
| 2989 if (frame_count < 0) // error |
| 2990 return false; |
| 2991 |
| 2992 // Write the current frame to the current cluster (if TestFrame |
| 2993 // returns 0) or to a newly created cluster (TestFrame returns 1). |
| 2994 if (result <= 1) |
| 2995 return true; |
| 2996 |
| 2997 // TestFrame returned 2, which means there was a large time |
| 2998 // difference between the cluster and the frame itself. Do the |
| 2999 // test again, comparing the frame to the new cluster. |
| 3000 } |
| 3001 } |
| 3002 |
| 3003 bool Segment::CheckHeaderInfo() { |
| 3004 if (!header_written_) { |
| 3005 if (!WriteSegmentHeader()) |
| 3006 return false; |
| 3007 |
| 3008 if (!seek_head_.AddSeekEntry(kMkvCluster, MaxOffset())) |
| 3009 return false; |
| 3010 |
| 3011 if (output_cues_ && cues_track_ == 0) { |
| 3012 // Check for a video track |
| 3013 for (uint32 i = 0; i < tracks_.track_entries_size(); ++i) { |
| 3014 const Track* const track = tracks_.GetTrackByIndex(i); |
| 3015 if (!track) |
| 3016 return false; |
| 3017 |
| 3018 if (tracks_.TrackIsVideo(track->number())) { |
| 3019 cues_track_ = track->number(); |
| 3020 break; |
| 3021 } |
| 3022 } |
| 3023 |
| 3024 // Set first track found |
| 3025 if (cues_track_ == 0) { |
| 3026 const Track* const track = tracks_.GetTrackByIndex(0); |
| 3027 if (!track) |
| 3028 return false; |
| 3029 |
| 3030 cues_track_ = track->number(); |
| 3031 } |
| 3032 } |
| 3033 } |
| 3034 return true; |
| 3035 } |
| 3036 |
| 3037 bool Segment::UpdateChunkName(const char* ext, char** name) const { |
| 3038 if (!name || !ext) |
| 3039 return false; |
| 3040 |
| 3041 char ext_chk[64]; |
| 3042 #ifdef _MSC_VER |
| 3043 sprintf_s(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext); |
| 3044 #else |
| 3045 snprintf(ext_chk, sizeof(ext_chk), "_%06d.%s", chunk_count_, ext); |
| 3046 #endif |
| 3047 |
| 3048 const size_t length = strlen(chunking_base_name_) + strlen(ext_chk) + 1; |
| 3049 char* const str = new (std::nothrow) char[length]; // NOLINT |
| 3050 if (!str) |
| 3051 return false; |
| 3052 |
| 3053 #ifdef _MSC_VER |
| 3054 strcpy_s(str, length-strlen(ext_chk), chunking_base_name_); |
| 3055 strcat_s(str, length, ext_chk); |
| 3056 #else |
| 3057 strcpy(str, chunking_base_name_); |
| 3058 strcat(str, ext_chk); |
| 3059 #endif |
| 3060 |
| 3061 delete [] *name; |
| 3062 *name = str; |
| 3063 |
| 3064 return true; |
| 3065 } |
| 3066 |
| 3067 int64 Segment::MaxOffset() { |
| 3068 if (!writer_header_) |
| 3069 return -1; |
| 3070 |
| 3071 int64 offset = writer_header_->Position() - payload_pos_; |
| 3072 |
| 3073 if (chunking_) { |
| 3074 for (int32 i = 0; i < cluster_list_size_; ++i) { |
| 3075 Cluster* const cluster = cluster_list_[i]; |
| 3076 offset += cluster->Size(); |
| 3077 } |
| 3078 |
| 3079 if (writer_cues_) |
| 3080 offset += writer_cues_->Position(); |
| 3081 } |
| 3082 |
| 3083 return offset; |
| 3084 } |
| 3085 |
| 3086 bool Segment::QueueFrame(Frame* frame) { |
| 3087 const int32 new_size = frames_size_ + 1; |
| 3088 |
| 3089 if (new_size > frames_capacity_) { |
| 3090 // Add more frames. |
| 3091 const int32 new_capacity = (!frames_capacity_) ? 2 : frames_capacity_ * 2; |
| 3092 |
| 3093 if (new_capacity < 1) |
| 3094 return false; |
| 3095 |
| 3096 Frame** const frames = new (std::nothrow) Frame*[new_capacity]; // NOLINT |
| 3097 if (!frames) |
| 3098 return false; |
| 3099 |
| 3100 for (int32 i = 0; i < frames_size_; ++i) { |
| 3101 frames[i] = frames_[i]; |
| 3102 } |
| 3103 |
| 3104 delete [] frames_; |
| 3105 frames_ = frames; |
| 3106 frames_capacity_ = new_capacity; |
| 3107 } |
| 3108 |
| 3109 frames_[frames_size_++] = frame; |
| 3110 |
| 3111 return true; |
| 3112 } |
| 3113 |
| 3114 int Segment::WriteFramesAll() { |
| 3115 if (frames_ == NULL) |
| 3116 return 0; |
| 3117 |
| 3118 if (cluster_list_size_ < 1) |
| 3119 return -1; |
| 3120 |
| 3121 Cluster* const cluster = cluster_list_[cluster_list_size_-1]; |
| 3122 |
| 3123 if (!cluster) |
| 3124 return -1; |
| 3125 |
| 3126 const uint64 timecode_scale = segment_info_.timecode_scale(); |
| 3127 |
| 3128 for (int32 i = 0; i < frames_size_; ++i) { |
| 3129 Frame*& frame = frames_[i]; |
| 3130 const uint64 frame_timestamp = frame->timestamp(); // ns |
| 3131 const uint64 frame_timecode = frame_timestamp / timecode_scale; |
| 3132 |
| 3133 if (frame->discard_padding() > 0) { |
| 3134 if (!cluster->AddFrameWithDiscardPadding(frame->frame(), |
| 3135 frame->length(), |
| 3136 frame->discard_padding(), |
| 3137 frame->track_number(), |
| 3138 frame_timecode, |
| 3139 frame->is_key())) { |
| 3140 return -1; |
| 3141 } |
| 3142 } else { |
| 3143 if (!cluster->AddFrame(frame->frame(), |
| 3144 frame->length(), |
| 3145 frame->track_number(), |
| 3146 frame_timecode, |
| 3147 frame->is_key())) { |
| 3148 return -1; |
| 3149 } |
| 3150 } |
| 3151 |
| 3152 if (new_cuepoint_ && cues_track_ == frame->track_number()) { |
| 3153 if (!AddCuePoint(frame_timestamp, cues_track_)) |
| 3154 return -1; |
| 3155 } |
| 3156 |
| 3157 if (frame_timestamp > last_timestamp_) |
| 3158 last_timestamp_ = frame_timestamp; |
| 3159 |
| 3160 delete frame; |
| 3161 frame = NULL; |
| 3162 } |
| 3163 |
| 3164 const int result = frames_size_; |
| 3165 frames_size_ = 0; |
| 3166 |
| 3167 return result; |
| 3168 } |
| 3169 |
| 3170 bool Segment::WriteFramesLessThan(uint64 timestamp) { |
| 3171 // Check |cluster_list_size_| to see if this is the first cluster. If it is |
| 3172 // the first cluster the audio frames that are less than the first video |
| 3173 // timesatmp will be written in a later step. |
| 3174 if (frames_size_ > 0 && cluster_list_size_ > 0) { |
| 3175 if (!frames_) |
| 3176 return false; |
| 3177 |
| 3178 Cluster* const cluster = cluster_list_[cluster_list_size_-1]; |
| 3179 if (!cluster) |
| 3180 return false; |
| 3181 |
| 3182 const uint64 timecode_scale = segment_info_.timecode_scale(); |
| 3183 int32 shift_left = 0; |
| 3184 |
| 3185 // TODO(fgalligan): Change this to use the durations of frames instead of |
| 3186 // the next frame's start time if the duration is accurate. |
| 3187 for (int32 i = 1; i < frames_size_; ++i) { |
| 3188 const Frame* const frame_curr = frames_[i]; |
| 3189 |
| 3190 if (frame_curr->timestamp() > timestamp) |
| 3191 break; |
| 3192 |
| 3193 const Frame* const frame_prev = frames_[i-1]; |
| 3194 const uint64 frame_timestamp = frame_prev->timestamp(); |
| 3195 const uint64 frame_timecode = frame_timestamp / timecode_scale; |
| 3196 const int64 discard_padding = frame_prev->discard_padding(); |
| 3197 |
| 3198 if (discard_padding > 0) { |
| 3199 if (!cluster->AddFrameWithDiscardPadding(frame_prev->frame(), |
| 3200 frame_prev->length(), |
| 3201 discard_padding, |
| 3202 frame_prev->track_number(), |
| 3203 frame_timecode, |
| 3204 frame_prev->is_key())) { |
| 3205 return false; |
| 3206 } |
| 3207 } else { |
| 3208 if (!cluster->AddFrame(frame_prev->frame(), |
| 3209 frame_prev->length(), |
| 3210 frame_prev->track_number(), |
| 3211 frame_timecode, |
| 3212 frame_prev->is_key())) { |
| 3213 return false; |
| 3214 } |
| 3215 } |
| 3216 |
| 3217 if (new_cuepoint_ && cues_track_ == frame_prev->track_number()) { |
| 3218 if (!AddCuePoint(frame_timestamp, cues_track_)) |
| 3219 return false; |
| 3220 } |
| 3221 |
| 3222 ++shift_left; |
| 3223 if (frame_timestamp > last_timestamp_) |
| 3224 last_timestamp_ = frame_timestamp; |
| 3225 |
| 3226 delete frame_prev; |
| 3227 } |
| 3228 |
| 3229 if (shift_left > 0) { |
| 3230 if (shift_left >= frames_size_) |
| 3231 return false; |
| 3232 |
| 3233 const int32 new_frames_size = frames_size_ - shift_left; |
| 3234 for (int32 i = 0; i < new_frames_size; ++i) { |
| 3235 frames_[i] = frames_[i+shift_left]; |
| 3236 } |
| 3237 |
| 3238 frames_size_ = new_frames_size; |
| 3239 } |
| 3240 } |
| 3241 |
| 3242 return true; |
| 3243 } |
| 3244 |
| 3245 } // namespace mkvmuxer |
OLD | NEW |