音频技术基础之PCM数据格式

什么是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样本数据。

推荐的PCM数据播放工具:

  • ffplay, 使用示例如下:
    //播放格式为f32le,单声道,采样频率48000Hz的PCM数据
    ffplay -f f32le -ac 1 -ar 48000 pcm_audio

  • Audacity:一款免费开源的跨平台音频处理软件。

  • Adobe Auditon。导入原始数据,打开的时候需要选择采样率、格式和字节序。

PCM数据格式

如果是单声道的音频文件,采样数据按时间的先后顺序依次存入(有的时候也会采用LRLRLR方式存储,只是另一个声道的数据为0),如果是双声道的话就按照LRLRLR的方式存储,存储的时候与字节序有关。big-endian模式如下图所示:

FFmpeg支持的PCM数据格式

使用ffmpeg -formats命令,获取ffmpeg支持的音视频格式,其中我们可以找到支持的PCM格式。

 DE alawPCM 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
s是有符号,u是无符号,f是浮点数。
be是大端,le是小端。

FFmpeg中Packed和Planar的PCM数据区别

FFmpeg中音视频数据基本上都有Packed和Planar两种存储方式,对于双声道音频来说,Packed方式为两个声道的数据交错存储;Planar方式为两个声道分开存储。假设一个L/R为一个采样点,数据存储的方式如下所示:

Packed: L R L R L R L R
Planar: L L L L R R R R

FFmpeg音频解码后的数据是存放在AVFrame结构中的。

Packed格式,frame.data[0]或frame.extended_data[0]包含所有的音频数据中。
Planar格式,frame.data[i]或者frame.extended_data[i]表示第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模式直接影响到保存文件时写文件的操作,操作数据的时候一定要先检测音频采样格式。

字节序

谈到字节序的问题,必然牵涉到两大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据。那么究竟什么是big endian,什么又是little endian?

big endian是指低地址存放最高有效字节(MSB,Most Significant Bit),而little endian则是低地址存放最低有效字节(LSB,Least Significant Bit)。

下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示:

Big Endian

低地址 高地址

—————————————————————————–>

| 12 | 34 | 56 | 78 |

Little Endian

低地址 高地址

—————————————————————————–>

| 78 | 56 | 34 | 12 |

所有网络协议都是采用big endian的方式来传输数据的。所以也把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。

PCM音频数据的处理

分离双声道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;
 }

参考>
链接:https://www.jianshu.com/p/fd43c1c82945


文章作者: YUV420.COM
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 YUV420.COM !
评论
 上一篇
音视频编码基础知识入门 音视频编码基础知识入门
前言如今我们所处的时代,是移动互联网时代,也可以说是视频时代。今天探长为大家整理了一篇音视频编码基础知识入门课,希望能对想了解这一领域的同学有帮助,更多内容请关注影音视频技术空间 : https://www.yuv420.com 从快播
2020-02-10
下一篇 
Android端音频变声方案 Android端音频变声方案
Android 端音频变声方案音频变声实质上是通过调整 PCM 数据的音调、节拍等属性,实现听感上的变化。如果是 mp3、aac 等压缩音频,则需提前解码成 PCM 数据,Android 端可以通过 MediaExtractor 及 Med
2020-01-21
  目录