FFmpeg音频格式转换.docx

上传人:b****6 文档编号:6852806 上传时间:2023-01-11 格式:DOCX 页数:9 大小:19.83KB
下载 相关 举报
FFmpeg音频格式转换.docx_第1页
第1页 / 共9页
FFmpeg音频格式转换.docx_第2页
第2页 / 共9页
FFmpeg音频格式转换.docx_第3页
第3页 / 共9页
FFmpeg音频格式转换.docx_第4页
第4页 / 共9页
FFmpeg音频格式转换.docx_第5页
第5页 / 共9页
点击查看更多>>
下载资源
资源描述

FFmpeg音频格式转换.docx

《FFmpeg音频格式转换.docx》由会员分享,可在线阅读,更多相关《FFmpeg音频格式转换.docx(9页珍藏版)》请在冰豆网上搜索。

FFmpeg音频格式转换.docx

FFmpeg音频格式转换

FFmpeg音频格式转换

前段时间,在学习试用FFmpeg播放音频的时候总是有杂音,网上的很多教程是基于之前版本的FFmpeg的,而新的FFmepg3中audio增加了平面(planar)格式,而SDL播放音频是不支持平面格式的,所以通过FFmpeg解码出来的数据不能直接发送到SDL进行播放,需要进行一个格式转换。

通过网上一些资料,也能够正确的播放音频了,但是对具体的音频转换过程不是很了解,这里就对FFmpeg的对音频的存储格式及格式转换做个总结。

本文主要有以下几个方面的内容:

AVSampleFormat音频sample的存储格式

channellayout各个通道存储顺序

使用FFmpeg对音频数据进行格式转换

音频解码APIavcodec_decode_audio4在新版中已废弃,替换为使用更为简单的

avcodec_send_packet和avcodec_receive_frame。

本文简单的介绍了该API的

使用。

AVSampleFormat

在FFmpeg中使用枚举AVSampleFormat表示音频的采样格式,其声明如下:

enumAVSampleFormat{

AV_SAMPLE_FMT_NONE=-1,

AV_SAMPLE_FMT_U8,///

AV_SAMPLE_FMT_S16,///

AV_SAMPLE_FMT_S32,///

AV_SAMPLE_FMT_FLT,///

AV_SAMPLE_FMT_DBL,///

AV_SAMPLE_FMT_U8P,///

AV_SAMPLE_FMT_S16P,///

AV_SAMPLE_FMT_S32P,///

AV_SAMPLE_FMT_FLTP,///

AV_SAMPLE_FMT_DBLP,///

AV_SAMPLE_FMT_NB///

iflinkingdynamically

};

和图像的像素存储格式类似,可以使用8位无符号整数、16位有符号整数、32位有符号

整数以及单精度浮点数,双精度浮点数表示一个采样。

但是,没有使用

24位的有符号整数,这是因为这些不同的格式使用的是原生的C类型,而C中是没有24位的长度的类型的。

SamplevaluecanbeexpressedbynativeCtypes,hencethe

lackofasigned24-bitsampleformateventhoughitisacommonrawaudiodataformat.

对于浮点格式,其值在[-1.0,1.0]之间,任何在该区间之外的值都超过了最大音量的范围。

和YUV的图像格式格式,音频的采样格式分为平面(planar)和打包(packed)两种类

型,在枚举值中上半部分是packed类型,后面(有P后缀的)是planar类型。

对于planar格式的,每一个通道的值都有一个单独的plane,所有的plane必须有相同的

大小;对于packed类型,所有的数据在同一个数据平面中,不同通道的数据

交叉保存。

另外,在AVFrame中表示音频采样格式的字段format是一个int型,在使用

AVSampleFormat时候需要进行一个类型转换,将int转换为AVSampleFormat枚举值。

在头文件samplefmt.h提供了和音频采样格式相关的一些函数,现列举一些如下:

constchar*av_get_sample_fmt_name(enumAVSampleFormatsample_f

mt)

根据枚举值获取其相应的格式名称(字符串)

enumAVSampleFormatav_get_sample_fmt(constchar*name)

