ffmpeg学习九:音频编码前奏-ubuntu下录音和播放

来源:互联网 发布:潍坊行知中学李嘉辉 编辑:程序博客网 时间:2024/06/02 00:41

上一篇博客,我们把一个Yuv编码格式的视频文件编码为H264格式。那么接下来,自然要学习下音频编码了。在学习音频编码之前,我们先看看ubuntu下如何采集声音和播放声音。

录音

录制5秒钟的一段音频。
audio_recorder.c:

/*created by Jinwei Liu*/#define ALSA_PCM_NEW_HW_PARAMS_API  #include <alsa/asoundlib.h>  int main(int argc,char **argv) {    long loops;    int rc;    int size;    snd_pcm_t *handle;    snd_pcm_hw_params_t *params;    unsigned int val;    int dir;    snd_pcm_uframes_t frames;    char *buffer;    FILE * fd_out;  if(argc!=2){    printf("usage:./a.out outfile\n");  }  /* Open PCM device for recording (capture). */    rc = snd_pcm_open(&handle, "default",                      SND_PCM_STREAM_CAPTURE, 0);    if (rc < 0) {      fprintf(stderr,              "unable to open pcm device: %s\n",              snd_strerror(rc));      exit(1);    }    /* Allocate a hardware parameters object. */    snd_pcm_hw_params_alloca(&params);    /* Fill it in with default values. */    snd_pcm_hw_params_any(handle, params);    /* Set the desired hardware parameters. */    /* Interleaved mode */    snd_pcm_hw_params_set_access(handle, params,                        SND_PCM_ACCESS_RW_INTERLEAVED);    /* Signed 16-bit little-endian format */    snd_pcm_hw_params_set_format(handle, params,                                SND_PCM_FORMAT_S16_LE);    /* Two channels (stereo) */    snd_pcm_hw_params_set_channels(handle, params, 2);    /* 11025 bits/second sampling rate (CD quality) */    val = 11025;    snd_pcm_hw_params_set_rate_near(handle, params,                                    &val, &dir);    /* Set period size to 32 frames. */    //frames = 32;   // snd_pcm_hw_params_set_period_size_near(handle,   //                             params, &frames, &dir);    /* Write the parameters to the driver */    rc = snd_pcm_hw_params(handle, params);    if (rc < 0) {      fprintf(stderr,              "unable to set hw parameters: %s\n",              snd_strerror(rc));      exit(1);    }    /* Use a buffer large enough to hold one period */    snd_pcm_hw_params_get_period_size(params,                                        &frames, &dir);    size = frames * 4; /* 2 bytes/sample, 2 channels */    buffer = (char *) malloc(size);    /* We want to loop for 5 seconds */    snd_pcm_hw_params_get_period_time(params,                                           &val, &dir);    loops = 5000000 / val;    fd_out = fopen(argv[1],"w+");  while (loops > 0) {      loops--;      rc = snd_pcm_readi(handle, buffer, frames);      if (rc == -EPIPE) {        /* EPIPE means overrun */        fprintf(stderr, "overrun occurred\n");        snd_pcm_prepare(handle);      } else if (rc < 0) {        fprintf(stderr,                "error from read: %s\n",                snd_strerror(rc));      } else if (rc != (int)frames) {        fprintf(stderr, "short read, read %d frames\n", rc);      }      rc = fwrite(buffer,1, size,fd_out);      if (rc != size)        fprintf(stderr,                "short write: wrote %d bytes\n", rc);    }    fclose(fd_out);  snd_pcm_drain(handle);    snd_pcm_close(handle);    free(buffer);    return 0;  }  

函数中已经有相关的注释了。录音可以认为有三部分组成:
第一:参数设置阶段。这一阶段需要设置波特率,帧率,通道数等。
第二:采集阶段。主要就是调用snd_pcm_readi都声音数据。
第三:保存阶段。把都出来的数据写入文件即可。
编译:
gcc -o recorder audio_recorder.c -lasound
运行:
./recorder audio.pcm
执行完成后生成audio.pcm文件。

播放原始音频数据

直接播放原始音频文件(注意,录制的参数和播放的参数要一致)
audio_palyer.c

