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