博客
关于我
【秒懂音视频开发】09_播放PCM
阅读量:458 次
发布时间:2019-03-06

本文共 4192 字,大约阅读时间需要 13 分钟。

如何使用ffplay和SDL播放PCM音频

概述

在音频编程中,使用ffplay和SDL是实现音频播放的常用方法。以下将详细介绍如何通过这些工具和库来播放PCM音频文件。

ffplay的基本使用

PCM文件播放参数

  • 采样率(ar):音频的时间分辨率,常见值如44100Hz。
  • 声道数(ac):音频信号的数量,常见为2(立体声)。
  • 采样格式(f):定义了音频数据的存储方式。常见格式包括:
    • s16le:PCM signed 16-bit little-endian。
    • 其他格式可以通过查看ffmpeg的文档或使用命令获取。

示例命令

ffplay -ar 44100 -ac 2 -f s16le out.pcm

SDL音频播放库简介

SDL(Simple DirectMedia Layer)是一款跨平台的多媒体开发库,广泛应用于游戏开发和音视频播放。其音频功能可以帮助开发者高效管理和播放音频内容。

SDL的优势

  • 跨平台支持:支持Windows、Mac OS X、Linux等多种操作系统。
  • 多媒体功能:包括音频、图形、输入设备(如键盘、鼠标)的支持。
  • 稳定性:SDL拥有活跃的社区和丰富的文档资源。

安装SDL和FFmpeg

Windows安装

  • 通过MinGW编译器安装SDL和FFmpeg。
  • 解压后的目录结构类似于FFmpeg的安装目录,不会重复说明每个文件夹的作用。

Mac安装

  • 使用Homebrew安装:
    brew install sdl2

安装目录位于 /usr/local/Cellar/sdl2

一个简单的HelloWorld示例

