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 来播:

  1. 创建 AudioTack 对象
  2. 启动循环,设置为播放状态 (play)
  3. 把数据推到指定数组中 (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相关推荐

  1. html乐谱播放器,html5 canvas音谱可视化mp3音乐播放器代码

    特效描述:html5 canvas 音谱可视化 mp3音乐 播放器代码.html5 canvas通过AudioContext对获取audio标签播放的音域和音高,调用js绘制相应高度的矩形或线条. 代 ...

  2. java音乐播放器视频_java 实现音乐播放器的简单实例

    java 实现音乐播放器的简单实例 实现效果图: 代码如下 package cn.hncu.games; import java.applet.Applet; import java.applet.A ...

  3. 音视频系列---最强播放器推荐

    PotPlayer 地址:https://daumpotplayer.com/download/ 基础功能: Daum PotPlayer -完全免费的多功能多媒体播放器,具有内置编解码器和灵活的设置 ...

  4. 单片机音乐播放器课程设计C语言,单片机课程设计简易音乐播放器

    单片机课程设计简易音乐播放器 论文题目:简易音乐播放器所属系部:电子工程系 指导教师: 学生姓名: 学 号: 专 业: 电子信息工程技术 题目: 简易音乐播放器任务与要求:本设计以 MCS-51 系列 ...

  5. 基于润和DAYU200开发套件的OpenHarmony分布式音乐播放器

    前言: DAYU200开发套件是润和软件推出的首款支持OpenHarmony富设备开发套件,目前已全面到货,可到如下全网指定官方购买链接购买: 淘宝:https://item.taobao.com/i ...

  6. 【041】基于51单片机的自动音乐播放器Proteus仿真设计

    一.压缩包资料内容 (1).基于51单片机的自动音乐播放器系统proteus仿真设计一份: (2).基于51单片机的自动音乐播放器系统proteus仿真设计keli源代码一份: (3).基于51单片机 ...

  7. iOS多种刷新样式、音乐播放器、仿抖音视频、旅游App等源码

    iOS精选源码 企业级开源项目,模仿艺龙旅行App 3D立体相册,可以旋转的立方体 横竖屏切换工具,使用陀螺仪检测手机设备方向,锁屏状- Swift版Refresh(可以自定义多种样式)架构方面有所优 ...

  8. Android 音视频深入 十三 OpenSL ES 制作音乐播放器,能暂停和调整音量(附源码下载)...

    项目地址 https://github.com/979451341/OpenSLAudio OpenSL ES 是基于NDK也就是c语言的底层开发音频的公开API,通过使用它能够做到标准化, 高性能, ...

  9. FFmpeg - 打造一款万能的音乐播放器

    从 c/c++ 基础.jni 基础.c/c++ 进阶.数据结构和算法.linux 内核.CMake 语法.Shell 脚本绕了一大圈之后,总算是勉强可以来写 FFmpeg 了,以上这些基础大家可以看下 ...

  10. 本地服务器的音乐如何才能播放视频文件夹,mx播放器如何添加本地文件 添加本地文件设置方法...

    更换播放器皮肤/主题颜色 在这个版本中 主题颜色的设置十分丰富 不再拘泥于原来的黑白二色 点击"设置"选择"列表"项 修改主题,选择一种自己喜欢的颜色 现在有十 ...

最新文章

  1. intel python加速效果初探
  2. 5. extjs 中buttonAlign什么意思
  3. Firefox OS, 向jQuery中XMLHttpRequest构造函数中传参,实现跨域请求数据
  4. when to book didi?
  5. 比较两张大小相同的照片的差异,返回数值
  6. python web应用_为您的应用选择最佳的Python Web爬网库
  7. C#控制台程序,发送邮件,可带附件
  8. ElasticSearch filter查询
  9. Vue 2.x 文件夹目录
  10. 面试官系统精讲Java源码及大厂真题 - 03 Java 常用关键字理解
  11. 建议重写equals方法时也一并重写hashCode方法
  12. 遇到一个Linux系统文件被删除后仍占用磁盘的问题
  13. 磨刀不误砍柴工,ORAchk健康检查好帮手
  14. 基于SSM的电影购票系统
  15. MySQL 的主从复制
  16. mongodb数组操作
  17. 机器学习十大算法原理总结
  18. HTTP Authorization
  19. 如何生成dll文件 采用VS2017生成dll文件(动态库文件)和lib文件(静态库文件)以C语言为例
  20. 普通IPC接入神目爱买系统操作说明(智能人脸抓拍盒利旧方案)

热门文章

  1. 《软件工程》第4章需求工程
  2. 我为什么选择博客园!
  3. Python调用淘宝ip库API实现地址显示
  4. 关于TTMS影院系统的总结
  5. influxdb遇到时间点存储不了
  6. NodeMCU文档中文翻译 4 烧写固件
  7. 专题-句向量(Sentence Embedding)
  8. 总结!Github年度最强Python库排行榜
  9. 新Macbook入手后必安装的好用的软件
  10. 华为云WeLink云空间专题(上篇:WeLink云空间是什么?)