音视频:12.FFmpeg-音乐播放器1
FFmpeg-音乐播放器
- 1.获取音频 Meta 信息
- 2.解码音频数据
- 3.播放音频
具体代码请看:NDKPractice项目的ffmpeg83
先来看下流程图:
1.获取音频 Meta 信息
extern "C" JNIEXPORT void JNICALL
Java_com_east_ffmpeg83_media_JaminPlayer_nPlay(JNIEnv *env, jobject instance,jstring url_) {const char *url = env->GetStringUTFChars(url_,0);// 1.初始化所有组件,只有调用了该函数,才能使用复用器和编解码器(源码)av_register_all();// 2.初始化网络avformat_network_init();AVFormatContext *pFormatContext = NULL;int formatOpenInputRes = -1;int formatFindStreamInfoRes = -1;int audioStreamIndex = -1;AVStream *audio_stream;// 3.打开输入formatOpenInputRes = avformat_open_input(&pFormatContext,url,NULL,NULL);if(formatOpenInputRes !=0 ){// 第一件事,需要回调给 Java层// 第二件 事,需要释放资源LOGE("format open input error:%s",av_err2str(formatOpenInputRes));// 一般不推荐这么写,但是的确很方便。goto __av_resources_destroy;}// 4.找出输入流的信息formatFindStreamInfoRes = avformat_find_stream_info(pFormatContext,NULL);if(formatFindStreamInfoRes < 0){LOGE("format find stream info error: %s",av_err2str(formatFindStreamInfoRes));goto __av_resources_destroy;}// 5.查找音频流的 indexaudioStreamIndex = av_find_best_stream(pFormatContext,AVMediaType::AVMEDIA_TYPE_AUDIO,-1,-1,NULL,0);if(audioStreamIndex < 0){LOGE("format audio stream error");goto __av_resources_destroy;}// 获取采样率和通道audio_stream = pFormatContext->streams[audioStreamIndex];LOGE("采样率:%d, 通道数: %d", audio_stream->codecpar->sample_rate, audio_stream->codecpar->channels);__av_resources_destroy:if(pFormatContext != NULL){avformat_close_input(&pFormatContext);avformat_free_context(pFormatContext);pFormatContext = NULL;}avformat_network_deinit();env->ReleaseStringUTFChars(url_,url);
}
2.解码音频数据
关于解码函数 avcodec_decode_audio4 已经过时了,取而代之的是 avcodec_send_packet 和 avcodec_receive_frame 。
// 6.查找解码pCodecParameters = pFormatContext->streams[audioStreamIndex]->codecpar;pCodec = avcodec_find_decoder(pCodecParameters->codec_id);if(pCodec == NULL){LOGE("codec find audio decoder error");goto __av_resources_destroy;}// 7.创建一个解码器的上下文pCodecContext = avcodec_alloc_context3(pCodec);if(pCodecContext == NULL){LOGE("codec alloc context error");goto __av_resources_destroy;}// 8.根据参数值填充Codec上下文参数codecParametersToContextRes = avcodec_parameters_to_context(pCodecContext,pCodecParameters);if (codecParametersToContextRes < 0) {LOGE("codec parameters to context error: %s", av_err2str(codecParametersToContextRes));goto __av_resources_destroy;}// 9.打开解码器codecOpenRes = avcodec_open2(pCodecContext,pCodec,NULL);if (codecOpenRes != 0) {LOGE("codec audio open error: %s", av_err2str(codecOpenRes));goto __av_resources_destroy;}pPacket = av_packet_alloc();pFrame = av_frame_alloc();// 循环从上下文中读取帧到包中while(av_read_frame(pFormatContext,pPacket) >= 0){if(pPacket->stream_index == audioStreamIndex){// Packet 包,压缩的数据,解码成 pcm 数据int codecSendPacketRes = avcodec_send_packet(pCodecContext,pPacket);if(codecSendPacketRes == 0){int codecReceiveFrameRes = avcodec_receive_frame(pCodecContext,pFrame);if(codecReceiveFrameRes == 0){// AVPacket -> AVFrameindex++;LOGE("解码第 %d 帧",index);}}}// 解引用av_packet_unref(pPacket);av_frame_unref(pFrame);}// 1.解引用数据 data, 2.销魂 pPacket 结构体内存, 3.pPacket = NULL;av_packet_free(&pPacket);av_frame_free(&pFrame);__av_resources_destroy:if(pCodecContext != NULL){avcodec_close(pCodecContext);avcodec_free_context(&pCodecContext);pCodecContext = NULL;}if(pFormatContext != NULL){avformat_close_input(&pFormatContext);avformat_free_context(pFormatContext);pFormatContext = NULL;}avformat_network_deinit();env->ReleaseStringUTFChars(url_,url);
3.播放音频
播放 pcm 数据目前比较流行的有两种方式,一种是通过 Android 的 AudioTrack 来播放,另一种是采用跨平台的 OpenSLES(直接用的底层操作驱动播放) 来播放,
个人比较倾向于用更加高效的 OpenSLES 来播放音频,大家可以先看看 Google 官方的 native-audio 事例,这里我们先采用 AudioTrack 来播放
AudioTrack 来播:
- 创建 AudioTack 对象
- 启动循环,设置为播放状态 (play)
- 把数据推到指定数组中 (write)
jobject createAudioTrack(JNIEnv *env){/*AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,int bufferSizeInBytes, int mode)*/jclass jAudioTrackClass = env->FindClass("android/media/AudioTrack");jmethodID jAudioTackCMid = env->GetMethodID(jAudioTrackClass,"<init>","(IIIIII)V");int streamType = 3;int sampleRateInHz = AUDIO_SAMPLE_RATE;int channelConfig = (0x4 | 0x8);int audioFormat = 2;int mode = 1;// int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat)jmethodID getMinBufferSizeMID = env->GetStaticMethodID(jAudioTrackClass,"getMinBufferSize","(III)I");int bufferSizeInBytes = env->CallStaticIntMethod(jAudioTrackClass,getMinBufferSizeMID,sampleRateInHz,channelConfig,audioFormat);LOGE("bufferSizeInBytes = %d",bufferSizeInBytes);jobject jAudioTrackObj = env->NewObject(jAudioTrackClass,jAudioTackCMid,streamType,sampleRateInHz,channelConfig,audioFormat,bufferSizeInBytes,mode);// playjmethodID playMid = env->GetMethodID(jAudioTrackClass,"play","()V");env->CallVoidMethod(jAudioTrackObj,playMid);return jAudioTrackObj;
}{// 10.使用AudioTrack播放jAudioTrackClass = env->FindClass("android/media/AudioTrack");// public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes)jWriteMid = env->GetMethodID(jAudioTrackClass,"write","([BII)I");jAudioTrackObj = createAudioTrack(env);pPacket = av_packet_alloc();pFrame = av_frame_alloc();// 循环从上下文中读取帧到包中while(av_read_frame(pFormatContext,pPacket) >= 0){if(pPacket->stream_index == audioStreamIndex){// Packet 包,压缩的数据,解码成 pcm 数据int codecSendPacketRes = avcodec_send_packet(pCodecContext,pPacket);if(codecSendPacketRes == 0){int codecReceiveFrameRes = avcodec_receive_frame(pCodecContext,pFrame);if(codecReceiveFrameRes == 0){// AVPacket -> AVFrameindex++;LOGE("解码第 %d 帧",index);// write 写到缓冲区 pFrame.data -> javabyte// size 是多大,装 pcm 的数据// 1s 44100 点,2通道, 2字节 44100*2*2// 1帧不是一秒,pFrame->nb_samples点int dataSize = av_samples_get_buffer_size(NULL,pFrame->channels,pFrame->nb_samples,pCodecContext->sample_fmt,0);jbyteArray jPcmByteArray = env->NewByteArray(dataSize);// native 创建 c 数组jbyte *jPcmData= env->GetByteArrayElements(jPcmByteArray,NULL);memcpy(jPcmData,pFrame->data,dataSize);// 0 把 c 的数组的数据同步到 jbyteArray,然后释放native数组env->ReleaseByteArrayElements(jPcmByteArray,jPcmData,0);env->CallIntMethod(jAudioTrackObj,jWriteMid,jPcmByteArray,0,dataSize);// 解除 jPcmDataArray 的持有,让 javaGC 回收env->DeleteLocalRef(jPcmByteArray);}}}// 解引用av_packet_unref(pPacket);av_frame_unref(pFrame);}// 1.解引用数据 data, 2.销魂 pPacket 结构体内存, 3.pPacket = NULL;av_packet_free(&pPacket);av_frame_free(&pFrame);env->DeleteLocalRef(jAudioTrackObj);__av_resources_destroy:if(pCodecContext != NULL){avcodec_close(pCodecContext);avcodec_free_context(&pCodecContext);pCodecContext = NULL;}if(pFormatContext != NULL){avformat_close_input(&pFormatContext);avformat_free_context(pFormatContext);pFormatContext = NULL;}avformat_network_deinit();env->ReleaseStringUTFChars(url_,url);
}
这块播放还是有杂音,而且内存持续上涨,下节我们通过多线程播放来解决
音视频:12.FFmpeg-音乐播放器1相关推荐
- html乐谱播放器,html5 canvas音谱可视化mp3音乐播放器代码
特效描述:html5 canvas 音谱可视化 mp3音乐 播放器代码.html5 canvas通过AudioContext对获取audio标签播放的音域和音高,调用js绘制相应高度的矩形或线条. 代 ...
- java音乐播放器视频_java 实现音乐播放器的简单实例
java 实现音乐播放器的简单实例 实现效果图: 代码如下 package cn.hncu.games; import java.applet.Applet; import java.applet.A ...
- 音视频系列---最强播放器推荐
PotPlayer 地址:https://daumpotplayer.com/download/ 基础功能: Daum PotPlayer -完全免费的多功能多媒体播放器,具有内置编解码器和灵活的设置 ...
- 单片机音乐播放器课程设计C语言,单片机课程设计简易音乐播放器
单片机课程设计简易音乐播放器 论文题目:简易音乐播放器所属系部:电子工程系 指导教师: 学生姓名: 学 号: 专 业: 电子信息工程技术 题目: 简易音乐播放器任务与要求:本设计以 MCS-51 系列 ...
- 基于润和DAYU200开发套件的OpenHarmony分布式音乐播放器
前言: DAYU200开发套件是润和软件推出的首款支持OpenHarmony富设备开发套件,目前已全面到货,可到如下全网指定官方购买链接购买: 淘宝:https://item.taobao.com/i ...
- 【041】基于51单片机的自动音乐播放器Proteus仿真设计
一.压缩包资料内容 (1).基于51单片机的自动音乐播放器系统proteus仿真设计一份: (2).基于51单片机的自动音乐播放器系统proteus仿真设计keli源代码一份: (3).基于51单片机 ...
- iOS多种刷新样式、音乐播放器、仿抖音视频、旅游App等源码
iOS精选源码 企业级开源项目,模仿艺龙旅行App 3D立体相册,可以旋转的立方体 横竖屏切换工具,使用陀螺仪检测手机设备方向,锁屏状- Swift版Refresh(可以自定义多种样式)架构方面有所优 ...
- Android 音视频深入 十三 OpenSL ES 制作音乐播放器,能暂停和调整音量(附源码下载)...
项目地址 https://github.com/979451341/OpenSLAudio OpenSL ES 是基于NDK也就是c语言的底层开发音频的公开API,通过使用它能够做到标准化, 高性能, ...
- FFmpeg - 打造一款万能的音乐播放器
从 c/c++ 基础.jni 基础.c/c++ 进阶.数据结构和算法.linux 内核.CMake 语法.Shell 脚本绕了一大圈之后,总算是勉强可以来写 FFmpeg 了,以上这些基础大家可以看下 ...
- 本地服务器的音乐如何才能播放视频文件夹,mx播放器如何添加本地文件 添加本地文件设置方法...
更换播放器皮肤/主题颜色 在这个版本中 主题颜色的设置十分丰富 不再拘泥于原来的黑白二色 点击"设置"选择"列表"项 修改主题,选择一种自己喜欢的颜色 现在有十 ...
最新文章
- intel python加速效果初探
- 5. extjs 中buttonAlign什么意思
- Firefox OS, 向jQuery中XMLHttpRequest构造函数中传参,实现跨域请求数据
- when to book didi?
- 比较两张大小相同的照片的差异,返回数值
- python web应用_为您的应用选择最佳的Python Web爬网库
- C#控制台程序,发送邮件,可带附件
- ElasticSearch filter查询
- Vue 2.x 文件夹目录
- 面试官系统精讲Java源码及大厂真题 - 03 Java 常用关键字理解
- 建议重写equals方法时也一并重写hashCode方法
- 遇到一个Linux系统文件被删除后仍占用磁盘的问题
- 磨刀不误砍柴工,ORAchk健康检查好帮手
- 基于SSM的电影购票系统
- MySQL 的主从复制
- mongodb数组操作
- 机器学习十大算法原理总结
- HTTP Authorization
- 如何生成dll文件 采用VS2017生成dll文件(动态库文件)和lib文件(静态库文件)以C语言为例
- 普通IPC接入神目爱买系统操作说明(智能人脸抓拍盒利旧方案)