整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          MediaSession在57中的開場白

          MediaSession在57中的開場白

          技術提升美好事物發生的概率。

          Technologically, for greater probability to be happy.

          從chrome57開始,MediaSession就可以在chrome中被使用了。MediaSession讓我們可以對通知欄進行自定義操作,方便了用戶直接在通知欄對媒體的控制。在有了MediaSession后,即使在鎖屏界面,我們也可以看到媒體的信息,以及控制播放狀態。對于提供了播放列表的網頁,我們還可以直接切換至另一個媒體。下圖為官方提供的樣圖。

          一、調起MediaSession的經過

          在Chromium運行時,有一個Browser進程,若干個Render進程和一個GPU進程。后面會涉及到Browser進程以及Render進程。通常情況下,一個Tab或者多個Tab對應一個Render進程。

          MediaSession的代碼在browser端,且在c++端和java端都有相對應的部分。接下來先分析一下調起MediaSession的代碼實現。

          看播放視頻的時候是如何調起MediaSession的(多種情況,此處只舉出一種)。情景為點擊播放媒體按鍵,開始創建播放器與MediaSession。下面是主要的流程:

          下面的是相對應的代碼:

          最初,會創建一個HTMLMediaElement的DOM節點。該代碼在 HTMLMediaElement.cpp中。在這里會調用play(),若調用成功媒體能正常播放,則promise為完成態,否則為拒絕態。

          接下來進入到剛剛被調用的play()方法中:

          若視頻能播放,則調用PlayInternal(),否則就返回錯誤信息。代碼也在HTMLMediaElement.cpp中。

          PlayInternal()中設置好當前是否暫停的狀態與能否自動播放后繼續調用UpdatePlayState(). UpdatePlayState()中在開始播放前設置好播放速率和聲音,然后調用GetWebMediaPlayer的Play()開始播放。代碼都在HTMLMediaElement.cpp里面。

          同樣,這里也是調用UpdatePlayState()來更新播放狀態。此處與前面提到的play()與UpdatePlayState()有什么關系呢?前面的我們理解為在Webkit層的播放器,而后者理解為Content層的播放器。此處代碼在webmediaplay_impl.cc中。

          UpdateSate()里調用SetDelegateState()來處理媒體狀態,然后在SetDelegateState()中處理了三種狀態,即關閉、播放與暫停。播放的話,則調用DidPlay()。代碼在webmediaplayer_impl.cc。

          DidPlay()中主要向Browser端發送了IPC消息,于是播放媒體的消息就傳給了Browser端了。代碼在renderer_webmediaplayer_delegate.cc里面。下面的代碼就直接到Browser部分了。

          這里Browser端收到IPC消息后執行OnMediaPlaying(),此處看到203行,已經調用了MediaSessionControllersManager的RequestPlay(),后面部分開始已經到MediaSession的主體了。代碼在media_web_contents_observer.cc。

          MediaSessionControllersManager 控制所有的MediaSessionController。在RequestPlay()的時候會創建一個MediaSessionController,在構造控制器的時候還會設置好對應的 MediaSession,一個MediaSessionController控制一個MediaSession。通過controller初始化MediaSession里的播放器。

          代碼在media_session_controller.cc里面。

          二、C++端整體邏輯

          MediaSessionControllersManager:由MediaWebContentsObserver調用,通過MediaSessionController來控制MediaSession。上面就是一個map,通過MediaPlayerId來對應不同的Controller,這里也可以看出一個播放器對應了一個Controller。

          MediaSessionController:當我們在通知中對媒體進行操作時,Java端會發送消息過來,在Controller中收到后向Renderer發送IPC消息,控制Renderer里面的WebMediaPlayer。在構造MediaSessionController的時候還會給他對應的MediaSessionImpl賦值,此處可以看出每個MediaSessionController控制一個MediaSessionImpl。

          MediaSessionImpl:整個MediaSession的中心,處理所有媒體與通知有關的事項。主要是播放狀態與焦點狀態的控制。其中的NotifyAboutStateChange()提供了能否控制與是否暫停兩個狀態,這兩個值對控制播放的非常重要。能否控制表示我們是否能通過通知欄控制這個播放器,如果不能控制,則通知欄中不會出現。

          MediaSessionServiceImpl:提供一些服務,主要是設置播放狀態與播放支持的動作,且這兩項決定了通知欄所展現的界面。代碼中setMetadata()也就是設置元數據,里面包含圖片,aritst,title等數據。Action部分也就是與支持的動作有關,如播放、暫停、前進、后退這些。

          MediaMetadataSanitizer:功能性的類,MediaMetadataSanitizer檢查與處理元數據格式的合理性,調用了Sanitize()。

          AudioFocusDelegateAndroid:RequestAudioFocus()和AbandonAudioFocus()分別為通過JNI獲取與丟失焦點。當系統要求MediaSession進行暫停、繼續等操作的時候,從Java端發送消息給MediaSession,如電話(系統級別),上面給出OnSuspend()的例子。AudioFocus相關簡介下面提到一些。

          MediaSessionAndroid:MediaSession通過JNI主要向Java端發送的是三個狀態變化,即MediaSessionStateChanged、MediaSessionMetadataChanged和MediaSessionActionsChanged。用戶在通知欄進行操作時消息會從Java端傳過來(UI級別),如上面例子給出的Suspend()。

          Audio Focus

          1. Audio Focus 是用來處理多處音頻同時出現的情況。
          2. 音頻焦點類型有persistent和transient,長音頻為persistent,會停止所有其他音頻;短音頻(如短信音效)為transient,只會降低其他音頻的聲音。
          3. 音頻都需與MediaSession交互,且MediaSession狀態(ACTIVE, SUSPENDED, INACTIVE)代表當前是否獲取到焦點與是否有音頻。
          4. AudioFocusManager提供焦點支持。音頻需要播放時通知MediaSession,MediaSession向AudioFocusManager請求獲取焦點,成功獲取焦點則播放,同時通知其他MediaSession焦點變化。
          5. 音頻停止播放則從MediaSession移除,同時丟棄焦點,AudioFocusManager通知其他MediaSession焦點變化。
          6. 焦點的控制是通過一個棧來實現。(實際為一個list容器,除了出入棧還用到了remove)

          三、Java端整體邏輯

          AudioFocusDelegate.java:主要有requestAudioFocus()和abandonAudioFocus(),分別用來獲取與丟棄焦點。這一部分通過SDK里面的AudioManager,最終調用了API來獲取與丟棄系統的音頻焦點。

          MediaSessionImpl.java:主要有mediaSessionStateChanged(), mediaSessionMetadataChanged(), mediaSessionActionsChanged()。都是native端傳給Java端的消息,分別表示媒體會話的狀態、元數據、動作的改變。此處動作表示網頁支持的操作集合。

          MediaSessionTabHelper.java :一方面處理native端傳過來的信息,令一方面對各種操作都基本調用MediaNotificationManager來處理,相當于傳遞了控制信息。

          到這里整個MediaSession的整個流程就走完了,MediaSession從播放器創建的時候創建出來, 并溝通了瀏覽器與通知欄,同時對媒體焦點的支持,讓其成為一個非常好的特性。

          檔對象模型DOM(Document Object Model)定義訪問和處理HTML文檔的標準方法。DOM 將HTML文檔呈現為帶有元素、屬性和文本的樹結構(節點樹).

          將HTML代碼分解為DOM節點層次圖:

          HTML文檔可以說由節點構成的集合,DOM節點有:

          1. 元素節點:上圖中<html>、<body>、<p>等都是元素節點,即標簽。

          2. 文本節點:向用戶展示的內容,如<li>...</li>中的JavaScript、DOM、CSS等文本

          3. 屬性節點:元素屬性,如<a>標簽的鏈接屬性href="https://www.google.com"。

          一. 獲取節點的方法

          1. 頂層API

          documment.getElementById() 返回一個元素;

          document.getElementsByName() 返回一個dom數組,具有相同name值的;

          domdocument.getElementsByTagName() 返回一個dom數組,具有相同的標簽名;

          2. 通過父節點獲取

          parent.firstChild;

          parent.lastChild;

          parent.childNodes;

          parent.children;

          parent.getElementsByTagName;

          3. 通過子節點獲取childNode.parentNode;

          4. 通過臨近節點獲取;

          neighbour.previousSibling;

          neighbour.nextSibling;

          二. 插入節點的方法

          appendChild()

          insertBefore();

          三. 刪除節點的方法

          replaceChild(替換節點, 被替換的節點)

          removeChild(被移除的節點)

          四. 復制節點

          cloneNode(bool)bool為true時,深復制,復制節點以及節點的所有子節點;

          bool為false時,淺復制,只復制節點本身;

          OM可以將任何HTML描繪成一個由多層節點構成的結構。節點分為12種不同類型,每種類型分別表示文檔中不同的信息及標記。每個節點都擁有各自的特點、數據和方法,也與其他節點存在某種關系。節點之間的關系構成了層次,而所有頁面標記則表現為一個以特定節點為根節點的樹形結構。

          節點中的各種關系可以用傳統的家族關系來描述,相當于把文檔樹比喻成家譜。接下來,將把DOM節點關系分為屬性和方法兩部分進行詳細說明

          屬性

          父級屬性parentNode

          每個節點都有一個parentNode屬性,該屬性指向文檔樹中的父節點。對于一個節點來說,它的父節點只可能是三種類型:element節點、document節點和documentfragment節點。如果不存在,則返回null

          parentElement

          parentNode跟parentElement除了前者是w3c標準,后者只ie支持(chrome現在都支持)

          當父節點的nodeType不是1,即不是element節點的話,它的parentElement就會是null

          一般情況parentNode可以取代parentElement的所有功能

          parentElement匹配的是parent為element的情況,而parentNode匹配的則是parent為node的情況。element是包含在node里的,它的nodeType是1

          <div id="myDiv"></div>
          <script>
          console.log(myDiv.parentElement);//body
          console.log(document.body.parentElement);//html
          console.log(document.documentElement.parentElement);//null
          console.log(document.parentElement);//null
          </script>
          

          [注意]在IE瀏覽器中,只有Element元素節點才有該屬性,其他瀏覽器則是所有類型的節點都有該屬性

          <div id="test">123</div>
          <script>
          //IE瀏覽器返回undefined,其他瀏覽器返回<div id="test">123</div>
          console.log(test.firstChild.parentElement);
          //所有瀏覽器都返回<body>
          console.log(test.parentElement);
          </script>
          

          子級屬性

          childNodes

          childNodes是一個只讀的類數組對象NodeList對象,它保存著該節點的第一層子節點

          <ul id="myUl">
           <li><div></div></li>
          </ul>
          <script>
          var myUl=document.getElementById('myUl');
          //結果是只包含一個li元素的類數組對象[li]
          console.log(myUl.childNodes);
          </script>
          

          children

          children是一個只讀的類數組對象HTMLCollection對象,但它保存的是該節點的第一層元素子節點

          <div id="myDiv">123</div>
          <script>
          var myDiv=document.getElementById('myDiv');
          //childNodes包含所有類型的節點,所以輸出[text]
          console.log(myDiv.childNodes);
          //children只包含元素節點,所以輸出[]
          console.log(myDiv.children);
          </script>
          

          childElementCount

          返回子元素節點的個數,相當于children.length

          [注意]IE8-瀏覽器不支持

          <ul id="myUl">
           <li></li>
           <li></li>
          </ul>
          <script>
          var myUl=document.getElementById('myUl');
          console.log(myUl.childNodes.length);//5,IE8-瀏覽器返回2,因為不包括空文本節點
          console.log(myUl.children.length);//2
          console.log(myUl.childElementCount);//2,IE8-瀏覽器返回undefined
          </script>
          

          firstChild

          第一個子節點

          lastChild

          最后一個子節點

          firstElementChild

          第一個元素子節點

          lastElementChild

          最后一個元素子節點

          上面四個屬性,IE8-瀏覽器和標準瀏覽器的表現并不一致。IE8-瀏覽器不考慮空白文本節點,且不支持firstElementChild和lastElementChild

          //ul標簽和li標簽之間有兩個空白文本節點,所以按照標準來說,ul的子節點包括[空白文本節點、li元素節點、空白文本節點]。但在IE8-瀏覽器中,ul的子節點只包括[li元素節點]
          <ul>
           <li></li>
          </ul>
          
          <ul id="list">
           <li>1</li>
           <li>2</li>
           <li>3</li>
          </ul>
          <script>
          console.log(list.firstChild);//標準瀏覽器中返回空白文本節點,IE8-瀏覽器中返回<li>1</li>
          console.log(list.lastChild);//標準瀏覽器中返回空白文本節點,IE8-瀏覽器中返回<li>3</li>
          console.log(list.firstElementChild);//標準瀏覽器中<li>1</li>,IE8-瀏覽器中返回undefined
          console.log(list.lastElementChild);//標準瀏覽器中<li>3</li>,IE8-瀏覽器中返回undefined
          </script>
          

          同級屬性

          nextSibling

          后一個節點

          previousSibling

          前一個節點

          nextElementSibling

          后一個元素節點

          previousElementSibling

          前一個元素節點

          與子級屬性類似,上面四個屬性,IE8-瀏覽器和標準瀏覽器的表現并不一致。IE8-瀏覽器不考慮空白文本節點,且不支持nextElementSibling和previousElementSibling

          <ul>
           <li>1</li>
           <li id="myLi">2</li>
           <li>3</li>
          </ul>
          <script>
          var myLi=document.getElementById('myLi');
          console.log(myLi.nextSibling);//空白節點,IE8-瀏覽器返回<li>3</li>
          console.log(myLi.nextElementSibling);//<li>3</li>,IE8-瀏覽器返回undefined
          console.log(myLi.previousSibling);//空白節點,IE8-瀏覽器返回<li>1</li>
          console.log(myLi.previousElementSibling);//<li>1</li>,IE8-瀏覽器返回undefined
          </script>
          

          方法

          包含方法hasChildNodes()

          hasChildNodes()方法在包含一個或多個子節點時返回true,比查詢childNodes列表的length屬性更簡單

          <div id="myDiv">123</div>
          <script>
          var myDiv=document.getElementById('myDiv');
          console.log(myDiv.childNodes.length);//1
          console.log(myDiv.hasChildNodes());//true
          </script>
          
          <div id="myDiv"></div>
          <script>
          var myDiv=document.getElementById('myDiv');
          console.log(myDiv.childNodes.length);//0
          console.log(myDiv.hasChildNodes());//false
          </script>
          

          contains()

          contains方法接受一個節點作為參數,返回一個布爾值,表示參數節點是否為當前節點的后代節點。參數為后代節點即可,不一定是第一層子節點

          <div id="myDiv">
           <ul id="myUl">
           <li id="myLi"></li>
           <li></li>
           </ul>
          </div>
          <script>
          console.log(myDiv.contains(myLi));//true
          console.log(myDiv.contains(myUl));//true
          console.log(myDiv.contains(myDiv));//true
          </script>
          

          [注意]IE和safari不支持document.contains()方法,只支持元素節點的contains()方法

          //IE和safari報錯,其他瀏覽器返回true
          console.log(document.contains(document.body));
          

          關系方法

          compareDocumentPosition()

          compareDocumentPosition方法用于確定節點間的關系,返回一個表示該關系的位掩碼

          000000 0 兩個節點相同

          000001 1 兩個節點不在同一個文檔(即有一個節點不在當前文檔)

          000010 2 參數節點在當前節點的前面

          000100 4 參數節點在當前節點的后面

          001000 8 參數節點包含當前節點

          010000 16 當前節點包含參數節點

          100000 32 瀏覽器的私有用途

          <div id="myDiv">
           <ul id="myUl">
           <li id="myLi1"></li>
           <li id="myLi2"></li>
           </ul>
          </div>
          <script>
          //20=16+4,因為myUl節點被myDiv節點包含,也位于myDiv節點的后面
          console.log(myDiv.compareDocumentPosition(myUl));
          //10=8+2,因為myDiv節點包含myUl節點,也位于myUl節點的前面
          console.log(myUl.compareDocumentPosition(myDiv));
          //0,兩個節點相同
          console.log(myDiv.compareDocumentPosition(myDiv));
          //4,myLi2在myLi1節點的后面
          console.log(myLi1.compareDocumentPosition(myLi2));
          //2,myLi1在myLi2節點的前面
          console.log(myLi2.compareDocumentPosition(myLi1));
          </script>
          

          isSameNode()和isEqualNode()

          這兩個方法都接受一個節點參數,并在傳入節點與引用節點相同或相等時返回true

          所謂相同(same),指的是兩個節點引用的是同一個對象

          所謂相等(equal),指的是兩個節點是相同的類型,具有相等的屬性(nodeName、nodeValue等等),而且它們的attributes和childNodes屬性也相等(相同位置包含相同的值)

          [注意]firefox不支持isSameNode()方法,而IE8-瀏覽器兩個方法都不支持

          <script>
          var div1=document.createElement('div');
          div1.setAttribute("title","test");
          var div2=document.createElement('div');
          div2.setAttribute("title","test");
          console.log(div1.isSameNode(div1));//true
          console.log(div1.isEqualNode(div2));//true
          console.log(div1.isSameNode(div2));//false
          </script>
          

          寫在后面

          這里列的很多方法大家可能都沒見過,或者學過JQuery之后對這些方法既熟悉又陌生,大家可以把我當這些小例子運行一下,就能更直觀的區分這些方法之間的關聯和區別,這對我們后面的分享和學習有一定的理解。

          傳送門JavaScript設計模式系列

          1、JavaScript設計模式之策略模式(Strategy Pattern)

          2、JavaScript設計模式之職責鏈模式(Chain of Responsibility...)

          3、JavaScript設計模式之享元模式(flyweight Pattern)

          4、JavaScript設計模式之裝飾者模式(Decorator Pattern)

          5、JavaScript設計模式之代理模式(Proxy Pattern)

          6、JavaScript設計模式之組合模式(Composite Pattern)

          7、JavaScript設計模式之工廠模式(Factory Method Pattern)

          8、JavaScript設計模式之中介者模式(Mediator Pattern)

          ....

          參考文章:

          https://www.cnblogs.com/xiaohuochai/p/5785297.html

          http://www.voidcn.com/article/p-gasrrkzi-bpb.html

          https://www.cnblogs.com/zhishaofei/p/4091865.html


          主站蜘蛛池模板: 亚洲韩国精品无码一区二区三区 | 国产SUV精品一区二区88L| 亚洲AV本道一区二区三区四区| 久久久久久一区国产精品| 无码av免费一区二区三区| 日本一区二区三区在线观看| 中文字幕av一区| 国产AV一区二区三区无码野战| 在线观看中文字幕一区| 国产高清在线精品一区二区| 无码一区二区三区免费视频| 三级韩国一区久久二区综合| 波多野结衣AV无码久久一区| 后入内射国产一区二区| 日本一区二区不卡在线| 视频一区二区在线观看| 精品不卡一区二区| 射精专区一区二区朝鲜| 2014AV天堂无码一区| 久夜色精品国产一区二区三区| 亚洲一区欧洲一区| 亚洲性色精品一区二区在线| 国产91久久精品一区二区 | 日韩精品一区二区午夜成人版| 蜜臀AV免费一区二区三区| 中文字幕一区在线观看| 在线精品国产一区二区三区 | 人妻少妇久久中文字幕一区二区| 麻豆精品一区二区综合av| 国产一区二区在线观看视频| 性色av一区二区三区夜夜嗨| 精品一区二区三区免费观看| 国产福利电影一区二区三区,免费久久久久久久精 | 亚洲欧洲无码一区二区三区| 福利一区二区三区视频在线观看| 内射女校花一区二区三区| 国产激情无码一区二区三区| 国产免费伦精品一区二区三区| 日本不卡免费新一区二区三区| 看电影来5566一区.二区| 91久久精品国产免费一区|