C++ 和 Java 函数互调

分享经验总结,欢迎加入

项目如下:


aa.gif

知识点:

  • CMakeLists.txt 的使用

  • c++ 创建子线程,消费者和生产者

  • ffmpeg 编译

  • ffmpeg 高低版本库动态切换

  • ffmpeg 解码音视频

  • OpenSLES 播放 pcm 音频

  • soundTouch (变调,变速)导入和使用

  • pcm 数据分包处理

  • pcm 数据使用 mediacodec 编码成 aac 文件

  • OpenGLES 渲染图片

  • OpenGLES 渲染 YUV 数据

  • 录音

  • 剪切音乐

源码地址:

https://github.com/taxiao213/ndk_project

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();
 }
}
(他晓),关注并转发,谢谢
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容