UPnP媒體渲染基礎知識

  • 一、UPnP 媒體渲染器的基本架構
  • 二、在 upmpdcli 源碼中的對應實現
  • 三、三者協作流程示例
  • 四、在 upmpdcli 中的依賴關係
  • 五、擴展提示

一、UPnP 媒體渲染器的基本架構

根據 UPnP AV Architecture v1/v2 規範,一個標準的 MediaRenderer 必須實現以下三個基礎服務:

服務

UPnP Service Type

主要功能

AVTransport

urn:schemas-upnp-org:service:AVTransport:1

控制播放狀態(播放/暫停/停止)、跳轉、獲取元數據等

ConnectionManager

urn:schemas-upnp-org:service:ConnectionManager:1

管理輸入/輸出協議、格式兼容性、連接建立

RenderingControl

urn:schemas-upnp-org:service:RenderingControl:1

控制音量、靜音、亮度(音頻為主)等渲染參數

在upmpdcli 源碼中 AVTransport、ConnectionManager 和 RenderingControl 這三個服務之間的關係。這三個服務都是 UPnP AV 架構(Universal Plug and Play Audio/Video Architecture)定義的標準服務,它們共同協作,為 UPnP 媒體渲染器(Media Renderer)提供完整的功能。upmpdcli 作為一個將 MPD(Music Player Daemon)包裝成 UPnP 媒體渲染器的網關,其核心就是實現這三個服務,並將 UPnP 控制命令翻譯成 MPD 的控制指令。

可以這樣理解它們之間的關係:

  • ConnectionManager 是 “門衞”“交通警察”,負責管理和建立與其他 UPnP 設備(如媒體服務器)的連接。
  • AVTransport 是 “播放指揮官”,負責控制媒體的傳輸和播放狀態(播放、暫停、停止、快進、快退等)。
  • RenderingControl 是 “效果調音師”,負責控制渲染設備的音頻和視頻參數(音量、靜音、亮度、對比度等)。

二、在 upmpdcli 源碼中的對應實現

upmpdcli 基於 libupnpp(作者自研的 C++ UPnP 庫)實現這些服務。關鍵源碼位置:

src/
├── avtransport.cxx      → AVTransport 服務
├── connectionmanager.cxx → ConnectionManager 服務
├── renderingcontrol.cxx → RenderingControl 服務
└── upmpdcli.cxx         → 主入口,註冊這三個服務
  1. AVTransport (avtransport.cxx)
  • 簡述
  • 核心職責:
  • 實現 Play, Pause, Stop, Next, Previous, Seek
  • 維護當前播放狀態(TransportState)
  • 獲取當前媒體信息(CurrentTrackMetaData)
  • 與 MPD 交互:
  • 通過 MPDInterface 類調用 MPD 命令(如 mpc play)
  • 將 MPD 的播放狀態映射為 UPnP 狀態(如 PLAYING ↔ playing)
  • AVTransport 服務作用:AVTransport (AV 傳輸) 是媒體渲染器的 “大腦”,負責所有與媒體播放相關的控制邏輯。
  1. 加載媒體:接收要播放的媒體資源的 URL。
  2. 控制播放狀態:提供播放、暫停、停止、上一曲、下一曲、快進、快退等控制。
  3. 報告狀態:通過 LastChange 事件變量,實時向控制點報告當前的播放狀態(TransportState,如 PLAYING, PAUSED_PLAYBACK, STOPPED)、播放位置(RelativeTimePosition)、媒體信息(CurrentTrackURI, CurrentTrackMetaData)等。
  4. 處理播放列表:管理一個簡單的播放隊列(CurrentURIMetaData 和 NextURIMetaData)。
  • 在 upmpdcli 中的實現:
    對應的源碼文件可能是 avtransport.cxx 或 avt.cxx。
  • 它實現 AVTransport 服務的標準動作,主要有:
  • SetAVTransportURI:控制點調用此方法來設置要播放的媒體文件的 URI 和元數據。AVTransport 模塊會解析這個 URI。
  • GetMediaInfo / GetTransportInfo / GetPositionInfo:這些方法用於查詢當前的媒體信息、傳輸狀態和播放位置。
  • Play, Pause, Stop, Seek:這些是控制播放的核心動作。
  • 關鍵交互點:這是 upmpdcli 中業務邏輯最密集的部分,它直接與 MPD 進行交互。
  • 當 SetAVTransportURI 被調用時,AVTransport 模塊會:
  1. 解析傳入的 URI。這個 URI 通常是一個指向媒體服務器上某個文件的 HTTP URL。
  2. 它會檢查 ConnectionManager 是否已經為這個 URI 建立了有效的連接(通過 PrepareForConnection)。雖然 AVTransport 不一定會直接調用 ConnectionManager 的方法,但它依賴於 ConnectionManager 已經完成的 “合法性檢查”。
  3. 然後,它會使用 MPD 的客户端庫(如 libmpdclient)向 MPD 發送 add 或 load 命令,將這個 URL 添加到 MPD 的播放列表中,並可能執行 play 命令。
  • 當 Play, Pause 等動作被調用時,AVTransport 模塊會直接將對應的 MPD 命令(play, pause, stop)發送給 MPD。
  • AVTransport 模塊會定期(或通過 MPD 的 idle 機制)查詢 MPD 的狀態,然後將這些狀態信息(如當前播放時間、總時長、播放狀態)轉換為 UPnP AVTransport 服務的標準狀態變量(State Variables),並在狀態變化時通過 LastChange 事件通知給所有訂閲的控制點。
  1. RenderingControl (renderingcontrol.cxx)
  • 簡述
  • 核心職責:
  • 實現 SetVolume, GetVolume, SetMute, GetMute
  • 支持多通道(如 Left/Right)
  • 與 MPD 交互:
  • 調用 MPD 的 setvol 和 volume 命令
  • 注意:MPD 本身不支持“靜音”,upmpdcli 通常用 臨時設音量為 0 模擬靜音
    ⚠️ 如果 MPD 後端不支持音量控制(如某些 ALSA 配置),此服務可能無效。
  • RenderingControl 服務作用:RenderingControl (渲染控制) 負責控制渲染設備的音頻和視頻輸出特性。
  1. 音量控制:設置和獲取音量級別。
  2. 靜音控制:設置和獲取靜音狀態。
  3. 低音、高音、平衡:(如果支持)控制音頻均衡器參數。
  4. 視頻控制:(如果支持)控制亮度、對比度、飽和度等。
  • 在 upmpdcli 中的實現:對應的源碼文件可能是 renderingcontrol.cxx 或 rctrl.cxx。
  • 它實現 RenderingControl 服務的標準動作,主要有:
  • GetVolume / SetVolume
  • GetMute / SetMute
  • GetVolumeDBRange:獲取音量的分貝範圍。
  • 關鍵交互點:RenderingControl 模塊也直接與 MPD 交互。
  • 當控制點調用 SetVolume 時,RenderingControl 模塊會將音量值(通常是 0-100 的整數)轉換為 MPD 可以理解的格式(可能也是 0-100),然後通過 libmpdclient 發送 setvol 命令給 MPD。
  • 當調用 SetMute 時,它會發送 mute 命令。
  • 與 AVTransport 類似,它也會監聽 MPD 的狀態變化(特別是音量和靜音狀態的變化),並更新自己的服務狀態變量,通過 LastChange 事件通知控制點。
  1. ConnectionManager (connectionmanager.cxx)
  • 簡述
  • 核心職責:
  • 聲明設備支持的傳輸協議(如 http-get::audio/mpeg:
  • 聲明支持的 容格式(通過 DIDL-Lite 元數據協商)
  • 實現 PrepareForConnection(較少使用,主要用於 Push 模式)
  • 關鍵方法:
  • GetProtocolInfo():返回字符串如
    http-get::audio/mpeg:,http-get::audio/flac:,…
  • ConnectionManager (連接管理器) 的主要職責:
  • 描述自身能力:向網絡上的其他設備宣告它支持哪些媒體格式(如 audio/mpeg, video/mp4)和傳輸協議(如 UPnP功能使用詳解 -------- 轉 - 大龍的博客_靜音)。
  • 管理連接:處理來自控制點(Control Point,如手機上的音樂 App)的連接請求,為 AVTransport 服務建立一個 “管道”,使得媒體數據流可以從媒體服務器流向渲染器。
  • 處理協議信息:在建立連接時,它會交換 ProtocolInfo,確保源設備(服務器)和目標設備(渲染器)對將要傳輸的數據格式達成一致。
  • 在 upmpdcli 中的實現:
    在 upmpdcli 的源碼中,你會找到一個類似 connectionmanager.cxx 或 cmgr.cxx 的文件。
  • 它會實現 ConnectionManager 服務的標準動作(Actions),主要是:
  • GetProtocolInfo:返回 upmpdcli 支持的所有輸入(Source)和輸出(Sink)協議信息。對於一個純渲染器,它只有 Sink 信息。
  • PrepareForConnection:這是最關鍵的動作。當控制點想要播放一個媒體文件時,它會先調用這個方法。ConnectionManager 會驗證請求的 ProtocolInfo 是否被支持,如果支持,它會創建一個 “連接 ID”(Connection ID),並記錄下數據源的信息(如媒體服務器的 URL)。
  • ConnectionComplete:當播放結束或取消時,控制點會調用此方法來關閉連接。
  • 關鍵交互點:ConnectionManager 並不直接與 MPD 交互。它的主要工作是在 UPnP 層面 “牽線搭橋”。它驗證並記錄下來自控制點的播放意圖。一旦 PrepareForConnection 成功,它就 “授權” 了 AVTransport 去執行後續的播放操作。

三、三者協作流程示例

  • 假設用户在手機 App 上點擊播放一首 MP3:
  • 控制點查詢能力
    → 調用 ConnectionManager.GetProtocolInfo()
    → 確認 http-get::audio/mpeg: 被支持
  • 推送媒體 URI
    → 調用 AVTransport.SetAVTransportURI(URI=“http://…/song.mp3”, MetaData=…)
  • 開始播放
    → 調用 AVTransport.Play()
  • 調整音量
    → 調用 RenderingControl.SetVolume(50)
  • 內部流轉
  • AVTransport → 通知 MPD 添加並播放該 URI
  • RenderingControl → 通知 MPD 設置音量為 50
  • ConnectionManager 不參與運行時控制,僅用於前期協商
  • 總結:三者如何協同工作?
    讓我們以 “用户通過手機 App(控制點)在 upmpdcli 上播放一首歌曲” 為例,來看它們是如何配合的:
  1. 發現階段:手機 App 在網絡上搜索 UPnP 設備,發現了 upmpdcli 提供的媒體渲染器設備。設備描述文件(device.xml)中聲明瞭它包含 ConnectionManager、AVTransport 和 RenderingControl 這三個服務。
  2. 準備連接 (ConnectionManager):
  • App(控制點)決定播放媒體服務器上的一首歌曲。它先調用 upmpdcli 的 ConnectionManager::PrepareForConnection 方法。
  • ConnectionManager 檢查 App 提供的 ProtocolInfo(例如 http-get::audio/mpeg:)是否在自己支持的範圍內。
  • 如果支持,ConnectionManager 返回一個 ConnectionID 和 AVTransportID(通常為 0),表示連接已準備好。
  1. 設置播放內容 (AVTransport):
  • App 接着調用 AVTransport::SetAVTransportURI 方法,傳入要播放的歌曲的 HTTP URL、元數據以及上一步得到的 ConnectionID。
  • AVTransport 模塊接收到命令。它可能會驗證一下 ConnectionID 是否有效(與 ConnectionManager 交互或查詢其狀態)。
  • AVTransport 然後使用 libmpdclient 向 MPD 發送 clear(清空列表)和 add “http://…”(添加歌曲 URL)命令。
  1. 開始播放 (AVTransport):
  • App 調用 AVTransport::Play 方法。
  • AVTransport 模塊向 MPD 發送 play 命令。
  • MPD 開始從指定的 URL 流獲取音頻數據並播放。
  • AVTransport 模塊通過輪詢或監聽 MPD 的事件,發現 MPD 的狀態變為 “播放中”,於是它更新自己的 TransportState 變量為 PLAYING,並通過 LastChange 事件通知給 App。
  1. 調節音量 (RenderingControl):
  • 用户覺得聲音太大,在 App 上調低音量。
  • App 調用 RenderingControl::SetVolume 方法,傳入新的音量值(例如 50)。
  • RenderingControl 模塊向 MPD 發送 setvol 50 命令。
  • MPD 的音量被調整。
  • RenderingControl 模塊檢測到 MPD 音量變化,更新自己的 Volume 變量,並通過 LastChange 事件通知 App。
  1. 停止播放 (AVTransport):
  • 用户在 App 上點擊停止。
  • App 調用 AVTransport::Stop 方法。
  • AVTransport 模塊向 MPD 發送 stop 命令。
  • AVTransport 更新狀態為 STOPPED 並通知 App。
  1. 結束連接 (ConnectionManager):
  • (可選)App 可能會調用 ConnectionManager::ConnectionComplete 來關閉之前建立的連接。

總結:

  • ConnectionManager 負責 “握手” 和 “協議協商”,確保數據通道是通的並且雙方都能理解。
  • AVTransport 負責 “內容管理” 和 “播放控制”,是執行播放邏輯、與 MPD 交互最頻繁的模塊。它依賴 ConnectionManager 完成前期準備。
  • RenderingControl 負責 “輸出效果控制”,獨立於媒體內容,只關心如何調整最終的音頻 / 視頻輸出。
    這三個服務各司其職,通過 UPnP 標準的動作調用和事件通知機制鬆散耦合地協同工作,共同構成了一個功能完整、符合標準的 UPnP 媒體渲染器。upmpdcli 的核心價值就在於巧妙地將這三個 UPnP 服務的抽象接口,與 MPD 這個具體的、強大的媒體播放後端的功能對應了起來。

四、在 upmpdcli 中的依賴關係

UPnP功能使用詳解 -------- 轉 - 大龍的博客_#媒體_02

  • AVTransport 和 RenderingControl 都依賴 MPDInterface 與 MPD 通信。
  • ConnectionManager 的協議列表由 upmpdcli.conf 中的 protocolinfo 或 script backend 決定。
  • 三者無直接調用關係,但共享同一個設備上下文(如當前播放 URI)。

五、擴展提示

  • 若你想添加 私有協議支持(如 myproto://xxx):
  • 在 ConnectionManager 中註冊新協議(修改 getProtocolInfo() 返回值)
  • 在 AVTransport 的 SetAVTransportURI 中處理該 URI(通常交給 script backend 解析)
  • 確保解析後的 URL 是 MPD 支持的格式(如 HTTP/HTTPS)
  • 源碼關鍵函數:
  • AVTransport::setAVTransportURI()
  • ConnectionManager::getProtocolInfo()
  • RenderingControl::setVolume()