存儲方式差異
音視頻都有packed和planar兩種存儲方式
-
packed方式為多個聲道交錯存儲,比如雙聲道data[0] = LRLRLR…
-
planar方式為多個聲道獨立存儲,比如雙聲道data[0] = LLL… data[1] = RRR…
-
圖示yuv444p與yuv444
對於planar的YUV格式,先連續存儲說有像素點的Y,緊接着存儲說用像素點的U,隨后是所有像素點的V
對於packed的YUV格式,每個像素點的Y,U,V是連續交錯存儲的。
數據所在位置
使用packed和planar存儲的額數據位於AVFrame結構中,即解碼后的數據和編碼前的數據。
- packed格式:frame.data[0]或frame.extended_data[0]包含說有的音頻數據
- planar格式:frame.data[i]活着frame.extended_data[i]表示第i個聲道的數據,AVFrame.data的數組大小固定為8,如果聲道超過8需要從frame.extended_data獲取聲道數據
存儲方式差異的影響
planar模式ffmepg內部存儲模式,但是實際使用的音頻文件都是packed模式存儲文件。
Planar或者Packed模式直接影響到保存文件時寫文件的操作,操作數據的時候一定要先檢測音頻采樣格式。
FFmpeg對存儲方式的支持
ffmpeg內部既支持packed模式存儲,同時也支持planar存儲,部分編碼器使用packed模式存儲,比如mp3(AV_SAMPLE_FMT_S16),部分使用planar模式存儲,比如aac(AV_SAMPLE_FMT_FLTP)。
本地存儲pcm數據均使用packed模式。
ffmpeg源碼中支持的存儲方式包括
enum AVSampleFormat {
AV_SAMPLE_FMT_NONE = -1,
// packed存儲方式
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
// planar存儲方式
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_S64, ///< signed 64 bits
AV_SAMPLE_FMT_S64P, ///< signed 64 bits, planar
AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically
};
常見編碼器定義
每種編碼器對應的默認編碼存儲格式是不同的
- aac
AVCodec ff_aac_encoder = {
.name = "aac",
.long_name = NULL_IF_CONFIG_SMALL("AAC (Advanced Audio Coding)"),
.type = AVMEDIA_TYPE_AUDIO,
.id = AV_CODEC_ID_AAC,
.priv_data_size = sizeof(AACEncContext),
.init = aac_encode_init,
.encode2 = aac_encode_frame,
.close = aac_encode_end,
.defaults = aac_encode_defaults,
.supported_samplerates = mpeg4audio_sample_rates,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE,
.capabilities = AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_DELAY,
.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLTP,
AV_SAMPLE_FMT_NONE },
.priv_class = &aacenc_class,
};
- fdk_aac
AVCodec ff_libfdk_aac_encoder = {
.name = "libfdk_aac",
.long_name = NULL_IF_CONFIG_SMALL("Fraunhofer FDK AAC"),
.type = AVMEDIA_TYPE_AUDIO,
.id = AV_CODEC_ID_AAC,
.priv_data_size = sizeof(AACContext),
.init = aac_encode_init,
.encode2 = aac_encode_frame,
.close = aac_encode_close,
.capabilities = AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_DELAY,
.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16,
AV_SAMPLE_FMT_NONE },
.priv_class = &aac_enc_class,
.defaults = aac_encode_defaults,
.profiles = profiles,
.supported_samplerates = aac_sample_rates,
.channel_layouts = aac_channel_layout,
.wrapper_name = "libfdk",
};
- mp3lamp
AVCodec ff_libmp3lame_encoder = {
.name = "libmp3lame",
.long_name = NULL_IF_CONFIG_SMALL("libmp3lame MP3 (MPEG audio layer 3)"),
.type = AVMEDIA_TYPE_AUDIO,
.id = AV_CODEC_ID_MP3,
.priv_data_size = sizeof(LAMEContext),
.init = mp3lame_encode_init,
.encode2 = mp3lame_encode_frame,
.close = mp3lame_encode_close,
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SMALL_LAST_FRAME,
.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S32P,
AV_SAMPLE_FMT_FLTP,
AV_SAMPLE_FMT_S16P,
AV_SAMPLE_FMT_NONE },
.supported_samplerates = libmp3lame_sample_rates,
.channel_layouts = (const uint64_t[]) { AV_CH_LAYOUT_MONO,
AV_CH_LAYOUT_STEREO,
0 },
.priv_class = &libmp3lame_class,
.defaults = libmp3lame_defaults,
.wrapper_name = "libmp3lame",
};
- flac
AVCodec ff_flac_encoder = {
.name = "flac",
.long_name = NULL_IF_CONFIG_SMALL("FLAC (Free Lossless Audio Codec)"),
.type = AVMEDIA_TYPE_AUDIO,
.id = AV_CODEC_ID_FLAC,
.priv_data_size = sizeof(FlacEncodeContext),
.init = flac_encode_init,
.encode2 = flac_encode_frame,
.close = flac_encode_close,
.capabilities = AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_LOSSLESS,
.sample_fmts = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_S16,
AV_SAMPLE_FMT_S32,
AV_SAMPLE_FMT_NONE },
.priv_class = &flac_encoder_class,
};