上篇学习了视频的播放,这篇学习音频的播放;关于音频还需要了解音频的一些知识,这里推荐一篇博客:https://blog.csdn.net/fireroll/article/details/83032025
音频播放大致流程和视频是同样的;
代码中只是将MP3文件解码后转换为原数据保存到文件了并没有做播放功能,这里只贴出音频转化代码,详细代码上传GitHub:https://github.com/YWeiY/MyPlayer
Java_com_yan_myplayer2_AudioPlayer_native_1start(JNIEnv *env, jobject instance, jstring input_,
jstring output_) {
const char *input = env->GetStringUTFChars(input_, 0);
const char *output = env->GetStringUTFChars(output_, 0);
// 初始化网络模块
avformat_network_init();
// 打开音频文件
AVFormatContext *avFormatContext = avformat_alloc_context();
if (avformat_open_input(&avFormatContext, input, NULL, NULL) != 0) {
LOGI("%s","无法打开音频文件");
return;
}
// 获取输入文件信息
if (avformat_find_stream_info(avFormatContext, NULL) < 0) {
LOGI("%s","无法获取输入文件信息");
return;
}
// 获取音频时长
int audio_stream_idx = -1;
for (int i = 0; i < avFormatContext->nb_streams; ++i) {
if (avFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
audio_stream_idx = i;
break;
}
}
// 获取解码参数
AVCodecParameters *avCodecParameters = avFormatContext->streams[audio_stream_idx]->codecpar;
//获取解码器
AVCodec *avCodec = avcodec_find_decoder(avCodecParameters->codec_id);
// 解码器上下文
AVCodecContext *avCodecContext = avcodec_alloc_context3(avCodec);
// 将解码参数和解码器上下文绑定
avcodec_parameters_to_context(avCodecContext, avCodecParameters);
avcodec_open2(avCodecContext, avCodec, NULL);
// 读取音频数据包
AVPacket *avPacket = av_packet_alloc();
// 设置数据转换配置
SwrContext *swrContext = swr_alloc();
int64_t out_ch_layout = AV_CH_LAYOUT_STEREO;//输出声道布局
AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;//输出采样格式
int out_sample_rate = 44100;//输出采样率
int64_t in_ch_layout = avCodecContext->channel_layout; // 输入声道布局
AVSampleFormat in_sample_fmt = avCodecContext->sample_fmt;// 输入采样格式
int in_sample_rate = avCodecContext->sample_rate;// 输入采样率
swr_alloc_set_opts(swrContext, out_ch_layout, out_sample_fmt, out_sample_rate, in_ch_layout,
in_sample_fmt, in_sample_rate, 0, NULL);
// 初始化转化器默认参数
swr_init(swrContext);
int count = 0;
// 初始化一个缓存区容器
uint8_t *out_buffer = (uint8_t *) (av_malloc(2 * 44100));
// 打开
FILE *file = fopen(output, "wb");
while (av_read_frame(avFormatContext, avPacket) >= 0) {
avcodec_send_packet(avCodecContext, avPacket);
AVFrame *avFrame = av_frame_alloc();
int ret = avcodec_receive_frame(avCodecContext, avFrame);
if (ret == AVERROR(EAGAIN)) {
continue;
} else if (ret < 0) {
LOGE("解码完成");
break;
}
if (avPacket->stream_index != audio_stream_idx) {
continue;
}
LOGE("正在解码%d",count++);
// frame --> 转换为统一格式
swr_convert(swrContext, &out_buffer, 2 * 44100,
(const uint8_t **) (avFrame->data), avFrame->nb_samples);
int nb_channels = av_get_channel_layout_nb_channels(out_ch_layout);
// 计算缓冲区大小
int buffer_count = av_samples_get_buffer_size(NULL, nb_channels, avFrame->nb_samples,
out_sample_fmt, 1);
// out_buffer --> FILE 输出到文件
fwrite(out_buffer, 1, buffer_count, file);
}
// 关闭
fclose(file);
av_free(out_buffer);
swr_free(&swrContext);
avcodec_close(avCodecContext);
avformat_close_input(&avFormatContext);
env->ReleaseStringUTFChars(input_, input);
env->ReleaseStringUTFChars(output_, output);
}