#include 
#include
int main(int argc, char *argv) { if (SDL_Init(SDL_INIT_AUDIO) != 0) { printf("SDL_Init Error: %s\n", SDL_GetError()); return -1; } // 初始化音频设备 SDL_AudioSpec spec; spec.freq = 44100; // 采样率 spec.format = AUDIO_S16LSB; // 采样格式 spec.channels = 2; // 声道数 spec.samples = 1024; // 音频缓冲区大小 spec.callback = pull_audio_data; spec.userdata = &buffer; if (SDL_OpenAudio(&spec, NULL) != 0) { printf("SDL_OpenAudio Error: %s\n", SDL_GetError()); SDL_Quit(); return -1; } // 打开文件 FILE *file = fopen("in.pcm", "rb"); if (!file) { printf("无法打开文件: %s\n", "in.pcm"); SDL_CloseAudio(); SDL_Quit(); return -1; } // 开始播放 SDL_PauseAudio(0); // 读取并播放PCM数据 while (!isInterruptionRequested()) { if (buffer.len > 0) continue; buffer.len = fread((char *)data, BUFFER_SIZE, file); if (buffer.len <= 0) { // 计算延迟并等待 int samples = buffer.pullLen / BYTES_PER_SAMPLE; int ms = samples * 1000 / 44100; SDL_Delay(ms); break; } buffer.data = data; } // 释放资源 fclose(file); SDL_CloseAudio(); SDL_Quit(); return 0;}

PCM播放实现详解

初始化音频系统

// 初始化Audio子系统if (SDL_Init(SDL_INIT_AUDIO)) {    qDebug() << "SDL_Init Error" << SDL_GetError();    return;}

打开音频设备

// 音频参数定义#define SAMPLE_RATE 44100#define SAMPLE_FORMAT AUDIO_S16LSB#define SAMPLE_SIZE (AUDIO_S16LSB & 0xff) ? 1 : 2  // 根据采样格式确定样本大小#define CHANNELS 2#define SAMPLES 1024typedef struct {    int len;    // 已处理的数据长度    int pullLen; // 从文件中读取的数据长度    Uint8 *data; // 存储音频数据的缓冲区} AudioBuffer;// 初始化SDL_AudioSpecSDL_AudioSpec spec;spec.freq = SAMPLE_RATE;  // 采样率spec.format = SAMPLE_FORMAT;  // 采样格式spec.channels = CHANNELS;  // 声道数spec.samples = SAMPLES;  // 缓冲区样本数量// 回调函数定义void pull_audio_data(void *userdata, Uint8 *stream, int len) {    AudioBuffer *buffer = (AudioBuffer *)userdata;    if (buffer->len == 0) return;    // 确保读取的数据不超过缓冲区长度    buffer->pullLen = (len > buffer->len) ? buffer->len : len;    // 将音频数据混合到缓冲区    SDL_MixAudio(stream, buffer->data, buffer->pullLen, SDL_MIX_MAXVOLUME);    // 更新缓冲区指针    buffer->data += buffer->pullLen;    buffer->len -= buffer->pullLen;}

打开文件并开始播放

// 定义文件路径#define FILENAME "in.pcm"// 创建QFile对象并打开文件QFile file(FILENAME);if (!file.open(QFile::ReadOnly)) {    qDebug() << "文件打开失败" << FILENAME;    // 关闭音频设备    SDL_CloseAudio();    // 退出SDL系统    SDL_Quit();    return;}

开始播放音频

// 开始播放SDL_PauseAudio(0);// 读取音频数据Uint8 data[BUFFER_LEN];while (!isInterruptionRequested()) {    if (buffer.len > 0) continue;    buffer.len = file.read((char *)data, BUFFER_SIZE);    if (buffer.len <= 0) {        // 计算剩余样本的时间延迟        int samples = buffer.pullLen / BYTES_PER_SAMPLE;        int ms = samples * 1000 / SAMPLE_RATE;        SDL_Delay(ms);        break;    }    buffer.data = data;}

回调函数处理

// 回调函数填充音频数据void pull_audio_data(void *userdata, Uint8 *stream, int len) {    AudioBuffer *buffer = (AudioBuffer *)userdata;    if (buffer->len == 0) return;    // 确保读取的数据不超过缓冲区长度    buffer->pullLen = (len > buffer->len) ? buffer->len : len;    // 将音频数据混合到缓冲区    SDL_MixAudio(stream, buffer->data, buffer->pullLen, SDL_MIX_MAXVOLUME);    // 更新缓冲区指针    buffer->data += buffer->pullLen;    buffer->len -= buffer->pullLen;}

释放资源

// 关闭文件file.close();// 关闭音频设备SDL_CloseAudio();// 退出SDL系统SDL_Quit();

总结

通过以上步骤,我们成功实现了使用ffplay和SDL来播放PCM音频文件。这个过程涵盖了从初始化音频系统到实际播放的各个环节,确保了音频数据能够正确读取和播放。这是一个基础但重要的技能,对于后续的多媒体开发工作至关重要。

转载地址:http://vxhyz.baihongyu.com/

你可能感兴趣的文章
Numpy多项式.Polynomial.fit()给出的系数与多项式.Polyfit()不同
查看>>
Numpy如何使用np.umprod重写range函数中i的python
查看>>
numpy学习笔记3-array切片
查看>>
numpy数组替换其中的值(如1替换为255)
查看>>
numpy数组索引-ChatGPT4o作答
查看>>
numpy最大值和最大值索引
查看>>
NUMPY矢量化np.prod不能构造具有超过32个操作数的ufunc
查看>>
Numpy矩阵与通用函数
查看>>
numpy绘制热力图
查看>>
numpy转PIL 报错TypeError: Cannot handle this data type
查看>>
Numpy闯关100题,我闯了95关,你呢?
查看>>
nump模块
查看>>
Nutch + solr 这个配合不错哦
查看>>
NuttX 构建系统
查看>>
NutUI:京东风格的轻量级 Vue 组件库
查看>>