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

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 plain copy
  1. LOCAL_STATIC_LIBRARIES += \  
  2.      libstagefright_ffmpeg  
  3. LOCAL_STATIC_LIBRARIES += \  
  4.     libavformat \  
  5.     libavcodec \  
  6.     libavdevice \  
  7.     libavfilter \  
  8.     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 plain copy
  1. <span style="font-size:16px;">typedef struct URLProtocol {  
  2.     const char *name;  
  3.     int (*url_open)(URLContext *h, const char *url, int flags);  
  4.     int (*url_read)(URLContext *h, unsigned char *buf, int size);  
  5.     int (*url_write)(URLContext *h, const unsigned char *buf, int size);  
  6.     int64_t (*url_seek)(URLContext *h, int64_t pos, int whence);  
  7.     int (*url_close)(URLContext *h);  
  8.     struct URLProtocol *next;  
  9.     int (*url_read_pause)(URLContext *h, int pause);  
  10.     int64_t (*url_read_seek)(URLContext *h, int stream_index,  
  11.                              int64_t timestamp, int flags);  
  12.     int (*url_get_file_handle)(URLContext *h);  
  13.     int priv_data_size;  
  14.     const AVClass *priv_data_class;  
  15.     int flags;  
  16.     int (*url_check)(URLContext *h, int mask);  
  17. } URLProtocol;  
  18. </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 plain copy
  1. <span style="font-size:16px;">    virtual ssize_t readAt(off_t offset, void *data, size_t size) = 0;  
  2.     virtual status_t getSize(off_t *size);  
  3. </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 plain copy
  1. <span style="font-size:16px;">URLProtocol ff_android_protocol  = {   
  2.     "android",  
  3.     android_open, // 不用干啥事,Stagefright都给我们准备好了,把new出来的AVAndroidStream(保存数据的结构体)存入URLContext.priv_data中。  
  4.     android_read, // 调用DataSource的reatAt()。Offset要保存在AVAndroidStream中。  
  5.     android_write, // 不支持。报错。  
  6.     android_seek, // 改Offset。  
  7.     android_close, // 释放资源  
  8.     NULL,  
  9.     NULL,  
  10.     NULL,  
  11.     android_get_handle, // 返回URLContext.priv_data  
  12.     0,  
  13.     NULL,  
  14.     0,  
  15.     NULL  
  16. };</span>  
    下面具体描述一下各个类。

    1).  FFMPEGPktManager. 

[cpp]  view plain copy
  1. <span style="font-size:16px;">struct FFMPEGPktManager {  
  2.     FFMPEGPktManager(AVFormatContext *pFormatCtx); // 根据pFormatCtx->nb_streams创建Vector,用以缓存不同stream的packet。  
  3.   
  4.     MediaBuffer* readPacket(unsigned int streamIndex); // 将stream[i]缓存的第一个packet封装并返回,若不存在则反复调用av_read_frame。  
  5.     void freeStreamPackets(unsigned int streamIndex); // 释放stream[i]的全部缓存packet。  
  6.   
  7.     AVPacket* getPacketInfo(uint32_t streamIndex, uint32_t numOffset); // 返回stream[i]的第n个packet。  
  8.    
  9. protected:  
  10.     ~FFMPEGPktManager();  
  11.   
  12. private:  
  13.     Mutex mLock; // 由于有多个stream同时访问,需要加锁保护  
  14.   
  15.     AVFormatContext *mFC;  
  16.     Vector< Vector<AVPacket> > mPacketArray; // 保存缓冲packet  
  17.   
  18.     int retrievePacket(unsigned int streamIndex, int cnt); // 反复调用av_read_frame直到获取n个stream[i]的packet。  
  19.     MediaBuffer *createMediaBuffer(AVPacket* pkt); // 根据AVPacket封装成MediaBuffer。注意buffer->meta_data()->set kKeyTime kKeyIsSyncFrame  
  20.    
  21.     FFMPEGPktManager(const FFMPEGPktManager &);  
  22.     FFMPEGPktManager &operator=(const FFMPEGPktManager &);  
  23. };  
  24. </span>  
    2).  FFMPEGSource
