您的位置:首页 > 移动开发 > Android开发

1. Android 2.3用ffmpeg替代stagefright自带的swdecoders && 2. Android2.3为FFMPEG编写Extractor

2014-10-23 17:34 801 查看
http://blog.csdn.net/woker/article/details/7631834

FFMPEG源文件放在$TOP/external/ffmpeg中, 编译成几个静态库libavcodec/libavformat/libavutil/...,以后再改成动态库。照网上的,写几个Android.mk和一个av.mk,可以 搞定。写了一个脚本调用configure, 在脚本中做一些配置,把需要的parser, decoder, encoder, demuxer等放进去,其它的disable掉。

写个ffdecoder, 在stagefright中调用ffmpeg.

C code:

    1. 仿照AVCDecoder在libstagefright/codecs中建立相关目录和文件:ffdecoder.cpp, ffdecoder.h, ffdecoder_api.cpp, ffdecoder_api.h

    2. 仿照AVCDecoder实现ffdecoder的函数:ffdecoder(), ~ffdecoder(), start(), stop(), getFormat(), read(), releaseFrames(),  signalBufferReturned().  start()中调用ffmpeg_init(); stop()中调用ffmpeg_deinit(); read()中调用ffmpeg_decode(bufptr, size, other output data), 若framefinished,
传出yuv的MediaBuffer.

    3. ffdecoder_api.cpp中 #include "ffmpeg headers" 时,需要加extern "C" {}.

    3.1) ffmpeg_init(): av_register_all(); 需要根据传入的mime设置ffmpeg中对应的CodecID, width, height, extradata, extradata_size都从参数传入. pCodecCtx中需要配置的参数有codec_id, time_base, coded_width, coded_height, codec_type, thread_count, opaque, flags2, extradata, extradata_size; 
avcodec_find_decoder(); avcodec_open(); avcodec_alloc_frame().

    3.2) ffmpeg_deinit(): av_free_packet(); avcodec_close(); av_free(pFrame).

    3.3) ffmpeg_decode(): 根据传入参数来设置packet的data和size参数; avcodec_decode_video2(); if(framefinished){传出yuv数据地址和linesize}

    4. libstagefright/OMXCodec.cpp中AVCDecoder都改成ffdecoder. kDecoderInfo[] 中可以更改不同格式

Makefile: 

    1. libstagefright/codecs/ffmpeg中的Android.mk中的 LOCAL_SRC_FILES, LOCAL_MODULE, LOCAL_MODULE_TAGS, LOCAL_C_INCLUDES需要设置, 最后BUILD_STATIC_LIBRARY.

    2. libstagefright/Android.mk中加

[cpp] view
plaincopy

LOCAL_STATIC_LIBRARIES += \  

     libstagefright_ffmpeg  

LOCAL_STATIC_LIBRARIES += \  

    libavformat \  

    libavcodec \  

    libavdevice \  

    libavfilter \  

    libavutil  

prelink-map:

    ffmpeg放入stagefright后太大了, 要改一下prelink,  把OpenCore和PV相关的库关掉,把stagefright放到一个大的空间中。
http://blog.csdn.net/woker/article/details/7659290
Android能解析的文件格式太少,自己写一个Extractor来实现其它格式( .avi .mov .rmvb .rm .flv ...)的解析。

    大体说一下ffmpeg内部的数据解析。ffmpeg内部对外部数据(文件,网络流等)的操作是通过选择合适的protocol来操作的,例如数据对象是文件,它会选择ff_file_protocol来操作。所有的protocol都是在av_register_all里面注册的(也可以在其它地方用ffurl_register_protocol()来注册,本文实现的ff_android_protocol就是在外部注册的)。每个protocol提供了对数据流的操作方法:

[cpp] view
plaincopy

<span style="font-size:16px;">typedef struct URLProtocol {  

    const char *name;  

    int (*url_open)(URLContext *h, const char *url, int flags);  

    int (*url_read)(URLContext *h, unsigned char *buf, int size);  

    int (*url_write)(URLContext *h, const unsigned char *buf, int size);  

    int64_t (*url_seek)(URLContext *h, int64_t pos, int whence);  

    int (*url_close)(URLContext *h);  

    struct URLProtocol *next;  

    int (*url_read_pause)(URLContext *h, int pause);  

    int64_t (*url_read_seek)(URLContext *h, int stream_index,  

                             int64_t timestamp, int flags);  

    int (*url_get_file_handle)(URLContext *h);  

    int priv_data_size;  

    const AVClass *priv_data_class;  

    int flags;  

    int (*url_check)(URLContext *h, int mask);  

} URLProtocol;  

</span>  

需要哪些功能就实现哪些接口。实际上,ffmpeg是根据数据名字来确定所选择的protocol, 如果名字是file:*,它就会用ff_file_protocol, 如果是http:*,它就会调用ff_http_protocol, 当然如果是android:*,它也会去找ff_android_protocol。以上就是ffmpeg对外部数据的访问。ffmpeg采集到原始数据后,会经过一系列操作,最后为解码器提供一个很方便的接口av_read_frame()。每次调用av_read_frame()都会返回一个packet,packet内封装了一个frame所需的原始数据。但是每次返回的packet内的数据不确定是哪个stream的,必须根据packet内的stream_index来确定。ffmpeg里面好像没有提供某个函数直接访问指定的stream(或者我没找到?)。

    下面说说Stagefright和ffmpeg的交互. Stagefright为所有的Extractor提供的原始数据,都被封装成DataSource,DataSource提供了一些基本的功能。

[cpp] view
plaincopy

<span style="font-size:16px;">    virtual ssize_t readAt(off_t offset, void *data, size_t size) = 0;  

    virtual status_t getSize(off_t *size);  

</span>  

    另外Stagefright内部是将每个stream封装成一个MediaSource,每个MediaSource相对独立地访问数据,数据被封装成MediaBuffer。

通过以上的了解,可以确定有两部分工作:

    1. 为ffmepg写一个protocol,用来实现对Stagefright提供的DataSource的访问;

    2. 写一个packet管理的类,确保MediaSource访问自己的packet,并将packet封装成MediaBuffer。

  定义的类有:

    FFMPEGExtractor, 接口类, 除了几个继承来的虚函数是public的, 其它成员都是private. 

    FFMPEGPktManager, 负责获取并管理从ffmpeg获得的packet.

    FFMPEGSource, 负责指定stream的操作。

  protocol:

[cpp] view
plaincopy

<span style="font-size:16px;">URLProtocol ff_android_protocol  = {   

    "android",  

    android_open, // 不用干啥事,Stagefright都给我们准备好了,把new出来的AVAndroidStream(保存数据的结构体)存入URLContext.priv_data中。  

    android_read, // 调用DataSource的reatAt()。Offset要保存在AVAndroidStream中。  

    android_write, // 不支持。报错。  

    android_seek, // 改Offset。  

    android_close, // 释放资源  

    NULL,  

    NULL,  

    NULL,  

    android_get_handle, // 返回URLContext.priv_data  

    0,  

    NULL,  

    0,  

    NULL  

};</span>  

    下面具体描述一下各个类。

    1).  FFMPEGPktManager. 

[cpp] view
plaincopy

<span style="font-size:16px;">struct FFMPEGPktManager {  

    FFMPEGPktManager(AVFormatContext *pFormatCtx); // 根据pFormatCtx->nb_streams创建Vector,用以缓存不同stream的packet。  

  

    MediaBuffer* readPacket(unsigned int streamIndex); // 将stream[i]缓存的第一个packet封装并返回,若不存在则反复调用av_read_frame。  

    void freeStreamPackets(unsigned int streamIndex); // 释放stream[i]的全部缓存packet。  

  

    AVPacket* getPacketInfo(uint32_t streamIndex, uint32_t numOffset); // 返回stream[i]的第n个packet。  

   

protected:  

    ~FFMPEGPktManager();  

  

private:  

    Mutex mLock; // 由于有多个stream同时访问,需要加锁保护  

  

    AVFormatContext *mFC;  

    Vector< Vector<AVPacket> > mPacketArray; // 保存缓冲packet  

  

    int retrievePacket(unsigned int streamIndex, int cnt); // 反复调用av_read_frame直到获取n个stream[i]的packet。  

    MediaBuffer *createMediaBuffer(AVPacket* pkt); // 根据AVPacket封装成MediaBuffer。注意buffer->meta_data()->set kKeyTime kKeyIsSyncFrame  

   

    FFMPEGPktManager(const FFMPEGPktManager &);  

    FFMPEGPktManager &operator=(const FFMPEGPktManager &);  

};  

</span>  

    2).  FFMPEGSource

[cpp] view
plaincopy

