分享经验总结,欢迎加入
项目如下:
aa.gif
知识点:
-
CMakeLists.txt 的使用
-
c++ 创建子线程,消费者和生产者
-
ffmpeg 编译
-
ffmpeg 高低版本库动态切换
-
ffmpeg 解码音视频
-
OpenSLES 播放 pcm 音频
-
soundTouch (变调,变速)导入和使用
-
pcm 数据分包处理
-
pcm 数据使用 mediacodec 编码成 aac 文件
-
OpenGLES 渲染图片
-
OpenGLES 渲染 YUV 数据
-
录音
-
剪切音乐
源码地址:
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();
}
}
网友评论