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

🍉🍉🍉文章目录🍉🍉🍉
🌻1.前言
本篇目的:
Linux PipeWire深度解析之pw_stream_new调用流程与实战。
要点概括
-
核心功能:创建一个PipeWireStream对象,为后续播放、录音、视频采集或视频输出建立客户端侧控制入口。
-
工作机制:基于已有pw_core创建pw_stream结构,保存Stream名称、属性、Core关联关系、状态管理和事件基础设施。
-
典型用途:应用已经连接PipeWire服务端后,需要创建一个可连接到Graph的媒体Stream。
pw_stream_new的本质是"创建Stream控制对象",不是"开始传输媒体数据"。它只让应用得到一个struct pw_stream*,后续还需要通过pw_stream_add_listener注册事件,通过pw_stream_connect接入PipeWireGraph,才能进入真正的数据处理链路。
它和pw_stream_connect的边界很清楚。pw_stream_new负责创建Stream对象;pw_stream_connect负责把Stream连接到PipeWireGraph。没有connect之前,Stream还没有远端Node、Port、Link和Buffer。
它和pw_stream_dequeue_buffer也不同。pw_stream_dequeue_buffer用于process回调中的Buffer取出,属于数据路径;pw_stream_new属于对象创建路径。前者处理媒体数据,后者建立Stream生命周期起点。
它和pw_stream_new_simple也不同。pw_stream_new适合应用已经持有pw_core的场景;pw_stream_new_simple更适合简单示例或轻量客户端,它把部分Core和事件循环处理做了封装。
🌻2.应用场景与用法
pw_stream_new
是PipeWireStream API中用于创建Stream对象的接口。
它位于PipeWire客户端接入链路的起点。应用连接到PipeWire服务端并获得pw_core之后,就可以调用pw_stream_new创建一个Stream对象。这个Stream对象后续可以配置事件、连接目标、协商格式、接收Buffer事件,并进入process回调处理媒体数据。
pw_stream_new用于基于已有pw_core创建一个PipeWireStream对象。
函数原型
c
struct pw_stream *pw_stream_new(struct pw_core *core,
const char *name,
struct pw_properties *props);
参数说明
c
struct pw_core *core;
core表示应用已经连接到PipeWire服务端后得到的Core对象。
它是Stream连接PipeWire服务端的基础。pw_stream_new创建出来的Stream会绑定到这个Core,后续pw_stream_connect会通过该Core与服务端交互。
c
const char *name;
name表示Stream名称。
这个名称通常用于调试、对象标识、日志显示和会话管理策略识别。例如播放器可以设置为"music-playback",录音程序可以设置为"audio-capture"。
c
struct pw_properties *props;
props表示Stream属性集合。
常见属性包括媒体类型、媒体分类、应用名称、节点名称、Stream角色等。它会影响PipeWire和WirePlumber对这个Stream的识别、路由和策略处理。
工程上通常通过pw_properties_new创建props,然后传给pw_stream_new。传入后不要再把同一个props当作普通外部对象随意释放或复用。
返回值
成功时返回:
c
struct pw_stream *
表示新创建的Stream对象。
失败时返回NULL。常见原因包括core无效、内存分配失败或内部初始化失败。
返回成功并不表示Stream已经连接成功,也不表示已经有Buffer可用。此时只是创建了客户端侧Stream对象。要进入媒体处理链路,还需要继续调用pw_stream_connect。
应用场景
第一类场景是播放器创建播放Stream。
播放器先连接PipeWire服务端,得到pw_core,然后调用pw_stream_new创建播放Stream,再设置媒体属性并连接到默认Sink或指定Node。
第二类场景是录音程序创建采集Stream。
录音程序同样先创建Stream对象,再通过pw_stream_connect以输入方向连接到Source或录音设备Node,之后在process回调中读取采集数据。
第三类场景是复杂客户端统一管理多个Stream。
例如一个音视频会议程序可能同时创建麦克风采集Stream、扬声器播放Stream、摄像头采集Stream和屏幕共享Stream。使用pw_stream_new可以让这些Stream共享同一个pw_core和事件循环上下文。
第四类场景是需要明确控制Core生命周期的程序。
如果应用已经自己管理pw_context、pw_core、pw_loop和权限逻辑,就更适合使用pw_stream_new,而不是使用封装更高的pw_stream_new_simple。
🌻3.调用流程剖析
🌻3.1核心步骤
1.应用初始化PipeWire运行环境,并连接PipeWire服务端。
2.应用获得pw_core对象。
3.应用准备Stream名称和属性props。
4.应用调用pw_stream_new(core,name,props)。
5.pw_stream_new检查core参数,并为Stream分配客户端侧结构。
6.Stream保存Core引用、Stream名称和属性集合。
7.Stream初始化内部状态、事件Hook、Buffer列表和监听结构。
8.Stream进入未连接状态,此时还没有接入PipeWireGraph。
9.pw_stream_new返回struct pw_stream*给应用。
10.应用调用pw_stream_add_listener注册Stream事件。
11.应用调用pw_stream_connect设置方向、目标ID、连接标志和格式参数。
12.连接完成后,Stream才进入格式协商、Buffer管理和process数据处理阶段。
🌻3.2调用流程图

🌻3.3生命周期图

