玩转 Android MediaPlayer之Media Proxy

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

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

       本文是在《Android MediaPlayer与Http Proxy结合之提高篇》基础上,进一步优化代理服务器,支持了Http的302、301重定向,获取Http Request和Http Response的文本内容。本文以视频播放结合Http Proxy,讲述MediaPlayer播放过程中的握手过程。

吐槽一下:用google搜索“代理服务器”无效,所以本文用Media Proxy,大家懂的......

先来看看本文程序运行的截屏动画:

再来看看程序运行时输出的Log信息.....这里是关键:

07-29 15:51:30.692: E/HttpGetProxy(449): ..........sckPlayer connected.......... MediaPlayer发出请求
07-29 15:51:30.692: E/to Media Server---->(449): GET /vids/family_guy_penis_car.3gp HTTP/1.1

07-29 15:51:30.692: E/to Media Server---->(449): User-Agent: stagefright/1.0 (Linux;Android 2.2)

07-29 15:51:30.692: E/to Media Server---->(449): Host: daily3gp.com

07-29 15:51:30.692: E/to Media Server---->(449):

07-29 15:51:31.119: E/HttpGetProxy(449): ..........remote Server connected..........
07-29 15:51:31.122: E/HttpGetProxy(449): ..........remote start to receive..........
07-29 15:51:31.775: E/from Media Server---->(449): HTTP/1.1 200 OK

07-29 15:51:31.775: E/from Media Server---->(449): Date: Sun, 29 Jul 2012 15:51:33 GMT

07-29 15:51:31.775: E/from Media Server---->(449): Server: Apache

07-29 15:51:31.775: E/from Media Server---->(449): Last-Modified: Thu, 14 Jan 2010 23:29:02 GMT

07-29 15:51:31.775: E/from Media Server---->(449): Accept-Ranges: bytes

07-29 15:51:31.775: E/from Media Server---->(449): Content-Length: 754777

07-29 15:51:31.775: E/from Media Server---->(449): Content-Type: video/3gpp


07-29 15:51:34.512: E/HttpGetProxy(449): ..........sckPlayer connected..........MediaPlayer发出请求
07-29 15:51:34.532: E/to Media Server---->(449): GET /vids/family_guy_penis_car.3gp HTTP/1.1

07-29 15:51:34.532: E/to Media Server---->(449): User-Agent: stagefright/1.0 (Linux;Android 2.2)

07-29 15:51:34.532: E/to Media Server---->(449): Host: daily3gp.com

07-29 15:51:34.532: E/to Media Server---->(449): Range: bytes=720896-

07-29 15:51:34.532: E/to Media Server---->(449):

07-29 15:51:34.873: E/HttpGetProxy(449): ..........remote Server connected..........
07-29 15:51:34.873: E/HttpGetProxy(449): ..........remote start to receive..........
07-29 15:51:35.505: E/from Media Server---->(449): HTTP/1.1 206 Partial Content

07-29 15:51:35.505: E/from Media Server---->(449): Date: Sun, 29 Jul 2012 15:51:36 GMT

07-29 15:51:35.505: E/from Media Server---->(449): Server: Apache

07-29 15:51:35.505: E/from Media Server---->(449): Last-Modified: Thu, 14 Jan 2010 23:29:02 GMT

07-29 15:51:35.505: E/from Media Server---->(449): Accept-Ranges: bytes

07-29 15:51:35.505: E/from Media Server---->(449): Content-Length: 33881

07-29 15:51:35.505: E/from Media Server---->(449): Content-Range: bytes 720896-754776/754777

07-29 15:51:35.505: E/from Media Server---->(449): Content-Type: video/3gpp
07-29 15:51:38.754: E/HttpGetProxy(449): ..........over..........


07-29 15:51:51.461: E/HttpGetProxy(449): ..........sckPlayer connected..........MediaPlayer发出请求
07-29 15:51:51.471: E/to Media Server---->(449): GET /vids/family_guy_penis_car.3gp HTTP/1.1

07-29 15:51:51.471: E/to Media Server---->(449): User-Agent: stagefright/1.0 (Linux;Android 2.2)

