ffmpeg关于音频的总结(一)

一:关于音频分片的问题

enum AVSampleFormat {
	AV_SAMPLE_FMT_NONE = -1,
	AV_SAMPLE_FMT_U8,          ///< unsigned 8 bits
	AV_SAMPLE_FMT_S16,         ///< signed 16 bits
	AV_SAMPLE_FMT_S32,         ///< signed 32 bits
	AV_SAMPLE_FMT_FLT,         ///< float
	AV_SAMPLE_FMT_DBL,         ///< double

	AV_SAMPLE_FMT_U8P,         ///< unsigned 8 bits, planar
	AV_SAMPLE_FMT_S16P,        ///< signed 16 bits, planar
	AV_SAMPLE_FMT_S32P,        ///< signed 32 bits, planar
	AV_SAMPLE_FMT_FLTP,        ///< float, planar
	AV_SAMPLE_FMT_DBLP,        ///< double, planar

	AV_SAMPLE_FMT_NB           ///< Number of sample formats. DO NOT USE if linking dynamically
};

int sample_size = av_get_bytes_per_sample(sample_fmt);
int planar      = av_sample_fmt_is_planar(sample_fmt);
//上面带P的都是分片的,和不分片的区别是:看下ffmpeg源代码
line_size = planar ? FFALIGN(nb_samples * sample_size,align) :
FFALIGN(nb_samples * sample_size * nb_channels, align);
if (linesize)
{
	*linesize = line_size;
}
return planar ? line_size * nb_channels : line_size;
1:无论是不是分片的数据总量是相同的.
2:分片的存储在内存中linesize如果两声道则左右分开占用linesize[0]和linesize[1].
3:不是分片的存储在内存中两声道不分开,左右左右....这样存储,只占用linesize[0].


二:音频信息
如果音频,样本:s16;采样率:44100;声道:2。
av_get_bytes_per_sample(s16) == 2;
1:假设从麦克风或者文件读出来的通过av_read_frame得到一个数据总量是88200个字节。
这个88200个字节是和帧无关的数据量。
2:如果接下来需要将这些数据编码成:
ffmpeg源码如下,AAC格式nb_samples和frame_size是1024,其他如下:

switch (id) {
case AV_CODEC_ID_ADPCM_ADX:    return   32;
case AV_CODEC_ID_ADPCM_IMA_QT: return   64;
case AV_CODEC_ID_ADPCM_EA_XAS: return  128;
case AV_CODEC_ID_AMR_NB:
case AV_CODEC_ID_EVRC:
case AV_CODEC_ID_GSM:
case AV_CODEC_ID_QCELP:
case AV_CODEC_ID_RA_288:       return  160;
case AV_CODEC_ID_AMR_WB:
case AV_CODEC_ID_GSM_MS:       return  320;
case AV_CODEC_ID_MP1:          return  384;
case AV_CODEC_ID_ATRAC1:       return  512;
case AV_CODEC_ID_ATRAC3:       return 1024 * framecount;
case AV_CODEC_ID_ATRAC3P:      return 2048;
case AV_CODEC_ID_MP2:
case AV_CODEC_ID_MUSEPACK7:    return 1152;
case AV_CODEC_ID_AC3:          return 1536;
}
1) AAC:
nb_samples和frame_size = 1024
一帧数据量:1024*2*av_get_bytes_per_sample(s16) = 4096个字节。
会编码:88200/(1024*2*av_get_bytes_per_sample(s16)) = 21.5帧数据
2) MP3:
nb_samples和frame_size = 1152
一帧数据量:1152*2*av_get_bytes_per_sample(s16) = 4608个字节。
MP3:则会编码:88200/(1152*2*av_get_bytes_per_sample(s16)) = 19.1帧数据


无论要编码成AAC还MP3都需要用到ffmpeg的fifo或者AVAudioFifo做数据缓冲。
3:持续时间方面
1) AAC
音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样频率(单位为s)
一帧 1024个 sample。采样率 Samplerate 44100KHz,每秒44100个sample, 所以根据公式   音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样频率
当前AAC一帧的播放时间是= 1024*1000000/44100= 22.2ms(单位为ms)
2) MP3
mp3 每帧均为1152个字节, 则:
frame_duration = 1152 * 1000000 / sample_rate
例如:sample_rate = 44100HZ时,计算出的时长为26.122ms,这就是经常听到的mp3每帧播放时间固定为26ms的由来


三:关于avcodec_fill_audio_frame
看下面ffmpeg源码:
AVFrame的nb_samples需要算出一帧数据,然后和buf做比较是否满足。剩下在这个函数里面没什么用,

