玩转 Android MediaPlayer之视频预加载

6-6 快速播放音频和视频预加载 更快的播放开始意味着更多的人观看您的视频或收听您的音频。这是众所周知的事实。在本文中,我将探讨可用于通过根据您的用例主动预加载资源来加速音频和视频播放的技术。 致谢:版权 Blender Foundation |www.blender.org。 我将描述预加载媒体文件的三种方法,从它们的优缺点开始。 这很棒... 但... 视频预加载属性 易于用于托管在 Web 服务器上的唯一文件。 浏览器可能会完全忽略该属性。 当 HTML 文档被完全加载和解析后,资源获取开始。. 阅读详情

本文来自http://blog.youkuaiyun.com/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.youkuaiyun.com/detail/hellogv/4486051下载,本文所用的MP4搜索自百度....

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

/**
 * 代理服务器类
 * @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();
	}
}


 

 

android videoView视频播放 简单的android videoView视频播放,希望对大家会有帮助! 立即下载

相关推荐

数字孪生在智慧医疗中的5个创新应用:从手术模拟到个性化治疗

数字孪生技术通过创建患者或医疗流程的动态虚拟映射,正在深刻变革智慧医疗。本文重点剖析了其在个性化治疗、手术模拟与导航、药物研发、医院管理优化及慢性病管理五大核心场景的创新应用,展现了如何利用智能映射与数据融合,为精准医疗和高效决策提供强大支持。

weixin_29189003的博客 443

基于AndroidVideoCache的预加载(附Demo)

最近有做需求关于视频缓存,了解到相关的开源库AndroidVideoCache,一款市面上相对比较流行的视频缓存框架,而我想利用该框架进行视频缓存的处理,并且希望能够支持预加载。然而该框架作者在18年就已经停止了维护,所以留下了无限的编程空间给其他程序员,对于视频预加载,只搜到一篇《AndroidVideoCache源码详解以及改造系列-源码篇》,然而点进该作者的博客列表,说好的预加载呢???后面也没有了下文,搜遍全网好像没有做AndroidVideoCache的预加载相关的事情,那么这样子的话……自己干

落叶挽歌的博客 3860

告别环境配置烦恼:用Conda一键搞定GATK4、samtools、fastp等生信分析全家桶

本文介绍如何利用Conda快速搭建包含GATK4、samtools、fastp等工具的生物信息学分析环境,解决软件安装和版本冲突问题。通过详细的配置步骤和实战案例,帮助研究者十分钟内完成重测序分析全家桶的环境配置,显著提升上游分析效率。

weixin_30753873的博客 334

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

本文来自http://blog.youkuaiyun.com/hellogv/ ,引用必须注明出处!       本文是在《玩转 Android MediaPlayer之视频预加载》基础上做更进一步的优化,适应更多终端的MediaPlayer,不再唠叨预加载的作用和基础,有兴趣的读者请看上回。       MediaPlayer由厂家定制,不同终端的MediaPlayer略有差异,例如:有些MediaPlay

hellogv的专栏 9万+

告别卡顿!OBS Studio最佳录制参数设置指南(适合游戏/网课/会议场景)

本文提供OBS Studio录制参数优化指南,针对游戏、网课及会议三大场景,详细解析帧率、码率与编码器设置。重点推荐利用硬件编码器(如NVENC)实现游戏录制零卡顿,并分享清晰稳定的网课录制与长时间会议录制配置方案,助你告别视频卡顿与音画不同步问题。

e4f5g6h7的博客 1330

二维光子晶体禁带的遗传优化算法实现

二维光子晶体禁带的遗传优化算法MATLAB源代码 光子晶体中因周期性结构而存在的频率禁带称为光子禁带,光子禁带的存在是光子晶体具有广泛应用前景的重要原因。 禁带越大,可控光的频带也越宽,因此如何设计合适的晶体结构以获得大的光子禁带一直是研究和应用的重点与热点。 给大家讲解在平面波展开法的基础上,使用遗传算法寻找具有最大光子禁带的二维光子晶体结构。 %-------------------------------------------------------------------------- % Ph

m0_53407570的博客 582

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

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

qq_44929301的博客 1571

手把手教你学Simulink——基于Simulink的CLLC谐振变换器双向对称控制

本文介绍了一种基于Simulink的CLLC谐振变换器双向对称控制方法。针对CLLC拓扑的多阶谐振特性,采用固定频率、50%占空比、仅调节相位差的对称控制策略,实现了宽电压范围调节和全负载ZVS软开关。详细阐述了Simulink建模步骤,包括功率级搭建、相位差生成逻辑和双向切换实现,并通过仿真验证了稳态性能和双向无缝切换能力。该方法解决了传统PFM控制的不足,具有控制简单、效率高、易于工程实现的优势,适用于车载充电和储能系统等双向功率应用场景。

MHD0815的博客 302
上一篇: 玩转 Android MediaPlayer之Media Proxy
下一篇: 玩转 Android MediaPlayer之视频预加载(优化)
欧巴Godwin
欧巴Godwin 领域专家: 操作系统技术领域 领域专家: 操作系统技术领域
博客等级 码龄20年 9690粉丝 141原创
评论 14
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值