本文来自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是本文的核心,代理服务器的主要实现,源码如下:
- <span style="font-family:Comic Sans MS;font-size:18px;">/**
- * 代理服务器类
- * @author hellogv
- *
- */
- public class HttpGetProxy{
- 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;
- private SocketAddress address;
- /**下载线程*/
- 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=ProxyUtils.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 = ProxyUtils.getRedirectUrl(urlString);
- // ----获取对应本地代理服务器的链接----//
- String localUrl = null;
- URI originalURI = URI.create(targetUrl);
- remoteHost = originalURI.getHost();
- if (originalURI.getPort() != -1) {// URL带Port
- address = new InetSocketAddress(remoteHost, originalURI.getPort());// 使用默认端口
- remotePort = originalURI.getPort();// 保存端口,中转时替换
- localUrl = targetUrl.replace(
- remoteHost + ":" + originalURI.getPort(), localHost + ":"
- + localPort);
- } else {// URL不带Port
- address = 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;
- int bytes_read;
- boolean enablePrebuffer=false;//必须放在这里
- byte[] local_request = new byte[1024];
- byte[] remote_reply = new byte[1024];
- while (true) {
- boolean hasResponseHeader = 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);
- 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();
- enablePrebuffer = isExists && request._isReqRange0;//两者具备才能使用预加载
- Log.e(TAG,"enablePrebuffer:"+enablePrebuffer);
- sentToServer(request._body);
- // ------------------------------------------------------
- // 把网络服务器的反馈发到MediaPlayer,网络服务器->代理服务器->MediaPlayer
- // ------------------------------------------------------
- boolean enableSendHeader=true;
- while ((bytes_read = sckServer.getInputStream().read(remote_reply)) != -1) {
- byte[] tmpBuffer = new byte[bytes_read];
- System.arraycopy(remote_reply, 0, tmpBuffer, 0, tmpBuffer.length);
- if(hasResponseHeader){
- sendToMP(tmpBuffer);
- }
- else{
- List<byte[]> httpResponse=httpParser.getResponseBody(remote_reply, bytes_read);
- if(httpResponse.size()>0){
- hasResponseHeader = true;
- if (enableSendHeader) {
- // send http header to mediaplayer
- sendToMP(httpResponse.get(0));
- String responseStr = new String(httpResponse.get(0));
- Log.e(TAG+"<---", responseStr);
- }
- if (enablePrebuffer) {//send prebuffer to mediaplayer
- int fileBufferSize = sendPrebufferToMP(request._prebufferFilePath);
- if (fileBufferSize > 0) {//重新发送请求到服务器
- String newRequestStr = httpParser.modifyRequestRange(request._body,
- fileBufferSize);
- Log.e(TAG + "-pre->", newRequestStr);
- enablePrebuffer = false;
- // 下次不处理response的http header
- sentToServer(newRequestStr);
- enableSendHeader = false;
- hasResponseHeader = false;
- continue;
- }
- }
- //发送剩余数据
- if (httpResponse.size() == 2) {
- 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,ProxyUtils.getExceptionMessage(e));
- }
- }
- }
- private int sendPrebufferToMP(String fileName) throws IOException {
- int fileBufferSize=0;
- byte[] file_buffer = new byte[1024];
- int bytes_read = 0;
- FileInputStream fInputStream = new FileInputStream(fileName);
- while ((bytes_read = fInputStream.read(file_buffer)) != -1) {
- fileBufferSize += bytes_read;
- byte[] tmpBuffer = new byte[bytes_read];
- System.arraycopy(file_buffer, 0, tmpBuffer, 0, bytes_read);
- sendToMP(tmpBuffer);
- }
- fInputStream.close();
- Log.e(TAG,"读取完毕...下载:"+download.getDownloadedSize()+",读取:"+fileBufferSize);
- return fileBufferSize;
- }
- private void sendToMP(byte[] bytes) throws IOException{
- sckPlayer.getOutputStream().write(bytes);
- sckPlayer.getOutputStream().flush();
- }
- private void sentToServer(String requestStr) throws IOException{
- try {
- if(sckServer!=null)
- sckServer.close();
- } catch (Exception ex) {}
- sckServer = new Socket();
- sckServer.connect(address);
- sckServer.getOutputStream().write(requestStr.getBytes());// 发送MediaPlayer的请求
- sckServer.getOutputStream().flush();
- }
- }</span>