FFMPEG 关于smaple_fmts的理解及ffplay播放PCM
问题
当我将一个aac的音频文件解码为原始的PCM数据后,使用ffplay播放测试是否成功时,需要提供给ffplay 采样率,通道数,PCM的格式类型 3个参数,否则无法播放!
所以使用ffprobe 查看原来的aac文件信息,这里我们可以看到采样率为 44100Hz, 通道为stereo表示双通道,后面跟着一个 fltp,这是个什么格式呢?
我们先看一下ffmpeg支持那些PCM格式,使用下面的命名查看
ffmpeg -formats | findstr PCM
可以看到PCM的格式还是很多,具体我们选择哪一种呢?总不能一个个的试吧!
le --- 小端模式 be --- 大端模式
答案
我们先在在ffmpeg源码中,首先我们看下在FFMPEG中格式定义,它定义在FFmpeg/libavutil/samplefmt.h文件中。根据位深度,是否有符号,打包类型,定义了12种类型,AV_SAMPLE_FMT_NONE表示未知类型,最后的AV_SAMPLE_FMT_NB表示样本格式个数为12个。
enum AVSampleFormat {AV_SAMPLE_FMT_NONE = -1,AV_SAMPLE_FMT_U8, ///< unsigned 8 bitsAV_SAMPLE_FMT_S16, ///< signed 16 bitsAV_SAMPLE_FMT_S32, ///< signed 32 bitsAV_SAMPLE_FMT_FLT, ///< floatAV_SAMPLE_FMT_DBL, ///< doubleAV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planarAV_SAMPLE_FMT_S16P, ///< signed 16 bits, planarAV_SAMPLE_FMT_S32P, ///< signed 32 bits, planarAV_SAMPLE_FMT_FLTP, ///< float, planarAV_SAMPLE_FMT_DBLP, ///< double, planarAV_SAMPLE_FMT_S64, ///< signed 64 bitsAV_SAMPLE_FMT_S64P, ///< signed 64 bits, planarAV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically
};
上面是程序内部使用的,而我们在命令行看到是fltp这种名字跟它有什么关系呢?具体对照表如下,定义在FFmpeg/libavutilsamplefmt.c文件中
typedef struct SampleFmtInfo {char name[8];int bits;int planar;enum AVSampleFormat altform; ///< planar<->packed alternative form
} SampleFmtInfo;/ this table gives more information about formats */
static const SampleFmtInfo sample_fmt_info[AV_SAMPLE_FMT_NB] = {[AV_SAMPLE_FMT_U8] = { .name = "u8", .bits = 8, .planar = 0, .altform = AV_SAMPLE_FMT_U8P },[AV_SAMPLE_FMT_S16] = { .name = "s16", .bits = 16, .planar = 0, .altform = AV_SAMPLE_FMT_S16P },[AV_SAMPLE_FMT_S32] = { .name = "s32", .bits = 32, .planar = 0, .altform = AV_SAMPLE_FMT_S32P },[AV_SAMPLE_FMT_S64] = { .name = "s64", .bits = 64, .planar = 0, .altform = AV_SAMPLE_FMT_S64P },[AV_SAMPLE_FMT_FLT] = { .name = "flt", .bits = 32, .planar = 0, .altform = AV_SAMPLE_FMT_FLTP },[AV_SAMPLE_FMT_DBL] = { .name = "dbl", .bits = 64, .planar = 0, .altform = AV_SAMPLE_FMT_DBLP },[AV_SAMPLE_FMT_U8P] = { .name = "u8p", .bits = 8, .planar = 1, .altform = AV_SAMPLE_FMT_U8 },[AV_SAMPLE_FMT_S16P] = { .name = "s16p", .bits = 16, .planar = 1, .altform = AV_SAMPLE_FMT_S16 },[AV_SAMPLE_FMT_S32P] = { .name = "s32p", .bits = 32, .planar = 1, .altform = AV_SAMPLE_FMT_S32 },[AV_SAMPLE_FMT_S64P] = { .name = "s64p", .bits = 64, .planar = 1, .altform = AV_SAMPLE_FMT_S64 },[AV_SAMPLE_FMT_FLTP] = { .name = "fltp", .bits = 32, .planar = 1, .altform = AV_SAMPLE_FMT_FLT },[AV_SAMPLE_FMT_DBLP] = { .name = "dblp", .bits = 64, .planar = 1, .altform = AV_SAMPLE_FMT_DBL },
};
这里我们看到fltp表示的格式是AV_SAMPLE_FMT_FLTP,位深32位,数据存储方式位planar,与之相反的packed格式为AV_SAMPLE_FMT_FLT。
好了我们已经知道fltp表示的是float 32位数据,我们再结合前面的PCM的格式数据,在根据我们的电脑的大小端类型知道,我们需要传递的参数是 s32le
好了,我们使用ffplay 播放试试看
ffplay -ar 44100 -ac 2 -f f32le -i audio.pcm
结果如下,完美播放:
备注
这里有一个planar,之前有一篇关于视频的采样格式的文章,这里我们需要注意的是音频样本的内存
存储方式。
planar --- 每个音频通道(channel )一个平面,例如双通道data[0] = LLL..., data[1] = RRR...
linesize表示单个plane的buffer size(以字节为单位)
packed --- 不管几个音频通道都只是用一个平面,数据交错存储,data[0] = LRLRLR...
linesize表示单个plane的buffer size(以字节为单位)
其他
查看aac解码器信息
ffmpeg -h decoder=aac