Android应用开发Android 集成 FFmpeg (三) 获取 FFmpeg 执行进度
白羽 2019-03-12 来源 :网络 阅读 1010 评论 0

摘要:本文将带你了解Android应用开发Android 集成 FFmpeg (三) 获取 FFmpeg 执行进度,希望本文对大家学Android有所帮助。

    本文将带你了解Android应用开发Android 集成 FFmpeg (三) 获取 FFmpeg 执行进度,希望本文对大家学Android有所帮助。


Android应用开发Android 集成 FFmpeg (三) 获取 FFmpeg 执行进度


Android 集成   FFmpeg (三) 获取 FFmpeg 执行进度,在以命令方式调用 FFmpeg   的时候,可能会执行一些比较耗时的任务,这时如果没有进度展示,用户可能会以为程序崩溃了,体验十分不好.能不能在以命令方式调用 FFmpeg   时实时获取执行进度呢?谷歌关键词 “Android FFmpeg 命令” 可以得到很多教程,但加上关键词 “进度”就没有相关文章了,看来以命令方式调用   FFmpeg 实时获取执行进度这个需求没有前人的肩膀可站,要开动自己的小脑筋了.
   
    首先来分析一下,以命令方式调用就是把一条命令交给 FFmpeg 执行,具体就是 ffmpeg.c 的 main 函数,待 main   函数执行完毕才会返回,执行过程相当于一个黑盒,执行进度显然是无法获取的.网上也没有相关文章,难道只有以函数方式调用 FFmpeg   才能获取到执行进度吗?当我快要下这样的定论时,看到了 FFmpeg 的 log 信息:
   
    这里写图片描述
   
    这是在执行混合音频命令时 FFmpeg 的日志输出,其中的 time 信息表示当前已合成的音频时长,这不就是进度信息吗!下面就针对混合音频命令获取实时执行进度.要做的就是提取日志中的进度信息,传递给   Android 层,首先回顾一下这些日志信息是怎样输出到 logcat 的,在Android 集成 FFmpeg(二)   以命令方式调用中有详细说明,这里只关注关键方法 log_callback_null ,位于 ffmpeg.c 中:
   
   
   
    static void log_callback_null(void *ptr, int level, const char *fmt,   va_list vl)
    {
        static int print_prefix =   1;
        static int count;
        static char prev[1024];
        char line[1024];
        static int is_atty;
        av_log_format_line(ptr, level,   fmt, vl, line, sizeof(line), &print_prefix);
        strcpy(prev, line);
        if (level <=   AV_LOG_WARNING){
            XLOGE(%s, line);
        }else{
            XLOGD(%s, line);
        }
    }
    日志信息都是通过第 13 行的 XLOGD 方法输入到 logcat 中的,我们需要的进度信息就在 line 字符串中,那只要在此处把进度提取出来传递给   Android 层就行了,在 XLOGD 方法下添加一个传递方法:
   
   
    XLOGD(%s, line);
    callJavaMethod(line);//传递进度信息
    需要明白 JNI 不仅可以实现 java 调用底层代码, c/c++ 也可以主动调用 java 代码,我在Android 集成 FFmpeg (一)   基础知识及简单调用 中对此也有说明. callJavaMethod 方法要做的就是主动调用 java 层的方法,从而实现进度信息的回调.   callJavaMethod 方法直接在 com_jni_FFmpegJni.c   接口文件中定义即可,在实现此方法前先明确要做什么.首先要对日志信息进行处理,把进度提取出来,日志信息形如:
   
   
    frame=    1 fps=0.0 q=0.0 size=       0kB time=00:01:02.71 bitrate=   0.0kbits/s speed=2.88x
    把关键的已处理时长 “00:01:02” 转换成秒数 “62” 就足够了,代码如下:
   
   
   
    void callJavaMethod(char *ret) {
       int result = 0;
       char timeStr[10] = time=;
      char *q = strstr(ret, timeStr);
      if(q !=   NULL){ //日志信息中若包含time=字符串
          char str[14] = {0};
          strncpy(str, q, 13);
          int h   =(str[5]-'0')*10+(str[6]-'0');
          int m =(str[8]-'0')*10+(str[9]-'0');
          int s   =(str[11]-'0')*10+(str[12]-'0');
          result = s+m*60+h*60*60;
       }else{
          return;
       }
       //已执行时长 result
     
    }
    其中的 strstr 为 < string.h > 中的方法,表示找出 timeStr 字符串在 ret   字符串中第一次出现的位置,并返回该位置的指针,如找不到,返回空指针。也就是说,如果日志信息中包含”time=”字符串,q 指针就指向字符 “t”,然后根据   “time=00:01:02” 这种固定格式,将总秒数提取出来,strncpy 及其他语法方法就不再细说了,不熟悉的话可以复习 c 语言.
   
    获取到进度信息后,就可以调用 java 层的方法了,首先在 FFmpegJni.java 中定义待调用方法:
   
   
    public static void onProgress(int second) {
     
    }
    然后在com_jni_FFmpegJni.c 的 callJavaMethod 方法中调用,代码很简单,只需两行:
   
    //获取java方法
     jmethodID methodID =   (*m_env)->GetStaticMethodID(m_env, m_clazz, onProgress, (I)V);
     //调用该方法
     (*m_env)->CallStaticVoidMethod(m_env,   m_clazz, methodID,result);
    其中 m_env, m_clazz 定义在 com_jni_FFmpegJni.c 中,在 java 层进入 c 语言层时赋值,如下:
   
   
    static jclass m_clazz = NULL;//当前类(面向java)
    static JNIEnv *m_env = NULL;
     
    JNIEXPORT jint JNICALL Java_com_jni_FFmpegJni_run(JNIEnv *env, jclass   clazz, jobjectArray commands) {
     
        //获取java虚拟机,在jni的c线程中不允许使用共用的env环境变量   但JavaVM在整个jvm中是共用的 可通过保存JavaVM指针,到时候再通过JavaVM指针取出JNIEnv *env
        (*env)->GetJavaVM(env,   &jvm);
      //获取调用此方法的java类,ICS之前(你可把NDK sdk版本改成低于11) 可以写m_clazz = clazz直接赋值,  然而ICS(sdk11) 后便改变了这一机制,在线程中回调java时 不能直接共用变量   必须使用NewGlobalRef创建全局对象
        m_clazz =   (*env)->NewGlobalRef(env, clazz);
        m_env = env;
     
       //以命令方式调用 FFmpeg
        ...
    }
    这样就可以实现 c 语言中调用 java 方法了,进度以形参传递到 Java 层,修改 onProgress 方法测试一下:
   
   
    public static void onProgress(int second) {
        Log.d(AAA, 已执行时长: +   second);
    }
    这里写图片描述
   
    如图,已经成功的将包含”time=00:01:02” 格式的日志进行处理,转换为总秒数(已合成时长),作为进度信息传递给 Java   层。需要的注意的是,这种方式将处理包括 “time=”日志的所有命令,不仅局限于合成音频,那如果要只在合成音频时输出进度呢?
   
    合成音频命令的关键词为”amix”,FFmpeg 开始执行这个命令时,会输出包含 “amix” 字符串的日志信息,那我们就可以再次使用 strstr   方法过滤日志信息,com_jni_FFmpegJni.c 完整代码如下:
   
   
    #include android_log.h
    #include com_jni_FFmpegJni.h
    #include ffmpeg.h
    #include <string.h>
     
    static JavaVM *jvm = NULL;//java虚拟机
    static jclass m_clazz = NULL;//当前类(面向java)
    static JNIEnv *m_env = NULL;
    static char amixStr[10] = amix;
    static char timeStr[10] = time=;
    static char amixing = 0;    //0:没遇到  1:遇到
     
    /**
     * 回调执行Java方法
     */
    void callJavaMethod(char *ret) {
        char *p = strstr(ret,   amixStr);
        if(p != NULL){
          //LOGE(遇到amix);
          amixing = 1;
        }
        int ss=0;
     
        if(amixing == 1){
           char *q = strstr(ret,   timeStr);
           if(q != NULL){
              //LOGE(遇到time=);
              char str[14] = {0};
              strncpy(str, q, 13);
              int h   =(str[5]-'0')*10+(str[6]-'0');
          int m   =(str[8]-'0')*10+(str[9]-'0');
          int s   =(str[11]-'0')*10+(str[12]-'0');
          ss = s+m*60+h*60*60;
           }else{
              return;
           }
        }else{
          return;
        }
     
        if (m_clazz == NULL) {
            LOGE(---------------clazz   isNULL---------------body{ }    

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之Android频道!

本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程