/*created by Jinwei Liu*//* Use the newer ALSA API */  #define ALSA_PCM_NEW_HW_PARAMS_API  #include <alsa/asoundlib.h>  int main(int argc,char **argv) {    long loops;    int rc;    int size;    snd_pcm_t *handle;    snd_pcm_hw_params_t *params;    unsigned int val;    int dir;    snd_pcm_uframes_t frames;    char *buffer;    int fd;  /* Open PCM device for playback. */    rc = snd_pcm_open(&handle, "default",                      SND_PCM_STREAM_PLAYBACK, 0);    if (rc < 0) {      fprintf(stderr,              "unable to open pcm device: %s\n",              snd_strerror(rc));      exit(1);    } else{    printf("open device sucess\n");  }   /* Allocate a hardware parameters object. */    snd_pcm_hw_params_alloca(&params);    /* Fill it in with default values. */    snd_pcm_hw_params_any(handle, params);    /* Set the desired hardware parameters. */    /* Interleaved mode */    snd_pcm_hw_params_set_access(handle, params,                        SND_PCM_ACCESS_RW_INTERLEAVED);    /* Signed 16-bit little-endian format */    snd_pcm_hw_params_set_format(handle, params,                                SND_PCM_FORMAT_S16_LE);    /* Two channels (stereo) */    snd_pcm_hw_params_set_channels(handle, params, 2);    /* 11025 bits/second sampling rate (CD quality) */    val = 11025;    snd_pcm_hw_params_set_rate_near(handle, params,                                    &val, &dir);    /* Set period size to 32 frames. */   // frames = 32;   // snd_pcm_hw_params_set_period_size_near(handle,   //                             params, &frames, &dir);    /* Write the parameters to the driver */    rc = snd_pcm_hw_params(handle, params);    if (rc < 0) {      fprintf(stderr,              "unable to set hw parameters: %s\n",              snd_strerror(rc));      exit(1);    }    /* Use a buffer large enough to hold one period */    snd_pcm_hw_params_get_period_size(params, &frames,                                      &dir);    printf("frames = %ld\n",frames);  size = frames * 4; /* 2 bytes/sample, 2 channels */    buffer = (char *) malloc(size);    /* We want to loop for 5 seconds */    snd_pcm_hw_params_get_period_time(params,                                      &val, &dir);    fd  = open(argv[1],O_RDONLY);  if(fd<0){    printf("open error\n");  }  while (1) {      rc = read(fd, buffer, size);    if (rc == 0) {        fprintf(stderr, "end of file on input\n");        break;      } else if (rc != size) {        fprintf(stderr,                "short read: read %d bytes\n", rc);      }    while((rc = snd_pcm_writei(handle, buffer, frames)<0))    {         usleep(2000);          if (rc == -EPIPE)        {          /* EPIPE means underrun */          fprintf(stderr, "underrun occurred\n");          snd_pcm_prepare(handle);         }         else if (rc < 0)         {             fprintf(stderr,              "error from writei: %s\n",              snd_strerror(rc));         }    }  }    close(fd);  snd_pcm_drain(handle);    snd_pcm_close(handle);    free(buffer);    return 0;  }  

函数中已经有相关的注释了。播放原始音频数据可以认为有三部分组成:
第一:参数设置阶段。这一阶段需要设置波特率,帧率,通道数等。
第二:读文件阶段。
第三:播放阶段。主要是snd_pcm_writei想pcm设备写入数据了。
编译:
gcc -o player audio_palyer.c -lasound
运行:
./player audio.pcm
播放的就是我们自己录制的音频数据,可以验证我们的录音程序是否正常。

播放wav格式的音频文件

这里要注意:wav是文件的格式,pcm是没有压缩的音频的编码格式。也就是说,一个是文件封装格式,一个是视频的编码格式,两个是有本质的区别的。在我们的例子中,wav文件的数据区,存储的正式pcm编码格式的音频数据。
头文件:palywav.h

#ifndef PLAY_WAV_H#define PLAY_WAV_H#include<stdio.h>#include<stdlib.h>#include <string.h>#include "alsa/asoundlib.h"struct WAV_HEADER{    char rld[4]; //riff 标志符号    int rLen;     char wld[4]; //格式类型(wave)    char fld[4]; //"fmt"    int fLen; //sizeof(wave format matex)    short wFormatTag; //编码格式    short wChannels; //声道数    int nSamplesPersec ; //采样频率    int nAvgBitsPerSample;//WAVE文件采样大小    short wBlockAlign; //块对齐    short wBitsPerSample; //WAVE文件采样大小    char dld[4]; //”data“    int wSampleLength; //音频数据的大小} ;int set_pcm_play(char * filename);#endif

