编译出了libapedec.so,后需要知道如何用这个库。

主要还是看MACLib.h这个头文件,因为我们要调的主要接口都在这个头文件中。其中有以下几个

:::c
    APE::IAPEDecompress * __stdcall CreateIAPEDecompress(const APE::str_utfn * pFilename, int * pErrorCode = NULL);

传入参数是文件名和错误代码的int指针,返回的是这个解码器的句柄IAPEDecompress。先获得APE的音频数据及相关信息,就要先调用此函数初始化解码器并获得其句柄。

:::c
class IAPEDecompress
{
public:
    // destructor (needed so implementation's destructor will be called)
    virtual ~IAPEDecompress() {};
    virtual int GetData(char * pBuffer, int nBlocks, int * pBlocksRetrieved) = 0;
    virtual int Seek(int nBlockOffset) = 0;
    virtual intn GetInfo(APE_DECOMPRESS_FIELDS Field, intn nParam1 = 0, intn nParam2 = 0) = 0;
};

获得了句柄后,就可以使用里面的函数获取我们需要的信息,例如GetInfo可以获得APE文件的相关信息,GetData可以获得解码后的数据,Seek可以跳转到APE数据的指定位置。

只要用好这些方法,就可以了,问题是如何在stagefright上面用。

##StageFright

android 4.2 中音视频处理的代码都集中在 frameworks/av/media 这个目录中,可以看到里面有很多目录,主要关注两个 libmediaplayerservicelibstagefright

先看 MediaPlayerFactory.cpp 其决定了下层用什么播放器来播放音视频文件。我们增加以下代码。

:::cpp
//MediaPlayerFactory.cpp
class StagefrightPlayerFactory :
    public MediaPlayerFactory::IFactory {
    ......

        // APE
        if (ident == 0x2043414D)
        {
            ALOGV("is APE");
            return 1.0;
        
        }

        return 0.0;
    }

    ......
};

这样就保证了使用 stagefright 来播放APE。

接着到 libstagefright 目录。

改造一下 FileSource.cpp 使其可以让我们获得文件路径,因为 APE 解码库需要这个路径。

:::cpp
//FileSource.cpp
    if( offset == 9223372036854775807UL)
    {
        ALOGE("file uri len %d is %s", mFilenameLength, mFileUri);
        memcpy(data, mFileUri, mFilenameLength);
        return OK;	
    }

这里算是 cheat 。因为不想增加方法。

