玩转 Android MediaPlayer之视频预加载(优化)

玩转 Android MediaPlayer之视频预加载(优化)       

http://blog.csdn.net/hellogv/article/details/7911293
 
分类:            Android MediaPlayer17748人阅读评论(29)收藏举报

本文来自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源码如下,可以读出大概的运行流程:

  1. publicclass HttpGetProxy{ 
  2.     finalstaticpublicint SIZE =  (int) (3 * 1024 * 1024); 
  3.     finalstaticpublic String TAG = "HttpGetProxy"
  4.     /** 链接带的端口 */ 
  5.     privateint remotePort=-1
  6.     /** 远程服务器地址 */ 
  7.     private String remoteHost; 
  8.     /** 代理服务器使用的端口 */ 
  9.     privateint 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.     publicvoid asynStartProxy() { 
  94.         new Thread() { 
  95.             publicvoid run() { 
  96.                 startProxy(); 
  97.             } 
  98.         }.start(); 
  99.     } 
  100.  
  101.     privatevoid startProxy() { 
  102.         HttpParser httpParser =null
  103.         HttpGetProxyUtils utils=null
  104.         int bytes_read; 
  105.      
  106.         byte[] local_request = newbyte[1024]; 
  107.         byte[] remote_reply = newbyte[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.     } 
posted @ 2014-03-28 10:37  萧萧  阅读(794)  评论(0)    收藏  举报