/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "MatroskaExtractor" #include #include "MatroskaExtractor.h" #include "mkvparser.hpp" #include #include #include #include #include #include #include #include #include #include namespace android { struct DataSourceReader : public mkvparser::IMkvReader { DataSourceReader(const sp &source) : mSource(source) { } virtual int Read(long long position, long length, unsigned char* buffer) { CHECK(position >= 0); CHECK(length >= 0); if (length == 0) { return 0; } ssize_t n = mSource->readAt(position, buffer, length); if (n <= 0) { return -1; } return 0; } virtual int Length(long long* total, long long* available) { off64_t size; if (mSource->getSize(&size) != OK) { *total = -1; *available = (long long)((1ull << 63) - 1); return 0; } if (total) { *total = size; } if (available) { *available = size; } return 0; } private: sp mSource; DataSourceReader(const DataSourceReader &); DataSourceReader &operator=(const DataSourceReader &); }; //////////////////////////////////////////////////////////////////////////////// struct BlockIterator { BlockIterator(MatroskaExtractor *extractor, unsigned long trackNum); bool eos() const; void advance(); void reset(); void seek(int64_t seekTimeUs, bool seekToKeyFrame); const mkvparser::Block *block() const; int64_t blockTimeUs() const; private: MatroskaExtractor *mExtractor; unsigned long mTrackNum; const mkvparser::Cluster *mCluster; const mkvparser::BlockEntry *mBlockEntry; long mBlockEntryIndex; void advance_l(); BlockIterator(const BlockIterator &); BlockIterator &operator=(const BlockIterator &); }; struct MatroskaSource : public MediaSource { MatroskaSource( const sp &extractor, size_t index); virtual status_t start(MetaData *params); virtual status_t stop(); virtual sp getFormat(); virtual status_t read( MediaBuffer **buffer, const ReadOptions *options); protected: virtual ~MatroskaSource(); private: enum Type { AVC, AAC, OTHER }; sp mExtractor; size_t mTrackIndex; Type mType; bool mIsAudio; BlockIterator mBlockIter; size_t mNALSizeLen; // for type AVC List mPendingFrames; status_t advance(); status_t readBlock(); void clearPendingFrames(); MatroskaSource(const MatroskaSource &); MatroskaSource &operator=(const MatroskaSource &); }; MatroskaSource::MatroskaSource( const sp &extractor, size_t index) : mExtractor(extractor), mTrackIndex(index), mType(OTHER), mIsAudio(false), mBlockIter(mExtractor.get(), mExtractor->mTracks.itemAt(index).mTrackNum), mNALSizeLen(0) { sp meta = mExtractor->mTracks.itemAt(index).mMeta; const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); mIsAudio = !strncasecmp("audio/", mime, 6); if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { mType = AVC; uint32_t dummy; const uint8_t *avcc; size_t avccSize; CHECK(meta->findData( kKeyAVCC, &dummy, (const void **)&avcc, &avccSize)); CHECK_GE(avccSize, 5u); mNALSizeLen = 1 + (avcc[4] & 3); ALOGV("mNALSizeLen = %d", mNALSizeLen); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { mType = AAC; } } MatroskaSource::~MatroskaSource() { clearPendingFrames(); } status_t MatroskaSource::start(MetaData *params) { mBlockIter.reset(); return OK; } status_t MatroskaSource::stop() { clearPendingFrames(); return OK; } sp MatroskaSource::getFormat() { return mExtractor->mTracks.itemAt(mTrackIndex).mMeta; } //////////////////////////////////////////////////////////////////////////////// BlockIterator::BlockIterator( MatroskaExtractor *extractor, unsigned long trackNum) : mExtractor(extractor), mTrackNum(trackNum), mCluster(NULL), mBlockEntry(NULL), mBlockEntryIndex(0) { reset(); } bool BlockIterator::eos() const { return mCluster == NULL || mCluster->EOS(); } void BlockIterator::advance() { Mutex::Autolock autoLock(mExtractor->mLock); advance_l(); } void BlockIterator::advance_l() { for (;;) { long res = mCluster->GetEntry(mBlockEntryIndex, mBlockEntry); ALOGV("GetEntry returned %ld", res); long long pos; long len; if (res < 0) { // Need to parse this cluster some more CHECK_EQ(res, mkvparser::E_BUFFER_NOT_FULL); res = mCluster->Parse(pos, len); ALOGV("Parse returned %ld", res); if (res < 0) { // I/O error LOGE("Cluster::Parse returned result %ld", res); mCluster = NULL; break; } continue; } else if (res == 0) { // We're done with this cluster const mkvparser::Cluster *nextCluster; res = mExtractor->mSegment->ParseNext( mCluster, nextCluster, pos, len); ALOGV("ParseNext returned %ld", res); if (res > 0) { // EOF mCluster = NULL; break; } CHECK_EQ(res, 0); CHECK(nextCluster != NULL); CHECK(!nextCluster->EOS()); mCluster = nextCluster; res = mCluster->Parse(pos, len); ALOGV("Parse (2) returned %ld", res); CHECK_GE(res, 0); mBlockEntryIndex = 0; continue; } CHECK(mBlockEntry != NULL); CHECK(mBlockEntry->GetBlock() != NULL); ++mBlockEntryIndex; if (mBlockEntry->GetBlock()->GetTrackNumber() == mTrackNum) { break; } } } void BlockIterator::reset() { Mutex::Autolock autoLock(mExtractor->mLock); mCluster = mExtractor->mSegment->GetFirst(); mBlockEntry = NULL; mBlockEntryIndex = 0; do { advance_l(); } while (!eos() && block()->GetTrackNumber() != mTrackNum); } void BlockIterator::seek(int64_t seekTimeUs, bool seekToKeyFrame) { Mutex::Autolock autoLock(mExtractor->mLock); mCluster = mExtractor->mSegment->FindCluster(seekTimeUs * 1000ll); mBlockEntry = NULL; mBlockEntryIndex = 0; do { advance_l(); } while (!eos() && block()->GetTrackNumber() != mTrackNum); if (seekToKeyFrame) { while (!eos() && !mBlockEntry->GetBlock()->IsKey()) { advance_l(); } } } const mkvparser::Block *BlockIterator::block() const { CHECK(!eos()); return mBlockEntry->GetBlock(); } int64_t BlockIterator::blockTimeUs() const { return (mBlockEntry->GetBlock()->GetTime(mCluster) + 500ll) / 1000ll; } //////////////////////////////////////////////////////////////////////////////// static unsigned U24_AT(const uint8_t *ptr) { return ptr[0] << 16 | ptr[1] << 8 | ptr[2]; } static size_t clz(uint8_t x) { size_t numLeadingZeroes = 0; while (!(x & 0x80)) { ++numLeadingZeroes; x = x << 1; } return numLeadingZeroes; } void MatroskaSource::clearPendingFrames() { while (!mPendingFrames.empty()) { MediaBuffer *frame = *mPendingFrames.begin(); mPendingFrames.erase(mPendingFrames.begin()); frame->release(); frame = NULL; } } status_t MatroskaSource::readBlock() { CHECK(mPendingFrames.empty()); if (mBlockIter.eos()) { return ERROR_END_OF_STREAM; } const mkvparser::Block *block = mBlockIter.block(); int64_t timeUs = mBlockIter.blockTimeUs(); for (int i = 0; i < block->GetFrameCount(); ++i) { const mkvparser::Block::Frame &frame = block->GetFrame(i); MediaBuffer *mbuf = new MediaBuffer(frame.len); mbuf->meta_data()->setInt64(kKeyTime, timeUs); mbuf->meta_data()->setInt32(kKeyIsSyncFrame, block->IsKey()); long n = frame.Read(mExtractor->mReader, (unsigned char *)mbuf->data()); if (n != 0) { mPendingFrames.clear(); mBlockIter.advance(); return ERROR_IO; } mPendingFrames.push_back(mbuf); } mBlockIter.advance(); return OK; } status_t MatroskaSource::read( MediaBuffer **out, const ReadOptions *options) { *out = NULL; int64_t seekTimeUs; ReadOptions::SeekMode mode; if (options && options->getSeekTo(&seekTimeUs, &mode) && !mExtractor->isLiveStreaming()) { clearPendingFrames(); // Apparently keyframe indication in audio tracks is unreliable, // fortunately in all our currently supported audio encodings every // frame is effectively a keyframe. mBlockIter.seek(seekTimeUs, !mIsAudio); } again: while (mPendingFrames.empty()) { status_t err = readBlock(); if (err != OK) { clearPendingFrames(); return err; } } MediaBuffer *frame = *mPendingFrames.begin(); mPendingFrames.erase(mPendingFrames.begin()); if (mType != AVC) { *out = frame; return OK; } // Each input frame contains one or more NAL fragments, each fragment // is prefixed by mNALSizeLen bytes giving the fragment length, // followed by a corresponding number of bytes containing the fragment. // We output all these fragments into a single large buffer separated // by startcodes (0x00 0x00 0x00 0x01). const uint8_t *srcPtr = (const uint8_t *)frame->data() + frame->range_offset(); size_t srcSize = frame->range_length(); size_t dstSize = 0; MediaBuffer *buffer = NULL; uint8_t *dstPtr = NULL; for (int32_t pass = 0; pass < 2; ++pass) { size_t srcOffset = 0; size_t dstOffset = 0; while (srcOffset + mNALSizeLen <= srcSize) { size_t NALsize; switch (mNALSizeLen) { case 1: NALsize = srcPtr[srcOffset]; break; case 2: NALsize = U16_AT(srcPtr + srcOffset); break; case 3: NALsize = U24_AT(srcPtr + srcOffset); break; case 4: NALsize = U32_AT(srcPtr + srcOffset); break; default: TRESPASS(); } if (srcOffset + mNALSizeLen + NALsize > srcSize) { break; } if (pass == 1) { memcpy(&dstPtr[dstOffset], "\x00\x00\x00\x01", 4); memcpy(&dstPtr[dstOffset + 4], &srcPtr[srcOffset + mNALSizeLen], NALsize); } dstOffset += 4; // 0x00 00 00 01 dstOffset += NALsize; srcOffset += mNALSizeLen + NALsize; } if (srcOffset < srcSize) { // There were trailing bytes or not enough data to complete // a fragment. frame->release(); frame = NULL; return ERROR_MALFORMED; } if (pass == 0) { dstSize = dstOffset; buffer = new MediaBuffer(dstSize); int64_t timeUs; CHECK(frame->meta_data()->findInt64(kKeyTime, &timeUs)); int32_t isSync; CHECK(frame->meta_data()->findInt32(kKeyIsSyncFrame, &isSync)); buffer->meta_data()->setInt64(kKeyTime, timeUs); buffer->meta_data()->setInt32(kKeyIsSyncFrame, isSync); dstPtr = (uint8_t *)buffer->data(); } } frame->release(); frame = NULL; *out = buffer; return OK; } //////////////////////////////////////////////////////////////////////////////// MatroskaExtractor::MatroskaExtractor(const sp &source) : mDataSource(source), mReader(new DataSourceReader(mDataSource)), mSegment(NULL), mExtractedThumbnails(false), mIsWebm(false) { off64_t size; mIsLiveStreaming = (mDataSource->flags() & (DataSource::kWantsPrefetching | DataSource::kIsCachingDataSource)) && mDataSource->getSize(&size) != OK; mkvparser::EBMLHeader ebmlHeader; long long pos; if (ebmlHeader.Parse(mReader, pos) < 0) { return; } if (ebmlHeader.m_docType && !strcmp("webm", ebmlHeader.m_docType)) { mIsWebm = true; } long long ret = mkvparser::Segment::CreateInstance(mReader, pos, mSegment); if (ret) { CHECK(mSegment == NULL); return; } if (isLiveStreaming()) { ret = mSegment->ParseHeaders(); CHECK_EQ(ret, 0); long len; ret = mSegment->LoadCluster(pos, len); CHECK_EQ(ret, 0); } else { ret = mSegment->Load(); } if (ret < 0) { delete mSegment; mSegment = NULL; return; } #if 0 const mkvparser::SegmentInfo *info = mSegment->GetInfo(); LOGI("muxing app: %s, writing app: %s", info->GetMuxingAppAsUTF8(), info->GetWritingAppAsUTF8()); #endif addTracks(); } MatroskaExtractor::~MatroskaExtractor() { delete mSegment; mSegment = NULL; delete mReader; mReader = NULL; } size_t MatroskaExtractor::countTracks() { return mTracks.size(); } sp MatroskaExtractor::getTrack(size_t index) { if (index >= mTracks.size()) { return NULL; } return new MatroskaSource(this, index); } sp MatroskaExtractor::getTrackMetaData( size_t index, uint32_t flags) { if (index >= mTracks.size()) { return NULL; } if ((flags & kIncludeExtensiveMetaData) && !mExtractedThumbnails && !isLiveStreaming()) { findThumbnails(); mExtractedThumbnails = true; } return mTracks.itemAt(index).mMeta; } bool MatroskaExtractor::isLiveStreaming() const { return mIsLiveStreaming; } static void addESDSFromAudioSpecificInfo( const sp &meta, const void *asi, size_t asiSize) { static const uint8_t kStaticESDS[] = { 0x03, 22, 0x00, 0x00, // ES_ID 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag 0x04, 17, 0x40, // Audio ISO/IEC 14496-3 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, // AudioSpecificInfo (with size prefix) follows }; // Make sure all sizes can be coded in a single byte. CHECK(asiSize + 22 - 2 < 128); size_t esdsSize = sizeof(kStaticESDS) + asiSize + 1; uint8_t *esds = new uint8_t[esdsSize]; memcpy(esds, kStaticESDS, sizeof(kStaticESDS)); uint8_t *ptr = esds + sizeof(kStaticESDS); *ptr++ = asiSize; memcpy(ptr, asi, asiSize); // Increment by codecPrivateSize less 2 bytes that are accounted for // already in lengths of 22/17 esds[1] += asiSize - 2; esds[6] += asiSize - 2; meta->setData(kKeyESDS, 0, esds, esdsSize); delete[] esds; esds = NULL; } void addVorbisCodecInfo( const sp &meta, const void *_codecPrivate, size_t codecPrivateSize) { // printf("vorbis private data follows:\n"); // hexdump(_codecPrivate, codecPrivateSize); CHECK(codecPrivateSize >= 3); const uint8_t *codecPrivate = (const uint8_t *)_codecPrivate; CHECK(codecPrivate[0] == 0x02); size_t len1 = codecPrivate[1]; size_t len2 = codecPrivate[2]; CHECK(codecPrivateSize > 3 + len1 + len2); CHECK(codecPrivate[3] == 0x01); meta->setData(kKeyVorbisInfo, 0, &codecPrivate[3], len1); CHECK(codecPrivate[len1 + 3] == 0x03); CHECK(codecPrivate[len1 + len2 + 3] == 0x05); meta->setData( kKeyVorbisBooks, 0, &codecPrivate[len1 + len2 + 3], codecPrivateSize - len1 - len2 - 3); } void MatroskaExtractor::addTracks() { const mkvparser::Tracks *tracks = mSegment->GetTracks(); for (size_t index = 0; index < tracks->GetTracksCount(); ++index) { const mkvparser::Track *track = tracks->GetTrackByIndex(index); if (track == NULL) { // Apparently this is currently valid (if unexpected) behaviour // of the mkv parser lib. continue; } const char *const codecID = track->GetCodecId(); ALOGV("codec id = %s", codecID); ALOGV("codec name = %s", track->GetCodecNameAsUTF8()); size_t codecPrivateSize; const unsigned char *codecPrivate = track->GetCodecPrivate(codecPrivateSize); enum { VIDEO_TRACK = 1, AUDIO_TRACK = 2 }; sp meta = new MetaData; switch (track->GetType()) { case VIDEO_TRACK: { const mkvparser::VideoTrack *vtrack = static_cast(track); if (!strcmp("V_MPEG4/ISO/AVC", codecID)) { meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); meta->setData(kKeyAVCC, 0, codecPrivate, codecPrivateSize); } else if (!strcmp("V_VP8", codecID)) { meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_VPX); } else { continue; } meta->setInt32(kKeyWidth, vtrack->GetWidth()); meta->setInt32(kKeyHeight, vtrack->GetHeight()); break; } case AUDIO_TRACK: { const mkvparser::AudioTrack *atrack = static_cast(track); if (!strcmp("A_AAC", codecID)) { meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); CHECK(codecPrivateSize >= 2); addESDSFromAudioSpecificInfo( meta, codecPrivate, codecPrivateSize); } else if (!strcmp("A_VORBIS", codecID)) { meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_VORBIS); addVorbisCodecInfo(meta, codecPrivate, codecPrivateSize); } else { continue; } meta->setInt32(kKeySampleRate, atrack->GetSamplingRate()); meta->setInt32(kKeyChannelCount, atrack->GetChannels()); break; } default: continue; } long long durationNs = mSegment->GetDuration(); meta->setInt64(kKeyDuration, (durationNs + 500) / 1000); mTracks.push(); TrackInfo *trackInfo = &mTracks.editItemAt(mTracks.size() - 1); trackInfo->mTrackNum = track->GetNumber(); trackInfo->mMeta = meta; } } void MatroskaExtractor::findThumbnails() { for (size_t i = 0; i < mTracks.size(); ++i) { TrackInfo *info = &mTracks.editItemAt(i); const char *mime; CHECK(info->mMeta->findCString(kKeyMIMEType, &mime)); if (strncasecmp(mime, "video/", 6)) { continue; } BlockIterator iter(this, info->mTrackNum); int32_t i = 0; int64_t thumbnailTimeUs = 0; size_t maxBlockSize = 0; while (!iter.eos() && i < 20) { if (iter.block()->IsKey()) { ++i; size_t blockSize = 0; for (int i = 0; i < iter.block()->GetFrameCount(); ++i) { blockSize += iter.block()->GetFrame(i).len; } if (blockSize > maxBlockSize) { maxBlockSize = blockSize; thumbnailTimeUs = iter.blockTimeUs(); } } iter.advance(); } info->mMeta->setInt64(kKeyThumbnailTime, thumbnailTimeUs); } } sp MatroskaExtractor::getMetaData() { sp meta = new MetaData; meta->setCString( kKeyMIMEType, mIsWebm ? "video/webm" : MEDIA_MIMETYPE_CONTAINER_MATROSKA); return meta; } uint32_t MatroskaExtractor::flags() const { uint32_t x = CAN_PAUSE; if (!isLiveStreaming()) { x |= CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK; } return x; } bool SniffMatroska( const sp &source, String8 *mimeType, float *confidence, sp *) { DataSourceReader reader(source); mkvparser::EBMLHeader ebmlHeader; long long pos; if (ebmlHeader.Parse(&reader, pos) < 0) { return false; } mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MATROSKA); *confidence = 0.6; return true; } } // namespace android