int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op){ oggpack_buffer opb; if(op){ oggpack_readinit(&opb,op->packet); /* Which of the three types of header is this? */ /* Also verify header-ness, vorbis */ { char buffer[6]; int packtype=oggpack_read(&opb,8); memset(buffer,0,6); _v_readstring(&opb,buffer,6); if(memcmp(buffer,"vorbis",6)){ /* not a vorbis header */ return(OV_ENOTVORBIS); } switch(packtype){ case 0x01: /* least significant *bit* is read first */ if(!op->b_o_s){ /* Not the initial packet */ return(OV_EBADHEADER); } if(vi->rate!=0){ /* previously initialized info header */ return(OV_EBADHEADER); } return(_vorbis_unpack_info(vi,&opb)); case 0x03: /* least significant *bit* is read first */ if(vi->rate==0){ /* um... we didn't get the initial header */ return(OV_EBADHEADER); } return(_vorbis_unpack_comment(vc,&opb)); case 0x05: /* least significant *bit* is read first */ if(vi->rate==0 || vc->vendor==NULL){ /* um... we didn;t get the initial header or comments yet */ return(OV_EBADHEADER); } return(_vorbis_unpack_books(vi,&opb)); default: /* Not a valid vorbis header type */ return(OV_EBADHEADER); break; } } } return(OV_EBADHEADER); }
int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op,unsigned int newpack) { int packtype; oggpack_buffer opb; char buffer[6]; if(op){ if(newpack) oggpack_readinit(&opb,op->packet,op->bytes); packtype=oggpack_read24(&opb,8); _v_readstring(&opb,buffer,6); if(memcmp(buffer,"vorbis",6)!=0) return(OV_ENOTVORBIS); switch(packtype){ case 0x01: if(!op->b_o_s) return(OV_EBADHEADER); if(vi->rate!=0) return(OV_EBADHEADER); return(_vorbis_unpack_info(vi,&opb)); case 0x03: if(vi->rate==0) return(OV_EBADHEADER); #ifdef MPXPLAY if(!newpack) _vorbis_skip_comment(&opb); return(0); // comments are readed at Ogg demuxing in Mpxplay #else return(_vorbis_unpack_comment(vc,&opb)); #endif case 0x05: if(vi->rate==0) return(OV_EBADHEADER); #ifndef MPXPLAY if(vc->vendor==NULL) return(OV_EBADHEADER); #endif return(_vorbis_unpack_books(vi,&opb)); default: return(OV_EBADHEADER); break; } } return(OV_EBADHEADER); }
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; }
void SoftVorbis::onQueueFilled(OMX_U32 portIndex) { List<BufferInfo *> &inQueue = getPortQueue(0); List<BufferInfo *> &outQueue = getPortQueue(1); if (mOutputPortSettingsChange != NONE) { return; } if (portIndex == 0 && mInputBufferCount < 2) { BufferInfo *info = *inQueue.begin(); OMX_BUFFERHEADERTYPE *header = info->mHeader; const uint8_t *data = header->pBuffer + header->nOffset; size_t size = header->nFilledLen; ogg_buffer buf; ogg_reference ref; oggpack_buffer bits; makeBitReader( (const uint8_t *)data + 7, size - 7, &buf, &ref, &bits); if (mInputBufferCount == 0) { CHECK(mVi == NULL); mVi = new vorbis_info; vorbis_info_init(mVi); CHECK_EQ(0, _vorbis_unpack_info(mVi, &bits)); } else { CHECK_EQ(0, _vorbis_unpack_books(mVi, &bits)); CHECK(mState == NULL); mState = new vorbis_dsp_state; CHECK_EQ(0, vorbis_dsp_init(mState, mVi)); notify(OMX_EventPortSettingsChanged, 1, 0, NULL); mOutputPortSettingsChange = AWAITING_DISABLED; } inQueue.erase(inQueue.begin()); info->mOwnedByUs = false; notifyEmptyBufferDone(header); ++mInputBufferCount; return; } while (!inQueue.empty() && !outQueue.empty()) { BufferInfo *inInfo = *inQueue.begin(); OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader; BufferInfo *outInfo = *outQueue.begin(); OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader; if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) { inQueue.erase(inQueue.begin()); inInfo->mOwnedByUs = false; notifyEmptyBufferDone(inHeader); outHeader->nFilledLen = 0; outHeader->nFlags = OMX_BUFFERFLAG_EOS; outQueue.erase(outQueue.begin()); outInfo->mOwnedByUs = false; notifyFillBufferDone(outHeader); return; } int32_t numPageSamples; CHECK_GE(inHeader->nFilledLen, sizeof(numPageSamples)); memcpy(&numPageSamples, inHeader->pBuffer + inHeader->nOffset + inHeader->nFilledLen - 4, sizeof(numPageSamples)); if (numPageSamples >= 0) { mNumFramesLeftOnPage = numPageSamples; } if (inHeader->nOffset == 0) { mAnchorTimeUs = inHeader->nTimeStamp; mNumFramesOutput = 0; } inHeader->nFilledLen -= sizeof(numPageSamples);; ogg_buffer buf; buf.data = inHeader->pBuffer + inHeader->nOffset; buf.size = inHeader->nFilledLen; buf.refcount = 1; buf.ptr.owner = NULL; ogg_reference ref; ref.buffer = &buf; ref.begin = 0; ref.length = buf.size; ref.next = NULL; ogg_packet pack; pack.packet = &ref; pack.bytes = ref.length; pack.b_o_s = 0; pack.e_o_s = 0; pack.granulepos = 0; pack.packetno = 0; int numFrames = 0; int err = vorbis_dsp_synthesis(mState, &pack, 1); if (err != 0) { ALOGW("vorbis_dsp_synthesis returned %d", err); } else { numFrames = vorbis_dsp_pcmout( mState, (int16_t *)outHeader->pBuffer, kMaxNumSamplesPerBuffer); if (numFrames < 0) { ALOGE("vorbis_dsp_pcmout returned %d", numFrames); numFrames = 0; } } if (mNumFramesLeftOnPage >= 0) { if (numFrames > mNumFramesLeftOnPage) { ALOGV("discarding %d frames at end of page", numFrames - mNumFramesLeftOnPage); numFrames = mNumFramesLeftOnPage; } mNumFramesLeftOnPage -= numFrames; } outHeader->nFilledLen = numFrames * sizeof(int16_t) * mVi->channels; outHeader->nOffset = 0; outHeader->nFlags = 0; outHeader->nTimeStamp = mAnchorTimeUs + (mNumFramesOutput * 1000000ll) / mVi->rate; mNumFramesOutput += numFrames; inInfo->mOwnedByUs = false; inQueue.erase(inQueue.begin()); inInfo = NULL; notifyEmptyBufferDone(inHeader); inHeader = NULL; outInfo->mOwnedByUs = false; outQueue.erase(outQueue.begin()); outInfo = NULL; notifyFillBufferDone(outHeader); outHeader = NULL; ++mInputBufferCount; } }
status_t OggVorbisSeekable::GetStreamInfo(int64 *frameCount, bigtime_t *duration, media_format *format) { TRACE("OggVorbisSeekable::GetStreamInfo\n"); status_t result = B_OK; ogg_packet packet; // get header packet if (GetHeaderPackets().size() < 1) { result = GetPacket(&packet); if (result != B_OK) { return result; } SaveHeaderPacket(packet); } packet = GetHeaderPackets()[0]; if (!packet.b_o_s) { return B_ERROR; // first packet was not beginning of stream } // parse header packet // based on libvorbis/info.c vorbis_synthesis_headerin(...) oggpack_buffer opb; oggpack_readinit(&opb, packet.packet, packet.bytes); int packtype = oggpack_read(&opb, 8); if (packtype != 0x01) { return B_ERROR; // first packet was not an info packet } // discard vorbis string for (uint i = 0 ; i < sizeof("vorbis") - 1 ; i++) { oggpack_read(&opb, 8); } vorbis_info info; if (_vorbis_unpack_info(&info, &opb) != 0) { return B_ERROR; // couldn't unpack info } // get the format for the description media_format_description description = vorbis_description(); BMediaFormats formats; result = formats.InitCheck(); if (result == B_OK) { result = formats.GetFormatFor(description, format); } if (result != B_OK) { *format = vorbis_encoded_media_format(); // ignore error, allow user to use ReadChunk interface } // fill out format from header packet if (info.bitrate_nominal > 0) { format->u.encoded_audio.bit_rate = info.bitrate_nominal; } else if (info.bitrate_upper > 0) { format->u.encoded_audio.bit_rate = info.bitrate_upper; } else if (info.bitrate_lower > 0) { format->u.encoded_audio.bit_rate = info.bitrate_lower; } if (info.channels == 1) { format->u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT; } else { format->u.encoded_audio.multi_info.channel_mask = B_CHANNEL_LEFT | B_CHANNEL_RIGHT; } fFrameRate = format->u.encoded_audio.output.frame_rate = (float)info.rate; format->u.encoded_audio.output.channel_count = info.channels; format->u.encoded_audio.output.buffer_size = AudioBufferSize(&format->u.encoded_audio.output); // get comment packet if (GetHeaderPackets().size() < 2) { result = GetPacket(&packet); if (result != B_OK) { return result; } SaveHeaderPacket(packet); } // get codebook packet if (GetHeaderPackets().size() < 3) { result = GetPacket(&packet); if (result != B_OK) { return result; } SaveHeaderPacket(packet); } format->SetMetaData((void*)&GetHeaderPackets(),sizeof(GetHeaderPackets())); // TODO: count the frames in the first page.. somehow.. :-/ int64 frames = 0; ogg_page page; // read the first page result = ReadPage(&page); if (result != B_OK) { return result; } int64 fFirstGranulepos = ogg_page_granulepos(&page); TRACE("OggVorbisSeekable::GetStreamInfo: first granulepos: %lld\n", fFirstGranulepos); // read our last page off_t last = inherited::Seek(GetLastPagePosition(), SEEK_SET); if (last < 0) { return last; } result = ReadPage(&page); if (result != B_OK) { return result; } int64 last_granulepos = ogg_page_granulepos(&page); // seek back to the start int64 frame = 0; bigtime_t time = 0; result = Seek(B_MEDIA_SEEK_TO_TIME, &frame, &time); if (result != B_OK) { return result; } // compute frame count and duration from sample count frames = last_granulepos - fFirstGranulepos; *frameCount = frames; *duration = (1000000LL * frames) / (long long)fFrameRate; return B_OK; }
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]); } }