C++ 和 Java 函数互调

C++ 和 Java 函数互调

分享经验总结,欢迎加入

QQ 群:686809487

项目如下:

在这里插入图片描述

知识点:

  • CMakeLists.txt 的使用
  • c++ 创建子线程,消费者和生产者
  • ffmpeg 编译
  • ffmpeg 高低版本库动态切换
  • ffmpeg 解码音视频
  • OpenSLES 播放 pcm 音频
  • soundTouch (变调,变速)导入和使用
  • pcm 数据分包处理
  • pcm 数据使用 mediacodec 编码成 aac 文件
  • OpenGLES 渲染图片
  • OpenGLES 渲染 YUV 数据
  • 录音
  • 剪切音乐

源码地址:

github.com/taxiao213/n…

build.gradle 配置

android {
    ```
    defaultConfig {
        ndk {
            abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
        }
        externalNativeBuild {
            cmake {
                // 传参数
                arguments "-DFFMPEG_BUILD_VERSION=${Integer.valueOf(FFMPEG_BUILD_VERSION)}"
                cppFlags "-fexceptions", "-frtti"
            }
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }
    ```
}
复制代码

CMakeLists.txt 配置

cmake_minimum_required(VERSION 3.4.1)

if (FFMPEG_BUILD_VERSION EQUAL 1)
    include_directories(include/low_version)
    set(version low_version)
    set(avcodec libavcodec-57.so)
    set(avdevice libavdevice-57.so)
    set(avfilter libavfilter-6.so)
    set(avformat libavformat-57.so)
    set(avutil libavutil-55.so)
    set(postproc libpostproc-54.so)
    set(swresample libswresample-2.so)
    set(swscale libswscale-4.so)
elseif (FFMPEG_BUILD_VERSION EQUAL 2)
    include_directories(include/high_version)
    set(version high_version)
    set(avcodec libavcodec.so)
    set(avdevice libavdevice.so)
    set(avfilter libavfilter.so)
    set(avformat libavformat.so)
    set(avutil libavutil.so)
    set(postproc libpostproc.so)
    set(swresample libswresample.so)
    set(swscale libswscale.so)
endif ()
set(rootPath ${CMAKE_SOURCE_DIR}/../../jniLibs/${version}/${CMAKE_ANDROID_ARCH_ABI})
message("rootPath: ${rootPath}")

add_library(${avcodec} SHARED IMPORTED)
set_target_properties(${avcodec} PROPERTIES IMPORTED_LOCATION ${rootPath}/${avcodec})

add_library(${avdevice} SHARED IMPORTED)
set_target_properties(${avdevice} PROPERTIES IMPORTED_LOCATION ${rootPath}/${avdevice})

add_library(${avfilter} SHARED IMPORTED)
set_target_properties(${avfilter} PROPERTIES IMPORTED_LOCATION ${rootPath}/${avfilter})

add_library(${avformat} SHARED IMPORTED)
set_target_properties(${avformat} PROPERTIES IMPORTED_LOCATION ${rootPath}/${avformat})

add_library(${avutil} SHARED IMPORTED)
set_target_properties(${avutil} PROPERTIES IMPORTED_LOCATION ${rootPath}/${avutil})

add_library(${postproc} SHARED IMPORTED)
set_target_properties(${postproc} PROPERTIES IMPORTED_LOCATION ${rootPath}/${postproc})

add_library(${swresample} SHARED IMPORTED)
set_target_properties(${swresample} PROPERTIES IMPORTED_LOCATION ${rootPath}/${swresample})

add_library(${swscale} SHARED IMPORTED)
set_target_properties(${swscale} PROPERTIES IMPORTED_LOCATION ${rootPath}/${swscale})

include_directories(soundtouch/source)
aux_source_directory(soundtouch/source DIR_SRCS)

add_library(
        native-lib
        SHARED
        ${DIR_SRCS}
        native-lib.cpp
        javaListener.cpp
        TXCallJava.cpp
        TXFFmpeg.cpp
        TXAudio.cpp
        TXQueue.cpp
        TXPlayStatus.cpp
        TXCallBack.cpp
        TXBufferQueue.cpp
        TXPcmBean.cpp
        test/thread_test.cpp
        test/Opensles_test.cpp
        TXVideo.cpp
)

target_link_libraries(
        native-lib
        ${avcodec}
        ${avdevice}
        ${avfilter}
        ${avformat}
        ${avutil}
        ${postproc}
        ${swresample}
        ${swscale}
        OpenSLES
        log)
复制代码

ndk 日志打印

//
// Created by yin13 on 2021/3/17.
//
#include "android/log.h"

#ifndef APPLE_ANDROID_LOG_H
#define APPLE_ANDROID_LOG_H
#endif //APPLE_ANDROID_LOG_H

#define TAG "TA_XIAO"
#define DEBUG 1
#if DEBUG
    #define SDK_LOG_V(FORMAT, ...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, "[%s] line: %d info: " FORMAT, __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
    #define SDK_LOG_D(FORMAT, ...) __android_log_print(ANDROID_LOG_DEBUG, TAG, "[%s] line: %d info: " FORMAT, __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
    #define SDK_LOG_W(FORMAT, ...) __android_log_print(ANDROID_LOG_WARN, TAG, "[%s] line: %d info: " FORMAT, __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
    #define SDK_LOG_E(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR, TAG, "[%s] line: %d info: " FORMAT, __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
    #define SDK_LOG_V(FORMAT, ...)
    #define SDK_LOG_D(FORMAT, ...)
    #define SDK_LOG_W(FORMAT, ...)
    #define SDK_LOG_E(FORMAT, ...)
