OpenHarmony播放音乐start请求从media_service到audio_server的调用流程02
1)文章由移远通信技术股份有限公司提供
2)以下内容包含了个人理解,仅供参考,如有不合理处,请联系笔者修改/删除
文章目录
一、背景与目标
1.1 环境信息
- 操作系统:OpenHarmony 6.1
- 硬件平台:RK3576
- 内核版本:Linux 6.6
- 软件版本:SDK 6.1.0.31,API 23
1.2 问题背景
在OpenHarmony音频播放架构中,播放器启动音频输出的完整链路涉及多个进程间的协作。当应用层(如MiniMusic)发起播放请求后,该请求首先通过Binder IPC传递到media_service进程。然而,真正的音频硬件驱动操作并不在media_service中完成,而是需要进一步将音频流控制命令发送到专门的audio_server进程。
本文聚焦于分析播放器管线启动音频输出后,Start请求如何从media_service通过AudioRenderer IPC下发到audio_server。这是音频播放链路中的关键跨进程通信环节,理解这一过程对于调试音频播放问题和深入理解OpenHarmony音频架构至关重要。
1.3 分析目标
本文的目标是通过源码分析和GDB调试,完整证明以下调用链路的正确性:
// media_service 侧调用链
AudioSinkFilter::DoStart
-> AudioSink::Start
-> AudioServerSinkPlugin::Start
-> AudioRendererPrivate::Start
-> AudioRendererPrivate::GetStartStreamResult
-> RendererInClientInner::StartAudioStream
-> IpcStreamProxy::Start
-> remote->SendRequest(COMMAND_START)
// audio_server 侧处理链
IPCObjectStub::SendRequestInner(code=4)
-> IpcStreamStub::OnRemoteRequest(code=4)
-> IpcStreamInServer::Start
-> RendererInServer::Start
具体需要证明两个核心点:
media_service中确实调用了IpcStreamProxy::Start- 该
IpcStreamProxy的Binder对端确实是audio_server
1.4 前置知识
在上一篇分析中,我们已经证明了从应用层到media_service的调用链:
MiniMusic app
-> PlayerServiceProxy::Play()
-> Binder handle
-> media_service中的IStandardPlayerService服务端对象
-> PlayerServer::HandlePlay()
-> HiPlayerImpl::Play()
-> pipeline_->Start()
-> Pipeline::Start()
-> Filter::Start()
-> AudioSinkFilter::DoStart()
因此,本文的起点不再是app进程,而是media_service中已经被pipeline启动起来的AudioSinkFilter::DoStart()。从这里开始,音频输出分支继续调用AudioSink::Start()、AudioServerSinkPlugin::Start()和AudioRendererPrivate::Start(),最终通过IpcStreamProxy::Start()发送到audio_server。
二、调试环境准备
2.1 为什么Start会出现在media_service
在OpenHarmony的音频架构设计中,播放管线通常不直接运行在应用进程中。对于系统播放器、媒体框架播放器或ohos.samples.distributedmusicplayer这类应用,播放管线运行在media_service的HiPlayer线程中。
这种设计带来了一个重要影响:调试播放器Start链路时,如果只attach应用进程,很可能无法观察到AudioRendererPrivate::Start()的调用。正确的调试目标应该是media_service进程。
2.2 调试步骤
步骤1:附加到media_service进程
# 获取media_service进程ID
pidof media_service
# 使用GDB附加到media_service进程
gdb-multiarch -q <media_service可执行文件>
步骤2:忽略常见实时信号
进入GDB后,为了避免调试过程中被实时信号干扰,建议先忽略SIG38信号:
handle SIG38 nostop noprint pass
三、关键断点设置
3.1 客户端IPC断点
要捕获framework client发起IPC的关键节点,建议设置以下断点:
# 客户端音频渲染器启动断点
b OHOS::AudioStandard::AudioRendererPrivate::Start
b OHOS::AudioStandard::AudioRendererPrivate::GetStartStreamResult
b OHOS::AudioStandard::RendererInClientInner::StartAudioStream
b OHOS::AudioStandard::IpcStreamProxy::Start
3.2 备用断点策略
如果IpcStreamProxy::Start符号暂时没有加载,可以采用以下备用方案:
方案1:在生成文件行号下断点
当命中RendererInClientInner::StartAudioStream后,可以对生成的IPC代理文件设置断点:
b gen/foundation/multimedia/audio_framework/services/audio_service/idl/ipc_stream_proxy.cpp:179
方案2:直接在源码调用行下断点
b ../../foundation/multimedia/audio_framework/services/audio_service/client/src/renderer_in_client_public.cpp:1065
3.3 服务端断点(audio_server侧)
# 附加到audio_server进程后设置
b OHOS::AudioStandard::IpcStreamInServer::Start
b OHOS::AudioStandard::RendererInServer::Start
四、media_service中的播放器管线调用栈分析
4.1 GDB调用栈捕获
当命中RendererInClientInner::StartAudioStream断点时,GDB显示的调用栈如下:

#0 OHOS::AudioStandard::RendererInClientInner::StartAudioStream
at renderer_in_client_public.cpp:1057
#1 OHOS::AudioStandard::AudioRendererPrivate::GetStartStreamResult
at audio_renderer.cpp:1025
#2 OHOS::AudioStandard::AudioRendererPrivate::Start
at audio_renderer.cpp:1255
#3 OHOS::Media::Plugins::AudioServerSinkPlugin::Start
at audio_server_sink_plugin.cpp:476
#4 OHOS::Media::AudioSink::Start
at audio_sink.cpp:326
#5 OHOS::Media::Pipeline::AudioSinkFilter::DoStart
at audio_sink_filter.cpp:160
#6 OHOS::Media::Pipeline::Filter::StartDone
at filter.cpp:165
#7 OHOS::Media::Pipeline::Filter::Start()::$_2::operator()()
at filter.cpp:143
#14 OHOS::Media::TaskInner::HandleJob
at taskInner.cpp:331
#15 OHOS::Media::PipeLineThread::Run
at pipeline_threadpool.cpp:207
#18 OHOS::Media::Thread::Run
at thread.cpp:161
4.2 业务链路提取
去除标准库std::function和线程封装帧后,核心业务调用链清晰可见:
AudioSinkFilter::DoStart
-> AudioSink::Start
-> AudioServerSinkPlugin::Start
-> AudioRendererPrivate::Start
-> AudioRendererPrivate::GetStartStreamResult
-> RendererInClientInner::StartAudioStream
关键发现:AudioRendererPrivate::Start()确实是由media_service内部的播放器管线触发的,这验证了我们的第一个假设。
五、StartAudioStream 真正发起 ipcStream_->Start
RendererInClientInner::StartAudioStream() 的关键源码:
// foundation/multimedia/audio_framework/services/audio_service/client/src/renderer_in_client_public.cpp
bool RendererInClientInner::StartAudioStream(StateChangeCmdType cmdType,
AudioStreamDeviceChangeReasonExt reason)
{
...
CHECK_AND_CALL_FUNC_RETURN_RET(ipcStream_ != nullptr, false,
HILOG_COMM_ERROR("[StartAudioStream]ipcStream is not inited!"));
int32_t ret = ipcStream_->Start();
CHECK_AND_CALL_FUNC_RETURN_RET(ret == SUCCESS, false,
HILOG_COMM_ERROR("[StartAudioStream]Start call server failed:%{public}u", ret));
...
}

在 GDB 中停到 1065 行:
b ../../foundation/multimedia/audio_framework/services/audio_service/client/src/renderer_in_client_public.cpp:1065
c
命中后可以看到:

1065 int32_t ret = ipcStream_->Start();
继续进入后,调用目标是 IpcStreamProxy::Start:


#0 OHOS::AudioStandard::IpcStreamProxy::Start
at gen/foundation/multimedia/audio_framework/services/audio_service/idl/ipc_stream_proxy.cpp:179
#1 OHOS::AudioStandard::RendererInClientInner::StartAudioStream
at renderer_in_client_public.cpp:1065
#2 OHOS::AudioStandard::AudioRendererPrivate::GetStartStreamResult
at audio_renderer.cpp:1025
#3 OHOS::AudioStandard::AudioRendererPrivate::Start
at audio_renderer.cpp:1255
#4 OHOS::Media::Plugins::AudioServerSinkPlugin::Start
at audio_server_sink_plugin.cpp:476
六、IpcStreamProxy::Start 发送 COMMAND_START
生成代码 IpcStreamProxy::Start() 的关键逻辑:
ErrCode IpcStreamProxy::Start()
{
MessageParcel data;
MessageParcel reply;
MessageOption option(MessageOption::TF_SYNC);
if (!data.WriteInterfaceToken(GetDescriptor())) {
return ERR_INVALID_VALUE;
}
sptr<IRemoteObject> remote = Remote();
if (!remote) {
return ERR_INVALID_DATA;
}
int32_t result = remote->SendRequest(
static_cast<uint32_t>(IIpcStreamIpcCode::COMMAND_START), data, reply, option);
...
}
这里有三个关键信息:
1. Remote() 返回远端 Binder 对象代理。
2. SendRequest 使用同步调用 TF_SYNC。
3. 请求码是 IIpcStreamIpcCode::COMMAND_START。
在 GDB 中可以直接反查 COMMAND_START 的数值:
p/d (uint32_t)OHOS::AudioStandard::IIpcStreamIpcCode::COMMAND_START
也可以在服务端拿到整数 code=4 后反推:
p (OHOS::AudioStandard::IIpcStreamIpcCode)4
输出:
$ = OHOS::AudioStandard::IIpcStreamIpcCode::COMMAND_START
所以第一段 IPC 的语义是:
IpcStreamProxy::Start
-> remote->SendRequest(4, ...)
-> IIpcStreamIpcCode::COMMAND_START
七、证明 remote 对端是 audio_server
只看到 remote->SendRequest() 还不够。客户端一定知道自己要给哪个 Binder 对象发送请求,
但这个信息不是从函数名直接看出来的,需要从 Remote() 返回的 IPCObjectProxy 里取 Binder handle,
再用 binder debugfs 反查。

