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