<span style="font-size:16px;">struct FFMPEGSource : public MediaSource {  

    FFMPEGSource(  

            const sp<FFMPEGExtractor> &extractor,   

            FFMPEGPktManager * pktManager, // 不同的FFMPEGSource需要使用同一个pktManager。  

            sp<MetaData> trackMeta, size_t streamIndex);  

  

    virtual status_t start(MetaData *params); // do nothing  

    virtual status_t stop(); // do nothing  

  

    virtual sp<MetaData> getFormat(); // return mTrackMeta  

  

    virtual status_t read( // 从mPktManager里获取一个packet的MediaBuffer  

            MediaBuffer **buffer, const ReadOptions *options);  

  

protected:  

    ~FFMPEGSource();  

  

private:  

    sp<FFMPEGExtractor> mExtractor;  

    FFMPEGPktManager * mPktManager;  

    size_t mStreamIndex;  

    sp<MetaData> mTrackMeta;  

    bool mIsAVC;  

  

    FFMPEGSource(const FFMPEGSource &);  

    FFMPEGSource &operator=(const FFMPEGSource &);  

};  

</span>  

    3). FFMPEGExtractor

[cpp] view
plaincopy

<span style="font-size:16px;">class FFMPEGExtractor : public MediaExtractor {  

public:  

    // Extractor assumes ownership of "source".  

    FFMPEGExtractor(const sp<DataSource> &source); // 初始化ffmpeg,av_find_stream_info获取信息。解析每个stream,获取其metadata。创建FFMPEGPktManager。  

  

    virtual size_t countTracks(); // mTracks.size()  

    virtual sp<MediaSource> getTrack(size_t index); // new FFMPEGSource  

    virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags); // 创建缩略图并返回track相应的metadata  

  

    virtual sp<MetaData> getMetaData(); // kKeyMIMEType 指定MEDIA_MIMETYPE_CONTAINER_FFMPEG  

  

protected:  

    virtual ~FFMPEGExtractor();  

  

private:  

    sp<DataSource> mDataSource;  

    FFMPEGPktManager *mPktManager;  

  

    AVFormatContext *mFormatCtx;  

  

    sp<MetaData> mIndexMap;  

    int32_t trackIndex2StreamIndex(int32_t trackIndex); // trackIndex和mFormatContex里面保存的stream序号可能不同,需要转换  

  

    struct TrackInfo {  

        unsigned long mTrackNum;  

        sp<MetaData> mMeta;  

    };  

    Vector<TrackInfo> mTracks; // 每条track的信息  

  

    bool mExtractedThumbnails;  

    void findThumbnails();  

  

    FFMPEGExtractor(const FFMPEGExtractor &);  

    FFMPEGExtractor &operator=(const FFMPEGExtractor &);  

};  

</span>  

    接下来需要是FFMPEGExtractor生效。Stagefright是通过DataSource::sniff() 来获取所需的Extractor的。它遍历所有注册了的Sniff,获得得分(confidence)最高的一个。所以我们自己写一个SniffFFMPEG,并将confidence设成1.0.

[cpp] view
plaincopy

<span style="font-size:16px;">bool SniffFFMPEG(  

        const sp<DataSource> &source, String8 *mimeType, float *confidence,  

        sp<AMessage> *) {  

  

    LOGD("SniffFFMPEG: confidence force to be 1.0");  

  

    // QGC: ffmpeg force to be high confidence  

    *mimeType = MEDIA_MIMETYPE_CONTAINER_FFMPEG;  

    *confidence = 1.0f;  

    return true;  

  

}  

</span>  

并在DataSource::RegisterDefaultSniffers()中注册:

[cpp] view
plaincopy

<span style="font-size:16px;">    RegisterSniffer(SniffFFMPEG);</span>  

在MediaDefs.h/cpp中要为ffmpeg添加自己的mime:

[cpp] view
plaincopy

<span style="font-size:16px;">const char *MEDIA_MIMETYPE_CONTAINER_FFMPEG = "video/ffmpeg";</span>  

在MediaExtractor.cpp中将MEDIA_MIMETYPE_CONTAINER_FFMPEG绑定到FFMPEGExtractor上:

[cpp] view
plaincopy

<span style="font-size:16px;">    if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_FFMPEG))   

        return new FFMPEGExtractor(source);  

</span>  

到这里,FFMPEGExtractor就完成了。如果想要播放.avi .mov 等文件,还需要改一下MediaScanner方面的东西,后面再说。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: