1. 程式人生 > >玩轉 Android MediaPlayer之視訊預載入 優化

玩轉 Android MediaPlayer之視訊預載入 優化

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

本文來自http://blog.csdn.net/hellogv/ ,引用必須註明出處!

       本文是在《玩轉 Android MediaPlayer之視訊預載入

》基礎上做更進一步的優化,適應更多終端的MediaPlayer,不再嘮叨預載入的作用和基礎,有興趣的讀者請看上回。

       MediaPlayer由廠家定製,不同終端的MediaPlayer略有差異,例如:有些MediaPlayer首次播放從頭buffer,有些MdiaPlayer首次播放會多次Request,Range到網路媒體檔案的頭部、中間和檔案尾,再從指定位置buffer...本文所做的優化就是適應播放前多次Request的MediaPlayer。

       決定預載入效果好壞由三因素決定:

  1. 網速
  2. 緩衝檔案大小
  3. 視訊位元速率

位元速率低、網速快的情況沒必要使用預載入,位元速率中等、網速一般的情況合適使用。另外,緩衝檔案也不能設定太大:過大的緩衝區會刷爆MediaPlayer內建的緩衝區,影響正常播放;再者,讀取緩衝檔案也耗時。

 

先看看本文程式的執行結果,以下是不使用預載入的執行LOG:

 

08-27 10:34:55.222: E//mnt/sdcard/ProxyBuffer/files(12949): --------共有0個快取檔案
08-27 10:34:55.327: E/MediaPlayer(12949): MediaPlayer::setVideoRect(0,25, 1280, 720)
08-27 10:34:55.327: E/(12949): IMediaPlayer::setVideoRect(0,25, 1280, 720)
08-27 10:34:55.367: E/MediaPlayer(12949): MediaPlayer::setVideoRect(0,25, 1280, 720)
08-27 10:34:55.367: E/(12949): IMediaPlayer::setVideoRect(0,25, 1280, 720)
08-27 10:35:01.152: E/MediaPlayer(12949): MediaPlayer::setVideoRect(0,25, 1280, 720)
08-27 10:35:01.152: E/(12949): IMediaPlayer::setVideoRect(0,25, 1280, 720)
08-27 10:35:01.402: E/MediaPlayer(12949): MediaPlayer::setVideoRect(0,25, 1280, 720)
08-27 10:35:01.402: E/(12949): IMediaPlayer::setVideoRect(0,25, 1280, 720)
08-27 10:35:02.382: E/testVideoPlayer(12949): 預載入開關:false,等待緩衝時間:8000,首次緩衝時間:7152

 

 

以下是使用預載入的執行LOG,內容有點多,這個MediaPlayer就是首次播放前多次Request:

 

08-27 10:40:02.627: E//mnt/sdcard/ProxyBuffer/files(13769): --------共有0個快取檔案
08-27 10:40:02.777: E/testVideoPlayer(13769): 預載入檔案:/mnt/sdcard/ProxyBuffer/files/videorzx201208151345010952759.mp4
08-27 10:40:02.972: E/DownloadThread(13769): /mnt/sdcard/ProxyBuffer/files/videorzx201208151345010952759.mp4
08-27 10:40:10.782: E/MediaPlayer(13769): MediaPlayer::setVideoRect(0,25, 1280, 720)
08-27 10:40:10.782: E/(13769): IMediaPlayer::setVideoRect(0,25, 1280, 720)
08-27 10:40:10.787: E/HttpGetProxy(13769): ------------------------------------------------------------------
08-27 10:40:10.787: E/HttpGetProxy(13769): java.lang.NullPointerException
08-27 10:40:10.787: E/HttpGetProxy(13769): com.proxy.HttpGetProxy.startProxy  171line

08-27 10:40:10.787: E/HttpGetProxy(13769): com.proxy.HttpGetProxy.access$0  134line

08-27 10:40:10.787: E/HttpGetProxy(13769): com.proxy.HttpGetProxy$1.run  129line

08-27 10:40:10.787: E/HttpGetProxy(13769): ------------------------------------------------------------------
08-27 10:40:10.792: E/HttpParser(13769): GET /video/rzx/201208/15/1345010952759.mp4 HTTP/1.1

08-27 10:40:10.792: E/HttpParser(13769): Range: bytes=0-

08-27 10:40:10.792: E/HttpParser(13769): Host: video.cztv.com

08-27 10:40:10.792: E/HttpParser(13769): Accept: */*

08-27 10:40:10.792: E/HttpParser(13769): Pragma: no-cache

08-27 10:40:10.792: E/HttpParser(13769):

08-27 10:40:10.792: E/HttpParser(13769): _prebufferFilePath:/mnt/sdcard/ProxyBuffer/files/videorzx201208151345010952759.mp4
08-27 10:40:10.792: E/HttpParser(13769): ------->rangePosition:0
08-27 10:40:10.792: E/HttpGetProxy(13769): prebuffer size:999296
08-27 10:40:10.797: E/DownloadThread(13769): mTotalSize:8311866,mTargetSize:3145728
08-27 10:40:11.762: E/HttpGetProxy<---(13769): HTTP/1.1 206 Partial Content

08-27 10:40:11.762: E/HttpGetProxy<---(13769): Date: Mon, 27 Aug 2012 02:33:24 GMT

08-27 10:40:11.762: E/HttpGetProxy<---(13769): Server: Apache

08-27 10:40:11.762: E/HttpGetProxy<---(13769): X-Mod-H264-Streaming: version=2.2.7

08-27 10:40:11.762: E/HttpGetProxy<---(13769): Last-Modified: Wed, 15 Aug 2012 06:24:38 GMT

08-27 10:40:11.762: E/HttpGetProxy<---(13769): Accept-Ranges: bytes

08-27 10:40:11.762: E/HttpGetProxy<---(13769): Cache-Control: max-age=315360000

08-27 10:40:11.762: E/HttpGetProxy<---(13769): Expires: Thu, 25 Aug 2022 02:33:24 GMT

08-27 10:40:11.762: E/HttpGetProxy<---(13769): Content-Type: video/mp4

08-27 10:40:11.762: E/HttpGetProxy<---(13769): Powered-By-ChinaCache: HIT from 01006613Y4

08-27 10:40:11.762: E/HttpGetProxy<---(13769): Content-Range: bytes 0-8311865/8311866

08-27 10:40:11.762: E/HttpGetProxy<---(13769): Content-Length: 8311866

08-27 10:40:11.762: E/HttpGetProxy<---(13769): Age: 407

08-27 10:40:11.762: E/HttpGetProxy<---(13769): Powered-By-ChinaCache: HIT from 01075913r4

08-27 10:40:11.762: E/HttpGetProxy<---(13769):

08-27 10:40:11.777: E/HttpGetProxy(13769): .........over..........
08-27 10:40:11.777: E/HttpGetProxy(13769): ------------------------------------------------------------------
08-27 10:40:11.782: E/HttpParser(13769): GET /video/rzx/201208/15/1345010952759.mp4 HTTP/1.1

08-27 10:40:11.782: E/HttpParser(13769): Range: bytes=101901-

08-27 10:40:11.782: E/HttpParser(13769): Host: video.cztv.com

08-27 10:40:11.782: E/HttpParser(13769): Accept: */*

08-27 10:40:11.782: E/HttpParser(13769): Pragma: no-cache

08-27 10:40:11.782: E/HttpParser(13769):

08-27 10:40:11.782: E/HttpParser(13769): _prebufferFilePath:/mnt/sdcard/ProxyBuffer/files/videorzx201208151345010952759.mp4
08-27 10:40:11.782: E/HttpParser(13769): ------->rangePosition:101901
08-27 10:40:11.782: E/HttpGetProxy(13769): prebuffer size:1000320
08-27 10:40:12.167: E/HttpGetProxy<---(13769): HTTP/1.1 206 Partial Content

08-27 10:40:12.167: E/HttpGetProxy<---(13769): Date: Mon, 27 Aug 2012 02:33:24 GMT

08-27 10:40:12.167: E/HttpGetProxy<---(13769): Server: Apache

08-27 10:40:12.167: E/HttpGetProxy<---(13769): X-Mod-H264-Streaming: version=2.2.7

08-27 10:40:12.167: E/HttpGetProxy<---(13769): Last-Modified: Wed, 15 Aug 2012 06:24:38 GMT

08-27 10:40:12.167: E/HttpGetProxy<---(13769): Accept-Ranges: bytes

