【视音频编程学习】:使用FFmpeg读取PCM文件编码发送

1. 什么是PCM?

PCM(Pulse Code Modulation,脉冲编码调制)音频数据是未经压缩的音频采样数据裸流,它是由模拟信号经过采样、量化、编码转换成的标准数字音频数据。

描述PCM数据的6个参数:

  1. Sample Rate : 采样频率。8kHz(电话)、44.1kHz(CD)、48kHz(DVD)。
  2. Sample Size : 量化位数。通常该值为16-bit。
  3. Number of Channels : 通道个数。常见的音频有立体声(stereo)和单声道(mono)两种类型,立体声包含左声道和右声道。另外还有环绕立体声等其它不太常用的类型。
  4. Sign : 表示样本数据是否是有符号位,比如用一字节表示的样本数据,有符号的话表示范围为-128 ~ 127,无符号是0 ~ 255。
  5. Byte Ordering : 字节序。字节序是little-endian还是big-endian。通常均为little-endian。字节序说明见第4节。
  6. Integer Or Floating Point : 整形或浮点型。大多数格式的PCM样本数据使用整形表示,而在一些对精度要求高的应用方面,使用浮点类型表示PCM样本数据。

pcm是音频的编码格式,不是文件的封装格式;aac既是编码格式也是封装格式,一般aac封装格式的内部音频数据编码格式也为aac。

pcm文件所占容量的公式:

​ 存储量 = (采样频率 * 采样位数 * 声道) * 时间 / 8 (单位: 字节数)

​ 故可以根据文件大小来计算时间。

2. FFmpeg数据结构

  1. AVCodecContext 编码器上下文
  2. AVCodec 编码器
  3. AVCodecID 编码器ID
  4. AVFrame 编码前、解码后保存的数据
  5. AVPacket 编码后、解码前保存的数据

3. 音频数据编码所必需的参数:

  1. sample_rate codecContext->sample_rate = frame->sample_rate;
    • 采样率,即我们一秒钟采集多少次声音样本
  2. channels codecContext->channels = frame->channels;
    • 通道数目,单通道双通道
  3. format codecContext->format = frame->format;
    • 样本的格式,一个音频样本一般用两个字节来描述,分为大小两端。
    • 例如:AV_
  4. channel_layout codecContext->channel_layout = AV_CH_LAYOUT_STEREO
    • 用来设置输出的通道布局

如果有两个通道(即channels = 2 时)pcm中的frame->data[0],frame->data[1]用于指向不同声道的数据。

4. fread

size_t fread( void * ptr, size_t size, size_t count, FILE * stream)

  • ptr, 指向一个内存块的指针,最小为size * count (Byte)
  • size, 被读取的每个元素的大小 (Byte)
  • count,被读取的元素的个数
  • stream, 指向输入文件流的指针

5. 例子

比如在我们的程序正常采集的过程中,每次采集应该是采集1024个采样点,然后再发送给channel,pcm每个采样点的大小为16位(2Byte),故对pcm的文件采集应该是每次采集2048Byte,即fread的对象大小为2048Byte,对象个数count应该用文件大小来除以2048Byte(2KB)来计算,(fread的count为单次读取的数目,所以这里的count应该是1,每次只读取1024个采样点,2048个字节),在我们的程序中中采样频率为32000hz,意味着每秒钟采集32000个pcm的sample,故每次从文件中读取的时间间隔应该是1024/32000s.

  uint8_t* pcm_buf = (uint8_t*)malloc(2048);   //pcm的buffer大小
  long fileLen = filelength(fileno(mikeFile)); //pcm文件的大小
	while(1){
      pcm_len = fread(pcm_buf, 2048, 1, mikeFile);   
      if (pcm_len <= 0) {//文件读取完毕,若读取文件pcm_len是1(count)
        I_LOG("read file over");
        break;
      }
    }//再将pcm_buf传给编码器encoder,用于init AVFrame,再通过编码到packet,获取packet便可以实现播放等操作。

版权声明:本文为Daibvly原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。