MPEG4Writer.cpp revision 18291bc20e55e8f3fd5feb786771a8ed32c19c59
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <arpa/inet.h> 18 19#include <ctype.h> 20#include <pthread.h> 21 22#include <media/stagefright/MPEG4Writer.h> 23#include <media/stagefright/MediaBuffer.h> 24#include <media/stagefright/MetaData.h> 25#include <media/stagefright/MediaDebug.h> 26#include <media/stagefright/MediaDefs.h> 27#include <media/stagefright/MediaSource.h> 28#include <media/stagefright/Utils.h> 29 30namespace android { 31 32class MPEG4Writer::Track { 33public: 34 Track(MPEG4Writer *owner, const sp<MediaSource> &source); 35 ~Track(); 36 37 status_t start(); 38 void stop(); 39 bool reachedEOS(); 40 41 int64_t getDuration() const; 42 void writeTrackHeader(int32_t trackID); 43 44private: 45 MPEG4Writer *mOwner; 46 sp<MetaData> mMeta; 47 sp<MediaSource> mSource; 48 volatile bool mDone; 49 50 pthread_t mThread; 51 52 struct SampleInfo { 53 size_t size; 54 off_t offset; 55 int64_t timestamp; 56 }; 57 List<SampleInfo> mSampleInfos; 58 59 void *mCodecSpecificData; 60 size_t mCodecSpecificDataSize; 61 62 bool mReachedEOS; 63 64 static void *ThreadWrapper(void *me); 65 void threadEntry(); 66 67 Track(const Track &); 68 Track &operator=(const Track &); 69}; 70 71MPEG4Writer::MPEG4Writer(const char *filename) 72 : mFile(fopen(filename, "wb")), 73 mOffset(0), 74 mMdatOffset(0) { 75 CHECK(mFile != NULL); 76} 77 78MPEG4Writer::~MPEG4Writer() { 79 stop(); 80 81 for (List<Track *>::iterator it = mTracks.begin(); 82 it != mTracks.end(); ++it) { 83 delete *it; 84 } 85 mTracks.clear(); 86} 87 88void MPEG4Writer::addSource(const sp<MediaSource> &source) { 89 Track *track = new Track(this, source); 90 mTracks.push_back(track); 91} 92 93status_t MPEG4Writer::start() { 94 if (mFile == NULL) { 95 return UNKNOWN_ERROR; 96 } 97 98 beginBox("ftyp"); 99 writeFourcc("isom"); 100 writeInt32(0); 101 writeFourcc("isom"); 102 endBox(); 103 104 mMdatOffset = mOffset; 105 write("\x00\x00\x00\x01mdat????????", 16); 106 107 for (List<Track *>::iterator it = mTracks.begin(); 108 it != mTracks.end(); ++it) { 109 status_t err = (*it)->start(); 110 111 if (err != OK) { 112 for (List<Track *>::iterator it2 = mTracks.begin(); 113 it2 != it; ++it2) { 114 (*it2)->stop(); 115 } 116 117 return err; 118 } 119 } 120 121 return OK; 122} 123 124void MPEG4Writer::stop() { 125 if (mFile == NULL) { 126 return; 127 } 128 129 int64_t max_duration = 0; 130 for (List<Track *>::iterator it = mTracks.begin(); 131 it != mTracks.end(); ++it) { 132 (*it)->stop(); 133 134 int64_t duration = (*it)->getDuration(); 135 if (duration > max_duration) { 136 max_duration = duration; 137 } 138 } 139 140 // Fix up the size of the 'mdat' chunk. 141 fseek(mFile, mMdatOffset + 8, SEEK_SET); 142 int64_t size = mOffset - mMdatOffset; 143 size = hton64(size); 144 fwrite(&size, 1, 8, mFile); 145 fseek(mFile, mOffset, SEEK_SET); 146 147 time_t now = time(NULL); 148 149 beginBox("moov"); 150 151 beginBox("mvhd"); 152 writeInt32(0); // version=0, flags=0 153 writeInt32(now); // creation time 154 writeInt32(now); // modification time 155 writeInt32(1000); // timescale 156 writeInt32(max_duration); 157 writeInt32(0x10000); // rate 158 writeInt16(0x100); // volume 159 writeInt16(0); // reserved 160 writeInt32(0); // reserved 161 writeInt32(0); // reserved 162 writeInt32(0x10000); // matrix 163 writeInt32(0); 164 writeInt32(0); 165 writeInt32(0); 166 writeInt32(0x10000); 167 writeInt32(0); 168 writeInt32(0); 169 writeInt32(0); 170 writeInt32(0x40000000); 171 writeInt32(0); // predefined 172 writeInt32(0); // predefined 173 writeInt32(0); // predefined 174 writeInt32(0); // predefined 175 writeInt32(0); // predefined 176 writeInt32(0); // predefined 177 writeInt32(mTracks.size() + 1); // nextTrackID 178 endBox(); // mvhd 179 180 int32_t id = 1; 181 for (List<Track *>::iterator it = mTracks.begin(); 182 it != mTracks.end(); ++it, ++id) { 183 (*it)->writeTrackHeader(id); 184 } 185 endBox(); // moov 186 187 CHECK(mBoxes.empty()); 188 189 fclose(mFile); 190 mFile = NULL; 191} 192 193off_t MPEG4Writer::addSample(MediaBuffer *buffer) { 194 Mutex::Autolock autoLock(mLock); 195 196 off_t old_offset = mOffset; 197 198 fwrite((const uint8_t *)buffer->data() + buffer->range_offset(), 199 1, buffer->range_length(), mFile); 200 201 mOffset += buffer->range_length(); 202 203 return old_offset; 204} 205 206void MPEG4Writer::beginBox(const char *fourcc) { 207 CHECK_EQ(strlen(fourcc), 4); 208 209 mBoxes.push_back(mOffset); 210 211 writeInt32(0); 212 writeFourcc(fourcc); 213} 214 215void MPEG4Writer::endBox() { 216 CHECK(!mBoxes.empty()); 217 218 off_t offset = *--mBoxes.end(); 219 mBoxes.erase(--mBoxes.end()); 220 221 fseek(mFile, offset, SEEK_SET); 222 writeInt32(mOffset - offset); 223 mOffset -= 4; 224 fseek(mFile, mOffset, SEEK_SET); 225} 226 227void MPEG4Writer::writeInt8(int8_t x) { 228 fwrite(&x, 1, 1, mFile); 229 ++mOffset; 230} 231 232void MPEG4Writer::writeInt16(int16_t x) { 233 x = htons(x); 234 fwrite(&x, 1, 2, mFile); 235 mOffset += 2; 236} 237 238void MPEG4Writer::writeInt32(int32_t x) { 239 x = htonl(x); 240 fwrite(&x, 1, 4, mFile); 241 mOffset += 4; 242} 243 244void MPEG4Writer::writeInt64(int64_t x) { 245 x = hton64(x); 246 fwrite(&x, 1, 8, mFile); 247 mOffset += 8; 248} 249 250void MPEG4Writer::writeCString(const char *s) { 251 size_t n = strlen(s); 252 253 fwrite(s, 1, n + 1, mFile); 254 mOffset += n + 1; 255} 256 257void MPEG4Writer::writeFourcc(const char *s) { 258 CHECK_EQ(strlen(s), 4); 259 fwrite(s, 1, 4, mFile); 260 mOffset += 4; 261} 262 263void MPEG4Writer::write(const void *data, size_t size) { 264 fwrite(data, 1, size, mFile); 265 mOffset += size; 266} 267 268bool MPEG4Writer::reachedEOS() { 269 bool allDone = true; 270 for (List<Track *>::iterator it = mTracks.begin(); 271 it != mTracks.end(); ++it) { 272 if (!(*it)->reachedEOS()) { 273 allDone = false; 274 break; 275 } 276 } 277 278 return allDone; 279} 280 281//////////////////////////////////////////////////////////////////////////////// 282 283MPEG4Writer::Track::Track( 284 MPEG4Writer *owner, const sp<MediaSource> &source) 285 : mOwner(owner), 286 mMeta(source->getFormat()), 287 mSource(source), 288 mDone(false), 289 mCodecSpecificData(NULL), 290 mCodecSpecificDataSize(0), 291 mReachedEOS(false) { 292} 293 294MPEG4Writer::Track::~Track() { 295 stop(); 296 297 if (mCodecSpecificData != NULL) { 298 free(mCodecSpecificData); 299 mCodecSpecificData = NULL; 300 } 301} 302 303status_t MPEG4Writer::Track::start() { 304 status_t err = mSource->start(); 305 306 if (err != OK) { 307 mDone = mReachedEOS = true; 308 return err; 309 } 310 311 pthread_attr_t attr; 312 pthread_attr_init(&attr); 313 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 314 315 mDone = false; 316 mReachedEOS = false; 317 318 pthread_create(&mThread, &attr, ThreadWrapper, this); 319 pthread_attr_destroy(&attr); 320 321 return OK; 322} 323 324void MPEG4Writer::Track::stop() { 325 if (mDone) { 326 return; 327 } 328 329 mDone = true; 330 331 void *dummy; 332 pthread_join(mThread, &dummy); 333 334 mSource->stop(); 335} 336 337bool MPEG4Writer::Track::reachedEOS() { 338 return mReachedEOS; 339} 340 341// static 342void *MPEG4Writer::Track::ThreadWrapper(void *me) { 343 Track *track = static_cast<Track *>(me); 344 345 track->threadEntry(); 346 347 return NULL; 348} 349 350void MPEG4Writer::Track::threadEntry() { 351 bool is_mpeg4 = false; 352 sp<MetaData> meta = mSource->getFormat(); 353 const char *mime; 354 meta->findCString(kKeyMIMEType, &mime); 355 is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4); 356 357 MediaBuffer *buffer; 358 while (!mDone && mSource->read(&buffer) == OK) { 359 if (buffer->range_length() == 0) { 360 buffer->release(); 361 buffer = NULL; 362 363 continue; 364 } 365 366 if (mCodecSpecificData == NULL && is_mpeg4) { 367 const uint8_t *data = 368 (const uint8_t *)buffer->data() + buffer->range_offset(); 369 370 const size_t size = buffer->range_length(); 371 372 size_t offset = 0; 373 while (offset + 3 < size) { 374 if (data[offset] == 0x00 && data[offset + 1] == 0x00 375 && data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) { 376 break; 377 } 378 379 ++offset; 380 } 381 382 // CHECK(offset + 3 < size); 383 if (offset + 3 >= size) { 384 // XXX assume the entire first chunk of data is the codec specific 385 // data. 386 offset = size; 387 } 388 389 mCodecSpecificDataSize = offset; 390 mCodecSpecificData = malloc(offset); 391 memcpy(mCodecSpecificData, data, offset); 392 393 buffer->set_range(buffer->range_offset() + offset, size - offset); 394 } 395 396 off_t offset = mOwner->addSample(buffer); 397 398 SampleInfo info; 399 info.size = buffer->range_length(); 400 info.offset = offset; 401 402 int32_t units, scale; 403 bool success = 404 buffer->meta_data()->findInt32(kKeyTimeUnits, &units); 405 CHECK(success); 406 success = 407 buffer->meta_data()->findInt32(kKeyTimeScale, &scale); 408 CHECK(success); 409 410 info.timestamp = (int64_t)units * 1000 / scale; 411 412 mSampleInfos.push_back(info); 413 414 buffer->release(); 415 buffer = NULL; 416 } 417 418 mReachedEOS = true; 419} 420 421int64_t MPEG4Writer::Track::getDuration() const { 422 return 10000; // XXX 423} 424 425void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) { 426 const char *mime; 427 bool success = mMeta->findCString(kKeyMIMEType, &mime); 428 CHECK(success); 429 430 bool is_audio = !strncasecmp(mime, "audio/", 6); 431 432 time_t now = time(NULL); 433 434 mOwner->beginBox("trak"); 435 436 mOwner->beginBox("tkhd"); 437 mOwner->writeInt32(0); // version=0, flags=0 438 mOwner->writeInt32(now); // creation time 439 mOwner->writeInt32(now); // modification time 440 mOwner->writeInt32(trackID); 441 mOwner->writeInt32(0); // reserved 442 mOwner->writeInt32(getDuration()); 443 mOwner->writeInt32(0); // reserved 444 mOwner->writeInt32(0); // reserved 445 mOwner->writeInt16(0); // layer 446 mOwner->writeInt16(0); // alternate group 447 mOwner->writeInt16(is_audio ? 0x100 : 0); // volume 448 mOwner->writeInt16(0); // reserved 449 450 mOwner->writeInt32(0x10000); // matrix 451 mOwner->writeInt32(0); 452 mOwner->writeInt32(0); 453 mOwner->writeInt32(0); 454 mOwner->writeInt32(0x10000); 455 mOwner->writeInt32(0); 456 mOwner->writeInt32(0); 457 mOwner->writeInt32(0); 458 mOwner->writeInt32(0x40000000); 459 460 if (is_audio) { 461 mOwner->writeInt32(0); 462 mOwner->writeInt32(0); 463 } else { 464 int32_t width, height; 465 bool success = mMeta->findInt32(kKeyWidth, &width); 466 success = success && mMeta->findInt32(kKeyHeight, &height); 467 CHECK(success); 468 469 mOwner->writeInt32(width); 470 mOwner->writeInt32(height); 471 } 472 mOwner->endBox(); // tkhd 473 474 mOwner->beginBox("mdia"); 475 476 mOwner->beginBox("mdhd"); 477 mOwner->writeInt32(0); // version=0, flags=0 478 mOwner->writeInt32(now); // creation time 479 mOwner->writeInt32(now); // modification time 480 mOwner->writeInt32(1000); // timescale 481 mOwner->writeInt32(getDuration()); 482 mOwner->writeInt16(0); // language code XXX 483 mOwner->writeInt16(0); // predefined 484 mOwner->endBox(); 485 486 mOwner->beginBox("hdlr"); 487 mOwner->writeInt32(0); // version=0, flags=0 488 mOwner->writeInt32(0); // predefined 489 mOwner->writeFourcc(is_audio ? "soun" : "vide"); 490 mOwner->writeInt32(0); // reserved 491 mOwner->writeInt32(0); // reserved 492 mOwner->writeInt32(0); // reserved 493 mOwner->writeCString(""); // name 494 mOwner->endBox(); 495 496 mOwner->beginBox("minf"); 497 498 mOwner->beginBox("dinf"); 499 mOwner->beginBox("dref"); 500 mOwner->writeInt32(0); // version=0, flags=0 501 mOwner->writeInt32(1); 502 mOwner->beginBox("url "); 503 mOwner->writeInt32(1); // version=0, flags=1 504 mOwner->endBox(); // url 505 mOwner->endBox(); // dref 506 mOwner->endBox(); // dinf 507 508 if (is_audio) { 509 mOwner->beginBox("smhd"); 510 mOwner->writeInt32(0); // version=0, flags=0 511 mOwner->writeInt16(0); // balance 512 mOwner->writeInt16(0); // reserved 513 mOwner->endBox(); 514 } else { 515 mOwner->beginBox("vmhd"); 516 mOwner->writeInt32(0x00000001); // version=0, flags=1 517 mOwner->writeInt16(0); // graphics mode 518 mOwner->writeInt16(0); // opcolor 519 mOwner->writeInt16(0); 520 mOwner->writeInt16(0); 521 mOwner->endBox(); 522 } 523 mOwner->endBox(); // minf 524 525 mOwner->beginBox("stbl"); 526 527 mOwner->beginBox("stsd"); 528 mOwner->writeInt32(0); // version=0, flags=0 529 mOwner->writeInt32(1); // entry count 530 if (is_audio) { 531 const char *fourcc = NULL; 532 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) { 533 fourcc = "samr"; 534 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) { 535 fourcc = "sawb"; 536 } else { 537 LOGE("Unknown mime type '%s'.", mime); 538 CHECK(!"should not be here, unknown mime type."); 539 } 540 541 mOwner->beginBox(fourcc); // audio format 542 mOwner->writeInt32(0); // reserved 543 mOwner->writeInt16(0); // reserved 544 mOwner->writeInt16(0); // data ref index 545 mOwner->writeInt32(0); // reserved 546 mOwner->writeInt32(0); // reserved 547 mOwner->writeInt16(2); // channel count 548 mOwner->writeInt16(16); // sample size 549 mOwner->writeInt16(0); // predefined 550 mOwner->writeInt16(0); // reserved 551 552 int32_t samplerate; 553 bool success = mMeta->findInt32(kKeySampleRate, &samplerate); 554 CHECK(success); 555 556 mOwner->writeInt32(samplerate << 16); 557 mOwner->endBox(); 558 } else { 559 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { 560 mOwner->beginBox("mp4v"); 561 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { 562 mOwner->beginBox("s263"); 563 } else { 564 LOGE("Unknown mime type '%s'.", mime); 565 CHECK(!"should not be here, unknown mime type."); 566 } 567 568 mOwner->writeInt32(0); // reserved 569 mOwner->writeInt16(0); // reserved 570 mOwner->writeInt16(0); // data ref index 571 mOwner->writeInt16(0); // predefined 572 mOwner->writeInt16(0); // reserved 573 mOwner->writeInt32(0); // predefined 574 mOwner->writeInt32(0); // predefined 575 mOwner->writeInt32(0); // predefined 576 577 int32_t width, height; 578 bool success = mMeta->findInt32(kKeyWidth, &width); 579 success = success && mMeta->findInt32(kKeyHeight, &height); 580 CHECK(success); 581 582 mOwner->writeInt16(width); 583 mOwner->writeInt16(height); 584 mOwner->writeInt32(0x480000); // horiz resolution 585 mOwner->writeInt32(0x480000); // vert resolution 586 mOwner->writeInt32(0); // reserved 587 mOwner->writeInt16(1); // frame count 588 mOwner->write(" ", 32); 589 mOwner->writeInt16(0x18); // depth 590 mOwner->writeInt16(-1); // predefined 591 592 CHECK(23 + mCodecSpecificDataSize < 128); 593 594 if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { 595 mOwner->beginBox("esds"); 596 597 mOwner->writeInt32(0); // version=0, flags=0 598 599 mOwner->writeInt8(0x03); // ES_DescrTag 600 mOwner->writeInt8(23 + mCodecSpecificDataSize); 601 mOwner->writeInt16(0x0000); // ES_ID 602 mOwner->writeInt8(0x1f); 603 604 mOwner->writeInt8(0x04); // DecoderConfigDescrTag 605 mOwner->writeInt8(15 + mCodecSpecificDataSize); 606 mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2 607 mOwner->writeInt8(0x11); // streamType VisualStream 608 609 static const uint8_t kData[] = { 610 0x01, 0x77, 0x00, 611 0x00, 0x03, 0xe8, 0x00, 612 0x00, 0x03, 0xe8, 0x00 613 }; 614 mOwner->write(kData, sizeof(kData)); 615 616 mOwner->writeInt8(0x05); // DecoderSpecificInfoTag 617 618 mOwner->writeInt8(mCodecSpecificDataSize); 619 mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); 620 621 static const uint8_t kData2[] = { 622 0x06, // SLConfigDescriptorTag 623 0x01, 624 0x02 625 }; 626 mOwner->write(kData2, sizeof(kData2)); 627 628 mOwner->endBox(); // esds 629 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { 630 mOwner->beginBox("d263"); 631 632 mOwner->writeInt32(0); // vendor 633 mOwner->writeInt8(0); // decoder version 634 mOwner->writeInt8(10); // level: 10 635 mOwner->writeInt8(0); // profile: 0 636 637 mOwner->endBox(); // d263 638 } 639 mOwner->endBox(); // mp4v or s263 640 } 641 mOwner->endBox(); // stsd 642 643 mOwner->beginBox("stts"); 644 mOwner->writeInt32(0); // version=0, flags=0 645 mOwner->writeInt32(mSampleInfos.size() - 1); 646 647 List<SampleInfo>::iterator it = mSampleInfos.begin(); 648 int64_t last = (*it).timestamp; 649 ++it; 650 while (it != mSampleInfos.end()) { 651 mOwner->writeInt32(1); 652 mOwner->writeInt32((*it).timestamp - last); 653 654 last = (*it).timestamp; 655 656 ++it; 657 } 658 mOwner->endBox(); // stts 659 660 mOwner->beginBox("stsz"); 661 mOwner->writeInt32(0); // version=0, flags=0 662 mOwner->writeInt32(0); // default sample size 663 mOwner->writeInt32(mSampleInfos.size()); 664 for (List<SampleInfo>::iterator it = mSampleInfos.begin(); 665 it != mSampleInfos.end(); ++it) { 666 mOwner->writeInt32((*it).size); 667 } 668 mOwner->endBox(); // stsz 669 670 mOwner->beginBox("stsc"); 671 mOwner->writeInt32(0); // version=0, flags=0 672 mOwner->writeInt32(mSampleInfos.size()); 673 int32_t n = 1; 674 for (List<SampleInfo>::iterator it = mSampleInfos.begin(); 675 it != mSampleInfos.end(); ++it, ++n) { 676 mOwner->writeInt32(n); 677 mOwner->writeInt32(1); 678 mOwner->writeInt32(1); 679 } 680 mOwner->endBox(); // stsc 681 682 mOwner->beginBox("co64"); 683 mOwner->writeInt32(0); // version=0, flags=0 684 mOwner->writeInt32(mSampleInfos.size()); 685 for (List<SampleInfo>::iterator it = mSampleInfos.begin(); 686 it != mSampleInfos.end(); ++it, ++n) { 687 mOwner->writeInt64((*it).offset); 688 } 689 mOwner->endBox(); // co64 690 691 mOwner->endBox(); // stbl 692 mOwner->endBox(); // mdia 693 mOwner->endBox(); // trak 694} 695 696} // namespace android 697