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