07-29 15:51:51.471: E/to Media Server---->(449): Host: daily3gp.com

07-29 15:51:51.471: E/to Media Server---->(449): Range: bytes=196608-

07-29 15:51:51.471: E/to Media Server---->(449):

07-29 15:51:51.722: E/HttpGetProxy(449): ..........remote Server connected..........
07-29 15:51:51.722: E/HttpGetProxy(449): ..........remote start to receive..........
07-29 15:51:52.285: E/from Media Server---->(449): HTTP/1.1 206 Partial Content

07-29 15:51:52.285: E/from Media Server---->(449): Date: Sun, 29 Jul 2012 15:51:53 GMT

07-29 15:51:52.285: E/from Media Server---->(449): Server: Apache

07-29 15:51:52.285: E/from Media Server---->(449): Last-Modified: Thu, 14 Jan 2010 23:29:02 GMT

07-29 15:51:52.285: E/from Media Server---->(449): Accept-Ranges: bytes

07-29 15:51:52.285: E/from Media Server---->(449): Content-Length: 558169

07-29 15:51:52.285: E/from Media Server---->(449): Content-Range: bytes 196608-754776/754777

07-29 15:51:52.285: E/from Media Server---->(449): Content-Type: video/3gpp


07-29 15:51:54.812: E/HttpGetProxy(449): ..........sckPlayer connected..........MediaPlayer发出请求
07-29 15:51:54.822: E/to Media Server---->(449): GET /vids/family_guy_penis_car.3gp HTTP/1.1

07-29 15:51:54.822: E/to Media Server---->(449): User-Agent: stagefright/1.0 (Linux;Android 2.2)

07-29 15:51:54.822: E/to Media Server---->(449): Host: daily3gp.com

07-29 15:51:54.822: E/to Media Server---->(449): Range: bytes=589824-

07-29 15:51:54.822: E/to Media Server---->(449):

07-29 15:51:55.117: E/HttpGetProxy(449): ..........remote Server connected..........
07-29 15:51:55.117: E/HttpGetProxy(449): ..........remote start to receive..........
07-29 15:51:55.693: E/from Media Server---->(449): HTTP/1.1 206 Partial Content

07-29 15:51:55.693: E/from Media Server---->(449): Date: Sun, 29 Jul 2012 15:51:57 GMT

07-29 15:51:55.693: E/from Media Server---->(449): Server: Apache

07-29 15:51:55.693: E/from Media Server---->(449): Last-Modified: Thu, 14 Jan 2010 23:29:02 GMT

07-29 15:51:55.693: E/from Media Server---->(449): Accept-Ranges: bytes

07-29 15:51:55.693: E/from Media Server---->(449): Content-Length: 164953

07-29 15:51:55.693: E/from Media Server---->(449): Content-Range: bytes 589824-754776/754777

07-29 15:51:55.693: E/from Media Server---->(449): Content-Type: video/3gpp
07-29 15:51:59.620: E/HttpGetProxy(449): ..........over..........

 

从截屏动画和Log信息看出,手动seek一次,但MediaPlayer进行了多次Range请求,这说明了“MediaPlayer会自动seek”,或许与MediaPlayer本地缓存有关。另外,不同硬解厂家实现的MediaPlayer估计会有不同的操作。

 

本文的代码可以到这里下载:

http://download.youkuaiyun.com/detail/hellogv/4463651

HttpGetProxy.JAVA还是本文的关键部分:

public class HttpGetProxy {  
    final static private String TAG = "HttpGetProxy";  
    final static private String LOCAL_IP_ADDRESS_1 = "127.0.0.1";
    final static private String LOCAL_IP_ADDRESS_2 = "10.0.2.2";
    final static private int HTTP_PORT = 80;
    final static private String HTTP_END="\r\n\r\n";
    
    /**代理服务器使用的端口*/
    private int proxy_ip_port;
    /**链接带的端口*/
    private String original_ip_port;
    /**远程服务器地址*/
    private String remoteHost;
    /**本地服务器地址*/
    private String localHost;
    private ServerSocket localServer = null;  
    /**收发Media Player请求的Socket*/
    private Socket sckPlayer = null;
    /**收发Media Server请求的Socket*/
    private Socket sckServer = null;
  
    private SocketAddress address;  
   
    /** 
     * 初始化代理服务器 
     * @param localport 代理服务器监听的端口 
     */  
    public HttpGetProxy(int localport) {  
    	try {
			_HttpGetProxy(LOCAL_IP_ADDRESS_1,localport);
		} catch (Exception e) {
			Log.e(TAG,LOCAL_IP_ADDRESS_1+"???"+e.toString());
			try {
				_HttpGetProxy(LOCAL_IP_ADDRESS_2,localport);
			}catch (Exception e1) {
				Log.e(TAG,LOCAL_IP_ADDRESS_2+"???"+e.toString());
				System.exit(0);
			}
		}
    }
    
    private void _HttpGetProxy(String ipAddress,int localport) throws UnknownHostException, IOException {  
    	proxy_ip_port=localport;  
        localServer = new ServerSocket(localport,1,InetAddress.getByName(ipAddress));
        localHost=ipAddress;
    }
   
    /** 
     * 把网络URL转为本地URL,127.0.0.1替换网络域名 
     * @param url 网络URL  
     * @return 本地URL 
     */  
    public String getLocalURL(String urlString){
    	//----排除HTTP特殊----//
    	String targetUrl=ProxyUtils.getRedirectUrl(urlString);
    	//----获取对应本地代理服务器的链接----//
        String result = null;
        URI originalURI=URI.create(targetUrl);  
        remoteHost=originalURI.getHost();  
        if(originalURI.getPort()!=-1){//URL带Port
            address = new InetSocketAddress(remoteHost,originalURI.getPort());//使用默认端口  
            original_ip_port = originalURI.getPort()+"";//保存端口,中转时替换
            result=targetUrl.replace(remoteHost+":"+originalURI.getPort(),  
            		localHost+":"+proxy_ip_port);  
        }  
        else{//URL不带Port  
            address = new InetSocketAddress(remoteHost,HTTP_PORT);//使用80端口 
            original_ip_port = "";
            result=targetUrl.replace(remoteHost,localHost+":"+proxy_ip_port);  
        }
        return result;     
    }  
    
    /** 
     * 启动代理服务器 
     * @throws IOException 
     */  
    public void asynStartProxy(){  
        new Thread() {  
            public void run() {
                int bytes_read;  
                byte[] local_request = new byte[1024];  
                byte[] remote_reply = new byte[1024];  
                while (true) {  
                    try {  
                        //--------------------------------------  
                        //监听MediaPlayer的请求,MediaPlayer->代理服务器  
                        //--------------------------------------  
                        sckPlayer = localServer.accept();  
                        Log.e(TAG, "..........sckPlayer connected..........");   
                        
                        String requestStr = "";
                        while ((bytes_read = sckPlayer.getInputStream().read(local_request)) != -1) {
                            byte[] tmpBuffer=new byte[bytes_read]; 
                            System.arraycopy(local_request,0,tmpBuffer,0,bytes_read);
                            String str = new String(tmpBuffer);
                            //Log.e("from MediaPlayer---->", str);  
                            requestStr = requestStr + str;  
                            if (requestStr.contains("GET")  
                                    && requestStr.contains(HTTP_END)) {  
                                break;
                            }   
                        }
                        
                        //把request中的本地ip改为远程ip
                        requestStr = requestStr.replace(localHost,remoteHost);
                        //把代理服务器端口改为原URL端口
                        if(TextUtils.isEmpty(original_ip_port))
                        	requestStr = requestStr.replace(":"+proxy_ip_port, "");
                        else
                        	requestStr = requestStr.replace(":"+proxy_ip_port, ":"+original_ip_port);

                        Log.e("to Media Server---->", requestStr);
                        //--------------------------------------  
                        //把MediaPlayer的请求发到网络服务器,代理服务器->网络服务器  
                        //--------------------------------------
                        sckServer = new Socket();  
                        sckServer.connect(address);  
                        Log.e(TAG,"..........remote Server connected..........");  
                        sckServer.getOutputStream().write(requestStr.getBytes());//发送MediaPlayer的请求  
                        //------------------------------------------------------  
                        //把网络服务器的反馈发到MediaPlayer,网络服务器->代理服务器->MediaPlayer  
                        //------------------------------------------------------
                        Log.e(TAG,"..........remote start to receive..........");
                        String responseStr = "";
                        boolean isCaptured=false;
                        while ((bytes_read = sckServer.getInputStream().read(remote_reply)) != -1) {
                        	byte[] tmpBuffer=new   byte[bytes_read]; 
                            System.arraycopy(remote_reply,0,tmpBuffer,0,bytes_read);
                            //----捕获收到的Response文本内容----//
							if (!isCaptured) {
								String str = new String(tmpBuffer);
								responseStr += str;
								if (responseStr.contains("HTTP/")  
	                                    && responseStr.contains(HTTP_END)) {
									
									int endIndex=responseStr.indexOf(HTTP_END, 0);
									responseStr=responseStr.substring(0, endIndex);
									Log.e("from Media Server---->", responseStr);
									isCaptured=true;
	                            }
							}
                            sckPlayer.getOutputStream().write(tmpBuffer);  
                            sckPlayer.getOutputStream().flush();
                        }
                        Log.e(TAG, "..........over.........."); 
                       
                        //关闭对内,对内 2个SOCKET
                        sckPlayer.close();  
                        sckServer.close();  
                    } catch (IOException e) {  
                        // TODO Auto-generated catch block  
                        e.printStackTrace();  
                    }  
                }  
            }  
        }.start();  
    }  
}  


