DASH流媒体MPD文件解析
MPD文件本質是XML文件,其說明了DASH服務端流媒體視頻切片的相關信息,包含分辨率,大小,幀率等等,因此在DASH客戶端實現的第一步便是解析服務端的MPD文件,其常見結構如圖所示:
?
MPD文件來源https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd
例如需要解析出的Representation中的id,width和height屬性值(圖例中MPD文件寫法比較復雜,同一<Representation - />標簽中包含的子屬性較多,因此需要調用xmlPullParser.getAttributeValue()方法遍歷子屬性)。如果一個標簽中僅含有一個屬性,如<name> fupenzi </name>這種,層層調用如果語句即可解析,參見《Android第一行代碼》。
Android 提供了三種解析XML的方式:SAX(Simple API XML),?DOM(Document Object Model),?Pull?(Android官方推薦)
SAX是一個用于處理XML事件驅動的“推”模型,?
優點是一種解析速度快并且占用內存少的xml解析器,它需要哪些數據再加載和解析哪些內容。?
缺點是它不會記錄標簽的關系,而要讓你的應用程序自己處理,這樣就增加了你程序的負擔。
DOM是一種文檔對象模型,DOM可以以一種獨立于平臺和語言的方式訪問和修改一個文檔的內容和結構。?
優點是DOM技術使得用戶頁面可以動態地變化,如可以動態地顯示或隱藏一個元素,改變它們的屬性,增加一個元素等,DOM技術使得頁面的交互性大大地增強。?
缺點是DOM解析XML文件時,會將XML文件的所有內容以文檔樹方式存放在內存中。
PULL解析和SAX解析很相似,PULL解析和SAX解析不一樣的地方是PULL讀取XML文件后觸發相應的事件調用方法返回的是數字,還有PULL可以在程序中控制想解析到哪里就可以停止解析。
DOM方式最直觀和容易理解,但是只適合XML文檔較小的時候使用,而SAX方式更適合在移動終端系統中使用,因為相比DOM占用內存少,適合處理比較大的XML文檔,最后的Pull方式使用場合和SAX類似,但是更適合需要提前結束XML文檔解析的場合。
具體解析過程可參見Android解析XML的三種方式_d_shadow的博客-CSDN博客_android 讀取xml
創建一個應用程序,簡單演示一下MPD文件的PULL方法解析過程,創建項目XMLParser。
依賴
dependencies {implementation fileTree(include: ['*.jar'], dir: 'libs')implementation 'com.android.support:appcompat-v7:27.1.1'implementation 'com.android.support.constraint:constraint-layout:1.1.3'testImplementation 'junit:junit:4.12'compile 'com.squareup.okhttp3:okhttp:3.4.1'//添加OKHTTP依賴庫androidTestImplementation 'com.android.support.test:runner:1.0.2'androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
MainActivity.java編寫如下:
package com.example.xmlparser;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Button;import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;
import android.util.Log;
import java.lang.String;
import java.io.StringReader;import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;/*** Created by fupenzi on 2018/12/19.*/public class MainActivity extends AppCompatActivity implements View.OnClickListener {TextView responseText;//聲明一個回應文本顯示對象@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button sendRequest = (Button) findViewById(R.id.send_request);responseText = (TextView) findViewById(R.id.response_text);sendRequest.setOnClickListener(this);}@Overridepublic void onClick(View v) {if (v.getId() == R.id.send_request) {
// sendRequestWithHttpURLConnection();sendRequestWithOkHttp();}}public void sendRequestWithOkHttp() {//利用OKHTTP對MPD文件進行下載new Thread(new Runnable() {@Overridepublic void run() {try {OkHttpClient client = new OkHttpClient();Request request = new Request.Builder().url("https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd")//MPD請求地址.build();Response response = client.newCall(request).execute();//發送請求并獲取服務端返回數據String responseData = response.body().string();//提取返回數據中請求的文件,并轉化為字符串parseXMLWithPull(responseData); //對請求的文件進行解析} catch (Exception e) {e.printStackTrace();}}}).start();}private void parseXMLWithPull(String xmlData) { //MPD解析方法Represent repres = new Represent();//實例化一個Representation對象String line = "";try {XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); //獲得一個XMLPULL工廠類的實例XmlPullParser xmlPullParser = factory.newPullParser(); //獲得一個XML解析器的實例xmlPullParser.setInput(new StringReader(xmlData));int eventType = xmlPullParser.getEventType();while (eventType != XmlPullParser.END_DOCUMENT) {String nodeName = xmlPullParser.getName();switch (eventType) {// 通過判斷事件類型來選擇執行不同的代碼// 開始解析某個結點case XmlPullParser.START_TAG: {if ("Representation".equals(nodeName)) {//由<Representation開始解析repres.setId(xmlPullParser.getAttributeValue(null, "id"));repres.setWidth(xmlPullParser.getAttributeValue(null, "width"));repres.setHeight(xmlPullParser.getAttributeValue(null, "height"));}break;}// 完成解析某個結點case XmlPullParser.END_TAG: {if ("Representation".equals(nodeName)) { //由Representation--/>結束解析Log.d("Representation", "id is " + repres.getId()+"; width is " + repres.getWidth()+
"; height is " + repres.getHeight()+";");//在調試窗口logcat打印出屬性值line = line +"ID: "+ repres.getId()+"; WIDTH: "+repres.getWidth()+
"; HEIGHT: "+repres.getHeight()+";\n";//將已經遍歷的屬性值連接成一個字符串,并注意換行}break;}default:break;}eventType = xmlPullParser.next();}showResponse(line);//將字符串對象傳入showResponse()方法,打印在UI的TextView} catch (Exception e) {e.printStackTrace();}}private void showResponse(final String response) {//子線程中不允許進行UI操作,因此利用showResponse()方法切換到主線程更新runOnUiThread(new Runnable() {@Overridepublic void run() {responseText.setText(response);//創建一個TextView顯示傳入字符串}});}
}
對于Represent類(數據成員id,width,height,方法成員的set(),get()設置)進行編寫:
package com.example.xmlparser;/*** Created by fupenzi on 2018/12/19.*/public class Represent {private String id;private String width;private String height;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getWidth() {return width;}public void setWidth(String width) {this.width = width;}public String getHeight() {return height;}public void setHeight(String height) {this.height = height;}
}
UI界面:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent" ><Buttonandroid:id="@+id/send_request"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Send Request" /><ScrollViewandroid:layout_width="match_parent"android:layout_height="match_parent" ><TextViewandroid:id="@+id/response_text"android:singleLine="false"android:layout_width="match_parent"android:layout_height="wrap_content" /></ScrollView></LinearLayout>
運行效果(在調用SetText()方法時需要注意,以字符串(流)一次性注入需要顯示出來的文本,若多次調用SetText()分別顯示每次得到的字符串,則會覆蓋前一次調用后顯示內容):
? 發出請求后:
Android studio調試臺顯示結果:
最后一個Representation標簽中的寬度和高度屬性為null,是因為MPD文件的最后一個Representation標簽是對音頻進行說明,因此沒有長寬屬性。
總結
以上是生活随笔為你收集整理的DASH流媒体MPD文件解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 繁体字个性签名小清新
- 下一篇: 综艺电影与电影的区别