int avcodec_fill_audio_frame(AVFrame *frame, int nb_channels,
enum AVSampleFormat sample_fmt, const uint8_t *buf,
int buf_size, int align)
{
	int ch, planar, needed_size, ret = 0;

	needed_size = av_samples_get_buffer_size(NULL, nb_channels,
		frame->nb_samples, sample_fmt,
		align);
	if (buf_size < needed_size)
		return AVERROR(EINVAL);

	planar = av_sample_fmt_is_planar(sample_fmt);
	if (planar && nb_channels > AV_NUM_DATA_POINTERS) {
		if (!(frame->extended_data = av_mallocz_array(nb_channels,
			sizeof(*frame->extended_data))))
			return AVERROR(ENOMEM);
	} else {
		frame->extended_data = frame->data;
	}

	if ((ret = av_samples_fill_arrays(frame->extended_data, &frame->linesize[0],
		(uint8_t *)(intptr_t)buf, nb_channels, frame->nb_samples,
		sample_fmt, align)) < 0) {
			if (frame->extended_data != frame->data)
				av_freep(&frame->extended_data);
			return ret;
	}
	if (frame->extended_data != frame->data) {
		for (ch = 0; ch < AV_NUM_DATA_POINTERS; ch++)
			frame->data[ch] = frame->extended_data[ch];
	}

	return ret;
}
//这里关于分片的问题在上面已经讲过了。
int av_samples_get_buffer_size(int *linesize, int nb_channels, int nb_samples,
	enum AVSampleFormat sample_fmt, int align)
{
	int line_size;
	int sample_size = av_get_bytes_per_sample(sample_fmt);
	int planar      = av_sample_fmt_is_planar(sample_fmt);

	/* validate parameter ranges */
	if (!sample_size || nb_samples <= 0 || nb_channels <= 0)
		return AVERROR(EINVAL);

	/* auto-select alignment if not specified */
	if (!align) {
		if (nb_samples > INT_MAX - 31)
			return AVERROR(EINVAL);
		align = 1;
		nb_samples = FFALIGN(nb_samples, 32);
	}

	/* check for integer overflow */
	if (nb_channels > INT_MAX / align ||
		(int64_t)nb_channels * nb_samples > (INT_MAX - (align * nb_channels)) / sample_size)
		return AVERROR(EINVAL);

	line_size = planar ? FFALIGN(nb_samples * sample_size,               align) :
		FFALIGN(nb_samples * sample_size * nb_channels, align);
	if (linesize)
		*linesize = line_size;

	return planar ? line_size * nb_channels : line_size;
}


四:关于swr_convert和resample后的数据量

//开始转换
/*
* @param s         allocated Swr context, with parameters set
* @param out       output buffers, only the first one need be set in case of packed audio
* @param out_count amount of space available for output in samples per channel
* @param in        input buffers, only the first one need to be set in case of packed audio
* @param in_count  number of input samples available in one channel
*
* @return number of samples output per channel, negative value on error
*/
这里看到:
out_count是为每一个声道resample后准备的buf大小
in_count是输入的每一个声道的buf长度,如果是摄像头采集的用av_read_frame得到的packet.size/channel.
//开始转换(如果是分片的关于in_size需要测试后改正,或本代码可以适应)
ret = swr_convert(swrcontext, pOutputFrame->data,buf_size_out/Channel_out,
	(const uint8_t**)pInputFrame->data,buf_ActualLen_in/Channel_in);
//得到resample后的buf大小(如果是分片的关于in_size需要测试后改正,或本代码可以适应)
int outsize = ret * Channel_out; 


如有错误请指正:

交流请加QQ群:62054820
QQ:379969650.



  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
ffmpeg可以用于音频的格式转换。在进行音频格式转换时,可以按照以下步骤进行操作: 1. 首先,实例化SwrContext,并设置转换所需的参数,包括通道数量、channel layout和sample rate等。这些参数可以根据需要进行设置。 2. 然后,使用avcodec_send_packet和avcodec_receive_frame函数获取解码后的原始数据。例如,可以使用以下代码片段获取解码后的数据: ``` int ret = avcodec_send_packet(aCodecCtx, &pkt); if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) return -1; ret = avcodec_receive_frame(aCodecCtx, frame); if (ret < 0 && ret != AVERROR_EOF) return -1; ``` 这样可以获取到解码后的音频数据。 3. 接下来,使用swr_alloc_set_opts函数来设置音频数据的输出参数,包括输出的通道数量、channel layout和sample rate等。然后使用swr_init函数来初始化SwrContext。这样就可以进行音频格式的转换了。 4. 最后,使用swr_convert函数将原始的音频数据转换为目标格式的音频数据。转换后的数据可以通过调用swr_convert函数来获取。 总结起来,通过实例化SwrContext,并设置转换参数,然后使用avcodec_send_packet和avcodec_receive_frame获取解码后的原始数据,接着使用swr_alloc_set_opts函数设置输出参数并初始化SwrContext,最后使用swr_convert函数进行音频格式转换。这样就可以完成ffmpeg音频转换了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [FFmpeg学习4:音频格式转换](https://blog.csdn.net/BrookIcv/article/details/52464505)[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: 100%"] [ .reference_list ]
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值