基于Android O8.1的ffmpeg NDK 开发 - 2 - APP显示ffmpeg所支持协议,编解码,过滤器,格式,配置等信息

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yanbixing123/article/details/81275672

我们今天的目标是仿照雷神的博客(https://blog.csdn.net/leixiaohua1020/article/details/47008825),把这个APP写出来,先上图:

APP中有5个按钮,点击不同的按钮,会有不同的ffmpeg信息显示出来,分别是我们编译的ffmpeg所支持的协议,编解码器,过滤器,格式和配置等。

1. 新建Android Studio工程,参照上一篇文章的详细介绍。

2. 在app/src/main/下新建一个jniLibs目录,并将ffmpeg的lib库和include文件夹拷贝过来。

3. 修改CMakeLists.txt文件,与上一篇的一致,以后都使用的是这个CMakList.txt文件,如下所示:


#CMake版本信息
cmake_minimum_required(VERSION 3.4.1)

#ffmpeg so文件路径
set(lib_src_DIR ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})

#配置加载native依赖
include_directories(${CMAKE_SOURCE_DIR}/src/main/jniLibs/include)

#动态方式加载ffmepg的so文件 第三方库加载方式
add_library(ffmpeg SHARED IMPORTED)

#引入libffmpeg.so文件
set_target_properties(ffmpeg PROPERTIES IMPORTED_LOCATION
${lib_src_DIR}/libffmpeg.so)

#CPP文件夹下待编译的c文件
add_library(native-lib SHARED ${CMAKE_SOURCE_DIR}/src/main/cpp/native-lib.cpp)

#C 日志 ndk官方库
find_library(log-lib log)

#静态库与动态库进行链接 相当于gcc命令行参数 -l。
target_link_libraries(native-lib
                      android
                      ffmpeg
                      ${log-lib})

4. 修改app/src/build.gradle文件,添加ndk选项,参照上篇。

5. APP的界面是5个按钮和一个显示文字的TextView,修改app/src/main/res/layout/activity_main.xml文件,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <LinearLayout
        android:id="@+id/linear_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/button_protocol"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onButtonClick"
            android:text="@string/button_protocol"
            android:textSize="12sp"/>
        <Button
            android:id="@+id/button_codec"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onButtonClick"
            android:text="@string/button_codec"
            android:textSize="12sp"/>
        <Button
            android:id="@+id/button_filter"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onButtonClick"
            android:text="@string/button_filter"
            android:textSize="12sp"/>
        <Button
            android:id="@+id/button_format"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onButtonClick"
            android:text="@string/button_format"
            android:textSize="12sp"/>
        <Button
            android:id="@+id/button_configuration"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onButtonClick"
            android:text="@string/button_configuration"
            android:textSize="12sp"/>
    </LinearLayout>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/linear_layout"
        android:padding="16dp">

        <TextView
            android:id="@+id/sample_text"
            android:layout_width="match_parent"
            android:textSize="24sp"
            android:textColor="#ff0000"
            android:layout_height="wrap_content"/>
    </ScrollView>


</RelativeLayout>

同时,这个layout文件中使用了的一些变量需要在app/src/main/res/values/strings.xml里面定义,如下所示:

<resources>
    <string name="app_name">ffmpeg_android_demo_2</string>
    <string name="button_protocol">Protocol</string>
    <string name="button_codec">Codec</string>
    <string name="button_filter">Filter</string>
    <string name="button_format">Format</string>
    <string name="button_configuration">Configuration</string>
</resources>

在做这一步操作时,当切换到app/src/main/res/layout/activity_main.xml的Design视图时,向里面拖拽Button,没有显示,同时提示Render problem,如下所示:

解决办法是找到app/src/main/res/values/styles.xml文件,在Theme前面添加个Base字符,如图所示:

6. 这时候就可以写我们的主程序了,打开app/src/main/java/xxx/MainActivity文件,主要目的就是复写onButtonClick方法,每当点击按钮时,就显示按钮所对应的内容,所以这个函数很好写,只需要设置TextView的显示内容即可,如下所示:

package com.example.nxf31081.ffmpeg_android_demo_2;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        mTextView = (TextView) findViewById(R.id.sample_text);
    }

    public void onButtonClick(View view) {
        int id = view.getId();

        switch (id) {
            case R.id.button_protocol:
                mTextView.setText(avProtocolInfo());
                break;
            case R.id.button_codec:
                mTextView.setText(avCodecInfo());
                break;
            case R.id.button_filter:
                mTextView.setText(avFilterInfo());
                break;
            case R.id.button_format:
                mTextView.setText(avFormatInfo());
                break;
            case R.id.button_configuration:
                mTextView.setText(avConfigurationInfo());
                break;
        }
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String avProtocolInfo();

    public native String avFormatInfo();

    public native String avCodecInfo();

    public native String avFilterInfo();

    public native String avConfigurationInfo();
}

7. 重点还是这几个native函数,根据上篇中的介绍,分别生成这个native函数的函数模板,在native-lib.cpp函数中,这几个函数就是ffmpeg API的简单应用了,如下所示:

#include <jni.h>
#include <string>
#include <android/log.h>

#define LOG_TAG "FFNative"
#define ALOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#define ALOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#define ALOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
#define ALOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
#define ALOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))

extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libswscale/swscale.h>

JNIEXPORT jstring JNICALL
Java_com_example_nxf31081_ffmpeg_1android_1demo_12_MainActivity_avProtocolInfo(JNIEnv *env,
                                                                      jobject instance) {
    char info[40000] = {0};
    av_register_all();

    void *opaque = NULL;
    const char *name;

    //Input
    while((name = avio_enum_protocols(&opaque, 0)))
        sprintf(info, "%s[Input] : [%10s] \n", info, name);

    //Output
    opaque = NULL;
    while((name = avio_enum_protocols(&opaque, 1)))
        sprintf(info, "%s[Output] : [%10s] \n", info, name);

    return env->NewStringUTF(info);
}

JNIEXPORT jstring JNICALL
Java_com_example_nxf31081_ffmpeg_1android_1demo_12_MainActivity_avFormatInfo(JNIEnv *env, jobject instance) {
    char info[40000] = {0};

    av_register_all();

    AVInputFormat *if_temp = av_iformat_next(NULL);
    AVOutputFormat *of_temp = av_oformat_next(NULL);
    while (if_temp != NULL) {
        sprintf(info, "%s[Input]: %10s, %s \n", info, if_temp->name, if_temp->long_name ? if_temp->long_name : " ");
        if_temp = if_temp->next;
    }
    while (of_temp != NULL) {
        sprintf(info, "%s[Output]: %10s, %s \n", info, of_temp->name, of_temp->long_name ? of_temp->long_name : " ");
        of_temp = of_temp->next;
    }
    ALOGI("%s", info);
    return env->NewStringUTF(info);
}

JNIEXPORT jstring JNICALL
Java_com_example_nxf31081_ffmpeg_1android_1demo_12_MainActivity_avCodecInfo(JNIEnv *env, jobject instance) {
    char info[40000] = {0};

    av_register_all();

    AVCodec *c_temp = av_codec_next(NULL);

    while (c_temp != NULL) {
        if (c_temp->decode != NULL) {
            sprintf(info, "%s[Dec]:", info);
        } else {
            sprintf(info, "%s[Enc]:", info);
        }
        switch (c_temp->type) {
            case AVMEDIA_TYPE_VIDEO:
                sprintf(info, "%s (video) : ", info);
                break;
            case AVMEDIA_TYPE_AUDIO:
                sprintf(info, "%s (audio) : ", info);
                break;
            case AVMEDIA_TYPE_SUBTITLE:
                sprintf(info, "%s (subtitle) : ", info);
                break;
            default:
                sprintf(info, "%s (other) : ", info);
                break;
        }
        sprintf(info, "%s[%10s] %s \n", info, c_temp->name, c_temp->long_name ? c_temp->long_name : " ");
        c_temp = c_temp->next;
    }
    ALOGI("%s", info);
    return env->NewStringUTF(info);
}

JNIEXPORT jstring JNICALL
Java_com_example_nxf31081_ffmpeg_1android_1demo_12_MainActivity_avFilterInfo(JNIEnv *env, jobject instance) {
    char info[40000] = {0};
    avfilter_register_all();

    AVFilter *f_temp = (AVFilter *) avfilter_next(NULL);
    while (f_temp != NULL) {
        sprintf(info, "%s%s\n", info, f_temp->name);
        f_temp = f_temp->next;
    }
    ALOGI("%s", info);
    return env->NewStringUTF(info);
}

}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_nxf31081_ffmpeg_1android_1demo_12_MainActivity_avConfigurationInfo(JNIEnv *env,
                                                                           jobject instance) {

    char info[10000] = {0};

    av_register_all();

    sprintf(info, "%s \n", avcodec_configuration());

    return env->NewStringUTF(info);
}

这时候,这个app函数就算写好了,图放在文章的开头,可以看到,我们配置的硬编解码器h264_mediacodec已经能够检测到了。在下一篇文章中再仔细分析CMakeLists.txt文件和native-lib.cpp中所使用的ffmpeg函数。

工程下载地址:https://github.com/ZER0000/ffmpeg_android_demo.git

猜你喜欢

转载自blog.csdn.net/yanbixing123/article/details/81275672