源文件:playwav.c

#include "play_wav.h"int set_pcm_play(char * filename){        int rc;        int ret;        int size;        snd_pcm_t* handle; //pcm文件句柄        snd_pcm_hw_params_t* params;//硬件参数结构体        unsigned int val;        int dir=0;        snd_pcm_uframes_t frames;    struct WAV_HEADER wav_header;        char *buffer;        int channels;        int frequency;        int bit;        int datablock;    int nread;   //unsigned char ch[100];         FILE *fp;        //首先打开wav文件        fp=fopen(filename,"rb");    if(fp==NULL)    {        perror("open file failed:\n");        exit(1);    }       //读取头部信息        nread=fread(&wav_header,1,sizeof(wav_header),fp);    printf("fread byte is:%d\n",nread);        //通过读取到的头部信息,初始化硬件参数        channels=wav_header.wChannels;        frequency=wav_header.nSamplesPersec;        bit=wav_header.wBitsPerSample;        datablock=wav_header.wBlockAlign;       //打开pcm设备        rc=snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);        if(rc<0)        {                perror("\nopen PCM device failed:");                exit(1);        }        //分配一个硬件参数结构体        snd_pcm_hw_params_alloca(&params);         if(rc<0)        {                perror("\nsnd_pcm_hw_params_alloca:");                exit(1);        }        //使用默认值初始化         rc=snd_pcm_hw_params_any(handle, params);        if(rc<0)        {                perror("\nsnd_pcm_hw_params_any:");                exit(1);        }        rc=snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); //交错模式        if(rc<0)        {                perror("\nsed_pcm_hw_set_access:");                exit(1);        }        //选择音频数据格式。        switch(bit/8)        {        case 1:snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_U8);                break ;        case 2:snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);                break ;        case 3:snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S24_LE);                break ;        }        rc=snd_pcm_hw_params_set_channels(handle, params, channels); //设置通道数        if(rc<0)        {                perror("\nsnd_pcm_hw_params_set_channels:");                exit(1);        }        val = frequency;        rc=snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); //设置比特率        if(rc<0)        {                perror("\nsnd_pcm_hw_params_set_rate_near:");                exit(1);        }        //设置硬件参数        rc = snd_pcm_hw_params(handle, params);        if(rc<0)        {        perror("\nsnd_pcm_hw_params: ");        exit(1);        }        //获取帧率        rc=snd_pcm_hw_params_get_period_size(params, &frames, &dir);        if(rc<0)        {                perror("\nsnd_pcm_hw_params_get_period_size:");                exit(1);        }        //通过帧率计算缓冲区的大小        size = frames * datablock;        buffer =(char*)malloc(size);    fseek(fp,58,SEEK_SET); //定位到数据区    //一下的循环就是不断的从数据区读数据出来,然后写入pcm设备中,从而播放出来    while (1)        {                memset(buffer,0,sizeof(buffer));                ret = fread(buffer, 1, size, fp);                if(ret == 0)                {                        printf("song write in completed\n");                        break;                }                 else if (ret != size)                {                 }        while((ret = snd_pcm_writei(handle, buffer, frames)<0))            {                 usleep(2000);                  if (ret == -EPIPE)                {                  /* EPIPE means underrun */                  fprintf(stderr, "underrun occurred\n");                  snd_pcm_prepare(handle);                 }                 else if (ret < 0)                 {                          fprintf(stderr,                      "error from writei: %s\n",                      snd_strerror(ret));                 }            }    }        fclose(fp);        snd_pcm_drain(handle);        snd_pcm_close(handle);        free(buffer);        return 0;}int main(int argc,char **argv){    if(argc !=2){        printf("usage:./playwav xxx.wav");        return -1;    }    set_pcm_play(argv[1]);return 0;}

函数中已经有相关的注释了。播放wav可以认为有四部分组成:
第一:解析文件头。
第二:参数设置阶段。这一阶段需要通过解析文件头得到的信息,来设置波特率,帧率,通道数等。
第二:读音频数据阶段。
第三:播放阶段。主要是snd_pcm_writei想pcm设备写入数据了。
编译:
gcc play_wav.c -lasound
执行:
./a.out song.wav

0 0
原创粉丝点击