Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> 優化Android使用MediaPlayer實現的視頻預加載

優化Android使用MediaPlayer實現的視頻預加載

編輯:Android開發實例

       本文是在 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源碼如下,可以讀出大概的運行流程:

 

  1. public class HttpGetProxy{ 
  2.     final static public int SIZE =  (int) (3 * 1024 * 1024); 
  3.     final static public String TAG = "HttpGetProxy"; 
  4.     /** 鏈接帶的端口 */ 
  5.     private int remotePort=-1; 
  6.     /** 遠程服務器地址 */ 
  7.     private String remoteHost; 
  8.     /** 代理服務器使用的端口 */ 
  9.     private int localPort; 
  10.     /** 本地服務器地址 */ 
  11.     private String localHost; 
  12.     private ServerSocket localServer = null; 
  13.     /** 收發Media Player請求的Socket */ 
  14.     private Socket sckPlayer = null; 
  15.     /** 收發Media Server請求的Socket */ 
  16.     private Socket sckServer = null; 
  17.     /**服務器的Address*/ 
  18.     private SocketAddress serverAddress; 
  19.      
  20.     /**下載線程*/ 
  21.     private DownloadThread download = null; 
  22.      
  23.     /** 
  24.      * 初始化代理服務器 
  25.      * @param localport 代理服務器監聽的端口 
  26.      */ 
  27.     public HttpGetProxy(int localport) { 
  28.         try { 
  29.             localPort = localport; 
  30.             localHost = C.LOCAL_IP_ADDRESS; 
  31.             localServer = new ServerSocket(localport, 1,InetAddress.getByName(localHost)); 
  32.         } catch (Exception e) { 
  33.             System.exit(0); 
  34.         } 
  35.     } 
  36.  
  37.     /** 
  38.      * 把URL提前下載在SD卡,實現預加載 
  39.      * @param urlString 
  40.      * @return 返回預加載文件名 
  41.      * @throws Exception 
  42.      */ 
  43.     public String prebuffer(String urlString,int size) throws Exception{ 
  44.         if(download!=null && download.isDownloading()) 
  45.             download.stopThread(true); 
  46.          
  47.         URI tmpURI=new URI(urlString); 
  48.         String fileName=Utils.urlToFileName(tmpURI.getPath()); 
  49.         String filePath=C.getBufferDir()+"/"+fileName; 
  50.          
  51.         download=new DownloadThread(urlString,filePath,size); 
  52.         download.startThread(); 
  53.          
  54.         return filePath; 
  55.     } 
  56.      
  57.     /** 
  58.      * 把網絡URL轉為本地URL,127.0.0.1替換網絡域名 
  59.      *  
  60.      * @param url網絡URL 
  61.      * @return [0]:重定向後MP4真正URL,[1]:本地URL 
  62.      */ 
  63.     public String[] getLocalURL(String urlString) { 
  64.          
  65.         // ----排除HTTP特殊----// 
  66.         String targetUrl = Utils.getRedirectUrl(urlString); 
  67.         // ----獲取對應本地代理服務器的鏈接----// 
  68.         String localUrl = null; 
  69.         URI originalURI = URI.create(targetUrl); 
  70.         remoteHost = originalURI.getHost(); 
  71.         if (originalURI.getPort() != -1) {// URL帶Port 
  72.             serverAddress = new InetSocketAddress(remoteHost, originalURI.getPort());// 使用默認端口 
  73.             remotePort = originalURI.getPort();// 保存端口,中轉時替換 
  74.             localUrl = targetUrl.replace( 
  75.                     remoteHost + ":" + originalURI.getPort(), localHost + ":" 
  76.                             + localPort); 
  77.         } else {// URL不帶Port 
  78.             serverAddress = new InetSocketAddress(remoteHost, C.HTTP_PORT);// 使用80端口 
  79.             remotePort = -1; 
  80.             localUrl = targetUrl.replace(remoteHost, localHost + ":" 
  81.                     + localPort); 
  82.         } 
  83.          
  84.         String[] result= new String[]{targetUrl,localUrl}; 
  85.         return result; 
  86.     } 
  87.  
  88.     /** 
  89.      * 異步啟動代理服務器 
  90.      *  
  91.      * @throws IOException 
  92.      */ 
  93.     public void asynStartProxy() { 
  94.         new Thread() { 
  95.             public void run() { 
  96.                 startProxy(); 
  97.             } 
  98.         }.start(); 
  99.     } 
  100.  
  101.     private void startProxy() { 
  102.         HttpParser httpParser =null; 
  103.         HttpGetProxyUtils utils=null; 
  104.         int bytes_read; 
  105.      
  106.         byte[] local_request = new byte[1024]; 
  107.         byte[] remote_reply = new byte[1024]; 
  108.  
  109.         while (true) { 
  110.             boolean sentResponseHeader = false; 
  111.             try {// 開始新的request之前關閉過去的Socket 
  112.                 if (sckPlayer != null) 
  113.                     sckPlayer.close(); 
  114.                 if (sckServer != null) 
  115.                     sckServer.close(); 
  116.             } catch (IOException e1) {} 
  117.             try { 
  118.                 // -------------------------------------- 
  119.                 // 監聽MediaPlayer的請求,MediaPlayer->代理服務器 
  120.                 // -------------------------------------- 
  121.                 sckPlayer = localServer.accept(); 
  122.                 Log.e(TAG,"------------------------------------------------------------------"); 
  123.                 if(download!=null && download.isDownloading()) 
  124.                     download.stopThread(false); 
  125.                  
  126.                 httpParser=new HttpParser(remoteHost,remotePort,localHost,localPort); 
  127.                 utils = new HttpGetProxyUtils(sckPlayer,sckServer,serverAddress); 
  128.                  
  129.                 ProxyRequest request = null; 
  130.                 while ((bytes_read = sckPlayer.getInputStream().read(local_request)) != -1) { 
  131.                     byte[] buffer=httpParser.getRequestBody(local_request,bytes_read); 
  132.                     if(buffer!=null){ 
  133.                         request=httpParser.getProxyRequest(buffer); 
  134.                         break; 
  135.                     } 
  136.                 } 
  137.                  
  138.                 boolean isExists=new File(request._prebufferFilePath).exists(); 
  139.                 if(isExists) 
  140.                     Log.e(TAG,"prebuffer size:"+download.getDownloadedSize()); 
  141.                  
  142.                 sckServer = utils.sentToServer(request._body); 
  143.                 // ------------------------------------------------------ 
  144.                 // 把網絡服務器的反饋發到MediaPlayer,網絡服務器->代理服務器->MediaPlayer 
  145.                 // ------------------------------------------------------ 
  146.                 while ((bytes_read = sckServer.getInputStream().read(remote_reply)) != -1) { 
  147.                     if(sentResponseHeader){ 
  148.                         try{//拖動進度條時,容易在此異常,斷開重連 
  149.                             utils.sendToMP(remote_reply,bytes_read); 
  150.                         }catch (Exception e) { 
  151.                             break;//發送異常直接退出while 
  152.                         } 
  153.                         continue;//退出本次while 
  154.                     } 
  155.  
  156.                     List<byte[]> httpResponse = httpParser.getResponseBody(remote_reply, bytes_read); 
  157.                     if (httpResponse.size() == 0) 
  158.                         continue;//沒Header則退出本次循環 
  159.  
  160.                     sentResponseHeader = true; 
  161.                     String responseStr = new String(httpResponse.get(0)); 
  162.                     Log.e(TAG + "<---", responseStr); 
  163.                     //send http header to mediaplayer 
  164.                     utils.sendToMP(httpResponse.get(0)); 
  165.                      
  166.                     if (isExists) {//需要發送預加載到MediaPlayer 
  167.                         isExists = false; 
  168.                         int sentBufferSize = 0; 
  169.                         try{ 
  170.                             sentBufferSize = utils.sendPrebufferToMP( 
  171.                                 request._prebufferFilePath, 
  172.                                 request._rangePosition); 
  173.                         }catch(Exception ex){ 
  174.                             break; 
  175.                         } 
  176.                         if (sentBufferSize > 0) {// 成功發送預加載,重新發送請求到服務器 
  177.                             int newRange=(int) (sentBufferSize + request._rangePosition); 
  178.                             String newRequestStr = httpParser.modifyRequestRange(request._body,newRange); 
  179.                             Log.e(TAG + "-pre->", newRequestStr); 
  180.                             //修改Range後的Request發送給服務器 
  181.                             sckServer = utils.sentToServer(newRequestStr); 
  182.                             //把服務器的Response的Header去掉 
  183.                             utils.removeResponseHeader(httpParser); 
  184.                             continue; 
  185.                         } 
  186.                     } 
  187.  
  188.                     // 發送剩余數據 
  189.                     if (httpResponse.size() == 2) { 
  190.                         utils.sendToMP(httpResponse.get(1)); 
  191.                     } 
  192.                 } 
  193.  
  194.                 Log.e(TAG, ".........over.........."); 
  195.  
  196.                 // 關閉 2個SOCKET 
  197.                 sckPlayer.close(); 
  198.                 sckServer.close(); 
  199.             } catch (Exception e) { 
  200.                 Log.e(TAG,e.toString()); 
  201.                 Log.e(TAG,Utils.getExceptionMessage(e)); 
  202.             } 
  203.         } 
  204.     } 

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved