status_t MP3Source::getNextFramePos(off_t *curPos, off_t *pNextPos,int64_t * frameTsUs) { uint8_t mp3header[4]; size_t frame_size; int samplerate=0; int num_sample =0; for(;;) { ssize_t n = mDataSource->readAt(*curPos, mp3header, 4); if (n < 4) { MP3_EXTR_DBG("For Seek Talbe :ERROR_END_OF_STREAM"); return ERROR_END_OF_STREAM; } // MP3_EXTR_DBG("mp3header[0]=%0x,mp3header[1]=%0x,mp3header[2]=%0x,mp3header[3]=%0x",mp3header[0],mp3header[1],mp3header[2],mp3header[3]); uint32_t header = U32_AT((const uint8_t *)mp3header); if ((header & kMask) == (mFixedHeader & kMask) && GetMPEGAudioFrameSize(header, &frame_size, &samplerate, NULL,NULL,&num_sample)) { break; } // Lost sync. //MP3_EXTR_DBG("getNextFramePos::lost sync! header = 0x%08x, old header = 0x%08x\n", header, mFixedHeader); off64_t pos = *curPos; if (!Resync(mDataSource, mFixedHeader, &pos, NULL,NULL)) { //MP3_EXTR_DBG("getNextFramePos---Unable to resync. Signalling end of stream."); return ERROR_END_OF_STREAM; } *curPos = pos; // Try again with the new position. } *pNextPos=*curPos+frame_size; *frameTsUs = 1000000ll * num_sample/samplerate; return OK; }
status_t MP3Source::read( MediaBuffer **out, const ReadOptions *options) { *out = NULL; int64_t seekTimeUs; ReadOptions::SeekMode mode; bool seekCBR = false; if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) { int64_t actualSeekTimeUs = seekTimeUs; #ifndef ANDROID_DEFAULT_CODE if(!mEnableTOC){ #endif if (mSeeker == NULL || !mSeeker->getOffsetForTime(&actualSeekTimeUs, &mCurrentPos)) { int32_t bitrate; if (!mMeta->findInt32(kKeyBitRate, &bitrate)) { // bitrate is in bits/sec. ALOGI("no bitrate"); return ERROR_UNSUPPORTED; } mCurrentTimeUs = seekTimeUs; mCurrentPos = mFirstFramePos + seekTimeUs * bitrate / 8000000; seekCBR = true; } else { mCurrentTimeUs = actualSeekTimeUs; } #ifndef ANDROID_DEFAULT_CODE }else{ MP3_EXTR_DBG("before getFramePos seekTimeUs=%lld",seekTimeUs); off_t ActualPos=0; status_t stat=getFramePos(seekTimeUs, &mCurrentTimeUs, &ActualPos, true); if(stat==BAD_VALUE){ int32_t bitrate; if (!mMeta->findInt32(kKeyBitRate, &bitrate)) { // bitrate is in bits/sec. MP3_EXTR_WARN("no bitrate"); return ERROR_UNSUPPORTED; } mCurrentTimeUs = seekTimeUs; mCurrentPos = mFirstFramePos + seekTimeUs * bitrate / 8000000; if (mSeeker == NULL || !mSeeker->getOffsetForTime(&actualSeekTimeUs, &mCurrentPos)) { int32_t bitrate; if (!mMeta->findInt32(kKeyBitRate, &bitrate)) { // bitrate is in bits/sec. ALOGI("no bitrate"); return ERROR_UNSUPPORTED; } mCurrentTimeUs = seekTimeUs; mCurrentPos = mFirstFramePos + seekTimeUs * bitrate / 8000000; seekCBR = true; } else { mCurrentTimeUs = actualSeekTimeUs; } }else if(stat == ERROR_END_OF_STREAM){ return stat; }else{ mCurrentPos= ActualPos; MP3_EXTR_DBG("after seek mCurrentTimeUs=%lld,pActualPos=%ld",mCurrentTimeUs,ActualPos); } } #endif mBasisTimeUs = mCurrentTimeUs; mSamplesRead = 0; } MediaBuffer *buffer; status_t err = mGroup->acquire_buffer(&buffer); if (err != OK) { return err; } size_t frame_size; int bitrate; int num_samples; int sample_rate; for (;;) { ssize_t n = mDataSource->readAt(mCurrentPos, buffer->data(), 4); if (n < 4) { buffer->release(); buffer = NULL; return ERROR_END_OF_STREAM; } uint32_t header = U32_AT((const uint8_t *)buffer->data()); if ((header & kMask) == (mFixedHeader & kMask) && GetMPEGAudioFrameSize( header, &frame_size, &sample_rate, NULL, &bitrate, &num_samples)) { // re-calculate mCurrentTimeUs because we might have called Resync() if (seekCBR) { mCurrentTimeUs = (mCurrentPos - mFirstFramePos) * 8000 / bitrate; mBasisTimeUs = mCurrentTimeUs; } break; } // Lost sync. ALOGV("lost sync! header = 0x%08x, old header = 0x%08x\n", header, mFixedHeader); off64_t pos = mCurrentPos; if (!Resync(mDataSource, mFixedHeader, &pos, NULL, NULL)) { ALOGE("Unable to resync. Signalling end of stream."); buffer->release(); buffer = NULL; return ERROR_END_OF_STREAM; } mCurrentPos = pos; // Try again with the new position. } CHECK(frame_size <= buffer->size()); ssize_t n = mDataSource->readAt(mCurrentPos, buffer->data(), frame_size); if (n < (ssize_t)frame_size) { buffer->release(); buffer = NULL; return ERROR_END_OF_STREAM; } buffer->set_range(0, frame_size); buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs); buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); mCurrentPos += frame_size; mSamplesRead += num_samples; mCurrentTimeUs = mBasisTimeUs + ((mSamplesRead * 1000000) / sample_rate); *out = buffer; return OK; }
bool FastSniffMP3( const sp<DataSource> &source, String8 *mimeType, float *confidence, sp<AMessage> *meta) { off64_t inout_pos = 0; off64_t post_id3_pos; uint32_t header; if (inout_pos == 0) { // Skip an optional ID3 header if syncing at the very beginning // of the datasource. for (;;) { uint8_t id3header[10]; if (source->readAt(inout_pos, id3header, sizeof(id3header)) < (ssize_t)sizeof(id3header)) { ALOGV("Read no enough data"); return false; } if (memcmp("ID3", id3header, 3)) { break; } size_t len = ((id3header[6] & 0x7f) << 21) | ((id3header[7] & 0x7f) << 14) | ((id3header[8] & 0x7f) << 7) | (id3header[9] & 0x7f); len += 10; inout_pos += len; } post_id3_pos = inout_pos; } off64_t pos = inout_pos; bool valid = true; uint32_t test_header = 0; for (int j = 0; j < 4; ++j) { uint8_t tmp[4]; if (source->readAt(pos, tmp, 4) < 4) { valid = false; break; } test_header = U32_AT(tmp); size_t test_frame_size; if (!GetMPEGAudioFrameSize( test_header, &test_frame_size)) { valid = false; break; } ALOGV("found subsequent frame #%d at %lld", j + 2, pos); pos += test_frame_size; } if (false == valid) { ALOGV("no dice, no valid sequence of frames found."); return false; } else header = test_header; *meta = new AMessage; (*meta)->setInt64("offset", inout_pos); (*meta)->setInt32("header", header); (*meta)->setInt64("post-id3-offset", inout_pos); *mimeType = MEDIA_MIMETYPE_AUDIO_MPEG; *confidence = 0.2f; return true; }
static bool Resync( const sp<DataSource> &source, uint32_t match_header, off64_t *inout_pos, off64_t *post_id3_pos, uint32_t *out_header) { if (post_id3_pos != NULL) { *post_id3_pos = 0; } if (*inout_pos == 0) { // Skip an optional ID3 header if syncing at the very beginning // of the datasource. for (;;) { uint8_t id3header[10]; if (source->readAt(*inout_pos, id3header, sizeof(id3header)) < (ssize_t)sizeof(id3header)) { // If we can't even read these 10 bytes, we might as well bail // out, even if there _were_ 10 bytes of valid mp3 audio data... return false; } if (memcmp("ID3", id3header, 3)) { break; } // Skip the ID3v2 header. size_t len = ((id3header[6] & 0x7f) << 21) | ((id3header[7] & 0x7f) << 14) | ((id3header[8] & 0x7f) << 7) | (id3header[9] & 0x7f); len += 10; *inout_pos += len; LOGV("skipped ID3 tag, new starting offset is %lld (0x%016llx)", *inout_pos, *inout_pos); } if (post_id3_pos != NULL) { *post_id3_pos = *inout_pos; } } off64_t pos = *inout_pos; bool valid = false; const size_t kMaxReadBytes = 1024; const size_t kMaxBytesChecked = 128 * 1024; uint8_t buf[kMaxReadBytes]; ssize_t bytesToRead = kMaxReadBytes; ssize_t totalBytesRead = 0; ssize_t remainingBytes = 0; bool reachEOS = false; uint8_t *tmp = buf; do { if (pos >= *inout_pos + kMaxBytesChecked) { // Don't scan forever. LOGV("giving up at offset %lld", pos); break; } if (remainingBytes < 4) { if (reachEOS) { break; } else { memcpy(buf, tmp, remainingBytes); bytesToRead = kMaxReadBytes - remainingBytes; /* * The next read position should start from the end of * the last buffer, and thus should include the remaining * bytes in the buffer. */ totalBytesRead = source->readAt(pos + remainingBytes, buf + remainingBytes, bytesToRead); if (totalBytesRead <= 0) { break; } reachEOS = (totalBytesRead != bytesToRead); totalBytesRead += remainingBytes; remainingBytes = totalBytesRead; tmp = buf; continue; } } uint32_t header = U32_AT(tmp); if (match_header != 0 && (header & kMask) != (match_header & kMask)) { ++pos; ++tmp; --remainingBytes; continue; } size_t frame_size; int sample_rate, num_channels, bitrate; if (!GetMPEGAudioFrameSize( header, &frame_size, &sample_rate, &num_channels, &bitrate)) { ++pos; ++tmp; --remainingBytes; continue; } LOGV("found possible 1st frame at %lld (header = 0x%08x)", pos, header); // We found what looks like a valid frame, // now find its successors. off64_t test_pos = pos + frame_size; valid = true; for (int j = 0; j < 3; ++j) { uint8_t tmp[4]; if (source->readAt(test_pos, tmp, 4) < 4) { valid = false; break; } uint32_t test_header = U32_AT(tmp); LOGV("subsequent header is %08x", test_header); if ((test_header & kMask) != (header & kMask)) { valid = false; break; } size_t test_frame_size; if (!GetMPEGAudioFrameSize( test_header, &test_frame_size)) { valid = false; break; } LOGV("found subsequent frame #%d at %lld", j + 2, test_pos); test_pos += test_frame_size; } if (valid) { *inout_pos = pos; if (out_header != NULL) { *out_header = header; } } else { LOGV("no dice, no valid sequence of frames found."); } ++pos; ++tmp; --remainingBytes; } while (!valid); return valid; }
MP3Extractor::MP3Extractor( const sp<DataSource> &source, const sp<AMessage> &meta) : mInitCheck(NO_INIT), mDataSource(source), mFirstFramePos(-1), mFixedHeader(0) { off64_t pos = 0; off64_t post_id3_pos; uint32_t header; bool success; int64_t meta_offset; uint32_t meta_header; int64_t meta_post_id3_offset; if (meta != NULL && meta->findInt64("offset", &meta_offset) && meta->findInt32("header", (int32_t *)&meta_header) && meta->findInt64("post-id3-offset", &meta_post_id3_offset)) { // The sniffer has already done all the hard work for us, simply // accept its judgement. pos = (off64_t)meta_offset; header = meta_header; post_id3_pos = (off64_t)meta_post_id3_offset; success = true; } else { success = Resync(mDataSource, 0, &pos, &post_id3_pos, &header); } if (!success) { // mInitCheck will remain NO_INIT return; } mFirstFramePos = pos; mFixedHeader = header; size_t frame_size; int sample_rate; int num_channels; int bitrate; GetMPEGAudioFrameSize( header, &frame_size, &sample_rate, &num_channels, &bitrate); mMeta = new MetaData; mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); mMeta->setInt32(kKeySampleRate, sample_rate); mMeta->setInt32(kKeyBitRate, bitrate * 1000); mMeta->setInt32(kKeyChannelCount, num_channels); mSeeker = XINGSeeker::CreateFromSource(mDataSource, mFirstFramePos); if (mSeeker == NULL) { mSeeker = VBRISeeker::CreateFromSource(mDataSource, post_id3_pos); } int64_t durationUs; if (mSeeker == NULL || !mSeeker->getDuration(&durationUs)) { off64_t fileSize; if (mDataSource->getSize(&fileSize) == OK) { durationUs = 8000LL * (fileSize - mFirstFramePos) / bitrate; } else { durationUs = -1; } } if (durationUs >= 0) { mMeta->setInt64(kKeyDuration, durationUs); } mInitCheck = OK; }
// static sp<VBRISeeker> VBRISeeker::CreateFromSource( const sp<DataSource> &source, off64_t post_id3_pos) { off64_t pos = post_id3_pos; uint8_t header[4]; ssize_t n = source->readAt(pos, header, sizeof(header)); if (n < (ssize_t)sizeof(header)) { return NULL; } uint32_t tmp = U32_AT(&header[0]); size_t frameSize; int sampleRate; if (!GetMPEGAudioFrameSize(tmp, &frameSize, &sampleRate)) { return NULL; } // VBRI header follows 32 bytes after the header _ends_. pos += sizeof(header) + 32; uint8_t vbriHeader[26]; n = source->readAt(pos, vbriHeader, sizeof(vbriHeader)); if (n < (ssize_t)sizeof(vbriHeader)) { return NULL; } if (memcmp(vbriHeader, "VBRI", 4)) { return NULL; } size_t numFrames = U32_AT(&vbriHeader[14]); int64_t durationUs = numFrames * 1000000ll * (sampleRate >= 32000 ? 1152 : 576) / sampleRate; ALOGV("duration = %.2f secs", durationUs / 1E6); size_t numEntries = U16_AT(&vbriHeader[18]); size_t entrySize = U16_AT(&vbriHeader[22]); size_t scale = U16_AT(&vbriHeader[20]); ALOGV("%zu entries, scale=%zu, size_per_entry=%zu", numEntries, scale, entrySize); size_t totalEntrySize = numEntries * entrySize; uint8_t *buffer = new uint8_t[totalEntrySize]; n = source->readAt(pos + sizeof(vbriHeader), buffer, totalEntrySize); if (n < (ssize_t)totalEntrySize) { delete[] buffer; buffer = NULL; return NULL; } sp<VBRISeeker> seeker = new VBRISeeker; seeker->mBasePos = post_id3_pos + frameSize; // only update mDurationUs if the calculated duration is valid (non zero) // otherwise, leave duration at -1 so that getDuration() and getOffsetForTime() // return false when called, to indicate that this vbri tag does not have the // requested information if (durationUs) { seeker->mDurationUs = durationUs; } off64_t offset = post_id3_pos; for (size_t i = 0; i < numEntries; ++i) { uint32_t numBytes; switch (entrySize) { case 1: numBytes = buffer[i]; break; case 2: numBytes = U16_AT(buffer + 2 * i); break; case 3: numBytes = U24_AT(buffer + 3 * i); break; default: { CHECK_EQ(entrySize, 4u); numBytes = U32_AT(buffer + 4 * i); break; } } numBytes *= scale; seeker->mSegments.push(numBytes); ALOGV("entry #%zu: %u offset 0x%016llx", i, numBytes, offset); offset += numBytes; } delete[] buffer; buffer = NULL; ALOGI("Found VBRI header."); return seeker; }