🌻4.实战应用案例
下面以"创建一个播放Stream对象"为例,说明pw_stream_new的典型使用方式。
这个案例只展示pw_stream_new在主链路中的位置,不展示编译命令和程序运行结果。
c
#include <pipewire/pipewire.h>
struct app_data {
struct pw_core *core;
struct pw_stream *stream;
struct spa_hook stream_listener;
};
首先定义应用数据结构。
core表示已经连接到PipeWire服务端的Core对象。
stream表示通过pw_stream_new创建出来的Stream对象。
stream_listener用于保存事件监听Hook,避免监听器生命周期过早结束。
c
static void on_stream_process(void *userdata)
{
struct app_data *data = userdata;
struct pw_buffer *b;
b = pw_stream_dequeue_buffer(data->stream);
if (b == NULL)
return;
/*
* 播放方向:
* 这里通常向Buffer写入PCM数据。
*
* 采集方向:
* 这里通常从Buffer读取PCM数据。
*/
pw_stream_queue_buffer(data->stream, b);
}
process回调不是由pw_stream_new直接触发的。它只有在Stream完成connect、进入Graph调度,并且有可用Buffer之后才会被触发。
pw_stream_new只创建Stream对象,不负责取Buffer,也不负责提交Buffer。
c
static const struct pw_stream_events stream_events = {
PW_VERSION_STREAM_EVENTS,
.process = on_stream_process,
};
这里定义Stream事件表。
对于播放或录音程序,process事件最重要。它代表PipeWireGraph已经调度到该Stream,应用需要在这个回调中生产或消费媒体数据。
c
static struct pw_stream *create_playback_stream(struct app_data *data)
{
struct pw_properties *props;
props = pw_properties_new(
PW_KEY_MEDIA_TYPE, "Audio",
PW_KEY_MEDIA_CATEGORY, "Playback",
PW_KEY_MEDIA_ROLE, "Music",
PW_KEY_APP_NAME, "pipewire-playback-demo",
NULL);
data->stream = pw_stream_new(data->core,
"playback-stream",
props);
if (data->stream == NULL)
return NULL;
pw_stream_add_listener(data->stream,
&data->stream_listener,
&stream_events,
data);
return data->stream;
}
这段代码体现了pw_stream_new的核心用法。
第一步,准备Stream属性。
c
props = pw_properties_new(
PW_KEY_MEDIA_TYPE, "Audio",
PW_KEY_MEDIA_CATEGORY, "Playback",
PW_KEY_MEDIA_ROLE, "Music",
PW_KEY_APP_NAME, "pipewire-playback-demo",
NULL);
这些属性会告诉PipeWire和会话管理器:这是一个音频播放流,角色是音乐播放。WirePlumber这类会话管理器可以根据这些属性进行路由、权限和策略处理。
第二步,创建Stream对象。
c
data->stream = pw_stream_new(data->core,
"playback-stream",
props);
这里的核心点是:pw_stream_new只需要已有core、Stream名称和属性集合。调用成功后,应用得到一个Stream控制对象。
第三步,注册Stream事件。
c
pw_stream_add_listener(data->stream,
&data->stream_listener,
&stream_events,
data);
pw_stream_new本身不接收事件表。事件需要通过pw_stream_add_listener单独注册。这样process、state_changed、param_changed、add_buffer、remove_buffer等事件才能通知到应用。
接下来,Stream还需要connect。
c
static int connect_playback_stream(struct app_data *data,
const struct spa_pod **params,
uint32_t n_params)
{
return 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,
n_params);
}
这一步才是Stream进入PipeWireGraph的关键。
PW_DIRECTION_OUTPUT表示应用向Graph输出数据,也就是播放方向。
PW_ID_ANY表示不指定固定目标,由会话管理器选择合适的播放设备。
PW_STREAM_FLAG_AUTOCONNECT表示允许自动连接目标Node。
PW_STREAM_FLAG_MAP_BUFFERS表示希望PipeWire帮应用映射Buffer内存,便于直接读写。
PW_STREAM_FLAG_RT_PROCESS表示process回调会进入实时处理路径,应用必须避免阻塞操作。
从工程视角看,pw_stream_new和pw_stream_connect要分开理解。
pw_stream_new阶段:
c
data->stream = pw_stream_new(data->core,
"playback-stream",
props);
此时应用只是拿到了Stream对象。
pw_stream_connect阶段:
c
pw_stream_connect(data->stream,
PW_DIRECTION_OUTPUT,
PW_ID_ANY,
flags,
params,
n_params);
此时Stream才开始和PipeWire服务端交互,进入格式协商、Node创建、Port连接和Buffer准备流程。
如果把pw_stream_new误解成"创建播放节点并开始播放",后面分析PipeWire链路就会混乱。更准确的说法是:
pw_stream_new创建客户端侧Stream对象。
pw_stream_connect让Stream进入Graph连接流程。
pw_stream_dequeue_buffer和pw_stream_queue_buffer负责process回调中的Buffer流转。
🌻5.一句话总结
pw_stream_new是PipeWireStream生命周期的创建入口:它基于已有pw_core创建客户端侧Stream对象,但不连接Graph、不创建Buffer、不处理媒体数据,真正的数据链路要从pw_stream_connect之后才开始。