08-27 10:40:12.167: E/HttpGetProxy<---(13769): Cache-Control: max-age=315360000

08-27 10:40:12.167: E/HttpGetProxy<---(13769): Expires: Thu, 25 Aug 2022 02:33:24 GMT

08-27 10:40:12.167: E/HttpGetProxy<---(13769): Content-Type: video/mp4

08-27 10:40:12.167: E/HttpGetProxy<---(13769): Powered-By-ChinaCache: HIT from 01006613Y4

08-27 10:40:12.167: E/HttpGetProxy<---(13769): Content-Range: bytes 101901-8311865/8311866

08-27 10:40:12.167: E/HttpGetProxy<---(13769): Content-Length: 8209965

08-27 10:40:12.167: E/HttpGetProxy<---(13769): Age: 408

08-27 10:40:12.167: E/HttpGetProxy<---(13769): Powered-By-ChinaCache: HIT from 01075913r4

08-27 10:40:12.167: E/HttpGetProxy<---(13769):

08-27 10:40:12.172: E/HttpGetProxy(13769): >>>skip:101901
08-27 10:40:12.182: E/HttpGetProxy(13769): .........over..........
08-27 10:40:12.182: E/HttpGetProxy(13769): ------------------------------------------------------------------
08-27 10:40:12.187: E/HttpParser(13769): GET /video/rzx/201208/15/1345010952759.mp4 HTTP/1.1

08-27 10:40:12.187: E/HttpParser(13769): Range: bytes=74-

08-27 10:40:12.187: E/HttpParser(13769): Host: video.cztv.com

08-27 10:40:12.187: E/HttpParser(13769): Accept: */*

08-27 10:40:12.187: E/HttpParser(13769): Pragma: no-cache

08-27 10:40:12.187: E/HttpParser(13769):

08-27 10:40:12.187: E/HttpParser(13769): _prebufferFilePath:/mnt/sdcard/ProxyBuffer/files/videorzx201208151345010952759.mp4
08-27 10:40:12.187: E/HttpParser(13769): ------->rangePosition:74
08-27 10:40:12.187: E/HttpGetProxy(13769): prebuffer size:1000320
08-27 10:40:12.372: E/HttpGetProxy<---(13769): HTTP/1.1 206 Partial Content

08-27 10:40:12.372: E/HttpGetProxy<---(13769): Date: Mon, 27 Aug 2012 02:33:24 GMT

08-27 10:40:12.372: E/HttpGetProxy<---(13769): Server: Apache

08-27 10:40:12.372: E/HttpGetProxy<---(13769): X-Mod-H264-Streaming: version=2.2.7

08-27 10:40:12.372: E/HttpGetProxy<---(13769): Last-Modified: Wed, 15 Aug 2012 06:24:38 GMT

08-27 10:40:12.372: E/HttpGetProxy<---(13769): Accept-Ranges: bytes

08-27 10:40:12.372: E/HttpGetProxy<---(13769): Cache-Control: max-age=315360000

08-27 10:40:12.372: E/HttpGetProxy<---(13769): Expires: Thu, 25 Aug 2022 02:33:24 GMT

08-27 10:40:12.372: E/HttpGetProxy<---(13769): Content-Type: video/mp4

08-27 10:40:12.372: E/HttpGetProxy<---(13769): Powered-By-ChinaCache: HIT from 01006613Y4

08-27 10:40:12.372: E/HttpGetProxy<---(13769): Content-Range: bytes 74-8311865/8311866

08-27 10:40:12.372: E/HttpGetProxy<---(13769): Content-Length: 8311792

08-27 10:40:12.372: E/HttpGetProxy<---(13769): Age: 408

08-27 10:40:12.372: E/HttpGetProxy<---(13769): Powered-By-ChinaCache: HIT from 01075913r4

08-27 10:40:12.372: E/HttpGetProxy<---(13769):

08-27 10:40:12.377: E/HttpGetProxy(13769): >>>skip:74
08-27 10:40:12.397: E/MediaPlayer(13769): MediaPlayer::setVideoRect(0,25, 1280, 720)
08-27 10:40:12.397: E/(13769): IMediaPlayer::setVideoRect(0,25, 1280, 720)
08-27 10:40:12.537: E/HttpGetProxy(13769): >>>讀取預載入耗時:164
08-27 10:40:12.537: E/HttpGetProxy(13769): >>>讀取完畢...下載:1000320,讀取:1000246

08-27 10:40:12.537: E/HttpGetProxy-pre->(13769): GET /video/rzx/201208/15/1345010952759.mp4 HTTP/1.1

08-27 10:40:12.537: E/HttpGetProxy-pre->(13769): Range: bytes=1000320-

08-27 10:40:12.537: E/HttpGetProxy-pre->(13769): Host: video.cztv.com

08-27 10:40:12.537: E/HttpGetProxy-pre->(13769): Accept: */*

