ffmpeg实现音频resample(重采样)(二)

本篇文章将增加AVFifoBuffer和音频样本是av_sample_fmt_is_planar的样式采样率讲解,下面上代码

AVFifoBuffer * m_fifo = NULL;

SwrContext * init_pcm_resample(AVFrame *in_frame, AVFrame *out_frame)
{
	SwrContext * swr_ctx = NULL;
	swr_ctx = swr_alloc();
	if (!swr_ctx)
	{
		printf("swr_alloc error \n");
		return NULL;
	}
	AVCodecContext * audio_dec_ctx = icodec->streams[audio_stream_idx]->codec;
	AVSampleFormat sample_fmt;
	sample_fmt = (AVSampleFormat)m_dwBitsPerSample; //样本
	if (audio_dec_ctx->channel_layout == 0)
	{
		audio_dec_ctx->channel_layout = av_get_default_channel_layout(icodec->streams[audio_stream_idx]->codec->channels);
	}
	/* set options */
	av_opt_set_int(swr_ctx, "in_channel_layout",    audio_dec_ctx->channel_layout, 0);
	av_opt_set_int(swr_ctx, "in_sample_rate",       audio_dec_ctx->sample_rate, 0);
	av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", audio_dec_ctx->sample_fmt, 0);

	av_opt_set_int(swr_ctx, "out_channel_layout",    audio_dec_ctx->channel_layout, 0);
	av_opt_set_int(swr_ctx, "out_sample_rate",       audio_dec_ctx->sample_rate, 0);
	av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", sample_fmt, 0);
	swr_init(swr_ctx);

	int64_t src_nb_samples = in_frame->nb_samples;
	out_frame->nb_samples = av_rescale_rnd(swr_get_delay(swr_ctx,oaudio_st->codec->sample_rate) + src_nb_samples,
		oaudio_st->codec->sample_rate, oaudio_st->codec->sample_rate, AV_ROUND_UP);

	int ret = av_samples_alloc(out_frame->data, &out_frame->linesize[0],
		icodec->streams[audio_stream_idx]->codec->channels, out_frame->nb_samples,oaudio_st->codec->sample_fmt,1);
	if (ret < 0)
	{
		return NULL;
	}

	//pcm分包初始化
	int buffersize = av_samples_get_buffer_size(NULL, oaudio_st->codec->channels,
		2048, oaudio_st->codec->sample_fmt, 1);
	m_fifo = av_fifo_alloc(buffersize);
	return swr_ctx;
}

int preform_pcm_resample(SwrContext * pSwrCtx,AVFrame *in_frame, AVFrame *out_frame)
{
	int ret = 0;
	if (pSwrCtx != NULL)
	{
		ret = swr_convert(pSwrCtx, out_frame->data, out_frame->nb_samples,
			(const uint8_t**)in_frame->data, in_frame->nb_samples);
		if (ret < 0)
		{
			return -1;
		}
		//修改分包内存
		int buffersize = av_samples_get_buffer_size(&out_frame->linesize[0], oaudio_st->codec->channels,
			ret, oaudio_st->codec->sample_fmt, 1);
		int sss = av_fifo_size(m_fifo);
		sss = av_fifo_realloc2(m_fifo, av_fifo_size(m_fifo) + out_frame->linesize[0]);
		sss = av_fifo_size(m_fifo);
		av_fifo_generic_write(m_fifo, out_frame->data[0], out_frame->linesize[0], NULL);

		out_frame->pkt_pts = in_frame->pkt_pts;
		out_frame->pkt_dts = in_frame->pkt_dts;
		//有时pkt_pts和pkt_dts不同,并且pkt_pts是编码前的dts,这里要给avframe传入pkt_dts而不能用pkt_pts
		//out_frame->pts = out_frame->pkt_pts;
		out_frame->pts = in_frame->pkt_dts;
	}
	return 0;
}
void uinit_pcm_resample(AVFrame * poutframe,SwrContext * swr_ctx)
{
	if (poutframe)
	{
		avcodec_free_frame(&poutframe);
		poutframe = NULL;
	}
	if (swr_ctx)
	{
		swr_free(&swr_ctx);
		swr_ctx = NULL;
	}
	//析构pcm分包结构
	if(m_fifo)
	{
		av_fifo_free(m_fifo);
		m_fifo = NULL;
	}
}
int perform_code(int stream_type,AVFrame * picture)
{
	AVCodecContext *cctext = NULL;
	AVPacket pkt_t;
	av_init_packet(&pkt_t);
	pkt_t.data = NULL; // packet data will be allocated by the encoder
	pkt_t.size = 0;
	int frameFinished = 0 ;

	if (stream_type == AUDIO_ID)
	{
		cctext = oaudio_st->codec;
		//如果进和出的的声道,样本,采样率不同,需要重采样
		if(icodec->streams[audio_stream_idx]->codec->sample_fmt != (AVSampleFormat)m_dwBitsPerSample ||
			icodec->streams[audio_stream_idx]->codec->channels != m_dwChannelCount ||
			icodec->streams[audio_stream_idx]->codec->sample_rate != m_dwFrequency)
		{
			int64_t pts_t = picture->pts;
			int duration_t = (double)cctext->frame_size * (icodec->streams[audio_stream_idx]->time_base.den /icodec->streams[audio_stream_idx]->time_base.num)/
				icodec->streams[audio_stream_idx]->codec->sample_rate;

			int frame_bytes = cctext->frame_size * av_get_bytes_per_sample(cctext->sample_fmt)* cctext->channels;
			AVFrame * pFrameResample = avcodec_alloc_frame();
			uint8_t * readbuff = new uint8_t[frame_bytes];

			if(av_sample_fmt_is_planar(cctext->sample_fmt))
			{
				frame_bytes /= cctext->channels;
			}

			while (av_fifo_size(m_fifo) >= frame_bytes) //取出写入的未读的包
			{
				pFrameResample->nb_samples = cctext->frame_size;
				av_fifo_generic_read(m_fifo, readbuff, frame_bytes, NULL);

				//这里一定要考虑音频分片的问题
				//如果是分片的avcodec_fill_audio_frame传入的buf是单声道的,但是buf_size 是两个声道加一起的数据量
				//如果不是分片的avcodec_fill_audio_frame传入的buf是双声道的,buf_size 是两个声道加一起的数据量
				if(av_sample_fmt_is_planar(cctext->sample_fmt))
				{
					avcodec_fill_audio_frame(pFrameResample,cctext->channels,cctext->sample_fmt,readbuff,frame_bytes * cctext->channels,1);
				}
				else
				{
					avcodec_fill_audio_frame(pFrameResample,cctext->channels,cctext->sample_fmt,readbuff,frame_bytes,0);
				}

				if(m_is_first_audio_pts == 0)
				{
					m_first_audio_pts = pts_t;
					m_is_first_audio_pts = 1;
				}
				pFrameResample->pts = m_first_audio_pts;
				m_first_audio_pts += duration_t;

				pFrameResample->pts = av_rescale_q_rnd(pFrameResample->pts, icodec->streams[audio_stream_idx]->codec->time_base, oaudio_st->codec->time_base, AV_ROUND_NEAR_INF);
				nRet = avcodec_encode_audio2(cctext,&pkt_t,pFrameResample,&frameFinished);
				if (nRet>=0 && frameFinished)
				{
					write_frame(ocodec,AUDIO_ID,pkt_t);
					av_free_packet(&pkt_t);
				}
			}
			if (readbuff)
			{
				delete []readbuff;
			}
			if (pFrameResample)
			{
				av_free(pFrameResample);
				pFrameResample = NULL;
			}
		}
		else
		{
			nRet = avcodec_encode_audio2(cctext,&pkt_t,picture,&frameFinished);
			if (nRet>=0 && frameFinished)
			{
				write_frame(ocodec,AUDIO_ID,pkt_t);
				av_free_packet(&pkt_t);
			}
		}
	}
	else if (stream_type == VIDEO_ID)
	{
		cctext = ovideo_st->codec;
		if(icodec->streams[video_stream_idx]->codec->ticks_per_frame != 1)
		{
			AVRational time_base_video_t;
			time_base_video_t.num = icodec->streams[video_stream_idx]->codec->time_base.num;
			time_base_video_t.den = icodec->streams[video_stream_idx]->codec->time_base.den /icodec->streams[video_stream_idx]->codec->ticks_per_frame;
			picture->pts = av_rescale_q_rnd(picture->pts, time_base_video_t, ovideo_st->codec->time_base, AV_ROUND_NEAR_INF);
		}
		else
		{
			picture->pts = av_rescale_q_rnd(picture->pts, icodec->streams[video_stream_idx]->codec->time_base, ovideo_st->codec->time_base, AV_ROUND_NEAR_INF);
		}
		avcodec_encode_video2(cctext,&pkt_t,picture,&frameFinished);
		picture->pts++;
		if (frameFinished)
		{
			write_frame(ocodec,VIDEO_ID,pkt_t);
			av_free_packet(&pkt_t);
		}
	}
	return 1;
}

1:由于mp3的sample是1152 aac是1024 有时候将解码的mp3编码成aac时如果不做AVFifoBuffer操作,编码的aac音频sample会比原来的少很多,生成的音频会一卡一卡的明显少声音。

2:当要编码的音频样本是av_sample_fmt_is_planar分片的时候需要将解码后的视频添加到AVFrame结构体中:但是如图

不知道ffmpeg什么这么设计或者我用的不对。不过这样用是成功的。

交流请加QQ群:62054820

QQ:379969650

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-05 17:29:32

ffmpeg实现音频resample(重采样)(二)的相关文章

FFMPEG视音频编解码零基础学习方法-b

感谢大神分享,虽然现在还看不懂,留着大家一起看啦 PS:有不少人不清楚“FFmpeg”应该怎么读.它读作“ef ef em peg” 0. 背景知识 本章主要介绍一下FFMPEG都用在了哪里(在这里仅列几个我所知的,其实远比这个多).说白了就是为了说明:FFMPEG是非常重要的. 使用FFMPEG作为内核视频播放器: Mplayer,ffplay,射手播放器,暴风影音,KMPlayer,QQ影音... 使用FFMPEG作为内核的Directshow Filter: ffdshow,lav fil

[总结]FFMPEG视音频编解码零基础学习方法【转】

本文转载自:http://blog.csdn.net/leixiaohua1020/article/details/15811977 在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“大神”和初学者之间好像有一个不可逾越的鸿沟.“大神”们水平高超,探讨着深奥的问题:而初学者们还停留在入门阶段.究竟是什么原因造成的这种“两极分化”呢?最后,我发现了问

最简单的基于FFMPEG的音频编码器(PCM编码为AAC)

本文介绍一个最简单的基于FFMPEG的音频编码器.该编码器实现了PCM音频採样数据编码为AAC的压缩编码数据.编码器代码十分简单,可是每一行代码都非常重要.通过看本编码器的源码.能够了解FFMPEG音频编码的流程. 本程序使用最新版的类库(编译时间为2014.5.6).开发平台为VC2010.全部的配置都已经做好,仅仅须要执行就能够了. 流程(2014.9.29更新) 以下附一张使用FFmpeg编码音频的流程图. 使用该流程.不仅能够编码AAC的音频,并且能够编码MP3,MP2等等各种FFmpe

又是正版!Win下ffmpeg源码调试分析二(Step into ffmpeg from Opencv for bugs in debug mode with MSVC)

最近工作忙一直没时间写,但是看看网络上这方面的资源确实少,很多都是linux的(我更爱unix,哈哈),而且很多是直接引入上一篇文章的编译结果来做的.对于使用opencv但是又老是被ffmpeg库坑害的朋友们,可能又爱又恨,毕竟用它处理和分析视频是第一选择,不仅是因为俩者配合使用方便,而且ffmpeg几乎囊括了我所知道的所有解编码器,但是正是因为这个导致了一些bug很难定位,所以有必要考虑一下如何快速定位你的ffmpeg bug. sorry,废话多了.首先给个思路: 1.使opencv 的hi

FFMPEG处理音频时间戳的主要逻辑

来源:http://www.xuebuyuan.com/1466771.html FFMPEG处理音频时间戳的主要逻辑 2013年12月09日 ⁄ 综合 ⁄ 共 2226字 ⁄ 字号 小 中 大 ⁄ 评论关闭 FFMPEG处理音频时间戳的主要逻辑是: 1. demux读取AVPacket.以输入flv为例,timebase是1/1000,第一个音频包可能是46,代表0.046秒. 2. decoder解码AVPacket为AVFrame,frame的pts为NOPTS,需要设置,否则后面都会有问

FFMPEG视音频编解码零基础学习方法

在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“大神”和初学者之间好像有一个不可逾越的鸿沟.“大神”们水平高超,探讨着深奥的问题:而初学者们还停留在入门阶段.究竟是什么原因造成的这种“两极分化”呢?最后,我发现了问题的关键:FFMPEG难度比较大,却没有一个循序渐进,由简单到复杂的教程.现在网上的有关FFMPEG的教程多半难度比较大,不太适合刚接

[总结]FFMPEG视音频编解码零基础学习方法

转至 http://my.oschina.net/leixiaohua1020/blog/302174 在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“大神”和初学者之间好像有一个不可逾越的鸿沟.“大神”们水平高超,探讨着深奥的问题:而初学者们还停留在入门阶段.究竟是什么原因造成的这种“两极分化”呢?最后,我发现了问题的关键:FFMPEG难度比较

转[总结]FFMPEG视音频编解码零基础学习方法 .

http://blog.csdn.net/leixiaohua1020/article/details/15811977 在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“大神”和初学者之间好像有一个不可逾越的鸿沟.“大神”们水平高超,探讨着深奥的问题:而初学者们还停留在入门阶段.究竟是什么原因造成的这种“两极分化”呢?最后,我发现了问题的关键:F

FFMPEG视音频编解码零基础学习方法 【荐】

在CSDN上的这一段日子,接触到了很多同行业的人,尤其是使用FFMPEG进行视音频 编解码的人,有的已经是有多年经验的“大神”,有的是刚开始学习的初学者.在和大家探讨的过程中,我忽然发现了一个问题:在“大神”和初学者之间好像有一 个不可逾越的鸿沟.“大神”们水平高超,探讨着深奥的问题:而初学者们还停留在入门阶段.究竟是什么原因造成的这种“两极分化”呢?最后,我发现了问题的 关键:FFMPEG难度比较大,却没有一个循序渐进,由简单到复杂的教程.现在网上的有关FFMPEG的教程多半难度比较大,不太适