玩转 Android MediaPlayer之视频预加载

本文是在《玩转 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/hcb1230/6593541下载,本文所用的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>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值