MPEG4Writer.cpp revision 58ae9c530247668f8af36e30d228c716c226b3d4
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 562size_t MPEG4Writer::numTracks() { 563 Mutex::Autolock autolock(mLock); 564 return mTracks.size(); 565} 566 567//////////////////////////////////////////////////////////////////////////////// 568 569MPEG4Writer::Track::Track( 570 MPEG4Writer *owner, const sp<MediaSource> &source) 571 : mOwner(owner), 572 mMeta(source->getFormat()), 573 mSource(source), 574 mDone(false), 575 mPaused(false), 576 mResumed(false), 577 mMaxTimeStampUs(0), 578 mEstimatedTrackSizeBytes(0), 579 mSamplesHaveSameSize(true), 580 mCodecSpecificData(NULL), 581 mCodecSpecificDataSize(0), 582 mGotAllCodecSpecificData(false), 583 mReachedEOS(false) { 584} 585 586MPEG4Writer::Track::~Track() { 587 stop(); 588 589 if (mCodecSpecificData != NULL) { 590 free(mCodecSpecificData); 591 mCodecSpecificData = NULL; 592 } 593} 594 595status_t MPEG4Writer::Track::start(int64_t startTimeUs) { 596 if (!mDone && mPaused) { 597 mPaused = false; 598 mResumed = true; 599 return OK; 600 } 601 602 sp<MetaData> meta = new MetaData; 603 meta->setInt64(kKeyTime, startTimeUs); 604 status_t err = mSource->start(meta.get()); 605 if (err != OK) { 606 mDone = mReachedEOS = true; 607 return err; 608 } 609 610 pthread_attr_t attr; 611 pthread_attr_init(&attr); 612 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 613 614 mDone = false; 615 mMaxTimeStampUs = 0; 616 mReachedEOS = false; 617 mEstimatedTrackSizeBytes = 0; 618 619 pthread_create(&mThread, &attr, ThreadWrapper, this); 620 pthread_attr_destroy(&attr); 621 622 return OK; 623} 624 625void MPEG4Writer::Track::pause() { 626 mPaused = true; 627} 628 629void MPEG4Writer::Track::stop() { 630 if (mDone) { 631 return; 632 } 633 634 mDone = true; 635 636 void *dummy; 637 pthread_join(mThread, &dummy); 638 639 mSource->stop(); 640} 641 642bool MPEG4Writer::Track::reachedEOS() { 643 return mReachedEOS; 644} 645 646// static 647void *MPEG4Writer::Track::ThreadWrapper(void *me) { 648 Track *track = static_cast<Track *>(me); 649 650 track->threadEntry(); 651 652 return NULL; 653} 654 655#include <ctype.h> 656static void hexdump(const void *_data, size_t size) { 657 const uint8_t *data = (const uint8_t *)_data; 658 size_t offset = 0; 659 while (offset < size) { 660 printf("0x%04x ", offset); 661 662 size_t n = size - offset; 663 if (n > 16) { 664 n = 16; 665 } 666 667 for (size_t i = 0; i < 16; ++i) { 668 if (i == 8) { 669 printf(" "); 670 } 671 672 if (offset + i < size) { 673 printf("%02x ", data[offset + i]); 674 } else { 675 printf(" "); 676 } 677 } 678 679 printf(" "); 680 681 for (size_t i = 0; i < n; ++i) { 682 if (isprint(data[offset + i])) { 683 printf("%c", data[offset + i]); 684 } else { 685 printf("."); 686 } 687 } 688 689 printf("\n"); 690 691 offset += 16; 692 } 693} 694 695 696status_t MPEG4Writer::Track::makeAVCCodecSpecificData( 697 const uint8_t *data, size_t size) { 698 // hexdump(data, size); 699 700 if (mCodecSpecificData != NULL) { 701 LOGE("Already have codec specific data"); 702 return ERROR_MALFORMED; 703 } 704 705 if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) { 706 LOGE("Must start with a start code"); 707 return ERROR_MALFORMED; 708 } 709 710 size_t picParamOffset = 4; 711 while (picParamOffset + 3 < size 712 && memcmp("\x00\x00\x00\x01", &data[picParamOffset], 4)) { 713 ++picParamOffset; 714 } 715 716 if (picParamOffset + 3 >= size) { 717 LOGE("Could not find start-code for pictureParameterSet"); 718 return ERROR_MALFORMED; 719 } 720 721 size_t seqParamSetLength = picParamOffset - 4; 722 size_t picParamSetLength = size - picParamOffset - 4; 723 724 mCodecSpecificDataSize = 725 6 + 1 + seqParamSetLength + 2 + picParamSetLength + 2; 726 727 mCodecSpecificData = malloc(mCodecSpecificDataSize); 728 uint8_t *header = (uint8_t *)mCodecSpecificData; 729 header[0] = 1; 730 header[1] = 0x42; // profile 731 header[2] = 0x80; 732 header[3] = 0x1e; // level 733 734#if USE_NALLEN_FOUR 735 header[4] = 0xfc | 3; // length size == 4 bytes 736#else 737 header[4] = 0xfc | 1; // length size == 2 bytes 738#endif 739 740 header[5] = 0xe0 | 1; 741 header[6] = seqParamSetLength >> 8; 742 header[7] = seqParamSetLength & 0xff; 743 memcpy(&header[8], &data[4], seqParamSetLength); 744 header += 8 + seqParamSetLength; 745 header[0] = 1; 746 header[1] = picParamSetLength >> 8; 747 header[2] = picParamSetLength & 0xff; 748 memcpy(&header[3], &data[picParamOffset + 4], picParamSetLength); 749 750 return OK; 751} 752 753void MPEG4Writer::Track::threadEntry() { 754 sp<MetaData> meta = mSource->getFormat(); 755 const char *mime; 756 meta->findCString(kKeyMIMEType, &mime); 757 bool is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || 758 !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC); 759 bool is_avc = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); 760 bool is_audio = !strncasecmp(mime, "audio/", 6); 761 int32_t count = 0; 762 const int64_t interleaveDurationUs = mOwner->interleaveDuration(); 763 int64_t chunkTimestampUs = 0; 764 int32_t nChunks = 0; 765 int32_t nZeroLengthFrames = 0; 766 int64_t lastTimestamp = 0; // Timestamp of the previous sample 767 int64_t lastDuration = 0; // Time spacing between the previous two samples 768 int32_t sampleCount = 1; // Sample count in the current stts table entry 769 uint32_t previousSampleSize = 0; // Size of the previous sample 770 int64_t previousPausedDurationUs = 0; 771 sp<MetaData> meta_data; 772 773 MediaBuffer *buffer; 774 while (!mDone && mSource->read(&buffer) == OK) { 775 if (buffer->range_length() == 0) { 776 buffer->release(); 777 buffer = NULL; 778 ++nZeroLengthFrames; 779 continue; 780 } 781 782 // If the codec specific data has not been received yet, delay pause. 783 // After the codec specific data is received, discard what we received 784 // when the track is to be paused. 785 if (mPaused && !mResumed) { 786 buffer->release(); 787 buffer = NULL; 788 continue; 789 } 790 791 ++count; 792 793 int32_t isCodecConfig; 794 if (buffer->meta_data()->findInt32(kKeyIsCodecConfig, &isCodecConfig) 795 && isCodecConfig) { 796 CHECK(!mGotAllCodecSpecificData); 797 798 if (is_avc) { 799 status_t err = makeAVCCodecSpecificData( 800 (const uint8_t *)buffer->data() 801 + buffer->range_offset(), 802 buffer->range_length()); 803 CHECK_EQ(OK, err); 804 } else if (is_mpeg4) { 805 mCodecSpecificDataSize = buffer->range_length(); 806 mCodecSpecificData = malloc(mCodecSpecificDataSize); 807 memcpy(mCodecSpecificData, 808 (const uint8_t *)buffer->data() 809 + buffer->range_offset(), 810 buffer->range_length()); 811 } 812 813 buffer->release(); 814 buffer = NULL; 815 816 mGotAllCodecSpecificData = true; 817 continue; 818 } else if (!mGotAllCodecSpecificData && 819 count == 1 && is_mpeg4 && mCodecSpecificData == NULL) { 820 // The TI mpeg4 encoder does not properly set the 821 // codec-specific-data flag. 822 823 const uint8_t *data = 824 (const uint8_t *)buffer->data() + buffer->range_offset(); 825 826 const size_t size = buffer->range_length(); 827 828 size_t offset = 0; 829 while (offset + 3 < size) { 830 if (data[offset] == 0x00 && data[offset + 1] == 0x00 831 && data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) { 832 break; 833 } 834 835 ++offset; 836 } 837 838 // CHECK(offset + 3 < size); 839 if (offset + 3 >= size) { 840 // XXX assume the entire first chunk of data is the codec specific 841 // data. 842 offset = size; 843 } 844 845 mCodecSpecificDataSize = offset; 846 mCodecSpecificData = malloc(offset); 847 memcpy(mCodecSpecificData, data, offset); 848 849 buffer->set_range(buffer->range_offset() + offset, size - offset); 850 851 if (size == offset) { 852 buffer->release(); 853 buffer = NULL; 854 855 continue; 856 } 857 858 mGotAllCodecSpecificData = true; 859 } else if (!mGotAllCodecSpecificData && is_avc && count < 3) { 860 // The TI video encoder does not flag codec specific data 861 // as such and also splits up SPS and PPS across two buffers. 862 863 const uint8_t *data = 864 (const uint8_t *)buffer->data() + buffer->range_offset(); 865 866 size_t size = buffer->range_length(); 867 868 CHECK(count == 2 || mCodecSpecificData == NULL); 869 870 size_t offset = mCodecSpecificDataSize; 871 mCodecSpecificDataSize += size + 4; 872 mCodecSpecificData = 873 realloc(mCodecSpecificData, mCodecSpecificDataSize); 874 875 memcpy((uint8_t *)mCodecSpecificData + offset, 876 "\x00\x00\x00\x01", 4); 877 878 memcpy((uint8_t *)mCodecSpecificData + offset + 4, data, size); 879 880 buffer->release(); 881 buffer = NULL; 882 883 if (count == 2) { 884 void *tmp = mCodecSpecificData; 885 size = mCodecSpecificDataSize; 886 mCodecSpecificData = NULL; 887 mCodecSpecificDataSize = 0; 888 889 status_t err = makeAVCCodecSpecificData( 890 (const uint8_t *)tmp, size); 891 free(tmp); 892 tmp = NULL; 893 CHECK_EQ(OK, err); 894 895 mGotAllCodecSpecificData = true; 896 } 897 898 continue; 899 } 900 901 if (!mGotAllCodecSpecificData) { 902 mGotAllCodecSpecificData = true; 903 } 904 905 // Make a deep copy of the MediaBuffer and Metadata and release 906 // the original as soon as we can 907 MediaBuffer *copy = new MediaBuffer(buffer->range_length()); 908 memcpy(copy->data(), (uint8_t *)buffer->data() + buffer->range_offset(), 909 buffer->range_length()); 910 copy->set_range(0, buffer->range_length()); 911 meta_data = new MetaData(*buffer->meta_data().get()); 912 buffer->release(); 913 buffer = NULL; 914 915 if (is_avc) StripStartcode(copy); 916 917 SampleInfo info; 918 info.size = is_avc 919#if USE_NALLEN_FOUR 920 ? copy->range_length() + 4 921#else 922 ? copy->range_length() + 2 923#endif 924 : copy->range_length(); 925 926 // Max file size or duration handling 927 mEstimatedTrackSizeBytes += info.size; 928 if (mOwner->exceedsFileSizeLimit()) { 929 mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0); 930 break; 931 } 932 if (mOwner->exceedsFileDurationLimit()) { 933 mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0); 934 break; 935 } 936 937 938 int32_t isSync = false; 939 meta_data->findInt32(kKeyIsSyncFrame, &isSync); 940 941 int64_t timestampUs; 942 CHECK(meta_data->findInt64(kKeyTime, ×tampUs)); 943 944//////////////////////////////////////////////////////////////////////////////// 945 if (mSampleInfos.empty()) { 946 mStartTimestampUs = timestampUs; 947 mOwner->setStartTimestampUs(mStartTimestampUs); 948 } 949 950 if (mResumed) { 951 previousPausedDurationUs += (timestampUs - mMaxTimeStampUs - 1000 * lastDuration); 952 mResumed = false; 953 } 954 955 timestampUs -= previousPausedDurationUs; 956 LOGV("time stamp: %lld and previous paused duration %lld", 957 timestampUs, previousPausedDurationUs); 958 if (timestampUs > mMaxTimeStampUs) { 959 mMaxTimeStampUs = timestampUs; 960 } 961 962 // Our timestamp is in ms. 963 info.timestamp = (timestampUs + 500) / 1000; 964 mSampleInfos.push_back(info); 965 if (mSampleInfos.size() > 2) { 966 if (lastDuration != info.timestamp - lastTimestamp) { 967 SttsTableEntry sttsEntry(sampleCount, lastDuration); 968 mSttsTableEntries.push_back(sttsEntry); 969 sampleCount = 1; 970 } else { 971 ++sampleCount; 972 } 973 } 974 if (mSamplesHaveSameSize) { 975 if (mSampleInfos.size() >= 2 && previousSampleSize != info.size) { 976 mSamplesHaveSameSize = false; 977 } 978 previousSampleSize = info.size; 979 } 980 lastDuration = info.timestamp - lastTimestamp; 981 lastTimestamp = info.timestamp; 982 983 if (isSync != 0) { 984 mStssTableEntries.push_back(mSampleInfos.size()); 985 } 986 987 if (mOwner->numTracks() == 1) { 988 off_t offset = is_avc? mOwner->addLengthPrefixedSample_l(copy) 989 : mOwner->addSample_l(copy); 990 if (mChunkOffsets.empty()) { 991 mChunkOffsets.push_back(offset); 992 } 993 copy->release(); 994 copy = NULL; 995 continue; 996 } 997 998 mChunkSamples.push_back(copy); 999 if (interleaveDurationUs == 0) { 1000 StscTableEntry stscEntry(++nChunks, 1, 1); 1001 mStscTableEntries.push_back(stscEntry); 1002 writeOneChunk(is_avc); 1003 } else { 1004 if (chunkTimestampUs == 0) { 1005 chunkTimestampUs = timestampUs; 1006 } else { 1007 if (timestampUs - chunkTimestampUs > interleaveDurationUs) { 1008 ++nChunks; 1009 mChunkDurations.push_back(timestampUs - chunkTimestampUs); 1010 if (nChunks == 1 || // First chunk 1011 (--(mStscTableEntries.end()))->samplesPerChunk != 1012 mChunkSamples.size()) { 1013 StscTableEntry stscEntry(nChunks, 1014 mChunkSamples.size(), 1); 1015 mStscTableEntries.push_back(stscEntry); 1016 } 1017 writeOneChunk(is_avc); 1018 chunkTimestampUs = timestampUs; 1019 } 1020 } 1021 } 1022 1023 } 1024 1025 if (mSampleInfos.empty()) { 1026 mOwner->notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_STOP_PREMATURELY, 0); 1027 } 1028 1029 // Last chunk 1030 if (mOwner->numTracks() == 1) { 1031 StscTableEntry stscEntry(1, mSampleInfos.size(), 1); 1032 mStscTableEntries.push_back(stscEntry); 1033 } else if (!mChunkSamples.empty()) { 1034 ++nChunks; 1035 StscTableEntry stscEntry(nChunks, mChunkSamples.size(), 1); 1036 mStscTableEntries.push_back(stscEntry); 1037 writeOneChunk(is_avc); 1038 } 1039 1040 // We don't really know how long the last frame lasts, since 1041 // there is no frame time after it, just repeat the previous 1042 // frame's duration. 1043 if (mSampleInfos.size() == 1) { 1044 lastDuration = 0; // A single sample's duration 1045 } else { 1046 ++sampleCount; // Count for the last sample 1047 } 1048 SttsTableEntry sttsEntry(sampleCount, lastDuration); 1049 mSttsTableEntries.push_back(sttsEntry); 1050 mReachedEOS = true; 1051 LOGI("Received total/0-length (%d/%d) buffers and encoded %d frames - %s", 1052 count, nZeroLengthFrames, mSampleInfos.size(), is_audio? "audio": "video"); 1053 1054 logStatisticalData(is_audio); 1055} 1056 1057void MPEG4Writer::Track::findMinMaxFrameRates(float *minFps, float *maxFps) { 1058 int32_t minSampleDuration = 0x7FFFFFFF; 1059 int32_t maxSampleDuration = 0; 1060 for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); 1061 it != mSttsTableEntries.end(); ++it) { 1062 int32_t sampleDuration = static_cast<int32_t>(it->sampleDuration); 1063 if (sampleDuration > maxSampleDuration) { 1064 maxSampleDuration = sampleDuration; 1065 } else if (sampleDuration < minSampleDuration) { 1066 minSampleDuration = sampleDuration; 1067 } 1068 } 1069 CHECK(minSampleDuration != 0 && maxSampleDuration != 0); 1070 *minFps = 1000.0 / maxSampleDuration; 1071 *maxFps = 1000.0 / minSampleDuration; 1072} 1073 1074// Don't count the last duration 1075void MPEG4Writer::Track::findMinMaxChunkDurations(int64_t *min, int64_t *max) { 1076 int64_t duration = mOwner->interleaveDuration(); 1077 int64_t minChunkDuration = duration; 1078 int64_t maxChunkDuration = duration; 1079 if (mChunkDurations.size() > 1) { 1080 for (List<int64_t>::iterator it = mChunkDurations.begin(); 1081 it != --mChunkDurations.end(); ++it) { 1082 if (minChunkDuration > (*it)) { 1083 minChunkDuration = (*it); 1084 } else if (maxChunkDuration < (*it)) { 1085 maxChunkDuration = (*it); 1086 } 1087 } 1088 } 1089 *min = minChunkDuration; 1090 *max = maxChunkDuration; 1091} 1092 1093void MPEG4Writer::Track::logStatisticalData(bool isAudio) { 1094 if (mMaxTimeStampUs <= 0 || mSampleInfos.empty()) { 1095 LOGI("nothing is recorded"); 1096 return; 1097 } 1098 1099 bool collectStats = false; 1100 char value[PROPERTY_VALUE_MAX]; 1101 if (property_get("media.stagefright.record-stats", value, NULL) 1102 && (!strcmp(value, "1") || !strcasecmp(value, "true"))) { 1103 collectStats = true; 1104 } 1105 1106 if (collectStats) { 1107 if (isAudio) { 1108 LOGI("audio track - duration %lld us", mMaxTimeStampUs); 1109 } else { 1110 float fps = (mSampleInfos.size() * 1000000.0) / mMaxTimeStampUs; 1111 float minFps; 1112 float maxFps; 1113 findMinMaxFrameRates(&minFps, &maxFps); 1114 LOGI("video track - duration %lld us", mMaxTimeStampUs); 1115 LOGI("min/avg/max frame rate (fps): %.2f/%.2f/%.2f", 1116 minFps, fps, maxFps); 1117 } 1118 1119 int64_t totalBytes = 0; 1120 for (List<SampleInfo>::iterator it = mSampleInfos.begin(); 1121 it != mSampleInfos.end(); ++it) { 1122 totalBytes += it->size; 1123 } 1124 float bitRate = (totalBytes * 8000000.0) / mMaxTimeStampUs; 1125 LOGI("avg bit rate (bps): %.2f", bitRate); 1126 1127 int64_t duration = mOwner->interleaveDuration(); 1128 if (duration != 0) { // If interleaving is enabled 1129 int64_t minChunk, maxChunk; 1130 findMinMaxChunkDurations(&minChunk, &maxChunk); 1131 LOGI("min/avg/max chunk duration (ms): %lld/%lld/%lld", 1132 minChunk, duration, maxChunk); 1133 } 1134 } 1135} 1136 1137void MPEG4Writer::Track::writeOneChunk(bool isAvc) { 1138 mOwner->lock(); 1139 for (List<MediaBuffer *>::iterator it = mChunkSamples.begin(); 1140 it != mChunkSamples.end(); ++it) { 1141 off_t offset = isAvc? mOwner->addLengthPrefixedSample_l(*it) 1142 : mOwner->addSample_l(*it); 1143 if (it == mChunkSamples.begin()) { 1144 mChunkOffsets.push_back(offset); 1145 } 1146 } 1147 mOwner->unlock(); 1148 while (!mChunkSamples.empty()) { 1149 List<MediaBuffer *>::iterator it = mChunkSamples.begin(); 1150 (*it)->release(); 1151 (*it) = NULL; 1152 mChunkSamples.erase(it); 1153 } 1154 mChunkSamples.clear(); 1155} 1156 1157int64_t MPEG4Writer::Track::getDurationUs() const { 1158 return mMaxTimeStampUs; 1159} 1160 1161int64_t MPEG4Writer::Track::getEstimatedTrackSizeBytes() const { 1162 return mEstimatedTrackSizeBytes; 1163} 1164 1165void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { 1166 const char *mime; 1167 bool success = mMeta->findCString(kKeyMIMEType, &mime); 1168 CHECK(success); 1169 1170 bool is_audio = !strncasecmp(mime, "audio/", 6); 1171 1172 time_t now = time(NULL); 1173 1174 mOwner->beginBox("trak"); 1175 1176 mOwner->beginBox("tkhd"); 1177 mOwner->writeInt32(0); // version=0, flags=0 1178 mOwner->writeInt32(now); // creation time 1179 mOwner->writeInt32(now); // modification time 1180 mOwner->writeInt32(trackID); 1181 mOwner->writeInt32(0); // reserved 1182 mOwner->writeInt32(getDurationUs() / 1000); 1183 mOwner->writeInt32(0); // reserved 1184 mOwner->writeInt32(0); // reserved 1185 mOwner->writeInt16(0); // layer 1186 mOwner->writeInt16(0); // alternate group 1187 mOwner->writeInt16(is_audio ? 0x100 : 0); // volume 1188 mOwner->writeInt16(0); // reserved 1189 1190 mOwner->writeInt32(0x10000); // matrix 1191 mOwner->writeInt32(0); 1192 mOwner->writeInt32(0); 1193 mOwner->writeInt32(0); 1194 mOwner->writeInt32(0x10000); 1195 mOwner->writeInt32(0); 1196 mOwner->writeInt32(0); 1197 mOwner->writeInt32(0); 1198 mOwner->writeInt32(0x40000000); 1199 1200 if (is_audio) { 1201 mOwner->writeInt32(0); 1202 mOwner->writeInt32(0); 1203 } else { 1204 int32_t width, height; 1205 bool success = mMeta->findInt32(kKeyWidth, &width); 1206 success = success && mMeta->findInt32(kKeyHeight, &height); 1207 CHECK(success); 1208 1209 mOwner->writeInt32(width << 16); // 32-bit fixed-point value 1210 mOwner->writeInt32(height << 16); // 32-bit fixed-point value 1211 } 1212 mOwner->endBox(); // tkhd 1213 1214 int64_t moovStartTimeUs = mOwner->getStartTimestampUs(); 1215 if (mStartTimestampUs != moovStartTimeUs) { 1216 mOwner->beginBox("edts"); 1217 mOwner->writeInt32(0); // version=0, flags=0 1218 mOwner->beginBox("elst"); 1219 mOwner->writeInt32(0); // version=0, flags=0 1220 mOwner->writeInt32(1); // a single entry 1221 int64_t durationMs = 1222 (mStartTimestampUs - moovStartTimeUs) / 1000; 1223 mOwner->writeInt32(durationMs); // edit duration 1224 mOwner->writeInt32(-1); // empty edit box to signal starting time offset 1225 mOwner->writeInt32(1); // x1 rate 1226 mOwner->endBox(); 1227 mOwner->endBox(); 1228 } 1229 1230 mOwner->beginBox("mdia"); 1231 1232 mOwner->beginBox("mdhd"); 1233 mOwner->writeInt32(0); // version=0, flags=0 1234 mOwner->writeInt32(now); // creation time 1235 mOwner->writeInt32(now); // modification time 1236 mOwner->writeInt32(1000); // timescale 1237 mOwner->writeInt32(getDurationUs() / 1000); 1238 mOwner->writeInt16(0); // language code XXX 1239 mOwner->writeInt16(0); // predefined 1240 mOwner->endBox(); 1241 1242 mOwner->beginBox("hdlr"); 1243 mOwner->writeInt32(0); // version=0, flags=0 1244 mOwner->writeInt32(0); // component type: should be mhlr 1245 mOwner->writeFourcc(is_audio ? "soun" : "vide"); // component subtype 1246 mOwner->writeInt32(0); // reserved 1247 mOwner->writeInt32(0); // reserved 1248 mOwner->writeInt32(0); // reserved 1249 mOwner->writeCString(is_audio ? "SoundHandler": ""); // name 1250 mOwner->endBox(); 1251 1252 mOwner->beginBox("minf"); 1253 if (is_audio) { 1254 mOwner->beginBox("smhd"); 1255 mOwner->writeInt32(0); // version=0, flags=0 1256 mOwner->writeInt16(0); // balance 1257 mOwner->writeInt16(0); // reserved 1258 mOwner->endBox(); 1259 } else { 1260 mOwner->beginBox("vmhd"); 1261 mOwner->writeInt32(0x00000001); // version=0, flags=1 1262 mOwner->writeInt16(0); // graphics mode 1263 mOwner->writeInt16(0); // opcolor 1264 mOwner->writeInt16(0); 1265 mOwner->writeInt16(0); 1266 mOwner->endBox(); 1267 } 1268 1269 mOwner->beginBox("dinf"); 1270 mOwner->beginBox("dref"); 1271 mOwner->writeInt32(0); // version=0, flags=0 1272 mOwner->writeInt32(1); 1273 mOwner->beginBox("url "); 1274 mOwner->writeInt32(1); // version=0, flags=1 1275 mOwner->endBox(); // url 1276 mOwner->endBox(); // dref 1277 mOwner->endBox(); // dinf 1278 1279 mOwner->endBox(); // minf 1280 1281 mOwner->beginBox("stbl"); 1282 1283 mOwner->beginBox("stsd"); 1284 mOwner->writeInt32(0); // version=0, flags=0 1285 mOwner->writeInt32(1); // entry count 1286 if (is_audio) { 1287 const char *fourcc = NULL; 1288 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) { 1289 fourcc = "samr"; 1290 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) { 1291 fourcc = "sawb"; 1292 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) { 1293 fourcc = "mp4a"; 1294 } else { 1295 LOGE("Unknown mime type '%s'.", mime); 1296 CHECK(!"should not be here, unknown mime type."); 1297 } 1298 1299 mOwner->beginBox(fourcc); // audio format 1300 mOwner->writeInt32(0); // reserved 1301 mOwner->writeInt16(0); // reserved 1302 mOwner->writeInt16(0x1); // data ref index 1303 mOwner->writeInt32(0); // reserved 1304 mOwner->writeInt32(0); // reserved 1305 int32_t nChannels; 1306 CHECK_EQ(true, mMeta->findInt32(kKeyChannelCount, &nChannels)); 1307 mOwner->writeInt16(nChannels); // channel count 1308 mOwner->writeInt16(16); // sample size 1309 mOwner->writeInt16(0); // predefined 1310 mOwner->writeInt16(0); // reserved 1311 1312 int32_t samplerate; 1313 bool success = mMeta->findInt32(kKeySampleRate, &samplerate); 1314 CHECK(success); 1315 1316 mOwner->writeInt32(samplerate << 16); 1317 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) { 1318 mOwner->beginBox("esds"); 1319 1320 mOwner->writeInt32(0); // version=0, flags=0 1321 mOwner->writeInt8(0x03); // ES_DescrTag 1322 mOwner->writeInt8(23 + mCodecSpecificDataSize); 1323 mOwner->writeInt16(0x0000);// ES_ID 1324 mOwner->writeInt8(0x00); 1325 1326 mOwner->writeInt8(0x04); // DecoderConfigDescrTag 1327 mOwner->writeInt8(15 + mCodecSpecificDataSize); 1328 mOwner->writeInt8(0x40); // objectTypeIndication ISO/IEC 14492-2 1329 mOwner->writeInt8(0x15); // streamType AudioStream 1330 1331 mOwner->writeInt16(0x03); // XXX 1332 mOwner->writeInt8(0x00); // buffer size 24-bit 1333 mOwner->writeInt32(96000); // max bit rate 1334 mOwner->writeInt32(96000); // avg bit rate 1335 1336 mOwner->writeInt8(0x05); // DecoderSpecificInfoTag 1337 mOwner->writeInt8(mCodecSpecificDataSize); 1338 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); 1339 1340 static const uint8_t kData2[] = { 1341 0x06, // SLConfigDescriptorTag 1342 0x01, 1343 0x02 1344 }; 1345 mOwner->write(kData2, sizeof(kData2)); 1346 1347 mOwner->endBox(); // esds 1348 } 1349 mOwner->endBox(); 1350 } else { 1351 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { 1352 mOwner->beginBox("mp4v"); 1353 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { 1354 mOwner->beginBox("s263"); 1355 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { 1356 mOwner->beginBox("avc1"); 1357 } else { 1358 LOGE("Unknown mime type '%s'.", mime); 1359 CHECK(!"should not be here, unknown mime type."); 1360 } 1361 1362 mOwner->writeInt32(0); // reserved 1363 mOwner->writeInt16(0); // reserved 1364 mOwner->writeInt16(0); // data ref index 1365 mOwner->writeInt16(0); // predefined 1366 mOwner->writeInt16(0); // reserved 1367 mOwner->writeInt32(0); // predefined 1368 mOwner->writeInt32(0); // predefined 1369 mOwner->writeInt32(0); // predefined 1370 1371 int32_t width, height; 1372 bool success = mMeta->findInt32(kKeyWidth, &width); 1373 success = success && mMeta->findInt32(kKeyHeight, &height); 1374 CHECK(success); 1375 1376 mOwner->writeInt16(width); 1377 mOwner->writeInt16(height); 1378 mOwner->writeInt32(0x480000); // horiz resolution 1379 mOwner->writeInt32(0x480000); // vert resolution 1380 mOwner->writeInt32(0); // reserved 1381 mOwner->writeInt16(1); // frame count 1382 mOwner->write(" ", 32); 1383 mOwner->writeInt16(0x18); // depth 1384 mOwner->writeInt16(-1); // predefined 1385 1386 CHECK(23 + mCodecSpecificDataSize < 128); 1387 1388 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { 1389 mOwner->beginBox("esds"); 1390 1391 mOwner->writeInt32(0); // version=0, flags=0 1392 1393 mOwner->writeInt8(0x03); // ES_DescrTag 1394 mOwner->writeInt8(23 + mCodecSpecificDataSize); 1395 mOwner->writeInt16(0x0000); // ES_ID 1396 mOwner->writeInt8(0x1f); 1397 1398 mOwner->writeInt8(0x04); // DecoderConfigDescrTag 1399 mOwner->writeInt8(15 + mCodecSpecificDataSize); 1400 mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2 1401 mOwner->writeInt8(0x11); // streamType VisualStream 1402 1403 static const uint8_t kData[] = { 1404 0x01, 0x77, 0x00, 1405 0x00, 0x03, 0xe8, 0x00, 1406 0x00, 0x03, 0xe8, 0x00 1407 }; 1408 mOwner->write(kData, sizeof(kData)); 1409 1410 mOwner->writeInt8(0x05); // DecoderSpecificInfoTag 1411 1412 mOwner->writeInt8(mCodecSpecificDataSize); 1413 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); 1414 1415 static const uint8_t kData2[] = { 1416 0x06, // SLConfigDescriptorTag 1417 0x01, 1418 0x02 1419 }; 1420 mOwner->write(kData2, sizeof(kData2)); 1421 1422 mOwner->endBox(); // esds 1423 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { 1424 mOwner->beginBox("d263"); 1425 1426 mOwner->writeInt32(0); // vendor 1427 mOwner->writeInt8(0); // decoder version 1428 mOwner->writeInt8(10); // level: 10 1429 mOwner->writeInt8(0); // profile: 0 1430 1431 mOwner->endBox(); // d263 1432 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { 1433 mOwner->beginBox("avcC"); 1434 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); 1435 mOwner->endBox(); // avcC 1436 } 1437 1438 mOwner->endBox(); // mp4v, s263 or avc1 1439 } 1440 mOwner->endBox(); // stsd 1441 1442 mOwner->beginBox("stts"); 1443 mOwner->writeInt32(0); // version=0, flags=0 1444 mOwner->writeInt32(mSttsTableEntries.size()); 1445 for (List<SttsTableEntry>::iterator it = mSttsTableEntries.begin(); 1446 it != mSttsTableEntries.end(); ++it) { 1447 mOwner->writeInt32(it->sampleCount); 1448 mOwner->writeInt32(it->sampleDuration); 1449 } 1450 mOwner->endBox(); // stts 1451 1452 if (!is_audio) { 1453 mOwner->beginBox("stss"); 1454 mOwner->writeInt32(0); // version=0, flags=0 1455 mOwner->writeInt32(mStssTableEntries.size()); // number of sync frames 1456 for (List<int32_t>::iterator it = mStssTableEntries.begin(); 1457 it != mStssTableEntries.end(); ++it) { 1458 mOwner->writeInt32(*it); 1459 } 1460 mOwner->endBox(); // stss 1461 } 1462 1463 mOwner->beginBox("stsz"); 1464 mOwner->writeInt32(0); // version=0, flags=0 1465 if (mSamplesHaveSameSize) { 1466 List<SampleInfo>::iterator it = mSampleInfos.begin(); 1467 mOwner->writeInt32(it->size); // default sample size 1468 } else { 1469 mOwner->writeInt32(0); 1470 } 1471 mOwner->writeInt32(mSampleInfos.size()); 1472 if (!mSamplesHaveSameSize) { 1473 for (List<SampleInfo>::iterator it = mSampleInfos.begin(); 1474 it != mSampleInfos.end(); ++it) { 1475 mOwner->writeInt32((*it).size); 1476 } 1477 } 1478 mOwner->endBox(); // stsz 1479 1480 mOwner->beginBox("stsc"); 1481 mOwner->writeInt32(0); // version=0, flags=0 1482 mOwner->writeInt32(mStscTableEntries.size()); 1483 for (List<StscTableEntry>::iterator it = mStscTableEntries.begin(); 1484 it != mStscTableEntries.end(); ++it) { 1485 mOwner->writeInt32(it->firstChunk); 1486 mOwner->writeInt32(it->samplesPerChunk); 1487 mOwner->writeInt32(it->sampleDescriptionId); 1488 } 1489 mOwner->endBox(); // stsc 1490 1491 mOwner->beginBox("co64"); 1492 mOwner->writeInt32(0); // version=0, flags=0 1493 mOwner->writeInt32(mChunkOffsets.size()); 1494 for (List<off_t>::iterator it = mChunkOffsets.begin(); 1495 it != mChunkOffsets.end(); ++it) { 1496 mOwner->writeInt64((*it)); 1497 } 1498 mOwner->endBox(); // co64 1499 1500 mOwner->endBox(); // stbl 1501 mOwner->endBox(); // mdia 1502 mOwner->endBox(); // trak 1503} 1504 1505} // namespace android 1506