#endif

复制代码

java 调用 c++ 方法

// 导入库,创建 native 方法
    
static {
    if (BuildConfig.FFMPEG_BUILD_VERSION == 1) {
    System.loadLibrary("avcodec-57");
    System.loadLibrary("avdevice-57");
    System.loadLibrary("avfilter-6");
    System.loadLibrary("avformat-57");
    System.loadLibrary("avutil-55");
    System.loadLibrary("postproc-54");
    System.loadLibrary("swresample-2");
    System.loadLibrary("swscale-4");
    } else if (BuildConfig.FFMPEG_BUILD_VERSION == 2) {
    System.loadLibrary("avcodec");
    System.loadLibrary("avdevice");
    System.loadLibrary("avfilter");
    System.loadLibrary("avformat");
    System.loadLibrary("avutil");
    System.loadLibrary("postproc");
    System.loadLibrary("swresample");
    System.loadLibrary("swscale");
    }
    System.loadLibrary("native-lib");
}

public native void javaMain2C();
复制代码

native-lib.cpp 实现相应的方法

extern "C"
JNIEXPORT void JNICALL
Java_com_taxiao_ffmpeg_JniSdkImpl_javaMain2C(JNIEnv *env, jobject jobject1) {
    javaListener = new JavaListener(jvm, env, env->NewGlobalRef(jobject1));
    javaListener->onError(1, 111, "main c++");
}
复制代码

c++ 调用 java 方法

native-lib.cpp 在 JNI_OnLoad 中获取 JavaVM

// 获取JVM
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    jvm = vm;
    JNIEnv *env;
    if (jvm->GetEnv((void **) (&env), JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }
    return JNI_VERSION_1_4;
}
复制代码

TXCallJava.cpp 实现回调方法

// 将 JavaVM 传入回调文件初始化需要回调的方法
TXCallJava::TXCallJava(JavaVM *vm, JNIEnv *env, jobject *obj) {
    this->javaVm = vm;
    this->jniEnv = env;
    
    this->job = jniEnv->NewGlobalRef(*obj);
    jclass aClass = jniEnv->GetObjectClass(job);
    if (aClass) {
        jmethodId = jniEnv->GetMethodID(aClass, JAVA_METHOD_PARPARED, "()V");
        jmethodIdCallLoad = jniEnv->GetMethodID(aClass, JAVA_METHOD_LOAD, "(Z)V");
        jmethodIdTimeInfo = jniEnv->GetMethodID(aClass, JAVA_METHOD_TIME_INFO, "(II)V");
        jmethodIdError = jniEnv->GetMethodID(aClass, JAVA_METHOD_ERROR, "(ILjava/lang/String;)V");
        jmethodIdComplete = jniEnv->GetMethodID(aClass, JAVA_METHOD_COMPLETE, "()V");
        jmethodIdValumeDB = jniEnv->GetMethodID(aClass, JAVA_METHOD_VALUME_DB, "(I)V");
        jmethodIdPcmAAc = jniEnv->GetMethodID(aClass, JAVA_METHOD_PCM_AAC, "(I[B)V");
        jmethodIdRecordTime = jniEnv->GetMethodID(aClass, JAVA_METHOD_RECORD_TIME, "(F)V");
        jmethodIdCutAudio = jniEnv->GetMethodID(aClass, JAVA_METHOD_CUT_AUDIO, "(I[B)V");
        jmethodIdRenderYUV = jniEnv->GetMethodID(aClass, JAVA_METHOD_RENDER_YUV, "(II[B[B[B)V");
        jmethodIdIsSupportMediaCodec = jniEnv->GetMethodID(aClass,
                                                           JAVA_METHOD_IS_SUPPORT_MEDIA_CODEC,
                                                           "(Ljava/lang/String;)Z");
        jmethodIdInitMediaCodecVideo = jniEnv->GetMethodID(aClass,
                                                           JAVA_METHOD_INIT_MEDIA_CODEC_VIDEO,
                                                           "(Ljava/lang/String;II[B[B)V");

        jmethodIdDecodeAvPacket = jniEnv->GetMethodID(aClass, JAVA_METHOD_DECODE_AV_PACKET,
                                                      "(I[B)V");
    }
}

void TXCallJava::onCallOnCutAudio(int type, int sampleRate, int size, void *pcmBuffer) {
    if (type == MAIN_THREAD) {
        jbyteArray pArray = jniEnv->NewByteArray(size);
        jniEnv->SetByteArrayRegion(pArray, 0, size, static_cast<const jbyte *>(pcmBuffer));
        jniEnv->CallVoidMethod(job, jmethodIdCutAudio, sampleRate, pArray);
        jniEnv->DeleteLocalRef(pArray);
    } else if (type == CHILD_THREAD) {
        JNIEnv *env;
        if (javaVm->AttachCurrentThread(&env, 0) != JNI_OK) {
            return;
        }
        jbyteArray pArray = env->NewByteArray(size);
        env->SetByteArrayRegion(pArray, 0, size, static_cast<const jbyte *>(pcmBuffer));
        env->CallVoidMethod(job, jmethodIdCutAudio, sampleRate, pArray);
        env->DeleteLocalRef(pArray);
        javaVm->DetachCurrentThread();
    }
}
复制代码

微信公众号(他晓),关注并转发,谢谢
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享