MPEG4Writer.cpp revision 365a963142093a1cd8efdcea76b5f65096a5b115
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//#define LOG_NDEBUG 0 18#define LOG_TAG "MPEG4Writer" 19#include <utils/Log.h> 20 21#include <arpa/inet.h> 22 23#include <ctype.h> 24#include <pthread.h> 25 26#include <media/stagefright/MPEG4Writer.h> 27#include <media/stagefright/MediaBuffer.h> 28#include <media/stagefright/MetaData.h> 29#include <media/stagefright/MediaDebug.h> 30#include <media/stagefright/MediaDefs.h> 31#include <media/stagefright/MediaErrors.h> 32#include <media/stagefright/MediaSource.h> 33#include <media/stagefright/Utils.h> 34#include <media/mediarecorder.h> 35#include <cutils/properties.h> 36 37namespace android { 38 39class MPEG4Writer::Track { 40public: 41 Track(MPEG4Writer *owner, const sp<MediaSource> &source); 42 ~Track(); 43 44 status_t start(); 45 void stop(); 46 bool reachedEOS(); 47 48 int64_t getDurationUs() const; 49 int64_t getEstimatedTrackSizeBytes() const; 50 void writeTrackHeader(int32_t trackID); 51 52private: 53 MPEG4Writer *mOwner; 54 sp<MetaData> mMeta; 55 sp<MediaSource> mSource; 56 volatile bool mDone; 57 int64_t mMaxTimeStampUs; 58 int64_t mEstimatedTrackSizeBytes; 59 60 pthread_t mThread; 61 62 struct SampleInfo { 63 size_t size; 64 int64_t timestamp; 65 }; 66 List<SampleInfo> mSampleInfos; 67 bool mSamplesHaveSameSize; 68 69 List<MediaBuffer *> mChunkSamples; 70 List<off_t> mChunkOffsets; 71 72 struct StscTableEntry { 73 74 StscTableEntry(uint32_t chunk, uint32_t samples, uint32_t id) 75 : firstChunk(chunk), 76 samplesPerChunk(samples), 77 sampleDescriptionId(id) {} 78 79 uint32_t firstChunk; 80 uint32_t samplesPerChunk; 81 uint32_t sampleDescriptionId; 82 }; 83 List<StscTableEntry> mStscTableEntries; 84 85 List<int32_t> mStssTableEntries; 86 List<int64_t> mChunkDurations; 87 88 struct SttsTableEntry { 89 90 SttsTableEntry(uint32_t count, uint32_t duration) 91 : sampleCount(count), sampleDuration(duration) {} 92 93 uint32_t sampleCount; 94 uint32_t sampleDuration; 95 }; 96 List<SttsTableEntry> mSttsTableEntries; 97 98 void *mCodecSpecificData; 99 size_t mCodecSpecificDataSize; 100 bool mGotAllCodecSpecificData; 101 102 bool mReachedEOS; 103 int64_t mStartTimestampUs; 104 105 static void *ThreadWrapper(void *me); 106 void threadEntry(); 107 108 status_t makeAVCCodecSpecificData( 109 const uint8_t *data, size_t size); 110 void writeOneChunk(bool isAvc); 111 void logStatisticalData(bool isAudio); 112 void findMinMaxFrameRates(float *minFps, float *maxFps); 113 void findMinMaxChunkDurations(int64_t *min, int64_t *max); 114 115 Track(const Track &); 116 Track &operator=(const Track &); 117}; 118 119#define USE_NALLEN_FOUR 1 120 121MPEG4Writer::MPEG4Writer(const char *filename) 122 : mFile(fopen(filename, "wb")), 123 mOffset(0), 124 mMdatOffset(0), 125 mEstimatedMoovBoxSize(0), 126 mInterleaveDurationUs(500000) { 127 CHECK(mFile != NULL); 128} 129 130MPEG4Writer::MPEG4Writer(int fd) 131 : mFile(fdopen(fd, "wb")), 132 mOffset(0), 133 mMdatOffset(0), 134 mEstimatedMoovBoxSize(0), 135 mInterleaveDurationUs(500000) { 136 CHECK(mFile != NULL); 137} 138 139MPEG4Writer::~MPEG4Writer() { 140 stop(); 141 142 for (List<Track *>::iterator it = mTracks.begin(); 143 it != mTracks.end(); ++it) { 144 delete *it; 145 } 146 mTracks.clear(); 147} 148 149status_t MPEG4Writer::addSource(const sp<MediaSource> &source) { 150 Track *track = new Track(this, source); 151 mTracks.push_back(track); 152 153 return OK; 154} 155 156status_t MPEG4Writer::start() { 157 if (mFile == NULL) { 158 return UNKNOWN_ERROR; 159 } 160 161 mStartTimestampUs = 0; 162 mStreamableFile = true; 163 mWriteMoovBoxToMemory = false; 164 mMoovBoxBuffer = NULL; 165 mMoovBoxBufferOffset = 0; 166 167 beginBox("ftyp"); 168 writeFourcc("isom"); 169 writeInt32(0); 170 writeFourcc("isom"); 171 endBox(); 172 173 mFreeBoxOffset = mOffset; 174 175 if (mEstimatedMoovBoxSize == 0) { 176 // XXX: Estimate the moov box size 177 // based on max file size or duration limit 178 mEstimatedMoovBoxSize = 0x0F00; 179 } 180 CHECK(mEstimatedMoovBoxSize >= 8); 181 fseeko(mFile, mFreeBoxOffset, SEEK_SET); 182 writeInt32(mEstimatedMoovBoxSize); 183 write("free", 4); 184 185 mMdatOffset = mFreeBoxOffset + mEstimatedMoovBoxSize; 186 mOffset = mMdatOffset; 187 fseeko(mFile, mMdatOffset, SEEK_SET); 188 write("\x00\x00\x00\x01mdat????????", 16); 189 for (List<Track *>::iterator it = mTracks.begin(); 190 it != mTracks.end(); ++it) { 191 status_t err = (*it)->start(); 192 193 if (err != OK) { 194 for (List<Track *>::iterator it2 = mTracks.begin(); 195 it2 != it; ++it2) { 196 (*it2)->stop(); 197 } 198 199 return err; 200 } 201 } 202 203 return OK; 204} 205 206void MPEG4Writer::stop() { 207 if (mFile == NULL) { 208 return; 209 } 210 211 int64_t max_duration = 0; 212 for (List<Track *>::iterator it = mTracks.begin(); 213 it != mTracks.end(); ++it) { 214 (*it)->stop(); 215 216 int64_t duration = (*it)->getDurationUs(); 217 if (duration > max_duration) { 218 max_duration = duration; 219 } 220 } 221 222 223 // Fix up the size of the 'mdat' chunk. 224 fseeko(mFile, mMdatOffset + 8, SEEK_SET); 225 int64_t size = mOffset - mMdatOffset; 226 size = hton64(size); 227 fwrite(&size, 1, 8, mFile); 228 fseeko(mFile, mOffset, SEEK_SET); 229 230 time_t now = time(NULL); 231 const off_t moovOffset = mOffset; 232 mWriteMoovBoxToMemory = true; 233 mMoovBoxBuffer = (uint8_t *) malloc(mEstimatedMoovBoxSize); 234 mMoovBoxBufferOffset = 0; 235 CHECK(mMoovBoxBuffer != NULL); 236 237 beginBox("moov"); 238 239 beginBox("mvhd"); 240 writeInt32(0); // version=0, flags=0 241 writeInt32(now); // creation time 242 writeInt32(now); // modification time 243 writeInt32(1000); // timescale 244 writeInt32(max_duration / 1000); 245 writeInt32(0x10000); // rate 246 writeInt16(0x100); // volume 247 writeInt16(0); // reserved 248 writeInt32(0); // reserved 249 writeInt32(0); // reserved 250 writeInt32(0x10000); // matrix 251 writeInt32(0); 252 writeInt32(0); 253 writeInt32(0); 254 writeInt32(0x10000); 255 writeInt32(0); 256 writeInt32(0); 257 writeInt32(0); 258 writeInt32(0x40000000); 259 writeInt32(0); // predefined 260 writeInt32(0); // predefined 261 writeInt32(0); // predefined 262 writeInt32(0); // predefined 263 writeInt32(0); // predefined 264 writeInt32(0); // predefined 265 writeInt32(mTracks.size() + 1); // nextTrackID 266 endBox(); // mvhd 267 268 int32_t id = 1; 269 for (List<Track *>::iterator it = mTracks.begin(); 270 it != mTracks.end(); ++it, ++id) { 271 (*it)->writeTrackHeader(id); 272 } 273 endBox(); // moov 274 275 mWriteMoovBoxToMemory = false; 276 if (mStreamableFile) { 277 CHECK(mMoovBoxBufferOffset + 8 <= mEstimatedMoovBoxSize); 278 279 // Moov box 280 fseeko(mFile, mFreeBoxOffset, SEEK_SET); 281 mOffset = mFreeBoxOffset; 282 write(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, mFile); 283 284 // Free box 285 mFreeBoxOffset = mStreamableFile? mOffset: mFreeBoxOffset; 286 fseeko(mFile, mFreeBoxOffset, SEEK_SET); 287 writeInt32(mEstimatedMoovBoxSize - mMoovBoxBufferOffset); 288 write("free", 4); 289 290 // Free temp memory 291 free(mMoovBoxBuffer); 292 mMoovBoxBuffer = NULL; 293 mMoovBoxBufferOffset = 0; 294 } 295 296 CHECK(mBoxes.empty()); 297 298 fflush(mFile); 299 fclose(mFile); 300 mFile = NULL; 301} 302 303status_t MPEG4Writer::setInterleaveDuration(uint32_t durationUs) { 304 mInterleaveDurationUs = durationUs; 305 return OK; 306} 307 308void MPEG4Writer::lock() { 309 mLock.lock(); 310} 311 312void MPEG4Writer::unlock() { 313 mLock.unlock(); 314} 315 316off_t MPEG4Writer::addSample_l(MediaBuffer *buffer) { 317 off_t old_offset = mOffset; 318 319 fwrite((const uint8_t *)buffer->data() + buffer->range_offset(), 320 1, buffer->range_length(), mFile); 321 322 mOffset += buffer->range_length(); 323 324 return old_offset; 325} 326 327static void StripStartcode(MediaBuffer *buffer) { 328 if (buffer->range_length() < 4) { 329 return; 330 } 331 332 const uint8_t *ptr = 333 (const uint8_t *)buffer->data() + buffer->range_offset(); 334 335 if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) { 336 buffer->set_range( 337 buffer->range_offset() + 4, buffer->range_length() - 4); 338 } 339} 340 341off_t MPEG4Writer::addLengthPrefixedSample_l(MediaBuffer *buffer) { 342 off_t old_offset = mOffset; 343 344 size_t length = buffer->range_length(); 345 346#if USE_NALLEN_FOUR 347 uint8_t x = length >> 24; 348 fwrite(&x, 1, 1, mFile); 349 x = (length >> 16) & 0xff; 350 fwrite(&x, 1, 1, mFile); 351 x = (length >> 8) & 0xff; 352 fwrite(&x, 1, 1, mFile); 353 x = length & 0xff; 354 fwrite(&x, 1, 1, mFile); 355#else 356 CHECK(length < 65536); 357 358 uint8_t x = length >> 8; 359 fwrite(&x, 1, 1, mFile); 360 x = length & 0xff; 361 fwrite(&x, 1, 1, mFile); 362#endif 363 364 fwrite((const uint8_t *)buffer->data() + buffer->range_offset(), 365 1, length, mFile); 366 367#if USE_NALLEN_FOUR 368 mOffset += length + 4; 369#else 370 mOffset += length + 2; 371#endif 372 373 return old_offset; 374} 375 376size_t MPEG4Writer::write( 377 const void *ptr, size_t size, size_t nmemb, FILE *stream) { 378 379 const size_t bytes = size * nmemb; 380 if (mWriteMoovBoxToMemory) { 381 if (8 + mMoovBoxBufferOffset + bytes > mEstimatedMoovBoxSize) { 382 for (List<off_t>::iterator it = mBoxes.begin(); 383 it != mBoxes.end(); ++it) { 384 (*it) += mOffset; 385 } 386 fseeko(mFile, mOffset, SEEK_SET); 387 fwrite(mMoovBoxBuffer, 1, mMoovBoxBufferOffset, stream); 388 fwrite(ptr, size, nmemb, stream); 389 mOffset += (bytes + mMoovBoxBufferOffset); 390 free(mMoovBoxBuffer); 391 mMoovBoxBuffer = NULL; 392 mMoovBoxBufferOffset = 0; 393 mWriteMoovBoxToMemory = false; 394 mStreamableFile = false; 395 } else { 396 memcpy(mMoovBoxBuffer + mMoovBoxBufferOffset, ptr, bytes); 397 mMoovBoxBufferOffset += bytes; 398 } 399 } else { 400 fwrite(ptr, size, nmemb, stream); 401 mOffset += bytes; 402 } 403 return bytes; 404} 405 406void MPEG4Writer::beginBox(const char *fourcc) { 407 CHECK_EQ(strlen(fourcc), 4); 408 409 mBoxes.push_back(mWriteMoovBoxToMemory? 410 mMoovBoxBufferOffset: mOffset); 411 412 writeInt32(0); 413 writeFourcc(fourcc); 414} 415 416void MPEG4Writer::endBox() { 417 CHECK(!mBoxes.empty()); 418 419 off_t offset = *--mBoxes.end(); 420 mBoxes.erase(--mBoxes.end()); 421 422 if (mWriteMoovBoxToMemory) { 423 int32_t x = htonl(mMoovBoxBufferOffset - offset); 424 memcpy(mMoovBoxBuffer + offset, &x, 4); 425 } else { 426 fseeko(mFile, offset, SEEK_SET); 427 writeInt32(mOffset - offset); 428 mOffset -= 4; 429 fseeko(mFile, mOffset, SEEK_SET); 430 } 431} 432 433void MPEG4Writer::writeInt8(int8_t x) { 434 write(&x, 1, 1, mFile); 435} 436 437void MPEG4Writer::writeInt16(int16_t x) { 438 x = htons(x); 439 write(&x, 1, 2, mFile); 440} 441 442void MPEG4Writer::writeInt32(int32_t x) { 443 x = htonl(x); 444 write(&x, 1, 4, mFile); 445} 446 447void MPEG4Writer::writeInt64(int64_t x) { 448 x = hton64(x); 449 write(&x, 1, 8, mFile); 450} 451 452void MPEG4Writer::writeCString(const char *s) { 453 size_t n = strlen(s); 454 write(s, 1, n + 1, mFile); 455} 456 457void MPEG4Writer::writeFourcc(const char *s) { 458 CHECK_EQ(strlen(s), 4); 459 write(s, 1, 4, mFile); 460} 461 462void MPEG4Writer::write(const void *data, size_t size) { 463 write(data, 1, size, mFile); 464} 465 466bool MPEG4Writer::exceedsFileSizeLimit() { 467 // No limit 468 if (mMaxFileSizeLimitBytes == 0) { 469 return false; 470 } 471 472 int64_t nTotalBytesEstimate = static_cast<int64_t>(mEstimatedMoovBoxSize); 473 for (List<Track *>::iterator it = mTracks.begin(); 474 it != mTracks.end(); ++it) { 475 nTotalBytesEstimate += (*it)->getEstimatedTrackSizeBytes(); 476 } 477 return (nTotalBytesEstimate >= mMaxFileSizeLimitBytes); 478} 479 480bool MPEG4Writer::exceedsFileDurationLimit() { 481 // No limit 482 if (mMaxFileDurationLimitUs == 0) { 483 return false; 484 } 485 486 for (List<Track *>::iterator it = mTracks.begin(); 487 it != mTracks.end(); ++it) { 488 if ((*it)->getDurationUs() >= mMaxFileDurationLimitUs) { 489 return true; 490 } 491 } 492 return false; 493} 494 495bool MPEG4Writer::reachedEOS() { 496 bool allDone = true; 497 for (List<Track *>::iterator it = mTracks.begin(); 498 it != mTracks.end(); ++it) { 499 if (!(*it)->reachedEOS()) { 500 allDone = false; 501 break; 502 } 503 } 504 505 return allDone; 506} 507 508void MPEG4Writer::setStartTimestamp(int64_t timeUs) { 509 LOGI("setStartTimestamp: %lld", timeUs); 510 Mutex::Autolock autoLock(mLock); 511 if (mStartTimestampUs != 0) { 512 return; // Sorry, too late 513 } 514 mStartTimestampUs = timeUs; 515} 516 517int64_t MPEG4Writer::getStartTimestamp() { 518 LOGI("getStartTimestamp: %lld", mStartTimestampUs); 519 Mutex::Autolock autoLock(mLock); 520 return mStartTimestampUs; 521} 522 523//////////////////////////////////////////////////////////////////////////////// 524 525MPEG4Writer::Track::Track( 526 MPEG4Writer *owner, const sp<MediaSource> &source) 527 : mOwner(owner), 528 mMeta(source->getFormat()), 529 mSource(source), 530 mDone(false), 531 mMaxTimeStampUs(0), 532 mEstimatedTrackSizeBytes(0), 533 mSamplesHaveSameSize(true), 534 mCodecSpecificData(NULL), 535 mCodecSpecificDataSize(0), 536 mGotAllCodecSpecificData(false), 537 mReachedEOS(false) { 538} 539 540MPEG4Writer::Track::~Track() { 541 stop(); 542 543 if (mCodecSpecificData != NULL) { 544 free(mCodecSpecificData); 545 mCodecSpecificData = NULL; 546 } 547} 548 549status_t MPEG4Writer::Track::start() { 550 status_t err = mSource->start(); 551 552 if (err != OK) { 553 mDone = mReachedEOS = true; 554 return err; 555 } 556 557 pthread_attr_t attr; 558 pthread_attr_init(&attr); 559 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 560 561 mDone = false; 562 mMaxTimeStampUs = 0; 563 mReachedEOS = false; 564 mEstimatedTrackSizeBytes = 0; 565 566 pthread_create(&mThread, &attr, ThreadWrapper, this); 567 pthread_attr_destroy(&attr); 568 569 return OK; 570} 571 572void MPEG4Writer::Track::stop() { 573 if (mDone) { 574 return; 575 } 576 577 mDone = true; 578 579 void *dummy; 580 pthread_join(mThread, &dummy); 581 582 mSource->stop(); 583} 584 585bool MPEG4Writer::Track::reachedEOS() { 586 return mReachedEOS; 587} 588 589// static 590void *MPEG4Writer::Track::ThreadWrapper(void *me) { 591 Track *track = static_cast<Track *>(me); 592 593 track->threadEntry(); 594 595 return NULL; 596} 597 598#include <ctype.h> 599static void hexdump(const void *_data, size_t size) { 600 const uint8_t *data = (const uint8_t *)_data; 601 size_t offset = 0; 602 while (offset < size) { 603 printf("0x%04x ", offset); 604 605 size_t n = size - offset; 606 if (n > 16) { 607 n = 16; 608 } 609 610 for (size_t i = 0; i < 16; ++i) { 611 if (i == 8) { 612 printf(" "); 613 } 614 615 if (offset + i < size) { 616 printf("%02x ", data[offset + i]); 617 } else { 618 printf(" "); 619 } 620 } 621 622 printf(" "); 623 624 for (size_t i = 0; i < n; ++i) { 625 if (isprint(data[offset + i])) { 626 printf("%c", data[offset + i]); 627 } else { 628 printf("."); 629 } 630 } 631 632 printf("\n"); 633 634 offset += 16; 635 } 636} 637 638 639status_t MPEG4Writer::Track::makeAVCCodecSpecificData( 640 const uint8_t *data, size_t size) { 641 // hexdump(data, size); 642 643 if (mCodecSpecificData != NULL) { 644 LOGE("Already have codec specific data"); 645 return ERROR_MALFORMED; 646 } 647 648 if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) { 649 LOGE("Must start with a start code"); 650 return ERROR_MALFORMED; 651 } 652 653 size_t picParamOffset = 4; 654 while (picParamOffset + 3 < size 655 && memcmp("\x00\x00\x00\x01", &data[picParamOffset], 4)) { 656 ++picParamOffset; 657 } 658 659 if (picParamOffset + 3 >= size) { 660 LOGE("Could not find start-code for pictureParameterSet"); 661 return ERROR_MALFORMED; 662 } 663 664 size_t seqParamSetLength = picParamOffset - 4; 665 size_t picParamSetLength = size - picParamOffset - 4; 666 667 mCodecSpecificDataSize = 668 6 + 1 + seqParamSetLength + 2 + picParamSetLength + 2; 669 670 mCodecSpecificData = malloc(mCodecSpecificDataSize); 671 uint8_t *header = (uint8_t *)mCodecSpecificData; 672 header[0] = 1; 673 header[1] = 0x42; // profile 674 header[2] = 0x80; 675 header[3] = 0x1e; // level 676 677#if USE_NALLEN_FOUR 678 header[4] = 0xfc | 3; // length size == 4 bytes 679#else 680 header[4] = 0xfc | 1; // length size == 2 bytes 681#endif 682 683 header[5] = 0xe0 | 1; 684 header[6] = seqParamSetLength >> 8; 685 header[7] = seqParamSetLength & 0xff; 686 memcpy(&header[8], &data[4], seqParamSetLength); 687 header += 8 + seqParamSetLength; 688 header[0] = 1; 689 header[1] = picParamSetLength >> 8; 690 header[2] = picParamSetLength & 0xff; 691 memcpy(&header[3], &data[picParamOffset + 4], picParamSetLength); 692 693 return OK; 694} 695 696void MPEG4Writer::Track::threadEntry() { 697 sp<MetaData> meta = mSource->getFormat(); 698 const char *mime; 699 meta->findCString(kKeyMIMEType, &mime); 700 bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || 701 !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC); 702 bool is_avc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); 703 bool is_audio = !strncasecmp(mime, "audio/", 6); 704 int32_t count = 0; 705 const int64_t interleaveDurationUs = mOwner->interleaveDuration(); 706 int64_t chunkTimestampUs = 0; 707 int32_t nChunks = 0; 708 int32_t nZeroLengthFrames = 0; 709 int64_t lastTimestamp = 0; // Timestamp of the previous sample 710 int64_t lastDuration = 0; // Time spacing between the previous two samples 711 int32_t sampleCount = 1; // Sample count in the current stts table entry 712 uint32_t previousSampleSize = 0; // Size of the previous sample 713 sp<MetaData> meta_data; 714 715 MediaBuffer *buffer; 716 while (!mDone && mSource->read(&buffer) == OK) { 717 if (buffer->range_length() == 0) { 718 buffer->release(); 719 buffer = NULL; 720 ++nZeroLengthFrames; 721 continue; 722 } 723 724 ++count; 725 726 int32_t isCodecConfig; 727 if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig) 728 && isCodecConfig) { 729 CHECK(!mGotAllCodecSpecificData); 730 731 if (is_avc) { 732 status_t err = makeAVCCodecSpecificData( 733 (const uint8_t *)buffer->data() 734 + buffer->range_offset(), 735 buffer->range_length()); 736 CHECK_EQ(OK, err); 737 } else if (is_mpeg4) { 738 mCodecSpecificDataSize = buffer->range_length(); 739 mCodecSpecificData = malloc(mCodecSpecificDataSize); 740 memcpy(mCodecSpecificData, 741 (const uint8_t *)buffer->data() 742 + buffer->range_offset(), 743 buffer->range_length()); 744 } 745 746 buffer->release(); 747 buffer = NULL; 748 749 mGotAllCodecSpecificData = true; 750 continue; 751 } else if (!mGotAllCodecSpecificData && 752 count == 1 && is_mpeg4 && mCodecSpecificData == NULL) { 753 // The TI mpeg4 encoder does not properly set the 754 // codec-specific-data flag. 755 756 const uint8_t *data = 757 (const uint8_t *)buffer->data() + buffer->range_offset(); 758 759 const size_t size = buffer->range_length(); 760 761 size_t offset = 0; 762 while (offset + 3 < size) { 763 if (data[offset] == 0x00 && data[offset + 1] == 0x00 764 && data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) { 765 break; 766 } 767 768 ++offset; 769 } 770 771 // CHECK(offset + 3 < size); 772 if (offset + 3 >= size) { 773 // XXX assume the entire first chunk of data is the codec specific 774 // data. 775 offset = size; 776 } 777 778 mCodecSpecificDataSize = offset; 779 mCodecSpecificData = malloc(offset); 780 memcpy(mCodecSpecificData, data, offset); 781 782 buffer->set_range(buffer->range_offset() + offset, size - offset); 783 784 if (size == offset) { 785 buffer->release(); 786 buffer = NULL; 787 788 continue; 789 } 790 791 mGotAllCodecSpecificData = true; 792 } else if (!mGotAllCodecSpecificData && is_avc && count < 3) { 793 // The TI video encoder does not flag codec specific data 794 // as such and also splits up SPS and PPS across two buffers. 795 796 const uint8_t *data = 797 (const uint8_t *)buffer->data() + buffer->range_offset(); 798 799 size_t size = buffer->range_length(); 800 801 CHECK(count == 2 || mCodecSpecificData == NULL); 802 803 size_t offset = mCodecSpecificDataSize; 804 mCodecSpecificDataSize += size + 4; 805 mCodecSpecificData = 806 realloc(mCodecSpecificData, mCodecSpecificDataSize); 807 808 memcpy((uint8_t *)mCodecSpecificData + offset, 809 "\x00\x00\x00\x01", 4); 810 811 memcpy((uint8_t *)mCodecSpecificData + offset + 4, data, size); 812 813 buffer->release(); 814 buffer = NULL; 815 816 if (count == 2) { 817 void *tmp = mCodecSpecificData; 818 size = mCodecSpecificDataSize; 819 mCodecSpecificData = NULL; 820 mCodecSpecificDataSize = 0; 821 822 status_t err = makeAVCCodecSpecificData( 823 (const uint8_t *)tmp, size); 824 free(tmp); 825 tmp = NULL; 826 CHECK_EQ(OK, err); 827 828 mGotAllCodecSpecificData = true; 829 } 830 831 continue; 832 } 833 834 // Make a deep copy of the MediaBuffer and Metadata and release 835 // the original as soon as we can 836 MediaBuffer *copy = new MediaBuffer(buffer->range_length()); 837 memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(), 838 buffer->range_length()); 839 copy->set_range(0, buffer->range_length()); 840 meta_data = new MetaData(*buffer->meta_data().get()); 841 buffer->release(); 842 buffer = NULL; 843 844 if (is_avc) StripStartcode(copy); 845 846 SampleInfo info; 847 info.size = is_avc 848#if USE_NALLEN_FOUR 849 ? copy->range_length() + 4 850#else 851 ? copy->range_length() + 2 852#endif 853 : copy->range_length(); 854 855 // Max file size or duration handling 856 mEstimatedTrackSizeBytes += info.size; 857 if (mOwner->exceedsFileSizeLimit()) { 858 mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0); 859 break; 860 } 861 if (mOwner->exceedsFileDurationLimit()) { 862 mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0); 863 break; 864 } 865 866 867 int32_t isSync = false; 868 meta_data->findInt32(kKeyIsSyncFrame, &isSync); 869 870 int64_t timestampUs; 871 CHECK(meta_data->findInt64(kKeyTime, ×tampUs)); 872 873//////////////////////////////////////////////////////////////////////////////// 874 if (mSampleInfos.empty()) { 875 mOwner->setStartTimestamp(timestampUs); 876 mStartTimestampUs = (timestampUs - mOwner->getStartTimestamp()); 877 } 878 879 if (timestampUs > mMaxTimeStampUs) { 880 mMaxTimeStampUs = timestampUs; 881 } 882 883 // Our timestamp is in ms. 884 info.timestamp = (timestampUs + 500) / 1000; 885 mSampleInfos.push_back(info); 886 if (mSampleInfos.size() > 2) { 887 if (lastDuration != info.timestamp - lastTimestamp) { 888 SttsTableEntry sttsEntry(sampleCount, lastDuration); 889 mSttsTableEntries.push_back(sttsEntry); 890 sampleCount = 1; 891 } else { 892 ++sampleCount; 893 } 894 } 895 if (mSamplesHaveSameSize) { 896 if (mSampleInfos.size() >= 2 && previousSampleSize != info.size) { 897 mSamplesHaveSameSize = false; 898 } 899 previousSampleSize = info.size; 900 } 901 lastDuration = info.timestamp - lastTimestamp; 902 lastTimestamp = info.timestamp; 903 904 if (isSync != 0) { 905 mStssTableEntries.push_back(mSampleInfos.size()); 906 } 907 908 909 mChunkSamples.push_back(copy); 910 if (interleaveDurationUs == 0) { 911 StscTableEntry stscEntry(++nChunks, 1, 1); 912 mStscTableEntries.push_back(stscEntry); 913 writeOneChunk(is_avc); 914 } else { 915 if (chunkTimestampUs == 0) { 916 chunkTimestampUs = timestampUs; 917 } else { 918 if (timestampUs - chunkTimestampUs > interleaveDurationUs) { 919 ++nChunks; 920 mChunkDurations.push_back(timestampUs - chunkTimestampUs); 921 if (nChunks == 1 || // First chunk 922 (--(mStscTableEntries.end()))->samplesPerChunk != 923 mChunkSamples.size()) { 924 StscTableEntry stscEntry(nChunks, 925 mChunkSamples.size(), 1); 926 mStscTableEntries.push_back(stscEntry); 927 } 928 writeOneChunk(is_avc); 929 chunkTimestampUs = timestampUs; 930 } 931 } 932 } 933 934 } 935 936 if (mSampleInfos.empty()) { 937 mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_STOP_PREMATURELY, 0); 938 } 939 940 // Last chunk 941 if (!mChunkSamples.empty()) { 942 ++nChunks; 943 StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1); 944 mStscTableEntries.push_back(stscEntry); 945 writeOneChunk(is_avc); 946 } 947 948 // We don't really know how long the last frame lasts, since 949 // there is no frame time after it, just repeat the previous 950 // frame's duration. 951 if (mSampleInfos.size() == 1) { 952 lastDuration = 0; // A single sample's duration 953 } else { 954 ++sampleCount; // Count for the last sample 955 } 956 SttsTableEntry sttsEntry(sampleCount, lastDuration); 957 mSttsTableEntries.push_back(sttsEntry); 958 mReachedEOS = true; 959 LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames - %s", 960 count, nZeroLengthFrames, mSampleInfos.size(), is_audio? "audio": "video"); 961 962 logStatisticalData(is_audio); 963} 964 965void MPEG4Writer::Track::findMinMaxFrameRates(float *minFps, float *maxFps) { 966 int32_t minSampleDuration = 0x7FFFFFFF; 967 int32_t maxSampleDuration = 0; 968 for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); 969 it != mSttsTableEntries.end(); ++it) { 970 int32_t sampleDuration = static_cast<int32_t>(it->sampleDuration); 971 if (sampleDuration > maxSampleDuration) { 972 maxSampleDuration = sampleDuration; 973 } else if (sampleDuration < minSampleDuration) { 974 minSampleDuration = sampleDuration; 975 } 976 } 977 CHECK(minSampleDuration != 0 && maxSampleDuration != 0); 978 *minFps = 1000.0 / maxSampleDuration; 979 *maxFps = 1000.0 / minSampleDuration; 980} 981 982// Don't count the last duration 983void MPEG4Writer::Track::findMinMaxChunkDurations(int64_t *min, int64_t *max) { 984 int64_t duration = mOwner->interleaveDuration(); 985 int64_t minChunkDuration = duration; 986 int64_t maxChunkDuration = duration; 987 if (mChunkDurations.size() > 1) { 988 for (List<int64_t>::iterator it = mChunkDurations.begin(); 989 it != --mChunkDurations.end(); ++it) { 990 if (minChunkDuration > (*it)) { 991 minChunkDuration = (*it); 992 } else if (maxChunkDuration < (*it)) { 993 maxChunkDuration = (*it); 994 } 995 } 996 } 997 *min = minChunkDuration; 998 *max = maxChunkDuration; 999} 1000 1001void MPEG4Writer::Track::logStatisticalData(bool isAudio) { 1002 if (mMaxTimeStampUs <= 0 || mSampleInfos.empty()) { 1003 LOGI("nothing is recorded"); 1004 return; 1005 } 1006 1007 bool collectStats = false; 1008 char value[PROPERTY_VALUE_MAX]; 1009 if (property_get("media.stagefright.record-stats", value, NULL) 1010 && (!strcmp(value, "1") || !strcasecmp(value, "true"))) { 1011 collectStats = true; 1012 } 1013 1014 if (collectStats) { 1015 if (isAudio) { 1016 LOGI("audio track - duration %lld us", mMaxTimeStampUs); 1017 } else { 1018 float fps = (mSampleInfos.size() * 1000000.0) / mMaxTimeStampUs; 1019 float minFps; 1020 float maxFps; 1021 findMinMaxFrameRates(&minFps, &maxFps); 1022 LOGI("video track - duration %lld us", mMaxTimeStampUs); 1023 LOGI("min/avg/max frame rate (fps): %.2f/%.2f/%.2f", 1024 minFps, fps, maxFps); 1025 } 1026 1027 int64_t totalBytes = 0; 1028 for (List<SampleInfo>::iterator it = mSampleInfos.begin(); 1029 it != mSampleInfos.end(); ++it) { 1030 totalBytes += it->size; 1031 } 1032 float bitRate = (totalBytes * 8000000.0) / mMaxTimeStampUs; 1033 LOGI("avg bit rate (bps): %.2f", bitRate); 1034 1035 int64_t duration = mOwner->interleaveDuration(); 1036 if (duration != 0) { // If interleaving is enabled 1037 int64_t minChunk, maxChunk; 1038 findMinMaxChunkDurations(&minChunk, &maxChunk); 1039 LOGI("min/avg/max chunk duration (ms): %lld/%lld/%lld", 1040 minChunk, duration, maxChunk); 1041 } 1042 } 1043} 1044 1045void MPEG4Writer::Track::writeOneChunk(bool isAvc) { 1046 mOwner->lock(); 1047 for (List<MediaBuffer *>::iterator it = mChunkSamples.begin(); 1048 it != mChunkSamples.end(); ++it) { 1049 off_t offset = isAvc? mOwner->addLengthPrefixedSample_l(*it) 1050 : mOwner->addSample_l(*it); 1051 if (it == mChunkSamples.begin()) { 1052 mChunkOffsets.push_back(offset); 1053 } 1054 } 1055 mOwner->unlock(); 1056 while (!mChunkSamples.empty()) { 1057 List<MediaBuffer *>::iterator it = mChunkSamples.begin(); 1058 (*it)->release(); 1059 (*it) = NULL; 1060 mChunkSamples.erase(it); 1061 } 1062 mChunkSamples.clear(); 1063} 1064 1065int64_t MPEG4Writer::Track::getDurationUs() const { 1066 return mMaxTimeStampUs; 1067} 1068 1069int64_t MPEG4Writer::Track::getEstimatedTrackSizeBytes() const { 1070 return mEstimatedTrackSizeBytes; 1071} 1072 1073void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { 1074 const char *mime; 1075 bool success = mMeta->findCString(kKeyMIMEType, &mime); 1076 CHECK(success); 1077 1078 bool is_audio = !strncasecmp(mime, "audio/", 6); 1079 1080 time_t now = time(NULL); 1081 1082 mOwner->beginBox("trak"); 1083 1084 mOwner->beginBox("tkhd"); 1085 mOwner->writeInt32(0); // version=0, flags=0 1086 mOwner->writeInt32(now); // creation time 1087 mOwner->writeInt32(now); // modification time 1088 mOwner->writeInt32(trackID); 1089 mOwner->writeInt32(0); // reserved 1090 mOwner->writeInt32(getDurationUs() / 1000); 1091 mOwner->writeInt32(0); // reserved 1092 mOwner->writeInt32(0); // reserved 1093 mOwner->writeInt16(0); // layer 1094 mOwner->writeInt16(0); // alternate group 1095 mOwner->writeInt16(is_audio ? 0x100 : 0); // volume 1096 mOwner->writeInt16(0); // reserved 1097 1098 mOwner->writeInt32(0x10000); // matrix 1099 mOwner->writeInt32(0); 1100 mOwner->writeInt32(0); 1101 mOwner->writeInt32(0); 1102 mOwner->writeInt32(0x10000); 1103 mOwner->writeInt32(0); 1104 mOwner->writeInt32(0); 1105 mOwner->writeInt32(0); 1106 mOwner->writeInt32(0x40000000); 1107 1108 if (is_audio) { 1109 mOwner->writeInt32(0); 1110 mOwner->writeInt32(0); 1111 } else { 1112 int32_t width, height; 1113 bool success = mMeta->findInt32(kKeyWidth, &width); 1114 success = success && mMeta->findInt32(kKeyHeight, &height); 1115 CHECK(success); 1116 1117 mOwner->writeInt32(width << 16); // 32-bit fixed-point value 1118 mOwner->writeInt32(height << 16); // 32-bit fixed-point value 1119 } 1120 mOwner->endBox(); // tkhd 1121 1122 if (mStartTimestampUs != 0) { 1123 mOwner->beginBox("edts"); 1124 mOwner->writeInt32(0); // version=0, flags=0 1125 mOwner->beginBox("elst"); 1126 mOwner->writeInt32(0); // version=0, flags=0 1127 mOwner->writeInt32(1); // a single entry 1128 mOwner->writeInt32(mStartTimestampUs / 1000); // edit duration 1129 mOwner->writeInt32(-1); // empty edit box to signal starting time offset 1130 mOwner->writeInt32(1); // x1 rate 1131 mOwner->endBox(); 1132 mOwner->endBox(); 1133 } 1134 1135 mOwner->beginBox("mdia"); 1136 1137 mOwner->beginBox("mdhd"); 1138 mOwner->writeInt32(0); // version=0, flags=0 1139 mOwner->writeInt32(now); // creation time 1140 mOwner->writeInt32(now); // modification time 1141 mOwner->writeInt32(1000); // timescale 1142 mOwner->writeInt32(getDurationUs() / 1000); 1143 mOwner->writeInt16(0); // language code XXX 1144 mOwner->writeInt16(0); // predefined 1145 mOwner->endBox(); 1146 1147 mOwner->beginBox("hdlr"); 1148 mOwner->writeInt32(0); // version=0, flags=0 1149 mOwner->writeInt32(0); // component type: should be mhlr 1150 mOwner->writeFourcc(is_audio ? "soun" : "vide"); // component subtype 1151 mOwner->writeInt32(0); // reserved 1152 mOwner->writeInt32(0); // reserved 1153 mOwner->writeInt32(0); // reserved 1154 mOwner->writeCString(is_audio ? "SoundHandler": ""); // name 1155 mOwner->endBox(); 1156 1157 mOwner->beginBox("minf"); 1158 if (is_audio) { 1159 mOwner->beginBox("smhd"); 1160 mOwner->writeInt32(0); // version=0, flags=0 1161 mOwner->writeInt16(0); // balance 1162 mOwner->writeInt16(0); // reserved 1163 mOwner->endBox(); 1164 } else { 1165 mOwner->beginBox("vmhd"); 1166 mOwner->writeInt32(0x00000001); // version=0, flags=1 1167 mOwner->writeInt16(0); // graphics mode 1168 mOwner->writeInt16(0); // opcolor 1169 mOwner->writeInt16(0); 1170 mOwner->writeInt16(0); 1171 mOwner->endBox(); 1172 } 1173 1174 mOwner->beginBox("dinf"); 1175 mOwner->beginBox("dref"); 1176 mOwner->writeInt32(0); // version=0, flags=0 1177 mOwner->writeInt32(1); 1178 mOwner->beginBox("url "); 1179 mOwner->writeInt32(1); // version=0, flags=1 1180 mOwner->endBox(); // url 1181 mOwner->endBox(); // dref 1182 mOwner->endBox(); // dinf 1183 1184 mOwner->endBox(); // minf 1185 1186 mOwner->beginBox("stbl"); 1187 1188 mOwner->beginBox("stsd"); 1189 mOwner->writeInt32(0); // version=0, flags=0 1190 mOwner->writeInt32(1); // entry count 1191 if (is_audio) { 1192 const char *fourcc = NULL; 1193 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) { 1194 fourcc = "samr"; 1195 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) { 1196 fourcc = "sawb"; 1197 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) { 1198 fourcc = "mp4a"; 1199 } else { 1200 LOGE("Unknown mime type '%s'.", mime); 1201 CHECK(!"should not be here, unknown mime type."); 1202 } 1203 1204 mOwner->beginBox(fourcc); // audio format 1205 mOwner->writeInt32(0); // reserved 1206 mOwner->writeInt16(0); // reserved 1207 mOwner->writeInt16(0x1); // data ref index 1208 mOwner->writeInt32(0); // reserved 1209 mOwner->writeInt32(0); // reserved 1210 int32_t nChannels; 1211 CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels)); 1212 mOwner->writeInt16(nChannels); // channel count 1213 mOwner->writeInt16(16); // sample size 1214 mOwner->writeInt16(0); // predefined 1215 mOwner->writeInt16(0); // reserved 1216 1217 int32_t samplerate; 1218 bool success = mMeta->findInt32(kKeySampleRate, &samplerate); 1219 CHECK(success); 1220 1221 mOwner->writeInt32(samplerate << 16); 1222 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) { 1223 mOwner->beginBox("esds"); 1224 1225 mOwner->writeInt32(0); // version=0, flags=0 1226 mOwner->writeInt8(0x03); // ES_DescrTag 1227 mOwner->writeInt8(23 + mCodecSpecificDataSize); 1228 mOwner->writeInt16(0x0000);// ES_ID 1229 mOwner->writeInt8(0x00); 1230 1231 mOwner->writeInt8(0x04); // DecoderConfigDescrTag 1232 mOwner->writeInt8(15 + mCodecSpecificDataSize); 1233 mOwner->writeInt8(0x40); // objectTypeIndication ISO/IEC 14492-2 1234 mOwner->writeInt8(0x15); // streamType AudioStream 1235 1236 mOwner->writeInt16(0x03); // XXX 1237 mOwner->writeInt8(0x00); // buffer size 24-bit 1238 mOwner->writeInt32(96000); // max bit rate 1239 mOwner->writeInt32(96000); // avg bit rate 1240 1241 mOwner->writeInt8(0x05); // DecoderSpecificInfoTag 1242 mOwner->writeInt8(mCodecSpecificDataSize); 1243 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); 1244 1245 static const uint8_t kData2[] = { 1246 0x06, // SLConfigDescriptorTag 1247 0x01, 1248 0x02 1249 }; 1250 mOwner->write(kData2, sizeof(kData2)); 1251 1252 mOwner->endBox(); // esds 1253 } 1254 mOwner->endBox(); 1255 } else { 1256 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { 1257 mOwner->beginBox("mp4v"); 1258 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { 1259 mOwner->beginBox("s263"); 1260 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { 1261 mOwner->beginBox("avc1"); 1262 } else { 1263 LOGE("Unknown mime type '%s'.", mime); 1264 CHECK(!"should not be here, unknown mime type."); 1265 } 1266 1267 mOwner->writeInt32(0); // reserved 1268 mOwner->writeInt16(0); // reserved 1269 mOwner->writeInt16(0); // data ref index 1270 mOwner->writeInt16(0); // predefined 1271 mOwner->writeInt16(0); // reserved 1272 mOwner->writeInt32(0); // predefined 1273 mOwner->writeInt32(0); // predefined 1274 mOwner->writeInt32(0); // predefined 1275 1276 int32_t width, height; 1277 bool success = mMeta->findInt32(kKeyWidth, &width); 1278 success = success && mMeta->findInt32(kKeyHeight, &height); 1279 CHECK(success); 1280 1281 mOwner->writeInt16(width); 1282 mOwner->writeInt16(height); 1283 mOwner->writeInt32(0x480000); // horiz resolution 1284 mOwner->writeInt32(0x480000); // vert resolution 1285 mOwner->writeInt32(0); // reserved 1286 mOwner->writeInt16(1); // frame count 1287 mOwner->write(" ", 32); 1288 mOwner->writeInt16(0x18); // depth 1289 mOwner->writeInt16(-1); // predefined 1290 1291 CHECK(23 + mCodecSpecificDataSize < 128); 1292 1293 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { 1294 mOwner->beginBox("esds"); 1295 1296 mOwner->writeInt32(0); // version=0, flags=0 1297 1298 mOwner->writeInt8(0x03); // ES_DescrTag 1299 mOwner->writeInt8(23 + mCodecSpecificDataSize); 1300 mOwner->writeInt16(0x0000); // ES_ID 1301 mOwner->writeInt8(0x1f); 1302 1303 mOwner->writeInt8(0x04); // DecoderConfigDescrTag 1304 mOwner->writeInt8(15 + mCodecSpecificDataSize); 1305 mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2 1306 mOwner->writeInt8(0x11); // streamType VisualStream 1307 1308 static const uint8_t kData[] = { 1309 0x01, 0x77, 0x00, 1310 0x00, 0x03, 0xe8, 0x00, 1311 0x00, 0x03, 0xe8, 0x00 1312 }; 1313 mOwner->write(kData, sizeof(kData)); 1314 1315 mOwner->writeInt8(0x05); // DecoderSpecificInfoTag 1316 1317 mOwner->writeInt8(mCodecSpecificDataSize); 1318 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); 1319 1320 static const uint8_t kData2[] = { 1321 0x06, // SLConfigDescriptorTag 1322 0x01, 1323 0x02 1324 }; 1325 mOwner->write(kData2, sizeof(kData2)); 1326 1327 mOwner->endBox(); // esds 1328 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { 1329 mOwner->beginBox("d263"); 1330 1331 mOwner->writeInt32(0); // vendor 1332 mOwner->writeInt8(0); // decoder version 1333 mOwner->writeInt8(10); // level: 10 1334 mOwner->writeInt8(0); // profile: 0 1335 1336 mOwner->endBox(); // d263 1337 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { 1338 mOwner->beginBox("avcC"); 1339 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); 1340 mOwner->endBox(); // avcC 1341 } 1342 1343 mOwner->endBox(); // mp4v, s263 or avc1 1344 } 1345 mOwner->endBox(); // stsd 1346 1347 mOwner->beginBox("stts"); 1348 mOwner->writeInt32(0); // version=0, flags=0 1349 mOwner->writeInt32(mSttsTableEntries.size()); 1350 for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); 1351 it != mSttsTableEntries.end(); ++it) { 1352 mOwner->writeInt32(it->sampleCount); 1353 mOwner->writeInt32(it->sampleDuration); 1354 } 1355 mOwner->endBox(); // stts 1356 1357 if (!is_audio) { 1358 mOwner->beginBox("stss"); 1359 mOwner->writeInt32(0); // version=0, flags=0 1360 mOwner->writeInt32(mStssTableEntries.size()); // number of sync frames 1361 for (List<int32_t>::iterator it = mStssTableEntries.begin(); 1362 it != mStssTableEntries.end(); ++it) { 1363 mOwner->writeInt32(*it); 1364 } 1365 mOwner->endBox(); // stss 1366 } 1367 1368 mOwner->beginBox("stsz"); 1369 mOwner->writeInt32(0); // version=0, flags=0 1370 if (mSamplesHaveSameSize) { 1371 List<SampleInfo>::iterator it = mSampleInfos.begin(); 1372 mOwner->writeInt32(it->size); // default sample size 1373 } else { 1374 mOwner->writeInt32(0); 1375 } 1376 mOwner->writeInt32(mSampleInfos.size()); 1377 if (!mSamplesHaveSameSize) { 1378 for (List<SampleInfo>::iterator it = mSampleInfos.begin(); 1379 it != mSampleInfos.end(); ++it) { 1380 mOwner->writeInt32((*it).size); 1381 } 1382 } 1383 mOwner->endBox(); // stsz 1384 1385 mOwner->beginBox("stsc"); 1386 mOwner->writeInt32(0); // version=0, flags=0 1387 mOwner->writeInt32(mStscTableEntries.size()); 1388 for (List<StscTableEntry>::iterator it = mStscTableEntries.begin(); 1389 it != mStscTableEntries.end(); ++it) { 1390 mOwner->writeInt32(it->firstChunk); 1391 mOwner->writeInt32(it->samplesPerChunk); 1392 mOwner->writeInt32(it->sampleDescriptionId); 1393 } 1394 mOwner->endBox(); // stsc 1395 1396 mOwner->beginBox("co64"); 1397 mOwner->writeInt32(0); // version=0, flags=0 1398 mOwner->writeInt32(mChunkOffsets.size()); 1399 for (List<off_t>::iterator it = mChunkOffsets.begin(); 1400 it != mChunkOffsets.end(); ++it) { 1401 mOwner->writeInt64((*it)); 1402 } 1403 mOwner->endBox(); // co64 1404 1405 mOwner->endBox(); // stbl 1406 mOwner->endBox(); // mdia 1407 mOwner->endBox(); // trak 1408} 1409 1410} // namespace android 1411