前言

音频解码播放的原理是将不同类型的音频转化为pcm格式进行播放

开发环境

Android Studio 3.5.3,运行环境:Mac,编译环境cmake

环境配置

  1. 新建一个module工程,前提是该project是一个C++项目
  2. 在main文件夹下面新建cpp文件夹,新建CMakeLists.txt文件,新建native-lib文件,然后点击右键,选择Link C++ With Gradle,具体内容如下所示

CMakeLists.txt

cmake_minimum_required(VERSION 3.4.1)
include_directories(../cpp/include)
set(SOURCES)
file(GLOB_RECURSE SOURCES ${CMAKE_SOURCE_DIR}/*.cpp ${CMAKE_SOURCE_DIR}/*.c)
# 把系统的log库导入进来
find_library( log-liblog )set(distribution_DIR ../../../../libs)
add_library( yuvSHAREDIMPORTED)
set_target_properties( yuvPROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libyuv.so)
# 把libfmod.so预加载进来
add_library( avcodec-56SHAREDIMPORTED)
set_target_properties( avcodec-56PROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libavcodec-56.so)# 把libfmodL.so预加载进来
add_library( avdevice-56SHAREDIMPORTED)
set_target_properties( avdevice-56PROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libavdevice-56.so)add_library( avfilter-5SHAREDIMPORTED)
set_target_properties( avfilter-5PROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libavfilter-5.so)
add_library( avformat-56SHAREDIMPORTED)
set_target_properties( avformat-56PROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libavformat-56.so)
add_library( avutil-54SHAREDIMPORTED)
set_target_properties( avutil-54PROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libavutil-54.so)
add_library( postproc-53SHAREDIMPORTED)
set_target_properties( postproc-53PROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libpostproc-53.so)
add_library( swresample-1SHAREDIMPORTED)
set_target_properties( swresample-1PROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libswresample-1.so)
add_library( swscale-3SHAREDIMPORTED)
set_target_properties( swscale-3PROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libswscale-3.so)#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11"add_library( # Sets the name of the library.ffmpeg# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).${SOURCES})
#用于native_window
#find_library(
#             android-lib
#             android
#)target_link_libraries( ffmpegyuvavutil-54swresample-1avcodec-56avformat-56swscale-3postproc-53avfilter-5avdevice-56-landroid#用于native_window${log-lib} )#cmake_minimum_required(VERSION 3.4.1)
#
#
#find_library( # Sets the name of the path variable.
#        log-lib
#        log )
#
#set(distribution_DIR ${CMAKE_SOURCE_DIR}/libs)
#
#add_library( fmod
#        SHARED
#        IMPORTED )
#set_target_properties( fmod
#        PROPERTIES IMPORTED_LOCATION
#        ${distribution_DIR}/${ANDROID_ABI}/libfmod.so )
#add_library( fmodL
#        SHARED
#        IMPORTED )
#set_target_properties( fmodL
#        PROPERTIES IMPORTED_LOCATION
#        ${distribution_DIR}/${ANDROID_ABI}/libfmodL.so )
#add_library( sound
#        SHARED
#        src/main/cpp/sound.cpp )
#
#include_directories(src/main/cpp/inc)
#
#target_link_libraries( sound fmod fmodL
#        ${log-lib} )#方式二
#cmake_minimum_required(VERSION 3.4.1)
#
## 把系统的log库导入进来
#find_library( log-lib
#        log )
#
#set(distribution_DIR ../../../../libs)
#
## 把libfmod.so预加载进来
#add_library( fmod
#        SHARED
#        IMPORTED)
#set_target_properties( fmod
#        PROPERTIES IMPORTED_LOCATION
#        ${distribution_DIR}/${ANDROID_ABI}/libfmod.so)
#
## 把libfmodL.so预加载进来
#add_library( fmodL
#        SHARED
#        IMPORTED)
#set_target_properties( fmodL
#        PROPERTIES IMPORTED_LOCATION
#        ${distribution_DIR}/${ANDROID_ABI}/libfmodL.so)
#
##set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11"
#
#
#add_library( native-lib
#        SHARED
#        src/main/cpp/native-lib.cpp )
#
#include_directories(src/main/cpp/inc)
#
#target_link_libraries( native-lib
#        fmod
#        fmodL
#        ${log-lib} )

native-lib,其中包含了前面章节讲到的视频解码功能代码

#include <jni.h>
#include <string>
#include <cstdlib>
#include <android/log.h>
#include <unistd.h>#define MAX_AUDIO_FRME_SIZE 48000 * 4
using namespace std;
extern "C"//由于是C和C++混编,必须添加
{
//编码
#include "libavcodec/avcodec.h"
//封装格式处理
#include "libavformat/avformat.h"
//像素处理
#include "libswscale/swscale.h"
#include "libavutil/avutil.h"
#include "libavutil/frame.h"
//重采样
#include "libswresample/swresample.h"
}#define LOGI(FORMAT, ...) __android_log_print(ANDROID_LOG_INFO,"jason",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT, ...) __android_log_print(ANDROID_LOG_ERROR,"jason",FORMAT,##__VA_ARGS__);extern "C" JNIEXPORT jstring JNICALL
Java_com_lee_jniffmpeg_MainActivity_stringFromJNI(JNIEnv *env,jobject /* this */) {string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}extern "C"
JNIEXPORT void JNICALL
Java_com_lee_jniffmpeg_VideoUtils_decode(JNIEnv *env, jclass clazz, jstring input_jstr,jstring output_jstr) {
//需要转码的视频文件(输入的视频文件)const char *input_cstr = env->GetStringUTFChars(input_jstr, nullptr);const char *output_cstr = env->GetStringUTFChars(output_jstr, nullptr);//1.注册所有组件av_register_all();//封装格式上下文,统领全局的结构体,保存了视频文件封装格式的相关信息AVFormatContext *pFormatCtx = avformat_alloc_context();//2.打开输入视频文件if (avformat_open_input(&pFormatCtx, input_cstr, NULL, NULL) != 0) {LOGE("%s", "无法打开输入视频文件");return;}//3.获取视频文件信息if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {LOGE("%s", "无法获取视频文件信息");return;}//获取视频流的索引位置//遍历所有类型的流(音频流、视频流、字幕流),找到视频流int v_stream_idx = -1;int i = 0;//number of streamsfor (; i < pFormatCtx->nb_streams; i++) {//流的类型if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {v_stream_idx = i;break;}}if (v_stream_idx == -1) {LOGE("%s", "找不到视频流\n");return;}//只有知道视频的编码方式,才能够根据编码方式去找到解码器//获取视频流中的编解码上下文AVCodecContext *pCodecCtx = pFormatCtx->streams[v_stream_idx]->codec;//4.根据编解码上下文中的编码id查找对应的解码AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);//(迅雷看看,找不到解码器,临时下载一个解码器)if (pCodec == NULL) {LOGE("%s", "找不到解码器\n");return;}//5.打开解码器if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {LOGE("%s", "解码器无法打开\n");return;}//输出视频信息LOGI("视频的文件格式:%s", pFormatCtx->iformat->name);int time = static_cast<int>(pFormatCtx->duration) / 1000000;LOGI("视频时长:%d", time);LOGI("视频的宽高:%d,%d", pCodecCtx->width, pCodecCtx->height);LOGI("解码器的名称:%s", pCodec->name);//准备读取//AVPacket用于存储一帧一帧的压缩数据(H264)//缓冲区,开辟空间AVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));//AVFrame用于存储解码后的像素数据(YUV)//内存分配AVFrame *pFrame = av_frame_alloc();//YUV420AVFrame *pFrameYUV = av_frame_alloc();//只有指定了AVFrame的像素格式、画面大小才能真正分配内存//缓冲区分配内存uint8_t *out_buffer = (uint8_t *) av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));//初始化缓冲区avpicture_fill((AVPicture *) pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width,pCodecCtx->height);//用于转码(缩放)的参数,转之前的宽高,转之后的宽高,格式等struct SwsContext *sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,pCodecCtx->pix_fmt,pCodecCtx->width, pCodecCtx->height,AV_PIX_FMT_YUV420P,SWS_BICUBIC, NULL, NULL, NULL);int got_picture, ret;FILE *fp_yuv = fopen(output_cstr, "wb+");int frame_count = 0;//6.一帧一帧的读取压缩数据while (av_read_frame(pFormatCtx, packet) >= 0) {//只要视频压缩数据(根据流的索引位置判断)if (packet->stream_index == v_stream_idx) {//7.解码一帧视频压缩数据,得到视频像素数据ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);if (ret < 0) {LOGE("%s", "解码错误");return;}//为0说明解码完成,非0正在解码if (got_picture) {//AVFrame转为像素格式YUV420,宽高//2 6输入、输出数据//3 7输入、输出画面一行的数据的大小 AVFrame 转换是一行一行转换的//4 输入数据第一列要转码的位置 从0开始//5 输入画面的高度sws_scale(sws_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height,pFrameYUV->data, pFrameYUV->linesize);//输出到YUV文件//AVFrame像素帧写入文件//data解码后的图像像素数据(音频采样数据)//Y 亮度 UV 色度(压缩了) 人对亮度更加敏感//U V 个数是Y的1/4int y_size = pCodecCtx->width * pCodecCtx->height;fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv);fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv);fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv);frame_count++;LOGI("解码第%d帧", frame_count);}}//释放资源av_free_packet(packet);}fclose(fp_yuv);env->ReleaseStringUTFChars(input_jstr, input_cstr);env->ReleaseStringUTFChars(output_jstr, output_cstr);av_frame_free(&pFrame);avcodec_close(pCodecCtx);avformat_free_context(pFormatCtx);
}extern "C"
JNIEXPORT void JNICALL
Java_com_lee_jniffmpeg_VideoUtils_sound(JNIEnv *env, jclass clazz, jstring input_jstr,jstring output_jstr) {const char *input_cstr = env->GetStringUTFChars(input_jstr, nullptr);const char *output_cstr = env->GetStringUTFChars(output_jstr, nullptr);LOGI("%s", "sound");//注册组件av_register_all();AVFormatContext *pFormatCtx = avformat_alloc_context();//打开音频文件if (avformat_open_input(&pFormatCtx, input_cstr, NULL, NULL) != 0) {LOGI("%s", "无法打开音频文件");return;}//获取输入文件信息if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {LOGI("%s", "无法获取输入文件信息");return;}//获取音频流索引位置int i = 0, audio_stream_idx = -1;for (; i < pFormatCtx->nb_streams; i++) {if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {audio_stream_idx = i;break;}}//获取解码器AVCodecContext *codecCtx = pFormatCtx->streams[audio_stream_idx]->codec;AVCodec *codec = avcodec_find_decoder(codecCtx->codec_id);if (codec == NULL) {LOGI("%s", "无法获取解码器");return;}//打开解码器if (avcodec_open2(codecCtx, codec, NULL) < 0) {LOGI("%s", "无法打开解码器");return;}//压缩数据AVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));//解压缩数据AVFrame *frame = av_frame_alloc();//frame->16bit 44100 PCM 统一音频采样格式与采样率SwrContext *swrCtx = swr_alloc();//重采样设置参数-------------start//输入的采样格式enum AVSampleFormat in_sample_fmt = codecCtx->sample_fmt;//输出采样格式16bit PCMenum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;//输入采样率int in_sample_rate = codecCtx->sample_rate;//输出采样率int out_sample_rate = 44100;//获取输入的声道布局//根据声道个数获取默认的声道布局(2个声道,默认立体声stereo)//av_get_default_channel_layout(codecCtx->channels);uint64_t in_ch_layout = codecCtx->channel_layout;//输出的声道布局(立体声)uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;swr_alloc_set_opts(swrCtx,out_ch_layout, out_sample_fmt, out_sample_rate,in_ch_layout, in_sample_fmt, in_sample_rate,0, NULL);swr_init(swrCtx);//输出的声道个数int out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout);//重采样设置参数-------------end//16bit 44100 PCM 数据uint8_t *out_buffer = (uint8_t *) av_malloc(MAX_AUDIO_FRME_SIZE);LOGI("output_cstr=%s", output_cstr);FILE *fp_pcm = fopen(output_cstr, "wb");//这里有一个坑,如果存储空间不够则会返回nullint got_frame = 0, index = 0, ret;//不断读取压缩数据while (av_read_frame(pFormatCtx, packet) >= 0) {//解码ret = avcodec_decode_audio4(codecCtx, frame, &got_frame, packet);if (ret < 0) {LOGI("%s", "解码完成");}//解码一帧成功if (got_frame > 0) {LOGI("解码:%d", index++);swr_convert(swrCtx, &out_buffer, MAX_AUDIO_FRME_SIZE,const_cast<const uint8_t **>(frame->data), frame->nb_samples);LOGI("%s", "jian1---");//获取sample的sizeint out_buffer_size = av_samples_get_buffer_size(NULL, out_channel_nb,frame->nb_samples, out_sample_fmt, 1);LOGI("%s", "jian2---");if (fp_pcm == nullptr) {LOGI("%s", "jian3---");}fwrite(out_buffer, 1, static_cast<size_t>(out_buffer_size), fp_pcm);}av_free_packet(packet);}fclose(fp_pcm);av_frame_free(&frame);av_free(out_buffer);swr_free(&swrCtx);avcodec_close(codecCtx);avformat_close_input(&pFormatCtx);env->ReleaseStringUTFChars(input_jstr, input_cstr);env->ReleaseStringUTFChars(output_jstr, output_cstr);
}
//调用Android自带的方法
extern "C"
JNIEXPORT void JNICALL
Java_com_lee_jniffmpeg_VideoUtils_audioTrack(JNIEnv *env, jobject jthiz, jstring input_jstr,jstring output_jstr) {const char *input_cstr = env->GetStringUTFChars(input_jstr, NULL);const char *output_cstr = env->GetStringUTFChars(output_jstr, NULL);LOGI("%s", "sound");//注册组件av_register_all();AVFormatContext *pFormatCtx = avformat_alloc_context();//打开音频文件if (avformat_open_input(&pFormatCtx, input_cstr, NULL, NULL) != 0) {LOGI("%s", "无法打开音频文件");return;}//获取输入文件信息if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {LOGI("%s", "无法获取输入文件信息");return;}//获取音频流索引位置int i = 0, audio_stream_idx = -1;for (; i < pFormatCtx->nb_streams; i++) {if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {audio_stream_idx = i;break;}}//获取解码器AVCodecContext *codecCtx = pFormatCtx->streams[audio_stream_idx]->codec;AVCodec *codec = avcodec_find_decoder(codecCtx->codec_id);if (codec == NULL) {LOGI("%s", "无法获取解码器");return;}//打开解码器if (avcodec_open2(codecCtx, codec, NULL) < 0) {LOGI("%s", "无法打开解码器");return;}//压缩数据AVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));//解压缩数据AVFrame *frame = av_frame_alloc();//frame->16bit 44100 PCM 统一音频采样格式与采样率SwrContext *swrCtx = swr_alloc();//重采样设置参数-------------start//输入的采样格式enum AVSampleFormat in_sample_fmt = codecCtx->sample_fmt;//输出采样格式16bit PCMenum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;//输入采样率int in_sample_rate = codecCtx->sample_rate;//输出采样率int out_sample_rate = in_sample_rate;//获取输入的声道布局//根据声道个数获取默认的声道布局(2个声道,默认立体声stereo)//av_get_default_channel_layout(codecCtx->channels);uint64_t in_ch_layout = codecCtx->channel_layout;//输出的声道布局(立体声)uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;swr_alloc_set_opts(swrCtx,out_ch_layout, out_sample_fmt, out_sample_rate,in_ch_layout, in_sample_fmt, in_sample_rate,0, NULL);swr_init(swrCtx);//输出的声道个数int out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout);//重采样设置参数-------------end//JNI begin------------------//VideoUtilsjclass player_class = env->GetObjectClass(jthiz);//AudioTrack对象jmethodID create_audio_track_mid = env->GetMethodID(player_class, "createAudioTrack","(II)Landroid/media/AudioTrack;");jobject audio_track = env->CallObjectMethod(jthiz, create_audio_track_mid, out_sample_rate,out_channel_nb);//调用AudioTrack.play方法jclass audio_track_class = env->GetObjectClass(audio_track);jmethodID audio_track_play_mid = env->GetMethodID(audio_track_class, "play", "()V");env->CallVoidMethod(audio_track, audio_track_play_mid);//AudioTrack.writejmethodID audio_track_write_mid = env->GetMethodID(audio_track_class, "write", "([BII)I");//JNI end------------------FILE *fp_pcm = fopen(output_cstr, "wb");//16bit 44100 PCM 数据uint8_t *out_buffer = (uint8_t *) av_malloc(MAX_AUDIO_FRME_SIZE);int got_frame = 0, index = 0, ret;//不断读取压缩数据while (av_read_frame(pFormatCtx, packet) >= 0) {//解码音频类型的Packetif (packet->stream_index == audio_stream_idx) {//解码ret = avcodec_decode_audio4(codecCtx, frame, &got_frame, packet);if (ret < 0) {LOGI("%s", "解码完成");}//解码一帧成功if (got_frame > 0) {LOGI("解码:%d", index++);swr_convert(swrCtx, &out_buffer, MAX_AUDIO_FRME_SIZE,(const uint8_t **) frame->data, frame->nb_samples);//获取sample的sizeint out_buffer_size = av_samples_get_buffer_size(NULL, out_channel_nb,frame->nb_samples, out_sample_fmt,1);fwrite(out_buffer, 1, out_buffer_size, fp_pcm);//out_buffer缓冲区数据,转成byte数组jbyteArray audio_sample_array = env->NewByteArray(out_buffer_size);jbyte *sample_bytep = env->GetByteArrayElements(audio_sample_array, NULL);//out_buffer的数据复制到sampe_bytepmemcpy(sample_bytep, out_buffer, out_buffer_size);//同步env->ReleaseByteArrayElements(audio_sample_array, sample_bytep, 0);//AudioTrack.write PCM数据env->CallIntMethod(audio_track, audio_track_write_mid,audio_sample_array, 0, out_buffer_size);//释放局部引用env->DeleteLocalRef(audio_sample_array);usleep(1000 * 16);}}av_free_packet(packet);}av_frame_free(&frame);av_free(out_buffer);swr_free(&swrCtx);avcodec_close(codecCtx);avformat_close_input(&pFormatCtx);env->ReleaseStringUTFChars(input_jstr, input_cstr);env->ReleaseStringUTFChars(output_jstr, output_cstr);
}

