玩转 Android MediaPlayer之视频预加载

本文来自http://blog.csdn.net/hellogv/ ,引用必须注明出处!

       本文是在《玩转 Android MediaPlayer之Media Proxy》基础上做更进一步的开发,实现一个视频客户端很常用的功能~~~预加载。要学会本文介绍的内容,强烈建议把《玩转 Android MediaPlayer之Media Proxy》看懂,由浅入深,你懂的。

预加载,分为两类,本文介绍的是“代理服务器”这种方式:

1.边存边播:下载多少播放多少。

优点:快速加载播放,实现简单;缺点:不能拖动未存区域;适合音频媒体

2.代理服务器:预先下载媒体的头部(头部Size为 s1 byte)->监听播放器的请求,当Request的是预加载的URL->代理把媒体头部作为Response返回给播放器,并改Ranage 为 s1 byte 发送Request->代理服务器纯粹作为透传。

优点:快速加载播放,支持拖动;缺点:实现非常复杂;适合视频媒体

      预加载不仅可以缩短视频媒体的加载过程,还为“分段拼接”提供支持......通俗地说,IOS的播放器是高帅富,支持2个播放器交替播放从而无缝播放分片视频;Android的播放器是男屌丝,只能有一个实例一个个播放,切换分片视频时有明显的蛋疼感......使用预加载可以缩短停顿的时间。

先来看看预加载的效果,预加载4000ms打开视频消耗1420ms,不用预加载打开视频消耗2633ms:

==================================================================================================

 

本文的源码可以到http://download.csdn.net/detail/hellogv/4486051下载,本文所用的MP4搜索自百度....

HttpGetProxy.java是本文的核心,代理服务器的主要实现,源码如下:

[java]  view plain copy print ?
  1. <span style="font-family:Comic Sans MS;font-size:18px;">/** 
  2.  * 代理服务器类 
  3.  * @author hellogv 
  4.  * 
  5.  */  
  6. public class HttpGetProxy{  
  7.     final static public String TAG = "HttpGetProxy";  
  8.     /** 链接带的端口 */  
  9.     private int remotePort=-1;  
  10.     /** 远程服务器地址 */  
  11.     private String remoteHost;  
  12.     /** 代理服务器使用的端口 */  
  13.     private int localPort;  
  14.     /** 本地服务器地址 */  
  15.     private String localHost;  
  16.     private ServerSocket localServer = null;  
  17.     /** 收发Media Player请求的Socket */  
  18.     private Socket sckPlayer = null;  
  19.     /** 收发Media Server请求的Socket */  
  20.     private Socket sckServer = null;  
  21.       
  22.     private SocketAddress address;  
  23.       
  24.     /**下载线程*/  
  25.     private DownloadThread download = null;  
  26.     /** 
  27.      * 初始化代理服务器 
  28.      *  
  29.      * @param localport 代理服务器监听的端口 
  30.      */  
  31.     public HttpGetProxy(int localport) {  
  32.         try {  
  33.             localPort = localport;  
  34.             localHost = C.LOCAL_IP_ADDRESS;  
  35.             localServer = new ServerSocket(localport, 1,InetAddress.getByName(localHost));  
  36.         } catch (Exception e) {  
  37.             System.exit(0);  
  38.         }  
  39.     }  
  40.   
  41.     /** 
  42.      * 把URL提前下载在SD卡,实现预加载 
  43.      * @param urlString 
  44.      * @return 返回预加载文件名 
  45.      * @throws Exception 
  46.      */  
  47.     public String prebuffer(String urlString,int size) throws Exception{  
  48.         if(download!=null && download.isDownloading())  
  49.             download.stopThread(true);  
  50.           
  51.         URI tmpURI=new URI(urlString);  
  52.         String fileName=ProxyUtils.urlToFileName(tmpURI.getPath());  
  53.         String filePath=C.getBufferDir()+"/"+fileName;  
  54.           
  55.         download=new DownloadThread(urlString,filePath,size);  
  56.         download.startThread();  
  57.           
  58.         return filePath;  
  59.     }  
  60.       
  61.     /** 
  62.      * 把网络URL转为本地URL,127.0.0.1替换网络域名 
  63.      *  
  64.      * @param url网络URL 
  65.      * @return [0]:重定向后MP4真正URL,[1]:本地URL 
  66.      */  
  67.     public String[] getLocalURL(String urlString) {  
  68.           
  69.         // ----排除HTTP特殊----//  
  70.         String targetUrl = ProxyUtils.getRedirectUrl(urlString);  
  71.         // ----获取对应本地代理服务器的链接----//  
  72.         String localUrl = null;  
  73.         URI originalURI = URI.create(targetUrl);  
  74.         remoteHost = originalURI.getHost();  
  75.         if (originalURI.getPort() != -1) {// URL带Port  
  76.             address = new InetSocketAddress(remoteHost, originalURI.getPort());// 使用默认端口  
  77.             remotePort = originalURI.getPort();// 保存端口,中转时替换  
  78.             localUrl = targetUrl.replace(  
  79.                     remoteHost + ":" + originalURI.getPort(), localHost + ":"  
  80.                             + localPort);  
  81.         } else {// URL不带Port  
  82.             address = new InetSocketAddress(remoteHost, C.HTTP_PORT);// 使用80端口  
  83.             remotePort = -1;  
  84.             localUrl = targetUrl.replace(remoteHost, localHost + ":"  
  85.                     + localPort);  
  86.         }  
  87.           
  88.         String[] result= new String[]{targetUrl,localUrl};  
  89.         return result;  
  90.     }  
  91.   
  92.     /** 
  93.      * 异步启动代理服务器 
  94.      *  
  95.      * @throws IOException 
  96.      */  
  97.     public void asynStartProxy() {  
  98.   
  99.         new Thread() {  
  100.             public void run() {  
  101.                 startProxy();  
  102.             }  
  103.         }.start();  
  104.     }  
  105.   
  106.     private void startProxy() {  
  107.         HttpParser httpParser =null;  
  108.         int bytes_read;  
  109.         boolean enablePrebuffer=false;//必须放在这里  
  110.           
  111.         byte[] local_request = new byte[1024];  
  112.         byte[] remote_reply = new byte[1024];  
  113.   
  114.         while (true) {  
  115.             boolean hasResponseHeader = false;  
  116.             try {// 开始新的request之前关闭过去的Socket  
  117.                 if (sckPlayer != null)  
  118.                     sckPlayer.close();  
  119.                 if (sckServer != null)  
  120.                     sckServer.close();  
  121.             } catch (IOException e1) {}  
  122.             try {  
  123.                 // --------------------------------------  
  124.                 // 监听MediaPlayer的请求,MediaPlayer->代理服务器  
  125.                 // --------------------------------------  
  126.                 sckPlayer = localServer.accept();  
  127.                 Log.e("TAG","------------------------------------------------------------------");  
  128.                 if(download!=null && download.isDownloading())  
  129.                     download.stopThread(false);  
  130.                   
  131.                 httpParser=new HttpParser(remoteHost,remotePort,localHost,localPort);  
  132.                   
  133.                 ProxyRequest request = null;  
  134.                 while ((bytes_read = sckPlayer.getInputStream().read(local_request)) != -1) {  
  135.                     byte[] buffer=httpParser.getRequestBody(local_request,bytes_read);  
  136.                     if(buffer!=null){  
  137.                         request=httpParser.getProxyRequest(buffer);  
  138.                         break;  
  139.                     }  
  140.                 }  
  141.                   
  142.                 boolean isExists=new File(request._prebufferFilePath).exists();  
  143.                 enablePrebuffer = isExists && request._isReqRange0;//两者具备才能使用预加载  
  144.                 Log.e(TAG,"enablePrebuffer:"+enablePrebuffer);  
  145.                 sentToServer(request._body);  
  146.                 // ------------------------------------------------------  
  147.                 // 把网络服务器的反馈发到MediaPlayer,网络服务器->代理服务器->MediaPlayer  
  148.                 // ------------------------------------------------------  
  149.                 boolean enableSendHeader=true;  
  150.                 while ((bytes_read = sckServer.getInputStream().read(remote_reply)) != -1) {  
  151.                     byte[] tmpBuffer = new byte[bytes_read];  
  152.                     System.arraycopy(remote_reply, 0, tmpBuffer, 0, tmpBuffer.length);  
  153.                       
  154.                     if(hasResponseHeader){  
  155.                     sendToMP(tmpBuffer);  
  156.                     }  
  157.                     else{  
  158.                         List<byte[]> httpResponse=httpParser.getResponseBody(remote_reply, bytes_read);  
  159.                         if(httpResponse.size()>0){  
  160.                             hasResponseHeader = true;  
  161.                             if (enableSendHeader) {  
  162.                                 // send http header to mediaplayer  
  163.                                 sendToMP(httpResponse.get(0));  
  164.                                 String responseStr = new String(httpResponse.get(0));  
  165.                                 Log.e(TAG+"<---", responseStr);  
  166.                             }  
  167.                             if (enablePrebuffer) {//send prebuffer to mediaplayer  
  168.                                 int fileBufferSize = sendPrebufferToMP(request._prebufferFilePath);  
  169.                                 if (fileBufferSize > 0) {//重新发送请求到服务器  
  170.                                     String newRequestStr = httpParser.modifyRequestRange(request._body,  
  171.                                                     fileBufferSize);  
  172.                                     Log.e(TAG + "-pre->", newRequestStr);  
  173.                                     enablePrebuffer = false;  
  174.   
  175.                                     // 下次不处理response的http header  
  176.                                     sentToServer(newRequestStr);  
  177.                                     enableSendHeader = false;  
  178.                                     hasResponseHeader = false;  
  179.                                     continue;  
  180.                                 }  
  181.                             }  
  182.   
  183.                             //发送剩余数据  
  184.                             if (httpResponse.size() == 2) {  
  185.                                 sendToMP(httpResponse.get(1));  
  186.                             }  
  187.                         }  
  188.                     }  
  189.                 }  
  190.                 Log.e(TAG, ".........over..........");  
  191.   
  192.                 // 关闭 2个SOCKET  
  193.                 sckPlayer.close();  
  194.                 sckServer.close();  
  195.             } catch (Exception e) {  
  196.                 Log.e(TAG,e.toString());  
  197.                 Log.e(TAG,ProxyUtils.getExceptionMessage(e));  
  198.             }  
  199.         }  
  200.     }  
  201.       
  202.     private int sendPrebufferToMP(String fileName) throws IOException {  
  203.         int fileBufferSize=0;  
  204.         byte[] file_buffer = new byte[1024];  
  205.         int bytes_read = 0;  
  206.         FileInputStream fInputStream = new FileInputStream(fileName);  
  207.         while ((bytes_read = fInputStream.read(file_buffer)) != -1) {  
  208.             fileBufferSize += bytes_read;  
  209.             byte[] tmpBuffer = new byte[bytes_read];  
  210.             System.arraycopy(file_buffer, 0, tmpBuffer, 0, bytes_read);  
  211.             sendToMP(tmpBuffer);  
  212.         }  
  213.         fInputStream.close();  
  214.           
  215.         Log.e(TAG,"读取完毕...下载:"+download.getDownloadedSize()+",读取:"+fileBufferSize);  
  216.         return fileBufferSize;  
  217.     }  
  218.       
  219.     private void sendToMP(byte[] bytes) throws IOException{  
  220.             sckPlayer.getOutputStream().write(bytes);  
  221.             sckPlayer.getOutputStream().flush();  
  222.     }  
  223.   
  224.     private void sentToServer(String requestStr) throws IOException{  
  225.         try {  
  226.             if(sckServer!=null)  
  227.                 sckServer.close();  
  228.         } catch (Exception ex) {}  
  229.         sckServer = new Socket();  
  230.         sckServer.connect(address);  
  231.         sckServer.getOutputStream().write(requestStr.getBytes());// 发送MediaPlayer的请求  
  232.         sckServer.getOutputStream().flush();  
  233.     }  
  234. }</span>  
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值