在 IpcStreamProxy::Start() 中,执行到:
189 sptr<IRemoteObject> remote = Remote();
190 if (!remote) {
GDB:
p remote
示例输出:
$10 = {refs_ = 0x7f29ee75f0}
remote 是 OHOS::sptr<OHOS::IRemoteObject>,真正指针保存在 refs_:
ptype remote
p ((OHOS::IPCObjectProxy*)remote.refs_)->handle_
p ((OHOS::IPCObjectProxy*)remote.refs_)->GetInterfaceDescriptor()
实测结果:
handle_ = 15
GetInterfaceDescriptor() = u"OHOS.AudioStandard.IIpcStream"
这说明:
media_service 当前持有一个 IIpcStream 远端对象代理。
该代理在 media_service 进程中的 Binder handle 是 15。
接着在板端查 binder debugfs。
先在 media_service 的 binder proc 中找到 desc 15:
cat /sys/kernel/debug/binder/proc/$(pidof media_service) | grep "desc 15"
实测:
ref 426743: desc 15 node 426742 s 1 w 0 d 0000000000000000
这表示:
media_service 的 binder desc 15 指向 binder node 426742。
再去 audio_server 的 binder proc 中查同一个 node:
cat /sys/kernel/debug/binder/proc/$(pidof audio_server) | grep "node 426742"
实测:
node 426742: u0000007f3d356760 c0000007f3d0afc90 hs 1 hw 1 ls 0 lw 0 is 1 iw 1 tr 1 proc 578
再确认:
pidof media_service
实测:
578
这就证明:
media_service 中的 IPCObjectProxy(handle=15)
-> binder desc 15
-> binder node 426742
-> node 426742 位于 audio_server
-> 引用者 proc 578 正是 media_service
因此,IpcStreamProxy::Start() 的请求对端确实是 audio_server。
八、audio_server 侧接收 COMMAND_START
attach audio_server 后,可以对服务端入口下断点:
handle SIG38 nostop noprint pass
b OHOS::AudioStandard::IpcStreamInServer::Start
b OHOS::AudioStandard::RendererInServer::Start
命中 IpcStreamInServer::Start 后,栈如下:

#0 OHOS::AudioStandard::IpcStreamInServer::Start
at ipc_stream_in_server.cpp:195
#1 OHOS::AudioStandard::IpcStreamStub::OnRemoteRequest
(this=<optimized out>, code=4, data=..., reply=..., option=...)
at gen/foundation/multimedia/audio_framework/services/audio_service/idl/ipc_stream_stub.cpp:591
#2 OHOS::IPCObjectStub::SendRequestInner
(this=0x7fa0cb25f0, code=4, data=..., reply=..., option=...)
at ipc_object_stub.cpp:409
#3 OHOS::BinderInvoker::GeneralServiceSendRequest
#4 OHOS::BinderInvoker::TargetStubSendRequest
#5 OHOS::BinderInvoker::Transaction
在 frame 1 反查 code:
frame 1
p code
p (OHOS::AudioStandard::IIpcStreamIpcCode)code
输出:
$1 = OHOS::AudioStandard::IIpcStreamIpcCode::COMMAND_START
这和客户端 IpcStreamProxy::Start() 中的 COMMAND_START 对上。
需要注意:有时候 GDB 在 IpcStreamStub::OnRemoteRequest 里显示的当前源码行可能落在附近其他 case 的返回行,
比如 ResetStaticPlayPosition()。这通常是优化或行号映射导致的显示迷惑。判断请求类型时,应以函数参数 code 和枚举反推为准。
九、结论
链路可以整理为:
media_service / HiPlayer_*:
AudioSinkFilter::DoStart
-> AudioSink::Start
-> AudioServerSinkPlugin::Start
-> AudioRendererPrivate::Start
-> AudioRendererPrivate::GetStartStreamResult
-> RendererInClientInner::StartAudioStream
-> IpcStreamProxy::Start
-> remote->SendRequest(IIpcStreamIpcCode::COMMAND_START)
audio_server / OS_IPC_*:
IPCObjectStub::SendRequestInner(code=4)
-> IpcStreamStub::OnRemoteRequest(code=4)
-> IpcStreamInServer::Start
-> RendererInServer::Start
核心证明点:
1. GDB 栈证明播放器管线在 media_service 中进入 AudioRendererPrivate::Start。
2. StartAudioStream 的关键调用是 ipcStream_->Start。
3. 动态调用目标是 IpcStreamProxy::Start。
4. IpcStreamProxy::Start 发送 IIpcStreamIpcCode::COMMAND_START。
5. COMMAND_START 的运行时 code 是 4。
6. media_service 中 remote IPCObjectProxy 的 handle 是 15。
7. binder debugfs 证明 media_service 的 desc 15 对应 audio_server 中的 node。
8. audio_server 中 OnRemoteRequest(code=4) 进入 IpcStreamInServer::Start。
因此,可以得出结论:
播放器 Start 请求首先由 media_service 中的 AudioRenderer client 发起,
通过 IIpcStream 的 Binder IPC 发送到 audio_server,
audio_server 侧收到的请求码是 COMMAND_START。
更多推荐


所有评论(0)