ffmpeg 音乐循环_Android使用FFmpeg(四)--ffmpeg实现音频播放(使用AudioTrack进行播放)

本文详细介绍了如何在Android中使用FFmpeg实现音乐循环播放,通过AudioTrack进行音频播放。涵盖了从注册组件、打开音频文件、解码到重采样、最后通过AudioTrack播放音频的完整过程。同时,展示了Java层和JNI层的交互,以及AudioTrack的使用方法。
摘要由CSDN通过智能技术生成

关于

准备工作

正文

和视频播放一样我们依照流程图来实现,从这个流程图可以看出,音频播放和视频播放的大概流程并没有多大差别,只是细节之处需要处理。

依照流程来实现

1.注册组件,打开音频文件并获取内容,找到音频流:

av_register_all();

AVFormatContext *pFormatCtx = avformat_alloc_context();

//open

if (avformat_open_input(&pFormatCtx, input, NULL, NULL) != 0) {

LOGE("%s","打开输入视频文件失败");

return;

}

//获取视频信息

if(avformat_find_stream_info(pFormatCtx,NULL) < 0){

LOGE("%s","获取视频信息失败");

return;

}

int audio_stream_idx=-1;

int i=0;

for (int i = 0; i < pFormatCtx->nb_streams; ++i) {

if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {

LOGE(" 找到音频id %d", pFormatCtx->streams[i]->codec->codec_type);

audio_stream_idx=i;

break;

}

}

2.获取解码的装置,申请avframe和avpacket:

//获取解码的装置上下文

AVCodecContext *pCodecCtx=pFormatCtx->streams[audio_stream_idx]->codec;

//获取解码的装置

AVCodec *pCodex = avcodec_find_decoder(pCodecCtx->codec_id);

//打开解码的装置

if (avcodec_open2(pCodecCtx, pCodex, NULL)<0) {

}

//申请avpakcet,装解码前的数据

AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));

//申请avframe,装解码后的数据

AVFrame *frame = av_frame_alloc();

3.初始化SwrContext,进行重采样

//得到SwrContext ,进行重采样,具体参考http://blog.csdn.net/jammg/article/details/52688506

SwrContext *swrContext = swr_alloc();

//缓存区

uint8_t *out_buffer = (uint8_t *) av_malloc(44100 * 2);

//输出的声道布局(立体声)

uint64_t out_ch_layout=AV_CH_LAYOUT_STEREO;

//输出采样位数 16位

enum AVSampleFormat out_formart=AV_SAMPLE_FMT_S16;

//输出的采样率必须与输入相同

int out_sample_rate = pCodecCtx->sample_rate;

//swr_alloc_set_opts将PCM源文件的采样格式转换为自己希望的采样格式

swr_alloc_set_opts(swrContext, out_ch_layout, out_formart, out_sample_rate,

pCodecCtx->channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0,

NULL);

swr_init(swrContext);

4.通过while循环读取内容,并通过AudioTrack进行播放:

// 获取通道数 2

int out_channer_nb = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);

// 反射得到Class类型

jclass david_player = env->GetObjectClass(instance);

// 反射得到createAudio方法

jmethodID createAudio = env->GetMethodID(david_player, "createTrack", "(II)V");

// 反射调用createAudio

env->CallVoidMethod(instance, createAudio, 44100, out_channer_nb);

jmethodID audio_write = env->GetMethodID(david_player, "playTrack", "([BI)V");

int got_frame;

while (av_read_frame(pFormatCtx, packet) >= 0) {

if (packet->stream_index == audio_stream_idx) {

// 解码 mp3 编码格式frame----pcm frame

avcodec_decode_audio4(pCodecCtx, frame, &got_frame, packet);

if (got_frame) {

LOGE("解码");

swr_convert(swrContext, &out_buffer, 44100 * 2, (const uint8_t **) frame->data, frame->nb_samples);

// 缓冲区的大小

int size = av_samples_get_buffer_size(NULL, out_channer_nb, frame->nb_samples,

AV_SAMPLE_FMT_S16, 1);

jbyteArray audio_sample_array = env->NewByteArray(size);

env->SetByteArrayRegion(audio_sample_array, 0, size, (const jbyte *) out_buffer);

env->CallVoidMethod(instance, audio_write, audio_sample_array, size);

env->DeleteLocalRef(audio_sample_array);

}

}

}

5.释放需要释放的资源:

av_frame_free(&frame);

swr_free(&swrContext);

avcodec_close(pCodecCtx);

avformat_close_input(&pFormatCtx);

env->ReleaseStringUTFChars(input_, input);

java层创建AudioTrack方法。

jni通过调用java层的audiotrack方法来实现播放,但是java层的audiotrack方法又是通过调用底层的openesl es来进行播放,相当于绕了一个圈,所以在下篇文章中将实现使用opensl es来直接播放音频。

public class MusicPlay {

static{

System.loadLibrary("avcodec-56");

System.loadLibrary("avdevice-56");

System.loadLibrary("avfilter-5");

System.loadLibrary("avformat-56");

System.loadLibrary("avutil-54");

System.loadLibrary("postproc-53");

System.loadLibrary("swresample-1");

System.loadLibrary("swscale-3");

System.loadLibrary("native-lib");

}

public native void playSound(String input);

private AudioTrack audioTrack;

// 这个方法 是C进行调用

public void createTrack(int sampleRateInHz,int nb_channals) {

int channaleConfig;//通道数

if (nb_channals == 1) {

channaleConfig = AudioFormat.CHANNEL_OUT_MONO;

} else if (nb_channals == 2) {

channaleConfig = AudioFormat.CHANNEL_OUT_STEREO;

}else {

channaleConfig = AudioFormat.CHANNEL_OUT_MONO;

}

int buffersize=AudioTrack.getMinBufferSize(sampleRateInHz,

channaleConfig, AudioFormat.ENCODING_PCM_16BIT);

audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,sampleRateInHz,channaleConfig,

AudioFormat.ENCODING_PCM_16BIT,buffersize,AudioTrack.MODE_STREAM);

audioTrack.play();

}

//C传入音频数据

public void playTrack(byte[] buffer, int lenth) {

if (audioTrack != null && audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {

audioTrack.write(buffer, 0, lenth);

}

}

}

小结

按照流程,使用AudioTrack播放音频就是这么回事。通过和上篇的播放视频相比较,流程就差不多了:注册->解封装->获取流->解码->播放。

源码地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用ffmpeg解码音频时,如果出现爆音问题,可能是由于以下几个原因引起的: 1. 音频数据的采样率不匹配:爆音可能是由于音频数据的采样率与播放设备的采样率不一致导致的。在使用ffmpeg解码音频之前,需要确保将音频数据的采样率设置为与播放设备相匹配的值。 2. 编码格式不正确:爆音问题还可能是由于音频数据的编码格式不正确导致的。确保使用正确的编码格式进行解码,并正确设置解码器的参数。 3. 音频数据损坏:如果音频数据本身存在损坏或错误,可能会导致爆音问题。在解码之前,可以检查音频数据的完整性,并尝试使用其他音频文件进行解码测试。 4. 音量控制不当:爆音问题可能还与音频的音量控制有关。确保在解码和播放过程中适当地控制音频的音量,避免音频过大或过小导致的爆音问题。 综上所述,当使用ffmpeg解码音频出现爆音问题时,可以检查采样率、编码格式、音频数据的完整性以及音量控制等方面的因素,逐一排查并解决问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [ffmpeg解码音频并保存PCM项目](https://download.csdn.net/download/u010683380/10930684)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [关于雷霄骅博士的博客FFMPEG+SDL的音频播放播放有杂音的问题](https://blog.csdn.net/m0_47472749/article/details/127528809)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [使用AudioTrack播放FFmpeg解码的PCM音频数据](https://blog.csdn.net/u012944685/article/details/104586574)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值