2020-08-14

新旧版ffmpeg版本的解码接口说明及注意事项

(1)旧版的解码函数avcodec_decode_audio4()用起来是挺好用的,就是只要设置好编码器上下文AVCodecContext *avctx、分配好输入AVFrame *frame的空间,以及输出的const AVPacket *avpkt,就可以进行解码和获取解码后的数据了。

//以往版本的解码函数
//函数原型
int avcodec_decode_audio4(AVCodecContext *avctx, AVFrame *frame,
                          int *got_frame_ptr, const AVPacket *avpkt);

//函数实例
ret = avcodec_decode_audio4(cod_ctx, frame, &got_picture, packet);

(2)新版本的解码函数

它的作用是将需要解码的数据送入到解码队列中,来依次进行解码,因此就存在一个旧版本没有的问题,新版在解码时可能是一帧一帧的解码,即可能此时送进去的AVPacket *avpkt的data包含多个帧的话,此时就需要使用avcodec_receive_frame多次来接收解码后的数据,这个是需要注意的,不然仍然像旧版的avcodec_decode_audio4的使用的话会报错而导致解码失败。

//新版本的解码函数接口
int avcodec_send_packet(AVCodecContext *avctx, const AVPacket *avpkt);
int avcodec_receive_frame(AVCodecContext *avctx, AVFrame *frame);

(3)以下代码是一段新版解码的实例,实例中存在纰漏请斟酌参考使用或可以与我交流学习。该实例主要是完成送来来aac音频数据来进行解码成s16P的音频数据,因为解码后得到的是fltp格式的,所以需要经过重采样才得到S16P格式的,对于这两种格式都是带P的,因此是需要分开存储的,详情可以参考https://blog.csdn.net/disadministrator/article/details/43734335?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159737528919725247629371%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=159737528919725247629371&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v3~pc_rank_v2-1-43734335.first_rank_ecpm_v3_pc_rank_v2&utm_term=s16+fltp&spm=1018.2118.3001.4187

/* 发送编码数据包给解码器 */
	
    ret = avcodec_send_packet(dec_ctx, pkt);
	printf("avcodec_send_packet,ret:%d\n", ret);
	if (ret < 0) 
	{
		char err[1024] = { 0 };
		av_strerror(ret, err, sizeof(err) - 1);
		printf("%s\n", err);
		//fprintf(stderr, "Error submitting the packet to the decoder, ret:%d\n", ret);
		return;
	}
	printf("avcodec_send_packet,ret:%d\n", ret);
	//getchar();

	/* 读取所有的输出帧*/
	while (ret >= 0) 
	{
		ret = avcodec_receive_frame(dec_ctx, frame);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
			return;
		else if (ret < 0) 
		{
			fprintf(stderr, "Error during decoding\n");
			//getchar();
			return;
			//exit(1);
		}
		data_size = av_get_bytes_per_sample(dec_ctx->sample_fmt);
		if (data_size < 0) 
		{
			/* 不应该出现的情况 */
			fprintf(stderr, "Failed to calculate data size\n");
			//exit(1);
			getchar();
			return ;
		}

		//重采样
		SwrContext *asc = NULL;	//重采样上下文
		//1.音频重采样 上下文初始化
		asc = swr_alloc_set_opts(asc, av_get_default_channel_layout(channels), outSamplefmt, samplerate,
			av_get_default_channel_layout(channels), inSamplefmt, samplerate, 0, 0);
		if (!asc)
		{
			printf("swr_alloc_set_opts failed\n");
			getchar();
			return ;
		}
		ret = swr_init(asc);
		if (ret != 0)
		{
			printf("swr_init failed\n");
			getchar();
			return ;
		}
		printf("重采样初始化成功\n");

		//2.重采样输出分配空间
		AVFrame *pcm = NULL;	//重采样输出空间						
		pcm = av_frame_alloc();
		pcm->format = outSamplefmt;
		pcm->channels = channels;
		pcm->channel_layout = av_get_default_channel_layout(channels);
		pcm->nb_samples = 1024;	//一帧音频一通道的采样数量
		ret = av_frame_get_buffer(pcm, 0);//给pcm分配存储空间
		if (ret != 0)
		{
			printf("重采样分配输出空间失败\n");
			getchar();
			return;
		}

		//3.进程重采样数据
		const uint8_t *indata[AV_NUM_DATA_POINTERS] = { 0 };
		indata[0] = (uint8_t *)frame->data[0];
		indata[1] = (uint8_t *)frame->data[1];
		printf("pcm->nb_samples:%d\n", pcm->nb_samples);
		//getchar();
		int len = swr_convert(asc, pcm->data, pcm->nb_samples, indata, pcm->nb_samples);
		printf("swr_convert, len:%d\n", len);
		//getchar();
		//getchar();
#if 0
		printf("data_size:%d\n", data_size);
		for (i = 0; i < frame->nb_samples; i++)
			for (ch = 0; ch < dec_ctx->channels; ch++)
				fwrite(frame->data[ch] + data_size*i, 1, data_size, outfile);
#endif
		printf("data_size:%d\n", data_size);
		for (i = 0; i < pcm->nb_samples; i++)
			for (ch = 0; ch < dec_ctx->channels; ch++)
				fwrite(pcm->data[ch] + 2*i, 1, 2, outfile);	//转换完成之后,还是分为两通道,不过每个通道由原来的fltp的4个字节变为s16p的2个字节,还是需要交替的保存才能正常播放
	}

猜你喜欢

转载自blog.csdn.net/weixin_41353840/article/details/108000466