FFmpeg For Android (三) 移植main函数到安卓上执行ffmpeg命令

来源:互联网 发布:怎样评价井柏然 知乎 编辑:程序博客网 时间:2024/06/11 18:20

根据上一篇文章《FFmpeg For Android (二) Ubuntu下编译FFmpeg源码》我们熟悉了如何在Ubuntu下编译FFmpeg, 但我们却不知道有何用…
本篇将讲解 移植ffmpeg 的main函数到安卓上 直接执行ffmpeg命令
现在你将可以做很多有趣的app 比如 支持几乎全格式的视频、音频转换、剪切、合并、水印、混合、视频转gif 获取某一帧图片(则获取支持全格式视频的缩略图)等。。。
有木有开始激动了???

1.重新编译FFmpeg, 开启configure相关参数

回到build_andorid.sh文件 编辑–enable一些相关配置
如下(注意把路径换成你系统的路径 并检查路径是否存在 (已经在上一篇说了 这篇不再详细讲解)

NDK=/home/ubuntu/桌面/Android/NDK/android-ndk-r10eSYSROOT=$NDK/platforms/android-9/arch-arm/TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64function build_android{./configure \--prefix=/home/ubuntu/桌面/Android/FFmpeg/ffmpeg-3.2/android/arm \--enable-neon \--enable-hwaccel=h264_vaapi \--enable-hwaccel=h264_vaapi \--enable-hwaccel=h264_dxva2 \--enable-hwaccel=mpeg4_vaapi \--enable-hwaccels \--enable-shared \--enable-jni \--enable-mediacodec \--disable-static \--disable-doc \--enable-ffmpeg \--disable-ffplay \--disable-ffprobe \--disable-ffserver \--enable-avdevice \--disable-doc \--disable-symver \--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \--target-os=linux \--arch=arm \--enable-cross-compile \--sysroot=$SYSROOT \--extra-cflags="-Os -fpic $ADDI_CFLAGS" \--extra-ldflags="$ADDI_LDFLAGS" \$ADDITIONAL_CONFIGURE_FLAGmake cleanmake -j4make install}ADDI_CFLAGS="-marm"build_android

然后保存并开始编译FFmpeg
编译好了 得到的so文件变多了, 如图
/home/ubuntu/桌面/Android/FFmpeg/ffmpeg-3.2/android/arm下

2.编写Android C/C++程序

这里 肯定用jni了…
win下也需要安装NDK环境 教程太多太简单 这里不再讲解 请自行解决(能看到这个文章的人一般都装好了 win版的NDK )…
我们回到Windows 打开Android Studio新建一个工程MyFFmpeg
开启Android Studio 对C/C++支持
编辑local.properties文件,添加ndk路径(请按你windows下的ndk实际路径 不要照抄)

ndk.dir=D\:\\Android\\sdk\\ndk-bundlesdk.dir=D\:\\Android\\sdk

编辑gradle.properties文件
末尾添加

android.useDeprecatedNdk = true

然后rebuild project一下
在MyFFmpeg\app\新建一个该路径的文件夹 jnicode\jni
在jni文件夹新建 prebuilt 文件夹
回到Ubuntu系统 进入
/home/ubuntu/桌面/Android/FFmpeg/ffmpeg-3.2
把已解压并且已编译好的FFmpeg so库一起压缩 , 就是直接压缩ffmpeg-3.2 文件夹
压缩格式选择rar 避免win下不能解压..
呐…压缩好了 如图

直接拖动到win下 (如果你的不支持 请想其他办法)
回到Win下 解压ffmpeg-3.2.rar文件 到我们刚才那个工程的目录里
D:\Android\AndroidStudioProjects\MyFFmpeg\ffmpeg-3.2\

进入复制 fmpeg-3.2\android\arm\lib下的所有so文件到
MyFFmpeg\app\jnicode\jni\prebuilt下
复制整个ffmpeg-3.2\android\arm\include文件夹 到MyFFmpeg\app\jnicode\jni\下
以上两次复制 项目结构变成了这样

编写native方法 , 新建类FFmpegCmd .java 简单的添加一个exec()方法(等会还要改)

package com.toshiba.ffmpeg;/** * 作者:东芝(2016/11/23). */public class FFmpegCmd {    public  static native int exec(int argc,String[] argv);}

然后编译为class文件 你可以直接使用android studio的make功能
点击菜单项里的>Build>Make Module app

然后class文件生成到 MyFFmpeg\app\build\intermediates\classes\debug\下

然后使用javah命令去生成头文件
打开win命令行
cd 进入到 刚才那个debug文件夹下执行 javah -jni 包名.类名,如:

javah -jni com.toshiba.ffmpeg.FFmpegCmd

如图

然后看到debug目录下生成了头文件
com_toshiba_ffmpeg_FFmpegCmd.h

把刚才生成的头文件复制到MyFFmpeg\app\jnicode\jni\下

com_toshiba_ffmpeg_FFmpegCmd.h的内容为:

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_toshiba_ffmpeg_FFmpegCmd */#ifndef _Included_com_toshiba_ffmpeg_FFmpegCmd#define _Included_com_toshiba_ffmpeg_FFmpegCmd#ifdef __cplusplusextern "C" {#endif/* * Class:     com_toshiba_ffmpeg_FFmpegCmd * Method:    exec * Signature: (I[Ljava/lang/String;)I */JNIEXPORT jint JNICALL Java_com_toshiba_ffmpeg_FFmpegCmd_exec  (JNIEnv *, jclass, jint, jobjectArray);#ifdef __cplusplus}#endif#endif

然后, 我们新建一个相同名称的文件 并修改后缀为.c 并导入相关头文件实现其对应函数

com_toshiba_ffmpeg_FFmpegCmd.c的内容为(等会还要改)

#include "com_toshiba_ffmpeg_FFmpegCmd.h"#include <string.h>/* * Class:     com_toshiba_ffmpeg_FFmpegCmd * Method:    exec * Signature: (I[Ljava/lang/String;)I */JNIEXPORT jint JNICALL Java_com_toshiba_ffmpeg_FFmpegCmd_exec  (JNIEnv *env, jclass clazz, jint cmdnum, jobjectArray cmdline){   return 1111;//临时测试数字}

在 MyFFmpeg\app\jnicode\jni\下 创建Android.mk Application.mk 空文件
Android.mk内容为

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE :=  libavutilLOCAL_SRC_FILES := prebuilt/libavutil-55.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE :=  libswresampleLOCAL_SRC_FILES := prebuilt/libswresample-2.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE :=  libswscaleLOCAL_SRC_FILES := prebuilt/libswscale-4.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := libavcodecLOCAL_SRC_FILES := prebuilt/libavcodec-57.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := libavformatLOCAL_SRC_FILES := prebuilt/libavformat-57.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := libavfilterLOCAL_SRC_FILES := prebuilt/libavfilter-6.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := libavdeviceLOCAL_SRC_FILES := prebuilt/libavdevice-57.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := ffmpegLOCAL_SRC_FILES :=com_toshiba_ffmpeg_FFmpegCmd.cLOCAL_C_INCLUDES := D:\Android\AndroidStudioProjects\MyFFmpeg\ffmpeg-3.2LOCAL_LDLIBS := -llog -ljnigraphics -lz -landroid -lm -pthread -L$(SYSROOT)/usr/libLOCAL_SHARED_LIBRARIES := libavcodec libavfilter libavformat libavutil libswresample libswscale libavdeviceinclude $(BUILD_SHARED_LIBRARY)

如果你不会这里这些语法 那么你肯定是跳级 直接来学ffmpeg了 这些配置的意义和注意事项应该是在你学完jni基础再来看这篇文章才能懂的 建议先去学一遍jni (我等后面有空再写这方面的文章)
先注意两个地方:
LOCAL_SRC_FILES 这里指定so文件或c/cpp等待编译文件的路径
LOCAL_C_INCLUDES 这里是重点 导入ffmpeg的源码 这样方便打包时不会提示缺少xxx文件 当然不会把ffmpeg全部打包进去
LOCAL_SHARED_LIBRARIES 里的名称要和LOCAL_MODULE 里的名称一致

Application.mk内容为

APP_ABI := armeabi-v7aAPP_PLATFORM=android-14

APP_ABI 这里打包只是为了支持ARMv7的设备 其他的请改成 armeabi armeabi-v7a x86以便支持更多设备
APP_PLATFORM 指定哪个版本没太多要求

最后经过上面的一番折腾
项目结构应该是这样子的 (注意观察你的是否跟我的结构一致)

3.移植main函数

到 MyFFmpeg\ffmpeg-3.2\下
复制以下文件

cmdutils.ccmdutils.hcmdutils_common_opts.hconfig.hffmpeg.cffmpeg.hffmpeg_filter.cffmpeg_opt.c

复制到我们的项目 MyFFmpeg\app\jnicode\jni\下
编辑ffmpeg.c 查找main(int argc, char **argv) 函数
并修改名称为ffmpeg_exec 如下:

int ffmpeg_exec(int argc, char **argv){//...略}

来到ffmpeg.h头文件
末尾 添加函数声明如下 (#endif之前)

int ffmpeg_exec(int argc, char **argv);

然后到ffmpeg.c 找到
static void ffmpeg_cleanup(int ret) 函数
在函数末尾添加以下内容 清除计数(实测 如果不这样做 第二次执行命令时会导致闪退 可能是ffmpeg的bug )

 nb_filtergraphs = 0; nb_output_files = 0; nb_output_streams = 0; nb_input_files = 0; nb_input_streams = 0;

为了防止ffmpeg_exec执行完成回调和错误时及时返回
我们需要使用子线程来调用ffmpeg_exec 以便某些时刻及时中断停止线程
于是 我们新建一个工具类ffmpeg_thread 目的是子线程执行ffmpeg命令 和 完成回调给com_toshiba_ffmpeg_FFmpegCmd.c

ffmpeg_thread.h内容为

#include "libavcodec/avcodec.h"#include "libavformat/avformat.h"#include "libswscale/swscale.h"#include "ffmpeg.h"#include <pthread.h>#include <string.h>int ffmpeg_thread_run_cmd(int cmdnum,char **argv);void ffmpeg_thread_exit();void ffmpeg_thread_callback(void (*cb)(int ret));

对应的ffmpeg_thread.c内容为:

//// Created by 东芝 on 2016/11/25.//#include "ffmpeg_thread.h"pthread_t ntid;char **argvs = NULL;int num=0;void *thread(void *arg){   //执行    int result = ffmpeg_exec(num, argvs);    return ((void *)0);}/** * 新建子线程执行ffmpeg命令 */int ffmpeg_thread_run_cmd(int cmdnum,char **argv){    num=cmdnum;    argvs=argv;    int temp =pthread_create(&ntid,NULL,thread,NULL);    if(temp!=0)    {        //LOGE("can't create thread: %s ",strerror(temp));        return 1;    }    return 0;}static void (*ffmpeg_callback)(int ret);/** * 注册线程回调 */void ffmpeg_thread_callback(void (*cb)(int ret)){    ffmpeg_callback = cb;}/** * 退出线程 */void ffmpeg_thread_exit(int ret){    if (ffmpeg_callback) {        ffmpeg_callback(ret);    }    pthread_exit("ffmpeg_thread_exit");}

来到cmdutils.c文件
导入头文件

#include "ffmpeg_thread.h"

为了防止指令调用错误或完成导致程序终止 我们修改exit_program函数 屏蔽 exit函数换成我们的停止线程函数
则:

void exit_program(int ret){    if (program_exit)        program_exit(ret);    exit(ret);}

修改为

void exit_program(int ret){    if (program_exit) {        program_exit(ret);    }    //退出线程    ffmpeg_thread_exit(ret);//    exit(ret);}

这时 我们要在java层实现相关回调, 此时重新回到我们的FFmpegCmd.java
并完善所有功能

package com.toshiba.ffmpeg;/** * 作者:东芝(2016/11/23). */public class FFmpegCmd {    /**     * 加载所有相关链接库     */    static {        System.loadLibrary("avutil-55");        System.loadLibrary("avcodec-57");        System.loadLibrary("swresample-2");        System.loadLibrary("avformat-57");        System.loadLibrary("swscale-4");        System.loadLibrary("avfilter-6");        System.loadLibrary("avdevice-57");        System.loadLibrary("ffmpeg");    }    private static OnExecListener listener;    /**     * 调用底层执行     * @param argc     * @param argv     * @return     */    public static native int exec(int argc, String[] argv);    public static void onExecuted(int ret) {        if (listener != null) {            listener.onExecuted(ret);        }    }    /**     * 执行ffmoeg命令     * @param cmds     * @param listener     */    public static void exec(String[] cmds, OnExecListener listener) {        FFmpegCmd.listener = listener;        exec(cmds.length, cmds);    }    /**     * 执行完成/错误 时的回调接口     */    public interface OnExecListener {        void onExecuted(int ret);    }}

重新回到我们的com_toshiba_ffmpeg_FFmpegCmd.c文件
导入相关头文件并且通过jni调用该ffmpeg_thread_run_cmd函数 ,如下,
代码注释清晰明了:

#include "com_toshiba_ffmpeg_FFmpegCmd.h"#include <string.h>#include "android_log.h"#include "ffmpeg_thread.h"static JavaVM *jvm = NULL;//java虚拟机static jclass m_clazz = NULL;//当前类(面向java)/** * 回调执行Java方法 * 参看 Jni反射+Java反射 */void callJavaMethod(JNIEnv *env, jclass clazz,int ret) {    if (clazz == NULL) {        LOGE("---------------clazz isNULL---------------");        return;    }    //获取方法ID (I)V指的是方法签名 通过javap -s -public FFmpegCmd 命令生成    jmethodID methodID = (*env)->GetStaticMethodID(env, clazz, "onExecuted", "(I)V");    if (methodID == NULL) {        LOGE("---------------methodID isNULL---------------");        return;    }    //调用该java方法    (*env)->CallStaticVoidMethod(env, clazz, methodID,ret);}/** * c语言-线程回调 */static void ffmpeg_callback(int ret) {    JNIEnv *env;    //附加到当前线程从JVM中取出JNIEnv, C/C++从子线程中直接回到Java里的方法时  必须经过这个步骤    (*jvm)->AttachCurrentThread(jvm, (void **) &env, NULL);    callJavaMethod(env, m_clazz,ret);    //完毕-脱离当前线程    (*jvm)->DetachCurrentThread(jvm);}/* * Class:     com_toshiba_ffmpeg_FFmpegCmd * Method:    exec * Signature: (I[Ljava/lang/String;)I */JNIEXPORT jintJNICALL Java_com_toshiba_ffmpeg_FFmpegCmd_exec        (JNIEnv *env, jclass clazz, jint cmdnum, jobjectArray cmdline) {    //---------------------------------C语言 反射Java 相关----------------------------------------    //在jni的c线程中不允许使用共用的env环境变量 但JavaVM在整个jvm中是共用的 可通过保存JavaVM指针,到时候再通过JavaVM指针取出JNIEnv *env;    //ICS之前(你可把NDK sdk版本改成低于11) 可以直接写m_clazz = clazz;直接赋值,  然而ICS(sdk11) 后便改变了这一机制,在线程中回调java时 不能直接共用变量 必须使用NewGlobalRef创建全局对象    //官方文档正在拼命的解释这一原因,参看:http://android-developers.blogspot.jp/2011/11/jni-local-reference-changes-in-ics.html    (*env)->GetJavaVM(env, &jvm);    m_clazz = (*env)->NewGlobalRef(env, clazz);    //---------------------------------C语言 反射Java 相关----------------------------------------    //---------------------------------java 数组转C语言数组----------------------------------------    int i = 0;//满足NDK所需的C99标准    char **argv = NULL;//命令集 二维指针    jstring *strr = NULL;    if (cmdline != NULL) {        argv = (char **) malloc(sizeof(char *) * cmdnum);        strr = (jstring *) malloc(sizeof(jstring) * cmdnum);        for (i = 0; i < cmdnum; ++i) {//转换            strr[i] = (jstring)(*env)->GetObjectArrayElement(env, cmdline, i);            argv[i] = (char *) (*env)->GetStringUTFChars(env, strr[i], 0);        }    }    //---------------------------------java 数组转C语言数组----------------------------------------    //---------------------------------执行FFmpeg命令相关----------------------------------------    //新建线程 执行ffmpeg 命令    ffmpeg_thread_run_cmd(cmdnum, argv);    //注册ffmpeg命令执行完毕时的回调    ffmpeg_thread_callback(ffmpeg_callback);    //---------------------------------执行FFmpeg命令相关----------------------------------------    free(strr);    return 0;}

回到Android.mk文件 编辑
找到LOCAL_SRC_FILES :=com_toshiba_ffmpeg_FFmpegCmd.c这行
我们继续添加一些需要编译的文件

 LOCAL_SRC_FILES :=com_toshiba_ffmpeg_FFmpegCmd.c \                 cmdutils.c \                  ffmpeg.c \                  ffmpeg_opt.c \                  ffmpeg_filter.c \                  ffmpeg_thread.c

算了 直接贴 完整Android.mk文件内容 ,给你们参考:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE :=  libavutilLOCAL_SRC_FILES := prebuilt/libavutil-55.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE :=  libswresampleLOCAL_SRC_FILES := prebuilt/libswresample-2.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE :=  libswscaleLOCAL_SRC_FILES := prebuilt/libswscale-4.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := libavcodecLOCAL_SRC_FILES := prebuilt/libavcodec-57.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := libavformatLOCAL_SRC_FILES := prebuilt/libavformat-57.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := libavfilterLOCAL_SRC_FILES := prebuilt/libavfilter-6.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := libavdeviceLOCAL_SRC_FILES := prebuilt/libavdevice-57.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := ffmpegLOCAL_SRC_FILES :=com_toshiba_ffmpeg_FFmpegCmd.c \                  cmdutils.c \                  ffmpeg.c \                  ffmpeg_opt.c \                  ffmpeg_filter.c \                  ffmpeg_thread.cLOCAL_C_INCLUDES := D:\Android\AndroidStudioProjects\MyFFmpeg\ffmpeg-3.2LOCAL_LDLIBS := -llog -ljnigraphics -lz -landroid -lm -pthread -L$(SYSROOT)/usr/libLOCAL_SHARED_LIBRARIES := libavcodec libavfilter libavformat libavutil libswresample libswscale libavdeviceinclude $(BUILD_SHARED_LIBRARY)

4.NDK编译

终于快完工了…
我们打开win下的命令行
cd 到 MyFFmpeg\app\jnicode\jni下

cd D:\Android\AndroidStudioProjects\MyFFmpeg\app\jnicode\jni

然后直接执行

ndk-build

不出意外 so文件正常编译输出…如图:

可能会有一些 warning: ‘xxxx ’ 警告 正常,请无视
但如果遇到error的话 那请检查代码是否有错误
文件输出在 MyFFmpeg\app\jnicode\libs下

5.编写测试例子

修改app的build.gradle
指定lib库的位置

 sourceSets {        main {            jniLibs.srcDirs = ['jnicode/libs']        }    }

如果手机是6.0的 请把build.gradle修改目标sdk如果大于23请改成小于23 如: targetSdkVersion 22
否则你将要自己申请权限 这是测试例子没必要搞的那么麻烦
然后
回到我们的java代码
新建一个简单的MainActivity
这里演示几个比较简单的
1.音频剪切 剪切一首歌曲从00:01:40 到00:02:30 这段音频
2.任意格式的视频转换为任意格式的视频 支持分辨率 比特率 音频数据流 帧数等相关设置
3.其他的请自己去测试…
布局:

 <Button       android:id="@+id/btnCutAudio"       android:layout_width="wrap_content"       android:layout_height="wrap_content"       android:text="剪切音频" />   <Button       android:id="@+id/btnConvertVideo"       android:layout_width="wrap_content"       android:layout_height="wrap_content"       android:text="视频格式转换" />

MainActivity :

package com.toshiba.ffmpeg;import android.app.ProgressDialog;import android.os.Environment;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Toast;public class MainActivity extends AppCompatActivity {    public ProgressDialog show;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        final String path = Environment.getExternalStorageDirectory().getPath();        findViewById(R.id.btnCutAudio).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                //原命令 ffmpeg -i input.mp3 -ss hh:mm:ss -t hh:mm:ss -acodec copy output.mp3                //flac有点问题,  支持mp3 wma m4a                String cmd = "ffmpeg -i " + path + "/test.mp3" + " -ss 00:01:40 -t 00:02:30 -acodec copy " + path + "/test_out.mp3";                toExec(cmd);            }        });        findViewById(R.id.btnConvertVideo).setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                //原命令  ffmpeg -i "xxx.mp4" -y -ab 32 -ar 22050 -qscale 10 -s 640*480 -r 15 xxx_out.flv//                -i 是 要转换文件名//                -y是 覆盖输出文件//                        -ab 是 音频数据流, 如 128 64//                        -ar 是 声音的频率 22050 基本都是这个。//                -qscale 是视频输出质量,后边的值越小质量越高,但是输出文件就越“肥”//                -s 是输出 文件的尺寸大小!//                -r 是 播放侦数。                String cmd = "ffmpeg -i " + path + "/test.mp4" + " -y -ab 32 -ar 22050 -qscale 10 -s 640*480 -r 15 " + path + "/test_out.flv";                toExec(cmd);            }        });    }    private void toExec(String cmd) {        show = ProgressDialog.show(MainActivity.this, null, "执行中...", true);        //转换为数组        String[] cmds = cmd.split(" ");        FFmpegCmd.exec(cmds, new FFmpegCmd.OnExecListener() {            @Override            public void onExecuted(final int ret) {                runOnUiThread(new Runnable() {                    @Override                    public void run() {                        Toast.makeText(MainActivity.this, "执行完成=" + ret, Toast.LENGTH_SHORT).show();                        show.dismiss();                    }                });            }        });    }}

最后别忘了添加权限

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

打包运行apk
先在手机sd卡根目录放一个test.mp3文件
然后回到我们的app 点击’剪切音频’按钮
结果是输出Toast 执行完成=0,回到sd卡下 我们看到test_out.mp3正常输出,笔者试听了下 并注意观察剪切的时间段 答案肯定是正确的—歌曲被剪切了,测试成功
(Ps:第二次点执行前要把sd卡的test_out.mp3删掉,否则执行失败, 这不属于程序的问题)

然后再尝试视频格式转换 为了方便测试 我是在sd卡根目录放了个很小的MP4视频文件
然后回到app转换 成功 视频越大转换时间越久 (用过电脑的格式工厂吧? )
然后回到sd卡 发现test_out.flv已经生成 点击正常播放 功能没问题…

然后 你可以做很多有趣的app了
本Demo完整源码在教程最后 …

6.FFmpeg命令使用

下面贴上ffmpeg常用命令
下面的命令部分内容转来自
http://blog.csdn.net/weiyuefei/article/details/51678582
http://www.cnblogs.com/wainiwann/p/4128154.html

命令格式:
ffmpeg -i [输入文件名] [参数选项] -f [格式] [输出文件]
ffmpeg [[options][`-i’ input_file]]… {[options] output_file}…
1、参数选项:
(1) -an: 去掉音频
(2) -acodec: 音频选项, 一般后面加copy表示拷贝
(3) -vcodec:视频选项,一般后面加copy表示拷贝
2、格式:
(1) h264: 表示输出的是h264的视频裸流
(2) mp4: 表示输出的是mp4的视频
(3)mpegts: 表示ts视频流
如果没有输入文件,那么视音频捕捉(只在Linux下有效,因为Linux下把音视频设备当作文件句柄来处理)就会起作用。作为通用的规则,选项一般用于下一个特定的文件。如果你给 –b 64选项,改选会设置下一个视频速率。对于原始输入文件,格式选项可能是需要的。缺省情况下,ffmpeg试图尽可能的无损转换,采用与输入同样的音频视频参数来输出。(by ternence.hsu)

H264视频转ts视频流

ffmpeg -i test.h264 -vcodec copy -f mpegts test.ts

H264视频转mp4

ffmpeg -i test.h264 -vcodec copy -f mp4 test.mp4

ts视频转mp4

ffmpeg -i test.ts -acodec copy -vcodec copy -f mp4 test.mp4

mp4视频转flv

ffmpeg -i test.mp4 -acodec copy -vcodec copy -f flv test.flv 

转换文件为3GP格式

ffmpeg -y -i test.mpeg -bitexact -vcodec h263 -b 128 -r 15 -s 176x144 -acodec aac -ac 2 -ar 22500 -ab 24 -f 3gp test.3gp

转换文件为3GP格式 v2

ffmpeg -y -i test.wmv -ac 1 -acodec libamr_nb -ar 8000 -ab 12200 -s 176x144 -b 128 -r 15 test.3gp

使用 ffmpeg 编码得到高质量的视频

ffmpeg.exe -i "D:\Video\Fearless\Fearless.avi" -target film-dvd -s 720x352 -padtop 64 -padbottom 64 -maxrate 7350000 -b 3700000 -sc_threshold 1000000000 -trellis -cgop -g 12 -bf 2 -qblur 0.3 -qcomp 0.7 -me full -dc 10 -mbd 2 -aspect 16:9 -pass 2 -passlogfile "D:\Video\ffmpegencode" -an -f mpeg2video "D:\Fearless.m2v"

转换指定格式文件到FLV格式

ffmpeg.exe -i test.mp3 -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 f:\test.flv ffmpeg.exe -i test.wmv -ab 56 -ar 22050 -b 500 -r 15 -s 320x240 f:\test.flv

转码解密的VOB

ffmpeg -i snatch_1.vob -f avi -vcodec mpeg4 -b 800 -g 300 -bf 2 -acodec mp3 -ab 128 snatch.avi(上面的命令行将vob的文件转化成avi文件,mpeg4的视频和mp3的音频。注意命令中使用了B帧,所以mpeg4流是divx5兼容的。GOP大小是300意味着29.97帧频下每10秒就有INTRA帧。该映射在音频语言的DVD转码时候尤其有用,同时编码到几种格式并且在输入流和输出流之间建立映射)

转换文件为3GP格式

ffmpeg -i test.avi -y -b 20 -s sqcif -r 10 -acodec amr_wb -ab 23.85 -ac 1 -ar 16000 test.3gp(如果要转换为3GP格式,则ffmpeg在编译时必须加上–enable-amr_nb –enable-amr_wb,详细内容可参考:转换视频为3GPP格式)

转换文件为MP4格式(支持iPhone/iTouch)

ffmpeg  -y  -i input.wmv  -f mp4 -async 1-s 480x320  -acodec libfaac -vcodec libxvid  -qscale 7 -dts_delta_threshold 1 output.mp4ffmpeg  -y  -i source_video.avi input -acodec libfaac -ab 128000 -vcodec mpeg4 -b 1200000 -mbd 2 -flags +4mv+trell -aic 2 -cmp 2 -subcmp 2 -s 320x180 -title X final_video.mp4

将一段音频与一段视频混合

ffmpeg -i son.wav -i video_origine.avi video_finale.mpg

将一段视频转换为DVD格式

ffmpeg -i source_video.avi -target pal-dvd -ps 2000000000 -aspect 16:9 finale_video.mpeg(target pal-dvd : Output format ps 2000000000 maximum size for the output file, in bits (here, 2 Gb) aspect 16:9 : Widescreen)

转换一段视频为DivX格式

ffmpeg -i video_origine.avi -s 320x240 -vcodec msmpeg4v2 video_finale.aviTurn X images to a video sequenceffmpeg -f image2 -i image%d.jpg video.mpg(This command will transform all the images from the current directory (named image1.jpg, image2.jpg, etc...) to a video file named video.mpg.)Turn a video to X imagesffmpeg -i video.mpg image%d.jpg(This command will generate the files named image1.jpg, image2.jpg, ... ;The following image formats are also availables : PGM, PPM, PAM, PGMYUV, JPEG, GIF, PNG, TIFF, SGI.

使用ffmpeg录像屏幕(仅限Linux平台)

ffmpeg -vcodec mpeg4 -b 1000 -r 10 -g 300 -vd x11:0,0 -s 1024x768 ~/test.avi(-vd x11:0,0 指录制所使用的偏移为 x=0 和 y=0-s 1024×768 指录制视频的大小为 1024×768。录制的视频文件为 test.avi,将保存到用户主目录中;如果你只想录制一个应用程序窗口或者桌面上的一个固定区域,那么可以指定偏移位置和区域大小。使用xwininfo -frame命令可以完成查找上述参数。)

重新调整视频尺寸大小(仅限Linux平台)

ffmpeg -vcodec mpeg4 -b 1000 -r 10 -g 300 -i ~/test.avi -s 800×600 ~/test-800-600.avi

把摄像头的实时视频录制下来,存储为文件(仅限Linux平台)

ffmpeg  -f video4linux -s 320*240 -r 10 -i /dev/video0 test.asf

使用ffmpeg压制H.264视频

ffmpeg -threads 4 -i INPUT -r 29.97 -vcodec libx264 -s 480x272 -flags +loop -cmp chroma -deblockalpha 0 -deblockbeta 0 -crf 24 -bt 256k -refs 1 -coder 0 -me umh -me_range 16 -subq 5 -partitions parti4x4+parti8x8+partp8x8 -g 250 -keyint_min 25 -level 30 -qmin 10 -qmax 51 -trellis 2 -sc_threshold 40 -i_qfactor 0.71 -acodec libfaac -ab 128k -ar 48000 -ac 2 OUTPUT(使用该指令可以压缩出比较清晰,而且文件转小的H.264视频文件)

网络推送

udp视频流的推送ffmpeg -re  -i 1.ts  -c copy -f mpegts   udp://192.168.0.106:1234

视频拼接

裸码流的拼接,先拼接裸码流,再做容器的封装ffmpeg -i "concat:test1.h264|test2.h264" -vcodec copy -f h264 out12.h264

图像相关

截取一张352x240尺寸大小的,格式为jpg的图片 ffmpeg -i test.asf -y -f image2 -t 0.001 -s 352x240 a.jpg

把视频的前30帧转换成一个Animated Gif

ffmpeg -i test.asf -vframes 30 -y -f gif a.gif

截取指定时间的缩微图,-ss后跟的时间单位为秒

ffmpeg -i test.avi -y -f image2 -ss 8 -t 0.001 -s 350x240 test.jpg

音频处理

转换wav到mp2格式ffmpeg -i /tmp/a.wav -ab 64 /tmp/a.mp2 -ab 128 /tmp/b.mp2 -map 0:0 -map 0:0(上面的命令行转换一个64Kbits 的a.wav到128kbits的a.mp2 ‘-map file:index’在输出流的顺序上定义了哪一路输入流是用于每一个输出流的。)

切割ts分片

ffmpeg -i input.mp4 -c:v libx264 -c:a aac -strict -2 -f hls -hls_list_size 6 -hls_time 5 output1.m3u8

分离视频音频流

ffmpeg -i input_file -vcodec copy -an output_file_video  //分离视频流ffmpeg -i input_file -acodec copy -vn output_file_audio  //分离音频流

视频解复用

ffmpeg –i test.mp4 –vcodec copy –an –f m4v test.264ffmpeg –i test.avi –vcodec copy –an –f m4v test.264

视频封装

ffmpeg –i test.avi –r 1 –f image2 image-%3d.jpeg        //提取图片ffmpeg -ss 0:1:30 -t 0:0:20 -i input.avi -vcodec copy -acodec copy output.avi    //剪切视频//-r 提取图像的频率,-ss 开始时间,-t 持续时间

视频剪切

ffmpeg –i test.avi –r 1 –f image2 image-%3d.jpeg        //提取图片ffmpeg -ss 0:1:30 -t 0:0:20 -i input.avi -vcodec copy -acodec copy output.avi    //剪切视频//-r 提取图像的频率,-ss 开始时间,-t 持续时间

视频录制

ffmpeg –i rtsp://192.168.3.205:5555/test –vcodec copy out.avi

YUV序列播放

ffplay -f rawvideo -video_size 1920x1080 input.yuv

YUV序列转AVI

ffmpeg –s w*h –pix_fmt yuv420p –i input.yuv –vcodec mpeg4 output.avi

最简单的抓屏:

ffmpeg -f gdigrab -i desktop out.mpg 

从屏幕的(10,20)点处开始,抓取640x480的屏幕,设定帧率为5 :

ffmpeg -f gdigrab -framerate 5 -offset_x 10 -offset_y 20 -video_size 640x480 -i desktop out.mpg 

ffmpeg将图片转换为视频:

http://blog.sina.com.cn/s/blog_40d73279010113c2.html

将直播媒体保存至本地文件

ffmpeg -i rtmp://server/live/streamName -c copy dump.flv

将文件当做直播送至live

ffmpeg -re -i localFile.mp4 -c copy -f flv rtmp://server/live/streamName

7.源码下载

http://download.csdn.net/detail/u014418171/9693793

1 0