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

解决Android平台移植ffmpeg的一揽子问题

2013-01-01 16:49 441 查看
Copy from http://blog.csdn.net/moruite/archive/2011/04/06/6305944.aspx

IT行业是一个踩在巨人肩膀上前进的行业,否则做的事情不一定有意义,所以我也是基于havlenapetr移植的ffmpeg基础上做了些改进,他做的主要贡献有:

1. 移植了ffmpeg并将与媒体相关的结构体在java层重新进行了封装,方便应用程序在java层直接操作ffmpeg API,如各种媒体格式转码及播放,如图1所示

2. 模仿Android的MediaPlayer类实现了ffmpeg的播放接口,如setDataSource(),setDisplay(),start(), stop(),pause()等,缺点是没有实现seek功能。

3. 实现了一个简单播放器功能,抛弃掉ffmpeg自带的ffplay播放器,他重新实现了音视频的分离播放和同步处理等播放器应有的功能。




基于Android移植ffmpeg的意义在于:

1.解决了Android媒体框架OpenCore的多媒体支持不足,虽然说Android平台的软解功耗大,但是从PC机的发展历史看,Android的视频处理以后也会走以硬解为主,软解为辅的路线。

2. 解决Android平台直播的问题,虽然Android支持RTSP/RTP的直播方案,但是这种方案主要是普遍用在电信设备上,基于互联网的海量视频服务提供者还是以http live streaming方案为主,测试时可以用ffmpeg将直播流打包成分段的ts流(如10秒钟),然后组织成m3u8文件实现完整的直播方案,而且互联网的直播内容还有很多是基于mms协议的,视频格式是wmv,要聚集这些内容都是离不开ffmpeg软解的。

IT行业是一个踩在巨人肩膀上前进的行业,否则做的事情不一定有意义,所以我也是基于havlenapetr移植的ffmpeg基础上做了些改进,他做的主要贡献有:

1. 移植了ffmpeg并将与媒体相关的结构体在java层重新进行了封装,方便应用程序在java层直接操作ffmpeg API,如各种媒体格式转码及播放,如图1所示

2. 模仿Android的MediaPlayer类实现了ffmpeg的播放接口,如setDataSource(),setDisplay(),start(), stop(),pause()等,缺点是没有实现seek功能。

3. 实现了一个简单播放器功能,抛弃掉ffmpeg自带的ffplay播放器,他重新实现了音视频的分离播放和同步处理等播放器应有的功能。

图1 ffmpeg的java层封装

基于Android移植ffmpeg的意义在于:

1.解决了Android媒体框架OpenCore的多媒体支持不足,虽然说Android平台的软解功耗大,但是从PC机的发展历史看,Android的视频处理以后也会走以硬解为主,软解为辅的路线。

2. 解决Android平台直播的问题,虽然Android支持RTSP/RTP的直播方案,但是这种方案主要是普遍用在电信设备上,基于互联网的海量视频服务提供者还是以http live streaming方案为主,测试时可以用ffmpeg将直播流打包成分段的ts流(如10秒钟),然后组织成m3u8文件实现完整的直播方案,而且互联网的直播内容还有很多是基于mms协议的,视频格式是wmv,要聚集这些内容都是离不开ffmpeg软解的。

移植步骤:

1. 下载havlenaptr移植的ffmpeg(https://github.com/havlenapetr/FFMpeg/zipball/debug).

2. 基于ndk编译下载的ffmpeg,出现的编译问题主要是文件的存放路径不对,修改jni目录下的Android.mk文件,增加头文件目录$(LOCAL_PATH)/../include/android,修改Vector.h文件为:

#include <cutils/log.h>

#include <utils/VectorImpl.h>

#include <utils/TypeHelpers.h>

3.utils目录下缺少TypeHelpers.h,添加该文件:

?
4.编译中出现 make: *** No rule to make target `/cygdrive/e/workspace/myffmpeg/obj/local/armeabi/libjniaudio.so', needed by `/cygdrive/e/workspace/myffmpeg/obj/local/armeabi/libmediaplayer.a'. Stop. 需要把下载的ffmpeg中的libjniaudio.so和libjnivideo.so放到错误中指定的目录下。

5. 编译成功后运行的结果如下:





当点击媒体文件播放时,发现画面显示不正常,如下所示:



调试后发现MediaPlayer.cpp中没有像Android自带播放器一样实现OnVideoSizeChangedListener的回调函数,当播放视图(SurfaceView)创建后没有根据实际播放的视频大小做调整,而且画面没有居中显示,所以我在此基础上做了如下改进

1. 实现OnVideoSizeChangedListener接口,通知播放界面调整大小

2. 播放界面剧中显示,等比率缩放视频大小。

3. 实现Seek功能

•3.1 要实现getCurrentPosition(), 因为拖动时传过来的是时间millisecond

•3.2 把milli second转化成timestamp掉用av_seek...

•3.3 在IDecoder类里面加入flush函数,实际上就是seek的时候把队列里的***Packet清除掉

•3.4 通过av_gettime()函数作为外部参考时钟,实现音视频同步,需要hurryup和实现drop frame的处理。

下面是调整后播放flv(h264+aac)格式(分辨率为320*240,25帧/S)的结果,软解播放达到平均22帧/s







其它wmv,ts流,avi等格式均可播放,但是音质效果需要改进。

最近的进展情况:

1. 实现ffmpeg播放的回调接口,可以通知界面刷新状态,如去掉缓冲提示等。

public interface IFFMpegPlayer {

public void onPlay();

public void onStop();

public void onRelease();

public void onError(String msg, Exception e);

}

2. 定制话播放控制栏界面,效果如下:



替换ffmpeg自带的控制栏界面

如何支持http网络播放,需要config.h里enable相关选项,我的配置参考如下:

?
FFMpeg自带的播放器ffplay对音视频的处理方法总结:

1. 如果声音是CBR的(也就是固定码率),就以音频的时间戳为基准

2. 如果视频时CBR,就以视频的时间戳为基准

3. 如果都是VBR的,就参考外部时钟,通过av_gettime()获取微妙集的时钟。

ffmpeg通过***Stream结构的time_base(有理数由分子和分母组成)可以获取一个参考时间单位,所有音视频流的timestamp都是基于这个时间单位顺序递增,比如time_base.num=1,time_base.den=90000,表示把1秒分成90000等份,音视频的pts和dts值就表示有多少个1/90000等份,更简单一点假设time_base.num=1,time_base.den=1000,就表示1秒分成1000等份,相当于1毫秒,那时间戳就表示是以毫秒为单位的,再做音视频处理时候,如果解码的速度比按照时间戳显示的速度快,那就简单不用丟帧(Drop
Frame)处理,当解码速度很慢时(比如手机设备),就需要丢帧处理,是每两帧丟一帧数据,还是每3帧丟掉一帧数据,就需要根据延时显示程度来计算丢帧的比率

havlenapetr最新的ffmpeg可以直接编译通过,不过有个bug,就是播放完毕后,video_decoder线程不能退出,主要原因是阻塞在packet队列的Queue->get函数里,这里处理逻辑需要考虑两种情况:

1. ***Packet队列里没有音视频帧数据时,解码线程挂起进入等待状态,有新的***Packet加入队列时激活解码线程

2. 如果文件读取到末尾,Queue->get直接返回-1,解码线程发现队列空时直接退出

转帖:http://www.cnblogs.com/binsys/articles/2073538.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: