status_t MyVorbisExtractor::seekToTime(int64_t timeUs) { if (mTableOfContents.isEmpty()) { // Perform approximate seeking based on avg. bitrate. off64_t pos = timeUs * approxBitrate() / 8000000ll; LOGV("seeking to offset %lld", pos); return seekToOffset(pos); } size_t left = 0; size_t right = mTableOfContents.size(); while (left < right) { size_t center = left / 2 + right / 2 + (left & right & 1); const TOCEntry &entry = mTableOfContents.itemAt(center); if (timeUs < entry.mTimeUs) { right = center; } else if (timeUs > entry.mTimeUs) { left = center + 1; } else { left = right = center; break; } } const TOCEntry &entry = mTableOfContents.itemAt(left); LOGV("seeking to entry %d / %d at offset %lld", left, mTableOfContents.size(), entry.mPageOffset); return seekToOffset(entry.mPageOffset); }
status_t MyVorbisExtractor::verifyHeader( MediaBuffer *buffer, uint8_t type) { const uint8_t *data = (const uint8_t *)buffer->data() + buffer->range_offset(); size_t size = buffer->range_length(); if (size < 7 || data[0] != type || memcmp(&data[1], "vorbis", 6)) { return ERROR_MALFORMED; } ogg_buffer buf; buf.data = (uint8_t *)data; buf.size = size; buf.refcount = 1; buf.ptr.owner = NULL; ogg_reference ref; ref.buffer = &buf; ref.begin = 0; ref.length = size; ref.next = NULL; oggpack_buffer bits; oggpack_readinit(&bits, &ref); CHECK_EQ(oggpack_read(&bits, 8), type); for (size_t i = 0; i < 6; ++i) { oggpack_read(&bits, 8); // skip 'vorbis' } switch (type) { case 1: { CHECK_EQ(0, _vorbis_unpack_info(&mVi, &bits)); mMeta->setData(kKeyVorbisInfo, 0, data, size); mMeta->setInt32(kKeySampleRate, mVi.rate); mMeta->setInt32(kKeyChannelCount, mVi.channels); LOGV("lower-bitrate = %ld", mVi.bitrate_lower); LOGV("upper-bitrate = %ld", mVi.bitrate_upper); LOGV("nominal-bitrate = %ld", mVi.bitrate_nominal); LOGV("window-bitrate = %ld", mVi.bitrate_window); off64_t size; if (mSource->getSize(&size) == OK) { uint64_t bps = approxBitrate(); if(0 != bps && bps < 10000000) { mMeta->setInt64(kKeyDuration, size * 8000000ll / bps); } else { uint64_t lastGranule, tDuration; lastGranule= findLastGranule(); tDuration = lastGranule*1000000/mVi.rate; mMeta->setInt64(kKeyDuration, tDuration); bps = size * 8000000ll / tDuration; mMeta->setInt64(kKeyBitRate, size * 8000000ll / tDuration); mVi.bitrate_nominal = bps; } } break; } case 3: { if (0 != _vorbis_unpack_comment(&mVc, &bits)) { return ERROR_MALFORMED; } parseFileMetaData(); break; } case 5: { if (0 != _vorbis_unpack_books(&mVi, &bits)) { return ERROR_MALFORMED; } mMeta->setData(kKeyVorbisBooks, 0, data, size); break; } } return OK; }
status_t MyVorbisExtractor::seekToTime(int64_t timeUs) { #ifdef MTK_AOSP_ENHANCEMENT if(timeUs == 0) return seekToOffset(0); if (isCachingDataSource()) { off64_t pos = timeUs * approxBitrate() / 8000000ll; SXLOGV("seeking to offset %lld", pos); return seekToOffset(pos); } if (mTableOfContents.isEmpty() || (mTocDone == false)) { // Perform seekto accurate page. uint64_t ts1,ts2; if(findGranulePositionofPage(mFirstDataOffset,&ts1) != OK) return seekToOffset(0); if(findGranulePositionofPage(mFileSize,&ts2) != OK) return seekToOffset(0); SXLOGD("bitrate seek--pos:%lld,ts1:%lld,ts2:%lld",timeUs * mVi.rate /1000000,ts1,ts2); off64_t pos = findAccuratePageOffset((uint64_t)(timeUs * mVi.rate /1000000),mFirstDataOffset,mFileSize); #else if (mTableOfContents.isEmpty()) { // Perform approximate seeking based on avg. bitrate. off64_t pos = timeUs * approxBitrate() / 8000000ll; #endif ALOGV("seeking to offset %lld", pos); return seekToOffset(pos); } size_t left = 0; size_t right_plus_one = mTableOfContents.size(); while (left < right_plus_one) { size_t center = left + (right_plus_one - left) / 2; const TOCEntry &entry = mTableOfContents.itemAt(center); if (timeUs < entry.mTimeUs) { right_plus_one = center; } else if (timeUs > entry.mTimeUs) { left = center + 1; } else { left = center; break; } } if (left == mTableOfContents.size()) { --left; } #ifndef MTK_AOSP_ENHANCEMENT const TOCEntry &entry = mTableOfContents.itemAt(left); ALOGV("seeking to entry %zu / %zu at offset %lld", left, mTableOfContents.size(), entry.mPageOffset); return seekToOffset(entry.mPageOffset); #else if(mTableOfContents.itemAt(mTableOfContents.size()-1).mTimeUs <= timeUs) return seekToOffset(mTableOfContents.itemAt(mTableOfContents.size()-1).mPageOffset); off64_t os = 0,oe = 0; for(size_t i = left ; i < mTableOfContents.size() ;i ++) { if(mTableOfContents.itemAt(i).mTimeUs > timeUs) { oe = mTableOfContents.itemAt(i).mPageOffset; break; } else if(mTableOfContents.itemAt(i).mTimeUs == timeUs) return seekToOffset(mTableOfContents.itemAt(left).mPageOffset); } for(size_t i = left ; (i >= 0) && (i < mTableOfContents.size()) ;i --) { if(mTableOfContents.itemAt(i).mTimeUs < timeUs) { os = mTableOfContents.itemAt(i).mPageOffset; break; } else if(mTableOfContents.itemAt(i).mTimeUs == timeUs) return seekToOffset(mTableOfContents.itemAt(left).mPageOffset); } /*for(size_t i = 0 ; i< mTableOfContents.size() ;i ++) { uint64_t ts; findGranulePositionofPage(mTableOfContents.itemAt(i).mPageOffset,&ts); SXLOGD("mTableOfContents.itemAt(%d)--mPageOffset:%lld,mTimeUs:%lld,ts:%lld",i,mTableOfContents.itemAt(i).mPageOffset,mTableOfContents.itemAt(i).mTimeUs,ts); }*/ uint64_t ts1,ts2,ts; findGranulePositionofPage(os,&ts1); findGranulePositionofPage(oe,&ts2); off64_t pos = findAccuratePageOffset((uint64_t)(timeUs * mVi.rate /1000000),os,oe); findGranulePositionofPage(pos,&ts); SXLOGD("seektable seek--pos:%lld,ts1:%lld,ts2:%lld,pos:%lld,ts:%lld",timeUs * mVi.rate /1000000,ts1,ts2,pos,ts); return seekToOffset(pos); #endif } #ifdef MTK_AOSP_ENHANCEMENT status_t MyVorbisExtractor::findNextPage_l( off64_t startOffset, off64_t *pageOffset) { *pageOffset = startOffset; char signature[2048]; for (;;) { ssize_t n = mSource->readAt(*pageOffset, &signature, 2000); if (n < 4) { *pageOffset = 0; return (n < 0) ? n : (status_t)ERROR_END_OF_STREAM; } int i = 0; int step = n-4; while(i<=step) { if (!memcmp(&signature[i], "OggS", 4) && ((*pageOffset + i) > startOffset)) { if ((*pageOffset + i) > startOffset) { SXLOGV("skipped %lld bytes of junk to reach next frame", (*pageOffset + i) - startOffset); } *pageOffset += i; return OK; } i++; } *pageOffset += n; } }
void MyVorbisExtractor::buildTableOfContents() { off64_t offset = mFirstDataOffset; Page page; ssize_t pageSize; #ifndef MTK_AOSP_ENHANCEMENT while ((pageSize = readPage(offset, &page)) > 0) { #else struct timeval tb,te; gettimeofday(&tb,NULL); while (mTocStarted && ((pageSize = readPage(offset, &page)) > 0)) { if(page.mGranulePosition < 0xFFFFFFFFFFFF) { #endif mTableOfContents.push(); TOCEntry &entry = mTableOfContents.editItemAt(mTableOfContents.size() - 1); entry.mPageOffset = offset; entry.mTimeUs = page.mGranulePosition * 1000000ll / mVi.rate; #ifdef MTK_AOSP_ENHANCEMENT //sleep 100ms for consumes over 2s gettimeofday(&te,NULL); if((te.tv_sec - tb.tv_sec) > 2) { gettimeofday(&tb,NULL); usleep(100000); } } #endif offset += (size_t)pageSize; } // Limit the maximum amount of RAM we spend on the table of contents, // if necessary thin out the table evenly to trim it down to maximum // size. static const size_t kMaxTOCSize = 8192; static const size_t kMaxNumTOCEntries = kMaxTOCSize / sizeof(TOCEntry); size_t numerator = mTableOfContents.size(); if (numerator > kMaxNumTOCEntries) { size_t denom = numerator - kMaxNumTOCEntries; size_t accum = 0; for (ssize_t i = mTableOfContents.size() - 1; i >= 0; --i) { accum += denom; if (accum >= numerator) { mTableOfContents.removeAt(i); accum -= numerator; } } } #ifdef MTK_AOSP_ENHANCEMENT mTocDone = true; #endif } status_t MyVorbisExtractor::verifyHeader( MediaBuffer *buffer, uint8_t type) { const uint8_t *data = (const uint8_t *)buffer->data() + buffer->range_offset(); size_t size = buffer->range_length(); if (size < 7 || data[0] != type || memcmp(&data[1], "vorbis", 6)) { return ERROR_MALFORMED; } ogg_buffer buf; buf.data = (uint8_t *)data; buf.size = size; buf.refcount = 1; buf.ptr.owner = NULL; ogg_reference ref; ref.buffer = &buf; ref.begin = 0; ref.length = size; ref.next = NULL; oggpack_buffer bits; oggpack_readinit(&bits, &ref); CHECK_EQ(oggpack_read(&bits, 8), type); for (size_t i = 0; i < 6; ++i) { oggpack_read(&bits, 8); // skip 'vorbis' } switch (type) { case 1: { #ifndef MTK_AOSP_ENHANCEMENT CHECK_EQ(0, _vorbis_unpack_info(&mVi, &bits)); #else _vorbis_unpack_info(&mVi, &bits);//skip the CHECK #endif mMeta->setData(kKeyVorbisInfo, 0, data, size); mMeta->setInt32(kKeySampleRate, mVi.rate); mMeta->setInt32(kKeyChannelCount, mVi.channels); #ifdef MTK_AOSP_ENHANCEMENT if(mVi.channels > 2) { #ifndef MTK_SWIP_VORBIS SXLOGE("Tremolo does not support multi channel"); return ERROR_UNSUPPORTED; #endif } #endif ALOGV("lower-bitrate = %ld", mVi.bitrate_lower); ALOGV("upper-bitrate = %ld", mVi.bitrate_upper); ALOGV("nominal-bitrate = %ld", mVi.bitrate_nominal); ALOGV("window-bitrate = %ld", mVi.bitrate_window); off64_t size; if (mSource->getSize(&size) == OK) { uint64_t bps = approxBitrate(); if (bps != 0) { mMeta->setInt64(kKeyDuration, size * 8000000ll / bps); } } break; } case 3: { if (0 != _vorbis_unpack_comment(&mVc, &bits)) { return ERROR_MALFORMED; } parseFileMetaData(); break; } case 5: { if (0 != _vorbis_unpack_books(&mVi, &bits)) { return ERROR_MALFORMED; } mMeta->setData(kKeyVorbisBooks, 0, data, size); break; } } return OK; } uint64_t MyVorbisExtractor::approxBitrate() { if (mVi.bitrate_nominal != 0) { return mVi.bitrate_nominal; } return (mVi.bitrate_lower + mVi.bitrate_upper) / 2; } void MyVorbisExtractor::parseFileMetaData() { mFileMeta = new MetaData; #ifdef MTK_AOSP_ENHANCEMENT if(mFileMeta.get() == NULL) return; #endif mFileMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_OGG); for (int i = 0; i < mVc.comments; ++i) { const char *comment = mVc.user_comments[i]; size_t commentLength = mVc.comment_lengths[i]; parseVorbisComment(mFileMeta, comment, commentLength); //ALOGI("comment #%d: '%s'", i + 1, mVc.user_comments[i]); } }