解决Android平台移植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,添加该文件:
- /*
- * TypeHelpers.h
- *
- * Copyright 2005 The Android Open Source Project
- *
- */
- #ifndef ANDROID_TYPE_HELPERS_H
- #define ANDROID_TYPE_HELPERS_H
- #include <new>
- #include <stdint.h>
- #include <string.h>
- #include <sys/types.h>
- // ---------------------------------------------------------------------------
- namespace android {
- /*
- * Types traits
- */
- template <typename T> struct trait_trivial_ctor { enum { value = false }; };
- template <typename T> struct trait_trivial_dtor { enum { value = false }; };
- template <typename T> struct trait_trivial_copy { enum { value = false }; };
- template <typename T> struct trait_trivial_assign{ enum { value = false }; };
- template <typename T> struct trait_pointer { enum { value = false }; };
- template <typename T> struct trait_pointer<T*> { enum { value = true }; };
- #define ANDROID_BASIC_TYPES_TRAITS( T ) \
- template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \
- template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \
- template<> struct trait_trivial_copy< T > { enum { value = true }; }; \
- template<> struct trait_trivial_assign< T >{ enum { value = true }; };
- #define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign ) \
- template<> struct trait_trivial_ctor< T > { enum { value = ctor }; }; \
- template<> struct trait_trivial_dtor< T > { enum { value = dtor }; }; \
- template<> struct trait_trivial_copy< T > { enum { value = copy }; }; \
- template<> struct trait_trivial_assign< T >{ enum { value = assign }; };
- template <typename TYPE>
- struct traits {
- enum {
- is_pointer = trait_pointer<TYPE>::value,
- has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value,
- has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value,
- has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value,
- has_trivial_assign = is_pointer || trait_trivial_assign<TYPE>::value
- };
- };
- template <typename T, typename U>
- struct aggregate_traits {
- enum {
- is_pointer = false,
- has_trivial_ctor = traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
- has_trivial_dtor = traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
- has_trivial_copy = traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
- has_trivial_assign = traits<T>::has_trivial_assign && traits<U>::has_trivial_assign
- };
- };
- // ---------------------------------------------------------------------------
- /*
- * basic types traits
- */
- ANDROID_BASIC_TYPES_TRAITS( void );
- ANDROID_BASIC_TYPES_TRAITS( bool );
- ANDROID_BASIC_TYPES_TRAITS( char );
- ANDROID_BASIC_TYPES_TRAITS( unsigned char );
- ANDROID_BASIC_TYPES_TRAITS( short );
- ANDROID_BASIC_TYPES_TRAITS( unsigned short );
- ANDROID_BASIC_TYPES_TRAITS( int );
- ANDROID_BASIC_TYPES_TRAITS( unsigned int );
- ANDROID_BASIC_TYPES_TRAITS( long );
- ANDROID_BASIC_TYPES_TRAITS( unsigned long );
- ANDROID_BASIC_TYPES_TRAITS( long long );
- ANDROID_BASIC_TYPES_TRAITS( unsigned long long );
- ANDROID_BASIC_TYPES_TRAITS( float );
- ANDROID_BASIC_TYPES_TRAITS( double );
- // ---------------------------------------------------------------------------
- /*
- * compare and order types
- */
- template<typename TYPE> inline
- int strictly_order_type(const TYPE& lhs, const TYPE& rhs) {
- return (lhs < rhs) ? 1 : 0;
- }
- template<typename TYPE> inline
- int compare_type(const TYPE& lhs, const TYPE& rhs) {
- return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs);
- }
- /*
- * create, destroy, copy and assign types...
- */
- template<typename TYPE> inline
- void construct_type(TYPE* p, size_t n) {
- if (!traits<TYPE>::has_trivial_ctor) {
- while (n--) {
- new(p++) TYPE;
- }
- }
- }
- template<typename TYPE> inline
- void destroy_type(TYPE* p, size_t n) {
- if (!traits<TYPE>::has_trivial_dtor) {
- while (n--) {
- p->~TYPE();
- p++;
- }
- }
- }
- template<typename TYPE> inline
- void copy_type(TYPE* d, const TYPE* s, size_t n) {
- if (!traits<TYPE>::has_trivial_copy) {
- while (n--) {
- new(d) TYPE(*s);
- d++, s++;
- }
- } else {
- memcpy(d,s,n*sizeof(TYPE));
- }
- }
- template<typename TYPE> inline
- void assign_type(TYPE* d, const TYPE* s, size_t n) {
- if (!traits<TYPE>::has_trivial_assign) {
- while (n--) {
- *d++ = *s++;
- }
- } else {
- memcpy(d,s,n*sizeof(TYPE));
- }
- }
- template<typename TYPE> inline
- void splat_type(TYPE* where, const TYPE* what, size_t n) {
- if (!traits<TYPE>::has_trivial_copy) {
- while (n--) {
- new(where) TYPE(*what);
- where++;
- }
- } else {
- while (n--) {
- *where++ = *what;
- }
- }
- }
- template<typename TYPE> inline
- void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
- if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) {
- d += n;
- s += n;
- while (n--) {
- --d, --s;
- if (!traits<TYPE>::has_trivial_copy) {
- new(d) TYPE(*s);
- } else {
- *d = *s;
- }
- if (!traits<TYPE>::has_trivial_dtor) {
- s->~TYPE();
- }
- }
- } else {
- memmove(d,s,n*sizeof(TYPE));
- }
- }
- template<typename TYPE> inline
- void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
- if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) {
- while (n--) {
- if (!traits<TYPE>::has_trivial_copy) {
- new(d) TYPE(*s);
- } else {
- *d = *s;
- }
- if (!traits<TYPE>::has_trivial_dtor) {
- s->~TYPE();
- }
- d++, s++;
- }
- } else {
- memmove(d,s,n*sizeof(TYPE));
- }
- }
- // ---------------------------------------------------------------------------
- /*
- * a key/value pair
- */
- template <typename KEY, typename VALUE>
- struct key_value_pair_t {
- KEY key;
- VALUE value;
- key_value_pair_t() { }
- key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { }
- key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { }
- key_value_pair_t(const KEY& k) : key(k) { }
- inline bool operator < (const key_value_pair_t& o) const {
- return strictly_order_type(key, o.key);
- }
- };
- template<>
- template <typename K, typename V>
- struct trait_trivial_ctor< key_value_pair_t<K, V> >
- { enum { value = aggregate_traits<K,V>::has_trivial_ctor }; };
- template<>
- template <typename K, typename V>
- struct trait_trivial_dtor< key_value_pair_t<K, V> >
- { enum { value = aggregate_traits<K,V>::has_trivial_dtor }; };
- template<>
- template <typename K, typename V>
- struct trait_trivial_copy< key_value_pair_t<K, V> >
- { enum { value = aggregate_traits<K,V>::has_trivial_copy }; };
- template<>
- template <typename K, typename V>
- struct trait_trivial_assign< key_value_pair_t<K, V> >
- { enum { value = aggregate_traits<K,V>::has_trivial_assign};};
- // ---------------------------------------------------------------------------
- }; // namespace android
- // ---------------------------------------------------------------------------
- #endif // ANDROID_TYPE_HELPERS_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功能(还在调试中)
下面是调整后播放flv(h264+aac)格式(分辨率为320*240,25帧/S)的结果,软解播放达到平均22帧/s
其它wmv,ts流,avi等格式均可播放,但是音质效果需要改进。