音频解码和视频解码有很多是相似的,我们直接贴代码
Java_com_example_ffmpegapplication_MyAudioPlayer_playaudio(JNIEnv *env, jobject instance,
jstring src_, jstring des_) {
const char *src = env->GetStringUTFChars(src_, 0);
const char *des = env->GetStringUTFChars(des_, 0);
avformat_network_init();
//总上下文
AVFormatContext *pContext = avformat_alloc_context();
//字典
AVDictionary *pDictionary = NULL;
av_dict_set(&pDictionary, "timeout", "3000000", 0);
// 打开源文件输入流
if(avformat_open_input(&pContext, src, NULL, &pDictionary) != 0) {
avformat_free_context(pContext);
return;
}
// 获取视频流信息,通知ffmpeg把流解析出来
if(avformat_find_stream_info(pContext,NULL) < 0){
LOGI("%s","无法获取输入文件信息");
return;
}
//音频流索引
int audio_stream_index = -1;
//nb_streams:视频里面流的数量,比如流0是视频,流1是音频,流2是字幕
for(int i = 0; i < pContext->nb_streams; i++) {
//codecpar:解码器参数,旧版本的是codec, codec_type:解码器类型。
if(pContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
//如果是音频流,保存索引
audio_stream_index = i;
break;
}
}
//音频流的解码参数
AVCodecParameters *pParameters = pContext->streams[audio_stream_index]->codecpar;
//通过解码器id找到解码器
AVCodec *pCodec = avcodec_find_decoder(pParameters->codec_id);
//创建解码器上下文
AVCodecContext *pCodecContext = avcodec_alloc_context3(pCodec);
//将解码器参数copy到解码器上下文
avcodec_parameters_to_context(pCodecContext, pParameters);
//打开解码器
avcodec_open2(pCodecContext, pCodec, &pDictionary);
//音频转换上下文
SwrContext *pSwrContext = swr_alloc();
//输入参数
//输入采样格式
AVSampleFormat in_sample = pCodecContext->sample_fmt;
//输入采样率
int in_sample_rate = pCodecContext->sample_rate;
//输入声道布局
uint64_t in_ch_layout = pCodecContext->channel_layout;
//输出参数 固定
//输出采样格式
AVSampleFormat out_sample = AV_SAMPLE_FMT_S16;
//输出采样率
int out_sample_rate = 44100;
//输出声道布局 AV_CH_LAYOUT_STEREO 双通道
uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
//设置转换参数
swr_alloc_set_opts(pSwrContext,out_ch_layout,out_sample,out_sample_rate
,in_ch_layout,in_sample,in_sample_rate,0,NULL);
//初始化转换器
swr_init(pSwrContext);
//为输出文件申请内存
uint8_t *out_buffer = (uint8_t *)(av_malloc(2 * 44100));
FILE *fp_pcm = fopen(des, "wb");
//数据存储在一个AVPacket中 旧版本需要用户自己去malloc。新版本的可以理解为:待解码队列
AVPacket *pPacket = av_packet_alloc();
//解码后的yuv数据容器
AVFrame *pFrame = av_frame_alloc();
while (av_read_frame(pContext, pPacket) >= 0) {
//AVCodecContext *avctx, const AVPacket *avpkt 从待解码队列中取出视频流数据发送给解码器
avcodec_send_packet(pCodecContext, pPacket);
//AVCodecContext *avctx, AVFrame *frame 从解码器取出解码后到数据(yuv)到frame
int ret = avcodec_receive_frame(pCodecContext, pFrame);
if (ret == AVERROR(EAGAIN)) {
continue;
} else if (ret < 0) {
LOGI("%s","解码完成");
break;
}
//frame ---->统一的格式
swr_convert(pSwrContext, &out_buffer, 2 * 44100,
(const uint8_t **)pFrame->data, pFrame->nb_samples);
int out_channerl_nb= av_get_channel_layout_nb_channels(out_ch_layout);
//缓冲区的 大小
int out_buffer_size= av_samples_get_buffer_size(NULL, out_channerl_nb, pFrame->nb_samples, out_sample, 1);
fwrite(out_buffer,1, out_buffer_size, fp_pcm);
}
fclose(fp_pcm);
av_free(out_buffer);
swr_free(&pSwrContext);
//释放容器
av_frame_free(&pFrame);
//释放AVPacket
av_packet_free(&pPacket);
//释放解码器上下文
avcodec_free_context(&pCodecContext);
//关闭文件
avformat_close_input(&pContext);
//释放总上下文
avformat_free_context(pContext);
env->ReleaseStringUTFChars(src_, src);
env->ReleaseStringUTFChars(des_, des);
}
我们来分析一下代码,相同的就不在看了
初始化网路
打开文件流
初始化解码器
转换器的初始化和使用
- struct SwrContext *swr_alloc(void);
音频转换上下文
使用swr_free释放 - struct SwrContext *swr_alloc_set_opts(struct SwrContext *s,
int64_t out_ch_layout, enum AVSampleFormat out_sample_fmt, int out_sample_rate,
int64_t in_ch_layout, enum AVSampleFormat in_sample_fmt, int in_sample_rate,
int log_offset, void *log_ctx);
设置转换参数
音频要播放出来,就需要把原始数据转换成固定格式的数据。比如代码中,不管原始数据的参数如何。都转成固定的输出采样格式、输出采样率、输出声道布局 - int swr_init(struct SwrContext *s);
初始化转换器,必须调用 - int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,
const uint8_t **in , int in_count);
转换文件到固定格式
输出文件
- uint8_t *out_buffer = (uint8_t *)(av_malloc(2 * 44100));
我们看下这个代码里面,申请的内存大小是244100,这个大小一般就是:通道数采样率 - int av_get_channel_layout_nb_channels(uint64_t channel_layout);
获取通道数 - int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples,
enum AVSampleFormat sample_fmt, int align);
获取这一帧声音的实际大小 - fwrite(out_buffer,1, out_buffer_size, fp_pcm);
这里面第二个参数是1,是因为音频的最小单位就是1个字节。如果是像素,就是4个字节
最终在输出目录下生成一个pcm文件
网友评论