08-27 10:40:12.537: E/HttpGetProxy-pre->(13769): Pragma: no-cache

08-27 10:40:12.537: E/HttpGetProxy-pre->(13769):

08-27 10:40:12.647: E/MediaPlayer(13769): MediaPlayer::setVideoRect(0,25, 1280, 720)
08-27 10:40:12.647: E/(13769): IMediaPlayer::setVideoRect(0,25, 1280, 720)
08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769): HTTP/1.1 206 Partial Content

08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769): Date: Mon, 27 Aug 2012 02:33:24 GMT

08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769): Server: Apache

08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769): X-Mod-H264-Streaming: version=2.2.7

08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769): Last-Modified: Wed, 15 Aug 2012 06:24:38 GMT

08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769): Accept-Ranges: bytes

08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769): Cache-Control: max-age=315360000

08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769): Expires: Thu, 25 Aug 2022 02:33:24 GMT

08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769): Content-Type: video/mp4

08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769): Powered-By-ChinaCache: HIT from 01006613Y4

08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769): Content-Range: bytes 1000320-8311865/8311866

08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769): Content-Length: 7311546

08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769): Age: 409

08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769): Powered-By-ChinaCache: HIT from 01075913r4

08-27 10:40:12.962: E/HttpGetProxy ~~~~(13769):

08-27 10:40:14.037: E/testVideoPlayer(13769): 預載入開關:true,等待緩衝時間:8000,首次緩衝時間:3261

本文的原始碼可以到這裡下載:http://download.csdn.net/detail/hellogv/4528270

HttpGetProxy.java原始碼如下,可以讀出大概的執行流程:

public class HttpGetProxyfinal static public int SIZE =  (int) (3 * 1024 * 1024); final static public String TAG = "HttpGetProxy"/** 連結帶的埠 */ private int remotePort=-1/** 遠端伺服器地址 */ private String remoteHost; /** 代理伺服器使用的埠 */ private int localPort; /** 本地伺服器地址 */ private String localHost; private ServerSocket localServer = null/** 收發Media Player請求的Socket */ private Socket sckPlayer = null/** 收發Media Server請求的Socket */ private Socket sckServer = null/**伺服器的Address*/ private SocketAddress serverAddress;  /**下載執行緒*/ private DownloadThread download = null;  /**  * 初始化代理伺服器  * @param localport 代理伺服器監聽的埠  */ public HttpGetProxy(int localport) {  try {   localPort = localport;   localHost = C.LOCAL_IP_ADDRESS;   localServer = new ServerSocket(localport, 1,InetAddress.getByName(localHost));  } catch (Exception e) {   System.exit(0);  } } /**  * 把URL提前下載在SD卡,實現預載入  * @param urlString  * @return 返回預載入檔名  * @throws Exception  */ public String prebuffer(String urlString,int size) throws Exception{  if(download!=null && download.isDownloading())   download.stopThread(true);    URI tmpURI=new URI(urlString);  String fileName=Utils.urlToFileName(tmpURI.getPath());  String filePath=C.getBufferDir()+"/"+fileName;    download=new DownloadThread(urlString,filePath,size);  download.startThread();    return filePath; }  /**  * 把網路URL轉為本地URL,127.0.0.1替換網路域名  *   * @param url網路URL  * @return [0]:重定向後MP4真正URL,[1]:本地URL  */ public String[] getLocalURL(String urlString) {    // ----排除HTTP特殊----//  String targetUrl = Utils.getRedirectUrl(urlString);  // ----獲取對應本地代理伺服器的連結----//  String localUrl = null;  URI originalURI = URI.create(targetUrl);  remoteHost = originalURI.getHost();  if (originalURI.getPort() != -1) {// URL帶Port   serverAddress = new InetSocketAddress(remoteHost, originalURI.getPort());// 使用預設埠   remotePort = originalURI.getPort();// 儲存埠,中轉時替換   localUrl = targetUrl.replace(     remoteHost + ":" + originalURI.getPort(), localHost + ":"       + localPort);  } else {// URL不帶Port   serverAddress = new InetSocketAddress(remoteHost, C.HTTP_PORT);// 使用80埠   remotePort = -1;   localUrl = targetUrl.replace(remoteHost, localHost + ":"     + localPort);  }    String[] result= new String[]{targetUrl,localUrl};  return result; } /**  * 非同步啟動代理伺服器  *   * @throws IOException  */ public void asynStartProxy() {  new Thread() {   public void run() {    startProxy();   }  }.start(); } private void startProxy() {  HttpParser httpParser =null;  HttpGetProxyUtils utils=null;  int bytes_read;   byte[] local_request = new byte[1024];  byte[] remote_reply = new byte[1024];  while (true) {   boolean sentResponseHeader = false;   try {// 開始新的request之前關閉過去的Socket    if (sckPlayer != null)     sckPlayer.close();    if (sckServer != null)     sckServer.close();   } catch (IOException e1) {}   try {    // --------------------------------------    // 監聽MediaPlayer的請求,MediaPlayer->代理伺服器    // --------------------------------------    sckPlayer = localServer.accept();    Log.e(TAG,"------------------------------------------------------------------");    if(download!=null && download.isDownloading())     download.stopThread(false);        httpParser=new HttpParser(remoteHost,remotePort,localHost,localPort);    utils = new HttpGetProxyUtils(sckPlayer,sckServer,serverAddress);        ProxyRequest request = null;    while ((bytes_read = sckPlayer.getInputStream().read(local_request)) != -1) {     byte[] buffer=httpParser.getRequestBody(local_request,bytes_read);     if(buffer!=null){      request=httpParser.getProxyRequest(buffer);      break;     }    }        boolean isExists=new File(request._prebufferFilePath).exists();    if(isExists)     Log.e(TAG,"prebuffer size:"+download.getDownloadedSize());        sckServer = utils.sentToServer(request._body);    // ------------------------------------------------------    // 把網路伺服器的反饋發到MediaPlayer,網路伺服器->代理伺服器->MediaPlayer    // ------------------------------------------------------    while ((bytes_read = sckServer.getInputStream().read(remote_reply)) != -1) {     if(sentResponseHeader){      try{//拖動進度條時,容易在此異常,斷開重連       utils.sendToMP(remote_reply,bytes_read);      }catch (Exception e) {       break;//傳送異常直接退出while      }      continue;//退出本次while     }     List<byte[]> httpResponse = httpParser.getResponseBody(remote_reply, bytes_read);     if (httpResponse.size() == 0)      continue;//沒Header則退出本次迴圈     sentResponseHeader = true;     String responseStr = new String(httpResponse.get(0));     Log.e(TAG + "<---", responseStr);     //send http header to mediaplayer     utils.sendToMP(httpResponse.get(0));          if (isExists) {//需要傳送預載入到MediaPlayer      isExists = false;      int sentBufferSize = 0;      try{       sentBufferSize = utils.sendPrebufferToMP(        request._prebufferFilePath,        request._rangePosition);      }catch(Exception ex){       break;      }      if (sentBufferSize > 0) {// 成功傳送預載入,重新發送請求到伺服器       int newRange=(int) (sentBufferSize + request._rangePosition);       String newRequestStr = httpParser.modifyRequestRange(request._body,newRange);       Log.e(TAG + "-pre->", newRequestStr);       //修改Range後的Request傳送給伺服器       sckServer = utils.sentToServer(newRequestStr);       //把伺服器的Response的Header去掉       utils.removeResponseHeader(httpParser);       continue;      }     }     // 傳送剩餘資料     if (httpResponse.size() == 2) {      utils.sendToMP(httpResponse.get(1));     }    }    Log.e(TAG, ".........over..........");    // 關閉 2個SOCKET    sckPlayer.close();    sckServer.close();   } catch (Exception e) {    Log.e(TAG,e.toString());    Log.e(TAG,Utils.getExceptionMessage(e));   }  } }


           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述