Created
November 24, 2011 11:18
-
-
Save shaobin0604/1391130 to your computer and use it in GitHub Desktop.
AACExtractor on Froyo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2011 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 "AACExtractor" | |
#include <utils/Log.h> | |
#include "include/AACExtractor.h" | |
#include <media/stagefright/DataSource.h> | |
#include <media/stagefright/MediaBufferGroup.h> | |
#include <media/stagefright/MediaDebug.h> | |
#include <media/stagefright/MediaDefs.h> | |
#include <media/stagefright/MediaErrors.h> | |
#include <media/stagefright/MediaSource.h> | |
#include <media/stagefright/MetaData.h> | |
#include <utils/String8.h> | |
namespace android { | |
static sp<MetaData> MakeAACCodecSpecificData( | |
unsigned profile, unsigned sampling_freq_index, | |
unsigned channel_configuration); | |
class AACSource : public MediaSource { | |
public: | |
AACSource(const sp<DataSource> &source, | |
const sp<MetaData> &meta, | |
const Vector<uint64_t> &offset_vector, | |
int64_t frame_duration_us); | |
virtual status_t start(MetaData *params = NULL); | |
virtual status_t stop(); | |
virtual sp<MetaData> getFormat(); | |
virtual status_t read( | |
MediaBuffer **buffer, const ReadOptions *options = NULL); | |
protected: | |
virtual ~AACSource(); | |
private: | |
static const size_t kMaxFrameSize; | |
sp<DataSource> mDataSource; | |
sp<MetaData> mMeta; | |
off64_t mOffset; | |
int64_t mCurrentTimeUs; | |
bool mStarted; | |
MediaBufferGroup *mGroup; | |
Vector<uint64_t> mOffsetVector; | |
int64_t mFrameDurationUs; | |
AACSource(const AACSource &); | |
AACSource &operator=(const AACSource &); | |
}; | |
//////////////////////////////////////////////////////////////////////////////// | |
// Returns the sample rate based on the sampling frequency index | |
uint32_t get_sample_rate(const uint8_t sf_index) | |
{ | |
static const uint32_t sample_rates[] = | |
{ | |
96000, 88200, 64000, 48000, 44100, 32000, | |
24000, 22050, 16000, 12000, 11025, 8000 | |
}; | |
if (sf_index < sizeof(sample_rates) / sizeof(sample_rates[0])) { | |
return sample_rates[sf_index]; | |
} | |
return 0; | |
} | |
// Returns the frame length in bytes as described in an ADTS header starting at the given offset, | |
// or 0 if the size can't be read due to an error in the header or a read failure. | |
// The returned value is the AAC frame size with the ADTS header length (regardless of | |
// the presence of the CRC). | |
// If headerSize is non-NULL, it will be used to return the size of the header of this ADTS frame. | |
static size_t getAdtsFrameLength(const sp<DataSource> &source, off64_t offset, size_t* headerSize) { | |
const size_t kAdtsHeaderLengthNoCrc = 7; | |
const size_t kAdtsHeaderLengthWithCrc = 9; | |
size_t frameSize = 0; | |
uint8_t syncword[2]; | |
if (source->readAt(offset, &syncword, 2) != 2) { | |
return 0; | |
} | |
if ((syncword[0] != 0xff) || ((syncword[1] & 0xf6) != 0xf0)) { | |
return 0; | |
} | |
uint8_t protectionAbsent; | |
if (source->readAt(offset + 1, &protectionAbsent, 1) < 1) { | |
return 0; | |
} | |
protectionAbsent &= 0x1; | |
uint8_t header[3]; | |
if (source->readAt(offset + 3, &header, 3) < 3) { | |
return 0; | |
} | |
frameSize = (header[0] & 0x3) << 11 | header[1] << 3 | header[2] >> 5; | |
// protectionAbsent is 0 if there is CRC | |
size_t headSize = protectionAbsent ? kAdtsHeaderLengthNoCrc : kAdtsHeaderLengthWithCrc; | |
if (headSize > frameSize) { | |
return 0; | |
} | |
if (headerSize != NULL) { | |
*headerSize = headSize; | |
} | |
return frameSize; | |
} | |
AACExtractor::AACExtractor(const sp<DataSource> &source) | |
: mDataSource(source), | |
mInitCheck(NO_INIT), | |
mFrameDurationUs(0) { | |
LOGD("+++++ AACExtractor"); | |
String8 mimeType; | |
float confidence; | |
if (!SniffAAC(mDataSource, &mimeType, &confidence)) { | |
return; | |
} | |
uint8_t profile, sf_index, channel, header[2]; | |
if (mDataSource->readAt(2, &header, 2) < 2) { | |
return; | |
} | |
profile = (header[0] >> 6) & 0x3; | |
sf_index = (header[0] >> 2) & 0xf; | |
uint32_t sr = get_sample_rate(sf_index); | |
if (sr == 0) { | |
return; | |
} | |
channel = (header[0] & 0x1) << 2 | (header[1] >> 6); | |
mMeta = MakeAACCodecSpecificData(profile, sf_index, channel); | |
off_t offset = 0; | |
off_t streamSize, numFrames = 0; | |
size_t frameSize = 0; | |
int64_t duration = 0; | |
if (mDataSource->getSize(&streamSize) == OK) { | |
while (offset < streamSize) { | |
if ((frameSize = getAdtsFrameLength(source, offset, NULL)) == 0) { | |
return; | |
} | |
mOffsetVector.push(offset); | |
offset += frameSize; | |
numFrames ++; | |
} | |
// Round up and get the duration | |
mFrameDurationUs = (1024 * 1000000ll + (sr - 1)) / sr; | |
duration = numFrames * mFrameDurationUs; | |
mMeta->setInt64(kKeyDuration, duration); | |
} | |
mInitCheck = OK; | |
LOGD("----- AACExtractor"); | |
} | |
AACExtractor::~AACExtractor() { | |
} | |
sp<MetaData> AACExtractor::getMetaData() { | |
sp<MetaData> meta = new MetaData; | |
if (mInitCheck != OK) { | |
return meta; | |
} | |
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC_ADTS); | |
return meta; | |
} | |
size_t AACExtractor::countTracks() { | |
return mInitCheck == OK ? 1 : 0; | |
} | |
sp<MediaSource> AACExtractor::getTrack(size_t index) { | |
if (mInitCheck != OK || index != 0) { | |
return NULL; | |
} | |
return new AACSource(mDataSource, mMeta, mOffsetVector, mFrameDurationUs); | |
} | |
sp<MetaData> AACExtractor::getTrackMetaData(size_t index, uint32_t flags) { | |
if (mInitCheck != OK || index != 0) { | |
return NULL; | |
} | |
return mMeta; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
// 8192 = 2^13, 13bit AAC frame size (in bytes) | |
const size_t AACSource::kMaxFrameSize = 8192; | |
AACSource::AACSource( | |
const sp<DataSource> &source, const sp<MetaData> &meta, | |
const Vector<uint64_t> &offset_vector, | |
int64_t frame_duration_us) | |
: mDataSource(source), | |
mMeta(meta), | |
mOffset(0), | |
mCurrentTimeUs(0), | |
mStarted(false), | |
mGroup(NULL), | |
mOffsetVector(offset_vector), | |
mFrameDurationUs(frame_duration_us) { | |
} | |
AACSource::~AACSource() { | |
if (mStarted) { | |
stop(); | |
} | |
} | |
status_t AACSource::start(MetaData *params) { | |
CHECK(!mStarted); | |
mOffset = 0; | |
mCurrentTimeUs = 0; | |
mGroup = new MediaBufferGroup; | |
mGroup->add_buffer(new MediaBuffer(kMaxFrameSize)); | |
mStarted = true; | |
return OK; | |
} | |
status_t AACSource::stop() { | |
CHECK(mStarted); | |
delete mGroup; | |
mGroup = NULL; | |
mStarted = false; | |
return OK; | |
} | |
sp<MetaData> AACSource::getFormat() { | |
return mMeta; | |
} | |
status_t AACSource::read( | |
MediaBuffer **out, const ReadOptions *options) { | |
*out = NULL; | |
LOGV("+++++ read"); | |
int64_t seekTimeUs; | |
if (options && options->getSeekTo(&seekTimeUs)) { | |
if (mFrameDurationUs > 0) { | |
int64_t seekFrame = seekTimeUs / mFrameDurationUs; | |
mCurrentTimeUs = seekFrame * mFrameDurationUs; | |
mOffset = mOffsetVector.itemAt(seekFrame); | |
} | |
} | |
size_t frameSize, frameSizeWithoutHeader, headerSize; | |
if ((frameSize = getAdtsFrameLength(mDataSource, mOffset, &headerSize)) == 0) { | |
return ERROR_END_OF_STREAM; | |
} | |
MediaBuffer *buffer; | |
status_t err = mGroup->acquire_buffer(&buffer); | |
if (err != OK) { | |
return err; | |
} | |
frameSizeWithoutHeader = frameSize - headerSize; | |
if (mDataSource->readAt(mOffset + headerSize, buffer->data(), | |
frameSizeWithoutHeader) != (ssize_t)frameSizeWithoutHeader) { | |
buffer->release(); | |
buffer = NULL; | |
return ERROR_IO; | |
} | |
buffer->set_range(0, frameSizeWithoutHeader); | |
buffer->meta_data()->setInt64(kKeyTime, mCurrentTimeUs); | |
buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1); | |
mOffset += frameSize; | |
mCurrentTimeUs += mFrameDurationUs; | |
*out = buffer; | |
LOGV("----- read"); | |
return OK; | |
} | |
static sp<MetaData> MakeAACCodecSpecificData( | |
unsigned profile, unsigned sampling_freq_index, | |
unsigned channel_configuration) { | |
sp<MetaData> meta = new MetaData; | |
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); | |
//CHECK_LE(sampling_freq_index, 11u); | |
static const int32_t kSamplingFreq[] = { | |
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, | |
16000, 12000, 11025, 8000 | |
}; | |
meta->setInt32(kKeySampleRate, kSamplingFreq[sampling_freq_index]); | |
meta->setInt32(kKeyChannelCount, channel_configuration); | |
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, 2, | |
// AudioSpecificInfo follows | |
// oooo offf fccc c000 | |
// o - audioObjectType | |
// f - samplingFreqIndex | |
// c - channelConfig | |
}; | |
#if 1 | |
size_t csdSize = sizeof(kStaticESDS) + 2; | |
uint8_t* csdData = (uint8_t*)malloc(csdSize); | |
memcpy(csdData, kStaticESDS, sizeof(kStaticESDS)); | |
csdData[sizeof(kStaticESDS)] = | |
((profile + 1) << 3) | (sampling_freq_index >> 1); | |
csdData[sizeof(kStaticESDS) + 1] = | |
((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3); | |
meta->setData(kKeyESDS, 0, csdData, csdSize); | |
free(csdData); | |
#else | |
// ABuffer is defined in android-2.3 | |
sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2); | |
memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS)); | |
csd->data()[sizeof(kStaticESDS)] = | |
((profile + 1) << 3) | (sampling_freq_index >> 1); | |
csd->data()[sizeof(kStaticESDS) + 1] = | |
((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3); | |
meta->setData(kKeyESDS, 0, csd->data(), csd->size()); | |
#endif | |
return meta; | |
} | |
//////////////////////////////////////////////////////////////////////////////// | |
bool SniffAAC( | |
const sp<DataSource> &source, String8 *mimeType, float *confidence) { | |
uint8_t header[2]; | |
if (source->readAt(0, &header, 2) != 2) { | |
return false; | |
} | |
// ADTS syncword | |
if ((header[0] == 0xff) && ((header[1] & 0xf6) == 0xf0)) { | |
*mimeType = MEDIA_MIMETYPE_AUDIO_AAC_ADTS; | |
*confidence = 0.2; | |
return true; | |
} | |
return false; | |
} | |
} // namespace android |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2011 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. | |
*/ | |
#ifndef AAC_EXTRACTOR_H_ | |
#define AAC_EXTRACTOR_H_ | |
#include <media/stagefright/MediaExtractor.h> | |
#include <utils/Vector.h> | |
namespace android { | |
struct AMessage; | |
class String8; | |
class AACExtractor : public MediaExtractor { | |
public: | |
AACExtractor(const sp<DataSource> &source); | |
virtual size_t countTracks(); | |
virtual sp<MediaSource> getTrack(size_t index); | |
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags); | |
virtual sp<MetaData> getMetaData(); | |
protected: | |
virtual ~AACExtractor(); | |
private: | |
sp<DataSource> mDataSource; | |
sp<MetaData> mMeta; | |
status_t mInitCheck; | |
Vector<uint64_t> mOffsetVector; | |
int64_t mFrameDurationUs; | |
AACExtractor(const AACExtractor &); | |
AACExtractor &operator=(const AACExtractor &); | |
}; | |
bool SniffAAC( | |
const sp<DataSource> &source, String8 *mimeType, float *confidence); | |
} // namespace android | |
#endif // AAC_EXTRACTOR_H_ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment