zoukankan      html  css  js  c++  java
  • 玩转 Android MediaPlayer之视频预加载(优化)

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

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

    文章源地址:http://blog.csdn.net/hellogv/article/details/7911293#comments

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

           本文是在《玩转 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 HttpGetProxy{
    	final 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));
    			}
    		}
    	}



    Meet so Meet. C plusplus I-PLUS....
  • 相关阅读:
    Ubuntu 14.04 卸载通过源码安装的库
    Ubuntu 14.04 indigo 相关依赖
    Ubuntu 14.04 indigo 安装 cartographer 1.0.0
    Ubuntu 14.04 改变文件或者文件夹的拥有者
    安装cartographer遇到Unrecognized syntax identifier "proto3". This parser only recognizes "proto2"问题
    Unrecognized syntax identifier "proto3". This parser only recognizes "proto2". ”问题解决方法
    查看所有用户组,用户名
    1卸载ROS
    Ubuntu14.04 软件安装卸载
    Ubuntu14.04系统显示器不自动休眠修改
  • 原文地址:https://www.cnblogs.com/iplus/p/4467391.html
Copyright © 2011-2022 走看看