zoukankan      html  css  js  c++  java
  • SDL播放PCM音频数据

    #include <stdio.h>
    #include <SDL.h>
    
    // 每次读取2帧数据, 以1024个采样点一帧 2通道 16bit采样点为例
    #define PCM_BUFFER_SIZE (1024*2*2*2)
    
    // 音频PCM数据缓存
    static Uint8 *s_audio_buf = NULL;
    // 目前读取的位置
    static Uint8 *s_audio_pos = NULL;
    // 缓存结束位置
    static Uint8 *s_audio_end = NULL;
    
    
    // 音频设备回调函数
    void fill_audio_pcm(void *udata, Uint8 *stream, int len)
    {
        SDL_memset(stream, 0, len);
    
        if(s_audio_pos >= s_audio_end) // 数据读取完毕
        {
            return;
        }
    
        // 数据够了就读预设长度,数据不够就只读部分(不够的时候剩多少就读取多少)
        int remain_buffer_len = s_audio_end - s_audio_pos;
        len = (len < remain_buffer_len) ? len : remain_buffer_len;
        // 拷贝数据到stream并调整音量
        SDL_MixAudio(stream, s_audio_pos, len, SDL_MIX_MAXVOLUME/8);
        printf("len = %d
    ", len);
        s_audio_pos += len;  // 移动缓存指针
    }
    
    // 提取PCM文件
    // ffmpeg -i input.mp4 -t 20 -codec:a pcm_s16le -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm
    // 测试PCM文件
    // ffplay -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm
    
    #undef main
    int main(int argc, char* argv[])
    {
        // 1.初始化SDL
        if (SDL_Init(SDL_INIT_AUDIO)) {
            fprintf(stderr, "SDL_Init error: %s
    ", SDL_GetError());
            return -1;
        }
    
        // 2.打开PCM文件
        FILE *audio_fd = fopen("44100_16bit_2ch.pcm", "rb");
        if (audio_fd == NULL) {
            printf("fopen pcm file error.
    ");
            return -1;
        }
    
        // 3.打开音频设备
        SDL_AudioSpec spec;
        spec.freq = 44100;          // 采样频率
        spec.format = AUDIO_S16SYS; // 采样格式
        spec.channels = 2;          // 通道
        spec.silence = 0;           // 静音值
        spec.samples = 1024;        // 采样个数
        spec.callback = fill_audio_pcm;
        spec.userdata = NULL;
        if (SDL_OpenAudio(&spec, NULL)) {
            fprintf(stderr, "SDL_OpenAudio error: %s
    ", SDL_GetError());
            fclose(audio_fd);
            SDL_Quit();
            return -1;
        }
    
        // 5.播放音频
        SDL_PauseAudio(0);
        int data_count = 0;
        s_audio_buf = (uint8_t *)malloc(PCM_BUFFER_SIZE);
        while(1) {
            // 读取pcm数据
            size_t len = fread(s_audio_buf, 1, PCM_BUFFER_SIZE, audio_fd);
            if (len == 0) {
                break;
            }
            data_count += len;
            printf("now playing %10d bytes data.
    ",data_count);
            s_audio_end = s_audio_buf + len;
            s_audio_pos = s_audio_buf;
            while(s_audio_pos < s_audio_end)
            {
                SDL_Delay(10);  // 等待PCM数据消耗
            }
        }
    
        // 6.关闭音频设备
        printf("play PCM finish
    ");
        SDL_CloseAudio();
    
        // 7.退出
        if (s_audio_buf) {
            free(s_audio_buf);
        }
        if (audio_fd) {
            fclose(audio_fd);
        }
        SDL_Quit();
        return 0;
    }
  • 相关阅读:
    VLC通过RTSP地址向IPC取流播放不了问题排查
    linux opt分区扩容操作案例
    win10远程桌面报错"出现身份验证错误"
    linux通过expect实现脚本自动交互
    oracle通过触发器记录登陆主机ip
    linux root密码忘记重置
    linux双网卡配置
    Tomcat报错合集
    UFT(QTP)中的Object Repository
    利用JAVA反射机制设计通用的DAO
  • 原文地址:https://www.cnblogs.com/vczf/p/13510065.html
Copyright © 2011-2022 走看看