简介: CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中...... 】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中...... 】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀
专题四:Android15快速自定义与集成音效实战课 🚀
专题五:Android15音频策略实战课 🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀
人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉
- [🌻1. 前言](#🌻1. 前言)
- [🌻2. 应用场景与用法](#🌻2. 应用场景与用法)
- 应用场景
- [🌻3. 调用流程剖析](#🌻3. 调用流程剖析)
-
- [🌻3.1 核心步骤](#🌻3.1 核心步骤)
-
- [1. PipeWire 环境已经初始化](#1. PipeWire 环境已经初始化)
- [2. 创建或获取 Core 对象](#2. 创建或获取 Core 对象)
- [3. 构造 Stream 属性](#3. 构造 Stream 属性)
- [4. 调用 pw_stream_new](#4. 调用 pw_stream_new)
- [5. 创建内部 Stream 对象](#5. 创建内部 Stream 对象)
- [6. 设置 Stream 初始状态](#6. 设置 Stream 初始状态)
- [7. 返回 Stream 句柄](#7. 返回 Stream 句柄)
- [8. 后续连接 Stream](#8. 后续连接 Stream)
- [🌻3.2 调用流程图](#🌻3.2 调用流程图)
- [🌻3.3 生命周期图](#🌻3.3 生命周期图)
- [🌻4. 实战应用案例](#🌻4. 实战应用案例)
- [🌻5. 一句话总结](#🌻5. 一句话总结)
🌻1. 前言
本篇目的:
Linux PipeWire 深度解析之 pw_stream_new 调用流程与实战。
要点概括
- 核心功能:创建一个 PipeWire 客户端侧的 Stream 对象,用于后续建立音频、视频或其他媒体数据流。
- 工作机制 :
pw_stream_new()基于pw_core创建struct pw_stream,初始化 Stream 属性、事件列表、内部状态和用户数据区,但不会立即连接设备,也不会开始数据传输。 - 典型用途 :创建播放流、创建录音流、创建自定义音视频处理流,并为后续
pw_stream_connect()、Buffer 分配和process回调建立基础对象。
🌻2. 应用场景与用法
pw_stream_new() 是 PipeWire Stream 生命周期管理中的创建接口。
应用在连接 PipeWire Core 后,可以调用:
c
stream =
pw_stream_new(
core,
"audio-playback",
props);
而该接口用于:
基于已有
pw_core创建一个客户端侧 Stream 对象,并为后续连接、格式协商、Buffer 管理和数据处理回调准备基础状态。
需要注意:
pw_stream_new()只负责创建 Stream 对象,不会自动连接 Sink、Source、Node 或设备,也不会启动音频数据流。
真正让 Stream 进入 PipeWire 图、参与路由和数据传输的接口是:
c
pw_stream_connect(
stream,
direction,
target_id,
flags,
params,
n_params);
函数原型
c
struct pw_stream *pw_stream_new(
struct pw_core *core,
const char *name,
struct pw_properties *props);
参数说明
c
core:
由 pw_context_connect() 或相关接口获得的 PipeWire Core 对象
name:
Stream 名称,用于标识这条媒体流
props:
Stream 属性集合,用于描述媒体角色、节点名称、应用名称、目标设备等信息
常见属性包括:
c
PW_KEY_MEDIA_TYPE
PW_KEY_MEDIA_CATEGORY
PW_KEY_MEDIA_ROLE
PW_KEY_NODE_NAME
PW_KEY_NODE_DESCRIPTION
PW_KEY_APP_NAME
例如创建音乐播放流时,可以设置:
c
props =
pw_properties_new(
PW_KEY_MEDIA_TYPE,
"Audio",
PW_KEY_MEDIA_CATEGORY,
"Playback",
PW_KEY_MEDIA_ROLE,
"Music",
NULL);
返回值
c
创建成功:
返回 struct pw_stream *
创建失败:
返回 NULL
返回的 struct pw_stream * 是后续 Stream 操作的核心句柄。
调用者需要在不再使用时调用:
c
pw_stream_destroy(
stream);
释放 Stream 对象。
应用场景
pw_stream_new() 常见应用场景主要有三类。
第一类是创建音频播放流。播放器、提示音程序、车载导航播报模块可以通过 pw_stream_new() 创建播放 Stream,再通过 pw_stream_connect() 指定 PW_DIRECTION_OUTPUT,最终把应用侧音频数据送入 PipeWire 图中。
第二类是创建音频录音流。录音程序、语音识别前端、通话采集模块可以通过 pw_stream_new() 创建录音 Stream,再使用 PW_DIRECTION_INPUT 连接到 Source 或 Capture 节点,从 PipeWire 获取音频采集数据。
第三类是创建自定义音视频处理流。PipeWire 不只处理普通音频播放和录音,也可以用于视频、相机、屏幕采集和自定义媒体节点。应用可以通过 Stream 抽象连接图中的媒体节点,并在 process 回调中处理 Buffer。
🌻3. 调用流程剖析
🌻3.1 核心步骤
1. PipeWire 环境已经初始化
应用首先初始化 PipeWire 环境:
c
pw_init(
&argc,
&argv);
随后创建 Main Loop 和 Context:
c
main_loop =
pw_main_loop_new(
NULL);
context =
pw_context_new(
pw_main_loop_get_loop(
main_loop),
NULL,
0);
2. 创建或获取 Core 对象
pw_stream_new() 依赖 pw_core。
应用通常通过:
c
core =
pw_context_connect(
context,
NULL,
0);
连接 PipeWire Core。
core 表示当前客户端与 PipeWire Server 之间的核心连接对象,也是后续创建 Stream、Registry、Proxy 等对象的基础。
3. 构造 Stream 属性
调用 pw_stream_new() 前,应用可以构造 pw_properties:
c
props =
pw_properties_new(
PW_KEY_MEDIA_TYPE,
"Audio",
PW_KEY_MEDIA_CATEGORY,
"Playback",
PW_KEY_MEDIA_ROLE,
"Music",
NULL);
这些属性不会直接启动数据流,但会影响后续 Stream 暴露给 PipeWire 图时的身份信息、路由策略和会话管理行为。
4. 调用 pw_stream_new
应用执行:
c
stream =
pw_stream_new(
core,
"audio-playback",
props);
此时进入 PipeWire Stream API。
该函数的核心任务是创建客户端侧 struct pw_stream,并把它和当前 pw_core 关联起来。
5. 创建内部 Stream 对象
pw_stream_new() 内部会分配并初始化 Stream 对象。
逻辑上可以理解为:
text
分配 struct pw_stream
初始化属性
初始化事件列表
初始化状态
保存 core
保存 name
准备 user_data 区域
此时 Stream 只是一个本地对象,还没有真正进入 PipeWire 图。
6. 设置 Stream 初始状态
Stream 创建完成后,会处于可配置状态。
此阶段适合继续调用:
c
pw_stream_add_listener(
stream,
&listener,
&stream_events,
userdata);
注册状态变化、参数变化、Buffer 处理等事件回调。
7. 返回 Stream 句柄
创建成功后,pw_stream_new() 返回:
c
struct pw_stream *
如果内存分配失败、参数非法或 Core 不可用,则返回:
c
NULL
调用者需要检查返回值:
c
if (!stream) {
goto finish;
}
8. 后续连接 Stream
pw_stream_new() 本身不连接设备。
应用还需要调用:
c
pw_stream_connect(
stream,
PW_DIRECTION_OUTPUT,
PW_ID_ANY,
PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS,
params,
n_params);
之后 Stream 才会进入 PipeWire 图,参与格式协商、路由选择、Buffer 分配和 process 回调。
🌻3.2 调用流程图

🌻3.3 生命周期图

🌻4. 实战应用案例
c
#include <pipewire/pipewire.h> // PipeWire核心API
#include <spa/param/audio/format-utils.h>
#include <spa/param/props.h>
#include <spa/pod/builder.h>
#include <stdlib.h>
#include <string.h>
struct app_data {
struct pw_main_loop *main_loop;
struct pw_context *context;
struct pw_core *core;
struct pw_stream *stream;
int result;
};
static void stream_state_changed_cb(
void *userdata,
enum pw_stream_state old,
enum pw_stream_state state,
const char *error) {
struct app_data *data =
userdata;
if (state == PW_STREAM_STATE_ERROR ||
state == PW_STREAM_STATE_UNCONNECTED) {
data->result =
EXIT_FAILURE;
pw_main_loop_quit(
data->main_loop);
}
}
static void stream_process_cb(
void *userdata) {
struct app_data *data =
userdata;
struct pw_buffer *buffer =
pw_stream_dequeue_buffer(
data->stream);
if (!buffer) {
return;
}
struct spa_buffer *spa_buffer =
buffer->buffer;
if (spa_buffer->datas[0].data &&
spa_buffer->datas[0].chunk) {
uint32_t size =
spa_buffer->datas[0].maxsize;
memset(
spa_buffer->datas[0].data,
0,
size);
spa_buffer->datas[0].chunk->offset =
0;
spa_buffer->datas[0].chunk->size =
size;
spa_buffer->datas[0].chunk->stride =
sizeof(float) * 2;
}
pw_stream_queue_buffer(
data->stream,
buffer);
}
static const struct pw_stream_events stream_events = {
PW_VERSION_STREAM_EVENTS,
.state_changed =
stream_state_changed_cb,
.process =
stream_process_cb,
};
int main(
int argc,
char *argv[]) {
struct app_data data = {0};
uint8_t buffer[1024];
struct spa_pod_builder builder =
SPA_POD_BUILDER_INIT(
buffer,
sizeof(buffer));
const struct spa_pod *params[1];
struct spa_audio_info_raw audio_info = {
.format =
SPA_AUDIO_FORMAT_F32,
.rate =
48000,
.channels =
2,
.position = {
SPA_AUDIO_CHANNEL_FL,
SPA_AUDIO_CHANNEL_FR,
},
};
data.result =
EXIT_SUCCESS;
pw_init(
&argc,
&argv); // 初始化PipeWire环境
data.main_loop =
pw_main_loop_new(
NULL); // 创建Main Loop
if (!data.main_loop) {
data.result =
EXIT_FAILURE;
goto finish_deinit;
}
data.context =
pw_context_new(
pw_main_loop_get_loop(
data.main_loop),
NULL,
0); // 创建Context
if (!data.context) {
data.result =
EXIT_FAILURE;
goto finish_main_loop;
}
data.core =
pw_context_connect(
data.context,
NULL,
0); // 连接PipeWire Core
if (!data.core) {
data.result =
EXIT_FAILURE;
goto finish_context;
}
data.stream =
pw_stream_new(
data.core,
"pw-stream-new-demo",
pw_properties_new(
PW_KEY_MEDIA_TYPE,
"Audio",
PW_KEY_MEDIA_CATEGORY,
"Playback",
PW_KEY_MEDIA_ROLE,
"Music",
PW_KEY_NODE_NAME,
"pw_stream_new_demo",
NULL)); // 创建Stream对象
if (!data.stream) {
data.result =
EXIT_FAILURE;
goto finish_core;
}
pw_stream_add_listener(
data.stream,
NULL,
&stream_events,
&data); // 注册Stream事件回调
params[0] =
spa_format_audio_raw_build(
&builder,
SPA_PARAM_EnumFormat,
&audio_info); // 构造音频格式参数
if (pw_stream_connect(
data.stream,
PW_DIRECTION_OUTPUT,
PW_ID_ANY,
PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS,
params,
1) < 0) {
data.result =
EXIT_FAILURE;
goto finish_stream;
}
pw_main_loop_run(
data.main_loop); // 进入事件循环
finish_stream:
pw_stream_destroy(
data.stream); // 销毁Stream
finish_core:
pw_core_disconnect(
data.core); // 断开Core
finish_context:
pw_context_destroy(
data.context); // 销毁Context
finish_main_loop:
pw_main_loop_destroy(
data.main_loop); // 销毁Main Loop
finish_deinit:
pw_deinit(); // 释放PipeWire环境资源
return data.result;
}
🌻5. 一句话总结
pw_stream_new() 本质上是:
"基于 PipeWire Core 创建一个客户端侧 Stream 对象,为后续连接、格式协商、Buffer 管理和数据处理回调建立基础句柄"。
它负责创建 Stream 对象、保存属性、关联 Core 并准备事件回调体系,但不负责连接设备、不负责启动数据传输、不负责完成路由选择。
它是 PipeWire 播放流、录音流和自定义媒体流创建流程中的基础接口。