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中加
- LOCAL_STATIC_LIBRARIES += \
- libstagefright_ffmpeg
- LOCAL_STATIC_LIBRARIES += \
- libavformat \
- libavcodec \
- libavdevice \
- libavfilter \
- libavutil
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提供了对数据流的操作方法:
- <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>
下面说说Stagefright和ffmpeg的交互. Stagefright为所有的Extractor提供的原始数据,都被封装成DataSource,DataSource提供了一些基本的功能。
- <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>
通过以上的了解,可以确定有两部分工作:
1. 为ffmepg写一个protocol,用来实现对Stagefright提供的DataSource的访问;
2. 写一个packet管理的类,确保MediaSource访问自己的packet,并将packet封装成MediaBuffer。
定义的类有:
FFMPEGExtractor, 接口类, 除了几个继承来的虚函数是public的, 其它成员都是private.
FFMPEGPktManager, 负责获取并管理从ffmpeg获得的packet.
FFMPEGSource, 负责指定stream的操作。
protocol:
- <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.
- <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>
- <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>
- <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.
- <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>
- <span style="font-size:16px;"> RegisterSniffer(SniffFFMPEG);</span>
- <span style="font-size:16px;">const char *MEDIA_MIMETYPE_CONTAINER_FFMPEG = "video/ffmpeg";</span>
- <span style="font-size:16px;"> if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_FFMPEG))
- return new FFMPEGExtractor(source);
- </span>
到这里,FFMPEGExtractor就完成了。如果想要播放.avi .mov 等文件,还需要改一下MediaScanner方面的东西,后面再说。