Linux下基于ALSA架构的音频录播测试程序

录制PCM音频文件(单通道 8000Hz),并进行播放.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <alsa/asoundlib.h>

typedef struct _ec_snd_pcm_info {
    snd_pcm_t *handle;              //PCM设备句柄.
    snd_pcm_stream_t stream;        //PCM流类型 捕获流 回放流.
    snd_pcm_access_t access_type;   //访问类型.
    snd_pcm_format_t format;        //访问大小端格式.
    unsigned int nChannels;         //通道数.
    unsigned int rate;              //采样率.
    snd_pcm_uframes_t psize_frames; //时段帧数.
    snd_pcm_uframes_t bsize_frames; //缓冲帧数.
    char *data_buf;                 //数据.
} ec_snd_pcm_info_t;

int record_param(ec_snd_pcm_info_t *info, snd_pcm_hw_params_t *&params)
{
    int dir = 0;
    //打开PCM设备.
    int err = snd_pcm_open(&info->handle, "default", info->stream, 0);
    if (err < 0) {
        printf("unabble to open pcm device : %s\n", snd_strerror(err));
        return -1;
    }
    //构造硬件参数空间.
    err = snd_pcm_hw_params_malloc(&params);
    if (err < 0) {
        printf("malloc space for snd_pcm_hw_params_t structure failed %s!\n", snd_strerror(err));
        return -1;
    }
    //初始化默认值.
    err = snd_pcm_hw_params_any(info->handle, params);
    if (err < 0) {
        printf("Broken configuration for this PCM %s\n", snd_strerror(err));
        return -1;
    }
    //设置访问类型.
    err = snd_pcm_hw_params_set_access(info->handle, params, info->access_type);
    if (err < 0) {
        printf("Access type not available %s\n", snd_strerror(err));
        return -1;
    }
    //设置大小端格式.
    err = snd_pcm_hw_params_set_format(info->handle, params, info->format);
    if (err < 0) {
        printf("Sample format non available %s\n", snd_strerror(err));
        return -1;
    }
    //设置通道.
    err = snd_pcm_hw_params_set_channels(info->handle, params, info->nChannels);
    if (err < 0) {
        printf("Channels count non available %s\n", snd_strerror(err));
        return -1;
    }
    //设置每秒采样率.
    err = snd_pcm_hw_params_set_rate_near(info->handle, params, &info->rate, &dir);
    if (err < 0) {
        printf("Set rate failed %s\n", snd_strerror(err));
        return -1;
    }
    //设置缓冲大小.
    err = snd_pcm_hw_params_set_buffer_size_near(info->handle, params,&info->bsize_frames);
    if (err < 0) {
        printf("Set period size failed %s\n", snd_strerror(err));
        return -1;
    }
    //设置时段大小.
    err = snd_pcm_hw_params_set_period_size_near(info->handle, params, &info->psize_frames, &dir);
    if (err < 0) {
        printf("Set buffer size failed %s\n", snd_strerror(err));
        return -1;
    }
    //参数设置到驱动.
    err = snd_pcm_hw_params(info->handle, params);
    if (err < 0) {
        printf("failed to set hardware parameters:%s\n", snd_strerror(err));
        return -1;
    }

    return 0;
}

int record_test(const char *filename)
{
    if (NULL == filename || strlen(filename) <= 0) {
        printf("filename is NULL!\n");
        return -1;
    }

    remove(filename);
    int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 666);
    if (fd < 0) {
        printf("open file failed!\n");
        return -1;
    }
    chmod(filename, 0777);
    printf("open record pcm file ok!\n");

    //设置参数.
    snd_pcm_hw_params_t *params = NULL;
    ec_snd_pcm_info_t info;
    memset(&info, 0, sizeof(ec_snd_pcm_info_t));
    info.stream = SND_PCM_STREAM_CAPTURE;
    info.access_type = SND_PCM_ACCESS_RW_INTERLEAVED;
    info.format = SND_PCM_FORMAT_S16_LE;
    info.nChannels = 1;
    info.rate = 8000;
    info.psize_frames = 320;
    info.bsize_frames = 640;
    if (record_param(&info, params) < 0) {
        close(fd);
        remove(filename);
        if (info.handle) {
            snd_pcm_close(info.handle);
        }
        if (info.data_buf) {
            free(info.data_buf);
            info.data_buf = NULL;
        }
        return -1;
    }
    printf("open pcm device and set param ok!\n");

    int dir = 0;
    //获取时段大小.
    int err = snd_pcm_hw_params_get_period_size(params, &info.psize_frames, &dir);
    if (err < 0) {
        printf("get period size fail %s\n", snd_strerror(err));
        close(fd);
        remove(filename);
        if (info.handle) {
            snd_pcm_close(info.handle);
        }
        if (info.data_buf) {
            free(info.data_buf);
            info.data_buf = NULL;
        }
        return -1;
    }
    printf("get_period_size=%d!\n", info.psize_frames);
    //获取时段时间.
    unsigned int period_time = 0;
    snd_pcm_hw_params_get_period_time(params, &period_time, &dir);
    int loop = 1000000 / period_time;
    loop *= 5;//录制5秒.
    printf("get_period_time=%d, loop=%d!\n", period_time, loop);

    info.data_buf = (char *)malloc(info.psize_frames * 2);

    int count = 0;
    while (loop--) {
        err = snd_pcm_readi(info.handle, info.data_buf, info.psize_frames);
        if (err == -EAGAIN || (err >= 0 && (size_t)err < info.psize_frames)) {
            snd_pcm_wait(info.handle, 1000);
        }
        else if (err == -EPIPE) {
            snd_pcm_prepare(info.handle);
        }
        else if (err == -ESTRPIPE) {
            snd_pcm_resume(info.handle);
        }
        else if (err < 0) {
            printf("Error snd_pcm_writei: [%s]", snd_strerror(err));
            close(fd);
            remove(filename);
            if (info.handle) {
                snd_pcm_close(info.handle);
            }
            if (info.data_buf) {
                free(info.data_buf);
                info.data_buf = NULL;
            }
            return -1;
        }

        //将音频数据写入文件
        count = write(fd, info.data_buf, info.psize_frames * 2);
        if (count != info.psize_frames * 2) {
            printf("short write: wrote %d bytes\n", count);
        }
    }

    snd_pcm_drain(info.handle);
    snd_pcm_close(info.handle);
    printf("record pcm data ok!\n");

    close(fd);

    if (NULL != info.data_buf) {
        free(info.data_buf);
        info.data_buf = NULL;
    }

    return 0;
}

int play_test(const char *filename)
{
    if (NULL == filename || strlen(filename) <= 0) {
        printf("filename is NULL!\n");
        return -1;
    }
    int fd = open(filename, O_RDONLY);
    if (fd < 0) {
        printf("open file failed!\n");
        return -1;
    }
    printf("open play pcm file ok!\n");

    //设置参数.
    snd_pcm_hw_params_t *params = NULL;
    ec_snd_pcm_info_t info;
    memset(&info, 0, sizeof(ec_snd_pcm_info_t));
    info.stream = SND_PCM_STREAM_PLAYBACK;
    info.access_type = SND_PCM_ACCESS_RW_INTERLEAVED;
    info.format = SND_PCM_FORMAT_S16_LE;
    info.nChannels = 1;
    info.rate = 8000;
    info.psize_frames = 320;
    info.bsize_frames = 640;
    if (record_param(&info, params) < 0) {
        close(fd);
        if (info.handle) {
            snd_pcm_close(info.handle);
        }
        if (info.data_buf) {
            free(info.data_buf);
            info.data_buf = NULL;
        }
        return -1;
    }
    printf("open pcm device and set param ok!\n");

    int dir = 0;
    int err = snd_pcm_hw_params_get_period_size_min(params, &info.psize_frames, &dir);
    if(err < 0) {
        printf("get period size min falied!\n");
        close(fd);
        if (info.handle) {
            snd_pcm_close(info.handle);
        }
        if (info.data_buf) {
            free(info.data_buf);
            info.data_buf = NULL;
        }
        return -1;
    }
    printf("get period size min = %d!\n", info.psize_frames);

    err = snd_pcm_hw_params_get_period_size(params, &info.psize_frames, &dir);
    if (err < 0) {
        printf("get period size fail\n");
        close(fd);
        if (info.handle) {
            snd_pcm_close(info.handle);
        }
        if (info.data_buf) {
            free(info.data_buf);
            info.data_buf = NULL;
        }
        return -1;
    }
    printf("get period size = %d!\n", info.psize_frames);

    err = snd_pcm_hw_params_get_buffer_size_min(params, &info.bsize_frames);
    if(err < 0) {
        printf("get buffer size min falied!\n");
        close(fd);
        if (info.handle) {
            snd_pcm_close(info.handle);
        }
        if (info.data_buf) {
            free(info.data_buf);
            info.data_buf = NULL;
        }
        return -1;
    }
    printf("get buffer size min = %d!\n", info.bsize_frames);

    err = snd_pcm_hw_params_get_buffer_size(params, &info.bsize_frames);
    if (err < 0) {
        printf("get buffer size failed\n");
        close(fd);
        if (info.handle) {
            snd_pcm_close(info.handle);
        }
        if (info.data_buf) {
            free(info.data_buf);
            info.data_buf = NULL;
        }
        return -1;
    }
    printf("get buffer size = %d!\n", info.bsize_frames);

    unsigned int buffer_time = 0;
    err = snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0);
    if(err < 0) {
        printf("Get buffer time failed!\n");
        close(fd);
        if (info.handle) {
            snd_pcm_close(info.handle);
        }
        if (info.data_buf) {
            free(info.data_buf);
            info.data_buf = NULL;
        }
        return -1;
    }
    printf("get_buffer_time_max = %d!\n", buffer_time);

    info.data_buf = (char*)malloc(info.psize_frames * 2);

    int count = 0;
    while (true) {
        count = read(fd, info.data_buf, info.psize_frames * 2);
        if (count <= 0) {
            break;
        }

        while((err = snd_pcm_writei(info.handle, info.data_buf, info.psize_frames)) < 0) {
            if(err == -EAGAIN) {
                snd_pcm_wait(info.handle, 1000);
            }
            else if(err == -EPIPE) {
                snd_pcm_prepare(info.handle);
            }
            else if(err == -ESTRPIPE) {
                snd_pcm_resume(info.handle);
            }
            else if(err < 0) {
                printf("snd_pcm_writei error:%s\n", snd_strerror(err));
            }
        }
    }

    snd_pcm_drain(info.handle);
    snd_pcm_close(info.handle);
    printf("play pcm data ok!\n");

    close(fd);

    if (info.data_buf) {
        free(info.data_buf);
        info.data_buf = NULL;
    }

    return 0;
}

你可能感兴趣的:(音视频)