Android MediaPlayer与Http Proxy结合之提高篇 在基础篇之上扩展,可以支持seek,更接近实际使用。 立即下载

相关推荐

android videoView视频播放

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

基于AndroidVideoCache的预加载(附Demo)

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

落叶挽歌的博客 3860

Android MediaPlayer与Http Proxy案例

Android MediaPlayer与Http Proxy案例源码,该源码案例将会教大家如何实现一个简单的代理服务器(仅支持Http Get),与Android的MediaPlayer结合,从而可以扩展出“播放 防盗链的媒体文件”,“边播放边保存”等的功能。 详细说明:http://android.662p.com/thread-4493-1-1.html

MediaPlayerProxy:智能媒体播放控制的新革命

MediaPlayerProxy:智能媒体播放控制的新革命 项目简介 是一个开源项目,由开发者 Andrew Hanks 创建。它提供了一个中间件解决方案,用于在多设备之间控制媒体播放,特别是针对那些不支持网络控制的本地媒体播放器。通过使用 WebSocket 进行通信,该项目能够实现远程操作和跨平台的媒体管理。 技术分析 WebSocket - MediaPlayerProxy 使用 Web...

gitblog_00064的博客 385

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

本文是在《玩转 Android MediaPlayer之视频预加载》基础上做更进一步的优化,适应更多终端的MediaPlayer,不再唠叨预加载的作用和基础,有兴趣的读者请看上回。

保姆级教程:用Unity为麒麟系统打包Linux应用(含路径权限全攻略)

本文提供了一份详细的Unity跨平台开发指南,重点介绍如何在麒麟系统上打包和部署Linux应用。从开发环境配置、路径权限处理到性能优化和常见问题解决,全面解析了Unity项目在国产操作系统上的适配流程,帮助开发者高效完成跨平台应用开发。

weixin_28692867的博客 504

Android MediaPlayer与Http Proxy结合之基础篇

本文来自http://blog.youkuaiyun.com/hellogv/ ,引用必须注明出处!              最近半年都忙着Android TV项目,在春节假期才有时间写点东西。先在这里给大家拜个年,祝大家龙年快乐...       直接进入主题:本文将会教大家如何实现一个简单的代理服务器(仅支持Http Get),与Android的MediaPlayer结合,从而可以扩展出“播放 防盗链的

hellogv的专栏 3万+

Remote ID的调制解调FPGA实现