根据格式名字(字符串)获取相应的枚举值

enumAVSampleFormatav_get_packed_sample_fmt(enumAVSampleForma

tsample_fmt)

传入planar类型的采样格式,返回其可转换的packed类型的采样格式。

例如传入A

V_SAMPLE_FMT_S32P,其返回值为AV_SAMPLE_FMT_S32。

enumAVSampleFormatav_get_planar_sample_fmt(enumAVSampleForma

tsample_fmt)

和上面函数类似,不同的是传入的是packed类型的格式。

intav_sample_fmt_is_planar(enumAVSampleFormatsample_fmt

判断一个采样格式是不是planar类型的

intav_get_bytes_per_sample(enumAVSampleFormatsample_fmt)

每个采样值所占用的字节数

intav_samples_get_buffer_size(int*linesize,intnb_channels,

intnb_samples,enumAVSampleFormatsample_fmt,intalign)

根据输入的参数,计算其所占用空间的大小(字节数)。

linesize可设为null,ali

gn是buff空间的对齐格式(0=default,1=noalignment)

channel_layout

从上面可知,sample有两种类型的存储方式:

平面(planar)和打包(packed),在planar中每一个通道独自占用一个存储平面;在packed中,所有通道的sample交织存储在同一个

平面。

但是,对于planar格式不知道具体的某一通道所在的平面;对于packed格式各个通道的数据是以怎么样的顺序交织存储的。

这就需要借助于channel_layout。

首先来看下FFmpeg对channel_layout的定义:

channel_layout是一个64位整数,每个值为1的位对应一个通道。

也就说,channel_layout的位模式中值为1的个数等于其通道数量。

Achannel_layoutisa64-bitsintergetwithabitsetforeverychannel.The

numberofbitssetmustbeequaltothenumberof

channels.

在头文件channel_layout.h中为将每个通道定义了一个mask,其定义如下:

#defineAV_CH_FRONT_LEFT0x00000001

#defineAV_CH_FRONT_RIGHT0x00000002

#defineAV_CH_FRONT_CENTER0x00000004

#defineAV_CH_LOW_FREQUENCY0x00000008

#defineAV_CH_BACK_LEFT0x00000010

#defineAV_CH_BACK_RIGHT0x00000020

#defineAV_CH_FRONT_LEFT_OF_CENTER0x00000040

#defineAV_CH_FRONT_RIGHT_OF_CENTER0x00000080

#defineAV_CH_BACK_CENTER0x00000100

#defineAV_CH_SIDE_LEFT0x00000200

#defineAV_CH_SIDE_RIGHT0x00000400

#defineAV_CH_TOP_CENTER0x00000800

#defineAV_CH_TOP_FRONT_LEFT0x00001000

#defineAV_CH_TOP_FRONT_CENTER0x00002000

#defineAV_CH_TOP_FRONT_RIGHT0x00004000

#defineAV_CH_TOP_BACK_LEFT0x00008000

#defineAV_CH_TOP_BACK_CENTER0x00010000

#defineAV_CH_TOP_BACK_RIGHT0x00020000

#defineAV_CH_STEREO_LEFT0x20000000///

这样,一个channel_layout就是上述channelmask的组合,部分定义如下:

#defineAV_CH_LAYOUT_MONO(AV_CH_FRONT_CENTER)

#defineAV_CH_LAYOUT_STEREO(AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT)

#defineAV_CH_LAYOUT_2POINT1(AV_CH_LAYOUT_STEREO|AV_CH_LOW_FREQUENCY)

#defineAV_CH_LAYOUT_2_1(AV_CH_LAYOUT_STEREO|AV_CH_BACK_CENTER)

#defineAV_CH_LAYOUT_SURROUND(AV_CH_LAYOUT_STEREO|AV_CH_FRONT_CENTER)

#defineAV_CH_LAYOUT_3POINT1(AV_CH_LAYOUT_SURROUND|AV_CH_LOW_FREQUENCY)

#defineAV_CH_LAYOUT_4POINT0(AV_CH_LAYOUT_SURROUND|AV_CH_BACK_CENTER)

#defineAV_CH_LAYOUT_4POINT1(AV_CH_LAYOUT_4POINT0|AV_CH_LOW_FREQUENCY)

#defineAV_CH_LAYOUT_2_2(AV_CH_LAYOUT_STEREO|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)

#defineAV_CH_LAYOUT_QUAD(AV_CH_LAYOUT_STEREO|AV_CH_BACK_LEFT|AV_CH_BACK_RIGHT)

#defineAV_CH_LAYOUT_5POINT0(AV_CH_LAYOUT_SURROUND|AV_CH_SIDE_LEFT|AV_CH_SIDE_RIGHT)

#defineAV_CH_LAYOUT_5POINT1(AV_CH_LAYOUT_5POINT0|AV_CH_LOW_FREQUENCY)

...

AV_CH_LAYOUT_STEREO是立体声(2通道),其通道的存放顺序为LEFT|RIGHT;

AV_CH_LAYOUT_4POINT0是4通道,其通道的存放顺序为

LEFT|RIGHT|FRONT-CENTER|BACK-CENTER;其它数量的声道与此类似。

下面列举一些和channel_layout相关的函数

uint64_tav_get_channel_layout(constchar*name)根据传入的字符

串,返回相对应的channel_layout。

传入的参数可以是:

常用的channellayout的名称:

mono,stereo,4.0,quad,5.0,5.0(side),

5.1等。

一个单通道的名称:

FL,FR,FC,BL,BR,FLC,FRC等

通道的数量

channel_layoutmask,以"0x"开头的十六进制串。

更多详细的说明,参见该函数的文档。

intav_get_channel_layout_nb_channels(uint64_tchannel_layou

t)根据通道的layout返回通道的个数

int64_tav_get_default_channel_layout(intnb_channels)根据通道

的个数返回默认的layout

intav_get_channel_layout_channel_index(uint64_tchannel_layout,

uint64_tchannel);返回通道在layout中的index,也就是某一通道

在layout的存储位置。

av_get_channel_layout_channel_index的实现如下:

intav_get_channel_layout_channel_index(uint64_tchannel_layo

ut,

uint64_tchannel),{

if(!

(channel_layout&channel)||

av_get_channel_layout_nb_channels(channel)!

=1),returnAVERROR(EINVAL);

channel_layout&=channel-1;

returnav_get_channel_layout_nb_channels(channel_layout);

}

首先判断传入的layout包含该通道,并且保证该传入的通道是一个单通道。

以4通道AV_CH_LAYOUT_4POINT0为例,说明下计算方法。

AV_CH_LAYOUT_4POIN

T0=AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT|AV_CH_FRONT_CENTER|

AV_CH_BACK_CENTER

其二进制表示为0001,0000,0111,假如想找AV_CH_BACK_CENTER在该layout中

的index。

AV_CH_BACK_CENTER的十六进制为0x0100,二进制为0001,0000,000

0,那么

AV_CH_BACK_CENTER-1=1111,1111。

0001,0000,0111&0000,1111,111

1=0111,函数av_get_channel_layout_nb_channels是获取某个layout对应

的通道的数量,

前面提到,layout中值为1的位的个数和通道的数量相等,所以AV_CH_BACK_CENT

ER在layoutAV_CH_LAYOUT_4POINT0的index为3。

Audio格式转换

在FFmpeg中进行音频的格式转换主要有三个步骤

1.实例化SwrContext,并设置转换所需的参数:

通道数量、channellayout、samplerate

有以下两种方式来实例SwrContext,并设置参数:

使用swr_alloc

SwrContext*swr=swr_alloc();

av_opt_set_channel_layout(swr,"in_channel_layout",AV_CH_L

AYOUT_5POINT1,0);

av_opt_set_channel_layout(swr,"out_channel_layout",AV_CH_L

AYOUT_STEREO,0);

av_opt_set_int(swr,"in_sample_rate",48000,

0);

av_opt_set_int(swr,"out_sample_rate",44100,

0);

av_opt_set_sample_fmt(swr,"in_sample_fmt",AV_SAMPLE_FMT_F

LTP,0);

av_opt_set_sample_fmt(swr,"out_sample_fmt",AV_SAMPLE_FMT_S16,

0);

使用swr_alloc_set_opts

SwrContext*swr=swr_alloc_set_opts(NULL,//we'reallocat

inganewcontext

AV_CH_LAYOUT_STEREO,//out_ch_layout,AV_SAMPLE_FMT_S16,//out_sample_fmt,44100,//out_sample_rate,AV_CH_LAYOUT_5POINT1,//in_ch_layout

AV_SAMPLE_FMT_FLTP,//in_sample_fmt,48000,//in_sample_rate,0,//log_offset

NULL);//log_ctx

上述两种方法设置那个的参数是将5.1声道,channellayout为AV_CH_LAYOUT

_5POINT1,采样率为48KHz转换为2声道,channel_layout为AV_SAMPLE_F

MT_S16,采样率为44.1KHz。

1.计算转换后的sample个数

转后后的sample个数的计算公式为:

src_nb_samples*dst_sample_rate/src_sample_rate,其计算如下:

intdst_nb_samples=av_rescale_rnd(swr_get_delay(swr_ctx,

frame->sample_rate)+frame->nb_samples,frame->sample_rate,

frame->sample_rate,AVRounding

(1));

函数av_rescale_rnd是按照指定的舍入方式计算a*b/c。

函数swr_get_delay得到输入sample和输出sample之间的延迟,并且其返回值的根据传入的第二个参数不同而不同。

如果是输入的采样率,则返回值是输入sample个数;如果输入的是输出采样率,则返回值是输出sample个数。

2.调用swr_convert进行转换

intnb=swr_convert(swr_ctx,&audio_buf,dst_nb_samples,(constuint8_t**)frame->data,frame->nb_samples);

其返回值为转换的sample个数。

SDL播放音频时的格式转换

首先使用avcodec_send_packet和avcodec_receive_frame获取解码后的原

始数据

intret=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;

这里不再使用avcodec_decode_audio4进行音频的解码,在FFmpeg3中该函数已

被废弃,使用avcodec_send_packet和avcodec_receive_frame替代。

新的解

码API使用更为方便,

设置通道数量和channellayout

在编码的时候有可能丢失通道数量或者channellayout,这里根据获取的参数设置

其默认值

if(frame->channels>0&&frame->channel_layout==0)

frame->channel_layout=av_get_default_channel_layout(fra

me->channels);

elseif(frame->channels==0&&frame->channel_layout>0)

frame->channels=av_get_channel_layout_nb_channels(frame->ch

annel_layout);

如果channellayout未知(channel_layout=0),根据通道数量获取其默认的c

hannellayout;如同通道的数量未知,则根据其channellayout得到其通道数量。

设置输出格式

由于SDL2的sample格式不支持浮点型(FFmpeg中是支持的浮点型的),这里简

单的设置输出格式为AV_SAMPLE_FMT_S16(16位有符号整型),输出的channell

ayout也

根据通道数量设置为默认值dst_layout=av_get_default_channel_layout

(frame->channels)(SDL2不支持planar格式)。

实例化SwrContext

swr_ctx=swr_alloc_set_opts(nullptr,dst_layout,dst_format,

frame->sample_rate,

frame->channel_layout,(AVSampleFormat)frame->format,fra

me->sample_rate,0,nullptr);

if(!

swr_ctx||swr_init(swr_ctx)<0)

return-1;

在设置完参数后,一定要调用swr_init进行初始化。

转换

//计算转换后的sample个数a*b/c

intdst_nb_samples=av_rescale_rnd(swr_get_delay(swr_ctx,fr

ame->sample_rate)+frame->nb_samples,frame->sample_rate,frame

->sample_rate,AVRounding

(1));

//转换,返回值为转换后的sample个数

intnb=swr_convert(swr_ctx,&audio_buf,dst_nb_samples,(co

nstuint8_t**)frame->data,frame->nb_samples);

data_size=frame->channels*nb*av_get_bytes_per_sample(dst_fo

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 总结汇报

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1