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

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

如何使用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 1024
typedef struct {
int len; // 已处理的数据长度
int pullLen; // 从文件中读取的数据长度
Uint8 *data; // 存储音频数据的缓冲区
} AudioBuffer;
// 初始化SDL_AudioSpec
SDL_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/

你可能感兴趣的文章
nodejs中express的使用
查看>>
Nodejs中搭建一个静态Web服务器,通过读取文件获取响应类型
查看>>
Nodejs中的fs模块的使用
查看>>
NodeJS使用淘宝npm镜像站的各种姿势
查看>>
NodeJs入门知识
查看>>
nodejs包管理工具对比:npm、Yarn、cnpm、npx
查看>>
NodeJs单元测试之 API性能测试
查看>>
nodejs图片转换字节保存
查看>>
nodejs在Liunx上的部署生产方式-PM2
查看>>
nodejs基于art-template模板引擎生成
查看>>
nodejs字符与字节之间的转换
查看>>
NodeJs学习笔记001--npm换源
查看>>
NodeJs学习笔记002--npm常用命令详解
查看>>
nodejs学习笔记一——nodejs安装
查看>>
vue3+Element-plus icon图标无法显示的问题(已解决)
查看>>
NodeJS实现跨域的方法( 4种 )
查看>>
nodejs封装http请求
查看>>
nodejs常用组件
查看>>
nodejs开发公众号报错 40164,白名单配置找不到,竟然是这个原因
查看>>
Nodejs异步回调的处理方法总结
查看>>