build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {compileSdkVersion 28defaultConfig {applicationId "com.lee.jniffmpeg"minSdkVersion 15targetSdkVersion 28versionCode 1versionName "1.0"testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"externalNativeBuild {cmake {cppFlags "-frtti -fexceptions"}}//ndk编译生成.so文件ndk {moduleName "ffmpeg"         //生成的so名字abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'  //输出指定三种abi体系结构下的so库。}}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}externalNativeBuild {cmake {path file('src/main/cpp/CMakeLists.txt')}}}dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"implementation 'com.android.support:appcompat-v7:28.0.0'implementation 'com.android.support.constraint:constraint-layout:1.1.3'testImplementation 'junit:junit:4.12'androidTestImplementation 'com.android.support.test:runner:1.0.2'androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

引入ffmpeg所需要的so文件,具体编译生成方式参考前面的篇章

查看class中方法的签名,首先编译工程,将编写的java类全部转化为.class文件
使用cd指令进入到/build/intermediates/javac/debug文件下,找到需要查看方法签名的.class文件,使用javap -s 类名,即可查看jni获取methodid时所需要的签名文件

  1. 编写相应的kotlin和java代码
package com.lee.jniffmpeg;import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;public class VideoUtils {public native static void decode(String input, String output);public native static void sound(String input, String ouput);public native void audioTrack(String input, String ouput);/*** 创建一个AudioTrac对象,用于播放** @param nb_channels* @return*/public AudioTrack createAudioTrack(int sampleRateInHz, int nb_channels) {//固定格式的音频码流int audioFormat = AudioFormat.ENCODING_PCM_16BIT;Log.i("hhh", "nb_channels:" + nb_channels);//声道布局int channelConfig;if (nb_channels == 1) {channelConfig = android.media.AudioFormat.CHANNEL_OUT_MONO;} else if (nb_channels == 2) {channelConfig = android.media.AudioFormat.CHANNEL_OUT_STEREO;} else {channelConfig = android.media.AudioFormat.CHANNEL_OUT_STEREO;}int bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,sampleRateInHz, channelConfig,audioFormat,bufferSizeInBytes, AudioTrack.MODE_STREAM);//播放//audioTrack.play();//写入PCM//audioTrack.write(audioData, offsetInBytes, sizeInBytes);return audioTrack;}static {
//        System.loadLibrary("avutil-54");
//        System.loadLibrary("swresample-1");
//        System.loadLibrary("avcodec-56");
//        System.loadLibrary("avformat-56");
//        System.loadLibrary("swscale-3");
//        System.loadLibrary("postproc-53");
//        System.loadLibrary("avfilter-5");
//        System.loadLibrary("avdevice-56");System.loadLibrary("ffmpeg");}
}

注意,以上audioTrack的jni实现的方式是通过调用系统的AudioTrack来实现,sound的native方法则是通过ffmpeg来实现的。

将音频文件或者视频文件拖动到手机SD卡中进行代码实现即可运行

FFmpeg实现音频解码并播放相关推荐

  1. ffmpeg进行音频解码,QAudioOutput播放解码后的音频

    项目在此 https://github.com/qyvlik/AudioTestByFFmpeg 首先我们按照Qt给的官方例子初始化一个QAudioFormat,设置好他的各项参数,ffmpeg解码后 ...

  2. ffmpeg播放器(二)音频解码与播放

    音频解码和播放的前面准备工作和视频的格式差不多,创建两个线程分别解码和播放,这里统一只放代码了. void AudioChannel::play() {//设置为播放状态packets.setWork ...

  3. 音视频开发之旅(36) -FFmpeg +OpenSL ES实现音频解码和播放

    目录 OpenSL ES基本介绍 OpenSL ES播放音频流程 代码实现 遇到的问题 资料 收获 上一篇我们通过AudioTrack实现了FFmpeg解码后的PCM音频数据的播放,在Android上 ...

  4. FFMpeg.AutoGen+D2D解码并播放视频(含音频流)

    最近在捣鼓FFMpeg这个东西,可惜网上的资料实在难找,对于c#里面的FFmpeg.AutoGen更是如此.所以走了不少弯路.(语言组织能力不太好,这篇文章的东西会很杂.涉及到d2d绘图的部分,我封装 ...

  5. ffmpeg库音频解码示例

    #include <stdio.h> #include <stdlib.h> extern "C"{// #include "avcodec.h& ...

  6. 基于 FFMPEG 的音频编解码(三):音频编码

    音频编码 基于 FFMPEG 的音频编解码(一):Hello FFMPEG,安装与编译 基于 FFMPEG 的音频编解码(二):音频解码 基于 FFMPEG 的音频编解码(三):音频编码 在前面文章中 ...

  7. ffmpeg 音频解码二

    1. ffmpeg 视频解码一 2. ffmpeg 视频解码二 3. ffmpeg 音频解码一 4. ffmpeg 音频解码二 5. ffmpeg 音视频解码 6. ffmpeg 视频编码一 7. f ...

  8. FFmpeg音频解码流程详解及简单demo参考

    本文主要讲解FFmpeg的音频解码具体流程,API使用.最后再以一个非常简单的demo演示将一个mp3格式的音频文件解码为原始数据pcm文件. 本文主要基于FFmpeg音频解码新接口. 一.FFmpe ...

  9. FFmpeg Visual Studio开发(四):音频解码

    上一篇文章我们学习了如何通过FFmpeg解码视频帧,本篇文章我们来学习如何解码音频帧.文章分段讲解视频解码的各个步骤,接着会贴上完整代码,最后进行测试. 准备工作 在开始学习前,我们先准备一个文件夹( ...

最新文章

  1. 一文带你学会国产加密算法SM4的java实现方案
  2. linux下C语言套接字编程sockaddr和sockaddr_in的区别
  3. 【视频课】零基础免费38课时深度学习+超60小时CV核心算法+15大Pytorch CV实践案例助你攻略CV...
  4. 切换用户_Mac如何在多个用户间快速切换?
  5. OpenFlow和SDN的历史和原理介绍
  6. 【洛谷 P2513】 [HAOI2009]逆序对数列(DP)
  7. [SDOI2017]遗忘的集合
  8. el-cascader超出屏幕问题
  9. 高效实用Kafka-入门介绍
  10. 慕课网 机器学习任务 笔记
  11. afm原子力分析软件_AFM数据处理软件|原子力显微镜配套数据处理软件 nanoscope analysis1.8 官方版 - 极光站...
  12. 回顾+纪念:离开帝都的第一年
  13. echarts瀑布图_Echarts自定义瀑布图开发
  14. 北京54坐标与西安80坐标相互转换的两种方法
  15. [概率论]艾波寧捎信(poisson分布)
  16. FMCW激光雷达科普(上):基本概念、技术路线、优势及争议和误解
  17. Secure CRT连接华三模拟器和华为模拟器(CRT通过pipe连接华三模拟器)
  18. 计算机专业用什么轴的键盘,机械键盘什么轴好
  19. 算法 | 03 字符串(KMP)
  20. random.seed(seed)、np.random.seed(seed)、torch.manual_seed(seed)作用

热门文章

  1. 1157 Anniversary
  2. php安装和环境配置
  3. 通过iscsi协议使用ceph rbd
  4. 安卓手机如何直接跟Mac电脑互传文件
  5. LTE终端开机流程----同步和小区驻留
  6. USB设备驱动开发之扩展(利用USB虚拟总线驱动模拟USB摄像头)
  7. 这几种常见的伪学习,看下你是不是也中招了?
  8. 教你5个大咖用的CMD命令,让人一看你就是个电脑高手
  9. php 事件,php的事件处理机制(回调函数)
  10. ECCV2022|何恺明团队开源ViTDet:只用普通ViT,不做分层设计也能搞定目标检测