[cpp]  view plain copy
  1. <span style="font-size:16px;">struct FFMPEGSource : public MediaSource {  
  2.     FFMPEGSource(  
  3.             const sp<FFMPEGExtractor> &extractor,   
  4.             FFMPEGPktManager * pktManager, // 不同的FFMPEGSource需要使用同一个pktManager。  
  5.             sp<MetaData> trackMeta, size_t streamIndex);  
  6.   
  7.     virtual status_t start(MetaData *params); // do nothing  
  8.     virtual status_t stop(); // do nothing  
  9.   
  10.     virtual sp<MetaData> getFormat(); // return mTrackMeta  
  11.   
  12.     virtual status_t read( // 从mPktManager里获取一个packet的MediaBuffer  
  13.             MediaBuffer **buffer, const ReadOptions *options);  
  14.   
  15. protected:  
  16.     ~FFMPEGSource();  
  17.   
  18. private:  
  19.     sp<FFMPEGExtractor> mExtractor;  
  20.     FFMPEGPktManager * mPktManager;  
  21.     size_t mStreamIndex;  
  22.     sp<MetaData> mTrackMeta;  
  23.     bool mIsAVC;  
  24.   
  25.     FFMPEGSource(const FFMPEGSource &);  
  26.     FFMPEGSource &operator=(const FFMPEGSource &);  
  27. };  
  28. </span>  
    3). FFMPEGExtractor
[cpp]  view plain copy
  1. <span style="font-size:16px;">class FFMPEGExtractor : public MediaExtractor {  
  2. public:  
  3.     // Extractor assumes ownership of "source".  
  4.     FFMPEGExtractor(const sp<DataSource> &source); // 初始化ffmpeg,av_find_stream_info获取信息。解析每个stream,获取其metadata。创建FFMPEGPktManager。  
  5.   
  6.     virtual size_t countTracks(); // mTracks.size()  
  7.     virtual sp<MediaSource> getTrack(size_t index); // new FFMPEGSource  
  8.     virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags); // 创建缩略图并返回track相应的metadata  
  9.   
  10.     virtual sp<MetaData> getMetaData(); // kKeyMIMEType 指定MEDIA_MIMETYPE_CONTAINER_FFMPEG  
  11.   
  12. protected:  
  13.     virtual ~FFMPEGExtractor();  
  14.   
  15. private:  
  16.     sp<DataSource> mDataSource;  
  17.     FFMPEGPktManager *mPktManager;  
  18.   
  19.     AVFormatContext *mFormatCtx;  
  20.   
  21.     sp<MetaData> mIndexMap;  
  22.     int32_t trackIndex2StreamIndex(int32_t trackIndex); // trackIndex和mFormatContex里面保存的stream序号可能不同,需要转换  
  23.   
  24.     struct TrackInfo {  
  25.         unsigned long mTrackNum;  
  26.         sp<MetaData> mMeta;  
  27.     };  
  28.     Vector<TrackInfo> mTracks; // 每条track的信息  
  29.   
  30.     bool mExtractedThumbnails;  
  31.     void findThumbnails();  
  32.   
  33.     FFMPEGExtractor(const FFMPEGExtractor &);  
  34.     FFMPEGExtractor &operator=(const FFMPEGExtractor &);  
  35. };  
  36. </span>  

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

[cpp]  view plain copy
  1. <span style="font-size:16px;">bool SniffFFMPEG(  
  2.         const sp<DataSource> &source, String8 *mimeType, float *confidence,  
  3.         sp<AMessage> *) {  
  4.   
  5.     LOGD("SniffFFMPEG: confidence force to be 1.0");  
  6.   
  7.     // QGC: ffmpeg force to be high confidence  
  8.     *mimeType = MEDIA_MIMETYPE_CONTAINER_FFMPEG;  
  9.     *confidence = 1.0f;  
  10.     return true;  
  11.   
  12. }  
  13. </span>  
并在DataSource::RegisterDefaultSniffers()中注册:
[cpp]  view plain copy
  1. <span style="font-size:16px;">    RegisterSniffer(SniffFFMPEG);</span>  
在MediaDefs.h/cpp中要为ffmpeg添加自己的mime:
[cpp]  view plain copy
  1. <span style="font-size:16px;">const char *MEDIA_MIMETYPE_CONTAINER_FFMPEG = "video/ffmpeg";</span>  
在MediaExtractor.cpp中将MEDIA_MIMETYPE_CONTAINER_FFMPEG绑定到FFMPEGExtractor上:
[cpp]  view plain copy
  1. <span style="font-size:16px;">    if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_FFMPEG))   
  2.         return new FFMPEGExtractor(source);  
  3. </span>  

到这里,FFMPEGExtractor就完成了。如果想要播放.avi .mov 等文件,还需要改一下MediaScanner方面的东西,后面再说。




  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值