DLNA ( Digital Living Network Alliance ) 詳解


目錄
  1. 大前言
  2. DLNA 背景(來自某百科)
    1. 簡介
    2. DLNA 的常見設備類型
    3. DLNA Guide 版本
    4. 通用名詞解釋
    5. DLNA 交互文檔中自定義的名詞解釋
    6. DLNA 主要用到的技術
  3. UPnP 設備規范( UDA 1.0)
    1. 前言
    2. 簡介
      1. What is UPnP™1 Technology?
      2. UPnP™ 論壇組
      3. 關於本文檔
      4. Audience
      5. Required vs. recommended
      6. Acronyms
      7. References and resources
    3. 0. Addressing
      1. 0.1 Addressing: Determining whether to use Auto-IP
      2. 0.2 Addressing: Choosing an address
      3. 0.3 Addressing: Testing the address
      4. 0.4 Addressing: Periodic checking for dynamic address availability
      5. 0.5 Addressing: Device naming and DNS interaction
      6. 0.6 Addressing: Name to IP address resolution
      7. 0.7 Addressing references
    4. 1. Discovery
      1. 1.1 Discovery: Advertisement
      2. 1.2 Discovery: Search
      3. 1.3 Discovery references
    5. 2. Description
      1. 2.1 Description: Device description
      2. 2.2 Description: UPnP Device Template
      3. 2.3 Description: Service description
      4. 2.4 Description: UPnP Service Template
      5. 2.5 Description: Non-standard vendor extensions
      6. 2.6 Description: UPnP Template Language for devices
      7. 2.7 Description: UPnP Template Language for services
      8. 2.8 Description: Retrieving a description
      9. 2.9 Description references>
    6. 3. Control
      1. 3.1 Control: Protocols
      2. 3.2 Control: Action
      3. 3.3 Control: Query for variable
      4. 3.4 Control references
    7. 4. Eventing
      1. 4.1 Eventing: Subscription
      2. 4.1.1 Eventing: Subscribing: SUBSCRIBE with NT and CALLBACK
      3. 4.2 Eventing: Event messages
      4. 4.3 Eventing: UPnP Template Language for eventing
      5. 4.4 Eventing: Augmenting the UPnP Template Language
      6. 4.5 Eventing references
    8. 5. Presentation
      1. 5.1 Presentation references
  4. UPnP device protection
    1. 名詞解釋
    2. 動機
    3. 該協議所提供的能力
    4. 該協議不提供的能力
    5. 注意
  5. DLNA 實戰前熱身(DLNA 抓包)
    1. 說明
    2. Discovery
    3. Description
    4. Control
    5. Event
    6. Presentation
    7. 說明
  6. DLNA 協議
    1. DLNA 交互設計架構
    2. DLNA 設備模型
    3. 關於 DLNA 涉及的四大服務
    4. DLNA AVTransport 服務狀態轉移流程
    5. DLNA AVTransport 事件模型
    6. DLNA AVTransport 狀態變量以及其作用和值域
    7. DLNA AVTransport 定義的 Action
    8. CSV ( Comma Separated Value ) List
    9. 參考文檔
  7. DLNA V4.0 RemoteUserInterface HTML5(基於 HTML5 的遠程用戶界面 )
    1. 簡介
    2. 名詞解釋
    3. HTML5 RUI 設備功能
    4. HTML5 RUI 設備能力
    5. 模型介紹
    6. RUI-H Server 與 Client
      1. RUI-H Server
      2. RUI-H Client
      3. DeviceProfile 的示例
      4. CompatibleUIs 的示例
    7. 一些交互指南
    8. 參考文檔
  8. Platinum 解析
  9. 國內各個投屏軟件缺陷公示以及分析 - 不完全指南
    1. 設備發現體驗
    2. 愛奇異
    3. netease-cloud-music
    4. bilibili
  10. 用 Node.js 實現 DLNA 測試工具
  11. 參考資料
  12. 最后的最后
大前言

此文首先會先介紹 DLNA 的基礎背景知識,然后着重介紹 UDA 1.0,完整的漢化版 UDA 1.0 協議,有需要的朋友可以參考一下,雖然不保證准確,但自己是要運用到工作開發中的,也不能對自己馬虎。 接着我會總體介紹一下 DLNA 規范的結構,內容。針對目前國內做得比較好的一款應用對其進行抓包分析。 之后會選取現在的一個開源 Platinum 對其進行分析。 當然最后面還有我自己在開發過程中遇到的國內一些應用的缺陷共識(大家就當八卦看看吧)。

其實我也不太贊同完全將 Refrence 一股腦而看到低,一者我認為對於普通開發人員來說沒必要,其實很多東西都是共通的,所以,越看到后面,越會發現,其實 Refrence 都是按照一個思路來寫的。 其次 DLNA 其實也不屬於一個熱門技術,用到的技術原理也不深,現在雖然有標准,但各家出的產品也與標准有差異,甚至在某些應用場景標准還會要妥協,所以適可而止即可。

另外你可能在文章中會多次發現有如下字眼 "發現消息", "廣播消息" 很多時候他們是名詞,而不是一個動作,請結合上下文理解。

資料包 中包含本文用到的幾乎所有 UPnP 方面資料,而且在持續收集中,請酌情下載。

下文中還有一些非官方的簡寫,也就是我自己為了方便描述文檔自定義的,如果在其他地方不能找到該簡寫也不要在意,如果你找到了,大概是純屬巧合。此外對於我自己定義的簡寫我會在名詞解釋部分對其釋義進行解釋。

DLNA 背景(來自某百科)
簡介

數字生活網絡聯盟(英語:Digital Living Network Alliance,簡稱:DLNA, 原名叫 Digital Home Working Group, DHWG)是一個由一群消費性電子公司在 2003 6 月份成立,由 intel 牽頭、 開發和推廣一套在認證標准主持下的多媒體設備中的數字媒體共享的互操作性的准則。 DLNA 認證的設備包括智能手機,平板電腦,PC,電視機和存儲服務器。

該小組於 2004 年 6 月發布了第一套准則。該准則包含了幾種現有的公共標准, 包括用於媒體管理以及設備發現和控制的通用即插即用(UPnP),以及廣泛使用的數字媒體格式以及有線和無線網絡標准。

DLNA 與電纜,衛星​​和電信服務提供商合作,在數據傳輸的每一端提供鏈路保護。 數字版權管理(DRM)安全性的額外層使廣播運營商能夠使消費者在多媒體設備上共享其內容,而不會遭受盜版風險。 DLNA 在 2014 年 3 月公開發布了 VidiPath 指南,最初稱為 “ DLNA CVP-2 指南”。 VidiPath 使消費者能夠在各種設備上觀看訂閱的電視內容,包括電視,平板電腦,電話,藍光播放器,機頂盒(STB),個人計算機(PC)和游戲機,而服務提供商無需任何其他中間設備。

截至 2014 年 9月,超過 25,000 種不同的設備型號獲得了 “DLNA認證” 狀態,其包裝上帶有徽標並確認了它們與其他設備的互操作性。 據估計,到2017年,將在用戶家中安裝超過 60 億種 DLNA 認證的設備,從數碼相機到游戲機和電視。 截至 2015 年 6 月,該組織聲稱擁有 “200多家公司” 的會員資格。DLNA 在 2017 年 1 月 5 日在其網站上宣布: “該組織已履行其使命,並將解散為一個非營利性貿易協會”。

其認證計划將由俄勒岡州波特蘭市的SpireSpark International進行。

DLNA 的常見設備類型
  • DMS 存儲內容並將其提供給聯網的數字媒體播放器(DMP)和數字媒體渲染器(DMR)。示例包括PC和網絡附加存儲(NAS)設備。
  • DMP 在數字媒體服務器(DMS)上查找內容,並提供播放和渲染功能。示例包括電視,立體聲音響和家庭影院,無線監視器和游戲機。
  • DMR 播放數字媒體控制器(DMC)指示的內容,數字媒體控制器將從數字媒體服務器(DMS)查找內容。 示例包括電視,音頻/視頻接收器,視頻顯示器和音樂的遠程揚聲器。 單個設備(例如電視,A/V 接收器等)有可能同時充當 DMR(從 DMS 接收“推送”內容)和 DMP(從 DMS 提取“內容”)。
  • DMC 在數字媒體服務器(DMS)上查找內容,並指示數字媒體渲染器(DMR)播放內容。內容不會從 DMC 流過。 例如平板電腦,支持Wi-Fi的數碼相機和智能手機。
  • DMPr 通常,具有打印功能的數字媒體播放器(DMP)和數字媒體控制器(DMC)可以打印到 DMPr。 示例包括聯網的照片打印機和聯網的多合一打印機。
  • M-DMS 存儲內容並將其提供給有線/無線聯網的移動數字媒體播放器(M-DMP)和數字媒體渲染器。 示例包括手機和便攜式音樂播放器。
  • M-DMP 在數字媒體服務器(DMS)或移動數字媒體服務器(M-DMS)上查找和播放內容。 示例包括設計用於查看多媒體內容的移動電話和移動媒體平板電腦。
  • M-DMU 將內容發送(上傳)到數字媒體服務器(DMS)或移動數字媒體服務器(M-DMS)。 示例包括數碼相機和移動電話。
  • M-DMD 查找和存儲(下載)來自數字媒體服務器(DMS)或移動數字媒體服務器(M-DMS)的內容。 示例包括便攜式音樂播放器和移動電話。
  • M-DMC 在數字媒體服務器(DMS)或移動數字媒體服務器(M-DMS)上查找內容,並將其發送到數字媒體渲染器(DMR)。 示例包括個人數字助理(PDA)和移動電話。
  • M-NCF 在移動手持設備網絡連接和家庭網絡連接之間建立橋梁。
  • MIU 提供家庭網絡和移動手持設備所需的媒體格式之間的內容轉換。
DLNA Guide 版本
  • 1.5 2004/6 頒布,包含 DLNA 的體系結構和協議,媒體格式; 2 種設備類別( DMP,DMS);大約50種媒體格式配置文件。
  • 1.5 2006/3 頒布,包含 DLNA 的架構和協議,媒體格式和連接安全; 12 種設備類別和 5 種設備功能;約 250 種媒體格式配置文件
  • 2.0 2015/8 頒布,包含包括諸如 EPG,內容同步,RUI,WPS,媒體格式,預定錄制,DRM之類的主題
  • 3.0 2015/8 頒布,延長響應時間,提高電源效率,支持HEVC
  • 4.0 2016/6 頒布,解決了PC,電視和移動設備之間的“不支持媒體格式”問題,同時支持超高清電視內容流
通用名詞解釋

