ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

OpenHarmony播放音乐start请求从media_service到audio_server的调用流程02

2026/8/21 8:26:59 拓冰建站 浏览量
OpenHarmony播放音乐start请求从media_service到audio_server的调用流程02 1文章由移远通信技术股份有限公司提供2以下内容包含了个人理解仅供参考如有不合理处请联系笔者修改/删除文章目录一、背景与目标1.1 环境信息1.2 问题背景1.3 分析目标1.4 前置知识二、调试环境准备2.1 为什么Start会出现在media_service2.2 调试步骤步骤1附加到media_service进程步骤2忽略常见实时信号三、关键断点设置3.1 客户端IPC断点3.2 备用断点策略方案1在生成文件行号下断点方案2直接在源码调用行下断点3.3 服务端断点audio_server侧四、media_service中的播放器管线调用栈分析4.1 GDB调用栈捕获4.2 业务链路提取五、StartAudioStream 真正发起 ipcStream_-Start六、IpcStreamProxy::Start 发送 COMMAND_START七、证明 remote 对端是 audio_server八、audio_server 侧接收 COMMAND_START九、结论一、背景与目标1.1 环境信息操作系统OpenHarmony 6.1硬件平台RK3576内核版本Linux 6.6软件版本SDK 6.1.0.31API 231.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(code4)-IpcStreamStub::OnRemoteRequest(code4)-IpcStreamInServer::Start-RendererInServer::Start具体需要证明两个核心点media_service中确实调用了IpcStreamProxy::Start该IpcStreamProxy的Binder对端确实是audio_server1.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进程IDpidof media_service# 使用GDB附加到media_service进程gdb-multiarch-qmedia_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::Start3.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:10653.3 服务端断点audio_server侧# 附加到audio_server进程后设置 b OHOS::AudioStandard::IpcStreamInServer::Start b OHOS::AudioStandard::RendererInServer::Start四、media_service中的播放器管线调用栈分析4.1 GDB调用栈捕获当命中RendererInClientInner::StartAudioStream断点时GDB显示的调用栈如下#0OHOS::AudioStandard::RendererInClientInner::StartAudioStream at renderer_in_client_public.cpp:1057#1OHOS::AudioStandard::AudioRendererPrivate::GetStartStreamResult at audio_renderer.cpp:1025#2OHOS::AudioStandard::AudioRendererPrivate::Start at audio_renderer.cpp:1255#3OHOS::Media::Plugins::AudioServerSinkPlugin::Start at audio_server_sink_plugin.cpp:476#4OHOS::Media::AudioSink::Start at audio_sink.cpp:326#5OHOS::Media::Pipeline::AudioSinkFilter::DoStart at audio_sink_filter.cpp:160#6OHOS::Media::Pipeline::Filter::StartDone at filter.cpp:165#7OHOS::Media::Pipeline::Filter::Start()::$_2::operator()()at filter.cpp:143#14OHOS::Media::TaskInner::HandleJob at taskInner.cpp:331#15OHOS::Media::PipeLineThread::Run at pipeline_threadpool.cpp:207#18OHOS::Media::Thread::Run at thread.cpp:1614.2 业务链路提取去除标准库std::function和线程封装帧后核心业务调用链清晰可见AudioSinkFilter::DoStart-AudioSink::Start-AudioServerSinkPlugin::Start-AudioRendererPrivate::Start-AudioRendererPrivate::GetStartStreamResult-RendererInClientInner::StartAudioStream关键发现AudioRendererPrivate::Start()确实是由media_service内部的播放器管线触发的这验证了我们的第一个假设。五、StartAudioStream 真正发起 ipcStream_-StartRendererInClientInner::StartAudioStream()的关键源码// foundation/multimedia/audio_framework/services/audio_service/client/src/renderer_in_client_public.cppboolRendererInClientInner::StartAudioStream(StateChangeCmdType cmdType,AudioStreamDeviceChangeReasonExt reason){...CHECK_AND_CALL_FUNC_RETURN_RET(ipcStream_!nullptr,false,HILOG_COMM_ERROR([StartAudioStream]ipcStream is not inited!));int32_tretipcStream_-Start();CHECK_AND_CALL_FUNC_RETURN_RET(retSUCCESS,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#0OHOS::AudioStandard::IpcStreamProxy::Start at gen/foundation/multimedia/audio_framework/services/audio_service/idl/ipc_stream_proxy.cpp:179#1OHOS::AudioStandard::RendererInClientInner::StartAudioStream at renderer_in_client_public.cpp:1065#2OHOS::AudioStandard::AudioRendererPrivate::GetStartStreamResult at audio_renderer.cpp:1025#3OHOS::AudioStandard::AudioRendererPrivate::Start at audio_renderer.cpp:1255#4OHOS::Media::Plugins::AudioServerSinkPlugin::Start at audio_server_sink_plugin.cpp:476六、IpcStreamProxy::Start 发送 COMMAND_START生成代码IpcStreamProxy::Start()的关键逻辑ErrCodeIpcStreamProxy::Start(){MessageParcel data;MessageParcel reply;MessageOptionoption(MessageOption::TF_SYNC);if(!data.WriteInterfaceToken(GetDescriptor())){returnERR_INVALID_VALUE;}sptrIRemoteObjectremoteRemote();if(!remote){returnERR_INVALID_DATA;}int32_tresultremote-SendRequest(static_castuint32_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也可以在服务端拿到整数code4后反推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 sptrIRemoteObject remote Remote(); 190 if (!remote) {GDBp remote示例输出$10 {refs_ 0x7f29ee75f0}remote是OHOS::sptrOHOS::IRemoteObject真正指针保存在refs_ptype remote p ((OHOS::IPCObjectProxy*)remote.refs_)-handle_ p ((OHOS::IPCObjectProxy*)remote.refs_)-GetInterfaceDescriptor()实测结果handle_ 15 GetInterfaceDescriptor() uOHOS.AudioStandard.IIpcStream这说明media_service 当前持有一个 IIpcStream 远端对象代理。 该代理在 media_service 进程中的 Binder handle 是 15。接着在板端查 binder debugfs。先在media_service的 binder proc 中找到 desc 15cat/sys/kernel/debug/binder/proc/$(pidof media_service)|grepdesc 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 中查同一个 nodecat/sys/kernel/debug/binder/proc/$(pidof audio_server)|grepnode 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(handle15) - binder desc 15 - binder node 426742 - node 426742 位于 audio_server - 引用者 proc 578 正是 media_service因此IpcStreamProxy::Start()的请求对端确实是audio_server。八、audio_server 侧接收 COMMAND_STARTattachaudio_server后可以对服务端入口下断点handle SIG38 nostop noprint pass b OHOS::AudioStandard::IpcStreamInServer::Start b OHOS::AudioStandard::RendererInServer::Start命中IpcStreamInServer::Start后栈如下#0OHOS::AudioStandard::IpcStreamInServer::Start at ipc_stream_in_server.cpp:195#1OHOS::AudioStandard::IpcStreamStub::OnRemoteRequest(thisoptimized out,code4,data...,reply...,option...)at gen/foundation/multimedia/audio_framework/services/audio_service/idl/ipc_stream_stub.cpp:591#2OHOS::IPCObjectStub::SendRequestInner(this0x7fa0cb25f0,code4,data...,reply...,option...)at ipc_object_stub.cpp:409#3OHOS::BinderInvoker::GeneralServiceSendRequest #4OHOS::BinderInvoker::TargetStubSendRequest #5OHOS::BinderInvoker::Transaction在frame 1反查 codeframe 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(code4)-IpcStreamStub::OnRemoteRequest(code4)-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(code4) 进入 IpcStreamInServer::Start。因此可以得出结论播放器 Start 请求首先由 media_service 中的 AudioRenderer client 发起 通过 IIpcStream 的 Binder IPC 发送到 audio_server audio_server 侧收到的请求码是 COMMAND_START。