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

这篇文档分享给那些在Android上做多媒体开发的网友!

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,添加该文件:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/*
*  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功能


•3.1 要实现getCurrentPosition(), 因为拖动时传过来的是时间millisecond
•3.2 把milli second转化成timestamp掉用av_seek...
•3.3 在IDecoder类里面加入flush函数,实际上就是seek的时候把队列里的AVPacket清除掉
•3.4 通过av_gettime()函数作为外部参考时钟,实现音视频同步,需要hurryup和实现drop frame的处理。

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

  

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值