本文中的代码来自于https://github.com/churnlabs/android-ffmpeg-sample,更多的可以参考这个项目代码。我会在代码中加一些自己的注释。感谢作者churnlabs给我们提供这么好的例子以供我们学习。
在Android的一些系统层应用开发大多数是采用jni的方式调用,另外对于一些比较吃CPU或者处理逻辑比较复杂的程序,也可以考虑使用jni方式来封装。可以提高程序的执行效率。
本文涉及到以下几个方面:
1 将3gp文件push到模拟机器的sdcard中
2 写jni代码,内部调用ffmpeg库的方法,编译jni库
3 loadLibrary生成的库,然后撰写相应的java代码
4 执行程序,并查看最终运行结果。
最终程序的显示效果如下:
1 使用eclipse的DDMS工具,将vid.3pg push到sdcard中
2 撰写相应的jni文件
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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
|
/*
* Copyright 2011 - Churn Labs, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* This is mostly based off of the FFMPEG tutorial:
* http://dranger.com/ffmpeg/
* With a few updates to support Android output mechanisms and to update
* places where the APIs have shifted.
*/
#include <jni.h>
#include <string.h>
#include <stdio.h>
#include <android/log.h>
#include <android/bitmap.h>
//包含ffmpeg库头文件,这些文件都直接方案jni目录下
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#define LOG_TAG "FFMPEGSample"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
/* Cheat to keep things simple and just use some globals. */
//全局对象
AVFormatContext *pFormatCtx;
AVCodecContext *pCodecCtx;
AVFrame *pFrame;
AVFrame *pFrameRGB;
int
videoStream;
/*
* Write a frame worth of video (in pFrame) into the Android bitmap
* described by info using the raw pixel buffer. It's a very inefficient
* draw routine, but it's easy to read. Relies on the format of the
* bitmap being 8bits per color component plus an 8bit alpha channel.
*/
//定义的静态方法,将某帧AVFrame在Android的Bitmap中绘制
static
void
fill_bitmap(AndroidBitmapInfo* info,
void
*pixels, AVFrame *pFrame)
{
uint8_t *frameLine;
int
yy;
for
(yy = 0; yy < info->height; yy++) {
uint8_t* line = (uint8_t*)pixels;
frameLine = (uint8_t *)pFrame->data[0] + (yy * pFrame->linesize[0]);
int
xx;
for
(xx = 0; xx < info->width; xx++) {
int
out_offset = xx * 4;
int
in_offset = xx * 3;
line[out_offset] = frameLine[in_offset];
line[out_offset+1] = frameLine[in_offset+1];
line[out_offset+2] = frameLine[in_offset+2];
line[out_offset+3] = 0;
}
pixels = (
char
*)pixels + info->stride;
}
}
//定义java回调函数,相当与 com.churnlabs中的ffmpegsample中的MainActivity类中的openFile方法。
void
Java_com_churnlabs_ffmpegsample_MainActivity_openFile(JNIEnv * env, jobject
this
)
{
int
ret;
int
err;
int
i;
AVCodec *pCodec;
uint8_t *buffer;
int
numBytes;
//注册所有的函数
av_register_all();
LOGE(
"Registered formats"
);
//打开sdcard中的vid.3gp文件
err = av_open_input_file(&pFormatCtx,
"file:/sdcard/vid.3gp"
, NULL, 0, NULL);
LOGE(
"Called open file"
);
if
(err!=0) {
LOGE(
"Couldn't open file"
);
return
;
}
LOGE(
"Opened file"
);
if
(av_find_stream_info(pFormatCtx)<0) {
LOGE(
"Unable to get stream info"
);
return
;
}
videoStream = -1;
//定义设置videoStream
for
(i=0; i<pFormatCtx->nb_streams; i++) {
if
(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {
videoStream = i;
break
;
}
}
if
(videoStream==-1) {
LOGE(
"Unable to find video stream"
);
return
;
}
LOGI(
"Video stream is [%d]"
, videoStream);
//定义编码类型
pCodecCtx=pFormatCtx->streams[videoStream]->codec;
//获取解码器
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if
(pCodec==NULL) {
LOGE(
"Unsupported codec"
);
return
;
}
//使用特定的解码器打开
if
(avcodec_open(pCodecCtx, pCodec)<0) {
LOGE(
"Unable to open codec"
);
return
;
}
//分配帧空间
pFrame=avcodec_alloc_frame();
//分配RGB帧空间
pFrameRGB=avcodec_alloc_frame();
LOGI(
"Video size is [%d x %d]"
, pCodecCtx->width, pCodecCtx->height);
//获取大小
numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height);
分配空间
buffer=(uint8_t *)av_malloc(numBytes*
sizeof
(uint8_t));
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
pCodecCtx->width, pCodecCtx->height);
}
//定义java回调函数,相当与 com.churnlabs中的ffmpegsample中的MainActivity类中的drawFrame方法。
void
Java_com_churnlabs_ffmpegsample_MainActivity_drawFrame(JNIEnv * env, jobject
this
, jstring bitmap)
{
AndroidBitmapInfo info;
void
* pixels;
int
ret;
int
|