- 浏览: 1181473 次
文章分类
最新评论
-
yawnp0426:
直接下載:(1) https://www.dropbox.co ...
开源文档管理系统LetoDMS的安装和汉化 -
yawnp0426:
直接下載:https://www.dropbox.com/s/ ...
开源文档管理系统LetoDMS的安装和汉化 -
yawnp0426:
直接下載:(1) https://www.dropbox.co ...
开源文档管理系统LetoDMS的安装和汉化 -
yawnp0426:
直接下載:https://www.dropbox.com/s/ ...
开源文档管理系统LetoDMS的安装和汉化 -
yawnp0426:
現今可以直接下載:引用(1) https://www.drop ...
开源文档管理系统LetoDMS的安装和汉化
rtsp网络协议的java实现
一.参考资料
1. 《RTSP简单命令》:http://blog.csdn.net/feidragon319/archive/2007/08/14/1742357.aspx
2.http://bbs.21eic.com/dispbbs.asp?boardid=15&Id=22948
3. 《RTSP客户端的Java实现》:http://hi.baidu.com/ssyuan/blog/item/566df6defac1dc5094ee37eb.html
二. RTSP的常用命令与解释
其中C是客户端,S是服务端。
2.1 OPTIONS
C->S: OPTION request //询问S有哪些方法可用
S->C: OPTION response //S回应信息中包括提供的所有可用方法
使用举例:
客户端到服务端:
- OPTIONSrtsp://218.207.101.236:554/mobile/3/67A451E937422331RTSP/1.0
- Cseq:1
服务端对OPTIONS的回应:
- RTSP/1.0200OK
- Server:PVSS/1.4.8(Build/20090111;Platform/Win32;Release/StarValley;)
- Cseq:1
- Public:DESCRIBE,SETUP,TEARDOWN,PLAY,PAUSE,OPTIONS,ANNOUNCE,RECORD
2.2 DESCRIBE
C->S: DESCRIBE request //要求得到S提供的媒体初始化描述信息
S->C: DESCRIBE response //S回应媒体初始化描述信息,主要是sdp
使用举例:
客户端到服务端:
- DESCRIBE
- rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdpRTSP/1.0
- Cseq:2
服务端对OPTIONS的回应:
- RTSP/1.0200OK
- Server:PVSS/1.4.8(Build/20090111;Platform/Win32;Release/StarValley;)
- Cseq:2
- Content-length:421
- Date:Mon,03Aug200908:21:33GMT
- Expires:Mon,03Aug200908:21:33GMT
- Content-Type:application/sdp
- x-Accept-Retransmit:our-retransmit
- x-Accept-Dynamic-Rate:1
- Content-Base:rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/
- v=0
- o=MediaBox127992137813INIP40.0.0.0
- s=RTSPSession
- i=StarvBoxLiveCast
- c=INIP4218.207.101.236
- t=00
- a=range:npt=now-
- a=control:*
- m=video0RTP/AVP96
- b=AS:20
- a=rtpmap:96MP4V-ES/1000
- a=fmtp:96profile-level-id=8;config=000001b008000001b5090000010000000120008440fa282c2090a31f;decode_buf=12586
- a=range:npt=now-
- a=framerate:5
- a=framesize:96176-144
- a=cliprect:0,0,144,176
- a=control:trackID=1
2.3 SETUP
C->S: SETUP request //设置会话的属性,以及传输模式,提醒S建立会话
S->C: SETUP response //S建立会话,返回会话标识符,以及会话相关信息
客户端到服务端的请求举例:
- SETUPrtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/trackID=1
- RTSP/1.0
- Cseq:3
- Transport:RTP/AVP;UNICAST;client_port=16264-16265;mode=play
服务端对客户端的回应举例:
- RTSP/1.0200OK
- Server:PVSS/1.4.8(Build/20090111;Platform/Win32;Release/StarValley;)
- Cseq:3
- Session:26633092229589
- Date:Mon,03Aug200908:21:33GMT
- Expires:Mon,03Aug200908:21:33GMT
- Transport:RTP/AVP;UNICAST;mode=play;client_port=16264-16265;server_port=20026-20027
2.4 PLAY
C->S: PLAY request //C请求播放
S->C: PLAY response //S回应该请求的信息
客户端到服务端的请求举例:
- PLAYrtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdpRTSP/1.0
- Session:26633092229589
- Cseq:4
服务端对客户端的回应举例:
- RTSP/1.0200OK
- Server:PVSS/1.4.8(Build/20090111;Platform/Win32;Release/StarValley;)
- Cseq:4
- Session:26633092229589
- RTP-Info:url=rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/trackID=1;seq=0;rtptime=0
2.5 PAUSE
C->S: PAUSE request //C请求暂停播放
S->C:PAUSE response //S回应该请求的信息
客户端到服务端的请求举例:
- PAUSErtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/RTSP/1.0
- Cseq:5
- Session:26633092229589
服务端对客户端的回应举例:
- RTSP/1.0200OK
- Server:PVSS/1.4.8(Build/20090111;Platform/Win32;Release/StarValley;)
- Cseq:5
- Session:26633092229589
2.6 TEARDOWN
C->S: TEARDOWN request //C请求关闭会话
S->C: TEARDOWN response //S回应该请求
客户端到服务端的请求举例:
- TEARDOWNrtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/RTSP/1.0
- Cseq:6
- User-Agent:RealMediaPlayerHelixDNAClient/10.0.0.11279(win32)
- Session:26633092229589
服务端对客户端的回应举例:
- RTSP/1.0200OK
- Server:PVSS/1.4.8(Build/20090111;Platform/Win32;Release/StarValley;)
- Cseq:6
- Session:26633092229589
- Connection:Close
三. RTSP客户端的Java实现
3.1接口IEvent.java
接口IEvent.java的代码如下:
- packagecom.amigo.rtsp;
- importjava.io.IOException;
- importjava.nio.channels.SelectionKey;
- /***//**
- *IEvent.java网络事件处理器,当Selector可以进行操作时,调用这个接口中的方法.
- *2007-3-22下午03:35:51
- *@authorsycheng
- *@version1.0
- */
- publicinterfaceIEvent{
- /***//**
- *当channel得到connect事件时调用这个方法.
- *@paramkey
- *@throwsIOException
- */
- voidconnect(SelectionKeykey)throwsIOException;
- /***//**
- *当channel可读时调用这个方法.
- *@paramkey
- *@throwsIOException
- */
- voidread(SelectionKeykey)throwsIOException;
- /***//**
- *当channel可写时调用这个方法.
- *@throwsIOException
- */
- voidwrite()throwsIOException;
- /***//**
- *当channel发生错误时调用.
- *@parame
- */
- voiderror(Exceptione);
- }
3.2RTSP的测试类:RTSPClient.java
RTSP的测试类RTSPClient.java类的代码如下所示:
- packagecom.amigo.rtsp;
- importjava.io.IOException;
- importjava.net.InetSocketAddress;
- importjava.nio.ByteBuffer;
- importjava.nio.channels.SelectionKey;
- importjava.nio.channels.Selector;
- importjava.nio.channels.SocketChannel;
- importjava.util.Iterator;
- importjava.util.concurrent.atomic.AtomicBoolean;
- publicclassRTSPClientextendsThreadimplementsIEvent{
- privatestaticfinalStringVERSION="RTSP/1.0/r/n";
- privatestaticfinalStringRTSP_OK="RTSP/1.0200OK";
- /***//**远程地址*/
- privatefinalInetSocketAddressremoteAddress;
- /***//***本地地址*/
- privatefinalInetSocketAddresslocalAddress;
- /***//***连接通道*/
- privateSocketChannelsocketChannel;
- /***//**发送缓冲区*/
- privatefinalByteBuffersendBuf;
- /***//**接收缓冲区*/
- privatefinalByteBufferreceiveBuf;
- privatestaticfinalintBUFFER_SIZE=8192;
- /***//**端口选择器*/
- privateSelectorselector;
- privateStringaddress;
- privateStatussysStatus;
- privateStringsessionid;
- /***//**线程是否结束的标志*/
- privateAtomicBooleanshutdown;
- privateintseq=1;
- privatebooleanisSended;
- privateStringtrackInfo;
- privateenumStatus{
- init,options,describe,setup,play,pause,teardown
- }
- publicRTSPClient(InetSocketAddressremoteAddress,
- InetSocketAddresslocalAddress,Stringaddress){
- this.remoteAddress=remoteAddress;
- this.localAddress=localAddress;
- this.address=address;
- //初始化缓冲区
- sendBuf=ByteBuffer.allocateDirect(BUFFER_SIZE);
- receiveBuf=ByteBuffer.allocateDirect(BUFFER_SIZE);
- if(selector==null){
- //创建新的Selector
- try{
- selector=Selector.open();
- }catch(finalIOExceptione){
- e.printStackTrace();
- }
- }
- startup();
- sysStatus=Status.init;
- shutdown=newAtomicBoolean(false);
- isSended=false;
- }
- publicvoidstartup(){
- try{
- //打开通道
- socketChannel=SocketChannel.open();
- //绑定到本地端口
- socketChannel.socket().setSoTimeout(30000);
- socketChannel.configureBlocking(false);
- socketChannel.socket().bind(localAddress);
- if(socketChannel.connect(remoteAddress)){
- System.out.println("开始建立连接:"+remoteAddress);
- }
- socketChannel.register(selector,SelectionKey.OP_CONNECT
- |SelectionKey.OP_READ|SelectionKey.OP_WRITE,this);
- System.out.println("端口打开成功");
- }catch(finalIOExceptione1){
- e1.printStackTrace();
- }
- }
- publicvoidsend(byte[]out){
- if(out==null||out.length<1){
- return;
- }
- synchronized(sendBuf){
- sendBuf.clear();
- sendBuf.put(out);
- sendBuf.flip();
- }
- //发送出去
- try{
- write();
- isSended=true;
- }catch(finalIOExceptione){
- e.printStackTrace();
- }
- }
- publicvoidwrite()throwsIOException{
- if(isConnected()){
- try{
- socketChannel.write(sendBuf);
- }catch(finalIOExceptione){
- }
- }else{
- System.out.println("通道为空或者没有连接上");
- }
- }
- publicbyte[]recieve(){
- if(isConnected()){
- try{
- intlen=0;
- intreadBytes=0;
- synchronized(receiveBuf){
- receiveBuf.clear();
- try{
- while((len=socketChannel.read(receiveBuf))>0){
- readBytes+=len;
- }
- }finally{
- receiveBuf.flip();
- }
- if(readBytes>0){
- finalbyte[]tmp=newbyte[readBytes];
- receiveBuf.get(tmp);
- returntmp;
- }else{
- System.out.println("接收到数据为空,重新启动连接");
- returnnull;
- }
- }
- }catch(finalIOExceptione){
- System.out.println("接收消息错误:");
- }
- }else{
- System.out.println("端口没有连接");
- }
- returnnull;
- }
- publicbooleanisConnected(){
- returnsocketChannel!=null&&socketChannel.isConnected();
- }
- privatevoidselect(){
- intn=0;
- try{
- if(selector==null){
- return;
- }
- n=selector.select(1000);
- }catch(finalExceptione){
- e.printStackTrace();
- }
- //如果select返回大于0,处理事件
- if(n>0){
- for(finalIterator<SelectionKey>i=selector.selectedKeys()
- .iterator();i.hasNext();){
- //得到下一个Key
- finalSelectionKeysk=i.next();
- i.remove();
- //检查其是否还有效
- if(!sk.isValid()){
- continue;
- }
- //处理事件
- finalIEventhandler=(IEvent)sk.attachment();
- try{
- if(sk.isConnectable()){
- handler.connect(sk);
- }elseif(sk.isReadable()){
- handler.read(sk);
- }else{
- //System.err.println("Ooops");
- }
- }catch(finalExceptione){
- handler.error(e);
- sk.cancel();
- }
- }
- }
- }
- publicvoidshutdown(){
- if(isConnected()){
- try{
- socketChannel.close();
- System.out.println("端口关闭成功");
- }catch(finalIOExceptione){
- System.out.println("端口关闭错误:");
- }finally{
- socketChannel=null;
- }
- }else{
- System.out.println("通道为空或者没有连接");
- }
- }
- @Override
- publicvoidrun(){
- //启动主循环流程
- while(!shutdown.get()){
- try{
- if(isConnected()&&(!isSended)){
- switch(sysStatus){
- caseinit:
- doOption();
- break;
- caseoptions:
- doDescribe();
- break;
- casedescribe:
- doSetup();
- break;
- casesetup:
- if(sessionid==null&&sessionid.length()>0){
- System.out.println("setup还没有正常返回");
- }else{
- doPlay();
- }
- break;
- caseplay:
- doPause();
- break;
- casepause:
- doTeardown();
- break;
- default:
- break;
- }
- }
- //doselect
- select();
- try{
- Thread.sleep(1000);
- }catch(finalExceptione){
- }
- }catch(finalExceptione){
- e.printStackTrace();
- }
- }
- shutdown();
- }
- publicvoidconnect(SelectionKeykey)throwsIOException{
- if(isConnected()){
- return;
- }
- //完成SocketChannel的连接
- socketChannel.finishConnect();
- while(!socketChannel.isConnected()){
- try{
- Thread.sleep(300);
- }catch(finalInterruptedExceptione){
- e.printStackTrace();
- }
- socketChannel.finishConnect();
- }
- }
- publicvoiderror(Exceptione){
- e.printStackTrace();
- }
- publicvoidread(SelectionKeykey)throwsIOException{
- //接收消息
- finalbyte[]msg=recieve();
- if(msg!=null){
- handle(msg);
- }else{
- key.cancel();
- }
- }
- privatevoidhandle(byte[]msg){
- Stringtmp=newString(msg);
- System.out.println("返回内容:");
- System.out.println(tmp);
- if(tmp.startsWith(RTSP_OK)){
- switch(sysStatus){
- caseinit:
- sysStatus=Status.options;
- break;
- caseoptions:
- sysStatus=Status.describe;
- trackInfo=tmp.substring(tmp.indexOf("trackID"));
- break;
- casedescribe:
- sessionid=tmp.substring(tmp.indexOf("Session:")+9,tmp
- .indexOf("Date:"));
- if(sessionid!=null&&sessionid.length()>0){
- sysStatus=Status.setup;
- }
- break;
- casesetup:
- sysStatus=Status.play;
- break;
- caseplay:
- sysStatus=Status.pause;
- break;
- casepause:
- sysStatus=Status.teardown;
- shutdown.set(true);
- break;
- caseteardown:
- sysStatus=Status.init;
- break;
- default:
- break;
- }
- isSended=false;
- }else{
- System.out.println("返回错误:"+tmp);
- }
- }
- privatevoiddoTeardown(){
- StringBuildersb=newStringBuilder();
- sb.append("TEARDOWN");
- sb.append(this.address);
- sb.append("/");
- sb.append(VERSION);
- sb.append("Cseq:");
- sb.append(seq++);
- sb.append("/r/n");
- sb.append("User-Agent:RealMediaPlayerHelixDNAClient/10.0.0.11279(win32)/r/n");
- sb.append("Session:");
- sb.append(sessionid);
- sb.append("/r/n");
- send(sb.toString().getBytes());
- System.out.println(sb.toString());
- }
- privatevoiddoPlay(){
- StringBuildersb=newStringBuilder();
- sb.append("PLAY");
- sb.append(this.address);
- sb.append(VERSION);
- sb.append("Session:");
- sb.append(sessionid);
- sb.append("Cseq:");
- sb.append(seq++);
- sb.append("/r/n");
- sb.append("/r/n");
- System.out.println(sb.toString());
- send(sb.toString().getBytes());
- }
- privatevoiddoSetup(){
- StringBuildersb=newStringBuilder();
- sb.append("SETUP");
- sb.append(this.address);
- sb.append("/");
- sb.append(trackInfo);
- sb.append(VERSION);
- sb.append("Cseq:");
- sb.append(seq++);
- sb.append("/r/n");
- sb.append("Transport:RTP/AVP;UNICAST;client_port=16264-16265;mode=play/r/n");
- sb.append("/r/n");
- System.out.println(sb.toString());
- send(sb.toString().getBytes());
- }
- privatevoiddoOption(){
- StringBuildersb=newStringBuilder();
- sb.append("OPTIONS");
- sb.append(this.address.substring(0,address.lastIndexOf("/")));
- sb.append(VERSION);
- sb.append("Cseq:");
- sb.append(seq++);
- sb.append("/r/n");
- sb.append("/r/n");
- System.out.println(sb.toString());
- send(sb.toString().getBytes());
- }
- privatevoiddoDescribe(){
- StringBuildersb=newStringBuilder();
- sb.append("DESCRIBE");
- sb.append(this.address);
- sb.append(VERSION);
- sb.append("Cseq:");
- sb.append(seq++);
- sb.append("/r/n");
- sb.append("/r/n");
- System.out.println(sb.toString());
- send(sb.toString().getBytes());
- }
- privatevoiddoPause(){
- StringBuildersb=newStringBuilder();
- sb.append("PAUSE");
- sb.append(this.address);
- sb.append("/");
- sb.append(VERSION);
- sb.append("Cseq:");
- sb.append(seq++);
- sb.append("/r/n");
- sb.append("Session:");
- sb.append(sessionid);
- sb.append("/r/n");
- send(sb.toString().getBytes());
- System.out.println(sb.toString());
- }
- publicstaticvoidmain(String[]args){
- try{
- //RTSPClient(InetSocketAddressremoteAddress,
- //InetSocketAddresslocalAddress,Stringaddress)
- RTSPClientclient=newRTSPClient(
- newInetSocketAddress("218.207.101.236",554),
- newInetSocketAddress("192.168.2.28",0),
- "rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp");
- client.start();
- }catch(Exceptione){
- e.printStackTrace();
- }
- }
- }
其中:rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp为我在网上找到的一个rtsp的sdp地址,读者可自行更换,RTSP的默认端口为554.
3.3运行结果
运行RTSPClient.java,运行结果如下所示:
- 端口打开成功
- OPTIONSrtsp://218.207.101.236:554/mobile/3/67A451E937422331RTSP/1.0
- Cseq:1
- 返回内容:
- RTSP/1.0200OK
- Server:PVSS/1.4.8(Build/20090111;Platform/Win32;Release/StarValley;)
- Cseq:1
- Public:DESCRIBE,SETUP,TEARDOWN,PLAY,PAUSE,OPTIONS,ANNOUNCE,RECORD
- DESCRIBErtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdpRTSP/1.0
- Cseq:2
- 返回内容:
- RTSP/1.0200OK
- Server:PVSS/1.4.8(Build/20090111;Platform/Win32;Release/StarValley;)
- Cseq:2
- Content-length:421
- Date:Mon,03Aug200908:50:36GMT
- Expires:Mon,03Aug200908:50:36GMT
- Content-Type:application/sdp
- x-Accept-Retransmit:our-retransmit
- x-Accept-Dynamic-Rate:1
- Content-Base:rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/
- v=0
- o=MediaBox127992137813INIP40.0.0.0
- s=RTSPSession
- i=StarvBoxLiveCast
- c=INIP4218.207.101.236
- t=00
- a=range:npt=now-
- a=control:*
- m=video0RTP/AVP96
- b=AS:20
- a=rtpmap:96MP4V-ES/1000
- a=fmtp:96profile-level-id=8;config=000001b008000001b5090000010000000120008440fa282c2090a31f;decode_buf=12586
- a=range:npt=now-
- a=framerate:5
- a=framesize:96176-144
- a=cliprect:0,0,144,176
- a=control:trackID=1
- SETUPrtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/trackID=1
- RTSP/1.0
- Cseq:3
- Transport:RTP/AVP;UNICAST;client_port=16264-16265;mode=play
- 返回内容:
- RTSP/1.0200OK
- Server:PVSS/1.4.8(Build/20090111;Platform/Win32;Release/StarValley;)
- Cseq:3
- Session:15470472221769
- Date:Mon,03Aug200908:50:36GMT
- Expires:Mon,03Aug200908:50:36GMT
- Transport:RTP/AVP;UNICAST;mode=play;client_port=16264-16265;server_port=20080-20081
- PLAYrtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdpRTSP/1.0
- Session:15470472221769
- Cseq:4
- 返回内容:
- RTSP/1.0200OK
- Server:PVSS/1.4.8(Build/20090111;Platform/Win32;Release/StarValley;)
- Cseq:4
- Session:15470472221769
- RTP-Info:url=rtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/trackID=1;seq=0;rtptime=0
- PAUSErtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/RTSP/1.0
- Cseq:5
- Session:15470472221769
- 返回内容:
- RTSP/1.0200OK
- Server:PVSS/1.4.8(Build/20090111;Platform/Win32;Release/StarValley;)
- Cseq:5
- Session:15470472221769
- TEARDOWNrtsp://218.207.101.236:554/mobile/3/67A451E937422331/8jH5QPU5GWS07Ugn.sdp/RTSP/1.0
- Cseq:6
- User-Agent:RealMediaPlayerHelixDNAClient/10.0.0.11279(win32)
- Session:15470472221769
- 返回内容:
- RTSP/1.0200OK
- Server:PVSS/1.4.8(Build/20090111;Platform/Win32;Release/StarValley;)
- Cseq:6
- Session:15470472221769
- Connection:Close
- 端口关闭成功
对照运行结果,读者可以熟悉RTSP的常用命令.
相关推荐
rtsp地址不同的nvr有不通的拼接方式 rtsp转flv用于网页前端播放 经验证可用
RTSP, RTMP协议推流, 以及保存到本地MP4,MKV录像文件。 如果要成功编译, 需要下载和编译ffmpeg库,libfdk-aac库,x264库。 编译这些库,非常耗时。如果懒得去编译, 可以直接使用已经编译好的stream_push.dll...
Real Time Streaming Protocol或者RTSP(实时流媒体协议),是由Real network 和 Netscape共同提出的如何有效地在IP网络上传输流媒体数据的应用层协议。RTSP提供一种可扩展的框架,使能够提供可控制的,按需传输实时...
JAVA实现的流服务(直播,点播),JAVA实现的RTSP协议,可以实现与网络客户端的交互
其中内置多个网络传输(标准与规范)的实现库,轻松添加或扩展协议。例如:HttpServlet,RPC,MQTT(物联网通讯协议),Websocket,RTSP(流媒体协议),DNS(域名解析协议),MYSQL协议。 1.可以替代tomcat或jetty....
基于GB28181标准实现的网络视频平台,负责实现核心信令与设备管理后台部分,支持NAT穿透,支持海康、大华、宇视等品牌的IPC、NVR接入。支持国标级联,支持将不带国标功能的摄像机/直播流/直播推流转发到其他国标平台...
通过JavaCV读取并解析rtsp/rtmp视频流,并转复用/转码为浏览器可以播放的flv格式缓存到内存中,利用netty将内存中的flv格式视频流通过http或者websocket协议实现的http-flv、websocket-flv传输到浏览器页面,浏览器...
1. sipdroid\src\org\zoolu 中是sip协议栈的实现 2. sipdroid\src\org\sipdroid 中是软电话的实现 3. sipdroid\src\com 中是stun相关的实现 4. sipdroid默认使用的编码格式为G711-A率。 5. 直接用ant debug的方法...
Java版水果管理系统源码 基于(兼容系统MediaPlayer与EXOPlayer2),实现了多功能的视频播放器。 (请仔细阅读下方各项说明,大多数问题可在下方找到解答)。 类型 功能 缓存 边播边缓存,使用了;ExoPlayer使用...
合金弹头java简单源码真棒明星 我的 GitHub 星星的精选列表!...协议的实现 - OpenVPN 是一个开源 VPN 守护进程 - 树保姆的 SQL 语法 - 像 TODO、FIXME(用户)这样的注释标签的 Tree-sitter 语法。 - 类
基于 (兼容系统MediaPlayer与EXOPlayer2),实现了多功能的视频播放器。(请仔细阅读下面的说明,大多数问题可在下方找到解答)。 类型 功能 缓存 边播边缓存,使用了 ; ExoPlayer使用SimpleCache。 协议 h263 \ 4...
基于(兼容系统MediaPlayer与EXOPlayer2),实现了多功能的视频播放器。 (请仔细阅读下方各项说明,大多数问题可在下方找到解答)。 类型 功能 缓存 边播边缓存,使用了;ExoPlayer使用SimpleCache。 协议 h263\4\5、...