:::cpp
//ACodec.cpp
status_t ACodec::configureCodec(
        const char *mime, const sp<AMessage> &msg) {
......
                }
            }
            err = setupFlacCodec(encoder, numChannels, sampleRate, compressionLevel);
        }
    }else if (!strcasecmp(mime, "audio/ape")){
        return OK;
        
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {

......

:::cpp
//DataSource.cpp
void DataSource::RegisterDefaultSniffers() {
......
    RegisterSniffer(SniffAPE);
......
}

这个 SniffAPE 函数是在 APEExtractor.cpp 里面添加的。

做完这些就差不多了。最后是添加我们的解码器

:::cpp
//APEExtractor.cpp
namespace android{


class APESource : public MediaSource{

    public:
        APESource(
                const sp<DataSource> &dataSource,
                const sp<MetaData> &trackMetadata,
                APE::IAPEDecompress *apeDec);

        virtual status_t start(MetaData *params);
        virtual status_t stop();
        virtual sp<MetaData> getFormat();

        virtual status_t read(
                MediaBuffer **buffer,
                const ReadOptions *options = NULL);

    protected:
        virtual ~APESource();

    private:
        sp<DataSource> mDataSource;
        sp<MetaData> mTrackMetadata;
        int32_t mSampleRate;
        int32_t mNumChannels;
        int32_t mBitsPerSample;
        int32_t mBlockSize;
        off64_t mOffset;
        size_t mSize;
        off64_t mCurrentPos;
        MediaBufferGroup *mGroup;
        bool mInitCheck;
        bool mStarted;
        APE::IAPEDecompress *mApeDec;

        status_t init();

        APESource(const APESource&);
        APESource &operator=(const APESource &);
};

APESource::APESource(
        const sp<DataSource> &dataSource,
        const sp<MetaData> &trackMetadata,
        APE::IAPEDecompress *apeDec)
    : mDataSource(dataSource),
      mTrackMetadata(trackMetadata),
      mInitCheck(false),
      mStarted(false)
{
    ALOGI("APESource::APESource");
    mApeDec = apeDec;
    mInitCheck = init();
}

APESource::~APESource()
{
    ALOGI("APESource::~APESource");
    if(mStarted)
    {
        stop();
    }
}

status_t APESource::start(MetaData *params)
{
    ALOGI("APESource::start");

    mGroup = new MediaBufferGroup;
    mGroup->add_buffer(new MediaBuffer(mBlockSize * mApeDec->GetInfo(APE::APE_INFO_BLOCK_ALIGN) ));

    mCurrentPos = mOffset;

    mStarted = true;
    return OK;
}

status_t APESource::stop()
{
    ALOGI("APESource::stop");

    delete mGroup;
    mGroup = NULL;

    mStarted = false;
    return OK;
}

sp<MetaData> APESource::getFormat()
{
    return mTrackMetadata;
}

status_t APESource::read(
        MediaBuffer **outBuffer, const ReadOptions *options)
{
//	ALOGI("APESource::read");

    *outBuffer = NULL;
    int trueblock;
    int numread = 0;

    int64_t seekTimeUs;
    ReadOptions::SeekMode mode;
    if(options != NULL && options->getSeekTo(&seekTimeUs, &mode))
    {
        //current_block = (currentus / 1000) * SAMPLERATE / 1000
        int64_t pos = seekTimeUs * mSampleRate / 1000000 ;
        mApeDec->Seek((int)pos);

    }

    MediaBuffer *buffer;
    status_t err = mGroup->acquire_buffer(&buffer);
    if (err != OK) {
        return err;
    }

    char *dst = (char *)buffer->data();
    mApeDec->GetData( dst, mBlockSize, &trueblock);
    numread = trueblock * mApeDec->GetInfo(APE::APE_INFO_BLOCK_ALIGN);

//	ALOGE("get trueblock is %d, numread is %d, mBlockSize is %d"
//			, trueblock, numread, mBlockSize );

    if(numread <= 0)
    {
        buffer->release();
        buffer = NULL;
        return ERROR_END_OF_STREAM;
    }

    buffer->meta_data()->setInt64(
            kKeyTime,
            mApeDec->GetInfo(APE::APE_DECOMPRESS_CURRENT_MS) * 1000LL
            );
    buffer->meta_data()->setInt32(kKeyIsSyncFrame, 1);

    *outBuffer = buffer;

/*********************************/
    return OK;
}

status_t APESource::init()
{
    ALOGI("APESource::init");
    mSampleRate = mApeDec->GetInfo(APE::APE_INFO_SAMPLE_RATE);
    mBitsPerSample = mApeDec->GetInfo(APE::APE_INFO_BITS_PER_SAMPLE);
    mNumChannels = mApeDec->GetInfo(APE::APE_INFO_CHANNELS);

    switch(mApeDec->GetInfo(APE::APE_INFO_COMPRESSION_LEVEL))
    {
        case COMPRESSION_LEVEL_FAST:
        case COMPRESSION_LEVEL_NORMAL:
        case COMPRESSION_LEVEL_HIGH:
            mBlockSize = (int32_t)(20 *
                    mApeDec->GetInfo(APE::APE_INFO_SAMPLE_RATE)/1000.0);
            break;
        case COMPRESSION_LEVEL_EXTRA_HIGH:
            mBlockSize = (int32_t)(10 *
                    mApeDec->GetInfo(APE::APE_INFO_SAMPLE_RATE)/1000.0);
            break;
        default:
            mBlockSize = (int32_t)(10 *
                    mApeDec->GetInfo(APE::APE_INFO_SAMPLE_RATE)/1000.0);
            break;
    }


    ALOGV("mSampleRate %d",mSampleRate);
    ALOGV("mBitsPerSample %d",mBitsPerSample);
    ALOGV("mNumChannels %d",mNumChannels);
    ALOGV("mBlockSize %d",mBlockSize);

    mTrackMetadata->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
    mTrackMetadata->setInt32(kKeyMaxInputSize, mBlockSize);

    return OK;
}


APEExtractor::APEExtractor(const sp<DataSource> &source)
    :mDataSource(source)
{
    mInitCheck = init();
}

APEExtractor::~APEExtractor()
{
    if(mApeDec)
    {
        delete mApeDec;
        mApeDec = NULL;
    }
}

sp<MetaData> APEExtractor::getMetaData()
{
    ALOGI("APEExtractor::getMetaData");
    sp<MetaData> meta = new MetaData;
    if(mInitCheck != OK)
    {
        return meta;
    }

    meta->setCString(kKeyMIMEType, "audio/ape");
    return meta;
}

size_t APEExtractor::countTracks()
{
    ALOGI("APEExtractor::countTracks mInitCheck is %d", mInitCheck == OK ? 1 : 0);
    return mInitCheck == OK ? 1 : 0;
}

sp<MediaSource> APEExtractor::getTrack(size_t index)
{
    ALOGI("APEExtractor::getTrack");
    if(mInitCheck != OK || index > 0)
    {
        return NULL;
    }

    return new APESource(
            mDataSource, mTrackMeta,mApeDec);
}

sp<MetaData> APEExtractor::getTrackMetaData(
        size_t index, uint32_t flags)
{
    ALOGI("APEExtractor::getTrackMetaData");
    if(mInitCheck != OK || index > 0)
    {
        return NULL;
    }
    return mTrackMeta;
}


status_t APEExtractor::init()
{
    int nRetVal;
    ALOGV("APEExtractor::init");
    char mFilePath[1024];
    memset(mFilePath, 0, sizeof(mFilePath));
    mDataSource->readAt(9223372036854775807UL, mFilePath, 0);
    ALOGV("file path is %s", mFilePath);

    mApeDec = CreateIAPEDecompress(APE::CAPECharacterHelper::GetUTF16FromANSI(mFilePath), &nRetVal);

    if(mApeDec == NULL || nRetVal != 0)
    {
        ALOGE("APE init error, nRetVal=%d",nRetVal);
        if(mApeDec)
        {
            delete mApeDec;
            mApeDec = NULL;
        }
        return NO_INIT;
    }

    mSampleRate = mApeDec->GetInfo(APE::APE_INFO_SAMPLE_RATE);
    mBitsPerSample = mApeDec->GetInfo(APE::APE_INFO_BITS_PER_SAMPLE);
    mNumChannels = mApeDec->GetInfo(APE::APE_INFO_CHANNELS);
    mChannelMask = CHANNEL_MASK_USE_CHANNEL_ORDER;

    mTrackMeta = new MetaData;

    mTrackMeta->setCString(kKeyMIMEType, "audio/ape");

    mTrackMeta->setInt32(kKeyChannelCount, mNumChannels);
    mTrackMeta->setInt32(kKeyChannelMask, mChannelMask);
    mTrackMeta->setInt32(kKeySampleRate, mSampleRate);
    mTrackMeta->setInt32(kKeyBitRate, mBitsPerSample);

    int64_t durationUs = 1000LL * mApeDec->GetInfo(APE::APE_DECOMPRESS_LENGTH_MS);
    mTrackMeta->setInt64(kKeyDuration, durationUs);

    return OK;
}

bool SniffAPE(
        const sp<DataSource> &source, String8 *mimeType, float *confidence,
        sp<AMessage> *)
{
    uint8_t header[8];
    long ident;

    if(source->readAt(0, header, sizeof(header)) != sizeof(header))
    {
        return false;
    }

    ident = *((long*)header);

    if(ident != 0x2043414D)
    {
        return false;
    }

    *mimeType = "audio/ape";
    *confidence = 0.5;

    return true;

}

}//namespace android