Remote ID源于航空安全需求,例如在无人机密集区域实现设备跟踪和防碰撞。它类似于航空领域的ADS-B系统,但针对低空飞行器。Remote ID系统通常工作在特定频段(如2.4 GHz或5.8 GHz),通过无线链路广播数据包,包含设备ID、经纬度、高度和速度等信息。调制解调过程确保这些数据在噪声环境中可靠传输。例如,标准如ASTM F3411定义了Remote ID的协议框架,其中调制解调是关键物理层组件。

yulongfenghu的博客 1505

C/S Framework in MediaPlayer 架构

(Bpxxx是一个代理外包,所有真正的工作是再Bnxxx里面做的,这里p指proxy,n指native)当Server端接受到Client端的请求,MediaPlayerService会为每一个Client进程创建一个会话,这里就是new一个MediaPlayer::Client对象与其交互,然后这个对象再根据Client端请求的资源类型去判断创建了什么类型的Player,就是最下面那些。

Madory06的博客 313

因内存不足原因导致fragment空白解决方案

在androidx之前,一般我们防止Fragment重建是在actvitity的onCreate方法中使用savedInstanceState?.remove(“android:support:fragments”)或者savedInstanceState?但是在androidx中,使用这两行代码不生效,但是看FragmentActivity中的FRAGMENT_TAG还是android:support:fragments,那为什么会不生效呢,后来点进去看发现。

qq_39431405的博客 546

Anomalib实战:从零开始构建自定义数据集的异常检测模型

本文详细介绍了如何使用Anomalib 2.1.0从零开始构建自定义数据集的异常检测模型。通过实战演示,指导读者准备符合规范的数据集结构,快速上手训练Patchcore等先进算法,并完成模型推理与调优,为工业质检、医疗影像等场景提供高效的视觉异常检测解决方案。

weixin_29165465的博客 109

MediaPlayer代码分析(1)-初始化和设置数据的过程

android_media_MediaPlayer_native_setup() static void android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) { ALOGV("native_setup"); sp mp = new MediaPlayer();

liken的专栏 5349

Python 音频: sounddevice 使用 左声道/右声道/立体声 --- 播放,录音

# coding: utf8 """ Python 音频: 使用 sounddevice 操作声卡的 左声道, 右声道, 立体声 仅限 MME, Windows DirectSound, Windows WASAPI, Windows WDM-KS 等支持双声道的驱动类型 """ import os import sounddevice as sd import soundfile mapping_left_ch = 1 # 左声道为 1 mapping_right_ch

weixin_42019925的博客 4926

Android多媒体MediaStore运用

MediaScanner与音乐信息扫描 在Android系统中,多媒体库是通过MediaScanner去扫描磁盘文件,对元信息的处理,并通过MediaProvider保存到MediaStore中。下图为MediaScannerr框架: 图1-1MediaScanner框架流程 Android系统在SD卡插入后,MediaScanner服务会在...

峻峰飞阳的博客 blog.youkuaiyun.com/while0 1057

Agent 智能体学习指南:从入门到求职的完整路线

AI Agent 不是更长的 prompt,而是一类系统设计——模型在受控环境里观察状态、选择动作、调用工具、接收反馈。本文系统梳理 Agent 的 5 个核心概念、7 个关键模块(Goal/State/Context/Tools/Loop/Guardrails/Eval)、三条学习路线(工程落地/研究算法/求职作品)、框架选型策略(LangGraph/OpenAI Agents SDK/Pydantic AI 等场景推荐),以及可执行的 7 天最小项目计划(从概念到简历 bullet)。

program哲学 229

anaconda navigator 卡在loading application界面一直没有反应解决办法

在打开anaconda navigator 程序的时候,遇到了2个问题。第一个问题:打开的时候弹出提示:There is an instance of Anaconda Navigator already running.第二个问题:程序一直卡在loading application界面一直没有反应。以下是我的解决办法,有效果。

猿小白的博客 4383
上一篇: Android MediaPlayer与Http Proxy结合之提高篇
下一篇: 玩转 Android MediaPlayer之视频预加载
欧巴Godwin
欧巴Godwin 领域专家: 操作系统技术领域 领域专家: 操作系统技术领域
博客等级 码龄20年 9690粉丝 141原创
评论 10
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值