MPEG4Writer.cpp revision 2dce41ad26cb3e9e15c9e456a84bcf5309548ca0
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <arpa/inet.h> 18 19#include <ctype.h> 20#include <pthread.h> 21 22#include <media/stagefright/MPEG4Writer.h> 23#include <media/stagefright/MediaBuffer.h> 24#include <media/stagefright/MetaData.h> 25#include <media/stagefright/MediaDebug.h> 26#include <media/stagefright/MediaDefs.h> 27#include <media/stagefright/MediaSource.h> 28#include <media/stagefright/Utils.h> 29 30namespace android { 31 32class MPEG4Writer::Track { 33public: 34 Track(MPEG4Writer *owner, const sp<MediaSource> &source); 35 ~Track(); 36 37 status_t start(); 38 void stop(); 39 bool reachedEOS(); 40 41 int64_t getDuration() const; 42 void writeTrackHeader(int32_t trackID); 43 44private: 45 MPEG4Writer *mOwner; 46 sp<MetaData> mMeta; 47 sp<MediaSource> mSource; 48 volatile bool mDone; 49 50 pthread_t mThread; 51 52 struct SampleInfo { 53 size_t size; 54 off_t offset; 55 int64_t timestamp; 56 }; 57 List<SampleInfo> mSampleInfos; 58 59 void *mCodecSpecificData; 60 size_t mCodecSpecificDataSize; 61 62 bool mReachedEOS; 63 64 static void *ThreadWrapper(void *me); 65 void threadEntry(); 66 67 Track(const Track &); 68 Track &operator=(const Track &); 69}; 70 71MPEG4Writer::MPEG4Writer(const char *filename) 72 : mFile(fopen(filename, "wb")), 73 mOffset(0), 74 mMdatOffset(0) { 75 CHECK(mFile != NULL); 76} 77 78MPEG4Writer::MPEG4Writer(int fd) 79 : mFile(fdopen(fd, "wb")), 80 mOffset(0), 81 mMdatOffset(0) { 82 CHECK(mFile != NULL); 83} 84 85MPEG4Writer::~MPEG4Writer() { 86 stop(); 87 88 for (List<Track *>::iterator it = mTracks.begin(); 89 it != mTracks.end(); ++it) { 90 delete *it; 91 } 92 mTracks.clear(); 93} 94 95status_t MPEG4Writer::addSource(const sp<MediaSource> &source) { 96 Track *track = new Track(this, source); 97 mTracks.push_back(track); 98 99 return OK; 100} 101 102status_t MPEG4Writer::start() { 103 if (mFile == NULL) { 104 return UNKNOWN_ERROR; 105 } 106 107 beginBox("ftyp"); 108 writeFourcc("isom"); 109 writeInt32(0); 110 writeFourcc("isom"); 111 endBox(); 112 113 mMdatOffset = mOffset; 114 write("\x00\x00\x00\x01mdat????????", 16); 115 116 for (List<Track *>::iterator it = mTracks.begin(); 117 it != mTracks.end(); ++it) { 118 status_t err = (*it)->start(); 119 120 if (err != OK) { 121 for (List<Track *>::iterator it2 = mTracks.begin(); 122 it2 != it; ++it2) { 123 (*it2)->stop(); 124 } 125 126 return err; 127 } 128 } 129 130 return OK; 131} 132 133void MPEG4Writer::stop() { 134 if (mFile == NULL) { 135 return; 136 } 137 138 int64_t max_duration = 0; 139 for (List<Track *>::iterator it = mTracks.begin(); 140 it != mTracks.end(); ++it) { 141 (*it)->stop(); 142 143 int64_t duration = (*it)->getDuration(); 144 if (duration > max_duration) { 145 max_duration = duration; 146 } 147 } 148 149 // Fix up the size of the 'mdat' chunk. 150 fseek(mFile, mMdatOffset + 8, SEEK_SET); 151 int64_t size = mOffset - mMdatOffset; 152 size = hton64(size); 153 fwrite(&size, 1, 8, mFile); 154 fseek(mFile, mOffset, SEEK_SET); 155 156 time_t now = time(NULL); 157 158 beginBox("moov"); 159 160 beginBox("mvhd"); 161 writeInt32(0); // version=0, flags=0 162 writeInt32(now); // creation time 163 writeInt32(now); // modification time 164 writeInt32(1000); // timescale 165 writeInt32(max_duration); 166 writeInt32(0x10000); // rate 167 writeInt16(0x100); // volume 168 writeInt16(0); // reserved 169 writeInt32(0); // reserved 170 writeInt32(0); // reserved 171 writeInt32(0x10000); // matrix 172 writeInt32(0); 173 writeInt32(0); 174 writeInt32(0); 175 writeInt32(0x10000); 176 writeInt32(0); 177 writeInt32(0); 178 writeInt32(0); 179 writeInt32(0x40000000); 180 writeInt32(0); // predefined 181 writeInt32(0); // predefined 182 writeInt32(0); // predefined 183 writeInt32(0); // predefined 184 writeInt32(0); // predefined 185 writeInt32(0); // predefined 186 writeInt32(mTracks.size() + 1); // nextTrackID 187 endBox(); // mvhd 188 189 int32_t id = 1; 190 for (List<Track *>::iterator it = mTracks.begin(); 191 it != mTracks.end(); ++it, ++id) { 192 (*it)->writeTrackHeader(id); 193 } 194 endBox(); // moov 195 196 CHECK(mBoxes.empty()); 197 198 fclose(mFile); 199 mFile = NULL; 200} 201 202off_t MPEG4Writer::addSample(MediaBuffer *buffer) { 203 Mutex::Autolock autoLock(mLock); 204 205 off_t old_offset = mOffset; 206 207 fwrite((const uint8_t *)buffer->data() + buffer->range_offset(), 208 1, buffer->range_length(), mFile); 209 210 mOffset += buffer->range_length(); 211 212 return old_offset; 213} 214 215off_t MPEG4Writer::addLengthPrefixedSample(MediaBuffer *buffer) { 216 Mutex::Autolock autoLock(mLock); 217 218 off_t old_offset = mOffset; 219 220 size_t length = buffer->range_length(); 221 CHECK(length < 65536); 222 223 uint8_t x = length >> 8; 224 fwrite(&x, 1, 1, mFile); 225 x = length & 0xff; 226 fwrite(&x, 1, 1, mFile); 227 228 fwrite((const uint8_t *)buffer->data() + buffer->range_offset(), 229 1, length, mFile); 230 231 mOffset += length + 2; 232 233 return old_offset; 234} 235 236void MPEG4Writer::beginBox(const char *fourcc) { 237 CHECK_EQ(strlen(fourcc), 4); 238 239 mBoxes.push_back(mOffset); 240 241 writeInt32(0); 242 writeFourcc(fourcc); 243} 244 245void MPEG4Writer::endBox() { 246 CHECK(!mBoxes.empty()); 247 248 off_t offset = *--mBoxes.end(); 249 mBoxes.erase(--mBoxes.end()); 250 251 fseek(mFile, offset, SEEK_SET); 252 writeInt32(mOffset - offset); 253 mOffset -= 4; 254 fseek(mFile, mOffset, SEEK_SET); 255} 256 257void MPEG4Writer::writeInt8(int8_t x) { 258 fwrite(&x, 1, 1, mFile); 259 ++mOffset; 260} 261 262void MPEG4Writer::writeInt16(int16_t x) { 263 x = htons(x); 264 fwrite(&x, 1, 2, mFile); 265 mOffset += 2; 266} 267 268void MPEG4Writer::writeInt32(int32_t x) { 269 x = htonl(x); 270 fwrite(&x, 1, 4, mFile); 271 mOffset += 4; 272} 273 274void MPEG4Writer::writeInt64(int64_t x) { 275 x = hton64(x); 276 fwrite(&x, 1, 8, mFile); 277 mOffset += 8; 278} 279 280void MPEG4Writer::writeCString(const char *s) { 281 size_t n = strlen(s); 282 283 fwrite(s, 1, n + 1, mFile); 284 mOffset += n + 1; 285} 286 287void MPEG4Writer::writeFourcc(const char *s) { 288 CHECK_EQ(strlen(s), 4); 289 fwrite(s, 1, 4, mFile); 290 mOffset += 4; 291} 292 293void MPEG4Writer::write(const void *data, size_t size) { 294 fwrite(data, 1, size, mFile); 295 mOffset += size; 296} 297 298bool MPEG4Writer::reachedEOS() { 299 bool allDone = true; 300 for (List<Track *>::iterator it = mTracks.begin(); 301 it != mTracks.end(); ++it) { 302 if (!(*it)->reachedEOS()) { 303 allDone = false; 304 break; 305 } 306 } 307 308 return allDone; 309} 310 311//////////////////////////////////////////////////////////////////////////////// 312 313MPEG4Writer::Track::Track( 314 MPEG4Writer *owner, const sp<MediaSource> &source) 315 : mOwner(owner), 316 mMeta(source->getFormat()), 317 mSource(source), 318 mDone(false), 319 mCodecSpecificData(NULL), 320 mCodecSpecificDataSize(0), 321 mReachedEOS(false) { 322} 323 324MPEG4Writer::Track::~Track() { 325 stop(); 326 327 if (mCodecSpecificData != NULL) { 328 free(mCodecSpecificData); 329 mCodecSpecificData = NULL; 330 } 331} 332 333status_t MPEG4Writer::Track::start() { 334 status_t err = mSource->start(); 335 336 if (err != OK) { 337 mDone = mReachedEOS = true; 338 return err; 339 } 340 341 pthread_attr_t attr; 342 pthread_attr_init(&attr); 343 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 344 345 mDone = false; 346 mReachedEOS = false; 347 348 pthread_create(&mThread, &attr, ThreadWrapper, this); 349 pthread_attr_destroy(&attr); 350 351 return OK; 352} 353 354void MPEG4Writer::Track::stop() { 355 if (mDone) { 356 return; 357 } 358 359 mDone = true; 360 361 void *dummy; 362 pthread_join(mThread, &dummy); 363 364 mSource->stop(); 365} 366 367bool MPEG4Writer::Track::reachedEOS() { 368 return mReachedEOS; 369} 370 371// static 372void *MPEG4Writer::Track::ThreadWrapper(void *me) { 373 Track *track = static_cast<Track *>(me); 374 375 track->threadEntry(); 376 377 return NULL; 378} 379 380void MPEG4Writer::Track::threadEntry() { 381 sp<MetaData> meta = mSource->getFormat(); 382 const char *mime; 383 meta->findCString(kKeyMIMEType, &mime); 384 bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4); 385 bool is_avc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); 386 int32_t count = 0; 387 388 MediaBuffer *buffer; 389 while (!mDone && mSource->read(&buffer) == OK) { 390 if (buffer->range_length() == 0) { 391 buffer->release(); 392 buffer = NULL; 393 394 continue; 395 } 396 397 ++count; 398 399 if (is_avc && count < 3) { 400 size_t size = buffer->range_length(); 401 402 switch (count) { 403 case 1: 404 { 405 CHECK_EQ(mCodecSpecificData, NULL); 406 mCodecSpecificData = malloc(size + 8); 407 uint8_t *header = (uint8_t *)mCodecSpecificData; 408 header[0] = 1; 409 header[1] = 0x42; // profile 410 header[2] = 0x80; 411 header[3] = 0x1e; // level 412 header[4] = 0xfc | 3; 413 header[5] = 0xe0 | 1; 414 header[6] = size >> 8; 415 header[7] = size & 0xff; 416 memcpy(&header[8], 417 (const uint8_t *)buffer->data() + buffer->range_offset(), 418 size); 419 420 mCodecSpecificDataSize = size + 8; 421 break; 422 } 423 424 case 2: 425 { 426 size_t offset = mCodecSpecificDataSize; 427 mCodecSpecificDataSize += size + 3; 428 mCodecSpecificData = realloc(mCodecSpecificData, mCodecSpecificDataSize); 429 uint8_t *header = (uint8_t *)mCodecSpecificData; 430 header[offset] = 1; 431 header[offset + 1] = size >> 8; 432 header[offset + 2] = size & 0xff; 433 memcpy(&header[offset + 3], 434 (const uint8_t *)buffer->data() + buffer->range_offset(), 435 size); 436 break; 437 } 438 } 439 440 buffer->release(); 441 buffer = NULL; 442 443 continue; 444 } 445 446 if (mCodecSpecificData == NULL && is_mpeg4) { 447 const uint8_t *data = 448 (const uint8_t *)buffer->data() + buffer->range_offset(); 449 450 const size_t size = buffer->range_length(); 451 452 size_t offset = 0; 453 while (offset + 3 < size) { 454 if (data[offset] == 0x00 && data[offset + 1] == 0x00 455 && data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) { 456 break; 457 } 458 459 ++offset; 460 } 461 462 // CHECK(offset + 3 < size); 463 if (offset + 3 >= size) { 464 // XXX assume the entire first chunk of data is the codec specific 465 // data. 466 offset = size; 467 } 468 469 mCodecSpecificDataSize = offset; 470 mCodecSpecificData = malloc(offset); 471 memcpy(mCodecSpecificData, data, offset); 472 473 buffer->set_range(buffer->range_offset() + offset, size - offset); 474 } 475 476 off_t offset = is_avc ? mOwner->addLengthPrefixedSample(buffer) 477 : mOwner->addSample(buffer); 478 479 SampleInfo info; 480 info.size = is_avc ? buffer->range_length() + 2 : buffer->range_length(); 481 info.offset = offset; 482 483 int64_t timestampUs; 484 CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs)); 485 486 // Our timestamp is in ms. 487 info.timestamp = (timestampUs + 500) / 1000; 488 489 mSampleInfos.push_back(info); 490 491 buffer->release(); 492 buffer = NULL; 493 } 494 495 mReachedEOS = true; 496} 497 498int64_t MPEG4Writer::Track::getDuration() const { 499 return 10000; // XXX 500} 501 502void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { 503 const char *mime; 504 bool success = mMeta->findCString(kKeyMIMEType, &mime); 505 CHECK(success); 506 507 bool is_audio = !strncasecmp(mime, "audio/", 6); 508 509 time_t now = time(NULL); 510 511 mOwner->beginBox("trak"); 512 513 mOwner->beginBox("tkhd"); 514 mOwner->writeInt32(0); // version=0, flags=0 515 mOwner->writeInt32(now); // creation time 516 mOwner->writeInt32(now); // modification time 517 mOwner->writeInt32(trackID); 518 mOwner->writeInt32(0); // reserved 519 mOwner->writeInt32(getDuration()); 520 mOwner->writeInt32(0); // reserved 521 mOwner->writeInt32(0); // reserved 522 mOwner->writeInt16(0); // layer 523 mOwner->writeInt16(0); // alternate group 524 mOwner->writeInt16(is_audio ? 0x100 : 0); // volume 525 mOwner->writeInt16(0); // reserved 526 527 mOwner->writeInt32(0x10000); // matrix 528 mOwner->writeInt32(0); 529 mOwner->writeInt32(0); 530 mOwner->writeInt32(0); 531 mOwner->writeInt32(0x10000); 532 mOwner->writeInt32(0); 533 mOwner->writeInt32(0); 534 mOwner->writeInt32(0); 535 mOwner->writeInt32(0x40000000); 536 537 if (is_audio) { 538 mOwner->writeInt32(0); 539 mOwner->writeInt32(0); 540 } else { 541 int32_t width, height; 542 bool success = mMeta->findInt32(kKeyWidth, &width); 543 success = success && mMeta->findInt32(kKeyHeight, &height); 544 CHECK(success); 545 546 mOwner->writeInt32(width); 547 mOwner->writeInt32(height); 548 } 549 mOwner->endBox(); // tkhd 550 551 mOwner->beginBox("mdia"); 552 553 mOwner->beginBox("mdhd"); 554 mOwner->writeInt32(0); // version=0, flags=0 555 mOwner->writeInt32(now); // creation time 556 mOwner->writeInt32(now); // modification time 557 mOwner->writeInt32(1000); // timescale 558 mOwner->writeInt32(getDuration()); 559 mOwner->writeInt16(0); // language code XXX 560 mOwner->writeInt16(0); // predefined 561 mOwner->endBox(); 562 563 mOwner->beginBox("hdlr"); 564 mOwner->writeInt32(0); // version=0, flags=0 565 mOwner->writeInt32(0); // predefined 566 mOwner->writeFourcc(is_audio ? "soun" : "vide"); 567 mOwner->writeInt32(0); // reserved 568 mOwner->writeInt32(0); // reserved 569 mOwner->writeInt32(0); // reserved 570 mOwner->writeCString(""); // name 571 mOwner->endBox(); 572 573 mOwner->beginBox("minf"); 574 575 mOwner->beginBox("dinf"); 576 mOwner->beginBox("dref"); 577 mOwner->writeInt32(0); // version=0, flags=0 578 mOwner->writeInt32(1); 579 mOwner->beginBox("url "); 580 mOwner->writeInt32(1); // version=0, flags=1 581 mOwner->endBox(); // url 582 mOwner->endBox(); // dref 583 mOwner->endBox(); // dinf 584 585 if (is_audio) { 586 mOwner->beginBox("smhd"); 587 mOwner->writeInt32(0); // version=0, flags=0 588 mOwner->writeInt16(0); // balance 589 mOwner->writeInt16(0); // reserved 590 mOwner->endBox(); 591 } else { 592 mOwner->beginBox("vmhd"); 593 mOwner->writeInt32(0x00000001); // version=0, flags=1 594 mOwner->writeInt16(0); // graphics mode 595 mOwner->writeInt16(0); // opcolor 596 mOwner->writeInt16(0); 597 mOwner->writeInt16(0); 598 mOwner->endBox(); 599 } 600 mOwner->endBox(); // minf 601 602 mOwner->beginBox("stbl"); 603 604 mOwner->beginBox("stsd"); 605 mOwner->writeInt32(0); // version=0, flags=0 606 mOwner->writeInt32(1); // entry count 607 if (is_audio) { 608 const char *fourcc = NULL; 609 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) { 610 fourcc = "samr"; 611 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) { 612 fourcc = "sawb"; 613 } else { 614 LOGE("Unknown mime type '%s'.", mime); 615 CHECK(!"should not be here, unknown mime type."); 616 } 617 618 mOwner->beginBox(fourcc); // audio format 619 mOwner->writeInt32(0); // reserved 620 mOwner->writeInt16(0); // reserved 621 mOwner->writeInt16(0); // data ref index 622 mOwner->writeInt32(0); // reserved 623 mOwner->writeInt32(0); // reserved 624 mOwner->writeInt16(2); // channel count 625 mOwner->writeInt16(16); // sample size 626 mOwner->writeInt16(0); // predefined 627 mOwner->writeInt16(0); // reserved 628 629 int32_t samplerate; 630 bool success = mMeta->findInt32(kKeySampleRate, &samplerate); 631 CHECK(success); 632 633 mOwner->writeInt32(samplerate << 16); 634 mOwner->endBox(); 635 } else { 636 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { 637 mOwner->beginBox("mp4v"); 638 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { 639 mOwner->beginBox("s263"); 640 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { 641 mOwner->beginBox("avc1"); 642 } else { 643 LOGE("Unknown mime type '%s'.", mime); 644 CHECK(!"should not be here, unknown mime type."); 645 } 646 647 mOwner->writeInt32(0); // reserved 648 mOwner->writeInt16(0); // reserved 649 mOwner->writeInt16(0); // data ref index 650 mOwner->writeInt16(0); // predefined 651 mOwner->writeInt16(0); // reserved 652 mOwner->writeInt32(0); // predefined 653 mOwner->writeInt32(0); // predefined 654 mOwner->writeInt32(0); // predefined 655 656 int32_t width, height; 657 bool success = mMeta->findInt32(kKeyWidth, &width); 658 success = success && mMeta->findInt32(kKeyHeight, &height); 659 CHECK(success); 660 661 mOwner->writeInt16(width); 662 mOwner->writeInt16(height); 663 mOwner->writeInt32(0x480000); // horiz resolution 664 mOwner->writeInt32(0x480000); // vert resolution 665 mOwner->writeInt32(0); // reserved 666 mOwner->writeInt16(1); // frame count 667 mOwner->write(" ", 32); 668 mOwner->writeInt16(0x18); // depth 669 mOwner->writeInt16(-1); // predefined 670 671 CHECK(23 + mCodecSpecificDataSize < 128); 672 673 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { 674 mOwner->beginBox("esds"); 675 676 mOwner->writeInt32(0); // version=0, flags=0 677 678 mOwner->writeInt8(0x03); // ES_DescrTag 679 mOwner->writeInt8(23 + mCodecSpecificDataSize); 680 mOwner->writeInt16(0x0000); // ES_ID 681 mOwner->writeInt8(0x1f); 682 683 mOwner->writeInt8(0x04); // DecoderConfigDescrTag 684 mOwner->writeInt8(15 + mCodecSpecificDataSize); 685 mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2 686 mOwner->writeInt8(0x11); // streamType VisualStream 687 688 static const uint8_t kData[] = { 689 0x01, 0x77, 0x00, 690 0x00, 0x03, 0xe8, 0x00, 691 0x00, 0x03, 0xe8, 0x00 692 }; 693 mOwner->write(kData, sizeof(kData)); 694 695 mOwner->writeInt8(0x05); // DecoderSpecificInfoTag 696 697 mOwner->writeInt8(mCodecSpecificDataSize); 698 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); 699 700 static const uint8_t kData2[] = { 701 0x06, // SLConfigDescriptorTag 702 0x01, 703 0x02 704 }; 705 mOwner->write(kData2, sizeof(kData2)); 706 707 mOwner->endBox(); // esds 708 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { 709 mOwner->beginBox("d263"); 710 711 mOwner->writeInt32(0); // vendor 712 mOwner->writeInt8(0); // decoder version 713 mOwner->writeInt8(10); // level: 10 714 mOwner->writeInt8(0); // profile: 0 715 716 mOwner->endBox(); // d263 717 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { 718 mOwner->beginBox("avcC"); 719 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); 720 mOwner->endBox(); // avcC 721 } 722 723 mOwner->endBox(); // mp4v, s263 or avc1 724 } 725 mOwner->endBox(); // stsd 726 727 mOwner->beginBox("stts"); 728 mOwner->writeInt32(0); // version=0, flags=0 729 mOwner->writeInt32(mSampleInfos.size() - 1); 730 731 List<SampleInfo>::iterator it = mSampleInfos.begin(); 732 int64_t last = (*it).timestamp; 733 ++it; 734 while (it != mSampleInfos.end()) { 735 mOwner->writeInt32(1); 736 mOwner->writeInt32((*it).timestamp - last); 737 738 last = (*it).timestamp; 739 740 ++it; 741 } 742 mOwner->endBox(); // stts 743 744 mOwner->beginBox("stsz"); 745 mOwner->writeInt32(0); // version=0, flags=0 746 mOwner->writeInt32(0); // default sample size 747 mOwner->writeInt32(mSampleInfos.size()); 748 for (List<SampleInfo>::iterator it = mSampleInfos.begin(); 749 it != mSampleInfos.end(); ++it) { 750 mOwner->writeInt32((*it).size); 751 } 752 mOwner->endBox(); // stsz 753 754 mOwner->beginBox("stsc"); 755 mOwner->writeInt32(0); // version=0, flags=0 756 mOwner->writeInt32(mSampleInfos.size()); 757 int32_t n = 1; 758 for (List<SampleInfo>::iterator it = mSampleInfos.begin(); 759 it != mSampleInfos.end(); ++it, ++n) { 760 mOwner->writeInt32(n); 761 mOwner->writeInt32(1); 762 mOwner->writeInt32(1); 763 } 764 mOwner->endBox(); // stsc 765 766 mOwner->beginBox("co64"); 767 mOwner->writeInt32(0); // version=0, flags=0 768 mOwner->writeInt32(mSampleInfos.size()); 769 for (List<SampleInfo>::iterator it = mSampleInfos.begin(); 770 it != mSampleInfos.end(); ++it, ++n) { 771 mOwner->writeInt64((*it).offset); 772 } 773 mOwner->endBox(); // co64 774 775 mOwner->endBox(); // stbl 776 mOwner->endBox(); // mdia 777 mOwner->endBox(); // trak 778} 779 780} // namespace android 781