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