注意,以下內容並不是嚴格按照 DLNA 協議規范中的文字翻譯所得,有些來源於標准文檔和各個百科。 意圖詳盡的在一句話內給讀者一個大概映像,但具體的細節還是建議讀者追本溯源。

  • AC 3( Audio Coding 3 ) AC3( 全稱 Audio Coding3 音頻編碼 3 )是杜比數碼的同義詞,杜比數碼是一種高級音頻壓縮技術, 它最多可以對6個比特率最高為448kbps的單獨聲道進行編碼。杜比 AC-3 提供的環繞聲系統由 5 個全頻域聲道和 1個 超低音聲道組成,被稱為 5.1 聲道。 5 個聲道包括左前、中央、右前、左后、右后。低音聲道主要提供一些額外的低音信息,使一些場景,如爆炸、撞擊等聲音效果更好。
  • ADU ( Application Data Unit ) 被作為 RTP 傳輸的單元,對於每個媒體流,ADU的定義都不同。 對於音頻媒體流,ADU 通常是音頻幀。 對於視頻媒體流,ADU 通常是“切片”(例如,NAL單元), 或者在某些情況下是完整的視頻圖片。同樣作為特殊情況,當使用 MPEG-2 TS 封裝時, 每個 TS 數據包都是一個 ADU。
  • ACK ( Acknowledge ) 通常用於描述一個網絡數據包被成功接收,而響應給用戶的消息。
  • AP ( Access Point ) 無線局域網(WLAN)上經過特殊配置的網絡基礎結構設備。 接入點充當WLAN無線電信號的中央發送器和接收器。 家庭網絡中使用的AP通常是帶有內置網絡適配器,天線和無線電發射器的小型專用硬件設備。 這些AP支持Wi-Fi無線通信標准。
  • ARP ( Address Resolution Protocol ) TCP/IP 家族的一個協議,用於通過 IP 地址解析硬件地址, 如以太網地址( mac 地址)。
  • ATSC (Advanced Television Systems Committee) 高級電視系統委員會,數字電視廣播的標准機構之一。
  • AV ( Audio with Video ) 泛指任何同時攜帶聲音和圖片的媒體內容。
  • AVP ( Audio/Video Profile ) 音頻和視頻的 RTP 配置文件,用於 RTP 媒體內容傳輸。
  • AVPF ( Extended Audio/Visual Profile for RTCP-based Feedback ) 基於 RTCP 反饋的一套可擴展音視頻配置文件,用於 RTP 流媒體傳輸。
  • AVT ( AVTransport:1 Service ) AVTransport 服務是 UPnP 服務,它為常見的傳輸操作(如播放,停止,暫停,下一個,上一個和搜索)提供基於網絡的控制。 AVTransport 服務規范是標准的 UPnP DCP。
  • BD_ADDR ( Bluetooth Device Address ) 藍牙設備地址,由 48 bit ( 6 個字節 )標識的藍牙硬件地址。地址的分配由藍牙協議委員會定義。
  • PAN ( Personal Area Network ) 藍牙個人局域網,藍牙 Profile 中的一個協議。
  • BNEP ( Bluetooth Network Encapsulation Protocol ) 藍牙封包協議,藍牙 PAN 配置文件中的概念。
  • L2CAP ( Logical Link Control and Adaptation Protocol ) 藍牙鏈路層的一個協議
  • BT 藍牙的英文簡稱
  • BK ( Background User Priority ) QoS 等級標志, 標識 Background User 的優先級。
  • BE ( Best-Effort User Priority ) QoS 等級標志, 標識 BestEffort User 的優先級。
  • CDB ( Coded Data Buffer ) 用於 RTP Media 傳輸,在解碼之前用於存儲編碼數據的緩沖區空間。
  • DCP ( Device Control Protocol ) 設備控制協議, 是一套 UPnP 論壇標准化的規范。 UPnP 工作委員會制定的相關規范通常由工作委員會的名稱來標識。 例如,UPnP AV 1 DCP
  • CDS ( ContentDirectory:1 Service ) ContentDirectory 是 UPnP 的一個服務,它提供基於網絡的發現的內容, ContentDirectory 服務規范是標准的 UPnP DCP.
  • CE ( Consumer Electronics ) 泛指放置在家中的一類設備,如 DVD, DVR, PVR, PDA, TV, 機頂盒,電話機,手機。
  • CMS ( ConnectionManager:1 Service ) ConnectionManager 是 UPnP 的一個服務,它提供傳輸協議和媒體格式方面的內容, ConnectionManager 服務規范是標准的 UPnP DCP.
  • CNAME ( Canonical Name ) RTP 媒體傳輸使用的,
  • CP ( UPnP Control Point ) UPnP 控制點。 詳見 UPnP UDA V1.0 文檔。
  • DA ( Device Architecture 1.0 ) 詳見 UPnP UDA V1.0 詳見文檔。
  • SSRC ( Synchronization Source ) RTP 通信中的全球唯一標識符,其由 32bit 的隨機數組成, 主要是用於區分哪些 RTP 包是屬於同一會話的。
  • CSRC ( Contributing Source ) RTP 中的概念, CSRC 會由 Mixer 添加到 RTP 封包中,用於區分生產者(請參閱下文)。 Mixer 將一個帶 SSRC 的標識符列表(此列表稱為CSRC列表)插入到流媒體源中,用於生成在 RTP 頭中帶特殊字段的封包。 一個示例應用程序是音頻會議,其中 Mixer 將誰在講話的信息插入到 Outgoing 的封包中, 即使所有音頻數據包包含相同的 SSRC 標識符(混合器的標識符),接收器也可以指示當前講話者。
  • DDC ( Device Discovery and Control ) 《Interoperability Guidelines》中的一節定義了發現和控制設備的基礎互操作性體系結構。
  • DH 1-5 ( Data - High Rates 1 through 5 ) 藍牙 Spec 中物理層的高頻通信封包類型。
  • DM 1-5 ( Data - Medium Rates 1 through 5 ) 藍牙 Spec 中物理層的中頻通信封包類型。
  • DHCP ( Dynamic Host Configuration Protocol ) 一個在網絡中幫助網絡節點,自動分配 IP 地址以及相關信息的協議。
  • DIDL ( Digital Item Declaration Language ) 一個 XML 規范文檔,用於描述數字內容(文檔)的元數據(metadata)的格式。
  • DIDL-Lite ( Digital Item Declaration Language - Lite ) 一個 XML 規范文檔,用於在 UPnP 傳輸中描述數字內容(文檔)的元數據(metadata)的格式。 是 DIDL 的子集和 UPnP 自定義的一些屬性集合的並集,
  • DLNA ( Digital Living Network Alliance ) DLNA 協議,由 DLNA 協議委員會制定的協議。
  • DLNAQOS_UP ( DLNA QoS User Priority ) 一個又 DLNA 委員會定義的一套 QoS 標准,用於將基礎的 802.1Q 用戶優先級和 WMM 訪問類別與 DLNA 通信類型相關聯。
  • DMC ( Digital Media Controller ) DLNA 定義的一套用於家庭網絡環境的設備, 被用於發現環境中的 DMS 和 DMR, 並讓 DMS 和 DMR 建立連接。
  • DMP ( Digital Media Player ) DLNA 定義的一套用於家庭網絡環境的設備, 用於連接暴露在網絡環境中的 DMS 並展示以上內容。
  • DMPr ( Digital Media Printer ) DLNA 定義的一套用於家庭網絡環境的設備, 可通過 DLNA 協議給環境中提供文檔或者圖片打印服務。
  • DMR ( Digital Media Renderer ) DLNA 定義的一套用於家庭網絡環境的設備, 用於連接和展示由 DMC 配置內容。
  • DMS ( Digital Media Server ) DLNA 定義的一套用於家庭網絡環境的設備, 用於向家庭的網絡設備提供內容分發的服務。
  • DNS ( Domain Name System ) 是互聯網的一項服務。它定義了一個協議,可用於將互聯網中的域名和 IP 地址互相解析。
  • DSCP ( Differentiated Services (DiffServ) Code Point ) 差分服務代碼點(Differentiated Services Code Point), IETF 於 1998 年 12 月發布了 Diff-Serv(Differentiated Service)的 QoS 分類標准。 它在每個數據包 IP 頭部的服務類別 TOS 標識字節中,利用已使用的 6 比特和未使用的 2 比特,通過編碼值來區分優先級。
  • DVB ( Digital Video Broadcasting ) 數字視頻廣播是於 1993 年建立起來的一種面向市場的數字服務體系結構,旨在推廣基於 MPEG-2 編碼國際標准的電視服務。 全世界已有 25 個國家超過 200 個組織加入到 DVB 項目中。數字視頻廣播是一種數字電視傳輸技術標准的總稱。
  • VCD ( Video Compact Disc ) 是一種在光碟(Compact Disk)上存儲視頻信息的標准。 VCD 可以在個人電腦或 VCD 播放器以及大部分 DVD 播放器中播放。 VCD 標准由索尼、飛利浦、JVC、松下等電器生產廠商聯合於1993年 [1] 制定,屬於數字光盤的白皮書標准。
  • DVD ( Digital Versatile Disc ) 高密度數字視頻光盤。它是比 VCD 更新一代的產品。 DVD 分別采用 MPEG—2 技術和 AC—3 標准對視頻和音頻信號進行壓縮編碼。 它可以記錄 135 分鍾的圖像畫面。與 VCD 不同的是它的圖像清晰度可達 720 線。
  • DVR ( Digital Video Recorder ) 數字視頻錄像機,它是一套進行圖像存儲處理的計算機系統,具有對圖像/語音進行長時間錄像、錄音、遠程監視和控制的功能。 DVR 采用的是數字記錄技術,在圖像處理、圖像儲存、檢索、備份、以及網絡傳遞、
  • ES ( Elementary Stream ) Elementary Stream(基本碼流)-由壓縮器輸出的用於傳送 單路視音頻信號的原始碼流。
  • HD ( High Definition ) 是指垂直分辨率大於等於 720 的圖像或視頻,也稱為高清圖像或高清視頻, 尺寸一般是 1280×720 和 1920×1080。“高清”的全稱為“高清晰度”。
  • HDTV ( High Definition Television ) “高清晰度電視”,源於DTV(Digital Television)“數字電視”技術,采用數字信號,擁有最佳的視頻、音頻效果。 HDTV有三種顯示格式,分別是:720P(1280×720,非交錯式,場頻為24、30或60), 1080i(1920×1080,交錯式,場頻60), 1080P(1920×1080,非交錯式,場頻為24或30)。
  • HID ( Home Infrastructure Device ) 家庭基礎設施設備,指家中支持 DLNA 設備的一組設備, 她們實現了不同設備之間的交互, 這里的這一類設備統稱 M-NCF 和 MIU。
  • HND ( Home Network Device ) 家庭網絡設備,家庭網絡環境中所有支持 DLNA 的設備的統稱, 這里的設備包括: DMS, DMP, DMR, DMC, and DMPr.
  • HTTP ( Hyper Text Transfer Protocol ) 超文本傳輸協議,一個通過 Internet 傳輸文件的協議,由 HTTP 客戶端和 HTTP 服務端構成。
  • ICMP ( Internet Control Message Protocol ) Internet 控制報文協議。它是 TCP/IP 協議簇的一個子協議,用於在 IP 主機、路由器之間傳遞控制消息。 控制消息是指網絡通不通、主機是否可達、 路由是否可用等網絡本身的消息。這些控制消息雖然並不傳輸用戶數據,但是對於用戶數據的傳遞起着重要的作用。
  • IP ( Internet Protocol ) 是 TCP/IP 體系中的網絡層協議。設計 IP 的目的是提高網絡的可擴展性: 一是解決互聯網問題,實現大規模、異構網絡的互聯互通; 二是分割頂層網絡應用和底層網絡技術之間的耦合關系,以利於兩者的獨立發展。 根據端到端的設計原則,IP 只為主機提供一種無連接、不可靠的、盡力而為的數據報傳輸服務。
  • IPV4 ( Internet Protocol version 4 ) 網際協議版本 4,又稱互聯網通信協議第四版,是網際協議開發過程中的第四個修訂版本, 也是此協議第一個被廣泛部署的版本。IPv4 是互聯網的核心,也是使用最廣泛的網際協議版本, 其后繼版本為 IPv6,直到 2011 年,IANA IPv4 位址完全用盡時,IPv6 仍處在部署的初期。
  • TCP ( Transmission Control Protocol ) 傳輸控制協議(TCP,Transmission Control Protocol)是一種面向連接的、可靠的、基於字節流的傳輸層通信協議, 由 IETF 的 RFC 793 定義。 TCP 旨在適應支持多網絡應用的分層協議層次結構。 連接到不同但互連的計算機通信網絡的主計算機中的成對進程之間依靠TCP提供可靠的通信服務。 TCP 假設它可以從較低級別的協議獲得簡單的,可能不可靠的數據報服務。
  • UDP ( User Datagram Protocol ) Internet 協議集支持一個無連接的傳輸協議, 該協議稱為用戶數據報協議(UDP,User Datagram Protocol)。 UDP 為應用程序提供了一種無需建立連接就可以發送封裝的 IP 數據包的方法。 RFC 768 描述了 UDP。
  • LAN ( Local Area Network ) 與終端用戶接近的網絡,例如在家庭或辦公室內。
  • IGD ( Internet Gateway Device ) 一個多功能網絡基礎設施,它負責將本地網絡的數據包路由或者橋接到 internet.
  • IPR ( Intellectual Property Rights ) 知識產權
  • JPEG ( Joint Photographic Experts Group ) 是JPEG標准的產物,該標准由國際標准化組織(ISO)制訂,是面向連續色調靜止圖像的一種壓縮標准。 后綴名為 'jpg' 或者 ‘jpeg’;
  • LFE ( Low Frequency Enhancement ) DVB 指定的一種傳輸低頻聲音信息的一種方式。
  • LMP ( Link Management Protocol ) 鏈路管理協議,用於鏈接設置和控制。 這里指的是藍牙 spec 里面的鏈路管理協議。
  • LPCM ( Linear Pulse Code Modulation ) 線性脈沖編碼調制,是一種非壓縮音頻數字化技術,是一種未壓縮的原音重現, 在普通 CD、DVD 及其他各種要求最高音頻質量的場合中已經得到廣泛的應用。
  • M-DMC ( Mobile Digital Media Controller ) 支持 DLNA 的移動手持式設備, 其作用是查找 M-DMS 暴露的內容並將其與 DMR 的呈現能力匹配,並建立 M-DMS 與 DMR 之間的連接。
  • M-DMD ( Mobile Digital Media Downloader ) 支持 DLNA 的移動手持式設備, 具有從 M-DMS 下載內容的作用。
  • M-DMP ( Mobile Digital Media Player ) 支持 DLNA 的移動手持式設備, 其作用是查找 M-DMS 公開的內容並在本地呈現內容。
  • M-DMS ( Mobile Digital Media Server ) 支持 DLNA 的移動手持式設備, 負責在整個家庭中公開和分發內容。
  • M-DMU ( Mobile Digital Media Uploader ) 支持 DLNA 的移動手持式設備, 具有將內容上載到 M-DMS 的作用。
  • M-NCF ( Mobile Network Connectivity Function ) 支持 DLNA 的移動手持式設備, 它通過橋接 HND 和 MHD 設備之間的網絡連接層來提供互操作性。
  • MF ( Media Formats ) 媒體格式類型
  • MHD ( Mobile Handheld Device ) “移動手持設備” 是一種設備類別,它將所有適用的 DLNA 設備類別與移動手持設備的環境特征(要求)組合在一起。 此設備類別中的設備類別為:M-DMS,M-DMP,M-DMD,M-DMU 和 M-DMC。
  • MHP ( Multimedia Home Platform ) 由 DVB 聯盟制定的一種標准,DVB-MHP 的工作不僅覆蓋應用程序接口 API, 而且還包括家庭數字網絡(IHDN)和本地集群,其目的是標准化家庭平台,這對於未來成功應用交互式多媒體是很關鍵的。 它同時也可以看作是 DVB 純廣播工作到交互式 TV 應用的自然升級,推動了電視業務從模擬電視到數字化電視的過渡。
  • MIME ( Multipurpose Internet Mail Extension ) 多用途互聯網郵件擴展類型, 是一種 Internet 協議,允許通過 Internet 作為電子郵件附件發送二進制文件。 是設定某種擴展名的文件用一種應用程序來打開的方式類型,當該擴展名文件被訪問的時候,瀏覽器會自動使用指定應用程序來打開。 MIME 這包括圖形,照片,聲音,視頻文件和格式化的文本文檔。
  • MIU ( Media Interoperability Unit ) 泛指一類 DLNA 設備,提供家庭網絡和移動手持設備所需的媒體格式之間的內容轉換。
  • MM ( Media Management ) 媒體內容管理,見 DLNA GuideLine 互操作性指南中的標題部分。
  • MPEG-2 ( Moving Picture Experts Group phase 2 ) MPEG-2 是 MPEG(Moving Picture Experts Group,運動圖像專家組)組織制定的視頻和音頻有損壓縮標准之一, 它的正式名稱為 “基於數字存儲媒體運動圖像和語音的壓縮標准”。 與 MPEG-1 標准相比,MPEG-2 標准具有更高的圖像質量、更多的圖像格式和傳輸碼率的圖像壓縮標准。 MPEG-2 標准不是 MPEG-1 的簡單升級,而是在傳輸和系統方面做了更加詳細的規定和進一步的完善。 它是針對標准數字電視和高清晰電視在各種應用下的壓縮方案,傳輸速率在 3 Mbit/s ~ 10 Mbit/s 之間。
  • MRCP ( MediaRenderer:1 Control Point ) UPnP 控制點,向 MRD 發出操作。
  • MRD ( MediaRenderer:1 Device ) MediaRenderer 設備(也稱為 MediaRenderer )是 UPnP 設備,可提供基於網絡的媒體內容呈現控制。 MediaRenderer 必須具有 RenderingControl 服務和 ConnectionManager 服務。 MediaRenderer 規范是標准的 UPnP DCP。
  • MSCP ( MediaServer:1 Control Point ) 向 MSD 發出 Action 的 UPnP AV 控制點。
  • MSD ( MediaServer:1 Device ) MediaServer 設備(也稱為 MediaServer)是 UPnP 設備,可提供基於網絡的內容發現。 MediaServer 必須具有 ConnectionManager 服務和 ContentDirectory 服務。 MediaServer 規范是標准的 UPnP DCP。
  • MT ( Media Transport ) 媒體內容傳輸,見 DLNA GuideLine 互操作性指南中的標題部分。
  • NAL Unit ( Network Abstraction Layer Unit ) 一個描述特定於 H.264 RTP 有效負載格式的術語。 它是一個 1 字節的標頭和有效載荷字節字符串(參考: http://www.ietf.org/rfc/rfc3984.txt)。
  • NAP ( Network Access Point ) 藍牙 PAN 規范中的概念, 是 Bluetooth 網絡與其他網絡( LAN 或 Internet ) 之間具有相當於橋接器或路由器功能的 Bluetooth 無線設備,可以是筆記本電腦、移動電話等。 Bluetooth 設備可以連接到這個網絡訪問點 NAP,並通過 NAP 訪問其他網絡上的共享資源,NAP 角色為連接的藍牙設備提供了網絡服務
  • NC (Networking and Connectivity ) 網絡和連接,見 DLNA GuideLine 互操作性指南中的標題部分。
  • NC-PS ( Network Connectivity Power Saving ) 網絡連接-節能
  • NID ( Network Infrastructure Device ) 在家庭網絡中提供支持功能的設備,例如接入點,網橋,Internet網關,路由器和交換機,但不是規范性 HID 設備類別的成員。 這些設備可促進 DLNA 設備的良好用戶體驗,但目前僅在本文檔中由附錄 A 網絡基礎結構設備(NID)建議中的信息性建議涵蓋。
  • NTSC* ( National Television Systems Committee ) 國家電視系統委員會(NTSC)是由美國聯邦通信委員會(FCC)於 1940 年成立的標准化機構,旨在對模擬電視廣播進行標准化。 該委員會提出的標准就是以 NTSC 自己命名的。 NTSC 成為北美,南美部分地區和亞洲使用的主要模擬電視系統。 該標准以及大多數模擬電視廣播已於 2009 年 6 月 12 日被淘汰並停止使用,以支持數字電視。
  • OSI ( Open Systems Interconnection ) 開放系統互連是一個參考模型,說明了如何通過電信網絡傳輸消息。 它不包括詳細的界面。 相反,它可以作為網絡創建者的指南,使他們的產品與其他創建者的產品兼容。 開放系統互連包括七個功能,稱為功能層,當通過網絡發送消息時應執行這些功能
  • PAL ( Phase Alternating Line ) 模擬電視信號的廣播和接收的標准。
  • PANU ( PAN User ) PAN User 即 PAN 用戶,PANU 角色相對於 GN 角色和 NAP 角色支持客戶端角色,並支持與 GN / NAP 的連接
  • PC ( Personal Computer ) 個人電腦
  • PCR ( Program Clock Reference ) 請參考 MPEG-2 標准 13818-1.
  • PNG ( Portable Network Graphics ) " It is a coding standard for compression of still images (pictures).
  • PrCP ( Printer Control Point ) 向支持 PrD 的設備發送 Action 的 CP
  • PrD ( Printer Device ) 圖像打印機設備是 UPnP 設備,可為打印提供基於網絡的控制。 至少,打印機設備必須具有 PrintEnhanced 服務。 打印機設備規范是標准的 UPnP DCP
  • PS ( Program Stream ) 在這里通常代之 MPEG-2 AV 媒體流的一種格式,請參考 MPEG-2 中的定義。
  • PVR ( Personal Video Recorder ) 消費者個人 video 錄影設備。
  • QoS ( Quality of Service ) 為網絡提供可預測結果的能力提供保證。
  • RCS ( RenderingControl:1 Service ) RenderingControl 服務是 UPnP 服務,它提供基於網絡的控制來調整渲染屬性,例如音量,亮度,對比度和靜音。 RenderingControl 服務規范是標准的 UPnP DCP。
  • RTP ( Real Time Protocol ) 提供端到端網絡傳輸功能以傳輸實時數據的媒體傳輸, 例如 AV。 它提供諸如有效負載類型識別,序列編號,時間戳和交付監視之類的服務。
  • RTCP ( Real-time Transport Control Protocol ) 是實時傳輸協議(RTP)的一個姐妹協議。RTCP 由 RFC 355 0定義(取代作廢的RFC 1889)。 RTP 使用一個 偶數 UDP port ;而RTCP 則使用 RTP 的下一個 port,也就是一個奇數 port。 RTCP 與 RTP 聯合工作,RTP 實施實際數據的傳輸,RTCP 則負責將控制包送至電話中的每個人。 其主要功能是就 RTP 正在提供的服務質量做出反饋。
  • RTSP ( Real Time Streaming Protocol ) RTSP(Real Time Streaming Protocol),RFC2326,實時流傳輸協議,是 TCP/IP 協議體系中的一個應用層協議,由哥倫比亞大學、網景和 RealNetworks 公司提交的 IETF RFC 標准。 該協議定義了一對多應用程序如何有效地通過 IP 網絡傳送多媒體數據。 RTSP 在體系結構上位於 RTP 和 RTCP 之上,它使用 TCP 或 UDP 完成數據傳輸。 HTTP 與 RTSP相比,HTTP 請求由客戶機發出,服務器作出響應;使用 RTSP 時,客戶機和服務器都可以發出請求,即 RTSP 可以是雙向的。 RTSP 是用來控制聲音或影像的多媒體串流協議,並允許同時多個串流需求控制,傳輸時所用的網絡通訊協定並不在其定義的范圍內, 服務器端可以自行選擇使用 TCP 或 UDP 來傳送串流內容,它的語法和運作跟 HTTP 1.1 類似,但並不特別強調時間同步,所以比較能容忍網絡延遲。 而前面提到的允許同時多個串流需求控制(Multicast),除了可以降低服務器端的網絡用量,更進而支持多方視訊會議(Video Conference)。 因為與 HTTP1.1 的運作方式相似,所以代理服務器〈Proxy〉的快取功能〈Cache〉也同樣適用於 RTSP,並因 RTSP 具有重新導向功能, 可視實際負載情況來轉換提供服務的服務器,以避免過大的負載集中於同一服務器而造成延遲。
  • SCPD ( Service Control Protocol Description ) 這是描述 UPnP 服務的 XML 編碼文件。 這也稱為服務描述文件。
  • SDES ( Session Description Item ) RTCP 中的概念
  • SDP ( Session Description Protocol ) RTCP 中的概念
  • SOAP ( Simple Object Access Protocol ) 基於XML的消息傳遞協議,用於通過網絡交換服務請求和響應。
  • SPTS ( Single Program Transport Stream ) RTCP 中的概念
  • SSDP ( Simple Service Discovery Protocol ) 這里指 UPnP 中使用的設備發現協議。
  • TIFF ( Tagged Image File Format ) 是一種靈活的位圖格式,主要用來存儲包括照片和藝術圖在內的圖像,最初由 Aldus 公司與微軟公司一起為 PostScript 打印開發。 TIFF 與 JPEG 和 PNG 一起成為流行的高位彩色圖像格式。 TIFF 格式在業界得到了廣泛的支持,如 Adobe 公司的 Photoshop、The GIMP Team 的 GIMP、Ulead PhotoImpact 和 Paint Shop Pro 等圖像處理應用、 QuarkXPress 和 Adobe InDesign 這樣的桌面印刷和頁面排版應用,掃描、傳真、文字處理、光學字符識別和其它一些應用等都支持這種格式。 從 Aldus 獲得了 PageMaker 印刷應用程序的 Adobe 公司控制着TIFF規范。
  • TS ( Transport Stream ) 在文中指 MPEG-2 AV 流的格式,現在指 TS 流文件,是一種 DVD 的文件格式, MPEG2-TS 格式的特點就是要求從視頻流的任一片段開始都是可以獨立解碼的。
  • TTS ( Timestamped Transport Stream ) 帶時間戳的流。
  • UP ( User Priority ) WMM 規范中 QoS 控制字段的 3 位字段,以及 802.1Q 的 VLAN 標簽報頭中的標簽控制信息字段,用於定義數據包的相對優先級。 請參考:( https://standards.ieee.org/getieee802/index.html )( https://www.wi-fi.org/ 對 WMM 的定義 )(這兩個文件都是付費的)
  • URI ( Uniform Resource Identifier ) W3C 對 Internet 上當前對象和將來對象的名稱和地址語法的編碼。 URI 以其最基本的形式,由方案名稱(例如文件,http,ftp,news,mailto,gopher)組成,后跟冒號,其后是路徑,其性質由其之前的方案確定。 URI 是 URN,URL 和所有其他統一資源標識符的總稱。
  • URL ( Uniform Resource Locator ) 同一資源標識符。
  • UTF( Unicode Transformation Format ) Unicode 轉換格式, 其中,UTF-8 是 UTF 中最常用的轉換格式,是 UNICODE 的一種變長字符編碼,由 Ken Thompson 於1992年創建。 現在已經標准化為 RFC 3629。UTF-8 用 1 到 6 個字節編碼 UNICODE 字符。
  • VI( Video User Priority ) A priority-based QoS level for Video User Priority.
  • VLAN 虛擬局域網(VLAN)是一組邏輯上的設備和用戶,這些設備和用戶並不受物理位置的限制,可以根據功能、部門及應用等因素將它們組織起來, 相互之間的通信就好像它們在同一個網段中一樣,由此得名虛擬局域網。 VLAN是一種比較新的技術,工作在 OSI 參考模型的第 2 層和第 3 層,一個 VLAN 就是一個廣播域,VLAN 之間的通信是通過第3層的路由器來完成的。
  • VO ( Voice User Priority ) 一個基於語音用戶優先級的 QoS 級別。
  • WAN ( Wide Area Network ) 萬維網,通常只整個 internet。
  • WMM Wireless Multimedia Extensions (WME), 更為人所熟知的名字是 Wi-Fi Multimedia (WMM), 是基於 IEEE 802.11e 標准的 Wi-Fi Alliance 互操作性認證。 它為 IEEE 802.11 網絡提供基本的服務質量(QoS)保證。 WMM 根據四個訪問類別(Access Categories AC)對流量進行優先級排序:語音(AC_VO),視頻(AC_VI),盡力而為(AC_BE)和背景(AC_BK)。 但是,它不能保證吞吐量。 它適用於需要 QoS 的明確定義的應用程序,例如 Wi-Fi 電話(VoWLAN)上的IP語音(VoIP)。 參考:傳送門
  • XML ( Extensible Markup Language ) 是一種文本標記語言,用於描述數據之間的結構和信息交換。
DLNA 交互文檔中自定義的名詞解釋
  • ΔTpcr
  • Authentication
  • Authorization
  • Bearer
  • Cacheable Content
  • Channel
  • Content
  • Content Binary
  • Content Transformation
  • Content Source
  • Content Receiver
  • Decodeer Friendly Point
  • Device Capability
  • Device Category
  • Device Class
  • Device Function
  • Device Option
  • Device Type
  • DLNA QOS
  • Full TS
  • Ideal Network Conditioins
  • Instance state variable
  • interactive Transfer
  • kHz
  • Link-level bearer
  • Media Class
  • Media Collection File
  • Media Format
  • Media Stream
  • Media Transfer Mode
  • Native Device
  • Network De-jitter Buffer
  • Non-Cacheable Content
  • Partial SPTS
  • Post-decoder Buffer
  • Pre-decoder Buffer
  • Printer
  • Printing Controller
  • Receiver Buffer
  • Receiving Endpoint
  • Rendering Endpoint
  • RTP Media Transport
  • RTP Session
  • RTP Stream
  • RTSP Session
  • Scalling
  • Serving Endpoint
  • Streaming Transfer
  • System Usage
  • Test Conditions
  • Transfercoding
  • Transrating
  • UPnP
  • Virtual Instance
  • Virtual Server
  • VLAN Tag
DLNA 主要用到的技術
  • Conectivity Ethernet*, 802.11, and Bluetooth(好像很少看到藍牙支持的場景)
  • Networking IPv4 Suite
  • Device Discovery and Control UPnP* Device Architecture v1.0(見前面介紹 UPnP 的部分,已經說得很詳細了)
  • Media Management and Control UPnP AV v1 and UPnP Printer:1
  • Media Formats Required and Optional Format Profiles(后續有時間補充)
  • Media Transport HTTP (必須具備) and RTP (可選)(現在 HTTP2 和 HTTP3 已經開始流行了,一個是為了加快響應速度,二者減輕網絡壓力這兩個都最好支持上)
UPnP 設備規范( UDA 1.0)
前言

本文檔合並了幾個單獨的文檔,這些文檔構成了 UPnP 設備體系結構 1.0 版的整體,包括以前針對 AutoIP,SSDP,HTTPU/MU,FXPP 和 GENA 的單獨規范, 以及《 UPnP Vendor Implementation Guide》中的說明。 它不引入任何新的技術要求,而僅構成編輯上的說明。

簡介
What is UPnP™1 Technology?

UPnP™技術定義了一種架構,該架構用於構建各種形式的智能設備,無線設備和 PC 設備普遍對v等網絡連接。 它旨在為家庭,小型企業,公共場所或連接到 Internet 的臨時或非托管網絡帶來易於使用,靈活,基於標准的連接。 UPnP技術提供了一種分布式,開放的網絡體系結構,該體系結構利用 TCP/IP 和 Web 技術來實現無縫的鄰近網絡, 以及在聯網設備之間進行控制和數據傳輸。

UPnP Device Architecture(UDA)不僅僅是即插即用外圍設備模型的簡單擴展。 它的設計旨在使能眾多廠商支持零配置,“無感” 聯網以及服務的自動發現。 這意味着設備可以動態加入網絡,獲取IP地址,申明其功能以及了發現了解其他設備的存在和功能。 最終,設備也可以自動順暢地離開網絡,而不會留下任何不必要的狀態。

UPnP 體系結構利用了普遍的 Internet 協議技術,例如 IP,TCP,UDP,HTTP 和 XML。 像 Internet 合約基於聲明性的有線協議一樣,UPNP 以 XML 表示並通過 HTTP 協議進行通信。 使用互聯網協議是 UDA 的一個不錯的選擇,因為它具有跨不同物理媒體的可靠能力, 可以實現現實世界中的多供應商互操作,並且可以與 Internet 以及許多家庭和辦公室內部網實現協同作用。 UPnP體系結構已明確設計為適應這些環境。 此外,作為一個橋接萬物的協議, UDA 還解決了設備因為成本,技術或傳統限制設備無法獲取 IP 時的場景

為什么 UPnP 技術是 “普遍性” 的? 沒有設備驅動程序, 使用通用協議。 UPnP 網絡是獨立的(媒體)。 UPnP 設備可以使用任何編程語言在任何操作系統上實現。 UDA 在程序/接口設計上沒有設置任何限制。 操作系統供應商可以為其客戶的需求實現合適的 API。

UPnP™ 論壇組

UPnP 論壇則是一項行業計划,旨在使來自許多不同的供應商的獨立設備和 PC 之間實現輕松而強大的連接。 UPnP 論壇尋求開發一套用於描述設備協議的標准。基於 XML 的設備的標准架構用於在可擴展網絡環境中,實現設備到設備互相操作。

The UPnP Implementers Corporation(UIC)由UPnP 論壇中的各個行業成員公司組成, 采用統一的設備互連技術標准以對這些設備進行測試和認證。 UIC 開發和管理測試和認證過程,管理 UPnP 徽標的分發,並向 UIC 成員和其他有關 UPnP 設備認證的相關方提供相關信息。 UPnP 設備認證對擁有 UPNP 設備和已支付 UIC 會費的 UPnP 論壇和 UIC 的成員開放。 有關更多信息,請參見 http://www.upnp-ic.org。

UPnP論壇已經建立了專門領域知識領域的工作委員會。 這些工作委員會負責創建建議的設備標准,構建示例實現以及構建適當的測試套件。 本文檔指出了UPnP論壇工作委員會職權范圍內的特定技術決策。

UPnP 供應商可以建立具有互操作性以及共享知識產權和徽標計划的好處的合規設備。 除了徽標程序之外,供應商還可以構建遵循本文定義的 UPnP 設備架構的設備,而無需使用正式的標准程序。 如果供應商制造非標准設備,則他們將決定由UPnP論壇工作委員會決定的技術決定(沒看懂,原文貼后面了)。If vendors build non-standard devices, they determine technical decisions that would otherwise be determined by a UPnP Forum working committee.

關於本文檔

本文包含的 UDA(以前稱為DCP框架)定義了控制器或控制點與設備之間進行通信的協議。 為了進行發現,描述,控制,事件和演示,UPnP設備體系結構使用以下協議棧():

在最上層,消息在邏輯上僅包含有關其設備的 UPnP 供應商特定信息。 在第二層, UPnP 論壇工作委員會定義補充供應商的內容的信息。 來自以上各層的消息以特定於 UPnP 的協議托管, 例如本文檔中定義的簡單服務發現協議(SSDP)和通用事件通知體系結構(GENA),以及所引用的其他協議。 以上消息是通過 HTTP(通過 UDP 的多播或單播來傳輸)或通過在 TCP 上的 HTTP 傳遞的。 最終,以上所有消息均通過 IP 網絡傳遞。 本文檔的其余部分詳細描述了每個協議層的內容和格式。 作為參考,以上[方括號]中的顏色表示在本文檔中哪個協議定義了特定的消息組件。

UDA 定義了兩種設備的一般分類:受控設備(或簡稱為“設備”)和控制點。 受控設備充當服務器的角色,響應來自控制點的請求。 控制點和受控設備都可以在包括個人計算機和嵌入式系統在內的各種平台上實現。 被控設備,控制點兩者可以在同一網絡終端上同時運行。

UPnP 網絡的基礎是 IP 尋址(IP addressing)。 每個設備必須具有動態主機配置協議(DHCP)客戶端,並在設備首次連接到網絡時搜索 DHCP 服務器。 如果 DHCP 服務器可用,則該設備必須使用分配給它的IP地址。 如果沒有可用的DHCP服務器,即網絡不受管理,則設備必須使用自動 IP ( AutoIP )來獲取地址。 簡而言之,“自動IP” 定義了設備如何從一組保留的地址中智能地選擇 IP 地址, 以及如何在托管網絡和非托管網絡之間輕松移動。 如果在進行 DHCP 事務處理期間,設備(例如,通過DNS服務器或DNS轉發)獲得了域名, 則設備應在后續的網絡操作中使用該名稱; 否則,設備應使用其IP地址。

給定一個IP地址,UPnP 網絡中的步驟 1 是發現( discovery )。 將設備添加到網絡后,UPnP 發現協議允許該設備將其服務通告給網絡上的控制點。 同樣,當將控制點添加到網絡時,UPnP 發現協議允許該控制點搜索網絡上感興趣的設備。 兩種情況下的基本交換是發現消息,該發現消息包含關於設備或其服務之一的一些基本細節, 例如,設備的類型,標識符以及指向更詳細信息的指針(URL)。 下面有關發現的部分說明了設備如何播發,控制點搜索方式以及發現消息格式的詳細信息。

UPnP 組網中的第 2 步為描述( description )。 控制點發現設備后,控制點對設備的了解仍然很少。 為了使控制點了解有關設備及其功能的更多信息,或與設備進行交互, 控制點必須從設備在發現消息中提供的 URL 檢索設備的描述。 設備可能包含其他邏輯設備以及功能單元或服務, 並且包括特定於供應商的制造商信息。例如型號名稱和編號,序列號,制造商名稱,特定於供應商的網站的 URL 等 (設備的 UPnP 描述以 XML 表示)。 該描述還包括任何嵌入式列表。設備或服務,以及用於控制,事件和演示的 URL。 對於每個服務,描述都包括服務響應的命令或動作的列表,以及每個動作的參數或自變量。 服務描述還包括(狀態)變量列表;這些變量在運行時對服務狀態進行建模,並根據其數據類型,范圍和事件特征進行描述。 下文“描述”部分說明了如何描述設備以及控制點如何檢索這些描述。

UPnP網絡中的第三步 3 是控制。 在控制點檢索到設備的描述之后,控制點可以將操作發送到設備的服務。 為此,控制點將適當的控制消息發送到服務的控制 URL(在設備描述中提供)。 控制消息還使用簡單對象訪問協議(SOAP)用 XML 表示。 像函數調用一樣,響應於控制消息,服務將返回任何特定於操作的值。 動作的效果(如果有的話)是通過描述服務運行時狀態的變量的變化來建模的。 下面有控制的部分的操作說明,狀態變量和控制消息格式的描述。

UPnP 網絡中的步驟 4 正在事件(eventing)。 服務的 UPnP 描述包括服務響應的操作列表和在運行時對服務狀態建模的變量列表。 這些變量更改時,服務將發布更新,並且控制點可以訂閱以接收此信息。 該服務通過發送事件消息( eventing msg )來發布更新。 事件消息包含多個狀態變量的名稱以及這些變量的當前值。這些消息也以XML表示。 當控制點首次訂閱時,會發送一個特殊的初始事件消息。 該事件消息包含所有事件變量的名稱和值,並允許訂戶初始化其服務狀態模型。 為了支持具有多個控制點的方案,事件設計為使所有控制點均等地了解任何操作的效果。 因此,並且無論狀態變量為何更改,被控點因向所有訂戶發送所有事件消息。 訂閱者會受到所有已經變更的消息。下面有事件的部分說明了訂閱和事件消息的格式。

UPnP 網絡中的第 5 步是展示(presentation)。 如果設備具有用於展示的 URL,則控制點可以從該 URL 檢索頁面, 將該頁面加載到瀏覽器中,並根據頁面的功能,允許用戶控制設備和/或查看設備狀態。 完成這些操作的程度取決於演示頁面和設備的特定功能。 下面有 “演示” 的部分說明了檢索演示頁面的協議。

Audience

本文檔的讀者包括 UPnP 設備供應商(vendor),UPnP 論壇工作委員會的成員以及需要了解 UPnP 協議技術細節的其他任何人。

本文檔假設讀者熟悉 HTTP,TCP,UDP,IP 協議家族; 本文檔未嘗試解釋它們。 本文檔還假定大多數讀者將不熟悉 XML,盡管它不是 XML 教程,但鑒於 XML 在 UPnP 設備體系結構中的中心地位,將詳細解決與 XML 有關的問題。 本文檔不假定讀者對各種編程或腳本語言的理解。

Required vs. recommended

在本文檔中,特性被描述成 Required(要求), Recommended(推薦), or Optional(可選) 三個等級。

  • Required (or Must or Shall)

    必須實現這些基本功能,以符合 UPnP 設備架構(UDA)。 短語“不應該(shall not)”和“不得( must not )”表示禁止的行為,如果執行,則表示該實現不符合要求。

  • Recommended (or Should).

    這些功能增加了 UPnP 設備體系結構支持的功能,應予以實現。 推薦的功能通常利用 UPnP 設備架構的功能,而不會造成較大的成本增加。 請注意,對於合規性測試,如果實施了推薦功能,則它必須滿足指定要求才能符合這些准則。 某些推薦功能可能會在將來成為要求。 短語“不應”表示允許但不建議的行為

  • Optional (or May).

    UPnP 設備體系結構既不需要也不推薦這些功能,但是如果實現了該功能,則必須滿足指定的要求才能符合這些准則。 這些功能將來不太可能成為必需。

Acronyms
  • ARP Address Resolution Protocol
  • CP Control Point
  • DCP Device Control Protocol
  • DHCP Dynamic Host Configuration Protocol
  • DNS Domain Name System
  • GENA General Event Notification Architecture
  • HTML HyperText Markup Language
  • HTTP Hypertext Transfer Protocol
  • HTTPU HTTP (Unicast over UDP)
  • HTTPMU HTTP (Multicast over UDP)
  • SOAP Simple Object Access Protocol
  • SSDP Simple Service Discovery Protocol
  • UDA UPnP™ Device Architecture
  • UPC Universal Product Code
  • URI Uniform Resource Identifier
  • URL Uniform Resource Locator<
  • URN Uniform Resource N me
  • UUID Universally Unique Identifier
  • XML Extensible Markup Language
References and resources

本文檔的每個部分都包含有關特定主題資源的其他信息。

0. Addressing

尋址是 UPnP™ 網絡的第 0 步。 通過尋址,設備可以獲得網絡地址。 尋址成功才能使控制點在步驟 1 (discovery)在找到有感興趣的設備, 步驟 2(description)在控制點了解設備功能, 步驟 3(control)在控制點向設備發送命令, 步驟4(eventing)控制點監聽設備狀態變化, 步驟5(presentation)控制點顯示設備用戶界面。

UPnP 網絡的基礎是 IP 尋址。 每個本身不實現 DHCP 服務器的 UPnP 設備都必須具有動態主機配置協議(DHCP)客戶端, 並在設備首次連接到網絡時搜索 DHCP 服務器(如果設備本身實現DHCP服務器,則它可以分配本身就是它所控制的池中的地址)。 如果 DHCP 服務器可用,即管理網絡,則該設備必須使用分配給它的 IP 地址。 如果沒有DHCP服務器可用,即網絡不受管理; 設備必須使用自動 IP 尋址(Auto-IP)來獲取地址。

此處定義的自動 IP 是定義設備(a)確定 DHCP 是否不可用,以及(b)從一組本地鏈路 IP 地址中智能地選擇 IP 地址。 這種地址分配方法使設備可以輕松地在托管和非托管網絡之間移動。

本節定義了自動 IP 的操作。 本節中描述的操作在下面列出的參考文檔中進行了詳細說明。 如果本文檔和參考文檔之間存在沖突,則始終以參考文檔為准。

0.1 Addressing: Determining whether to use Auto-IP

支持自動 IP 並的設備被配置為首先通通過 DHCP 發送 DHCPDISCOVER 消息請求 IP 地址開始。 此 DHCP 客戶端應偵聽 DHCPOFFER 的時間量取決於廠商自己的實現。 如果在此期間收到 DHCPOFFER,則設備必須進行繼續動態地址分配過程。 如果沒有收到有效的 DHCPOFFER,則設備開始自動配置 IP 地址流程。

0.2 Addressing: Choosing an address

為了使用自動 IP 自動配置 IP 地址,設備需要實現有關的算法來選擇 169.254.0.0/16 范圍內的地址。 此范圍中的前 256 個地址和后 256 個地址被保留,不能使用。

經過算法選擇的地址必須測試所選的地址在局域網中的唯一性,以確定該地址是否已被使用。 如果該地址正被另一個設備使用,則必須選擇並測試另一個地址,直到達到廠商自己定義的最大重試次數。 當多個設備試圖分配地址時,應將地址選擇隨機化以避免沖突。 建議設備使用偽隨機算法(分布在從 169.254.1.0 到 169.254.254.255 的整個地址范圍內)選擇地址, 以最大程度地減少同時加入網絡的設備選擇相同地址的可能性,並在檢測到沖突時按相同順序選擇備用地址。 可以使用設備的以太網硬件 MAC 地址來作為偽隨即算法的種子。

0.3 Addressing: Testing the address

要測試所選的地址,設備必須使用地址解析協議(ARP)探針。 ARP 探針是一個 ARP 請求,其中設備 IP 地址用作發件人的 IP 地址,而發件人的 MAC 地址設置為 0s。 ( hwsrc == 自己 MAC 地址, psrc == 0.0.0.0, pdst == 探測的 IP 地址,也就是自己隨機選擇出來的 IP 地址 ) 然后,設備將偵聽 ARP 探針或針對同一 IP 地址的其他 ARP 探針的響應。 如果看有一個使用設備當前選擇的 IP 的 ARP 數據包,則設備必須考慮使用其他地址。 可以重復進行 ARP 探測,以確保該地址尚未使用。 建議每兩秒鍾發送四次探測。

成功配置鏈接本地地址后,設備應發送兩個自己的 ARP 報文,間隔 2 秒,這次將配置的 IP 填充為了發送方 IP 地址。 這些 ARP 的目的是確保網絡上的其他主機沒有因為以前可能使用相同地址而遺留的陳舊 ARP 緩存。

擁有非易失性存儲的設備可以記錄其選擇的 IP 地址, 並在下一次引導時將其用作探測時的第一個候選地址,以提高地址的穩定性並減少解決地址沖突的需要。

當設備正在發送 ARP 探針並偵聽答復時,地址沖突檢測不應局限於地址測試階段。 地址沖突檢測是一個持續不斷的過程,只要設備使用鏈接本地地址,該過程就一直有效。 在任何時候,如果設備接收到一個 ARP 數據包,該 ARP 數據包具有自己的 IP 地址作為發送方 IP 地址, 但發送方硬件地址與自己的硬件地址不匹配,則該設備應將此視為地址沖突並應做出響應 如以下(a)或(b)中所述:

  • a: 立即配置新的本地鏈接IP地址,或者
  • b: 如果設備當前具有活動的 TCP 連接或其他原因希望保留相同的IP地址, 並且最近(例如,在過去的十秒鍾內)沒有看到任何其他沖突的 ARP 數據包, 則它可以選擇嘗試保護其地址一次 ,方法是記錄收到沖突的 ARP 數據包的時間, 然后廣播一個 gratuitous ARP,並為將自己的 IP 和硬件地址作為 ARP 的源地址。 但是,如果在此后的短時間內(例如十秒鍾之內)收到另一個沖突的ARP數據包, 則設備應立即如上所述配置新的自動IP地址。

設備應按照上述(a)或(b)中的說明響應沖突的 ARP 數據包; 它不應忽略沖突的 ARP 數據包。 要從一個 IP 地址切換到新的 IP 地址, 設備應盡可能取消在前一個地址上發布的所有未完成的廣告, 並且必須在新地址上發布新的廣播。 發現部分介紹了廣播及其取消。

成功配置自動 IP 地址后, 應使用鏈接級廣播而不是鏈接級單播發送包含自動 IP 源地址的所有后續 ARP 數據包(答復和請求), 以便及時發現重復地址。 作為替代方案,無法發送廣播 ARP 答復的設備應發送單播 ARP 答復, 但隨后忽略遵循 RFC 826 中有關記錄來自已接收ARP請求的發件人信息的指令。 這意味着,在未能記錄發送方信息的情況下,該設備很可能稍后會發送自己的廣播ARP請求, 從而使使用相同IP地址的另一設備能夠檢測到沖突並對其進行響應。

源或目標地址在 169.254.0.0/16 范圍內的 IP 數據包不得發送到任何路由器進行轉發。 具有多播目標地址和自動IP源地址的IP數據報不應從本地鏈路轉發出去。 設備和控制點可以假定所有 169.254.0.0/16 目標地址都在鏈接上並且可以直接訪問。 169.254.0.0/16 地址范圍一定不能划分子網。

0.4 Addressing: Periodic checking for dynamic address availability

自動配置 IP 地址的設備必須定期檢查 DHCP 服務器的存在。 這是通過發送 DHCPDISCOVER 消息來完成的。 進行此檢查的頻率取決於實現方式,但是每 5 分鍾檢查一次將在所需的網絡帶寬和連接性維護之間保持平衡。 如果收到DHCPOFFER,則設備必須繼續進行動態地址分配。 DHCP分配的地址到位后,設備可以釋放自動配置的地址, 但也可以選擇在一段時間內(或無限期地)維護該地址以保持連接性。

要從一個 IP 地址切換到新的 IP 地址,設備應盡可能取消在前一個地址上發布的所有未完成的廣告, 並且必須在該新地址上發布新的廣告。 發現部分介紹了廣告及其取消。

0.5 Addressing: Device naming and DNS interaction

設備具有網絡的有效 IP 地址后,就可以通過該地址在該網絡上對其進行定位和引用。 最終用戶可能需要定位和識別設備。 在這些情況下,設備的友好名稱比 IP 地址更容易被人使用。 如果 UPnP 設備選擇向 DHCP 服務器提供主機名並向 DNS 服務器注冊,則該設備應確保所請求的主機名是唯一的, 或者應為用戶提供一種更改所請求的主機名的方法。 UPnP設備通常不提供主機名,而是使用文字(數字)IP地址提供URL。

而且,名稱比 IP 地址更加靜態。 當設備的 IP 地址更改時,按名稱引用設備的客戶端不需要任何修改。 設備的 DNS 名稱到其 IP 地址的映射可以手動輸入,也可以根據 RFC 2136 動態輸入到 DNS 數據庫中。 盡管支持動態 DNS 更新的設備可以直接在 DNS 中注冊其 DNS 記錄, 但也可以配置 DHCP 服務器進行注冊 DNS 記錄代表這些DHCP客戶端。

0.6 Addressing: Name to IP address resolution

如果一台設備需要通過 DNS 聯系另外一台設備,那么我們先要從 DNS 的名字中取得設備的 IP 地址。 設備根據 RFC1034 和 1035 向預配置的 DNS 服務器提交 DNS 查詢, 並從 DNS 服務器接收包含目標設備 IP 地址的響應。 可以使用 DNS 服務器列表對設備進行靜態預配置。 或者,可以通過DHCP,或在通過DHCPINFORM消息分配地址之后,為設備配置 DNS 服務器列表。

0.7 Addressing references
1. Discovery

發現是 UPnP™ 網絡中的第一步。 發現是在尋址(第 0 步)之后獲得的,其中設備獲得了網絡地址。 通過發現,控制點可以找到有趣的設備。 發現可以進行描述(第 2 步),其中控制點了解設備功能,進行控制(第 3 步),控制點向設備發送命令, 進行事件(第 4 步),控制點偵聽設備的狀態變化, 和演示(第 5 步),其中控制點顯示設備的用戶界面。

發現是 UPnP 網絡的第一步。將設備添加到網絡后,UPnP 發現協議允許該設備將其服務通告給網絡上的控制點。 同樣,當將控制點添加到網絡時,UPnP 發現協議允許該控制點搜索網絡上感興趣的設備。 兩種情況下的基本交換是發現消息,該消息包含有關設備或其服務之一的一些基本細節, 例如,設備的類型,設備的唯一的標識符,及指向更詳細信息的指針。

將新設備添加到網絡后,它會用多播的方式廣播一些消息,這些消息會宣傳其自身,其嵌入式設備及其服務。 任何感興趣的控制點都可以偵聽標准多播地址,以獲取有關新功能可用的通知。

同樣,將新的控制點添加到網絡后,它會用多播的方式廣播一條搜索消息,搜索有趣的設備,服務或兩者。 所有設備必須偵聽這些消息的標准多播地址,並且如果它們的任何嵌入式設備或服務符合發現消息中的搜索條件, 則必須做出響應。

重申一下,控制點可以獲悉感興趣的設備,因為該設備發送了廣告宣傳自己的消息, 或者因為該設備響應了搜索消息。 在任何一種情況下,如果控制點對設備感興趣並希望了解更多信息, 則控制點將使用發現消息中的信息來發送描述查詢消息。 “描述”部分詳細說明了描述消息。

當設備從網絡中刪除時,如果可能的話,它應該多播多條發現消息,以撤銷其先前的公告, 從而有效地聲明其嵌入式設備和服務將不再可用。 更改設備的IP地址后,它應也應該撤消所有之前的公告,並使用新的IP地址發布廣告。

對於具有多個網絡接口的設備和控制點, 應該在所有啟用 UPnP 網絡的網絡接口上發送 UPnP 通告和搜索。 每個廣播消息或搜索消息都必須在 LOCATION 標頭中指定一個可在該接口上訪問的地址

為了避免網絡擁塞,每個多播消息的每個 IP 數據包的生存時間(TTL)應該默認為4,並且應該是可配置的。 當 TTL 大於 1 時,多播消息可能會遍歷多個路由器。 因此,使用非 AutoIP 地址的控制點和設備必須發送 IGMP Join 消息, 以便路由器將多播消息轉發給它們 (使用 Auto-IP 地址時,這是不必要的,因為具有 Auto-IP 地址的數據包將不會被路由器轉發)。

發現在不同版本的 UPnP 網絡的設備和控制點的互操作性中起着重要作用。 UPnP 設備體系結構(在此定義)同時具有主要版本和次要版本,通常寫為 major.minor, 其中,主要和次要均為整數(例如,版本2.10 比版本2.2更新)。 次要版本的提升必須是同一主版本的較早次要版本的兼容超集。 主要版本中的進步不一定是早期版本的超集,也不保證其向后兼容。 版本信息在發現和描述消息中傳達。 在前一種情況下,每個發現消息都包括設備支持的 UPnP 網絡版本(在 SERVER 標頭中); 相關的發現消息中還包含支持的設備和服務類型的版本。 作為備份,后者也包含相同的信息。 本節將說明發現消息中版本信息的格式以及對發現消息的特定要求,以保持與次要版本的改進的兼容性。

本節的其余部分詳細解釋 UPnP 發現協議(SSDP), 列舉設備如何通告和撤消其通告以及控制點搜索和設備如何響應。

1.1 Discovery: Advertisement

將設備添加到網絡后,該設備會將其服務通告給控制點。 它通過將發現消息多播到標准地址和端口(239.255.255.250:1900)來實現此目的。 控制點偵聽此端口以檢測網絡上何時有新功能可用。 為了公布其功能的全部范圍,設備會多播設備自身的服務相對應的發現消息。 每個消息都包含特定於設備(或服務)的信息和有關其封閉設備的信息。 消息應應該包含該消息的過期信息; 如果設備仍然可用,則應重新發送廣告。 如果設備不可用,則設備應明確取消其廣告,但是如果設備無法執行此操作,則廣告將自行失效。

1.1.1 Discovery: Advertisement protocols and standards

為了發送(和接收)廣播,設備(和控制點)需要遵從以下提到的 UPNP 的子集。 (UPnP 的完整協議棧在文章開頭有提及)

在最上層,發現消息包含供應商的信息,例如,用於設備描述和設備標識符的URL。 從協議棧向下看,供應商內容將由 UPnP 論壇工作委員會提供的信息(例如設備類型)進行補充。 來自以上各層的消息以本文檔中定義的 UPnP 的協議承載。 同樣的,上述消息是通過使用 HTTPMU( HTTP 多播變體) 的頭部或者方法進行封裝傳遞。 HTTP 消息是通過 UDP over IP 傳遞的。 作為參考,上方[方括號]中的顏色表示哪種協議定義了下面列出的發現消息中的特定標頭和值。

1.1.2 Discovery: Advertisement: Device available -- NOTIFY with ssdp:alive

將設備添加到網絡后,它會多播發現消息,以通告其根設備,任何嵌入式設備和所有服務。 每個發現消息包含四個主要組件:

    1. 在 NT(通知類型)標頭中發送的潛在搜索目標(例如設備類型),
    2. 在 USN(唯一服務名稱)標頭中發送該設備綜合服務的組合標識符,

在 LOCATION 標頭中發送的有關該設備(或在服務中為封閉設備)的更多信息的 URL,

在 CACHE-CONTROL 標頭中發送的廣播消息的有效期限。

為了公布其功能,設備會廣播多次自身服務的多條發現消息。 具體來說,根設備必須多播:

  • 更設備(服務)的發現消息。
  • 兩條為兩個嵌入設備發送的搜索消息。
  • 設備各自的服務。

** 請注意,USN 標頭的前綴(在雙冒號之前)必須與設備描述中的 UDN 元素的值匹配。 (“描述”部分說明了UDN元素。)

** 請注意,此NT標頭的值必須與設備描述中的UDN元素的值匹配。

如果根設備具有 d 個嵌入式設備和 s 個嵌入式服務,但只有 k 種不同的服務類型, 那么該設備將可達到 3 + 2d + k 個請求。 如果特定設備或嵌入式設備包含特定服務類型的多個實例, 則僅需要通告一次該服務類型(而不是每個實例一次)。 這會將設備功能的全部范圍通告給感興趣的控制點。 這些消息必須按照到期時間粗略的排好先后順序,然后按照序列順序發送出去; 嚴格的順序無關緊要,但是單獨的刷新或取消消息是禁止的。 (不知道翻譯對不對,原文在這里: order is unimportant, but refreshing or canceling individual messages is prohibited)

更新的 UPnP 設備和服務類型需要與該類型的先前版本完全向后兼容。 設備必須公布每種受支持類型的最高受支持版本。 例如,如果設備支持“音頻”服務的版本2,即使它也支持版本 1,它也只會發布版本 2。 由此,固定版本的設備或服務的控制點也可以與更高版本進行交互。 但由於僅具有向后兼容性要求,因此只能使用較低版本中定義的功能。 例如,如果控制點僅支持“音頻”服務的版本“ 1”,並且設備宣傳其支持“音頻”服務的版本“ 2”, 則控制點應識別並能夠使用該設備 。

為廣播選擇合適的有效時間是在最小化網絡流量和最大化設備狀態新鮮度之間的平衡。 相對較短的有效時間(最短為1800秒)將確保控制點具有當前設備狀態, 但會增加網絡流量; 較長的持續時間(例如一天左右)會損害設備狀態的新鮮度,但可以大大減少網絡流量。 通常,設備供應商應該選擇一個與預期設備使用率相對應的值: 對於短期內預計將成為網絡一部分的設備,有效期的時間應該設置比較短; 對於預期長期成為網絡成員的設備,有效期時間應該設置較長。 頻繁連接和離開網絡的設備(例如移動無線設備)應使用較短的有效期時間,以便控制點可以更准確地了解其可用性。 初始的廣告集應具有比較合適的有效時間,並且整個組應盡快發送。 廣播的后續刷新可能會隨時間分布,而不是作為一個組發送。

設備應在發送初始廣告集之前隨機的等待小於 100 毫秒的間隔,以減少網絡風暴的可能性; 該隨機間隔也應在設備獲得新 IP 地址或安裝新網絡接口的情況下應用。

由於 UDP 的不可靠特性,設備應多次發送上述每個發現消息,盡管為避免網絡擁塞, 發現消息的發送次數不應超過 3 次。另外,設備必須在 CACHE-CONTROL 標頭中指定的超市時間到期之前, 定期重新發送其廣告。 建議以小於廣播到期時間一半的隨機分布間隔進行這種廣告刷新, 以便為在廣告到期之前從為丟失的廣播提供恢復的機會,並隨着時間的流逝在網絡上隨機分布多個設備的廣告刷新, 以避免網絡流量激增。 但請注意,UDP 數據包的長度也是有界的(在某些實現中可能只有512字節)。 每個發現消息必須完全適合單個UDP數據包。而且,不保證上述 3 + 2d + k 個消息將以特定順序到達。

將設備添加到網絡后,它必須使用以下格式廣播自己的請求,其中 NTS 頭字段的值應該為 ssdp:alive, Method 字段應該為 NOTIFY。其中斜體代表待填充字段。

NOTIFY 方法的請求沒有負載(body)部分,但是在頭部后了空行一定不能少。(結尾是兩個 \r\n)

IP 包的 TTL 默認為 4,但也可以更具具體的情況進行調整。

下面會詳細介紹一下上面請求體中各個字段的作用。如非特殊說明,所有的頭字段的 Key( field ) 都為大些( UpperCase )。

  • request line
    • NOTIFY 發送 notifications 和 events 的方法
    • * 必須具備 | 請求通用而非特定資源
    • HTTP/1.1 HTTP 的版本
  • headers
    • HOST 必須具備 | Internet 號碼分配機構(IANA)為 SSDP 保留的多播信道和端口(239.255.255.250:1900)。 如果在該字段中省略了 1900 這個端口號, 那么接受端應該默認使用 1900 這個端口。
    • CACHE-CONTROL 必須具備 | 指定廣播的有效秒數的 max-age 指令。 超過此時間之后,控制點應假定設備(或服務)不再可用。 | 該字段應大於或等於 1800 秒(30分鍾)。 但可由 UPnP 供應商配置指定。 | 數據類型為整數。
    • LOCATION 需要包含 | 包含指向根設備的 UPnP 描述的 URL。 通常,在非托管網絡中,主機部分包含的是 IP 地址字符串,而不是域名。 由 UPnP 供應商指定。單一網址。
    • NT 需要支持 | 指定 Notification 的類型,具體請參考一下 NT 那張表
    • NTS 需要支持 | Notification 的子類型,目前必須是該字段 ssdp:alive
    • SERVER 需要支持。 | 包含操作系統名稱,操作系統版本,UPnP / 1.0,產品名稱和產品版本的串聯。 這些信息由 UPnP 供應商自行指定。 | 數據類型:字符串。 | 該頭字段必須准確反映設備支持的 UPnP 設備體系結構的版本號。 控制點必須准備好接受比控制點本身實現的更高的次要版本號。 | 例如,實現 UDA 版本 1.0 的控制點將能夠與實現 UDA 版本 1.1 的設備進行互操作。
    • USN 需要支持。 | 唯一服務名稱。標識設備或服務的唯一實例。 必須是以下之一。 | 該字符串的前綴(在雙冒號之前)必須與設備描述中的 UDN 元素的值匹配。 (“描述”部分將介紹了 UDN 元素。) | 僅存在單個 URI。
  • NT 支持的可選項
    • upnp:rootdevice 根設備,此類消息在一個設備中只會出現一條。
    • uuid:device-UUID 每一個設備只會發送一次,根設備或者嵌入設備 | UUID 由 UPNP 供應商(vendor)自己決定。
    • urn:schemas-upnp-org:device:deviceType:v 每一個設備只會發送一次, 根設備或者嵌入式設備。 | 設備類型和版本號由 UPnP 供應商自己確定。 | 指定的版本號為最高支持的版本
    • urn:schemas-upnp-org:service:serviceType:v 每個服務發送一次。 | 服務類型和版本號有 UPnP Forum 制定, | 指定的版本號為最高支持的版本
    • urn:domain-name:device:deviceType:v 每一個設備只會發送一次, 根設備或者嵌入式設備。 | 域名的定義由 UPnP 供應商決定, | 指定的版本號為最高支持的版本 | 域名中的句點字符必須根據 RFC 2141 替換為連字符。
    • urn:domain-name:service:serviceType:v 每一個服務都會發送一次。 | 域名,服務類型和版本號都由 UPnP 供應商自己定義, | 這里指定的版本號為最高支持的版本 | 域名中的句點字符必須根據 RFC 2141 替換為連字符。
  • USN 支持的可選項
    • uuid:device-UUID::upnp:rootdevice 根設備發送一次 | 設備 UUID 由供應商決定
    • uuid:device-UUID 每一個設備都會發送一次, 根設備或者嵌入設備 | 設備 UUID 由供應商決定
    • uuid:device-UUID::urn:schemas-upnp-org:device:deviceType:v 每一個設備都會發送一次, 根設備或者嵌入設備 | 設備 UUID 由供應商決定 | 設備的類型和版本號有 UPnP 委員會確定 | 這里指定的版本號為最高支持的版本
    • uuid:device-UUID::urn:schemas-upnp-org:service:serviceType:v 每一個服務都會發送一次 | 設備 UUID 由供應商決定 | 設備的類型和版本號有 UPnP 委員會確定 | 這里指定的版本號為最高支持的版本
    • uuid:device-UUID::urn:domain-name:device:deviceType:v 每一個設備都會發送一次, 根設備或者嵌入設備 | 設備 UUID, 域名,設備類型,版本由供應商決定 | 這里指定的版本號為最高支持的版本 | 域名中的句點字符必須根據 RFC 2141 替換為連字符。
    • uuid:device-UUID::urn:domain-name:service:serviceType:v 每一個服務都會發送一次 | 設備 UUID, 域名,服務類型,版本由供應商決定 | 這里指定的版本號為最高支持的版本 | 域名中的句點字符必須根據 RFC 2141 替換為連字符。

NOTIFY 的請求不需要響應。

1.1.3 Discovery: Advertisement: Device unavailable -- NOTIFY with ssdp:byebye

當要將某個設備及其服務從網絡中刪除時,該設備應多播一個 ssdp:byebye 消息, 該消息對應於其多播的尚未到期的每個 ssdp:alive 消息。 如果設備突然從網絡中刪除,則可能無法組播消息。 作為后備,發現消息必須在 CACHE-CONTROL 標頭中包含一個到期值(如上所述)。 如果不重新發布,發現消息最終將會自行失效,並且必須從任何控制點緩存中刪除。

(注意:當一個控制點從網絡中推出時,不需要 discovery 相關的請求發出。)

當設備將要從網絡中刪除時,應通過為發送的每個 ssdp:alive 消息發送一個多播請求,顯式撤消其發現消息。 每個多播請求在 NTS 標頭中必須具有以下格式的 NOTIFY 和 ssdp:byebye 方法。 斜體值是實際值的占位符

(該請求無 Body 字段,但是后面的空行不能省略,即 IP 負載字段應該以 \r\n\r\n 結尾)

IP 包的 TTL 應該設置成 4, 但是該字段 UPnP 可以根據實際情況修改。

下面將會根據上敘協議中各個字段的詳細解釋。 除非特殊說明,上敘頭中的字段都是大小寫敏感的。

  • Request line
    • NOTIFY 發送 notifications 和 events 的方法
    • * 請求通用而非特定資源 | 必須具備
    • HTTP/1.1 HTTP 的版本
  • headers
    • HOST 必須具備 | Internet 號碼分配機構(IANA)為 SSDP 保留的多播信道和端口(239.255.255.250:1900)。 如果在該字段中省略了 1900 這個端口號, 那么接受端應該默認使用 1900 這個端口。
    • NT 需要支持 | (請參考 ssdp:alive 中的說明) | 這里只包含單個 URI
    • NTS 需要支持 | Notificatioin 的子類型 | 該字段必須為 ssdp:byebye | 這里只包含單個 URI
    • USN 需要支持 | 唯一的服務名字 | (請參考 ssdp:alive 中的說明) | 這里只包含單個 URI

(該 NOTIFY 信息不要服務端響應)

由於 UDP 的不可靠特性, 設備應多次發送上述每個消息。 作為后備,如果控制點無法接收到有關設備或服務不可用的通知, 原始發現消息最終將失效,從而產生相同的效果。

1.2 Discovery: Search

將控制點添加到網絡后,UPnP 發現協議允許該控制點搜索網絡上感興趣的設備。 它通過在保留的地址和端口(239.255.255.250:1900)上多播搜索消息, 該搜索消息的特征碼或目標等於設備或服務的類型或標識符。 來自設備的響應與新設備進入網絡時廣播的消息基本相同,區別是,前者是單播的,后者是組播的。

1.2.1 Discovery: Search protocols and standards

當要搜索設備(並由控制點發現), 控制點(和設備)使用如下的 UPnP 協議棧的子集。 (本文檔開頭列出了整個 UPnP 協議棧。)

在協議頂層,搜索消息包含特定於供應商的信息,例如控制點,設備和服務標識符。 協議棧向下一層,供應商內容將由 UPnP 論壇工作委員會提供的信息進行補充,例如設備或服務類型。 來自以上各層的消息都將托管在本文檔中定義的 UPnP 協議中。 相應的,搜索請求是通過 HTTP 的多播變體傳遞的,該變體已使用其他方法和標頭進行了擴展。 而搜索響應是通過 HTTP 的單播變體(已擴展)傳遞的。 兩種 HTTP 消息都是通過 UDP over IP 傳遞的。 作為參考,上方[方括號]中的顏色表示哪種協議定義了下面列出的發現消息中的特定標頭和值。

1.2.2 Discovery: Search: Request with M-SEARCH

當一個控制點加入網絡之后,它應該發送一個 M-SEARCH 類型的多播請求。 該請求服從下面的格式。Values in italics are placeholders for actual values.

( M-SEARCH 請求沒有負載(body 字段),但是結尾的空行必須存在,即數據包應該以 \r\n\r\n 結束。 )

IP 包的 TTL 應該設置成 4, 但是該字段 UPnP 可以根據實際情況修改。

下面將會根據上敘協議中各個字段的詳細解釋。除非特殊說明,上敘頭中的字段都是大小寫敏感的。

  • Request line
    • NOTIFY 發送 notifications 和 events 的方法
    • * 請求通用而非特定資源 | 必須具備
    • HTTP/1.1 HTTP 的版本
  • headers
    • HOST 必須具備 | Internet 號碼分配機構(IANA)為 SSDP 保留的多播信道和端口(239.255.255.250:1900)。 如果在該字段中省略了 1900 這個端口號, 那么接受端應該默認使用 1900 這個端口。
    • MAN 該字段服從 HTTP 擴展框架要求。 | 與 NTS 和 ST 標頭不同, MAN 標頭的值用雙引號引起來; | 它定義擴展的范圍(名稱空間)。 字段值必須為 “ssdp:discover”。
    • MX 需要支持,該字段指定最大的等待時長,單位為 s, 推薦值在 1 - 120 s 之間。 | 設備的響應該在經過 0 - MAX 之間隨機的一段時間后發出, | 而且不應該為遷就網絡狀況而去增大該值,(下面會說明原因) | 這個字段由 UPnP 供應商指定, | 數據類型為整數
    • ST 需要支持,指定搜索的目標。 | 必須從以下幾個中選擇一個(見接下來的 ST 表) | 僅僅包含一個指向資源的 URI
  • ST 中包含的可選項
    • ssdp:all 搜索所有的服務
    • upnp:rootdevice 僅搜索根設備
    • uuid:device-UUID 搜索 UUID 指定的設備,UUID 由供應商決定
    • urn:schemas-upnp-org:device:deviceType:v 搜索任何 deviceType 類型的設備, DeviceType 由 UPnP 委員會定義
    • urn:schemas-upnp-org:service:serviceType:v 搜索任何 serviceType 類型的設備, serviceType 由 UPnP 委員會定義
    • urn:domain-name:device:deviceType:v 搜索任何服從該域名,設備類型的設備, | 這里的域名和設備類型有 UPNP 供應商定義 | 域名中的句點字符必須根據 RFC 2141 替換為連字符。
    • urn:domain-name:service:serviceType:v 搜索任何服從該域名,服務類型的服務, | 這里的域名和服務類型有 UPNP 供應商定義 | 域名中的句點字符必須根據 RFC 2141 替換為連字符。

由於 UDP 的不可靠特性,控制點應多次發送各個 M-SEARCH 消息。 作為備用,為防止設備收不到控制點發送的 M-SEARCH 消息的可能性, 設備應定期重新發送其廣告(請參見上面 NOTIFY 中帶有 ssdp:alive 的 CACHE-CONTROL 標頭)。

控制點應該在 MX s 內一直等待設備發出響應。 MX 間隔上響應的隨機分布意味着響應者可能在收到 M-SEARCH 請求后的 MX 秒發送響應。 因此供應商應該基於觀察到的響應者的數量,然后根據經驗調一個合適的 MX 值。 也因為上敘原因,因此無法通過增加 MX 值來解決影響流量傳播的網絡特征。 請求者可以根據觀察到的網絡行為,通過啟發式方法適應網絡特征(確切的啟發式方法不在本文范圍內)。 算法最終應該會導致請求者等待 M-SEArCH 的時間會略微超過 MX 的時間,從而適應網絡的傳輸特性。 以最小化丟失設備的響應。

需要設備和服務類型的更新版本與以前的版本完全向后兼容。 設備必須響應任何受支持版本的 M-SEARCH 請求。 例如,如果設備實現“ urn:schemas-upnporg:service:Audio:2”, 則該設備也應該響應類型為 “ urn:schemas-upnp-org:service:Audio:1” 的搜索請求。 響應中應指定與搜索請求中包含的版本相同的版本。 如果控制點搜索特定版本的設備或服務並且未收到任何響應(大概是因為網絡上沒有設備支持指定的版本),但願意使用較低的版本進行操作,則它可能會重復搜索指定較低的版本。

1.2.3 Discovery: Search: Response

為了找到這些設備,設備必須為每一個從該多播地址收到的請求返回一個響應。 如果發起 MSEARCH 設備的 ST 頭為 “ssdp:all” 或者 "upnp:rootdevices" 或者 "uuid:device-UUID"(device-UUID 和本設備一致), 設備則必須響應這些消息,

設備在收到 DMC 發出的請求之后應該隨機的響應 0 - MX 指定的時間然后才響應設備的請求。 這么做是為了防止環境中的設備集中的響應 DMC 的請求,造成短暫的網絡擁塞, 如果一個搜索請求有多個響應,那么這些響應也應該隨機的分布在 0 - MX 時間段內。 如果搜索請求不包含 MX 標頭,則設備必須靜默丟棄並忽略搜索請求。 如果 MX 標頭指定的值大於120,則設備應假定其包含的值等於或小於 120。 設備在發送響應之前等待隨機延遲的同時,不應停止響應其他請求。

在 M-SEARCH 的響應消息中,LOCATION 字段指定的 URL 必須是可以訪問的。

設備對 M-SEARCH 的響應是有意相似於 ssdp:alive 的廣播消息的,比如說響應的頭幾乎與 ‘ssdp:alive’ 消息的頭一致(除了 NT 頭換成了 ST 頭)。 響應必須以以下格式發送。 斜體值是實際值的占位符

( M-SEARCH 請求沒有負載(body 字段),但是結尾的空行必須存在,即數據包應該以 \r\n\r\n 結束。 )

IP 包的 TTL 應該設置成 4, 但是該字段 UPnP 可以根據實際情況修改。

下面將會根據上敘協議中各個字段的詳細解釋。除非特殊說明,上敘頭中的字段都是大小寫敏感的。

  • request line
    • HTTP/1.1 HTTP 的版本
    • 200 HTTP 狀態碼,200 代表成功響應
    • OK 響應成功的描述字段
  • headers
    • CACHE-CONTROL 必須具備 | 指定廣播的有效秒數的 max-age 指令。 超過此時間之后,控制點應假定設備(或服務)不再可用。 | 該字段應大於或等於 1800 秒(30分鍾)。 但可由 UPnP 供應商配置指定。 | 數據類型為整數。
    • DATE 建議攜帶此字段 | 響應的時候生成, | 格式 follow “rfc1123-date”, 在 RFC 2616 中有定義。
    • EXT 該字段由 ( HTTP Extension Framework) 引入 | 主要用於確定理解請求中 “MAN” 頭 | 該頭不需要攜帶任何值
    • LOCATION 需要包含 | 包含指向根設備的 UPnP 描述的 URL。 | 通常,在非托管網絡中,主機部分包含的是 IP 地址字符串,而不是域名。 | 由 UPnP 供應商指定。單一網址。
    • SERVER 需要支持。 | 包含操作系統名稱,操作系統版本,UPnP / 1.0,產品名稱和產品版本的串聯。 這些信息由 UPnP 供應商自行指定。 | 數據類型:字符串。 | 該頭字段必須准確反映設備支持的 UPnP 設備體系結構的版本號。 控制點必須准備好接受比控制點本身實現的更高的次要版本號。 | 例如,實現 UDA 版本 1.0 的控制點將能夠與實現 UDA 版本 1.1 的設備進行互操作。
    • ST 如果請求中指定搜索此頭,那么在這里就要包含此頭。 | 具體請參考 請求部分的說明
    • USN 需要支持。這里指定唯一服務名稱。 | 具體請參考 "ssdp:alive" NOTIFY 部分 的說明

如果搜索請求出現錯誤(例如 MAN 標頭中的值無效,MX 標頭丟失或其他格式錯誤的內容), 則設備應靜默丟棄並忽略搜索請求。 不建議發送錯誤響應,因為如果許多設備向同一請求發送錯誤響應,則可能會出現數據包風暴。

1.3 Discovery references
2. Description

Description 是 UPnP™ 網絡中的步驟 2,在尋址(步驟0)和設備發現(步驟1)之后。

在控制點發現設備之后,控制點仍然對設備知之甚少 - 僅僅能獲取發現消息中涉及到的信息,即設備(或服務)的 UPnP 類型, 設備的通用標識符以及設備 UPnP 描述的 URL。 為了控制點了解有關設備及其功能的更多信息,或與設備交互, 控制點必須從發現消息中由設備提供的 URL 檢索設備及其功能的詳細描述。

設備的 UPnP 描述分為兩個邏輯部分: 一個設備描述(名詞)描述(動詞)物理和邏輯容器,以及描述設備公開的功能的服務描述。 UPnP 設備描述包括特定於供應商的制造商信息, 例如型號名稱和編號,序列號,制造商名稱,特定的供應商的網站的 URL 等(以下詳細信息)。 對於設備中包含的每個服務,設備描述都列出了服務類型,名稱,服務描述的 URL, 控制的 URL 和事件的 URL。 設備描述還包括所有嵌入式設備的描述以及用於展示設備狀態信息或者控制接口的 URL。 本節說明 UPnP 設備說明,而“控制”,“事件”和“演示”部分分別說明如何使用用於控制,事件和演示的 URL。

注意,單個物理設備可以包括多個邏輯設備。 多個邏輯設備也可以組合成一個嵌入式設備上的根設備(或服務)。 當然也可以組合成多個根設備。 在前一種情況下,根設備只有一個 UPnP 設備描述,並且該設備描述包含所有嵌入式設備的描述。 在后一種情況下,有多個 UPnP 設備描述,每個根設備一個。

UPnP 設備的描述由 UPnP 供應商編寫。 該描述采用 XML 格式來表示,通常基於標准的 UPnP 設備模板。 UPnP 論壇工作委員會制作了 UPnP 設備模板; 他們從 UPnP 模板語言(該模板語言是從 XML 的標准構造派生而來)派生模板。 本節說明 UPnP 設備描述,UPnP 設備模板的格式以及 UPnP 模板語言涵蓋設備的部分

UPnP 服務描述包括服務響應的命令或動作的列表以及,每個動作的參數或自變量。 服務描述同樣也包括變量列表,這些變量決定了設備的運行時狀態,同樣服務表述還包括各項數據的數據類型,范圍和事件特征。 本節說明操作,自變量,狀態變量以及這些變量的屬性的描述。而事件的特征將會在事件部分說明。

像 UPnP 設備描述一樣,UPnP 服務描述由 UPnP 供應商編寫。 該描述采用 XML 語法,通常基於標准的 UPnP 服務模板。 UPnP 論壇工作委員會制作了 UPnP 服務模板; 他們從 UPnP 模板語言中獲得了模板,並在必要時使用人類語言對其進行了擴充。 UPnP 模板語言源自 XML 的標准構造。 本節將說明 UPnP 服務描述的格式,UPnP 服務模板,典型的人類語言擴展以及 UPnP 模板語言涵蓋服務的部分。

UPnP 供應商可以通過擴展服務(包括其他 UPnP 服務)或嵌入其他設備來區分其設備。 當控制點檢索特定設備的描述時,這些添加的功能將顯示的提供給控制點,以進行設備的控制和事件處理。 從設備和服務描述也嚴格地記錄了設備的實現的功能。

檢索 UPnP 設備描述很簡單: 控制點使用 HTTP GET 請求去訪問設備描述的 URL(在 Discovery 階段獲取),然后設備即會返回設備對應的描述。 檢索 UPnP 服務描述的過程與在設備描述中使用 URL 的過程一樣。 響應和請求的協議棧,方法,標頭和主體在下面詳細說明。

只要來自設備的發現廣播還沒有過期, 控制點就可以假定該設備及其服務可用。 由於設備和服務描述是靜態的,因此只要設備及其服務可用,就可以在任何時候檢索設備和服務描述。 如果設備取消其廣播或廣播到期,則控制點應假定該設備及其服務不再可用。 如果設備需要更改這些描述之一,則必須取消其未完成的廣播並重新進行廣播。 因此,如果設備重新出現在網絡上,則控制點不應假定設備和服務描述不變。

像發現一樣,描述在使用不同版本的 UPnP 網絡的設備和控制點的互操作性中也起着重要作用。 如發現部分所述,UPnP 設備體系結構既有主要版本也有次要版本。 主版本和次版本是單獨的整數; 即使它們可能出現在打印中,也不應將其解釋為一個帶小數點的數值。 次要版本的提升必須是同一主版本的較早次要版本的兼容超集。 主要版本中的進步不一定是早期版本的超集,也不保證其向后兼容。 版本信息在描述消息中傳遞,作為后備,此信息也存在在設備的發現消息中。 本節將會說明描述消息中版本信息的格式。

設備和服務的標准由 UPnP 論壇工作委員會標准化或由供應商創建。 設備或服務的每個更高版本都必須是先前版本的完全向后兼容的超集, 即,與該設備的早期版本相比,當前版本必須包括前一版本的所有嵌入式設備和服務。 在設備的所有版本中,UPnP 設備或服務類型均相同,而對於更高版本,設備或服務版本必須更大。 在工作委員會的開發過程中,設備和服務模板的版本可能具有非整數版本(例如“ 0.9”), 但是標准化后必須成為整數。 設備和服務的版本號可能大於其設計所針對的體系結構的主要版本號 (例如,“ Power:2”可能被設計為在 UDA 版本1.0上工作); 設備或服務模板的版本與設計用來工作的體系結構的版本之間沒有直接關聯。 如果定義了設備或服務的非向后兼容版本,則該設備或服務必須具有不同的設備或服務名稱, 以指示其不向后兼容(新類型的版本號應從1重新開始)。

UPnP 設備和服務類型可以以各種組合形式組裝成 “building blocks”。 標准設備類型和供應商定義的設備類型都可以嵌入標准設備類型中。 標准和供應商定義的設備類型都可以嵌入在供應商定義的設備類型中。 同樣,標准和供應商定義的服務類型都可以嵌入在標准和供應商定義的設備類型中。 能夠與特定設備或服務類型一起工作的控制點,即使將其嵌入在無法識別的另一種設備類型(標准或供應商定義)中, 也應識別該設備或服務類型。 例如,如果定義了標准服務類型“ Print:1”,並且定義了包含“ Print:1”服務的標准設備類型“ Printer:1”, 控制點希望找到並使用“ Print:1” 服務應,無論服務是嵌入在 “urn:schemas-upnp-org:device:Printer:1” 設備中, 還是嵌入在供應商定義的 “urn:acme-com:device:Printer:1” 中, 或者 “urn:acme-com:device:AcmeMultifunctionPrinter:1”

本節的其余部分首先說明如何描述設備,並詳細說明特定於供應商的信息, 嵌入式設備以及用於控制,事件和顯示的URL。 其次,它說明了 UPnP 設備模板。 第三,它解釋了如何描述服務,解釋了動作,參數,狀態變量以及這些變量的屬性的詳細信息。 然后說明 UPnP 服務模板和 UPnP 模板語言。 最后,本節詳細說明控制點如何從設備檢索設備和服務描述。

2.1 Description: Device description

設備的 UPnP 描述包含幾條特定於供應商的信息,所有嵌入式設備的定義,用於設備顯示的 URL,所有服務的列表以及用於控制和事件的URL。 除了定義非標准設備(供應商定義的設備和標准嵌入式設備和服務)之外, UPnP供應商還可以提供這些信息,以下是實際元素和值的占位符列表(斜體)。 其中一些占位符將由 UPnP 論壇工作委員會(紅色)或 UPnP 供應商(紫色)指定。 對於非標准設備,所有這些占位符將由 UPnP 供應商指定。 (由UPnP設備體系結構定義的元素顯示為綠色,以備后用。)緊隨清單之后的是元素,屬性和值的詳細說明。

下面列出的是上面列表中出現的每個元素,屬性和值的詳細信息。 所有元素和屬性都區分大小寫; HTTP 指定 URL 也區分大小寫; 除非另有說明,否則其他值不區分大小寫。 元素的順序無關緊要。 除非另有說明:必需元素必須只出現一次(沒有重復), 推薦或可選元素最多也只能出現一次。 請注意,某些實現可能會嚴格執行以下各個元素的長度限制,因此建議工作委員會注意所有指定的限制。

  • xml Required | 此字段區分大小寫。
  • root Required | 此元素必須設置 xmlns 屬性,而且值必須是 urn:schemas-upnp-org:device-1-0, 這引用了 UPnP 模板語言(如下所述)。 該元素下的子元素一起描述了根設備,區分大小寫。其子設備類型如下:
    • specVersion Required | 包含下列元素
      • major Required | UDA 的主版本號,必須為 1
      • minor Required | UDA 的子版本號. 對於實現 UDA 1.0 版本的設備此元素的值 必須為 0
      這兩個子元素必須准確的反應所實現的 UDA 的版本。而對於控制端,則必須准備接受設備端的版本高於自身的情況。
    • URLBase Optional | 定義 Base URL。 用於構造標准 URL。根據 RFC 2396 中的規則,描述中其他位置出現的 URL 將是 Base URL 的相對路徑, 在使用其他 URL 的時候需要先與 Base URL 拼接成一個完整的 URL。 如果 URLBase 為空或未給出,則可以直接訪問其他位置出現的 URL(這是現在的首選實現,不再建議使用 URLBase)。 由UPnP供應商指定。| Single URI。
    • device Required. 包含下面的元素:
      • deviceType REQUIRED | UPnP 設備類型. Single URI.
        • 對於由 UPnP 論壇工作委員會定義的標准設備,必須以 "urn:schemasupnp-org:device""開頭, 然后是標准化設備類型后綴,冒號和整數設備版本,即 urn: schemas-upnp-org:divice deviceType:ver。 必須指定設備類型的最高支持版本。
        • 對於UPnP供應商指定的非標准設備,必須以 "urn:" + 供應商域名 + ”:device:” + ”deviceType“ + 冒號 + 整數版本, 即“ urn:域名:device:deviceType:ver”。 供應商域名中的句點字符必須根據 RFC 2141 替換為連字符。 必須指定設備類型的最高支持版本。
        設備類型的后綴由 UPNP 委員會定義,或者由設備供應商定義,長度必須小於 64 字節(版本號和冒號不在計數范圍)。
      • friendlyName Required | 一個對終端用戶友好的簡短描述,應支持本地化語言(參見. ACCEPT-LANGUAGE 和 CONTENTLANGUAGE 頭). 由設備供應商指定。該元素值必須小雨 64 字節 | String 類型。
      • manufacturer Required | 制造商名字. 應該支持本地化 (參見. ACCEPT-LANGUAGE 和 CONTENTLANGUAGE 頭). 由設備供應商指定。該元素值必須小雨 64 字節 | String 類型。
      • manufacturerURL Optional | 制造商的主頁,應該支持本地化 (參見. ACCEPT-LANGUAGE 和 CONTENTLANGUAGE 頭). 由設備供應商指定。該元素值必須小雨 64 字節 | String 類型。
      • modelDescription Recommended | 關於設備模塊的詳細描述, 應該支持本地化 (參見. ACCEPT-LANGUAGE 和 CONTENTLANGUAGE 頭). 由設備供應商指定。該元素值必須小雨 128 字節 | String 類型。
      • modelName Required | 設備模塊名字, 應該支持本地化 (參見. ACCEPT-LANGUAGE 和 CONTENTLANGUAGE 頭). 由設備供應商指定。該元素值必須小雨 32 字節 | String 類型。
      • modelNumber Recommended | 設備模塊數量, 應該支持本地化 (參見. ACCEPT-LANGUAGE 和 CONTENTLANGUAGE 頭). 由設備供應商指定。該元素值必須小雨 32 字節 | String 類型。
      • modelURL Optional | 模塊的介紹網址, 應該支持本地化 (參見. ACCEPT-LANGUAGE 和 CONTENTLANGUAGE 頭). 由設備供應商指定。該字段有可能是資源相對與 BaseURL 的相對位置 | String 類型。
      • serialNumber Recommended | 設備序列號, 應該支持本地化 (參見. ACCEPT-LANGUAGE 和 CONTENTLANGUAGE 頭). 由設備供應商指定。該元素值必須小雨 64 字節 | String 類型。
      • UDN Required | 唯一的設備名稱,設備的通用唯一標識符(與根標識符還有嵌入式標識符也保持唯一性)。 在設備的生命周期內,該元素的值應該保持不變(即重啟后任然有效,恢復出廠設置之后可以變更), 必須與設備發現消息中 NT 標頭的值匹配。 在所有發現消息中,必須與 USN 標頭的前綴匹配。 (關於發現的部分說明了 NT 和 USN 標頭)。 必須為以下格式( ”uuid:“ + PnP 供應商指定的UUID后綴) | 單個URI。
      • UPC Optional | 通用產品代碼(Universal Product Code),由 12 個數字組成, 標識消費者包裝的代碼。 由統一代碼委員會管理(the Uniform Code Council)。 由 UPnP 供應商指定 | Single UPC。
      • iconList 如果產品有自己的 ICON, 那么此字段就需要存在。 有供應商自己指定,包含以下子元素:
        • icon Recommended | 用於在控制點 UI 中描繪設備的圖標。 應該支持本地化 (參見. ACCEPT-LANGUAGE 和 CONTENTLANGUAGE 頭). ICON 的大小是取決於供應商設計。包含以下子元素:
          • mimetype Required | Icon 的 MIME type, (參考 RFC 2045, 2046, and 2387) | Single MIME image type | 至少應該包含 image/png (Portable Network Graphics, see IETF RFC 2083) 類型
          • width Required | icon 的水品大小(寬) | 單位為像素 | Integer
          • height Required | icon 的垂直大小(高) | 單位為像素 | Integer
          • depth Required | icon 每個像素的(色彩深度) | Integer
          • url Required | img 的 URL( XML 不支持直接嵌入圖片在描述文檔中. 參看下面的注釋) | 該字段有可能是資源相對與 BaseURL 的相對位置 | 由 UPNP 供應商指定 | Single URL.
      • serviceList Optional | 包含以下子元素:
        • service Optional | 對於 UPNP 委員會定義的每個服務都要有描述, 對於供應商額外自定義的用來區分設備的服務也需要在這里有描述。 該服務元素有一下屬性。
          • serviceType Required | 服務類型 | 必須包含一個 23 位 UTF-8 編碼的散列值 | Single URI.
            • 對於由 UPnP 委員會定義的標准服務類型, 必須遵守以下格式( “urn:schemas-upnp-org:service" + 標准服務類型后綴 + : + 整數服務版本), 即 urn: schemas-upnp-org:device:serviceType:ver 。 必須指定服務的最高支持版本。
            • 對於非標服務,則必須遵守以下格式 ( "urn:" + 供應商的域名 + ”:service:“ + 服務類型后綴 + 服務版本號-整數 ),供應商域名中的句點字符必須根據RFC 2141替換為連字符。必須指定服務類型的最高支持版本。
            這些服務的類型有 UPnP 委員會或者供應商定義,長度必須小於 64 字節 ( 類型后綴和冒號分隔符不包括在內 )。
          • serviceId REQUIRED | 服務的唯一標識 | 在整個設備描述中必須是唯一的 | Single URI.
            • 對於 UPNP 委員會定義的標准服務,這里必須是服從以下格式 ( urn:upnp-org:serviceId: + 服務的 ID 后綴 )(如 urn:upnporg:serviceId:serviceID )。 如果這個實例在上面指定的服務類型(如: <serviceType>)屬於上面指定的設備類型(如: <deviceType>)之一, 則服務 ID 后綴的值必須為設備 ID 為該服務實例定義的服務 ID。 否則,服務 ID 后綴的值是供應商定義的。 (請注意,在這種情況下,使用 upnp-org 代替了 schemas-upnp-org,因為沒有為每個服務ID定義XML模式。)
            • 對於非標設備,必須服從以下格式(”urn:“ + 供應商域名 + ":serverId:" + 服務 ID 后綴), 例如: “urn:domain-name:serviceId:serviceID”。 如果這個實例在上面指定的服務類型(如: <serviceType>)屬於上面指定的設備類型(如: <deviceType>)之一, 則服務 ID 后綴的值必須為設備 ID 為該服務實例定義的服務 ID。 否則,服務 ID 后綴的值是供應商定義的。 (請注意,在這種情況下,使用 upnp-org 代替了 schemas-upnp-org,因為沒有為每個服務ID定義XML模式。) 域名中的句點字符必須根據 RFC 2141 替換為連字符。
            這里的設備 ID 后綴由 UPNP 委員會或者供應商定義,必須小於 64 字節。
          • SCPDURL Required | 詳細描述此服務的 URL, (nee 服務控制協議定義 URL). (參考以下服務描述章節的說明 ) 又 UPNP 供應商指定 | Single URL.
          • controlURL Required | 用於控制的 URL (參考以下控制章節的說明). 此字段有可能是 BaseURL 的相對路徑 | 有供應商指定 | Single URL.
          • eventSubURL Required | 事件的 URL(請參閱事件部分)。 可能是 BaseURL 的相對路徑。 設備內必須唯一; 沒有兩個服務可以具有用於事件的相同 URL。 如果服務沒有事件變量,則不應包含事件(請參見事件部分); 如果服務沒有事件,則此元素必須存在但應為空,即<eventSubURL></ eventSubURL>。 | 由UPnP供應商指定 | Single URL.
      • deviceList 當此根設備擁有嵌入設備的時候,此元素存在。該元素擁有以下子元素:
        • device Required | 有供應商決定 | 沒有嵌入設備都必須擁有一個描述, 如果供應商這里的子元素的定義和上面根設備包含的子元素一致。
      • presentationURL Recommended | 展示設備狀態和控制界面的 URL (參考展示章節). 此字段有可能是 BaseURL 的相對路徑 | 有供應商指定 | Single URL.

控制點在識別和與設備交互時使用的 serviceID 應該要與設備定義的值不同。 如果存在一個服務有多個實例,則控制點應默認(除非用戶操作另有指示)應使用與設備類型定義的 serviceId 值關聯的服務實例。 如果服務的所有實例都不具有由設備類型定義的 serviceId 值,則控制點可以使用任何服務實例。 當僅存在一個服務實例時,即使 serviceId 值與設備類型定義的值不匹配,控制點也應使用該實例。

為看可擴展性,當像上面的清單那樣處理 XML 時,設備和控制點必須忽略: (a)任何未知元素及其子元素或內容 以及 (b)任何未知屬性及其值。

控制點和設備應忽略它們不理解的 UPnP 設備和服務描述中嵌入的任何 XML 注釋或 XML 處理指令。

UPnP設備描述應使用UTF-8編碼。

當任何文本元素或屬性的值包含一個或多個保留標記的字符時(例如 ampersand(&)或 less than(“ <”)), 必須按照XML規范第2.4節的規定對文本進行轉義,並將每個此類字符替換為等效的數字表示形式或字符串(例如“&”或“<”)。 出現在 URL 中的此類字符也可以根據 RFC 2396 第 2.4 節中指定的 URL 轉義規則進行轉義。

XML 不支持直接將二進制數據(例如,圖標)嵌入 UPnP 設備描述中。 如果一定要嵌入,可以使用 bin.base64(二進制數據的 MIME-style 的 base 64 編碼)或 bin.hex(十六進制數字代表八位字節)的 XML 數據類型將二進制數據轉換為文本。 或者,可以通過在 XML 中嵌入 URL 並響應單獨的 HTTP 請求來傳輸數據來間接傳遞數據。 UPnP 設備描述中的圖標以后一種方式傳輸。

如果有包括任何圖標,則至少應該采用 RFC 2083 中���義的可移植網絡圖形(PNG)格式, 由 MIME 類型“ image/png” 指示,而且不應該采用 progressive encoding 編碼方式。 由於各種控制點偏愛的種類繁多,因此不建議使用特定的圖標大小。 鼓勵控制點供應商發布實施指南。

請注意,在 UPnP 設備體系結構的 1.0 版中, serviceList 元素是必需的,並且它至少包含一個服務元素。 但隨后的版本取消了這些要求,以適應網關和基本設備類型。 如果設備沒有服務,則 serviceList 元素可以完全省略,也可以存在但不包含服務元素。

2.2 Description: UPnP Device Template

上面的清單說明了 UPnP 設備描述和 UPnP 設備模板之間的關系。 如上所述,UPnP 設備描述是由 UPnP 供應商按照 UPnP 設備模板以 XML 編寫的。 UPnP 委員會制作了 UPnP 設備模板,作為標准化設備的一種方法。

通過選擇適當的占位符,上面列出的內容可以是 UPnP 設備模板或 UPnP 設備描述。 其中,某些占位符由 UPnP 論壇工作委員會(紅色)定義(如:UPnP 設備類型標識符 / UPnP 服務 / UPnP 嵌入式設備(如果有的話))。 如果這些被編寫到代碼中,則生成的 XML 文檔即是 UPNP 設備模板。 UPnP 設備模板是 UPnP 論壇工作委員會的主要成果之一。

更進一步,由 UPnP 供應商(紫色)指定上面列表中的其余占位符, 即特定於供應商的信息。 如果在代碼中指定了這些占位符(以及其他占位符), 則列表將是 UPnP 設備描述,適合傳遞給控制點以用來后續的控制 / 事件 / 演示。

換句話說,UPnP 設備模板定義了設備的整體類型, 每個 UPnP 設備描述都使用供應商特定的信息實例化該模板。 前者 UPnP 論壇工作委員會定義, 而后者由 UPnP 供應商定義。

2.3 Description: Service description

PnP 描述定義了服務的可執行動作, 參數,狀態變量及其數據類型,范圍和事件特征。

每個服務可能有零個或多個動作。 每個動作可以具有零個或多個參數。 這些參數可以是輸入參數或也可以是輸出參數。 如果一個動作具有一個或多個輸出自變量, 則這些自變量中的一個可被標記為返回值。 每個參數應對應一個狀態變量。 這給直接操縱的編程模型增強了簡單性。

每一個服務必須有一個或者多個狀態變量。

為了定義非標服務,UPnP 供應商可能會增加自己定義的 action 或者 service 給標准設備, 而且也可能嵌入非標准設備到標准設備。

為了說明這些觀點,下面列出了包含實際元素的占位符(以斜體顯示)和值。 對於標准的 UPnP 服務,其中一些占位符將由 UPnP 論壇工作委員會定義(紅色)或由UPnP供應商指定(紫色)。 對於非標准服務,所有這些占位符將由 UPnP 供應商指定。 (由UPnP設備體系結構定義的元素顯示為綠色,以備后用。)緊隨清單之后的是元素,屬性和值的詳細說明。

下面列出的是上面列表中出現的每個元素,屬性和值的詳細信息。 所有元素和屬性(包括動作,參數和狀態變量名稱)均區分大小寫, 順序也無關緊要,必需元素必須僅出現一次(沒有重復),推薦或可選元素最多只能出現一次, 除非另有說明。

  • xml所有的 XML 文檔都大小寫敏感
  • scpd Required | Must | 屬性 xmlns 的值必須是 urn:schemas-upnp-org:service-1-0; 這引用了UPnP模板語言(如下所述)。下面詳細介紹該元素的子元素:
    • specVersion Required | 包含以下子元素:
      • major Required | UDA 的主版本號,必須為 1
      • minor Required | UDA 的子版本號. 對於實現 UDA 1.0 版本的設備此元素的值 必須為 0
      這兩個子元素必須准確的反應所實現的 UDA 的版本。而對於控制端,則必須准備接受設備端的版本高於自身的情況。
    • actionList Required ( 如果服務擁有 action ) (每一個服務必須包含 >= 0 個 action), action 的子元素有如下幾種:
      • action Required | 對 UPnP 委員會定義的每個操作都要實現一次。 如果 UPnP 供應商需要通過添加其他 action 來區分服務, 則也要為添加的 action 實現在此處。一個 action 包含以下子元素:
        • name Required | action 的名字, 不得包含連字符(-,UTF-8 中為 0x2D )或 哈希字符(#,UTF-8 中為 0x23)。 | 區分大小寫 | 第一個字符必須是 USASCII 字母(A-Z,a-z)/ USASCII 數字(0-9) / 下划線(“ _”)/ 大於 U+007F 的非實驗性 Unicode 字母或數字。
          支持的字符必須在以下范圍 USASCII 字母(A-Z,a-z) / USASCII 數字(0-9) / 下划線(“ _”) / 句點(“.”) / Unicode組合字符 / 擴展程序或非實驗性 Unicode 大於 U+007F 的字母或數字。 在任何情況下,前三個字母不得為“ XML”。
          • 對於標准的 action 必須以 X_ 或者 A_ 開頭。
          • 對於非標 action, 但是是添加到標准設備中的 action, 則必須以 X_ 開頭。
          String | 必須小於 32 個字符。
        • argumentList Required (當且僅當 action 定義了要操作的參數時)( 每一個 action 可能會有 >= 0 個參數),參數有以下的子元素
          • argument Required | 每一個參數都擁有這樣的一個元素,該元素又如下子元素:
            • name Required | 參數的名字 | 不得包含連字符(UTF-8中為 '-',0x2D)。 第一個字符必須是 USASCII 字母(A-Z,a-z), USASCII 數字(0-9), 下划線(“ _”)或大於 U+007F 的非實驗性 Unicode 字母或數字。 中間包含的字符必須在以下范圍內: USASCII 字母(A-Z,az) / USASCII數字(0-9) / 下划線(“ _”),句點(“.”),Unicode 組合字符,擴展程序或非實驗 Unicode 大於 U+007F的字母或數字。 任何組合中,前三個字母不得為“ XML”。 應小於32個字符。
            • direction Required | 參數是輸入參數還是輸出參數。 值必須是 in or out。
            • retval Optional | 最多將一個 out 參數指定為返回值。 如果在一個 action 中指定了一個返回值, 則該參數在文檔順序中一定要是在 out 類型的參數列表中排第一。
            • relatedStateVariable Required | 該參數關聯的狀態變量名稱,
    • serviceStateTable Required | 每一個服務包含大於 0 個狀態變量,該狀態變量的元素對象包含如下元素:
      • stateVariable Required | 對 UPnP 委員會定義的每個狀態變量都要在此實現。 如果 UPnP 供應商有通過添加其他狀態變量來區分服務, 則每個其他變量也需要在此實現。 sendEvents 屬性定義當此狀態變量的值更改時是否生成事件消息; 非事件狀態變量具有 sendEvents = “no”; 默認值為 sendEvents = yes。 該狀態變量的元素對象包含如下元素:
        • name Required | 狀態變量的名字, 不得包含連字符(-,UTF-8 中為 0x2D )或 哈希字符(#,UTF-8 中為 0x23)。 | 區分大小寫 | 第一個字符必須是 USASCII 字母(A-Z,a-z)/ USASCII 數字(0-9) / 下划線(“ _”)/ 大於 U+007F 的非實驗性 Unicode 字母或數字。
          支持的字符必須在以下范圍 USASCII 字母(A-Z,a-z) / USASCII 數字(0-9) / 下划線(“ _”) / 句點(“.”) / Unicode組合字符 / 擴展程序或非實驗性 Unicode 大於 U+007F 的字母或數字。 在任何情況下,前三個字母不得為“ XML”。
        • dataType Required | 與 XML Schema 第 2 部分《數據類型定義》的數據類型相同。 由 UPnP 委員會為標准狀態變量定義, 或者由UPnP供應商指定用於擴展。 必須為以下值之一:
          • ui1 1 Byte 無符號整形。 數值范圍 [0, 255] 。
          • ui2 2 Byte 無符號整形。 數值范圍 [0, 65535]。
          • ui4 4 Byte 無符號整形。 數值范圍 [0, 4294967295] 。
          • i1 1 Byte 有符號整形。 數值范圍 [-128 , 127 ]。
          • i2 2 Byte 有符號整形。 數值范圍 [-32768 ,32767 ]。
          • i4 4 Byte 有符號整形。 數值范圍 [-2147483648, 2147483647] 。
          • int Fixed point, integer number. May have leading sign. May have leading zeros. (No currency symbol.) (No grouping of digits to the left of the decimal, e.g., no commas.)
          • r4 4 字節浮點型,格式和 float 一樣, 數值范圍在 [ 3.40282347E+38 , 1.17549435E-38 ] 之間。
          • r8 8 字節浮點型,格式和 float 一樣, 復數數值范圍在 [ -1.79769313486232E308, -4.94065645841247E-324 ] 之間。正數在 [4.94065645841247E-324 , 1.79769313486232E308 ] 之間。
          • number Same as r8.
          • fixed.14.4 Same as r8, 但是不會超過 14 個數字在小數點左邊,不會超過 4 個數字在小數點右邊。
          • float Floating point number. Mantissa (left of the decimal) and/or exponent may have a leading sign. Mantissa and/or exponent may have leading zeros. Decimal character in mantissa is a period, i.e., whole digits in mantissa separated from fractional digits by period. Mantissa separated from exponent by E. (No currency symbol.) (No grouping of digits in the mantissa, e.g., no commas.)
          • char Unicode string. 一個字節。
          • string Unicode string.不限長度。
          • date 格式遵從 ISO 8601 標准,但不包括 time data 類型.
          • dateTime 格式遵從 ISO 8601 標准,但不包括 timezone.
          • dateTime.tz 格式遵從 ISO 8601 標准,time 和 timezone 都是可選的。
          • boolean 值包含兩個值 0 / 1, 0 代表 假,1 代表真,”true“ / ”false“, ”yes“ / ”no“ 也可以使用,但是不推薦。
          • bin.base64 MIME 樣式的 Base64 編碼的二進制 BLOB。 每 3 個字節分成 4 個 par,每一 Par 占 6 個 bit。 然后將 6 個 bit 映射到 8 個 bit 中。大小不受限制。
          • bin.hex 將一個字節拆分成兩個十六進制字符表示,每 4 個 bit 對應一個 字符。 大小不受限制。
          • bin.uri uri
          • bin.uuid UUID( niversally Unique ID )。 代表八位位組的十六進制數字。 可選的嵌入式連字符將被忽略。
        • defaultValue Recommended | 初始值, 由 UPnP 委員會定義或者 UPnP 供應商定義. 必須符合以上的數值類型,必須在以下提到的 allowedValueList 或者 allowedValueRange 內。
        • allowedValueList Recommended | 枚舉合法的字符串值。 禁止使用非字符串數據類型。 該字段與allowedValueRange 互斥。 子元素是有序的(參見NEXT_STRING_BOUNDED)。 包含以下子元素:
          • allowedValue Required | 一個合法的字符串值,對於標准的狀態變量,這里的值由 UPnP 委員會定義,對於 UPnP 擴展的則又供應商自己決定。 該字段的值必須小於 32 個字符。
        • allowedValueRange Recommended. 定義一個合法的數值范圍,該字段與 allowedValueList 互斥。 包含以下子元素:
          • minimum Required | 可選范圍的下邊界, 由 UPnP 委員會定義,對於 UPnP 擴展的則又供應商自己決定。 此處為單個數值。
          • maxiumum Required | 可選范圍的上邊界, 由 UPnP 委員會定義,對於 UPnP 擴展的則又供應商自己決定。 此處為單個數值。
          • step Recommended | 每一次 increment 或者 decrement 是的步進。例如在操作 v = v + s 中 s 的值。 由UPnP論壇工作委員會定義或委托給UPnP供應商。 單個數值。

元素 relatedStateVariable 的值必須是定義在同一服務里狀態變量的名稱。 relatedStateVariable 定義了參數的類型; argument 和 relatedStateVariable 之間不一定存在任何語義關系。 relatedStateVariable 必須在 serviceStateTable 中指定 stateVariable 的名稱,該名稱具有的 dataType, allowedValueList 和 allowedValueRange。 如果不存在帶有適當定義的 stateVariable, 則工作委員會(或供應商)必須為此目的定義其他狀態變量; 用於描述參數類型而定義的狀態變量的名稱的前綴應該是 “ A_ARG_TYPE_”。

allowedValueList 和 allowedValueRange 元素可用於指示設備可選的功能。 UPnP 委員會可能要求列表或范圍中的所有值都應由所有供應商支持(無選項),或帶有最小可選值的最小子集,或允許供應商完全決定。 如果委員會允許,供應商可以將供應商定義的 allowedValues 添加到標准 allowedValueList 中(名稱前綴必須為 'X_')。 但是,應注意,定制的可選功能應該盡可能的減少了控制點可能依賴的值的數量,從而防止影響到交互操作時的兼容性。 如果設備功能會在設備運行期間發生變化,則委員會應定義單獨的 action 來檢測設備功能, 而不是將區分功能的信息嵌入到服務描述中。 而每次服務描述文檔更改時,設備都應該取消之前的廣播並重新發布新的功能的廣播。 如果服務描述用於傳達功能信息,則設備必須從服務描述中省略未實現的可選元素(動作,allowedValues等)。

為了將來的可擴展性,當像上面的清單那樣處理 XML 時,設備和控制點必須忽略: (a)任何未知元素及其子元素或內容,以及(b)任何未知屬性及其值。

控制點和設備應忽略嵌入在 XML 描述文檔中的注釋,和控制點本身不支持或者不懂的那些描述。

UPnP 服務描述應使用 UTF-8 編碼。

任何屬性或者元素的值都包含一個或者多個保留字(即 '&' 符號或者是 '<'), 如一定要使用此類字符則必須按照 XML 規范的 2.4 節的規定對文本進行轉義, 並用等效的數字表示形式或字符串替換每個此類字符(例如 '& amp;” 或 “& lt;”)。 出現在 URL 中的此類字符也可以根據 RFC 2396 第 2.4 節中指定的 URL 轉義規則進行轉義。 注意,服務在邏輯上可能沒有任何動作,但具有狀態變量和事件; 盡管這種服務不太可能,但是它將成為自治信息源。 但是注意,禁止使用沒有狀態變量的服務。

與設備描述不同,服務描述和關聯的值不應使用 locale-specific(本地化) 的值。 應用程序應將信息從標准格式轉換(格式化)為語言環境的正確語言(或格式)。 例如,日期以與語言環境無關的格式(ISO 8601)表示,而整數則以沒有語言環境特定的格式表示(例如,沒有貨幣符號,沒有數字分組)。 字符串值應以標准的 “語言環境” 或與語言環境無關的方式表示。 具有 allowedValueList 的變量應使用 UPnP 標准語言的標記值。

但是,在某些情況下,動作的行為取決於語言環境。 在這種情況下,應該定義一個參數來指示語言環境, 比如可以使用 ACCEPT-LANGUAGE 和 CONTENT-LANGUAGE 標頭(RFC 1766)。 如果存在多個依賴於語言環境的 action,則該服務可以包括一個操作, 該操作用於設置狀態變量以指示語言環境,以消除了將語言環境標識符分別傳遞給每個操作的需要。

UPnP 委員會標准化的服務具有整數版本。 服務的每個更高版本都必須是先前版本的超集, 即,它必須完全包含服務的早期版本所定義的所有操作和狀態變量。 UPnP 服務類型在服務的所有版本中都相同,而對於更高版本,該服務版本必須更大。 在工作委員會的開發過程中,設備和服務模板的版本可能具有非整數版本(例如“ 0.9”),但是標准化后必須成為整數。 設備和服務的版本號可能大於其設計所針對體系結構的主要版本號,但是設備服務也要應該響應低版本控制端的指令 (例如,“ Power:2” 可能被設計為在 UDA 版本 1.0 上工作)。

2.4 Description: UPnP Service Template

上面的清單還說明了 UPnP 服務描述和 UPnP 服務模板之間的關系。 如上所述,服務的 UPnP 描述是由 UPnP 供應商按照 UPnP 服務模板以 XML 編寫的。 UPnP 委員會制作了 UPnP 服務模板,作為標准化設備的一種方法。

通過適當的占位符規范, 上面的清單可以是 UPnP 服務模板也可以是 UPnP 服務描述。 回想一下,某些占位符將由 UPnP 論壇工作委員會(紅色)定義,即 action 及其 arguments,status 及其 dataType, range 和 event characteristics 。 如果將上面指定的這些實例化為代碼,那么這就是一份 UPnP 服務模板, UPnP 服務模板是 UPnP 論壇工作委員會的主要交付成果之一。

更進一步,如果 UPnP 供應商(紫色)指定了上面列表中的其余占位符, 即由供應商指定的其他 action 和 status variables。 如果指定了這些占位符(以及其他占位符), 則列表將是 UPnP 服務描述,適用於對設備內服務的有效控制。

換句話說,UPnP 服務模板定義了服務的整體類型, 而 UPnP 服務描述則在模板的基礎上包含了供應商的添加特定的特性。 第一個由 UPnP 委員會創建; 后者由 UPnP 供應商提供。

2.5 Description: Non-standard vendor extensions

如上所述,UPnP 供應商可以通過添加其他服務和嵌入式設備來區分其設備並擴展標准設備。 同樣,UPnP 供應商可以通過添加其他操作或狀態變量來擴展標准服務。 下表中列出了每種方法的命名約定,並在上面進行了詳細說明。

如上表的最后兩行所示, UPnP 供應商還可以將非標准 XML 添加到設備或服務描述中。 每個添加項都必須由供應商的 XML namespace 限制。 供應商添加的 XML 元素必須包含在以 X_ 開頭的元素中, 並且該元素必須是標准元素的子元素。 可以將非標准屬性添加到標准元素中, 前提是這些屬性的元素名稱是 X_ 開頭的。

為了說明這一點, 下面列舉了實際元素和值的占位符(斜體)。 以及一些 UPnP 供應商指定(紫色)字段替換后的樣例。 和一些則由 UPnP 設備體系結構定義的樣例(綠色)。

  • RootStandardElement Required | 一個標准的根元素. xmlns 屬性定義了 namespaces。 在這個案例中,描述了一個標准 UPnP namespace 和一個以 'n' 為前綴的非標 namespace。
    • 注意對於設備的描述,這個描述必須存在 root 元素中。
    • 注意對於設備的描述,這個描述必須存在 scpd 元素中。
    AnyStandardElement
    Required | 任何標准元素,無論是根元素還是其他元素,文本的內容或者元素的內容。 必須作為標准設備或標准服務服務的一部分。 X_VendorAttribute 必須以 X_ 開頭,后可以接任意字符串。 (前綴 A_ 是保留的。)
  • EltOnlyStandardElement Required | Element with content of element only. Must already be included as part of the standard device or service description. 僅包含元素內容的元素。 這些元素必須包含在標准設備或標准服務描述中。
    • 對於設備的描述,必須是其中之一: root, specVersion, device, iconList, icon, serviceList, service, and/or deviceList.
    • 對於服務的描述,必須是其中之一: scpd, actionList, action, argumentList, argument, serviceStateTable, stateVariable, allowedValueList, and/or allowedValueRange.
    X_VendorElement
    Required | 必須是以 'X_' 開頭( 前綴 ‘A_’ 為保留字)。必須有 xmlns 屬性. 可能還包括其他任意的 XML 內容。

如果控制點不理解自定義的一些標簽,那么控制點應該忽略之。

2.6 Description: UPnP Template Language for devices

上面的段落解釋了 UPnP 設備描述,並說明了如何從 UPnP 設備模板中實例化該描述。 如前所述,UPnP 設備模板是由 UPnP 論壇工作委員會制作的,這些模板是從 UPnP 模板語言衍生而來的。 該模板語言定義了設備和服務的有效模板。以下是與設備有關的該語言的清單和說明。

UPnP 模板語言是用 XML 語法編寫的,並且派生自 XML Schema(第1部分:結構,第2部分:數據類型)。 XML 模式提供了一組 XML 的結構,這些結構表達(展示/闡述)了描述語言概念, 例如必需元素與可選元素,元素嵌套和值的數據類型(以及此處不感興趣的其他屬性)。 UPnP 模板語言使用這些 XML 架構構造來定義元素,例如以上詳細列出的 specVersion,URLBase,deviceType 等。 因為 UPnP 模板語言是使用另一種精確的語言構造的,所以它是明確的。 並且由於 UPnP 模板語言,UPnP 設備模板和 UPnP 設備描述都是機器可讀的, 因此自動化工具可以自動檢查以確保后兩個具有所有必需的元素,正確地嵌套並且具有正確的數據類型的值。

下面是由 UDA 定義的設備的 UPnP 模板。 這些元素在 UPnP 設備模板中有介紹(這里和上面的解釋都用綠色表示); Below is where these elements are defined; above is where they are used.

緊隨其后的是對使用的 XML Schema 元素,屬性和值的簡要說明。 本節末尾的 XML Schema 參考有更多詳細信息。

UPnP Template Language for devices
  • ElementType 用新的派生語言定義元素。 name 屬性定義元素名稱。 dt:type 屬性使用新的派生語言為 element 的值定義數據類型。
  • element 使用嵌套的方式來引用元素。 minOccurs 屬性定義元素必須出現的最小次數。 默認值為 minOccurs = 1; 可選元素的 minOccurs =0。maxOccurs屬性定義元素必須出現的最大次數。 默認值為maxOccurs = 1; 可以出現一次或多次的元素具有 maxOccurs = *。 Content =“textOnly” 表示元素不包含子元素; content =“eltOnly”表示元素包含子元素。
2.7 Description: UPnP Template Language for services

本章節介紹服務描述是如何從 UPnP 服務模板中實例化到的。像設備模板一樣,UPnP 的服務模板也是有 UPnP 委員會定義的每個操作都要實現一次。 服務模板和設備模板也都是由 UPnP 模板語言衍生而來,像上面介紹的一樣,UPnP 模板文檔服從 XML 的語法格式(由 XML Schema 衍生而來), (這里主要產靠 Part1: struct 和 Part 2: DataTypes)。 下面是一個 service 描述模板的例子,這里綠色代表涉及的內容對應上面提到的內容。 下面定義的元素也揭示了他們的用法。

緊隨其后的是對使用的 XML Schema 元素,屬性和值的簡要說明。本節末尾的 XML Schema 參考有更多詳細信息。

UPnP Template Language for services
  • attribute 引用屬性以聲明該可能出現在哪些元素中。 像任何 XML 元素一樣,AttributeType 元素可以具有自己的屬性。 在此元素內使用 required 屬性指示該屬性是否必須存在; 可選屬性 required=no。
  • AttributeType 定義一個新的 AttributeType,像任何XML元素一樣,AttributeType元素可以具有自己的屬性。 在此元素中使用 name 屬性定義屬性名稱,因為它將在派生語言中使用。
  • element 使用嵌套的方式來引用元素。 minOccurs 屬性定義元素必須出現的最小次數。 默認值為 minOccurs = 1; 可選元素的 minOccurs =0。maxOccurs屬性定義元素必須出現的最大次數。 默認值為maxOccurs = 1; 可以出現一次或多次的元素具有 maxOccurs = *。 Content =“textOnly” 表示元素不包含子元素; content =“eltOnly”表示元素包含子元素。
  • ElementType 定義一個新的 element。 name 屬性定義元素名稱。dt:type 屬性使用新的 element 的值定義數據類型。 model 定義此處是否有一個新的 elements, 屬性指示新的派生語言中的元素是否可以包含此處未明確指定的元素; 如果僅可以使用以前的特定元素,則 model=closed。 content 屬性指示可能包含的內容; 僅包含其他元素的元素具有 content = eltOnly; 僅包含字符串的元素的content = textOnly。
    • group 將內容組織為一組以指定序列。 minOccurs 屬性定義組必須出現的最小次數。 maxOccurs 屬性定義組必須出現的最大次數。 順序屬性限制元素的順序; 當最多允許一個元素時,order = one。
2.8 Description: Retrieving a description

如上所述,控制點發現設備后,仍然對設備知之甚少。 要了解有關設備及其功能的更多信息,控制點必須使用設備在發現消息中提供的 URL 檢索設備的 UPnP description(描述)。 然后,控制點必須使用設備描述中提供的 URL 檢索一個或多個服務描述。 這是一個簡單的基於 HTTP 的過程,並使用了整個 UPnP 協議堆棧的以下子集。 (本文檔開頭列出了整個UPnP協議棧。)

在協議的最上層,描述消息包含特定於廠商的信息,例如,設備類型,服務類型和所需的服務。 從協議棧向下移動,供應商內容將由 UPnP 論壇工作委員會提供的信息進行補充,例如型號名稱,型號編號和特定的 URL。 來自以上各層的消息以本文檔中定義的特定於 UPnP 的協議托管。 反過來,上述消息通過基於 IP/TCP 上的 HTTP 傳遞。 作為參考,上面[方括號]中的顏色表示在下面列出的描述消息中哪個協議定義了特定的標頭和主體元素。

使用此協議棧,檢索UPnP設備描述非常簡單: 控制點向發現消息中的 URL 發出 HTTP GET 請求,並且設備在 HTTP 響應的正文中返回其描述。 類似地,為了檢索 UPnP 服務描述,控制點向設備描述中的 URL 發出 HTTP GET 請求,然后設備在 HTTP 響應的正文中返回該描述。 響應和請求的標頭和正文將在下面詳細說明。

首先,控制點必須使用以下格式的GET方法發送請求。 斜體部分需要填上實際的值

獲取 description 的請求沒有負載字段, 但在請求頭之后一定要有空行(即以 \r\n\r\n 結束)。

下面列出的是請求行的詳細信息以及出現在上面列表中的標題。 除非另有說明,否則所有標頭值均區分大小寫。

  • Request line
    • GET HTTP GET Method
    • path to description 獲取 device 或者 service 的 description 的資源路徑。device description 的 URL 在 discovery 響應的 LOCATION 字段中。 service description 的 URL 在 device description 的 SCPDURL 字段中。
    • HTTP/1.1 HTTP 版本
  • headers
    • HOST Required | 服務的域名或者 IP 地址(端口號可省略), device description 的 URL 在 discovery 響應的 LOCATION 字段中。 service description 的 URL 在 device description 的 SCPDURL 字段中。 如果端口號沒有給出,則默認為 80 端口。
    • ACCEPT-LANGUAGE 在獲取設備描述的時候推薦添加此字段,該字段表示有限選擇的語言, 如果這個字段沒有指定可用的語言,則返回的描述采用用默認語言。 請參考( RFC 1766 )。

在 Control Point 發出獲取描述的請求之后,設備需要馬上響應 ControlPoint 請求,將 description 文檔發給 ControlPoint。 設備必須在合適的時間(一定要小於 30s)響應該請求。 如果在這個時間內響應失敗了,那么 ControlPoint 必須馬上重新發送一個請求。 設備必須以以下的格式返回響應信息。(斜線部分應該替換成具體的值)

這個響應的負載部即是設備或者服務的描述部分。在上面的幾個小節中闡述了。

下面將會介紹請求頭字段部分,所有的值都是大小寫敏感的。

  • headers
    • CONTENT-LANGUAGE 如果請求的頭字段中有 ACCEPT-LANGUAGE 字段則必須存在該字段。否則應該遵從 RFC 1766 中對語言格式的要求。
    • CONTENT-LENGTH Required | Body 的大小,單位字節 | Integer
    • CONTENT-TYPE Required | 該處的值必須為 text/xml
    • DATE Recommended | 該處記錄響應生成是的時間,格式遵從 RFC 2616 中定義的 “rfc1123-date” 格式
    • SERVER 關於請求 description 的響應,不需要此頭字段。(我不知道為什么要存在此說明,莫名其妙)

請注意,由於 HTTP 1.1 允許使用分塊編碼,因此,如果 GET 請求指定 HTTP 1.1, 則某些設備可能會使用分塊編碼發送描述。 因此,建議所有在 GET 請求中包含 HTTP 1.1的實現都支持接收分塊編碼。

2.9 Description references>
  • ISO 8601 ISO (International Organization for Standardization). Representations of dates and times, 1988-06-15. 傳送門
  • RFC 822 Standard for the format of ARPA Internet text messages. 傳送門
  • RFC 1123 Includes format for dates, for, e.g., HTTP DATE header 傳送門
  • RFC 1766 Format for language tag for, e.g., HTTP ACCEPT-LANGUAGE header. 傳送門 See also for language codes 傳送門.
  • RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies. 傳送門
  • RFC 2046 Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types. 傳送門
  • RFC 2083 PNG (Portable Network Graphics) Specification Version 1.0 傳送門傳送門
  • RFC 2387 Format for representing content type, e.g., mimetype element for an icon. 傳送門
  • RFC 2396 Uniform Resource Identifiers: Generic Syntax. 傳送門
  • RFC 2616 HTTP: Hypertext Transfer Protocol 1.1. 傳送門
  • UPC Universal Product Code. 12-digit, all-numeric code that identifies the consumer package. Managed by the Uniform Code Council. 傳送門
  • XML Extensible Markup Language. 傳送門
  • XML Schema (Part 1: Structures, Part 2: Datatypes) Grammar defining UPnP Template Language. 傳送門1, 傳送門2
3. Control

UPnP™ networking 的第三步就是 Control。該步驟在 addressing, discovery, description 之后,但是該步驟與第四步(eventing)是獨立的。 通過控制,控制點通過調用設備描述中的 action 並附帶參數來控制。 Control 和 Eventing 是 presentation 的延伸。

給定一個設備和該設備服務的信息,控制點可以向該設備發起請求調用服務的 action,並接受服務執行動作的響應。 調用的方式是通過 RPC 的方式實現的,控制點向服務點發送請求控制數據,服務點接收控制命令,並且返回成功或者失敗的結果給控制點

如何控制設備?在設備的 description 中有個 controlURL 元素,該元素的值即為服務器地址, 控制點向這個地址發送合適的請求,即可實現對設備的控制。 控制點的動作會最終影響設備的運行時狀態。 當這些狀態改變,設備應該將這些狀態發送給感興趣(之前訂閱過)的控制點。 這個章節將會介紹發起控制的消息格式,響應格式。 Eventing 章節將會介紹 event 相關的信息。

委員會和供應商會定義一些 actions 的狀態, 以允許控制點確定設備的當前值的動作。 與 action 的調用類似,控制點需要發送一個定義好的請求。服務收到該請求之后, 服務需要將請求的狀態變量,以及變量的值返回給控制點。 每個服務需要負責保持其狀態表的一致性,以便控制點在輪詢時查到的值是有意義的。 事件部分介紹了變量值的自動通知。

只要設備 discovery 時廣播的消息沒有過期,那么控制點即可認為廣播的服務是可用的。 如果設備取消了廣播,控制點必須認定之前廣播的設備和服務不可用了。

UPnP 的控制消息和響應的負載應該是 UTF-8 編碼的。

盡管 UDA 確實定義了一套方法來調用動作並輪詢值, 但 UDA 並未指定或約束針對在控制點上運行的應用程序的 API 設計; 操作系統供應商可以創建適合其客戶需求的API。

如果有很大的一部分數據需要隨 action 一起傳輸(特別是事先不知道數據的大小), 則不建議將數據作為 SOAP 參數的一部分或者作為 MIME 附件發送, 相反的應該采用 out-of-band 的方式傳輸。 例如,你可以在 argument 中包含一個 URL。 控制點可以通過該 URL 去GET / PUT /POST 獲取目標數據。 如果事先不知道數據量,則可以使用 HTTP chunked encoding。

在接下來的章節將對 Control Msg 的格式進行詳細解釋。

3.1 Control: Protocols

為了執行動作或者獲取狀態,控制點需要用到下面的這個 UPNP 協議棧。 ( UPnP 的完整協議棧在本文檔開頭有說明)

在協議棧最上層,控制信息包含了供應商(vendor)的一些信息(比如:參數值)。 接下來供應商的 Content 由 UPnP 委員會補充(如:action 的名字,argument 的名字,variable 的名字)。 以上各層的消息都由 UPnP-specific protocols 承載,協議定義在本文檔中。 因此,上述消息使用簡單對象訪問協議(SOAP)標頭和主體元素進行格式化, 並且這些消息是通過基於 IP/TCP 上的 HTTP 傳遞。 作為參考,上面[方括號]中的顏色表示在下面列出的訂閱消息中哪個協議定義了特定的標頭元素。

3.2 Control: Action

控制點調用服務 action, 服務返回操作結果或者錯誤,都采用 SOAP 封包的形式進行通信(傳輸協議是 HTTP )。

3.2.1 Control: Action: Invoke

簡單對象訪問協議(SOAP)定義了利用 XML 和 HTTP 實現遠程過程調用的一組規范。 UDA 使用 SOAP 將控制消息傳遞到設備,並將結果或錯誤返回給控制點。

SOAP 定義了其他 HTTP 標頭,並且為了確保它們不會與其他 HTTP 擴展名混淆, SOAP 遵循 HTTP 擴展框架(RFC 2774),並在 MAN 標頭中指定 SOAP 唯一 URI,並以 M- 作為 HTTP 方法的前綴。 在這里,方法為 M-POST。 使用 M-POST 要求 HTTP 服務器查找和理解 SOAP 唯一 URI 和特定於 SOAP 的標頭。

為了向防火牆和代理提供更大的管理靈活性,SOAP 指定必須首先嘗試不使用 MAN 頭或 M- 前綴的請求。 如果請求的響應為 “405不允許的方法” 而被拒絕,則必須使用 MAN 頭和 M 前綴發送第二個請求。 如果該請求以 “未實現501” 或 “未擴展510” 的響應被拒絕, 則該請求將失敗。 (其他HTTP響應應根據HTTP規范進行處理。)

下面是使用 POST 方法發送的控制消息的列表(不帶 MAN 頭),后面是頭和主體的說明。 緊隨其后的是使用 M-POST 方法和 MAN 標頭發送的控制消息列表。

要調用設備服務上的操作,控制點必須使用以下格式的POST方法發送請求。 斜體值是實際值的占位符。

下面列出的是上面列表中出現的請求行,標題和正文元素的詳細說明。 所有標頭值和元素名稱均區分大小寫,值不區分大小寫,除非另有說明。 元素的順序無關緊要,除非另有說明。 必需元素必須僅出現一次(沒有重復),推薦或可選元素最多只能出現一次,除非另有說明。

  • Request line
    • POST HTTP method.
    • path control url 組件 URL 路徑,用於控制此服務(設備描述的 service 元素的 controlURL 子元素)。| single URL
    • HTTP/1.1 HTTP/1.1 版本。
  • headers
    • HOST Required | URL 的域名或 IP 地址以及用於控制此服務的 URL 的可選端口組件(設備描述的 service 元素的 controlURL 子元素)。 如果端口為空或未指定,則假定端口為 80。
    • ACCEPT-LANGUAGE (該控制消息無 ACCEPT-LANGUAGE 頭)
    • CONTENT-LENGTH Required | Body 的大小,單位字節 | Integer
    • CONTENT-TYPE Required | 必須為 text/xml, 指明 Body 采用的字符編碼,這里必須為 “uft-8”。
    • MAN (POST 請求無此字段, M-POST 請求則需要附帶此字段)
    • SOAPACTION Required | SOAP 定義的此標頭 | 必須是服務類型 | Hash mark | 所有的名稱都應該用雙引號引起來。 如果是使用 M-POST 方法, HTTP 頭中必須有 MAN 頭。 | Single URI | 必須指定控制服務的版本,這里方便服務識別當前控制點可支持的版本。 其值可以是定義了指定操作的服務類型的任何版本。
  • body
    • Envelope Required | 由 SOAP 定義的元素 | xmlns 屬性的值必須為 http://schemas.xmlsoap.org/soap/envelope/. | encodingStyle 屬性的值必須為 http://schemas.xmlsoap.org/soap/encoding/ | 該元素包含以下子元素:
      • Body Required | 由 SOAP 定義 | 應該符合 SOAP 的命名空間,該元素包含以下子元素:
        • actionName Required | 元素的名字為被調用 action 的名字, xmlns 的屬性值必須為服務類型,用雙引號包含起來, 這里指定的版本號必須與 SOAPACTION 中的版本號一致。 該元素的值大小寫銘感。 該元素標簽必須為 Body 的第一個標簽,該元素標簽包含如下子元素。
          • argumentName 如果 action 擁有參數,那么此元素必須存在, 每一個參數(argument) 需要實現一次(元素的名稱不受命名空間的限制,元素名稱符合上下文即可), | 區分大小寫 | 該元素的值必須是符合 UPnP 服務描述中定義的數據類型和數據范圍 | 順序也必須按照 SCPD 中指定的順序(該處的順序服從 description spec 中 in 的排列順序),

如果 CONTENT-TYPE 是不支持的值(不是'text/xml'), 那么設備必須返回一下 HTTP 錯誤狀態碼(“415 Unsupported Media Type”)。

為了將來的可擴展性,當像上面的清單那樣處理 XML 時,設備控制點必須忽略它不認識的元素,子元素,內容,屬性,值。

設備和控制點應該忽略任何有可能受到的但是不認識的 XML 注釋或者 XML 的處理說明。

XML 命名空間前綴不必是上面給出的特定示例(例如,“ s”或“ u”); 它們可以是任何遵循常規 XML 命名空間機制規則的值; 設備必須接受使用其他合法 XML 命名空間前綴的請求。

如果一個 action 沒有 'in' 參數,那么你可以用 combine the opening and closing XML tags 表示。 (比如: 可以用“<actionname/>” 代替 “<actionname></actionname>”).

當一個參數的值中包含保留符號時(如 ‘&’ / ’<‘),必須按照 XML 規范第 2.4 節的規定對文本進行轉義, 並用等效的數字表示形式或字符串(例如 ‘&amp;’ 或 ‘&lt;')替換每個此類字符。 出現在 URL 中的此類字符也可以根據 RFC 2396 的 2.4 節中指定的 URL 轉義規則進行轉義。

請注意,由於 HTTP 1.1 允許使用分塊編碼,因此,如果 POST 方法標頭指定 HTTP 1.1, 則某些控制點可能會使用分塊編碼發送操作請求。 不支持使用分塊編碼接收操作請求的設備實現應返回 505 HTTP Version Not Supported 錯誤。

如果帶有 POST 請求的響應為 405 Method Not Allowed 被拒絕, 則控制點必須以以下格式發送帶有方法 M-POST 和 MAN 的第二個請求。 斜體值是實際值的占位符。

(Message body for request with method M-POST is the same as body for request with method POST. See above.)

  • Request Line
    • M-POST HTTP 可擴展框架定義的方法 (RFC 2774)
    • path control url 組件 URL 路徑,用於控制此服務(設備描述的 service 元素的 controlURL 子元素)。| single URL
    • HTTP/1.1 HTTP/1.1 版本。
  • Headers
    • HOST Required | URL 的域名或 IP 地址以及用於控制此服務的 URL 的可選端口組件(設備描述的 service 元素的 controlURL 子元素)。 如果端口為空或未指定,則假定端口為 80。
    • ACCEPT-LANGUAGE (該控制消息無 ACCEPT-LANGUAGE 頭)
    • CONTENT-LENGTH Required | Body 的大小,單位字節 | Integer
    • CONTENT-TYPE Required | 必須為 text/xml, 指明 Body 采用的字符編碼,這里必須為 “uft-8”。
    • MAN Required | 此處的值必須是 "http://schemas.xmlsoap.org/soap/envelope/"。 ns 則定義了為其他字段(如:SOAPACTION)定義了命名空間( namespace)(如 01)
    • SOAPACTION Required | SOAP 定義的此標頭 | 必須是服務類型 | Hash mark | 所有的名稱都應該用雙引號引起來。 如果是使用 M-POST 方法, HTTP 頭中必須有 MAN 頭。 | Single URI | 必須指定控制服務的版本,這里方便服務識別當前控制點可支持的版本。 其值可以是定義了指定操作的服務類型的任何版本。
3.2.2 Control: Action: Response

服務必須在 30s 內完成請求的操作,並做出響應,這里的 30s 包括預期的傳輸時間(從傳輸操作消息的時間到接收到相關響應的時間測得的時間)。 應該定義的完成操作時間的時間比 30s 要短,以便盡早返回響應。 如果服務在此時間內無法響應,則控制點應執行的操作取決於應用程序交互設計。 服務必須以以下格式發送響應。 斜體值是實際值的占位符。

下面將對 response line / headers / body elements 作詳細解釋。 所有頭( header )的值和元素( element )的名稱均區分大小寫; 除非特殊申明,所有值都不區分大小寫; 除非特殊申明,所有的元素的順序都是無關緊要的。 除非另有說明,否則必需元素必須僅出現一次(沒有重復),推薦或可選元素當且僅當出現一次。

  • Response line
    • HTTP/1.1 HTTP version
    • 200 OK HTTP success code
  • Headers
    • CONTENT-LANGUAGE (控制消息中無此頭)
    • CONTENT-LENGTH Required | Body 的大小,單位字節 | Integer
    • CONTENT-TYPE Required | 必須為 text/xml, 指明 Body 采用的字符編碼,這里必須為 “uft-8”。
    • DATE Recommended | 該處的時間為響應生成時的時間,時間格式為 “rfc1123-date”, 此格式定義於 RFC 2616.
    • EXT Required. 確認 MAN 中指定的請求類型是可被識別的 | 此處只有頭,無值
    • SERVER Required | 此處並列如下參數,操作系統名稱和操作系統版本,此兩個值用 / 充當分割符, UPnP的版本,以及產品的名稱和產品的版本。其中操作系統信息,UPnP 信息,和產品信息之間用空格充當分割符。| String | 必須准確反映設備支持的 UPnP 設備體系結構的版本號。 | 控制點必須准備好接受比控制點本身實現的更高的次要版本號(但主版本號相同)。 例如,實現 UDA 版本 1.0 的控制點應當能夠能夠與實現 UDA 版本 1.1 的設備進行互操作。
  • Body
    • Envelope Required | 由 SOAP 定義的元素 | xmlns 屬性的值必須為 http://schemas.xmlsoap.org/soap/envelope/. | encodingStyle 屬性的值必須為 http://schemas.xmlsoap.org/soap/encoding/ | 該元素包含以下子元素:
      • Body Required | 由 SOAP 定義 | 應該符合 SOAP 的命名空間,該元素包含以下子元素:
        • 指明 Required | 該元素的名稱對應請求操作的 action 的名稱, xmlns 屬性的值必須使用雙引號引起 | 而這里的 Version 也必須與請求頭中 SOAPACTION 指定的 version 相同 | 大小寫敏感 | 而且必須是 Body 元素中的第一個子元素 | 此元素包含一下子元素:
          • argumentName Required | 當且僅當 action 擁有 out 參數的時候才擁有此字段, 這里的值即填寫 action 執行之后的。 | 每一個 out 參數只允許出現一次 | 如果 action 有一個標記為 retval 的參數,則此參數必須是第一個元素。 (元素名稱不受名稱空間限制,符合上下文語義即可) | 區分大小寫。 UPnP服務描述所定義的單一數據類型 | 參數出現的順序必須與設備可用的服務描述(SCPD)中指定的順序相同

為了將來的可擴展性,當像上面的清單那樣處理,設備和控制點必須忽略自己不認識的元素或者子元素,和任何認識的屬性和該屬性的值。

所有的控制點和設備應該忽略 XML 的注釋或者他們看不懂的 XML 的流程說明。

XML 命名空間的前綴不必是上面給出的特定示例(例如,“ s”或“ u”), 它們可以是任何遵循常規 XML 命名空間規則的值, 控制點必須接受使用其他合法 XML 命名空間前綴的響應。

如果一個 action 沒有一個 out 參數,那么該參數使用單閉合標簽是有效的。 (如: 可以用 “<actionnameResponse/>” 替換 “<actionnameResponse></actionnameResponse>”)

當任何參數的值包含一個或多個保留為標記的字符(例如,“&”或小於(“<”))時, 必須根據 XML 規范 2.4 節的規定對文本進行轉義。 並用等效的數字表示形式或字符串替換每個此類字符(例如 “&amp;”或“&lt;”)。 出現在 URL 中的此類字符也可以根據 RFC 2396 第 2.4 節中指定的URL轉義規則進行轉義。

如果控制點使用 HTTP/1.0 承載 SOAP 協議,但是沒有攜帶 KeepAlive 頭, 設備必須在響應結束之后關閉通信 socket。 如果控制點使用 HTTP/1.1 承載 SOAP 協議,而且設置 Connection: CLOSE, 那么設備必須在������之后關閉通信 socket。

請注意,由於 HTTP 1.1 允許使用分塊編碼, 因此,如果 POST 請求指定 HTTP 1.1, 則某些設備可能會使用分塊編碼來發送操作響應。 因此,建議在 POST 請求中包括 HTTP 1.1 的所有實現都支持接收分塊編碼。

如果服務在調用控制點發送的操作時遇到錯誤, 則該服務必須在 30 秒內發送響應(包括預期的傳輸時間)。 注意 out 參數不能用於傳達錯誤信息, out 參數只能用於返回數據。 錯誤響應必須以以下格式發送。 斜體值是實際值的占位符。

下面列出的是上面列表中出現的 response line / Headers 和 Body 的詳細信息。 所有頭( header )的值和元素( element )的名稱均區分大小寫; 除非特殊申明,所有值都不區分大小寫; 除非特殊申明,所有的元素的順序都是無關緊要的。 除非另有說明,否則必需元素必須僅出現一次(沒有重復),推薦或可選元素當且僅當出現一次。

  • Response line
    • HTTP/1.1 HTTP version
    • 500 Internal Server Error HTTP error code
  • Headers
    • CONTENT-LANGUAGE (無此頭字段)(PS: 不清楚衛生么要這樣提示,我理解應該是如果請求中包含 ACCEPT-LANGRAGE, 而且當前控制參數的確包含本地化的一些值,則這里會出現該字段)
    • CONTENT-LENGTH Required | Body 的大小,單位字節 | Integer
    • CONTENT-TYPE Required | 必須為 text/xml, 指明 Body 采用的字符編碼,這里必須為 “uft-8”。
    • DATE Recommended | 該處的時間為響應生成時的時間,時間格式為 “rfc1123-date”, 此格式定義於 RFC 2616.
    • EXT Required. 確認 MAN 中指定的請求類型是可被識別的 | 此處只有頭,無值
    • SERVER Required | 此處並列如下參數,操作系統名稱和操作系統版本,此兩個值用 / 充當分割符, UPnP的版本,以及產品的名稱和產品的版本。其中操作系統信息,UPnP 信息,和產品信息之間用空格充當分割符。| String | 必須准確反映設備支持的 UPnP 設備體系結構的版本號。 | 控制點必須准備好接受比控制點本身實現的更高的次要版本號(但主版本號相同)。 例如,實現 UDA 版本 1.0 的控制點應當能夠能夠與實現 UDA 版本 1.1 的設備進行互操作。
  • Body
    • Envelope Required | 由 SOAP 定義的元素 | xmlns 屬性的值必須為 http://schemas.xmlsoap.org/soap/envelope/. | encodingStyle 屬性的值必須為 http://schemas.xmlsoap.org/soap/encoding/ | 該元素包含以下子元素:
      • Body Required | 由 SOAP 定義 | 應該符合 SOAP 的命名空間,該元素包含以下子元素:
      • Fault Required | 由 SOAP 定義 | 當調用 action 失敗的時候存在此字段 | 錯誤的值應該符合 SOAP 定義的命名規則 | 包含以下子元素:
        • faultCode Required | 由 SOAP 定義 | 值必須為 ‘Client’ (該處的值應該服從 SOAP 的命名規范)
        • faultstring Required | 由 SOAP 定義 | 值必須為 'UPnPError'
        • detail Required | 由 SOAP 定義 | 包含以下子元素:
          • UPnPError Required | 由 UDA 定義 | 包含以下子元素:
            • errorCode Required | 由 UDA 定義 | 這里定義遇到的是哪一種錯誤 | 請參考下面的表格中的值,以及對應的錯誤類型 | 整形
            • errorDescription UDA 定義的推薦字段,用於簡短描述錯誤類型 | 具體 Code 對應的推薦值參考下表 | 供應商也可以自定義值,但最好設計成是人類可讀的 | 此處描述字段值的長度應該 < 256 個字符

下表總結了已定義的錯誤類型以及 errorCode 和 errorDescription 元素的相應值。

為了將來的可擴展性,當像上面的清單那樣處理,設備和控制點必須忽略自己不認識的元素或者子元素,和任何認識的屬性和該屬性的值。

XML 命名空間前綴不必是上面給出的特定示例(例如,“ s”或“ u”); 它們可以是任何遵循常規 XML 名稱空間機制規則的值; 控制點必須接受使用其他合法 XML 名稱空間前綴的響應。

3.3 Control: Query for variable

UPnP 論壇已將 QueryStateVariable action 作廢, 除非在有限的測試場景測試出現非期望結果,否則控制點不應該再使用此方法, 委員會和供應商應明確定義查詢狀態變量所需的功能。 如果需要,這樣的顯式查詢動作可以包括多個狀態變量。 UPnP 設備體系結構中保留了 QueryStateVariable 的以下定義,以供參考並與早期實現向后兼容。

除了調用設備服務上的操作外,控制點還可以通過發送查詢消息來輪詢服務以獲取狀態變量的值。 查詢消息一次只能查詢一個狀態變量;如果要查詢多個,則必須發送多條查詢消息。

此查詢消息與服務的事件(如果有)是相互獨立的。 如果想要對 variable 的值進行檢查,查詢的次數會遠遠多余事件發送的次數, 關於事件的部分描述了事件管理。

3.3.1 Control: Query: Invoke

要查詢狀態變量的值,控制點必須以以下格式發送請求。 斜體值是實際值的占位符。

下面列出的是上面列表中出現的 request line / Headers 和 Body 的詳細信息。 所有頭( header )的值和元素( element )的名稱均區分大小寫; 除非特殊申明,所有值都不區分大小寫; 除非特殊申明,所有的元素的順序都是無關緊要的。 除非另有說明,否則必需元素必須僅出現一次(沒有重復),推薦或可選元素當且僅當出現一次。

  • Request line
    • POST HTTP 的請求方法
    • path of control URL 服務器用於接受控制的服務資源路徑 (該路徑在設備描述文檔的 service 元素的 controlURL 元素下)。 | Single relative URL.
    • HTTP/1.1 HTTP 版本.
  • Headers
    • HOST Required | 用於發送控制指令的服務器的服務地址(可能是域名也可能是 IP 地址) (該地址存在與設備描述文檔的 service 元素下), 該地址可能會攜帶端口號,如果沒有攜帶,則默認為是 80 端口。
    • ACCEPT-LANGUAGE (Control 消息不需要此頭)
    • CONTENT-LENGTH Required | Body 的大小,單位字節 | Integer
    • CONTENT-TYPE Required | 必須為 text/xml, 指明 Body 采用的字符編碼,這里必須為 “uft-8”。
    • MAN (POST 請求無此字段, M-POST 請求則需要附帶此字段)
    • SOAPACTION Required | SOAP 定義的此標頭 | 之必須是 urn:schemas-upnp-org:control-1-0#QueryStateVariable | 如果 HTTP method 使用的是 M-POST, 則頭名必須使用 MAN 頭中使用的 namespace 來限定 | Single URI
  • Body
    • Envelope Required | 由 SOAP 定義的元素 | xmlns 屬性的值必須為 http://schemas.xmlsoap.org/soap/envelope/. | encodingStyle 屬性的值必須為 http://schemas.xmlsoap.org/soap/encoding/ | 該元素包含以下子元素:
      • Body Required | 由 SOAP 定義 | 應該符合 SOAP 的命名空間,該元素包含以下子元素:
        • QueryStateVariable Required | 由 UPnP 定義,這里填寫的即使 Action 的名字, 其中 xmlns 屬性的值必須為 urn:schemas-upnp-org:control-1-0, 而且此元素必須是 Body 元素的第一個子元素, | 包含一下子元素。
          • varName Required | 由 UPnP 定義,是 state 變量的名字,該狀態必須從屬於 QueryStateVariable 的命名空間 | String

為了將來的可擴展性,當按照 Flexible XML Processing Profile(FXPP)的規定處理上述清單中的 XML 時, 設備和控制點必須忽略:(a)任何未知元素及其子元素或內容,以及(b)任何未知元素 屬性及其值。

如果帶有 POST 的請求被拒絕並返回響應 405 Method Not Allowed, 則控制點必須使用上述方法 M-POST 和 MAN 發送第二個請求。

3.3.2 Control: Query: Response

要回答有關狀態變量值的查詢,服務必須在 30 秒內響應(此 30 秒包含傳輸時間), 如果服務在此時間內無法響應,則控制點應執行的操作取決於應用程序。 服務必須以以下格式發送響應。 斜體值是實際值的占位符。

下面列出的是上面列表中出現的 response line / Headers 和 Body 的詳細信息。 所有頭( header )的值和元素( element )的名稱均區分大小寫; 除非特殊申明,所有值都不區分大小寫; 除非特殊申明,所有的元素的順序都是無關緊要的。 除非另有說明,否則必需元素必須僅出現一次(沒有重復),推薦或可選元素當且僅當出現一次。

  • Response line
    • HTTP/1.1 HTTP version.
    • 200 OK HTTP success code.
  • Headers
    • CONTENT-LANGUAGE (無此頭字段)(PS: 不清楚衛生么要這樣提示,我理解應該是如果請求中包含 ACCEPT-LANGRAGE, 而且當前控制參數的確包含本地化的一些值,則這里會出現該字段)
    • CONTENT-LENGTH Required | Body 的大小,單位字節 | Integer
    • CONTENT-TYPE Required | 必須為 text/xml, 指明 Body 采用的字符編碼,這里必須為 “uft-8”。
    • DATE Recommended | 該處的時間為響應生成時的時間,時間格式為 “rfc1123-date”, 此格式定義於 RFC 2616.
    • EXT Required. 確認 MAN 中指定的請求類型是可被識別的 | 此處只有頭,無值
    • SERVER Required | 此處並列如下參數,操作系統名稱和操作系統版本,此兩個值用 / 充當分割符, UPnP的版本,以及產品的名稱和產品的版本。其中操作系統信息,UPnP 信息,和產品信息之間用空格充當分割符。| String | 必須准確反映設備支持的 UPnP 設備體系結構的版本號。 | 控制點必須准備好接受比控制點本身實現的更高的次要版本號(但主版本號相同)。 例如,實現 UDA 版本 1.0 的控制點應當能夠能夠與實現 UDA 版本 1.1 的設備進行互操作。
  • Body
    • Envelope Required | 由 SOAP 定義的元素 | xmlns 屬性的值必須為 http://schemas.xmlsoap.org/soap/envelope/. | encodingStyle 屬性的值必須為 http://schemas.xmlsoap.org/soap/encoding/ | 該元素包含以下子元素:
      • Body Required | 由 SOAP 定義 | 應該符合 SOAP 的命名空間,該元素包含以下子元素:
        • QueryStateVariableResponse Required | 由 SOAP 定義 | xmlns 屬性的值必須為 urn:schemasupnp-org:control-1-0 | 必須是 Body 元素的第一個元素 | 包含如下子元素:
          • return Required | 由 UPnP 定義 | 元素名字 element defined by UPnP. (元素名稱不受名稱空間限制;語言符合下文即可。) 此處的值是在請求中的 varName 元素中指定的狀態變量的當前值。

為了將來的可擴展性,當按照 Flexible XML Processing Profile(FXPP)的規定處理上述清單中的 XML 時, 設備和控制點必須忽略:(a)任何未知元素及其子元素或內容,以及(b)任何未知元素 屬性及其值。

當任何參數的值包含一個或多個保留為標記的字符(例如,“&”或小於(“<”))時, 必須根據 XML 規范 2.4 節的規定對文本進行轉義。 並用等效的數字表示形式或字符串替換每個此類字符(例如 “&amp;”或“&lt;”)。 出現在 URL 中的此類字符也可以根據 RFC 2396 第 2.4 節中指定的URL轉義規則進行轉義。

如果服務無法為變量提供值,則服務必須在 30 秒內發送響應(該 30s 包含預期的傳輸時間)。 響應必須以以下格式發送。 斜體值是實際值的占位符。

下面列出的是上面列表中出現的 response line / Headers 和 Body 的詳細信息。 所有頭( header )的值和元素( element )的名稱均區分大小寫; 除非特殊申明,所有值都不區分大小寫; 除非特殊申明,所有的元素的順序都是無關緊要的。 除非另有說明,否則必需元素必須僅出現一次(沒有重復),推薦或可選元素當且僅當出現一次。

  • Response line
    • HTTP/1.1 HTTP version.
    • 500 Internal Server Error HTTP error code.
  • Headers
    • CONTENT-LANGUAGE (無此頭字段)(PS: 不清楚衛生么要這樣提示,我理解應該是如果請求中包含 ACCEPT-LANGRAGE, 而且當前控制參數的確包含本地化的一些值,則這里會出現該字段)
    • CONTENT-LENGTH Required | Body 的大小,單位字節 | Integer
    • CONTENT-TYPE Required | 必須為 text/xml, 指明 Body 采用的字符編碼,這里必須為 “uft-8”。
    • DATE Recommended | 該處的時間為響應生成時的時間,時間格式為 “rfc1123-date”, 此格式定義於 RFC 2616.
    • EXT Required. 確認 MAN 中指定的請求類型是可被識別的 | 此處只有頭,無值
    • SERVER Required | 此處並列如下參數,操作系統名稱和操作系統版本,此兩個值用 / 充當分割符, UPnP的版本,以及產品的名稱和產品的版本。其中操作系統信息,UPnP 信息,和產品信息之間用空格充當分割符。| String | 必須准確反映設備支持的 UPnP 設備體系結構的版本號。 | 控制點必須准備好接受比控制點本身實現的更高的次要版本號(但主版本號相同)。 例如,實現 UDA 版本 1.0 的控制點應當能夠能夠與實現 UDA 版本 1.1 的設備進行互操作。
  • Body
    • Envelope Required | 由 SOAP 定義的元素 | xmlns 屬性的值必須為 http://schemas.xmlsoap.org/soap/envelope/. | encodingStyle 屬性的值必須為 http://schemas.xmlsoap.org/soap/encoding/ | 該元素包含以下子元素:
      • Body Required | 由 SOAP 定義 | 應該符合 SOAP 的命名空間,該元素包含以下字段:
        • Fault Required | 由 SOAP 定義. 此標簽包含為什么服務沒有返回請求變量值的原因。 格式需要服從 SOAP 的命名規范 | 包含以下子元素:
          • faultcode Required | 由 SOAP 定義 | 值必須為 ‘Client’ (該處的值應該服從 SOAP 的命名規范)
          • faultstring Required | 由 SOAP 定義 | 通用描述 UPnP ErrorCode 的字符串 | 請查看以下表格中的說明
          • detail Required | 由 SOAP 定義 | 包含以下子元素:
            • UPnPError Required | 由 UPnP 定義 | 包含以下子元素:
              • errorCode Required | 由 UPnP 定義 | 定義遭遇何種 Error 的 Code | 具體的值請查看下表定義 | Integer
              • errorDescription Required | 由 UPnP 定義 | 一個解釋該錯誤類型的簡短說明 | 請查看以下表格中的說明 | 建議小於 256 個字符 | String

下表總結了 UPnP 定義的常用的 errorCode 和 errorDescription。

如果設備沒有實現 QueryStateVariable 指定的 Action, 設備服務應該返回 401 Invalid Action

為了將來的可擴展性,當像上面的清單那樣處理,設備和控制點必須忽略自己不認識的元素或者子元素,和任何認識的屬性和該屬性的值。

3.4 Control references
  • RFC 1123 Includes format for dates, for, e.g., HTTP DATE header. 傳送門
  • RFC 2396 Uniform Resource Identifiers: Generic Syntax 傳送門
  • RFC 2616 HTTP: Hypertext Transfer Protocol 1.1 傳送門
  • RFC 2774 HTTP Extension Framework. 傳送門
  • SOAP Simple Object Access Protocol. 傳送門
  • XML Extensible Markup Language. 傳送門
4. Eventing

Eventing 是 UPnP™ networking 的第四步,通過 Eventing 控制點監聽設備的變化。 Control 和 Eventing 作為設備的控制和展示借口,相當於 presntation (第五步)的外延。

在控制點 discovery 和獲取設備 description 之后,控制點知道了設備包含的設備和服務信息。 正如 Description 那節描述的那樣,UPnP 服務描述包括服務的 action 列表和運行時服務狀態變量列表。 如果這些狀態變量中的一個或多個發生了事件,則服務在這些變量更改時發布更新, 並且控制點可以訂閱以接收此信息。 在本節中, Publisher 指事件的源(通常是設備的服務), 而訂戶指事件的目的地(通常是控制點)。

為了訂閱事件,訂閱者發送訂閱消息。 如果訂閱被接受,則設備需要返回一個有效期給訂閱者。 為了使訂閱保持活動狀態,訂閱者必須在訂閱到期之前對其訂閱進行續訂。 當訂閱者不再需要來自 Publisher 的事件時,訂閱者應取消其訂閱。 本節在下面詳細說明了訂閱,續訂和取消消息。

Publisher 通過發送事件消息來通知狀態變量的更改。 事件消息包含一個或多個狀態變量的名稱以及這些變量的當前值,以 XML 表示。 當訂戶第一次訂閱時,會發送一個特殊的初始事件消息。 該事件消息包含所有事件變量的名稱和值,並允許訂戶初始化其服務狀態模型。 為了支持具有多個控制點的方案,可以使用事件來使感興趣的控制點及時的知曉設備(服務)的狀態變更(即便這個變更是由其他控制點影響的)。 當設備(服務)的狀態變更,應該向所有訂閱者發送所有事件消息,所有的訂閱者也應該接收所有的 event, 並且無論狀態變量為何更改(不管是因為響應 action 的請求導致的或因為服務因自身原因導致的)都發送事件消息更改。 本節將在下面詳細說明事件消息的格式。

一些狀態變量可能變化的速度很快,以至於事件並不能及時的將這些變化發出, 一個可選的做法是,如果來不及報告,那么就過濾或者平滑的處理掉中間變化的狀態變量, 只報告最后的狀態即可。 有些狀態可能包含的數值太大或者 size 太大,而以至於訂閱者無法識別(使用),針對這種情況, 由於這個原因或其他原因,服務可以將一個或多個狀態變量指定為 non-event, 並且不將該事件消息發送給訂戶即可。 為了確定此類設置為 non-event 變量的當前值,控制點必須顯式輪詢,當然前提是假定設備(服務)提供了接口來獲取狀態變量的值。 本節說明如何在服務描述中描述變量事件。

為了發送或者接受事件的消息,控制點和服務必須 following 完整 UPnP 協議棧的子集。 (完整的 UPnP 協議棧在本文最開始已經說明了)。

在協議最上層,訂閱和事件消息包含供應商特有的消息,例如訂閱的 URL / 訂閱的持續時間 / 特定的變量值。 在協議棧中向下移動,供應商內容將由 UPnP 委員會提供的信息進行補充,例如服務標識符或變量名。 來自以上各層的消息用本文檔中定義的 UPnP 的協議承載。 而 UPnP 消息由通過 HTTP 或者擴展了 HTTP 頭的 HTTP 協議承載。 作為參考,圖片中[方括號]中的顏色表示在下面列出的訂閱消息中哪個協議定義了特定的標頭元素。

在本章剩下的內容中,首先會解釋說明訂閱相關的內容, 包括詳細的訂閱消息,刷新消息,取消訂閱等操作。 其次,還會介紹 eventing 消息的格式,和如何發送給控制點。 最后,將會介紹 eventing 的 the UPnP Template Language。

4.1 Eventing: Subscription

當且僅當它擁有一個或一個以上的狀態變量在服務描述文檔中被標記為可被 event 時,一個服務擁才有 event 事件,

如果一個服務擁有 event 時間, 它將會發送該時間給感興趣的訂閱者。 Publisher 擁有一個 Subscriber 列表,而且保存這每一個 Subscriber 的如下信息:

  • unique subscription identifier Required | 在訂閱的整個生命周期內此 ID 必須是唯一的,但是對長度並沒有做要求。 由 Publisher 生成並在響應訂閱請求時發送給 Subscriber 。 推薦使用 universally-unique identifiers ( 參考 RFC4122 ) 來保證唯一性 | Single URI
  • delivery URL for event messages Required | 由 Subscriber 在請求訂閱的消息中提供 | Single URL
  • event key Required | 訂閱初始的時候值為 0。 Key 必須是序列增長的,即每發送一個 event, Key 應該自增 1。 Subscriber 接收到排序好的 key 之后即可知道自己丟失了哪些 event 消息。 當 Key 增長到 4294967295 之后在增加的 event, key 應該變成 1 (32-bit 無符號整形最大值). 有些實現可能包括了 0, 遇到 0 的 key 應該將之忽略。
  • subscription duration Required | 訂閱到期之前的時間或持續時間。 單個整數或關鍵字“ infinite”。

Publisher 應接受盡可能多的管理 / 維護 / 交付訂閱。

Publisher 可能希望在電源故障時保留訂閱。 雖然控制點可以從完全的網絡故障中恢復,但是如果問題很簡單並且已定位到設備, 則重新使用存儲的訂閱可能會加快恢復速度。

訂戶列表的更新是和訂閱動作(subscription, renewal, and cancellation)相關的,

為了訂閱服務的事件,訂閱者發送訂閱消息,該消息包含 Publisher 的 URL, Publisher 的服務標識符以及 Subscriber 接收事件消息的 URL。 訂閱消息還可以包括所請求的訂閱持續時間。 Publisher 的 URL 和服務標識符來自描述消息。 如 “描述” 部分中所述,描述消息包含設備描述。 設備描述包含(除其他事項外)每個服務的事件 URL(在 eventSubURL 元素中)和服務標識符(在 serviceId 元素中); 它們分別對應於 Publisher 的 URL 和服務標識符。 Publisher 的網址對於該設備中的特定服務必須是唯一的。

訂閱請求發出,即代表訂閱該 URL 下的所有事件,沒有提供任何機制來可變地訂閱事件消息。 向訂戶發送該服務的所有事件消息。 這是設計服務時要考慮的一個因素。

如果訂閱請求被接受,則 Publisher 應該立即做出響應,響應的內容包括該訂閱的 UUID 和該訂閱的有效期。 有效期的選擇應該考慮到控制點從該網絡中移除的頻率,如果控制點每隔幾分鍾就會從網絡中離開, 那么過期時間就應該設置得比較短,以保證 Publiser 能更快的處理過期的 Subscriber; 如果控制點幾乎是永久性的存在網絡中,則訂閱時間應該設置得非常長, 以減小因為經常的刷新訂閱信息而造成網絡擁塞。

訂閱被接受后,Publisher 首先應該發送第一個 event, 該 event 包含當前服務的基礎狀態,好讓 Subscriber 對當前設備有一個基本的了解。 該消息包括當前可被 event 狀態變量的名字,以及搜索狀態變量的值(狀態變量的值和范圍之前已經在 description 文檔中有說明)。 即使控制點在傳遞之前取消訂閱,該消息也始終會發送出去。 設備必須在發送初始事件消息之前確保控制點已收到對訂閱請求的響應,以確保控制點已接收到 SID(訂閱ID),從而可以將事件消息與訂閱相關聯。

為了保持訂閱的在線狀態,Subscriber 必須在訂閱過期前 發送 renewal 消息執行 renew 操作, renewal 消息需要發送到和訂閱消息相同的 URL, 但是 renewal 消息並不需要包括 Subscriber 接受 event 的 URL; 相反的 renewal 消息應該包含之前訂閱的時響應的 UUID。 Publisher 返回給 renewal 消息的響應和訂閱時的響應一樣。

如果訂閱過期了,這個訂閱的 UUID 以及對應的信息都將變得無效, Publisher 應該停止向該 Subscriber 發送 event,並且可以將該 Subscriber 的信息從定戶列表中清除。 如果定於試圖發送除訂閱消息的其他消息給服務,那么 Publisher 應該拒絕該 Subscriber 的所有請求。

當 Subscriber 不再需要來自特定服務的事件時,訂戶應取消其訂閱。 取消訂閱通常會減少服務,控制點和網絡負載。 如果將訂戶突然從網絡中刪除,則可能無法發送取消消息。 作為后備,除非最終續訂,否則訂閱最終將自行失效。

Subscriber 應監視來自 Publisher 的發現消息。 如果 Publisher 取消其設備和服務的廣播, 則訂閱者應假定其訂閱已被有效取消。

以下是對訂閱,續訂和取消消息的請求,響應和錯誤的特定格式的說明。

4.1.1 Eventing: Subscribing: SUBSCRIBE with NT and CALLBACK

對於設備中的每個服務,描述消息均包含事件 URL(設備描述中 service 元素的 eventSubURL 子元素)和 UPnP 服務標識符(設備描述中 service 元素中的 serviceId 子元素)。 為了訂閱特定服務的事件,將訂閱消息發送到該服務的事件 URL。 (請注意,事件 URL 可能是相對於 Base URL 的)。 消息包含該服務的標識符以及事件消息的傳遞 URL。 訂閱消息還可以包括請求的訂閱過期時間(時間段)。

要訂閱服務事件,訂閱者必須使用以下格式發送帶有方法 SUBSCRIBE 以及 NT 和 CALLBACK 標頭的請求。 斜體值是實際值的占位符。

SUBSCRIBER 方法的請求沒有負載(body)部分,但是在頭部后了空行一定不能少。(結尾是兩個 \r\n)

下面的列表詳細描述了上圖中出現的請求的 request line / headers 字段. 除非特殊申明,所有的頭的值都是大小寫敏感的。

  • Request line
    • SUBSCRIBE 訂閱和刷新訂閱的 HTTP Method.
    • publisher path 訂閱事件 URL 中的資源路徑,(該 URL 在設備描述文檔中 eventSubURL 子元素中提供 ) | Single, relative URL.
    • HTTP/1.1 HTTP version
  • Headers
    • HOST Required | 訂閱事件的域名或者 IP 地址,端口信息是可選標注的 ( 該 URL 在 device 描述文檔中的 eventSubURL 元素有提及)。 如果端口號未指定則默認為是 80 端口。
    • CALLBACK Required | 接受訂閱事件的服務器 location ( URL ), 如果 CALLBACK 中包含了多個 URL,那么在發送事件的時候,Publisher 會按照順序一個一個嘗試,直到有一個成功。 每個 URL 用 '<' 和 ‘>’ 包圍。 每一個 URL 應該指定的是 HTTP/TCP 的服務器(URL 前綴為 'http://')。 設備不得以任何方式截斷此 URL; 如果沒有足夠的內存來存儲整個 CALLBACK URL,則設備必須拒絕訂閱。
    • NT Required | Notification 類型, 值必須是 upnp:event。
    • SID (此訂閱請求,無 SID 標頭)
    • TIMEOUT Recommended | 此訂閱消息失效的時間間隔,值微微 infinite 或者數妙。 UPnP 委員會的建議。 由 UPnP 供應商定義。 格式由關鍵字 “Second-” 組成,后跟(無中間空格)是整數或關鍵字 “infinite”。

如果有足夠的資源支持新的定戶,Publisher 就應該接受新定戶的請求。 為了接受新的請求, Publisher 應該為該請求生成一個唯一碼,分配一個訂閱的有效時間。 並發送初始事件消息(本節稍后將詳細說明)。 要接受訂閱請求,發布者必須在 30 秒內(包括傳輸時間)以以下格式發送響應。 斜體值是實際值的占位符。

SUBSCRIBER 方法的請求沒有負載(body)部分,但是在頭部后了空行一定不能少。(結尾是兩個 \r\n)

如果設備發送的響應基於 HTTP/1.0 但是有 KeepAlive 頭, 或者是基於 HTTP/1.1 但是沒有 Connection: CLose 頭, 那么設備必須保證在發送初始化事件之前先發送 TCP FIN Flag。 在其他的情況下(除非響應使用了 HTTP Chunk 功能),必須在發送初始事件之前指定 Content-Length(並將其設置為0)。

下面的列表詳細描述了上圖中出現的請求的 request line / headers 字段. 除非特殊申明,所有的頭的值都是大小寫敏感的。

  • Headers
    • DATE Recommended | 該時間為響應生成時的時間,服從 RFC 2616 定義的 “rfc1123-date” 格式。
    • SERVER Required | 此處並列如下參數,操作系統名稱和操作系統版本,此兩個值用 / 充當分割符, UPnP的版本,以及產品的名稱和產品的版本。其中操作系統信息,UPnP 信息,和產品信息之間用空格充當分割符。| String | 必須准確反映設備支持的 UPnP 設備體系結構的版本號。 | 控制點必須准備好接受比控制點本身實現的更高的次要版本號(但主版本號相同)。 例如,實現 UDA 版本 1.0 的控制點應當能夠能夠與實現 UDA 版本 1.1 的設備進行互操作。
    • SID Required | 為該訂閱生成的 UUID. 必須是全局唯一的, 而且必須以 uuid: 作為前綴。 由 UPnP 供應商定義。Single URI.
    • TIMEOUT Recommended | 此訂閱消息失效的時間間隔,值微微 infinite 或者數妙。 UPnP 委員會的建議。 由 UPnP 供應商定義,該值應該大於或者等於 1800 秒。 格式由關鍵字 “Second-” 組成,后跟(無中間空格)是整數或關鍵字 “infinite”。

如果 Publisher 不能接受訂閱,或者在訂閱請求消息中檢查到了錯誤,Publisher 必須發送一個響應同志定戶,錯誤信息如下。 響應消息必須在 30s 內發送出去(該時間包含消息傳輸時間)。

  • Errors
    • Incompatible headers 400 Bad Request | 如果存在 SID 標頭以及 NT 或 CALLBACK 標頭之一, 則發布者必須發送 400 Bad Request 錯誤消息。
    • Missing or invalid CALLBACK 412 Precondition Failed | 如果 CALLBACK 標頭丟失或不包含有效的 HTTP URL,則發布者必須以 HTTP 錯誤 412 Precondition Failed 進行響應。
    • Invalid NT 412 Precondition Failed | 如果 NT 標頭不等於 upnp:event , 則發布者必須響應 412 Precondition Failed 錯誤消息。
    • Unable to accept subscription 5xx | 如果發布者無法接受訂閱(例如由於資源不足),則它必須以 HTTP 500 系列錯誤代碼響應。

UPnP 協議底層協議棧中的層可能會返回其他錯誤。 有關詳細信息,請查閱這些協議的文檔。

4.1.2 Eventing: Renewing a subscription: SUBSCRIBE with SID

為了刷新訂閱的狀態,Subscriber 應當發送 renew 消息到訂閱事件的 URL。 與初始訂閱消息不同,續訂消息既不包含服務的標識符也不包含事件消息的傳遞 URL。 而是,該消息包含由發布者分配的訂閱標識符,從而提供對要更新的訂閱的明確引用。 像訂閱消息一樣,續訂消息也可以包括請求的訂閱持續時間。

續訂消息的請求方法和訂閱消息的請求方法一致,只是兩個消息包含的頭集合不一樣。 續訂消息使用 SID,而訂閱消息使用 NT 和 CALLBACK。 一個既包含 SID,又包含 NT 和 CALLBACK 的消息是錯誤的。

為了續訂服務的事件, Subscriber 應該發送以下格式請求到訂閱服務的 URL, 斜體值是實際值的占位符。

SUBSCRIBER 方法的請求沒有負載(body)部分,但是在頭部后了空行一定不能少。(結尾是兩個 \r\n)

下面的列表詳細描述了上圖中出現的請求的 request line / headers 字段. 除非特殊申明,所有的頭的值都是大小寫敏感的。

  • Request line
    • SUBSCRIBE 訂閱和刷新訂閱的 HTTP Method.
    • publisher path 訂閱事件 URL 中的資源路徑,(該 URL 在設備描述文檔中 eventSubURL 子元素中提供 ) | Single, relative URL.
    • HTTP/1.1 HTTP version
  • Headers
    • HOST Required | 訂閱事件的域名或者 IP 地址,端口信息是可選標注的 ( 該 URL 在 device 描述文檔中的 eventSubURL 元素有提及)。 如果端口號未指定則默認為是 80 端口。
    • CALLBACK (續訂消息中無此頭)
    • NT (續訂消息中無此頭)
    • SID Required | 由 Publisher 之前分配個定戶的 SID, 必須是全局唯一的,而且值必須以 uuid: 開頭,由 UPnP 供應商定義 | Single URI
    • TIMEOUT Recommended | 此訂閱消息失效的時間間隔,值微微 infinite 或者數妙。 UPnP 委員會的建議。 由 UPnP 供應商定義。 格式由關鍵字 “Second-” 組成,后跟(無中間空格)是整數或關鍵字 “infinite”。

要接受續訂,發布者會重新分配訂閱期限,並且必須以與新訂閱請求相同的格式和條件發送響應,但不需要再次發送初始事件消息。

如果發布者不能接受續訂,或者續訂請求有錯誤,則發布者必須發送帶有以下錯誤之一的響應。 響應必須在 30s 內發送(包括預期的傳輸時間)。

  • Errors
    • Incompatible headers 400 Bad Request | 如果存在 SID 標頭以及 NT 或 CALLBACK 標頭之一, 則發布者必須發送 400 Bad Request 錯誤消息。
    • Invalid SID 412 Precondition Failed | 如果 SID 其值未可知,或者該訂閱已過期,則 Publisher 必須響應 412 Precondition Failed 錯誤信息。
    • Missing SID 412 Precondition Failed | 如果 SID 缺失,或者為空,則 Publisher 必須響應 412 Precondition Failed 錯誤信息。
    • Unable to accept renewal 5xx | 如果發布者無法接受續訂(例如由於資源不足),則它必須以 HTTP 500 系列錯誤代碼響應。

UPnP 協議底層協議棧中的層可能會返回其他錯誤。 有關詳細信息,請查閱這些協議的文檔。

4.1.3 Eventing: Canceling a subscription: UNSUBSCRIBE

當不再需要訂閱服務的事件時,應將取消訂閱的消息發送到該服務的事件 URL(請注意,事件 URL 可能是基於 Base URL 的), 該消息中包含由 Publisher 分配的 SID, 取消訂閱通常會減少服務,控制點和網絡負載。 如果將控制點突然從網絡中刪除,則可能無法發送取消訂閱消息。 作為后備,除非最終續訂,否則訂閱最終將自行失效。

若要取消對服務事件的訂閱,訂閱者應使用以下格式的 UNSUBSCRIBE 方法發送請求。 斜體值是實際值的占位符。

UNSUBSCRIBE 方法的請求沒有負載(body)部分,但是在頭部后了空行一定不能少。(結尾是兩個 \r\n)

下面的列表詳細描述了上圖中出現的請求的 request line / headers 字段. 除非特殊申明,所有的頭的值都是大小寫敏感的。

  • Request line
    • UNSUBSCRIBE 取消訂閱的 HTTP Method.
    • publisher path 訂閱事件 URL 中的資源路徑,(該 URL 在設備描述文檔中 eventSubURL 子元素中提供 ) | Single, relative URL.
    • HTTP/1.1 HTTP version
  • Headers
    • HOST Required | 訂閱事件的域名或者 IP 地址,端口信息是可選標注的 ( 該 URL 在 device 描述文檔中的 eventSubURL 元素有提及)。 如果端口號未指定則默認為是 80 端口。
    • CALLBACK ( UNSUBSCRIBE 方法沒有 CALLBACK 頭 )
    • NT ( UNSUBSCRIBE 方法沒有 NT 頭 )
    • SID Required | 由 Publisher 之前分配個定戶的 SID, 必須是全局唯一的,而且值必須以 uuid: 開頭,由 UPnP 供應商定義 | Single URI
    • TIMEOUT ( UNSUBSCRIBE 方法沒有 TIMEOUT 頭 )

為了取消訂閱,Publisher 必須在 30s 內發送如下的響應消息(改 30s 包括傳輸時間),

                HTTP/1.1 200 OK

如果在取消訂閱的時候發生了錯誤,Publisher 必須發送如下的錯誤信息給定戶。 該消息必須在 30s 內發送如下的響應消息(改 30s 包括傳輸時間),

  • Errors
    • Incompatible headers 400 Bad Request | 如果存在 SID 標頭以及 NT 或 CALLBACK 標頭之一, 則發布者必須發送 400 Bad Request 錯誤消息。
    • Invalid SID 412 Precondition Failed | 如果 SID 其值未可知,或者該訂閱已過期,則 Publisher 必須響應 412 Precondition Failed 錯誤信息。
    • Missing SID 412 Precondition Failed | 如果 SID 缺失,或者為空,則 Publisher 必須響應 412 Precondition Failed 錯誤信息。

UPnP 協議底層協議棧中的層可能會返回其他錯誤。 有關詳細信息,請查閱這些協議的文檔。

4.2 Eventing: Event messages

服務通過發送事件消息來發布對其狀態變量的更改。 這些消息包含一個或多個狀態變量的名稱以及這些變量的當前值。 事件消息應盡快發送給訂戶,以使定戶能及時的獲取有關該服務的精准信息,並使訂戶能及時的響應到用戶界面。 如果同時更改多個變量的值,則發布者應將這些更改捆綁到單個事件消息中,以減少處理和網絡流量。

如上所述,初始事件消息是在訂戶首次訂閱時發送的; 該事件消息包含所有事件變量的名稱和值,並允許訂戶初始化其服務狀態模型。 發布者接受訂閱后,應盡快發送此消息。 即使控制點在傳遞消息之前取消訂閱,也應始終發送此消息。

事件消息用 event key 標記。 發布者必須為每個訂閱維護一個單獨的事件密鑰,以便於錯誤檢測(如下所述)。 當發布者發送初始事件消息時,訂閱的 event key 被初始化為 0。 對於每個后續事件消息,發布者都會遞增訂閱的 event key,並將更新后的密鑰包括在事件消息中。 事件密鑰的任何實現都應處理溢出,並將事件密鑰從 4294967295 包裝回 1(而不是0)。 當下一個事件鍵不是上一個鍵的增量時,訂閱服務器還必須處理這種特殊情況。 應實現為4字節(32位)無符號整數。

如果定戶沒有對任何事件消息做出響應,則發布者應繼續嘗試向訂閱者發送后續事件消息,直到訂閱到期。

為了修復事件訂閱,例如,如果訂戶錯過了一個或多個事件消息, 則訂戶必須退訂並重新訂閱。 這樣,訂戶將獲得新的訂閱標識符,新的初始事件消息和新的事件密鑰。

所有 UPnP 的事件消息應該使用 UTF-8 編碼。

4.2.1 Eventing: Event messages: NOTIFY

為了發送事件消息,Publisher 必須發送一個如下格式的請求,該請求的請求方法為 NOTIFY。 斜體值是實際值的占位符。

下面列出的是上圖中出現的 response line / Headers 和 Body 的詳細信息。 所有頭( header )的值和元素( element )的名稱均區分大小寫; 除非特殊申明,所有值都不區分大小寫; 除非特殊申明,所有的元素的順序都是無關緊要的。 除非另有說明,否則必需元素必須僅出現一次(沒有重復),推薦或可選元素當且僅當出現一次。 特別是,單個 propertyset 元素不得包含多個指定同一 variableName 元素的 property 元素; 每一個同名的 variableName 狀態必須使用單獨的事件通知消息。

  • Request line
    • NOTIFY 用於 Notify event 的 HTTP 請求方法。
    • delivery path Notify 消息服務的 URL 的資源路徑(該值存在與定戶發送訂閱消息的 CALLBACK 頭中)。 必須是 CALLBACK 標頭中包含的 URL 之一,不能截斷或修改。| Single, relative URL
    • HTTP/1.1 HTTP version.
  • Headers
    • HOST Required | 接受訂閱事件的域名或者 IP 地址,端口信息是可選標注的 ( 該 URL 在 Subscriber 發送的訂閱請求的 CALLBACK 頭有提及)。 如果端口號未指定則默認為是 80 端口。
    • ACCEPT-LANGUAGE (事件消息無此頭)
    • CONTENT-LENGTH Required | Body 的大小,單位字節 | Integer
    • CONTENT-TYPE Required | 必須為 text/xml, 指明 Body 采用的字符編碼,這里必須為 “uft-8”。
    • NT Required | Notification 類型, 值必須是 upnp:event。
    • NTS Required | 事件推送的子類型 | 值必須為 upnp:propchange
    • SID Required | 由 Publisher 之前分配個定戶的 SID, 必須是全局唯一的,而且值必須以 uuid: 開頭,由 UPnP 供應商定義 | Single URI URI.
    • SEQ Required | Event key | initial event 的值必須為 0, 沒向定戶發送一次事件消息,該定戶的該值就相應的增加 1, 為防止溢出,當值為 4294967295 再發送的下一個事件的改值將變為 1。 該值為 32 位無符號的十進制,不帶前導零(某些實現可能包含前導零,接收者應將其忽略)。
  • Body
    • properyset Required | xmlns 屬性必須為 urn:schemas-upnp-org:event-1-0 包含如下子元素:
      • property Required | 每一個狀態變量名在每一個時間中只重復一次,而且必須屬於 propertyset 命名空間前綴的響應。 包含如下元素:
        • variableName Required | 有變動的狀態變量的名稱(服務描述中 stateVariable 元素的 name 子元素)。 不得使用任何命名空間限定。 值是此狀態變量的新值。 區分大小寫。 UPnP 服務描述所指定的單一數據類型。

為了將來的可擴展性,當像上面的清單那樣處理 XML 時,設備和控制點必須忽略: (a)任何未知元素及其子元素或內容,以及(b)任何未知屬性及其值。 請注意,當使用比控制點支持的版本更高的服務訂閱事件時,該服務可能會將原本定戶不能識別的狀態變量發送給控制點。 此時,控制點應丟棄並忽略事件通知消息中的此類無法識別的狀態變量。

所有的控制點和設備應該忽略 XML 的注釋或者他們看不懂的 XML 的流程說明。

請注意,由於 HTTP 1.1 允許使用分塊編碼, 因此,如果 SUBSCRIBE 請求指定 HTTP 1.1, 則某些設備可能會使用分塊編碼來發送操作響應。 因此,建議在 SUBSCRIBE 請求中包括 HTTP 1.1 的所有實現都支持接收分塊編碼。

為了確認收到此事件消息,訂戶必須在 30s 內做出響應,包括預期的傳輸時間。 如果訂閱者在 30s 內沒有響應,或者發布者無法連接到訂閱 URL, 則發布者應放棄向訂閱者發送此消息,但應保持訂閱處於活動狀態,並向訂閱者發送將來的事件消息, 直到訂閱過期或被取消。 訂戶必須以以下格式發送響應。

            HTTP/1.1 200 OK

NOTIFY 方法的請求沒有負載(body)部分,但是在頭部后了空行一定不能少。(結尾是兩個 \r\n)

如果控制點使用 HTTP/1.0 承載 SOAP 協議,但是沒有攜帶 KeepAlive 頭, 設備必須在響應結束之后關閉通信 socket。 如果控制點使用 HTTP/1.1 承載 SOAP 協議,而且設置 Connection: CLOSE, 那么設備必須在響應之后關閉通信 socket。

如果事件消息有錯誤,則訂戶必須以以下錯誤之一進行響應。 響應必須在 30 秒內發送,包括預期的傳輸時間。

  • Errors
    • MISS SID 412 Precondition Failed | 如果 SID 缺失,或者為空,則 Publisher 必須響應 412 Precondition Failed 錯誤信息。
    • Invalid SID 412 Precondition Failed | 如果 SID 其值未可知,或者該訂閱已過期,則 Publisher 必須響應 412 Precondition Failed 錯誤信息。 (收到此錯誤響應時必須終止此 SID。)
    • Missing NT or NTS header 400 Bad Request | 如果 NT 或者 NTS 頭缺失,則發布者必須響應 400 Bad Request 錯誤消息。
    • Invalid NT header 412 Precondition Failed | 如果 NT 標頭不等於 upnp:event , 則發布者必須響應 412 Precondition Failed 錯誤消息。
    • Invalid NTS header 412 Precondition Failed | 如果 NTS 頭不等於 upnp:propchange ,則發布者必須響應 412 Precondition Failed 錯誤消息。

UPnP 協議底層協議棧中的層可能會返回其他錯誤。 有關詳細信息,請查閱這些協議的文檔。

4.3 Eventing: UPnP Template Language for eventing

UPnP 模板語言為設備和服務定義了格式正確的模板。 在較小程度上,它還提供了事件消息主體的模板。 “描述” 部分說明了與設備和服務有關的 UPnP 模板語言。 如該部分所述,UPnP 模板語言是用 XML 語法編寫的,並且是從XML架構派生的(第1部分:結構,第2部分:數據類型)。 以下是與事件有關的該語言列表。它定義的元素用於事件消息中。 它們在這里是綠色,在上面的清單中是綠色。 以下是定義這些元素的位置(盡管這是最低限度的定義); 以及使用它們的地方。

緊隨其后的是對使用的 XML Schema 元素,屬性和值的簡要說明。 本節末尾的 XML Schema 參考文獻有更多詳細信息。

UPnP Template Language for eventing

  • element 引用元素以聲明嵌套。 maxOccurs 屬性定義元素必須出現的最大次數。 默認值為 maxOccurs = 1 ; 可以出現一次或多次的元素具有 maxOccurs = *
  • ElementType 用新的派生語言定義元素。 name 屬性定義元素名稱。 model 屬性定義元素是否包含子元素。 如果有元素中的子元素包含的內容不確定的時候 model 等於 open, 如果只有元素中的子元素才擁有內容的時候 model 等於 eltOnly, name 屬性定義元素名稱。 model 性指示新的派生語言中的元素是否可以包含此處未明確指定的元素, 僅在未指定的子元素可能會包括在內的時候,model = open。 content 屬性指示可能包含的內容, 僅包含其他元素的元素具有 content = eltOnly。

如 “描述” 部分中所述,服務的 UPnP 模板語言還為狀態變量指定 sendEvents 屬性。 此屬性的默認值為 yes。 為了表示狀態變量可 event,在服務描述中此屬性的值為 yes(或省略該屬性)。 如果為 non-event, 則其值為 no。 請注意,如果所有服務的狀態變量均為 non-event,則該服務沒有任何要發布的內容,並且控制點無法訂閱,也不會從該服務接收事件消息。

4.4 Eventing: Augmenting the UPnP Template Language

在 UPnP 模板語言中,對設備和服務做 annotations 是很有用的。 在較小程度上,這些批注中的值可以平滑和溫和的捕獲事件。

如上所述,某些狀態變量可能更改值的速度太快,以至於無法使用。 以下是 UPnP 論壇工作委員會或 UPnP 供應商推薦的詞匯表,用於記錄由於變量值更改而導致發送的事件消息數量的度。

  • maximumRate = n Optional | 在發送事件之后,狀態變量的值產生了變化,但是你設置了該值, 那么即便在 ns 秒內產生了事件。下次發送事件也會在 ns 以后。 但是如果 v 在 ns 內停止了連續的更新,那么此時應該及時的吧該變量的最新狀態發送出去。 | 建議給頻繁,持續變化的狀態變量設置該值(比如說播放進度) | 整數。
  • minimumDelta = n Optional | 在發送事件之后,狀態變量的值產生了變化,但是你設置了該值, 那么即便值的變化在 +-n 范圍內,此時並不會產生事件, 但是如果 v 此時值超出了 allowedValueRange 的范圍,那么此時應該及時的吧該變量的最新狀態發送出去。 | 建議需要進行濾波,或者計數類型的狀態變量設置該值 | 整數。

事件發生后,發布者可以發送任何已更改的調節變量。 發布者也應盡最大努力滿足上述審核規則, 但發布者在發出事件時可以過濾掉最近的更改。

請注意,查詢狀態變量,僅僅會影響事件,而不會導致狀態變量的更新。 具體來說,返回狀態變量值的控制動作可能會返回比通過事件發布的狀態值更接近於真是值。 換句話說,調節導致的狀態表更改並非意味都會導致事件的發生。

關於那個狀態變量是否可被 event 或者調節,這由 UPnP 委員會或者 UPnP 供應商決定(非標服務)。

4.5 Eventing references
  • RFC 2396 Uniform Resource Identifiers: Generic Syntax 傳送門
  • RFC 2616 HTTP: Hypertext Transfer Protocol 1.1. 傳送門
  • XML Extensible Markup Language. 傳送門
  • XML Schema (Part 1: Structures, Part 2: Datatypes) Grammar defining UPnP Template Language. 傳送門 / 傳送門
  • A Universally Unique IDentifier (UUID) URN Namespace 傳送門
5. Presentation

演示是 UPnP™ 網絡中的第 5 步,是對 3 3步和第 4 步的補充。

在控制點(1)發現設備並(2)檢索到該設備的描述之后,控制點就可以打開演示功能。 如果設備具有用於演示的 URL,則控制點可以從該 URL 檢索頁面,將頁面加載到瀏覽器中,並根據頁面的功能,允許用戶控制設備和/或查看設備狀態。 這些功能可以實現的程度取決於演示頁面和設備的特定功能。

用於演示的 URL 包含在設備描述中的 presentationURL 元素內。 設備描述是通過描述消息傳遞的。 “描述” 部分詳細說明了設備描述和描述消息。

為了發送或者接受事件的消息,控制點和服務必須 following 完整 UPnP 協議棧的子集。 (完整的 UPnP 協議棧在本文最開始已經說明了)。

協議最上層,演示頁面由 UPnP 供應商指定。 在協議第二層,UDA 指定該頁面用 HTML 編寫。 該頁面通過基於 TCP/IP 上的 HTTP 傳遞。 作為參考,為了與本文檔中的其他部分保持一致,在[方括號]中添加��顏色��

為了檢索演示頁面,控制點向演示 URL 發出 HTTP GET 請求,然后設備返回一個演示頁面。

與 UPnP 設備和服務模板以及標准設備和服務類型不同,演示頁面的功能完全由 UPnP 供應商指定。 演示頁面不在 UPnP 委員會的主持下。 該頁面必須是 HTML 頁面; 它應該是 HTML 3.0 或更高版本。 但是,其他設計方面留給供應商指定。 這包括但不限於控制點瀏覽器的所有功能,使用的腳本語言或瀏覽器插件以及與設備進行交互的方式。 為了實現演示頁面,UPnP 供應商可能希望利用 UPnP 機制進行控制和/或事件化,以利用設備的現有功能,但並不限於此。

演示頁面應使用 HTML 提供的本地化機制(例如,具有 charset 屬性的 META 標簽)。 控制點應使用 HTTP 的 ACCEPT-LANGUAGE 和 CONTENT-LANGUAGE 功能來嘗試檢索本地化的演示文稿頁面。 具體地說,控制點可以在對演示頁面的請求中包括 HTTP ACCEPT-LANGUAGE 標頭。 如果請求中存在 ACCEPT-LANGUAGE 標頭,則響應必須包含 CONTENT-LANGUAGE 標頭以標識頁面的語言。

5.1 Presentation references
UPnP device protection

DeviceProtection:1 服務旨在提供一個大致等價於 UPnP Security 1.0 的功能. 引入這項新服務的目的是解決阻礙行業中較早設計部署的各種問題。 下文將解釋為什么。

DeviceProtection 訪問控制模型基於訪問控制列表(ACL), 該訪問控制列表將特定設備和特定服務的角色分配給控制點和用戶。 每個設備都維護自己的 ACL,設備可以支持在多個設備間自動同步共享 ACL, 也可不支持。 每一個角色對應於執行一組特定 SOAP 操作的權限。 執行一個 Action 所需的角色可以取決於傳遞給該動作的參數值。 UPnP 服務規范推薦了 Action 和角色對應關系的定義,但是設備可以忽略這些建議。 因此,DeviceProtection 還提供了查詢設備以發現執行特定操作所需的角色的功能。 DeviceProtection 使用自己的服務來防止未經授權的 ACL 修改。

名詞解釋
  • SW-CP Security-aware Control Points, 支持 UPnP 安全協議的控制點(非官方簡稱)
動機

UDA 1.0 在 2003 年 11 月發布,但自從那時開始,產品的開發和發布都面臨極大的限制。 一個極大的影響因素如下:

  1. 設置過程的用戶體驗過於復雜和繁瑣。(有人認為)
  2. 某些高級 UPnP 安全功能取決於諸如 SPKI 之類的安全標准,而這些標准在業界並未得到廣泛支持。
  3. UPnP Security 1.0 基於 “3box” 模型,但該模型要求存在帶有豐富用戶界面的安全控制台。 這是產品開發部署的一個很大的障礙。
  4. UPnP 論壇內部對於 UPnP Security 部署缺乏共識,這增加了風險並降低了產品實施的收益。

此外,在 1.0 發布以后,下列功能在新的產品中也發展成熟了起來。

  1. Wi-Fi 網絡已在家庭中普及,並且已經建立了安全設置 Wi-Fi 的新標准。 這項稱為 Wi-Fi 保護設置[WPS]的新標准基於比 UPnP Security 1.0 更簡單的用戶體驗。 WPS 的迅速采用表明制造商相信 WPS 的用戶體驗可以為廣大市場所接受。從 2020 年的中國普通用戶來看 wps 功能看起來很雞肋,雖然各大廠商都支持,但是普通用戶幾乎不會使用到該功能,該功能看起來似乎像研發經費太多開發出來的
  2. DLNA 已經確定了幾種需要安全性的新方案,這些新方案增加了為 UPnP 開發可部署的安全性框架的緊迫性。
  3. 惡意網站通過家用 PC 上的瀏覽器擴展發起的主動攻擊,針對家用設備的新威脅已經出現。
  4. 基於 VPN 技術的用於 UPnP 網絡遠程訪問的 UPnP 服務正在標准化。 此技術提供了另一種方法來支持對 UPnP 設備的安全遠程訪問。
該協議所提供的能力
  • SWCP 可以根據 SSDP 的擴展頭提取到設備服務的安全描述信息。
  • Initial Introduction – 提供最基本的信息,安全的將自己介紹給目標設備(或者 CP),以建立其最基本權限的訪問通道。
  • 設備與控制點安全通信鏈路建立(設備和 CP 的信任關系建立)(設備和 CP 認證)(Device and Control Point Authentication) – 設備授權給 CP 之前先需要完成 TLS 的雙向證書(x.509)認證,從而建立安全的通訊鏈路。 其中用於身份驗證過程中的證書信任鏈並不需要由權威的證書頒發機構頒發。 相反的,更推薦 Device 和 CP 去生成自己的證書(可以使用自簽名證書,這樣可以更好的管控風險), DeviceProtection 安全模型信任關系的建立取決於本地點對點的配置,而並非基於初始預置的根證書取得信任關系。 設備的實現者可能會需要設計一個更加復雜的模型去將用戶引入信任構建流程中。 但該方法的好處也是顯而易見的,它並不需要提供一個機制去保證所有的設備(或者控制設備)簽發的證書都基於同一個受信的 CA。 因此,基於受信任的CA根和更長的證書鏈(這將需要單獨的TLS握手)的訪問控制策略不在DeviceProtection的范圍內。
  • 用戶鑒權(User Authentication) – 在已經建立了 TLS 通訊通道的基礎上,用戶可以建立屬於他自己的 username/password 鑒權機制。
  • SOAP 服務和 Presentation Pages 的私密性和完整性 ( Privacy and integrity protection for SOAP services and Presentation Pages ) – 私密性和完整性由 基於標准的 TLS 的 HTTPS 保證。 (目前推薦至少應該上 TLS 1.2 而且在鑒權階段應該協商好 Token, 在此處的通訊應該將該 Token 帶上)
  • 訪問控制的策略與審計(Examination and manipulation of access control policy) - – 經過身份驗證和授權的控制點可以讀取和配置設備針對控制點和/或用戶身份的訪問控制策略。
  • 設備支持的角色的枚舉( Enumeration of Roles supported by Devices ) – 角色是與一組訪問權限關聯的名稱。 當一個角色或一組角色分配給控制點身份或用戶身份時,該身份將被授予與角色相關聯的訪問權限。( 這里可以參考 AWS 的 IAM 服務機制 )
該協議不提供的能力

DeviceProtection service 明確不包括的目標有:

  • UPnP 設備的平台安全(Platform security for UPnP devices) – DeviceProtection 不能解決內部存在信任完整性問題設備的安全問題。
  • 數字權限管理(Digital rights management) – DeviceProtection 不能解決關於數字媒體或者數字內容的拷貝的問題。
  • 代碼級別的安全問題(Code base trust) – 和平台安全性類似,DeviceProtection 不能保證 downloading, hosting, verifying the integrity 實現代碼的安全性問題。
  • Vendor 自定義安全機制(Application-specific security needs)- DeviceProtection 提供了一組機制,但是每個設備或應用程序負責決定如何應用這些機制來解決特定於域的問題。 例如,其他 UPnP DCP 可以定義其自己的使用 DeviceProtection 的特定要求。 對於 Vendor 自定義部分產生的安全問題,DeviceProtection 無法保證安全性。
  • 安全策略執行的策略/驗證過程中產生的安全問題
注意
  • 《UPnP gw DeviceProtection ..》提供了一個 UUID 的計算方法,請參考該文檔的 2.6.8.2 章節,但也不需要完全遵守該方法
  • DeviceProtection 定義了三個角色,擁有三種等級的權限,可以參考一下:Public, Basic, Admin
  • 為兼容傳統設備 DeviceProtection 也允許暴露一些基礎的功能到環境中,但這些功能必須通過 HTTPS 進行通信,而且在雙方都支持的前提下,應該選用最接近最新版本的 TLS 來進行消息通訊。
  • DeviceProtection 並不能保證 SSDP 階段的攻擊,因此黑客有可能監聽此階段的信息並加以利用,但是 DeviceProtection 能保證 SSDP 的后續操作安全性。
  • DeviceProtection 所增加的 SSDP 擴展頭為: SECURELOCATION.UPNP.ORG, 而且 LOCATION 字段也應該存在。 兩個 URI 應該對應不同的端口,但應該指到同一個 XML 描述文檔。
    如果根設備不包含 DeviceProtection 那么跟設備的 SSDP 中不需要包含 SECURELOCATION.UPNP.ORG 字段,但是如果該設備的嵌入設備有任何一個包含 DeviceProtection 服務,那么所有的設備 SSDP 都必須攜帶 SECURELOCATION.UPNP.ORG 頭
    SECURELOCATION.UPNP.ORG 頭指定的 URI 協議必須是 https
    安全銘感的 CP, 所有的請求都必須通過 SECURELOCATION.UPNP.ORG 指定的服務來訪問
  • 針對同一請求 Device 可能會更具訪問用戶的權限不同而返回不同的響應,而權限的指派是由最初擁有該設備的 CP 來指定分配的。
  • 如果一個根設備或者嵌入設備中包含 DeviceProtection,那么該設備下的所有服務的 ACL 都由 DeviceProtection 來維護。但如果設備中位包含 DeviceProtection, 那么各個服務的 ACL 都存在與各個服務當中。
DLNA 實戰前熱身(DLNA 抓包)
說明

DLNA 的基礎就是 UPnP, 目前 UPnP 已經更新到 2.0 了, DLNA 也已經更新到 2016 了,但是相對的 DLNA 組織也解散了。 我覺得這也是最好的使命吧,這種標准由這么多個商業利益不同的組織一起維護本來就是一件很容易出問題的事情。

雖然我還沒看 2.0 部分的 spec, 但是就 1.0 來說,一個是文章太羅嗦,很簡單的東西講這么久。 第二個路由器廠商對組播其實支持不是特別好,遠遠沒有 TCP/IP 支持得那樣好,這樣造成用戶體驗很差,而且作為控制端, 服務端的開發者我們對此毫無辦法。 第三個是雖然協議是在局域網傳輸信息,但是一方面這也就限制在了局域網,第二方面,在協議層面幾乎沒有考慮安全這方面的需求, 通信協議幾乎是在網絡中裸奔,這個放在消費者安全意識日益增強的現在,算是一個非常嚴重的劣勢。 最后一個我覺得用 XML 一個是解析速度方面是個問題,另外一個方面單位 Byte 承載的信息量太小了。 雖然局域網通信更加有保障,也不太需要注意這種 size 的封包消耗,但如果將這個服務放在智能家居,小平台上,這個協議就幾乎無勇武之地,如果用 ProtoBuf 會不會更好? 另外對於接受端和發送短要維護版本兼容,在通信協議里面其實可以加入更多東西,這樣,升級也更方便。

這個協議本身就是為了打通家里的設備而實現的(使家庭設備能互相發現,互相調用,互相分享信息),而現在越來越多的家電也開始連入了,家庭的網絡。 而從這方面來看,不管是 UPnP 也好,DLNA 也好,完全失敗了,幾乎被 MQTT 摁在地上來回摩擦。 但 MQTT 其實剛好缺少設備發現,設備宣告,設備描述那方面的功能,而 UPnP 沒有跟上時代,這不能說不是一個遺憾。

我個人覺得統一標准這中事情最好是由開源組織,或者無組織團隊,個人來維護,可能協議有很多個版本,也有很多個分支, 但是大家總會看見好的分支版本被引用,被傳誦。只有協議本身不斷的去舊添新,不斷的適應環境,才能夠留下更加適應當前的環境。

接下來,先結合 UPnP 的后 5 個步驟,我從樂播官網下載了樂播的服務端 APK,然后用網易雲 DLNA 播放音樂,抓出通信過程中產生的數據包。 把數據包記錄到本章。

Discovery
  • DMC 發送的 M-Search 請求
        Internet Protocol Version 6, Src: fe80::e9c7:bf5e:e75c:18cb, Dst: ff02::c
        User Datagram Protocol, Src Port: 63040, Dst Port: 1900
    
        M-SEARCH * HTTP/1.1
        Host: [FF02::C]:1900
        ST: urn:Microsoft Windows Peer Name Resolution Protocol: V4:IPV6:LinkLocal
        Man: "ssdp:discover"
        MX: 3
    
  • Device 發送的組播信息: rootdevice
        NOTIFY * HTTP/1.1
        HOST: 239.255.255.250:1900
        CACHE-CONTROL: max-age=66
        LOCATION: http://192.168.3.65:49152/description.xml
        NT: upnp:rootdevice
        NTS: ssdp:alive
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        USN: uuid:F7CA5454-3F48-4390-8009-5892d163053a::upnp:rootdevice
    
  • Device 發送的組播信息: 子設備
        NOTIFY * HTTP/1.1
        HOST: 239.255.255.250:1900
        CACHE-CONTROL: max-age=66
        LOCATION: http://192.168.3.65:49152/description.xml
        NT: uuid:F7CA5454-3F48-4390-8009-5892d163053a
        NTS: ssdp:alive
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        USN: uuid:F7CA5454-3F48-4390-8009-5892d163053a
    
  • Device 發送的組播信息: MediaRenderer 信息
        NOTIFY * HTTP/1.1
        HOST: 239.255.255.250:1900
        CACHE-CONTROL: max-age=66
        LOCATION: http://192.168.3.65:49152/description.xml
        NT: urn:schemas-upnp-org:device:MediaRenderer:1
        NTS: ssdp:alive
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        USN: uuid:F7CA5454-3F48-4390-8009-5892d163053a::urn:schemas-upnp-org:device:MediaRenderer:1
    
  • Device 發送的組播信息: AVTransport service
        NOTIFY * HTTP/1.1
        HOST: 239.255.255.250:1900
        CACHE-CONTROL: max-age=66
        LOCATION: http://192.168.3.65:49152/description.xml
        NT: urn:schemas-upnp-org:service:AVTransport:1
        NTS: ssdp:alive
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        USN: uuid:F7CA5454-3F48-4390-8009-5892d163053a::urn:schemas-upnp-org:service:AVTransport:1
    
  • Device 發送的組播信息: ConnectionManager service
        NOTIFY * HTTP/1.1
        HOST: 239.255.255.250:1900
        CACHE-CONTROL: max-age=66
        LOCATION: http://192.168.3.65:49152/description.xml
        NT: urn:schemas-upnp-org:service:ConnectionManager:1
        NTS: ssdp:alive
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        USN: uuid:F7CA5454-3F48-4390-8009-5892d163053a::urn:schemas-upnp-org:service:ConnectionManager:1
    
  • Device 發送的組播信息: RenderingControl service
        NOTIFY * HTTP/1.1
        HOST: 239.255.255.250:1900
        CACHE-CONTROL: max-age=66
        LOCATION: http://192.168.3.65:49152/description.xml
        NT: urn:schemas-upnp-org:service:RenderingControl:1
        NTS: ssdp:alive
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        USN: uuid:F7CA5454-3F48-4390-8009-5892d163053a::urn:schemas-upnp-org:service:RenderingControl:1
    
Description
  • 控制點發送 GET 請求獲取設備描述文件
        GET /description.xml HTTP/1.1
        Content-Length: 0
        HOST: 192.168.3.65
        User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1.1; OS105 Build/NGI77B)
        Connection: Keep-Alive
        Accept-Encoding: gzip
    
    
  • 設備響應控制點設備描述文件
        HTTP/1.1 200 OK
        CONTENT-LENGTH: 2695
        CONTENT-TYPE: text/xml
        DATE: Mon, 03 Aug 2020 01:12:24 GMT
        LAST-MODIFIED: Mon, 03 Aug 2020 01:09:24 GMT
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        X-User-Agent: redsonic
        CONNECTION: close
    
        <?xml version="1.0"?>
        <root xmlns="urn:schemas-upnp-org:device-1-0">
            <specVersion>
                <major>1</major>
                <minor>0</minor>
            </specVersion>
            <device>
                <deviceType>urn:schemas-upnp-org:device:MediaRenderer:1</deviceType>
                <presentationURL>/</presentationURL>
                <friendlyName>TV(192.168.3.65ï¼</friendlyName>
                <manufacturer>LEBO</manufacturer>
                <manufacturerURL>http://www.hpplay.com.cn</manufacturerURL>
                <modelDescription>Lebo Media Render</modelDescription>
                <modelName>HappyCast</modelName>
                <modelURL>http://www.hpplay.com.cn</modelURL>
                <dlna:X_DLNADOC xmlns:dlna="urn:schemas-dlna-org:device-1-0">DMR-1.50</dlna:X_DLNADOC>
                <UDN>uuid:F7CA5454-3F48-4390-8009-5892d163053a</UDN>
                <UID>-1482191554774566800</UID>
                <serviceList>
                    <service>
                        <serviceType>urn:schemas-upnp-org:service:AVTransport:1</serviceType>
                        <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId>
                        <SCPDURL>/dlna/Render/AVTransport_scpd.xml</SCPDURL>
                        <controlURL>_urn:schemas-upnp-org:service:AVTransport_control</controlURL>
                        <eventSubURL>_urn:schemas-upnp-org:service:AVTransport_event</eventSubURL>
                    </service>
                    <service>
                        <serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>
                        <serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId>
                        <SCPDURL>/dlna/Render/ConnectionManager_scpd.xml</SCPDURL>
                        <controlURL>_urn:schemas-upnp-org:service:ConnectionManager_control</controlURL>
                        <eventSubURL>_urn:schemas-upnp-org:service:ConnectionManager_event</eventSubURL>
                    </service>
                    <service>
                        <serviceType>urn:schemas-upnp-org:service:RenderingControl:1</serviceType>
                        <serviceId>urn:upnp-org:serviceId:RenderingControl</serviceId>
                        <SCPDURL>/dlna/Render/RenderingControl_scpd.xml</SCPDURL>
                        <controlURL>_urn:schemas-upnp-org:service:RenderingControl_control</controlURL>
                        <eventSubURL>_urn:schemas-upnp-org:service:RenderingControl_event</eventSubURL>
                        <LELINKFT>{ &quot;deviceip&quot;: &quot;192.168.3.65&quot;,192.168.3.65}</LELINKFT>
                    </service>
                </serviceList>
                <av:X_RController_DeviceInfo xmlns:av="urn:mi-com:av">
                    <av:X_RController_Version>1.0</av:X_RController_Version>
                    <av:X_RController_ServiceList>
                        <av:X_RController_Service>
                            <av:X_RController_ServiceType>controller</av:X_RController_ServiceType>
                            <av:X_RController_ActionList_URL>http://192.168.3.65:6095/</av:X_RController_ActionList_URL>
                        </av:X_RController_Service>
                        <av:X_RController_Service>
                            <av:X_RController_ServiceType>data</av:X_RController_ServiceType>
                            <av:X_RController_ActionList_URL>http://api.tv.duokanbox.com/bolt/3party/</av:X_RController_ActionList_URL>
                        </av:X_RController_Service>
                    </av:X_RController_ServiceList>
                </av:X_RController_DeviceInfo>
            </device>
            <URLBase>http://192.168.3.65:49152</URLBase>
        </root>
    

    可以看到上敘的設備描述文檔中有一個根設備,三個服務,還有一個 X_RController_DeviceInfo。其中,一個設備和三個服務屬於 DLNA 中規范的, 而 <av:X_RController_DeviceInfo xmlns:av="urn:mi-com:av"> 則是小米自己定義的一套規范,這個我在網上也沒找到相關的資料。

  • 控制點發送請求獲取 AVTransport 服務 SCPD
        GET /dlna/Render/AVTransport_scpd.xml HTTP/1.1
        Content-Length: 0
        HOST: 192.168.3.65
        User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1.1; OS105 Build/NGI77B)
        Connection: Keep-Alive
        Accept-Encoding: gzip
    
    
  • 設備響應控制點請求,返回 AVTransport 服務 SCPD
        HTTP/1.1 200 OK
        CONTENT-LENGTH: 14868
        CONTENT-TYPE: text/xml
        DATE: Mon, 03 Aug 2020 01:12:41 GMT
        LAST-MODIFIED: Thu, 01 Jan 1970 00:00:00 GMT
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        X-User-Agent: redsonic
        CONNECTION: close
        <?xml version="1.0" encoding="utf-8"?>
    
        <scpd xmlns="urn:schemas-upnp-org:service-1-0">
        <specVersion>
            <major>1</major>
            <minor>0</minor>
        </specVersion>
        <actionList>
            <action>
            <name>GetCurrentTransportActions</name>
            <argumentList>
                <argument>
                <name>InstanceID</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                <name>Actions</name>
                <direction>out</direction>
                <relatedStateVariable>CurrentTransportActions</relatedStateVariable>
                </argument>
            </argumentList>
            </action>
            <action>
            <name>GetDeviceCapabilities</name>
            <argumentList>
                <argument>
                <name>InstanceID</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                <name>PlayMedia</name>
                <direction>out</direction>
                <relatedStateVariable>PossiblePlaybackStorageMedia</relatedStateVariable>
                </argument>
                <argument>
                <name>RecMedia</name>
                <direction>out</direction>
                <relatedStateVariable>PossibleRecordStorageMedia</relatedStateVariable>
                </argument>
                <argument>
                <name>RecQualityModes</name>
                <direction>out</direction>
                <relatedStateVariable>PossibleRecordQualityModes</relatedStateVariable>
                </argument>
            </argumentList>
            </action>
            <action>
            <name>GetMediaInfo</name>
            <argumentList>
                <argument>
                <name>InstanceID</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                <name>NrTracks</name>
                <direction>out</direction>
                <relatedStateVariable>NumberOfTracks</relatedStateVariable>
                </argument>
                <argument>
                <name>MediaDuration</name>
                <direction>out</direction>
                <relatedStateVariable>CurrentMediaDuration</relatedStateVariable>
                </argument>
                <argument>
                <name>CurrentURI</name>
                <direction>out</direction>
                <relatedStateVariable>AVTransportURI</relatedStateVariable>
                </argument>
                <argument>
                <name>CurrentURIMetaData</name>
                <direction>out</direction>
                <relatedStateVariable>AVTransportURIMetaData</relatedStateVariable>
                </argument>
                <argument>
                <name>NextURI</name>
                <direction>out</direction>
                <relatedStateVariable>NextAVTransportURI</relatedStateVariable>
                </argument>
                <argument>
                <name>NextURIMetaData</name>
                <direction>out</direction>
                <relatedStateVariable>NextAVTransportURIMetaData</relatedStateVariable>
                </argument>
                <argument>
                <name>PlayMedium</name>
                <direction>out</direction>
                <relatedStateVariable>PlaybackStorageMedium</relatedStateVariable>
                </argument>
                <argument>
                <name>RecordMedium</name>
                <direction>out</direction>
                <relatedStateVariable>RecordStorageMedium</relatedStateVariable>
                </argument>
                <argument>
                <name>WriteStatus</name>
                <direction>out</direction>
                <relatedStateVariable>RecordMediumWriteStatus</relatedStateVariable>
                </argument>
            </argumentList>
            </action>
            <action>
            <name>GetPositionInfo</name>
            <argumentList>
                <argument>
                <name>InstanceID</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                <name>Track</name>
                <direction>out</direction>
                <relatedStateVariable>CurrentTrack</relatedStateVariable>
                </argument>
                <argument>
                <name>TrackDuration</name>
                <direction>out</direction>
                <relatedStateVariable>CurrentTrackDuration</relatedStateVariable>
                </argument>
                <argument>
                <name>TrackMetaData</name>
                <direction>out</direction>
                <relatedStateVariable>CurrentTrackMetaData</relatedStateVariable>
                </argument>
                <argument>
                <name>TrackURI</name>
                <direction>out</direction>
                <relatedStateVariable>CurrentTrackURI</relatedStateVariable>
                </argument>
                <argument>
                <name>RelTime</name>
                <direction>out</direction>
                <relatedStateVariable>RelativeTimePosition</relatedStateVariable>
                </argument>
                <argument>
                <name>AbsTime</name>
                <direction>out</direction>
                <relatedStateVariable>AbsoluteTimePosition</relatedStateVariable>
                </argument>
                <argument>
                <name>RelCount</name>
                <direction>out</direction>
                <relatedStateVariable>RelativeCounterPosition</relatedStateVariable>
                </argument>
                <argument>
                <name>AbsCount</name>
                <direction>out</direction>
                <relatedStateVariable>AbsoluteCounterPosition</relatedStateVariable>
                </argument>
            </argumentList>
            </action>
            <action>
            <name>GetTransportInfo</name>
            <argumentList>
                <argument>
                <name>InstanceID</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                <name>CurrentTransportState</name>
                <direction>out</direction>
                <relatedStateVariable>TransportState</relatedStateVariable>
                </argument>
                <argument>
                <name>CurrentTransportStatus</name>
                <direction>out</direction>
                <relatedStateVariable>TransportStatus</relatedStateVariable>
                </argument>
                <argument>
                <name>CurrentSpeed</name>
                <direction>out</direction>
                <relatedStateVariable>TransportPlaySpeed</relatedStateVariable>
                </argument>
            </argumentList>
            </action>
            <action>
            <name>GetTransportSettings</name>
            <argumentList>
                <argument>
                <name>InstanceID</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                <name>PlayMode</name>
                <direction>out</direction>
                <relatedStateVariable>CurrentPlayMode</relatedStateVariable>
                </argument>
                <argument>
                <name>RecQualityMode</name>
                <direction>out</direction>
                <relatedStateVariable>CurrentRecordQualityMode</relatedStateVariable>
                </argument>
            </argumentList>
            </action>
            <action>
            <name>Next</name>
            <argumentList>
                <argument>
                <name>InstanceID</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
            </argumentList>
            </action>
            <action>
            <name>Pause</name>
            <argumentList>
                <argument>
                <name>InstanceID</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
            </argumentList>
            </action>
            <action>
            <name>Play</name>
            <argumentList>
                <argument>
                <name>InstanceID</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                <name>Speed</name>
                <direction>in</direction>
                <relatedStateVariable>TransportPlaySpeed</relatedStateVariable>
                </argument>
            </argumentList>
            </action>
            <action>
            <name>Previous</name>
            <argumentList>
                <argument>
                <name>InstanceID</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
            </argumentList>
            </action>
            <action>
            <name>Seek</name>
            <argumentList>
                <argument>
                <name>InstanceID</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                <name>Unit</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_SeekMode</relatedStateVariable>
                </argument>
                <argument>
                <name>Target</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_SeekTarget</relatedStateVariable>
                </argument>
            </argumentList>
            </action>
            <action>
            <name>SetAVTransportURI</name>
            <argumentList>
                <argument>
                <name>InstanceID</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                <name>CurrentURI</name>
                <direction>in</direction>
                <relatedStateVariable>AVTransportURI</relatedStateVariable>
                </argument>
                <argument>
                <name>CurrentURIMetaData</name>
                <direction>in</direction>
                <relatedStateVariable>AVTransportURIMetaData</relatedStateVariable>
                </argument>
            </argumentList>
            </action>
            <action>
            <name>SetPlayMode</name>
            <argumentList>
                <argument>
                <name>InstanceID</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                <name>NewPlayMode</name>
                <direction>in</direction>
                <relatedStateVariable>CurrentPlayMode</relatedStateVariable>
                </argument>
            </argumentList>
            </action>
            <action>
            <name>Stop</name>
            <argumentList>
                <argument>
                <name>InstanceID</name>
                <direction>in</direction>
                <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
            </argumentList>
            </action>
        </actionList>
        <serviceStateTable>
            <stateVariable sendEvents="no">
            <name>TransportStatus</name>
            <dataType>string</dataType>
            <allowedValueList>
                <allowedValue>OK</allowedValue>
                <allowedValue>ERROR_OCCURRED</allowedValue>
                <allowedValue>vendor-defined</allowedValue>
            </allowedValueList>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>NextAVTransportURI</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>NextAVTransportURIMetaData</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>CurrentTrackMetaData</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>RelativeCounterPosition</name>
            <dataType>i4</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>A_ARG_TYPE_InstanceID</name>
            <dataType>ui4</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>A_ARG_TYPE_SeekTarget</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>PlaybackStorageMedium</name>
            <dataType>string</dataType>
            <allowedValueList>
                <allowedValue>UNKNOWN</allowedValue>
                <allowedValue>DV</allowedValue>
                <allowedValue>MINI-DV</allowedValue>
                <allowedValue>VHS</allowedValue>
                <allowedValue>W-VHS</allowedValue>
                <allowedValue>S-VHS</allowedValue>
                <allowedValue>D-VHS</allowedValue>
                <allowedValue>VHSC</allowedValue>
                <allowedValue>VIDEO8</allowedValue>
                <allowedValue>HI8</allowedValue>
                <allowedValue>CD-ROM</allowedValue>
                <allowedValue>CD-DA</allowedValue>
                <allowedValue>CD-R</allowedValue>
                <allowedValue>CD-RW</allowedValue>
                <allowedValue>VIDEO-CD</allowedValue>
                <allowedValue>SACD</allowedValue>
                <allowedValue>MD-AUDIO</allowedValue>
                <allowedValue>MD-PICTURE</allowedValue>
                <allowedValue>DVD-ROM</allowedValue>
                <allowedValue>DVD-VIDEO</allowedValue>
                <allowedValue>DVD-R</allowedValue>
                <allowedValue>DVD+RW</allowedValue>
                <allowedValue>DVD-RW</allowedValue>
                <allowedValue>DVD-RAM</allowedValue>
                <allowedValue>DVD-AUDIO</allowedValue>
                <allowedValue>DAT</allowedValue>
                <allowedValue>LD</allowedValue>
                <allowedValue>HDD</allowedValue>
                <allowedValue>MICRO-MV</allowedValue>
                <allowedValue>NETWORK</allowedValue>
                <allowedValue>NONE</allowedValue>
                <allowedValue>NOT_IMPLEMENTED</allowedValue>
                <allowedValue>vendor-defined</allowedValue>
            </allowedValueList>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>RelativeTimePosition</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>PossibleRecordStorageMedia</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>CurrentPlayMode</name>
            <dataType>string</dataType>
            <allowedValueList>
                <allowedValue>NORMAL</allowedValue>
                <allowedValue>REPEAT_ALL</allowedValue>
                <allowedValue>INTRO</allowedValue>
            </allowedValueList>
            <defaultValue>NORMAL</defaultValue>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>TransportPlaySpeed</name>
            <dataType>string</dataType>
            <allowedValueList>
                <allowedValue>1</allowedValue>
                <allowedValue>vendor-defined</allowedValue>
            </allowedValueList>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>PossiblePlaybackStorageMedia</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>AbsoluteTimePosition</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>CurrentTrack</name>
            <dataType>ui4</dataType>
            <allowedValueRange>
                <minimum>0</minimum>
                <maximum>4294967295</maximum>
                <step>1</step>
            </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>CurrentTrackURI</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>CurrentTransportActions</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>NumberOfTracks</name>
            <dataType>ui4</dataType>
            <allowedValueRange>
                <minimum>0</minimum>
                <maximum>4294967295</maximum>
            </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>AVTransportURI</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>AbsoluteCounterPosition</name>
            <dataType>i4</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>CurrentRecordQualityMode</name>
            <dataType>string</dataType>
            <allowedValueList>
                <allowedValue>0:EP</allowedValue>
                <allowedValue>1:LP</allowedValue>
                <allowedValue>2:SP</allowedValue>
                <allowedValue>0:BASIC</allowedValue>
                <allowedValue>1:MEDIUM</allowedValue>
                <allowedValue>2:HIGH</allowedValue>
                <allowedValue>NOT_IMPLEMENTED</allowedValue>
                <allowedValue>vendor-defined</allowedValue>
            </allowedValueList>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>CurrentMediaDuration</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>A_ARG_TYPE_SeekMode</name>
            <dataType>string</dataType>
            <allowedValueList>
                <allowedValue>ABS_TIME</allowedValue>
                <allowedValue>REL_TIME</allowedValue>
                <allowedValue>ABS_COUNT</allowedValue>
                <allowedValue>REL_COUNT</allowedValue>
                <allowedValue>TRACK_NR</allowedValue>
                <allowedValue>CHANNEL_FREQ</allowedValue>
                <allowedValue>TAPE-INDEX</allowedValue>
                <allowedValue>FRAME</allowedValue>
            </allowedValueList>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>AVTransportURIMetaData</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>RecordStorageMedium</name>
            <dataType>string</dataType>
            <allowedValueList>
                <allowedValue>UNKNOWN</allowedValue>
                <allowedValue>DV</allowedValue>
                <allowedValue>MINI-DV</allowedValue>
                <allowedValue>VHS</allowedValue>
                <allowedValue>W-VHS</allowedValue>
                <allowedValue>S-VHS</allowedValue>
                <allowedValue>D-VHS</allowedValue>
                <allowedValue>VHSC</allowedValue>
                <allowedValue>VIDEO8</allowedValue>
                <allowedValue>HI8</allowedValue>
                <allowedValue>CD-ROM</allowedValue>
                <allowedValue>CD-DA</allowedValue>
                <allowedValue>CD-R</allowedValue>
                <allowedValue>CD-RW</allowedValue>
                <allowedValue>VIDEO-CD</allowedValue>
                <allowedValue>SACD</allowedValue>
                <allowedValue>MD-AUDIO</allowedValue>
                <allowedValue>MD-PICTURE</allowedValue>
                <allowedValue>DVD-ROM</allowedValue>
                <allowedValue>DVD-VIDEO</allowedValue>
                <allowedValue>DVD-R</allowedValue>
                <allowedValue>DVD+RW</allowedValue>
                <allowedValue>DVD-RW</allowedValue>
                <allowedValue>DVD-RAM</allowedValue>
                <allowedValue>DVD-AUDIO</allowedValue>
                <allowedValue>DAT</allowedValue>
                <allowedValue>LD</allowedValue>
                <allowedValue>HDD</allowedValue>
                <allowedValue>MICRO-MV</allowedValue>
                <allowedValue>NETWORK</allowedValue>
                <allowedValue>NONE</allowedValue>
                <allowedValue>NOT_IMPLEMENTED</allowedValue>
                <allowedValue>vendor-defined</allowedValue>
            </allowedValueList>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>RecordMediumWriteStatus</name>
            <dataType>string</dataType>
            <allowedValueList>
                <allowedValue>WRITABLE</allowedValue>
                <allowedValue>PROTECTED</allowedValue>
                <allowedValue>NOT_WRITABLE</allowedValue>
                <allowedValue>UNKNOWN</allowedValue>
                <allowedValue>NOT_IMPLEMENTED</allowedValue>
            </allowedValueList>
            </stateVariable>
            <stateVariable sendEvents="yes">
            <name>LastChange</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>CurrentTrackDuration</name>
            <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>TransportState</name>
            <dataType>string</dataType>
            <allowedValueList>
                <allowedValue>STOPPED</allowedValue>
                <allowedValue>PAUSED_PLAYBACK</allowedValue>
                <allowedValue>PAUSED_RECORDING</allowedValue>
                <allowedValue>PLAYING</allowedValue>
                <allowedValue>RECORDING</allowedValue>
                <allowedValue>TRANSITIONING</allowedValue>
                <allowedValue>NO_MEDIA_PRESENT</allowedValue>
            </allowedValueList>
            </stateVariable>
            <stateVariable sendEvents="no">
            <name>PossibleRecordQualityModes</name>
            <dataType>string</dataType>
            </stateVariable>
        </serviceStateTable>
        </scpd>
    
  • 控制點發送請求獲取 RenderingControl 服務 SCPD
        GET /dlna/Render/RenderingControl_scpd.xml HTTP/1.1
        Content-Length: 0
        HOST: 192.168.3.65
        User-Agent: Dalvik/2.1.0 (Linux; U; Android 7.1.1; OS105 Build/NGI77B)
        Connection: Keep-Alive
        Accept-Encoding: gzip
    
    
  • 設備響應控制點請求,返回 RenderingControl 服務 SCPD
        HTTP/1.1 200 OK
        CONTENT-LENGTH: 17287
        CONTENT-TYPE: text/xml
        DATE: Mon, 03 Aug 2020 01:12:41 GMT
        LAST-MODIFIED: Thu, 01 Jan 1970 00:00:00 GMT
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        X-User-Agent: redsonic
        CONNECTION: close
    
        <?xml version="1.0" encoding="utf-8"?>
    
        <scpd xmlns="urn:schemas-upnp-org:service-1-0">
          <specVersion>
            <major>1</major>
            <minor>0</minor>
          </specVersion>
          <actionList>
            <action>
              <name>GetBlueVideoBlackLevel</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentBlueVideoBlackLevel</name>
                  <direction>out</direction>
                  <relatedStateVariable>BlueVideoBlackLevel</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetBlueVideoGain</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentBlueVideoGain</name>
                  <direction>out</direction>
                  <relatedStateVariable>BlueVideoGain</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetBrightness</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentBrightness</name>
                  <direction>out</direction>
                  <relatedStateVariable>Brightness</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetColorTemperature</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentColorTemperature</name>
                  <direction>out</direction>
                  <relatedStateVariable>ColorTemperature</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetContrast</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentContrast</name>
                  <direction>out</direction>
                  <relatedStateVariable>Contrast</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetGreenVideoBlackLevel</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentGreenVideoBlackLevel</name>
                  <direction>out</direction>
                  <relatedStateVariable>GreenVideoBlackLevel</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetGreenVideoGain</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentGreenVideoGain</name>
                  <direction>out</direction>
                  <relatedStateVariable>GreenVideoGain</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetHorizontalKeystone</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentHorizontalKeystone</name>
                  <direction>out</direction>
                  <relatedStateVariable>HorizontalKeystone</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetLoudness</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>Channel</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentLoudness</name>
                  <direction>out</direction>
                  <relatedStateVariable>Loudness</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetMute</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>Channel</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentMute</name>
                  <direction>out</direction>
                  <relatedStateVariable>Mute</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetRedVideoBlackLevel</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentRedVideoBlackLevel</name>
                  <direction>out</direction>
                  <relatedStateVariable>RedVideoBlackLevel</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetRedVideoGain</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentRedVideoGain</name>
                  <direction>out</direction>
                  <relatedStateVariable>RedVideoGain</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetSharpness</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentSharpness</name>
                  <direction>out</direction>
                  <relatedStateVariable>Sharpness</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetVerticalKeystone</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentVerticalKeystone</name>
                  <direction>out</direction>
                  <relatedStateVariable>VerticalKeystone</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetVolume</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>Channel</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentVolume</name>
                  <direction>out</direction>
                  <relatedStateVariable>Volume</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetVolumeDB</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>Channel</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentVolume</name>
                  <direction>out</direction>
                  <relatedStateVariable>VolumeDB</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>GetVolumeDBRange</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>Channel</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
                </argument>
                <argument>
                  <name>MinValue</name>
                  <direction>out</direction>
                  <relatedStateVariable>VolumeDB</relatedStateVariable>
                </argument>
                <argument>
                  <name>MaxValue</name>
                  <direction>out</direction>
                  <relatedStateVariable>VolumeDB</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>ListPresets</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>CurrentPresetNameList</name>
                  <direction>out</direction>
                  <relatedStateVariable>PresetNameList</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SelectPreset</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>PresetName</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_PresetName</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetBlueVideoBlackLevel</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredBlueVideoBlackLevel</name>
                  <direction>in</direction>
                  <relatedStateVariable>BlueVideoBlackLevel</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetBlueVideoGain</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredBlueVideoGain</name>
                  <direction>in</direction>
                  <relatedStateVariable>BlueVideoGain</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetBrightness</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredBrightness</name>
                  <direction>in</direction>
                  <relatedStateVariable>Brightness</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetColorTemperature</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredColorTemperature</name>
                  <direction>in</direction>
                  <relatedStateVariable>ColorTemperature</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetContrast</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredContrast</name>
                  <direction>in</direction>
                  <relatedStateVariable>Contrast</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetGreenVideoBlackLevel</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredGreenVideoBlackLevel</name>
                  <direction>in</direction>
                  <relatedStateVariable>GreenVideoBlackLevel</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetGreenVideoGain</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredGreenVideoGain</name>
                  <direction>in</direction>
                  <relatedStateVariable>GreenVideoGain</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetHorizontalKeystone</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredHorizontalKeystone</name>
                  <direction>in</direction>
                  <relatedStateVariable>HorizontalKeystone</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetLoudness</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>Channel</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredLoudness</name>
                  <direction>in</direction>
                  <relatedStateVariable>Loudness</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetMute</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>Channel</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredMute</name>
                  <direction>in</direction>
                  <relatedStateVariable>Mute</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetRedVideoBlackLevel</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredRedVideoBlackLevel</name>
                  <direction>in</direction>
                  <relatedStateVariable>RedVideoBlackLevel</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetRedVideoGain</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredRedVideoGain</name>
                  <direction>in</direction>
                  <relatedStateVariable>RedVideoGain</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetSharpness</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredSharpness</name>
                  <direction>in</direction>
                  <relatedStateVariable>Sharpness</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetVerticalKeystone</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredVerticalKeystone</name>
                  <direction>in</direction>
                  <relatedStateVariable>VerticalKeystone</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetVolume</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>Channel</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredVolume</name>
                  <direction>in</direction>
                  <relatedStateVariable>Volume</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
            <action>
              <name>SetVolumeDB</name>
              <argumentList>
                <argument>
                  <name>InstanceID</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
                </argument>
                <argument>
                  <name>Channel</name>
                  <direction>in</direction>
                  <relatedStateVariable>A_ARG_TYPE_Channel</relatedStateVariable>
                </argument>
                <argument>
                  <name>DesiredVolume</name>
                  <direction>in</direction>
                  <relatedStateVariable>VolumeDB</relatedStateVariable>
                </argument>
              </argumentList>
            </action>
          </actionList>
          <serviceStateTable>
            <stateVariable sendEvents="no">
              <name>GreenVideoGain</name>
              <dataType>ui2</dataType>
              <allowedValueRange>
                <minimum>0</minimum>
                <maximum>100</maximum>
                <step>1</step>
              </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>BlueVideoBlackLevel</name>
              <dataType>ui2</dataType>
              <allowedValueRange>
                <minimum>0</minimum>
                <maximum>100</maximum>
                <step>1</step>
              </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>VerticalKeystone</name>
              <dataType>i2</dataType>
              <allowedValueRange>
                <minimum>-32768</minimum>
                <maximum>32767</maximum>
                <step>1</step>
              </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>GreenVideoBlackLevel</name>
              <dataType>ui2</dataType>
              <allowedValueRange>
                <minimum>0</minimum>
                <maximum>100</maximum>
                <step>1</step>
              </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>Volume</name>
              <dataType>ui2</dataType>
              <allowedValueRange>
                <minimum>0</minimum>
                <maximum>100</maximum>
                <step>1</step>
              </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>Loudness</name>
              <dataType>boolean</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>A_ARG_TYPE_InstanceID</name>
              <dataType>ui4</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>RedVideoGain</name>
              <dataType>ui2</dataType>
              <allowedValueRange>
                <minimum>0</minimum>
                <maximum>100</maximum>
                <step>1</step>
              </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>ColorTemperature</name>
              <dataType>ui2</dataType>
              <allowedValueRange>
                <minimum>0</minimum>
                <maximum>65535</maximum>
                <step>1</step>
              </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>Sharpness</name>
              <dataType>ui2</dataType>
              <allowedValueRange>
                <minimum>0</minimum>
                <maximum>100</maximum>
                <step>1</step>
              </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>A_ARG_TYPE_PresetName</name>
              <dataType>string</dataType>
              <allowedValueList>
                <allowedValue>FactoryDefaults</allowedValue>
                <allowedValue>InstallationDefaults</allowedValue>
                <allowedValue>Vendor defined</allowedValue>
              </allowedValueList>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>RedVideoBlackLevel</name>
              <dataType>ui2</dataType>
              <allowedValueRange>
                <minimum>0</minimum>
                <maximum>100</maximum>
                <step>1</step>
              </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>BlueVideoGain</name>
              <dataType>ui2</dataType>
              <allowedValueRange>
                <minimum>0</minimum>
                <maximum>100</maximum>
                <step>1</step>
              </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>Mute</name>
              <dataType>boolean</dataType>
            </stateVariable>
            <stateVariable sendEvents="yes">
              <name>LastChange</name>
              <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>A_ARG_TYPE_Channel</name>
              <dataType>string</dataType>
              <allowedValueList>
                <allowedValue>Master</allowedValue>
                <allowedValue>LF</allowedValue>
                <allowedValue>RF</allowedValue>
              </allowedValueList>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>HorizontalKeystone</name>
              <dataType>i2</dataType>
              <allowedValueRange>
                <minimum>-32768</minimum>
                <maximum>32767</maximum>
                <step>1</step>
              </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>VolumeDB</name>
              <dataType>i2</dataType>
              <allowedValueRange>
                <minimum>-32768</minimum>
                <maximum>32767</maximum>
              </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>PresetNameList</name>
              <dataType>string</dataType>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>Contrast</name>
              <dataType>ui2</dataType>
              <allowedValueRange>
                <minimum>0</minimum>
                <maximum>100</maximum>
                <step>1</step>
              </allowedValueRange>
            </stateVariable>
            <stateVariable sendEvents="no">
              <name>Brightness</name>
              <dataType>ui2</dataType>
              <allowedValueRange>
                <minimum>0</minimum>
                <maximum>100</maximum>
                <step>1</step>
              </allowedValueRange>
            </stateVariable>
          </serviceStateTable>
        </scpd>
    
Control
  • 控制點發送控制事件,設置播放內容
        POST /_urn:schemas-upnp-org:service:AVTransport_control HTTP/1.1
        Content-Type: text/xml; charset="utf-8"
        HOST: 192.168.3.65
        Content-Length: 1808
        SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"
        Connection: close
    
        <?xml version="1.0" encoding="utf-8"?>
        <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
           <s:Body>
              <u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
                 <InstanceID>0</InstanceID>
                 <CurrentURI>http://m801.music.126.net/20200803093741/d5af3e28d583af57bac81d5d341208ba/jdyyaac/055f/515d/0e0e/259b90e667f79bfeab219d9524a256d4.m4a</CurrentURI>
                 <CurrentURIMetaData>&lt;DIDL-Lite xmlns:dc=&quot;http://purl.org/dc/elements/1.1/&quot; xmlns:upnp=&quot;urn:schemas-upnp-org:metadata-1-0/upnp/&quot; xmlns=&quot;urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/&quot; xmlns:netease=&quot;http://music.163.com/dlna/&quot;&gt;&lt;item id=&quot;5276809&quot;&gt;&lt;dc:title&gt;&amp;#20891;&amp;#38431;&amp;#36827;&amp;#34892;&amp;#26354;&lt;/dc:title&gt;&lt;dc:creator&gt;Franz Schubert&lt;/dc:creator&gt;&lt;upnp:class&gt;object.item.audioItem.musicTrack&lt;/upnp:class&gt;&lt;res protocolInfo=&quot;http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;&quot; duration=&quot;00:05:05.000&quot;&gt;http://m801.music.126.net/20200803093741/d5af3e28d583af57bac81d5d341208ba/jdyyaac/055f/515d/0e0e/259b90e667f79bfeab219d9524a256d4.m4a&lt;/res&gt;&lt;upnp:artist&gt;Franz Schubert&lt;/upnp:artist&gt;&lt;upnp:album&gt;&amp;#21490;&amp;#19978;&amp;#26368;&amp;#20248;&amp;#32654;&amp;#38050;&amp;#29748;&amp;#23567;&amp;#21697;&amp;#31934;&amp;#21326;&lt;/upnp:album&gt;&lt;upnp:albumArtURI&gt;http://p1.music.126.net/UTrFxVAoq-Qh50dB_M74dw==/51677046522530.jpg&lt;/upnp:albumArtURI&gt;&lt;netease:musicId&gt;5276809&lt;/netease:musicId&gt;&lt;neteasemusicdlna&gt;1&lt;/neteasemusicdlna&gt;&lt;/item&gt;&lt;/DIDL-Lite&gt;</CurrentURIMetaData>
              </u:SetAVTransportURI>
           </s:Body>
        </s:Envelope>
    
    
  • 設備處理 Action 並響應控制點
        HTTP/1.1 200 OK
        CONTENT-LENGTH: 270
        CONTENT-TYPE: text/xml; charset="utf-8"
        DATE: Mon, 03 Aug 2020 01:12:41 GMT
        EXT:
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        X-User-Agent: redsonic
    
        <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body>
            <u:SetAVTransportURIResponse xmlns:u="urn:schemas-upnp-org:service:AVTransport:1"></u:SetAVTransportURIResponse>
            </s:Body> </s:Envelope>
    
  • 控制點發送控制事件,設置開始播放
        POST /_urn:schemas-upnp-org:service:AVTransport_control HTTP/1.1
        Content-Type: text/xml; charset="utf-8"
        HOST: 192.168.3.65
        Content-Length: 349
        SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#Play"
        Connection: close
    
        <?xml version="1.0" encoding="utf-8"?>
        <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
           <s:Body>
              <u:Play xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
                 <InstanceID>0</InstanceID>
                 <Speed>1</Speed>
              </u:Play>
           </s:Body>
        </s:Envelope>
    
    
  • 設備處理 Action 並響應控制點
        HTTP/1.1 200 OK
        CONTENT-LENGTH: 244
        CONTENT-TYPE: text/xml; charset="utf-8"
        DATE: Mon, 03 Aug 2020 01:12:41 GMT
        EXT:
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        X-User-Agent: redsonic
    
        
            
             
    
Event
  • 訂閱 AVTransport 事件
        SUBSCRIBE /_urn:schemas-upnp-org:service:AVTransport_event HTTP/1.1
        Content-Length: 0
        HOST: 192.168.3.65
        CALLBACK: 
        NT: upnp:event
        TIMEOUT: Second-36000
        Connection: close
    
    
  • 響應訂閱請求
        HTTP/1.1 200 OK
        DATE: Mon, 03 Aug 2020 01:12:40 GMT
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        CONTENT-LENGTH: 0
        X-User-Agent: redsonic
        SID: uuid:6ce3110c-1dd2-11b2-bab1-d0f9824e2d38
        TIMEOUT: Second-36000
    
    
  • 訂閱 AVTransport 事件
        SUBSCRIBE /_urn:schemas-upnp-org:service:RenderingControl_event HTTP/1.1
        Content-Length: 0
        HOST: 192.168.3.65
        CALLBACK: 
        NT: upnp:event
        TIMEOUT: Second-36000
        Connection: close
    
    
  • 響應訂閱請求
        HTTP/1.1 200 OK
        DATE: Mon, 03 Aug 2020 01:12:40 GMT
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        CONTENT-LENGTH: 0
        X-User-Agent: redsonic
        SID: uuid:6ce7ec36-1dd2-11b2-bab1-d0f9824e2d38
        TIMEOUT: Second-36000
    
    
  • 控制點查詢設備狀態:Position
        POST /_urn:schemas-upnp-org:service:AVTransport_control HTTP/1.1
        Content-Type: text/xml; charset="utf-8"
        HOST: 192.168.3.65
        Content-Length: 345
        SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#GetPositionInfo"
        Connection: close
    
        <?xml version="1.0" encoding="utf-8"?>
        <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
           <s:Body>
              <u:GetPositionInfo xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
                 <InstanceID>0</InstanceID>
              </u:GetPositionInfo>
           </s:Body>
        </s:Envelope>
    
    
  • 設備響應查詢
        HTTP/1.1 200 OK
        CONTENT-LENGTH: 1880
        CONTENT-TYPE: text/xml; charset="utf-8"
        DATE: Mon, 03 Aug 2020 01:12:41 GMT
        EXT:
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        X-User-Agent: redsonic
    
        <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body>
            <u:GetPositionInfoResponse xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
            <Track>0</Track>
            <TrackDuration>00:05:05</TrackDuration>
            <TrackMetaData>&lt;DIDL-Lite xmlns:dc=&quot;http://purl.org/dc/elements/1.1/&quot; xmlns:upnp=&quot;urn:schemas-upnp-org:metadata-1-0/upnp/&quot; xmlns=&quot;urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/&quot; xmlns:netease=&quot;http://music.163.com/dlna/&quot;&gt;&lt;item id=&quot;5276809&quot;&gt;&lt;dc:title&gt;&amp;#20891;&amp;#38431;&amp;#36827;&amp;#34892;&amp;#26354;&lt;/dc:title&gt;&lt;dc:creator&gt;Franz Schubert&lt;/dc:creator&gt;&lt;upnp:class&gt;object.item.audioItem.musicTrack&lt;/upnp:class&gt;&lt;res protocolInfo=&quot;http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;&quot; duration=&quot;00:05:05.000&quot;&gt;http://m801.music.126.net/20200803093741/d5af3e28d583af57bac81d5d341208ba/jdyyaac/055f/515d/0e0e/259b90e667f79bfeab219d9524a256d4.m4a&lt;/res&gt;&lt;upnp:artist&gt;Franz Schubert&lt;/upnp:artist&gt;&lt;upnp:album&gt;&amp;#21490;&amp;#19978;&amp;#26368;&amp;#20248;&amp;#32654;&amp;#38050;&amp;#29748;&amp;#23567;&amp;#21697;&amp;#31934;&amp;#21326;&lt;/upnp:album&gt;&lt;upnp:albumArtURI&gt;http://p1.music.126.net/UTrFxVAoq-Qh50dB_M74dw==/51677046522530.jpg&lt;/upnp:albumArtURI&gt;&lt;netease:musicId&gt;5276809&lt;/netease:musicId&gt;&lt;neteasemusicdlna&gt;1&lt;/neteasemusicdlna&gt;&lt;/item&gt;&lt;/DIDL-Lite&gt;</TrackMetaData>
            <TrackURI>http://m801.music.126.net/20200803093741/d5af3e28d583af57bac81d5d341208ba/jdyyaac/055f/515d/0e0e/259b90e667f79bfeab219d9524a256d4.m4a</TrackURI>
            <RelTime>00:04:32</RelTime>
            <AbsTime>00:04:32</AbsTime>
            <RelCount>2147483647</RelCount>
            <AbsCount>2147483647</AbsCount>
            </u:GetPositionInfoResponse>
            </s:Body> </s:Envelope>
    
  • 控制點查詢設備狀態:Volume
        POST /_urn:schemas-upnp-org:service:RenderingControl_control HTTP/1.1
        Content-Type: text/xml; charset="utf-8"
        HOST: 192.168.3.65
        Content-Length: 373
        SOAPACTION: "urn:schemas-upnp-org:service:RenderingControl:1#GetVolume"
        Connection: close
    
        <?xml version="1.0" encoding="utf-8"?>
        <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
           <s:Body>
              <u:GetVolume xmlns:u="urn:schemas-upnp-org:service:RenderingControl:1">
                 <InstanceID>0</InstanceID>
                 <Channel>Master</Channel>
              </u:GetVolume>
           </s:Body>
        </s:Envelope>
    
    
  • 設備響應查詢
        HTTP/1.1 200 OK
        CONTENT-LENGTH: 296
        CONTENT-TYPE: text/xml; charset="utf-8"
        DATE: Mon, 03 Aug 2020 01:12:41 GMT
        EXT:
        SERVER: Linux/4.9.118+, UPnP/1.0, Portable SDK for UPnP devices/1.6.13
        X-User-Agent: redsonic
    
        <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
        <s:Body>
            <u:GetVolumeResponse xmlns:u="urn:schemas-upnp-org:service:RenderingControl:1">
            <CurrentVolume>29</CurrentVolume>
            </u:GetVolumeResponse>
        </s:Body>
        </s:Envelope>
    
  • 設備 Notify LastChange 事件
        NOTIFY /evetSub HTTP/1.1
        HOST: 192.168.3.60:8058
        CONTENT-TYPE: text/xml; charset="utf-8"
        CONTENT-LENGTH: 364
        NT: upnp:event
        NTS: upnp:propchange
        SID: uuid:6ce3110c-1dd2-11b2-bab1-d0f9824e2d38
        SEQ: 2
        
        <e:propertyset xmlns:e="urn:schemas-upnp-org:event-1-0">
        <e:property>
        <LastChange>&lt;Event xmlns = &quot;urn:schemas-upnp-org:metadata-1-0/AVT/&quot;&gt;&lt;InstanceID val=&quot;0&quot;&gt;&lt;TransportState val=&quot;PLAYING&quot;/&gt;&lt;TransportStatus val=&quot;OK&quot;/&gt;&lt;/InstanceID&gt;&lt;/Event&gt;</LastChange>
        </e:property>
        </e:propertyset>            
    
    
  • 控制點響應設備上報
        HTTP/1.1 200 OK
        Content-Type: text/html; charset="utf-8"
        Server: Linux/4.4.21-perf+ CyberHTTP/1.0
        Content-Length: 0
        Date: Mon, 03 Aug 2020 09:12:49 GMT    
    
    
Presentation

待補充

說明

首先這里的抓包並不全,而且此部分后續也不會在補錄了。 但是即便是這樣,上面的這些協議包已經足夠對 UPnP 協議有個大概了解了。 而如果是了解 HTTP 協議和原理和服務器實現原理的同學,最后一部分 Presentation 也應該是很容易理解的。 但是市面上實現 Presentation 這部分的機器確實比較少見, 甚至可以說幾乎沒有。 我估摸着即便 DLNA 設備賣得再火,應該也比不上路由器賣得火吧。

還有一部分是關於 DHCP 部分的,這部分下次找時間另開專題解釋吧。 至於 Auto-IP 這個我是不太懂,而且也不想懂, 感覺這個協議是上個世紀的,現在基本上沒有路由器不支持 DHCP 了吧。 有市場才會促進技術的革新,這種老古董,現在也沒有必要再翻出來研究了。 不過商定 IP 的過程,可能以后在家庭設備主服務自舉方面可能會用得着。

DLNA 協議
DLNA 交互設計架構

先祭出架構圖:

上圖所展示的是 DLNA 網絡交互協議的組件框架,其中主要分成網絡基礎設施(網絡連接),網絡,協議棧,流媒體傳輸,媒體格式這么幾個部分。

網絡與連接部分主要由支持 IPv4 的網絡基礎設施提供,雖然在短距離場景下還會有藍牙的身影,但是現在市面上藍牙這方面的產品是越來越少了,其中主要原因是受制於藍牙的通信速率。 而 IPv4 (后續由擴充 IPv6 部分,但是我還沒花時間去研究那部分)作為應用最廣泛,擁有非常成熟的技術, 已經滲透到生活工作的方方面面的一個協議來說,作為 DLNA 協議的載體是再好不過了。 (IPv4 在 DLNA 官方的參考指南中是作為 DLNA 實現參考的一部分的)

多媒體應用在 IP 網絡中的一個好處是, IP 網絡提供了 QoS 的 功能,該功能會決定和優化資源在不同應用之間的分配方式, 換而言之,每個應用在 IP 網絡中都能得到響應的傳輸數據的機會。 像提供視頻流這類的應用,對網絡的延遲極為敏感,在 QoS 的幫助下,這類的數據包便會在網絡傳輸中享有更優的權限(Qos Priority 會指定為 User Priority (UP) 權限)。 DLNA QoS 模型試圖希望利用 UP 的 DLNA 應用程序能夠標記自己的數據包。但如果不希望使用 QoS 的應用也必須有途徑關閉此標記。 除了互操作性之外,DLNA QoS 模型還促進了所有 DLNA 流量類型之間公平,一致地使用優先級和平衡性能。 從而增強整體用戶體驗。

被控設備可向網絡中宣告自己的存在,CP 可發現環境中的設備,並加以控制,而這剛好是 UPnP 實現的功能, 因此, UDA 是 DLNA 發現控制協議的首選。

媒體管理使設備和應用程序可以在家庭網絡設備上識別,管理和分發媒體內容。 UPnP Audio/Video (AV) and UPnP Printer 技術滿足了家庭網絡的所有這些需求,並且是 DLNA 設備的媒體管理解決方案。 UPnP A/V 體系結構定義了 UPnP AV 設備與關聯的控制點應用程序之間的交互模型。 UPnP AV 設備可以以多種形式實例化自己,包括(但不限於)電視,VCR,DVD 播放器,機頂盒,立體聲系統,靜止圖像相機,便攜式媒體播放器,手機和PC。 UPnP AV 體系結構允許設備使用任何媒體傳輸協議以任何格式支持娛樂內容。 UPnP AV 規范定義了家庭網絡上的兩種類型的UPnP設備:UPnP AV MediaServers 和 UPnP AV MediaRenderer。 該規范還定義了 UPnP AV MediaServer 和 UPnP AV MediaRenderer 托管的四種服務。暗示存在與 UPnP AV 設備和服務交互的 UPnP 控制點。 這些服務隱含了 CP 和設備之間的交互關系。 1) Content Directory Service 向環境中提供可下載/播放/的媒體內容; 2) Connection Manager Service 決定那些內容可以從 UPnP AV MediaServer 傳輸至 UPnP AV MediaRenderer 設備; 3) AV Transport Service: 管理和控制流的傳輸; 4) Rendering Control Service: 展示播放媒體內容。

UPnP Printer 架構定義了 UPnP 打印設備和控制點之間的交互模型。 比如一台支持 UPnP 的照片打印設備,該設備支持了 UPnP PrintEnhanced:1 服務, 該服務又將 XHTML-Print / CSS Print / CSS Print增強版面擴展指定為打印頁面描述語言。

媒體格式描述了,如何對內容進行編碼和格式化以在家庭網絡上進行傳輸和呈現。 DLNA 媒體格式模型旨在實現網絡互操作性的 baseline,同時鼓勵媒體編解碼器技術的不斷創新。

DLNA 定義了三類設備,圖像/音頻和視頻設備,每一類設備都定義了強制支持的格式和可選支持的格式。 媒體格式的描述文檔是由一系列的屬性/參數/系統/編碼級別所決定的, 為了提供不同的設備類別之間的良好的交互性能,媒體交互模型最好支持最基本的不同媒體格式之間的轉換。 另外,DLNA 媒體格式模型指定了有關在可選格式和強制格式之間進行轉換的規則,以確保可以在所有設備上展示內容。 DLNA 的交互性設計文檔中有對此的詳細說明,我有時間也會將相關信息在接下來的文檔中說明。

媒體傳輸模塊定義了媒體內容是如何在家庭網絡中傳輸的。 DLNA 設備通過 HTTP 協議從媒體源獲取媒體內容,而支持 HTTP 方式傳輸是媒體內容,則是傳輸中最基礎的部分, 其次某些媒體內容可能是通過 RTP 傳輸,因此也推薦實現此功能。

DLNA 設備模型

在家庭網絡中存在不同的設備類型,如 HND (家庭網絡設備)和 MHD(手持設備), 他們在網絡連接和媒體格式方面都有不同的需求。 本小節為這些相似的設備提供了統一的術語和用法模型。

為了支持家庭網絡設備和移動手持設備之間的互操作性,家庭網絡設備有可能滿足相應移動手持設備的所有要求。移動手持設備也需要滿足相應家庭網絡設備的所有要求。 在這些情況下,此類設備既是 HND 的成員,也是 MHD 設備的成員。

但是在大多數情況下可能不可行,因此實現互操作性的另一種方法是通過一組設備,這些設備將能夠在這兩個設備類別之間提供橋接或內容轉換服務。 這些設備屬於稱為家用信息設備(HID)的設備類別。以下總結了這些設備:

如第4節所述,遵守DLNA家庭網絡設備互操作性准則的設備具有六個體系結構層。 總之,它們是用於描述一致內容的媒體格式, 媒體管理,用於描述如何找到和控制內容以實現不同的系統使用情況,用於設備控制的設備發現和控制, 媒體傳輸用於內容傳輸,網絡堆棧用於IPv4協議要求,網絡連接用於支持不同的網絡物理層。

關於 DLNA 涉及的四大服務

DLNA 主要涉及的四個服務 Content Directory Service, Connection Manager Service, AV Transport Service, Rendering Control Service 均參考 UPnP 分別針對這四個服務定義。下面我們分別對這四個服務做詳細的介紹。

  1. Content Directory Service 在 home network 中可能有許多設備有各種各樣的內容想要分享給其他設備展示(如: 音樂,圖片,視頻)。 為了方便 homeowner 能方便的瀏覽設備中的內容並投射到相應的播放器中播放,一般存儲這些內容的設備都自己存在 UI 界面幫助 homeowner 控制內容的播放, 如果這樣的內容比較多,這樣的操作是非常繁瑣的。Content Directory Service 即是為了解決此場景的用戶需求,提出了一種為瀏覽設備中內容的統一機制, 以供 UI 設備能方便的存儲/瀏覽/獲取/查找各個設備中的內容。
    一般該服務的設備類別屬於:urn:schemas-upnp-org:service:ContentDirectory:1
  2. Connection Manager Service 該類服務用於對設備之間的 stream 關系進行建模,任何一個擁有一個 ConnectionManager 實例的設備都可以發送(或接收)媒體流。 該服務為控制點(CP)提供如下功能:
    a. 將相應的設備配置為源設備(或者渲染設備)
    b. 發現(查找)當前網絡中正在進行傳輸的信息
    c. 幫助設備之間建立(斷開)鏈接
    ConnectionManager 是一個正對流傳輸協議的通用抽象,使用此服務可以使控制點不必關注設備之間采用何種流傳輸協議。
    一般該服務的設備類別屬於:urn:schemas-upnp-org:service:ConnectionManager:1
  3. AV Transport Service UPnP AV Architecture 中提到的 AV 架構定義了通用的 UPnP 控制點和 AV 設備之間的交互邏輯。該架構獨立於任何設備類型,媒體格式和傳輸協議。 他支持廣泛的設備類型,比如說 TV, VCR, CD/DVD, MP3, PC ... AV Architecture 還允許傳輸不同類格式的媒體內容(比如: MPEG2, JPEG, MP4, WMA, BMP, NTSC, PAL, ATSC)。 還支持多種類型的傳輸協議(如:IEC-61883/IEEE-13194, HTTP GET, RTP, HTTP PUT/POST, TCP/IP ...)。
    AV Archtecure 旨在實現一種 AV 架構,它能支持傳輸任何的媒體合適類型; 不需要控制點過多的干預就能隨心所欲的控制媒體內容在設備之間傳輸;同時使控制點獨立與任何的傳輸協議,媒體格式,並在新傳輸協議或者格式發明的時候能快速方便的兼容;同時還能以極小的代價輕松的在任何設備運行。
    (上敘目的僅僅出現在 UPnP AV Arch... 第一版本中)
    一般該服務的設備類別屬於:urn:schemas-upnp-org:service:AVTransport:1
  4. Rendering Control Service 大部分的渲染設備都具備多項配置參數,當這些參數設置不同的時候,渲染的內容也會隨之改變(比如 TV 在播放內容的時候可以改變屏幕的亮度,對比度,清晰度之類的參數)。 而 RenderingControl 服務則是提供一組控制接口用於控制渲染設備的各項參數。
    一般該服務的設備類別屬於:urn:schemas-upnp-org:service:RenderingControl:1
DLNA AVTransport 服務狀態轉移流程

以圖祭天,以下是 V1 版本的:

以圖祭天,以下是 V2 版本的:

DLNA AVTransport 事件模型

自從 AVTransport 支持多實例,傳統的 UPnP 的事件模型機制便已無法區分擁有同樣狀態的多個實例。 因此 AVTransport 服務設計了一套獨特的機制 ( LastChange )來描述各個實例獨自的狀態變化。 在這個模型中 LastChange 是唯一可 Event 的事件。其他的狀態變量都需要通過 LastChange 來間接的向定戶發送狀態變化 ( Position 信息除外,A_ARG_TYPE_ 信息除外, A_ARG_TYPE_ 禁止直接和間接的 eventing )。

為了使感興趣的接受端能及時准確的獲取到媒體內容的 Position 信息,接受端主動查詢此類信息是更明智的選擇, 因此 RelativeTimePosition, AbsoluteTimePosition, RelativeCounterPosition, AbsoluteCounterPosition, 不會通過 LastChange 機制來 Eventing, 而是由感興趣的終端來主動獲取。 對於終端,它可以在 AVTransport 實例處於 PLAYING, RECORDING, TRANSITIONING 狀態時的任意時刻, 以其自定義的速率調用 GetPositionInfo 來獲取上敘信息,

自從采取了 LastChange 這種方式之后,多個狀態會聚集到 LastChange 中,此時可能並不會馬上觸發 LastChange 的發送, 而是會等到 moderation 過期時間的到來。當這個發生的時候,LastChange 會 Follow 標准 UPnP 的機制將事件發送出去。 所有對此感興趣的 DMC 都會收到此消息。 (但據我目前測試的情況所知,很多 DMC 因為處理自己的訂閱不當,會誤認為其他 DMC 控制的內容是自己的, 從而影響到自己的狀態,從而影響到整個 DLNA 的投屏體驗)

在 LastChange 將狀態變量發送出去之后,它應該將自己緩存中的狀態都清理干凈,以准備存儲下一次的狀態變化。 (注意:清除狀態的時候不需要再產生額外的事件,以免產生不必要的壅塞)

因為 LastChange State 是帶有 filtter 屬性的,因此在有些狀態可能在一個 Moderation Priod Expires 里面產生多次變化。 這時, LastChange 僅會為這個變量保存一個用戶表示設備當前狀態的單一的值。 標准的 UPnP 協議規定了 CP 何時去訂閱接收的事件,在訂閱之時,當前所有的可 event 時間都應該 return 給 subscriber. 雖然 LastChange 是唯一的直接可發送狀態變量,但是使用 LastChange 狀態變量的當前值響應事件訂閱請求並不是很有意義。 因此,當接收到事件訂閱時,設備應使用該服務的所有有效實例中所有(間接事件)狀態變量的當前值進行響應。

DLNA AVTransport 狀態變量以及其作用和值域

下面我只會對特定的幾個常用的狀態做說明,其他狀態變量的說明還是請參考《UPnP-av-AVTransport-v1-Service.pdf》。

  • TransportState
    不管是播放還是錄制,該狀態包含下列表列舉的值,而該狀態構成了 AVTransport 服務的核心狀態, 設備也並不需要實現所有的這些狀態(比如只支持播放的設備就沒有 RECODING 之類的狀態),
    注意: PAUSED_RECORDING 和 STOPPED 的意義不盡相同,PAUSED_RECORDING 說明已經開始錄制,只是暫停了,恢復的時候能盡快的恢復。
    注意: PAUSED_PLAYBACK 和 PAUSED_RECORDING 也不是只同一意義
    其他字段的意義和字面意義一致
    以下為 TransportState 的值域:(Request 代表必須實現,Option 代表可選實現)
    • STOPPED Request
    • PLAYING Request
    • TRANSITIONING Option
    • PAUSED_PLAYBACK Option
    • PAUSED_RECORDING Option
    • RECORDING Option
    • NO_MEDIA_PRESENT Option
  • TransportStatus
    此狀態不同於 state, state 主要是指示 AVTransport 服務的當前播放狀態,而 Status 主要是指示當前 AVTransport 服務是否正常, 因為在一些時候可能因為網絡的原因出現暫時的卡頓,或者 DMR 有某些異常需要 DMC (或其他)接收。 此時可以通過該狀態變量通知定戶。
    服務無異常,該狀態變量值為 OK
    DLNA 官方只定義了一個模糊的錯誤值 ERROR_OCCURRED, 但並沒有指定該值的詳細錯誤場景,因此如果有特殊需求,供應商可自行設計自己的錯誤代碼
    以下為 TransportStatus 的值域:(Request 代表必須實現,Option 代表可選實現)
    • OK Request
    • ERROR_OCCURRED Request
  • PlaybackStorageMedium
    用於指示當前播放的存儲媒介。 該狀態是可選( Option )實現的。 具體的值請參考 《 UPnP-av-AVTransport-v1-Service.pdf 》
  • RecordStorageMedium
    用於指示當前錄音的存儲媒介,和 PlaybackStorageMedium 的值域相同。 該狀態是可選( Option )實現的。 具體的值請參考 《 UPnP-av-AVTransport-v1-Service.pdf 》
  • PossiblePlaybackStorageMedia
    使用 CSV 格式的一個字符串列表,指明設備可支持從那些存儲介質上播放媒體內容。
  • PossibleRecordStorageMedia
    使用 CSV 格式的一個字符串列表,指明設備可將媒體內容存儲到哪些介質上。
  • CurrentPlayMode
    當前的播放模式,擁有一下的可選值:
    • NORMAL Request
    • SHUFFLE Option
    • REPEAT_ONE Option
    • REPEAT_ALL Option
    • RANDOM Option
    • DIRECT_1 Option | 播放當前一首,然后結束播放
    • INTRO Option | 代表每首歌播放一個很簡短的 Sample
  • TransportPlaySpeed
    當前播放速度,以有理數/分數字符串表示,比如說當前播放速度為 x1, 那么值就是 "1"。 速度 1 是必須實現的,其他根據供應商自己的應用場景自己設置。
  • RecordMediumWriteStatus
    當前存儲介質的讀寫狀態,有一下可選項目:WRITABLEPROTECTEDNOT_WRITEBLEUNKNOWNNOT_IMPLEMENTED
  • CurrentRecordQualityMode
    當前的錄制質量等級,有以下可選項目:
  • PossibleRecordQualityModes
  • NumberOfTracks
    描述當前播放的那個音軌,如果設備當前未存在播放或者錄制,那么當前的值應當為 0, 但對於沒有音軌概念的設備(如磁帶播放器),則值恆為 1, 對於 CD 類的設備,該值應該對應 CD 音軌數, 而對於網絡播放設備來說,如果當前 AVTransportURI 指向的是播放列表中的內容,那么該值列表的總數,否則值恆為 1。
  • CurrentTrack
    指代當前播放的 Track 在 NumberOfTracks 中的哪一個,如果當前 NumberOfTracks 為 0, 那么此值即為 0。 注意,NumberOfTracks 的步進應當為 1。
  • CurrentTrackDuration
    指示當前 Track 的總播放時常。 時間戳格式應當為 H+:MM:SS[.F+] 或者 H+:MM:SS[.F0/F1]。時間戳前可能攜帶有 '-' 或者 '+' 字符。 如:1:01:02 即為一個小時一分鍾兩秒,該時間戳也可表示為 01:01:02, 01:01:02, 01:01:02.000 ...
  • CurrentMediaDuration
    指示當前 AVTransport 實例所播放媒體的總時長。 值的格式和 CurrentTrackDuration 相同。 CurrentMediaDuration 的時常應該總是大於等於 CurrentTrackDuration 的時長。
  • CurrentTrackMetaData
    指示使用 DIDL-Lite XML 模板框架描述的媒體信息。該信息也可能存在於 AVTransportURIMetaData 字段, 或者音頻文件中(如 MP3 的 ID3 標簽中)。
  • CurrentTrackURI
  • AVTransportURI
    這里的 URI 可能是單個的 URI, 也可能是一個 URI 的列表。 當 AVTransport 只有一個 Track 時,那么 AVTransportURI 和 CurrentTrackURI 是相同的。
  • AVTransportURIMetaData
  • NextAVTransportURI
  • NextAVTransportURIMetaData
  • RelativeTimePosition
    指示當前播放處在 media track 的時長,從 Track 的開始位置開始計算。值的范圍為 "00:00:00" 到 CurrentTrackDuration 狀態變量指示的范圍。
    對於磁帶類雖然有播放列表,但無法定位具體的 Track 起始位置的這類設備,該值即代表從磁帶開始播放到現在這個位置的時長。
    如果不支持,那么該字段應該被填寫 NOT_IMPLEMENTED
  • AbsoluteTimePosition
    指示當前播放處在整個 PlayList 中的時長。
    如果當前設備不支持檢測播放的 Position 但是能夠知道何時能夠播放結束。那么在播放過程中應該使用 NOT_IMPLEMENTED 而在播放結束使用 END_OF_MEDIA
    如果不支持,那么該字段應該被填寫 NOT_IMPLEMENTED
  • RelativeCounterPosition
    意義與 RelativeTimePosition 類似,不過這里采用的數據格式為 i4,單位為 s, (這是我猜的,如果清楚的朋友可以告訴我)
    當不支持該狀態變量的時候應該填寫 i4 的最大值。
  • AbsoluteTimePosition
    意義與 RelativeTimePosition 類似,不過這里采用的數據格式為 i4,單位為 s, (這是我猜的,如果清楚的朋友可以告訴我)
    當不支持該狀態變量的時候應該填寫 i4 的最大值。
  • CurrentTransportActions
  • LastChange
    是唯一的一個用於 eventing 的狀態變量,它通過 UPnP event 的標准機制通知定戶。 所有的其他狀態都間接的通過 LastChange 來發送事件。 理論上該變量的一級標簽為當前 AVTransport 的實例 ID, 二級標簽為各個狀態變量,以及其對應的值。
    下面為一個 LastChange 例子:
    <?xml version="1.0" encoding="UTF-8"?>
    <e:propertyset xmlns:e="urn:schemas-upnp-org:event-1-0">
        <e:property>
            <LastChange>
            <Event xmlns=”urn:schemas-upnp-org:metadata-1-0/AVT_RCS">
                <InstanceID val=”0”>
                <Brightness val=”36”/>
                <Contrast val=”54”/>
                ...
                </InstanceID>
                <InstanceID val=”1”>
                <Mute channel=”Master” val=”0”/>
                <Volume channel=”CF”val=”24”/>
                ...
                </InstanceID>
                </Event>
            </LastChange>
        </e:property>
    </e:propertyset>
    
    
  • A_ARG_TYPE_SeekMode
  • A_ARG_TYPE_SeekTarget
  • A_ARG_TYPE_InstanceID

以下是 2.0 中新增加的

  • CurrentMediaCategory
  • PlaybackStorageMedium
  • DRMState
  • A_ARG_TYPE_DeviceUDN
  • A_ARG_TYPE_ServiceType
  • A_ARG_TYPE_ServiceID
  • A_ARG_TYPE_StateVariableValuePairs
  • A_ARG_TYPE_StateVariableList

其次供應商還可以制定自己的 status。

DLNA AVTransport 定義的 Action
  • SetAVTransportURI
    該方法用於設置 AVTransport 實例所使用的資源。 該方法會攜帶三個標簽信息:InstanceID, CurrentURI, CurrentURIMetaData 建議實例在執行該方法時先檢查一下 MIME-type。 CP 可以在制定 CurrentURI 的時候還可以指定該資源的 Metadata. MetaData 使用 DIDL-Lite 格式的字符串表示, 這里可以參考 CurrentURIMetaData。如果不想指定該字段,那么可以給該字段設置為空串。
    在任何 AVTransport 處於任何狀態的時候都可以執行該 Action,但要注意做好狀態之間的轉移和收尾工作。
    當該 URI 指向的是單個媒體文件,那么 NumberOfTracks 的值將變為 1, 如果指向的是一個 PlayList 那么 NumberOfTracks 將變成列表的大小。
    如果播放器無法成功定位到 URI 指向的資源,或者不能成功下載,那么此時 TransportState 應該變為 STOPPED 狀態
    如果當前的狀態為 PLAYING, 但是在向用戶成功展示之前需要花費比較長一段時間來緩沖數據,那么該狀態應該為 TRANSITIONING, 成功播放后變為 PLAYING
    如果當前播放結束之后狀態處於 NO MEDIA PRESENT, 那么 TransportState 應該變成 STOPPED 狀態。
    如果遇到出錯的情況,請參考一下列表返回錯誤狀態
  • SetNextAVTransportURI
    該字段與 SetAVTransportURI 類似,傳輸的內容也類似,主要是用於當前媒體內容將要播放完,需要推送下一條資源,用於提前緩沖,提升用戶體驗作用。 但是目前幾乎很少有 DMC 支持此 Action(好像 QPlay 支持),不多做贅述,詳情請翻閱參考文檔。
  • GetMediaInfo
    獲取當前播放 Media 的信息,響應時需要攜帶以下信息(如果有些狀態不支持,傳輸的時候指定空串即可)(該 Action 多見於視頻內 DMC 實現):
    • InstanceID
    • NrTracks 參考狀態變量 NumberOfTracks
    • MediaDuration 參考狀態變量 CurrentMediaDuration
    • CurrentURI 參考狀態變量 AVTransportURI
    • CurrentURIMetaData 參考狀態變量 AVTransportURIMetaData
    • NextURI 參考狀態變量 NextAVTransportURI
    • NextURIMetaData 參考狀態變量 NextAVTransportURIMetaData
    • PlayMedium 參考狀態變量 PlaybackStorageMedium
    • RecordMedium 參考狀態變量 RecordStorageMedium
    • WriteStatus 參考狀態變量 RecordMediumWriteStatus
  • GetTransportInfo
    獲取當前播放 Media 的狀態信息,響應時需要攜帶以下信息(如果有些狀態不支持,傳輸的時候指定空串即可):
    • InstanceID
    • CurrentTransportState 參考狀態變量 TransportState
    • CurrentTransportStatus 參考狀態變量 TransportStatus
    • CurrentSpeed 參考狀態變量 TransportPlaySpeed
  • GetPositionInfo
    獲取當前播放 Media 的播放位置信息,響應時需要攜帶以下信息(如果有些狀態不支持,傳輸的時候指定空串即可):
    • InstanceID
    • Track
    • TrackDuration 參考狀態變量 CurrentTrackDuration
    • TrackMetaData 參考狀態變量 CurrentTrackMetaData
    • TrackURI 參考狀態變量 CurrentTrackURI
    • RelTime 參考狀態變量 RelativeTimePosition
    • AbsTime 參考狀態變量 AbsoluteTimePosition
    • RelCount 參考狀態變量 RelativeCounterPosition
    • AbsCount 參考狀態變量 AbsoluteCounterPosition
  • GetDeviceCapabilities
  • GetTransportSettings
  • Stop
    該 Action 用於停止指定 InstanceID 的當前的資源輸出。
    在某些設備上,Stop Action 會導致 Position 的變化,CP 可以通過 CurrenTrack 的事件通知得知該變化。當然也可以通過 GetPositionInfo 來主動獲取該變化。
  • Play
    該 Action 會攜帶兩個參數:IstanceID, speed
    根據當前設置的播放模式,預先設置的 Position, 預先設置的 URI, 開始播放。
    直到播放結束,或者 TransportState 的狀態變成 STOPPED / PAUSED_PLAYBACK
    該 Action 應該可以在 STOPPED, PLAYING 或者 PAUSED_PLAYBACK 狀態下執行,在其他狀態下,可能會返回 701, 但是更具 vendor 設計的區別。
    如果在執行 Play Action 之后用戶還需要等待相當長的一段時間才能開始 PLAY, 那么此時最好加入 TRANSITIONING 狀態,等到真正開始播放再變成 PLAYING 狀態。(這里之的過長在平時音視頻應用中一般是 1s)
  • Pause
    當設備處於 PLAYING 狀態的時候,該動作用於停止當前制定 Instance 的播放進度,任何該資源對外展示的信息都處於靜態的樣子 (針對圖像則畫面處於靜止狀態,對於音頻則聲音處於安靜狀態)。
    區別於 STOP 狀態, PAUSED 狀態必須保存當前的播放進度,而且當前播放的資源還保持在前台
    對於處於錄制設備,暫停則是讓錄制處在發生 PAUSE Action 的時間點,當再次收到 Record 的時候從該時間點開始錄制。
  • Record
    該 Action 用於啟動指定實例(instance)開始錄制。 如果 AVTransportURI 有指定資源,那么錄制信息應從當前指定資源獲取(比如:mic)。 如果沒有指定,那么就選擇默認的。 在以上兩種方案中,是否將錄制信息轉播到屏幕或者揚聲器都取決於設備的交互。 如果錄制設備實現了 內容服務器,那么 record 的內容應該以設備自定義的方式轉存到設備配置的內容服務器(ContentDirectory)。 具體地來說,UPnP 沒有 定義所記錄內容在 ContentDirectory 層次結構中的位置。
    該 Action 只能從 STOPPED 或者 PAUSED_RECORDING 狀態下啟動,如果是其他狀態則會返回 701, 但實際的交互還是要結合設備自身定義來實現。
    當成功執行該 Action 之后,狀態將變成 RECORDING。
  • Seekmarkdown播放模式,該 Action 有兩個參數,InstanceID 和 NewPlayMode, 其中 NewPlayMode 所支持的值域可參考 CurrentPlayMode。

     

    該字段在任何時候都可以執行,當設置該字段之后,接下來的播放動作應該與當前設置的一致。

  • SetRecordQualityMode
  • GetCurrentTransportActions
  • 供應商自定義 Actions
CSV ( Comma Separated Value ) List

屬於 DLNA 定義的兩種派生數據類型的一種,主要用於承載 list 的值(一維數組)。 CSV 主要用於表達傳輸控制時的具體數據,該字段通常與 XML 中的 type="xsd:xxx" 屬性搭配使用。 xsd:xxx 主要用於指示 CSV 承載的內容的數據類型。 list 中的值可以是統一類型(比如 xsd:string), 也可以是不同的(比如 xsd:string,xsd:integer), 但一般情況下,只會存在一種類型。

參考文檔
  1. RFC / UPnP / 藍牙官方的文檔參考鏈接我就不放在這里了,DLNA 官方盡量完全資料在文首資料包中。
  2. 藍牙PAN
  3. 藍牙核心技術概述
DLNA V4.0 RemoteUserInterface HTML5(基於 HTML5 的遠程用戶界面 )

因為查找到中文規范文檔,或者官方的中文解釋,標題中的中文標題為我直譯而來,並非官方定義。該部分定義出現在 2016 年的 《Part 6-1: Remote User Interface - HTML5》 文檔中(資料包中包含該文檔)。

RUI-H 定義了用 HTML5 開發遠程用戶界面的准則。該規范依賴與 HTML5 的通用性,開發商可以利用 HTML5 開發一套 APP, 並可在任何支持 HTML5 標准的瀏覽器的設備上運行。 利用該技術,公司可以快速迭代自己的產品,並降低了開發/運營成本,為每個設備提供一個統一的 UI。

RUIx-CP 的定義基本涵蓋在 29341-12-1,29341-12-2,29341-12-10,29341-12-11 這幾個文檔中,但是這幾個都是要付費購買的,目前沒有搜集到這幾個文檔。

注意: 下文中可能會經常性的提到有無 A/V 功能模塊,此處的意思並非指不帶音視頻播放功能,而是之不攜帶 DLNA 中定義的 AV 模塊。

簡介

2012 年 3 月 19 日–數字生活網絡聯盟®(DLNA®)和 RVU Alliance™ 宣布RVU聯盟遠程用戶界面(RUI)已被納入 DLNA 互操作性指南。 兩家公司之間的關系強調了消費者在整個數字家庭中訪問服務提供商內容的重要性。這些新准則允許服務提供商將其功能的外觀導出到DLNA認證的設備。

DLNA 了解到,如今的消費者並不局限於客廳看電影。DLNA 主席兼總裁 Nidhish Parikh 表示: “他們希望能夠在多種設備上訪問從服務提供商的訂閱中獲得的優質內容。” “通過將RVU RUI納入我們的互操作性指南,我們為消費者提供了新穎的創新方式來連接和使用其數字內容。 想象一下能夠在非DVR設備上訪問DVR功能。RVU RUI使這成為可能,並且現在可以包含在DLNA認證的產品中。”

關於 RVU, RVU 是一種技術協議,它向 UPnP 設備發現和 DLNA 音頻/視頻內容流規范添加了一個遠程用戶界面。 RVU RUI 圖形命令對於 RVU 協議是唯一的,並且不與任何其他現有協議或標准相對應。 該協議基於客戶端-服務器體系結構,其特定目的是利用低成本客戶端,並且不增加硬件開銷。該服務器通常由內容服務提供商提供, 該內容服務提供商允許向一個或多個消費電子設備分發和管理視頻以及一致的用戶體驗。 RVU 協議白皮書介紹了 RVU 技術及其應用。RVU 協議規范中包含 RVU 技術,並且通過認證程序可確保協議符合性。

RVU 聯盟是一個由服務提供商,技術公司和消費電子產品制造商組成的行業聯盟。 RVU RUI 是一種遠程用戶界面技術,當與兼容的 RVU 服務器一起使用時,可以在兼容的客戶端設備上准確顯示服務提供商的用戶界面。 RVU 聯盟董事會主席 Henry Derovanessian 表示:“將 RVU RUI 納 入 DLNA 互操作性指南是 RVU 規范發展的另一個主要里程碑。” “與DLNA的合作進一步將 RVU 應用於互聯家庭設備的全球社區,為消費者和服務提供商都提供了幫助。”

RVU 系統已經投放市場,服務提供商 DIRECTV 於 2011 年底推出了 HR34 RVU 服務器。 在 1 月份的 2012 年國際 CES 活動中,三星在其 2011 年連接的三種電視型號中展示了該協議,表明 RVU 支持將在在所有 2012 年連接的電視型號中。

關於支持 RVU 的產品,可在 https://rvuproject.org/products 中查詢,其中帶有 RVU 認證的設備一般在信源選擇處可以找到類似下面的圖表:

下面是幾個實際生產出來設備中的例子:

下面是實現 RVU 需要用到的技術標准:

  • Device and Service Discovery and Control
    • UPnP SSDP
  • Media Management, Distribution, and Control
    • Digital Living Network Alliance™ (DLNA®)
    • UPnP
  • Content streaming and media format interoperability
    • DLNA AV Transport HTTP
  • Digital content protection
    • DTCP-IP link protection
  • Remote User Interface
    • Low-overhead remote RUI, including remote control commands and status from client devices to the server

下面我們就針對其中涉及到 DLNA 和 UPnP 的技術進行介紹。

名詞解釋
  • UI User Interface 用於和用戶交互的一套界面應用。
  • Remote UI 一個由設備上的服務提供的一台用於用戶交互的界面應用(User Interface),該應用可有一個或者多個設備提供。
  • RUI-H HTML5 Reomte User Interface
  • RUI-H Content 實現 UI 所需要的 HTML 文檔以及其附屬文檔(如 image,javascript, CSS, fonts ...),但此處的內容不包括與 HTML5MediaElement 相關的 A/V 資源。
HTML5 RUI 設備功能

RUI-H 交互指南定義了如下的設備功能(原文為 Device Functionis):

  • RUIHS( RUI-H Server ) 顧名思義,該功能提供一個或者多個接口給一個或者多個 UI 用戶進行 HTML5 范疇內的操作(如基於 HTML 的 GET/POST 方法)。
  • RUIHS-CP( RUI-H Server Control Point) 該功能是用於為瀏覽器選擇 RUIHS 提供 RUI-H 服務的控制點。
  • RUIHC( RUI-H Client ) 該功能為基於公開的 HTML5 設計標准設計的一個客戶端,用於給用戶進行 HTML5 應用展示和接受用戶操作。
  • RUIHC-CP( RUI-H Client Control Point ) 該功能作為一個控制者,協助 RUIHC 和 RUIHS 建立鏈接。
  • RUIHTS( RUI-H Transport Server ) RUIHTC( RUI-H Transport Client ) 這個兩個功能分別用於擔任 RUI-H Content 的分發和接受。
  • RUIHUA( RUI-H User Agent ) 該功能為 RUIHC 的一部分,接受 RUIHTC 接收到的信息進行解碼,渲染,展示,並提供輸入功能接受用戶的操作,對 RUIS 進行檢索,修改,控制等操作。
HTML5 RUI 設備能力

RUI-H 交互指南定義了如下設備能力(原文為 Device Capabilities)(下文中所有的設備能力(或者說角色)的簡稱在 DLNA Guildeline 中都用 + 包圍,比如說 RUIHPL 在 GL 中的表示為 +RUIHPL+,但在此文中我將把 + 符號全部去除 ):

  • RUIHPL ( RUI-H Pull Controller ) 該角色可從環境中暴露的 RUIHSRC 發現加載 RUIH Content 並向用戶展示和提供交互借口, 該角色包含如下 Functions: RUIHS-CP, RUIHTC, RUIHUA 和可選的用於接收媒體資源的客戶端 ( depend vendor )。
  • RUIHSRC ( RUI-H Source Controller ) 該角色擁有想環境中暴露 RUI-H Content 的能力, 該角色包含如下 Functions: RUIHS, RUIHTS 和可選的用於發送媒體資源的服務端。
  • RUIHSINK ( RUI-H Sink Capability ) 該角色用於向環境中展示 RUI-H Content and Exposing HTML5 remote UI functionality(個人感覺有歧義), 該角色包含如下 Functions: RUIHC, RUIHTC, RUIHUA 和可選的用於接收媒體資源的客戶端。
  • RUIHCTRL ( RUI-H Controller ) 該角色用與發現 RUIHSRC 和 RUIHSINK,並協助這兩者建立連接。 該角色包含如下 Functions: RUIHS-CP, RUIHC-CP

下圖為 RUI-H 各個角色在 2Box 和 3Box 模型中的應用:

模型介紹

目前 DLNA Gildeline 介紹了三種使用場景,但其實際的應用可以根據 Vendor 的產品類型靈活組合,一下例子只是為了加深大家理解尚需提到模塊和功能的概念(詳細說明請參考: 《DLNA GL June 2016 - Part 6-1 ...》)。

  1. 2Box 模型,其中一方為 +RUIHSRC+ 一方為 +RUIHPL+。 其中 +RUIHSRC+ 負責存儲,分發 RUIH Content,而 +RUIHPL+ 負責發現,獲取,與 +RUIHSRC+ 進行交互。 基於具體 vendor 的設備定義可能還會具備 A/V 內容傳輸展示方面的功能。
    以下為無 A/V 方面的模型的交互流程:
    1. 發現遠程的 RUI-H remote 設備
    2. 請求 RUI-H Content
    3. 傳輸 RUI-H
    4. 傳輸用戶交互以及 SRC 的響應信息
    具體圖示如下:

    下圖為有 A/V 方面的模型的交互流程,在上圖的基礎上增加了如下兩個流程:
    5. 請求 A/V Content
    6. 傳輸 A/V Content
  2. 3Box 模型,該模型不涉及 A/V 內容的發現和傳輸。其中涉及 +RUIHCTRL+, +RUIHSRC+, +RUIHSINK+。 其中 +RUIHCTRL+ 負責 發現環境中的 +RUIHSRC++RUIHSINK+, 並協助這方建立鏈接。 其中 +RUIHSRC+ 主要負責存儲,分發 RUIH Content。 而 +RUIHSINK+ 負責負責獲取 RUI-H Content 和與 +RUIHSRC+ 進行交互。 該模型與上述模型的區別在於吧控制部分的邏輯獨立出來了。
    一下為該模型下的一種使用場景(比如你的手機發現和瀏覽 RUI, 而你家的 TV 也支持��你突然��在手機上控制 TV 展示你在手機選中的內容 )以及操作步驟:
    1. 發現環境中的 RUI-H 服務
    2. 組織 xxx 與 xxx 建立連接
    3. 從 SRC 處獲取 RUI-H Content
    4. 傳輸 RUI-H Content 至 SINK
    5. 或許由用戶主導所觸發導致 SRC 和 SINK 中的交互
    6. 發現環境中的其他 SINK2
    7. 組織 SINK2 和 SRC 建立連接
    8. 從 SRC 處獲取 RUI-H Content
    9. 傳輸 RUI-H Content 至 SINK2
    10. 或許由用戶主導所觸發導致 SRC 和 SINK 中的交互
    圖示如下:
  3. 3Box 模型,該模型涉及 A/V 內容的發現和傳輸。其中涉及 +RUIHCTRL+, +RUIHSRC+, +RUIHSINK+,而且還引入了 DMC, DMS, DMR 角色。 其中 DMR 包含 +RUIHCTRL++RUIHSINK+, DMC 則包含 +RUIHSRC+, 其流程大致如下:
    1. 設備開機之后 DMR 中的 +RUIHCTRL+ 會自動的發現環境中的 +RUIHSRC+ 並讓 +RUIHSINK++RUIHSRC+ 建立鏈接
    2. 其中 +RUIHSRC+ 包含 DMC 功能模塊,可發現環境中的 DMS, 並將 DMS 嵌入到 RUI 中,一便於展示給用戶。
    3. 用戶在 RUI 上的操作會導致 +RUIHSINK++RUIHSRC+ 的交互
    4. 當用戶在 RUI 上選定了展示的 DMS 內容之后,+RUIHSINK+ 會告知 +RUIHSRC+, 而 +RUIHSRC+ 會調用 DMC 去促使 DMR 和 DMS 建立鏈接,並在 DMR 上呈現給用戶
    圖示如下:
RUI-H Server 與 Client

關於 RUI 的 Server 和 Client 的設備,在此我僅做一些簡單的描述,具體細節還請參考 《UPnP-rui-RemoteUI... 》 相關文檔。

RUI 的 Server 和 Client 都與 UDA1.0 中提到的設備服務模型兼容,在承載 Server & Client 的設備在網絡環境中上線之后可向環境中廣播 RUI Server 和 Client 的存在。 其中 RUI Server 的設備類型為 urn:schemas-upnp-org:device:RemoteUIServerDevice:1 RUI Server 的服務(Service)類型為 urn:schemas-upnp-org:service:RemoteUIServer:1 。 RUI Client 的設備類型為 urn:schemas-upnp-org:device:RemoteUIClientDevice:1, RUI Client 的服務(Service)類型為 urn:schemas-upnp-org:service:RemoteUIClient:1

其設備描述文件可以搭在在 root device 下, 如下圖所示(在此處僅列出 RUI Server device 的描述文檔 ):

RUI-H Server

RUI Server GL 中定義的標准 Status 有下面幾種,其中 A_ARG_TYPE_DeviceProfile, A_ARG_TYPE_CompatibleUIs,A_ARG_TYPE_String,是必須支持的 Status, 其中 A_ARG_TYPE_DeviceProfile 指明當前 Server 支持的所有協議,而 A_ARG_TYPE_CompatibleUIs 則提供了一個當前 Server 兼容的所有 UI 列表。

  • Status
    • UIListingUpdate
    • A_ARG_TYPE_DeviceProfile
      該狀態的值為一個 XML 文檔,知名了當前 Server 所支持的協議列表
    • A_ARG_TYPE_URI
    • A_ARG_TYPE_CompatibleUIs
      Server 必須支持 <= 10KB 的描述文件
      該 Status 的值為一個 XML 文檔,它指定了當前 Server 所能提供的 UI 列表,其每一個 UI 同時還包含了 UI 名字,ID, 簡明描述, 適配各個客戶端尺寸大小的 IconList, 以及最重要的所提供服務的協議以及 URI 列表。
      CP 正是通過該文檔中的協議列表配置 Client 通過何種方式進行 UI 交互的。
      該變量的值可通過 GetCompatibleUIs Action 獲取
      具體的文檔格式請參考 《UPnP-rui-RemoteUIServer-Service ... 》文檔。
    • A_ARG_TYPE_String
    • A_ARG_TYPE_Int

RUI Server GL 中定義的標准 Actions 有下面幾種

  • Actions
    • GetCompatibleUIs
      該請求需要提供三個參數:InputDeviceProfile, UIFilter, UIListing
      從指定的 DeviceProfile 中提供一個 UIs 列表,因為有可能 Server 並不支持制定的 DeviceProfile 或者沒有 Filter / UIListing 中指定的類型設備,則此返回值有可能為空 UI 列表。
    • SetUILifetime
      該請求需要提供兩個參數:UI, Lifetime
      該 Action 用於 Set UILifetime
      因為 Server 在交互的過程中可能保留 Client 的一些信息,這些信息在 Client disconnect 之后是不需要在使用的,此時 Client 最好給 Server Set 此值, 當設置時間到達的時候 Server 將會釋放與此 Client 通信的實例的相關資源。

目前 UPnP RUI 中定義的協議簡稱有如下幾種:

  • Remote UI Protocols
    • HTTP/HTML
    • RDP 微軟定義的一個遠程桌面協議(Remote Desktop Protocol)
    • VNC AT&T 定義的一套虛擬網絡控制台協議(Vritual Network Console)
    • XRT2 Intel 提出的一個遠程 I/O 協議
    • LRDP 諾基亞提出的一個輕量級遠程顯示協議(Remote Display Protocol)
    • XHT Samsung 提出的可擴展的家庭影院(eXpandable Home Theater)
    • XGXML 西門子提出的一套遠程控制協議
    • UIF 飛利浦提出的一套 UI Fragments protocol.
RUI-H Client

RUI Client GL 中定義的標准 Status 有下面幾種

  • DeviceProfile
    Required
    該值為一個 UTF8 編碼的 XML 文檔,該文檔下的 protocol 標簽標識了 Client 支持的每一個協議
    具體的 XML 格式請參考 Server 中的 DeviceProfile 說明
  • CurrentConnections
    Required
    該狀態是必須實現的
    該狀態的值為一個逗號分割的的字符串,該字符串包含一個遞增序列和一系列的 UI URI
    其中第一個字段為遞增序列,該序列自第一次設置 ConnectionsUpdateID, 開始每一次完成 Connect() 或者 Disconnect() 之后就遞增一次, 遞增的最大值為 2147483647(i4 的最大值),當再次遞增時,該值回歸 0
    隨后的第二個字段則為當前 Active 的 UI URI,如果當前沒有任何 Active UI,那么展示的應該是本地的一個默認 UI 地址,如:http://localhost:8080/null
    再之后的 URI 為建立第一個會話之后使用過的所有 UI URI,
    其值格式如下:5604,XRT://1.23.345.1/My_Music_Player0xa12c67,local://127.4.6.1/null,VNC://1.23.345.2/My_Photo_Viewer,RDP://1.23.345.3/Super_Chess
  • CurrentConnectionsEvent
    Optional
    該狀態變量用於在 CurrentConnections 發生變化的時候給定戶發送變更消息的用途
  • CompatibleUIsUpdateIDEvent
  • A_ARG_TYPE_String
  • A_ARG_TYPE_CompatibleUIs
  • A_ARG_TYPE_DisplayMessageType
  • A_ARG_TYPE_InputDataType
  • A_ARG_TYPE_Int

RUI Client GL 中定義的標准 Actions 有下面幾種:(對於能從名字一眼看清楚工用的 Action,或者不太用途不太廣泛的 Action, 在此不再過多贅述)

  • Connect
    Required
    創建一個新的鏈接,該請求必須在建立連接成功,或者失敗后才能返回
    該 Action 接受兩個參數, RequestedConnections 和 CurrentConnectionsList,他們的格式的都與 status 中介紹的 CurrentConnections 一致
    一個 RequestedConnections 中只能有一個 connect,如果存在多個,那么該請求會直接返回 701; 而且如果該參數的第一個參數與 Client 目前的 ConnectionsUpdateID 不同,那么此時該請求會直接返回 705;
    而 CurrentConnectionsList 則還包含了需要保持鏈接的一些 UI ( 如果當前 Client 支持 on-hold session 的話 )
  • Disconnect
    Required
    參數與 Connect 一致
    區別在於對於支持 on-hold 的 Client, Disconnect() 當前的連接之后會展示 CurrentConnectionsList 中的第一個 Connections.
  • GetCurrentConnections
    Required
  • GetDeviceProfile
    Required
  • GetUIListing
  • AddUIListing
  • RemoveUIListing
  • DisplayMessage
  • ProcessInput
DeviceProfile 的示例
CompatibleUIs 的示例
一些交互指南

建立鏈接需要兩個元素,一個是 ConnectionsUpdateID, 另外一個是 CompatiableUI URI。 其中,ConnectionsUpdateID 需要和 Client 中 Current ID 一致,因此在 Connect() 之前,最好調用 GetCurrentConnections 獲取當前的 ID; 而 CompatiableUI URI 則可以向 Server 發起 GetCompatibleUIs() 去獲取。 當然在匹配 Client 和 Server 的時候可能還需要用 GetDeviceProfile() 去獲取 Client 的 DeviceProfile。

而 Disconnect() 的時候亦是如此,用 GetCurrentConnections() 去獲取當前的 ID 和 Activity URI。

用戶在交互的過程中應該是可以自由的選擇環境中的 UIs 的。 一種可實現此交互的方案是在 Client 中維護一個 UIs List, 而 CP 可以調用 Add(Remove/Get) 來控制這個列表。

Client 中 Display Message 這個功能是一個給用戶發送提示信息的功能,比如在建立連接的時候,可能需要花費比較多的時間來 loading, 此時可以使用此方式快速的給用戶一個信息,但是實現此信息展示的方式油 vendor 來決定。

參考文檔
Platinum 解析
國內各個投屏軟件缺陷公示以及分析 - 不完全指南
設備發現體驗

LEVEL 1 體驗最好,LEVEL 2 體驗次之,LEVEL 3 體驗最差

  1. LEVEL 1 Kugou, Netease Music, iQiyi, bilibili
  2. LEVEL 2 騰訊視頻, 快點投屏, 芒果 TV
  3. LEVEL 3 QQMusic,
愛奇異
  1. BD一貫的吃相一貫就難看,其實發現體驗是一流的一批,但是他做了一個很微妙的操作,在 IOS 手機 iqiyi 會屏蔽樂播的投屏接受端,在 Android 應用會屏蔽 QQ 音樂的接收端。 至於是 iqiyi 的本意,還是 iqiyi 插件供應商的本意就不得而知了。不要問我為什么知道,因為我將樂播的 SCPD 改成其他 廠商名字就可以被發現了。amazing 啊。
netease-cloud-music
  1. 目前案只支持 HH:MM:SS 格式的時間戳,不支持 H+:MM:SS[.F+] 或者 H+:MM:SS[.F0/F1] 格式的時間戳。
  2. 切換曲目的時候,如果 DMR 發送的 LastChange Size 過大(900字節),很容易出現 DLNA 失去連接。
  3. 通過 GetPositionInfo 獲取 Position 信息之后並不會校驗 TrackDuration, TrackMetaData, TrackURI 等信息, 從而會影響 DMR 在播放其他內容時的體驗。
  4. 收到的 LastChange 消息過大會直接報 DLNA lost connection。
bilibili
  1. 發送 SetAVTransportURI 的時候 DIDL 中攜帶的 dc:creator 字段英文語法錯誤, unkown 應該改成 UNKNOWN (不區分大小寫)。
  2. SetAVTransportURI 中附帶的 DIDL-Lite 格式不符合規范。
用 Node.js 實現 DLNA 測試工具
最后的最后

此系列文章本着開源,自由,分享的前提,盡可能的提供准確的數據,但是本人無法窮盡所有細節。 因此作如下申明: 1)如果有人或者公司因為本文而造成損失,本人不承擔任何責任。 2)如果本文檔中的內容有涉及到法律法規風險,請即使提醒我,我會及時做出修改或者刪除,如無提前通知本人對此造成的后果不負任何責任。 3)本文不承諾,不保證會即使的跟進最新的協議狀態,因此,如果因為文檔過時產生的偏差,從而導致您產品的缺陷,本人不負任何責任。 4)如本文涉及侵權行為,請聯系 majtsdd@163.com

原創文章,版權所有,轉載請獲得作者本人允許並注明出處
我是留白;我是留白;我是留白;(重要的事情說三遍)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM