伙伴云客服论坛»论坛 S区 S软件开发 查看内容

0 评论

0 收藏

分享

FFmpeg实战之分别出PCM数据

目录

    什么是PCM字节序FFmpeg支持的PCM数据格式FFmpeg中Packed和Planar的PCM数据区别实战FFmpeg分别出PCM数据Audicity播放分别双声道PCM音频数据左右声道的数据函数说明
      fread函数fwrite函数fopen函数



什么是PCM

PCM(Pulse Code Modulation,脉冲编码调制)音频数据是未经压缩的音频采样数据裸流,它是由模仿信号经过采样、量化、编码转换成的规范数字音频数据。
描绘PCM数据的6个参数:
    Sample Rate : 采样频率。8kHz(电话)、44.1kHz(CD)、48kHz(DVD)。Sample Size : 量化位数。通常该值为16-bit。Number of Channels : 通道个数。常见的音频有立体声(stereo)和单声道(mono)两品种型,立体声包含左声道和右声道。另外还有环绕立体声等其它不太常用的类型。Sign : 表示样本数据是否是有符号位,比如用一字节表示的样本数据,有符号的话表示范围为-128 ~ 127,无符号是0 ~ 255。Byte Ordering : 字节序。字节序是little-endian还是big-endian。通常均为little-endian。字节序说明见第4节。Integer Or Floating Point : 整形或浮点型。大多数格式的PCM样本数据使用整形表示,而在一些对精度要求高的应用方面,使用浮点类型表示PCM样本数据。

字节序

字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。
大端字节序(Big Endian):将多个字节值的最高有效字节贮存于较低的内存位置。在大端处置器的机器上,数值0xABCD1234在内存存储为连续字节0xAB、0xCD、0x12、0x34。
小端字节序(Little endian):将多个字节值的最低有效字节存储于较低的内存位置。比如在小段处置器的机器上,数值0xABCD1234在内存中存储为连续的字节0x34、0x12、0xCD、0x341。
FFmpeg实战之分别出PCM数据-1.jpg


FFmpeg支持的PCM数据格式

在cmder中使用ffmpeg -formats | grep PCM命令,获取ffmpeg支持的音视频格式,其中我们可以找到支持的PCM格式。
DE alaw            PCM A-law
DE f32be           PCM 32-bit floating-point big-endian
DE f32le           PCM 32-bit floating-point little-endian
DE f64be           PCM 64-bit floating-point big-endian
DE f64le           PCM 64-bit floating-point little-endian
DE mulaw           PCM mu-law
DE s16be           PCM signed 16-bit big-endian
DE s16le           PCM signed 16-bit little-endian
DE s24be           PCM signed 24-bit big-endian
DE s24le           PCM signed 24-bit little-endian
DE s32be           PCM signed 32-bit big-endian
DE s32le           PCM signed 32-bit little-endian
DE s8              PCM signed 8-bit
DE u16be           PCM unsigned 16-bit big-endian
DE u16le           PCM unsigned 16-bit little-endian
DE u24be           PCM unsigned 24-bit big-endian
DE u24le           PCM unsigned 24-bit little-endian
DE u32be           PCM unsigned 32-bit big-endian
DE u32le           PCM unsigned 32-bit little-endian
DE u8              PCM unsigned 8-bit
DE vidc            PCM Archimedes VIDC
s是有符号,u是无符号,f是浮点数。
be是大端,le是小端。

FFmpeg中Packed和Planar的PCM数据区别

FFmpeg中音视频数据根本上都有Packed(打包格式)和Planar(平面格式)两种存储方式,对于双声道音频来说,Packed方式为两个声道的数据交错存储;Planar方式为两个声道分开存储。假设一个L/R为一个采样点,数据存储的方式如下所示:
    Packed: L R L R L R L RPlanar: L L L L R R R R
FFmpeg音频解码后的数据是寄存在AVFrame构造中的。
    Packed格式,frame.data[0]或frame.extended_data[0]包含所有的音频数据中。Planar格式,frame.data或者frame.extended_data表示第i个声道的数据(假设声道0是第一个), AVFrame.data数组大小固定为8,假设声道数超越8,需要从frame.extended_data获取声道数据。
下面为FFmpeg内部存储音频使用的采样格式,所有的Planar格式后面都有字母P标识。
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_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
};说明:
Planar形式是ffmpeg内部存储形式,我们实际使用的音频文件都是Packed形式的。
FFmpeg解码不同格式的音频输出的音频采样格式不是一样。测试发现,其中AAC解码输出的数据为浮点型的 AV_SAMPLE_FMT_FLTP 格式,MP3解码输出的数据为 AV_SAMPLE_FMT_S16P 格式(使用的mp3文件为16位深)。详细采样格式可以查看解码后的AVFrame中的format成员或解码器的AVCodecContext中的sample_fmt成员。
Planar或者Packed形式直接影响到保管文件时写文件的操作,操作数据的时候一定要先检测音频采样格式。

实战FFmpeg分别出PCM数据

先用ffprobe命令查看文件详情
ffprobe -i input.mp4
详情
#音频
  Stream #0:0(und): Audio: aac (LC) (mp4a / 0x6134706D),
  44100 Hz, stereo, fltp, 128 kb/s (default)
    Metadata:
      creation_time   : 2020-10-12T15:12:33.000000Z
      vendor_id       : [0][0][0][0]
#视频      
  Stream #0:1(und): Video: h264 (High) (avc1 / 0x31637661),
  yuv420p, 368x384, 383 kb/s, 29.95 fps,
  29.97 tbr, 90k tbn, 60 tbc (default)
    Metadata:
      creation_time   : 2020-10-12T15:12:33.000000Z
      vendor_id       : [0][0][0][0]
      encoder         : JVT/AVC Coding
用ffmpeg命令转换
ffmpeg -i input.mp4 -ar 44100 -ac 2 -f s16le output.pcm
其中
# 输入文件
-i
# 格式
-f fmt              force format
#设置音频采样率
-ar rate            set audio sampling rate (in Hz)
#设置音频通道数
-ac channels        set number of audio channels
输出
FFmpeg实战之分别出PCM数据-2.jpg


Audicity播放

文件-原理音频
FFmpeg实战之分别出PCM数据-3.jpg


分别双声道PCM音频数据左右声道的数据

依照双声道的LRLRLR的PCM音频数据可以通过将它们交叉的读出来的方式来分别左右声道的数据。
int pcm_s16le_split(const char* file, const char* out_lfile, const char* out_rfile) {
     FILE *fp = fopen(file, "rb+");
     if (fp == NULL) {
         printf("open %s failed\n", file);
         return -1;
     }
     FILE *fp1 = fopen(out_lfile, "wb+");
     if (fp1 == NULL) {
         printf("open %s failed\n", out_lfile);
         return -1;
     }
     FILE *fp2 = fopen(out_rfile, "wb+");
     if (fp2 == NULL) {
         printf("open %s failed\n", out_rfile);
         return -1;
     }
     char * sample = (char *)malloc(4);
     while(!feof(fp)) {
         fread(sample, 1, 4, fp);
         //L
         fwrite(sample, 1, 2, fp1);
         //R
         fwrite(sample + 2, 1, 2, fp2);
     }
     free(sample);
     fclose(fp);
     fclose(fp1);
     fclose(fp2);
     return 0;
}
函数说明


fread函数

描绘
C 库函数 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) 从给定流 stream 读取数据到 ptr 所指向的数组中。
声明
下面是 fread() 函数的声明。
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
参数
    ptr – 这是指向带有最小尺寸 size*nmemb 字节的内存块的指针。size – 这是要读取的每个元素的大小,以字节为单位。nmemb – 这是元素的个数,每个元素的大小为 size 字节。stream – 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流。
返回值
胜利读取的元素总数会以 size_t 对象返回,size_t 对象是一个整型数据类型。假设总数与 nmemb 参数不同,则可能发生了一个错误或者到达了文件末尾。

fwrite函数

描绘
C 库函数 size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) 把 ptr 所指向的数组中的数据写入到给定流 stream 中。
声明
下面是 fwrite() 函数的声明。
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
参数
    ptr – 这是指向要被写入的元素数组的指针。size – 这是要被写入的每个元素的大小,以字节为单位。nmemb – 这是元素的个数,每个元素的大小为 size 字节。stream – 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流。
返回值
假设胜利,该函数返回一个 size_t 对象,表示元素的总数,该对象时一个整型数据类型。假设该数字与 nmemb 参数不同,则会显示一个错误。

fopen函数

描绘
C 库函数 FILE *fopen(const char *filename, const char *mode) 使用给定的形式 mode 翻开 filename 所指向的文件。
声明
下面是 fopen() 函数的声明。
FILE *fopen(const char *filename, const char *mode)
参数
    filename – 这是 C 字符串,包含了要翻开的文件名称。mode – 这是 C 字符串,包含了文件访问形式,形式如下:
形式描绘
“r”翻开一个用于读取的文件。该文件必需存在。
“w”创建一个用于写入的空文件。假设文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件。
“a”追加到一个文件。写操作向文件末尾追加数据。假设文件不存在,则创建文件。
“r+”翻开一个用于更新的文件,可读取也可写入。该文件必需存在。
“w+”创建一个用于读写的空文件。
“a+”翻开一个用于读取和追加的文件。
返回值
该函数返回一个 FILE 指针。否则返回 NULL,且设置全局变量 errno 来标识错误。
以上就是FFmpeg实战之分别出PCM数据的详细内容,更多关于FFmpeg分别PCM数据的资料请关注网站其它相关文章!

回复

举报 使用道具

全部回复
暂无回帖,快来参与回复吧
本版积分规则 高级模式
B Color Image Link Quote Code Smilies

伙伴的伙伴
注册会员
主题 27
回复 15
粉丝 0
|网站地图
快速回复 返回顶部 返回列表