织梦末班和dw建设网站哪个方便优化,做ppt免费模板软件,宣传片拍摄构思,wordpress授权登录界面被老板叼了几次了#xff0c;最近实在忍不了#xff0c;准备离职#xff1b;
但是担心离职后长时间没有办法找到工作
就想贡献一套平时琢磨出来的程序#xff0c;请各位有能力的话带我熬过这凛冽的寒冬。
目前写出来的#xff0c;有三个端#xff1a;安卓TV端#xf…被老板叼了几次了最近实在忍不了准备离职
但是担心离职后长时间没有办法找到工作
就想贡献一套平时琢磨出来的程序请各位有能力的话带我熬过这凛冽的寒冬。
目前写出来的有三个端安卓TV端安卓手机端Windows-Qt端
这篇是TV端的部分代码当然要理解一下我并不能粘贴出完整的代码请见谅
想要获取完整源码可以直接私信我万分感激
那下面就开始粘贴部分源码
1.SSDP的发送
DLNA是通过监听网络当中的广播进行解析的也就是在循环发送几个消息
这些消息有的是关于设备信息的有些是关于控制命令的总之得根据自己的来
public String toString(int i) {//都是些固定格式NT和USN是两辆相关StringBuilder content new StringBuilder();content.append(NOTIFY * HTTP/1.1).append(\r\n);content.append(SERVER: Linux/3.14.29 UPnP/1.0 Cling/2.0).append(\r\n);content.append(CACHE-CONTROL: max-age1800).append(\r\n);//指定通知消息存活时间如果超过此时间间隔控制点可以认为设备不存在content.append(LOCATION: _LOCATION).append(\r\n);//乐播投屏加了下面三行加了就搜索不到了
// content.append(OPT: \http://schemas.upnp.org/upnp/1/0/\; ns01; ds0).append(\r\n);
// content.append(01-NLS: 0dcebdf8-1dd2-11b2-8b72-f07170d94291).append(\r\n);
// content.append(X-User-Agent: redsonic).append(\r\n);content.append(NTS: ssdp:alive).append(\r\n);if(i0){content.append(NT: upnp:rootdevice).append(\r\n);content.append(USN: uuid: MainActivity._UUID::upnp:rootdevice).append(\r\n);}else if(i1){content.append(NT: uuid:MainActivity._UUID).append(\r\n);content.append(USN: uuid:MainActivity._UUID).append(\r\n);} else if(i2){content.append(NT: urn:schemas-upnp-org:device:MediaRenderer:1).append(\r\n);content.append(USN: uuid:MainActivity._UUID::urn:schemas-upnp-org:device:MediaRenderer:1).append(\r\n);}else if(i3){content.append(NT: urn:schemas-upnp-org:service:RenderingControl:1).append(\r\n);content.append(USN: uuid:MainActivity._UUID::urn:schemas-upnp-org:service:RenderingControl:1).append(\r\n);}else if(i4){content.append(NT: urn:schemas-upnp-org:service:ConnectionManager:1).append(\r\n);content.append(USN: uuid:MainActivity._UUID::urn:schemas-upnp-org:service:ConnectionManager:1).append(\r\n);}else if(i5){content.append(NT: urn:schemas-upnp-org:service:AVTransport:1).append(\r\n);content.append(USN: uuid:MainActivity._UUID::urn:schemas-upnp-org:service:AVTransport:1).append(\r\n);}content.append(HOST: 239.255.255.250:1900).append(\r\n);content.append(\r\n);//Log.e(SSDPSendMsg,MainActivity._UUID\r\n\r\n);return content.toString();//https://blog.csdn.net/thebestleo/article/details/50800781}
安卓端发送成功后其实并不能在各大平台的设备上正常显示
这时候就需要在安卓端搭建一个web服务web服务里面指向一个描述的xml有关这个投屏设备的一些可以使用的信息比如酒店某个“房间号”“品牌房间号”都可以动态设置比如我就设置的MyDLNA
2.搭建一个web服务
//noinspection GradleDependency
api org.eclipse.jetty:jetty-server:8.1.21.v20160908
//noinspection GradleDependency
api org.eclipse.jetty:jetty-servlet:8.1.21.v20160908
//noinspection GradleDependency
api org.eclipse.jetty:jetty-client:8.1.21.v20160908
api org.nanohttpd:nanohttpd:2.3.1我这边就不粘贴很多
粘贴一下认证部分的内容认证说起来也简单就是电视端给一个链接让手机端可以访问电视端把这个链接转换成二维码
手机要先连接和电视机端同样的网络下面手机扫码后直接就请求电视机了 扫码后请求电视机会拿到请求的头部也就请求来的手机ip地址把这个ip缓存一下做一下判断只给这个手机ip发送广播这样就可以实现扫码后单一的投屏就不会在同一个酒店下面显示所有房间的电视机了 public class ControlHandler extends org.eclipse.jetty.server.handler.DefaultHandler {private Activity myactivity;private String PlayerURL ;private String DesiredVolume ;private String PlayTarget ;private String PlayState NO_MEDIA_PRESENT;private String PlayerURLTemp xxx;public ControlHandler(Activity activity) {super();this.myactivity activity;}Overridepublic void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {if (response.isCommitted() || baseRequest.isHandled()) {return;}Log.e(RequestShare, request.getRequestURI());//认证过就返回if (request.getRequestURI().contains(/certification)) {Constants.IPDEVICE request.getRemoteAddr();Log.e(RequestShare, 正在认证设备);baseRequest.setHandled(true);ServletMsg.certificationMsg(response);}//这个是读取认证的设备的(可以使得投屏设备具有唯一性)if (request.getRequestURI().contains(/upnp/dev/ MainActivity._UUID /desc)) {//验证可以通过对比IP实现指定设备投屏if (Constants.ALLDEVICES) {Log.e(RequestShare, 所有设备都可以投屏);baseRequest.setHandled(true);ServletMsg.defaultMsg(response);} else {if (request.getRemoteAddr().contains(Constants.IPDEVICE)) {//验证可以通过对比IP实现指定设备投屏Log.e(RequestShare, 指定: request.getRemoteAddr() 设备符合投屏条件);baseRequest.setHandled(true);response.addHeader(Content-type: , text/xml);response.addHeader(SERVER: , Linux/3.14.29 UPnP/1.0 Cling/2.0);response.addHeader(Cache-control: , max-age1800);
// response.addHeader(X-User-Agent: ,redsonic);ServletMsg.defaultMsg(response);}}}//请求分析if (Constants.ALLDEVICES) {getHeader(request, baseRequest, response); //头部执行播放暂停之类的简单操作getBody(request, baseRequest, response);//这个主要是主要处理有携带参数的数据response还是加了一下避免以后自己做控制端//爱奇艺和腾讯会请求里面链接单纯回复可能不管用加的一项ServletRequest(request, baseRequest, response);} else {if (request.getRemoteAddr().contains(Constants.IPDEVICE)) {//验证可以通过对比IP实现指定设备投屏Log.e(RequestShare1, request.getRemoteAddr());//IP地址Log.e(RequestShare2, request.getRequestURI());//请求的路径getHeader(request, baseRequest, response); //头部执行播放暂停之类的简单操作getBody(request, baseRequest, response);//这个主要是主要处理有携带参数的数据response还是加了一下避免以后自己做控制端//爱奇艺和腾讯会请求里面链接单纯回复可能不管用加的一项ServletRequest(request, baseRequest, response);}}}
3.投射视频
讲到投射视频就很简单了可以理解为手机向电视机发送了一个请求只不过这个请求头请求体都需要根据特定格式来
例如 ?xml version1.0 encodingUTF-8?s:Envelope s:encodingStylehttp://schemas.xmlsoap.org/soap/encoding/ xmlns:shttp://schemas.xmlsoap.org/soap/envelope/s:Bodyu:SetAVTransportURI xmlns:uurn:schemas-upnp-org:service:AVTransport:1InstanceID0/InstanceIDCurrentURIhttp://183.131.147.16/upgcxcode/09/78/1255627809/1255627809-1-192.mp4?eig8euxZM2rNcNbNBhWdVhwdlhbU1hwdVhoNvNC8BqJIzNbfqXBvEuENvNC8aNEVEtEvE9IMvXBvE2ENvNCImNEVEIj0Y2J_aug859r1qXg8gNEVE5XREto8z5JZC2X2gkX5L5F1eTX1jkXlsTXHeux_f2o859IMvNC8xNbLEkF6MuwLStj8fqJ0EkX1ftx7Sqr_aio8_amp;uatvprojamp;uipk5amp;nbs1amp;deadline1693797974amp;genplayurlv2amp;osbcacheamp;oi3728755326amp;trid00002c92df64c42a43fa9d53a3d4ca9263eaTamp;mid434537794amp;upsiga446d61c8f79924e141b2973d94a0715amp;uparamse%2Cua%2Cuipk%2Cnbs%2Cdeadline%2Cgen%2Cos%2Coi%2Ctrid%2Cmidamp;cdnid22222amp;bvcvodamp;nettype0amp;bw251536amp;orderid0%2C3amp;buvidY947C4998063ABA44C5595C15C70401D62DFamp;build70100100amp;mobi_appiphoneamp;fT_0_0amp;logo80000000amp;_nva_ext_/CurrentURICurrentURIMetaDatalt;DIDL-Lite xmlnsurn:schemas-upnp-org:metadata-1-0/DIDL-Lite/ xmlns:dchttp://purl.org/dc/elements/1.1/ xmlns:upnpurn:schemas-upnp-org:metadata-1-0/upnp/gt;lt;item id0 parentID-1 restricted1gt;lt;dc:titlegt;我花1万块重庆打车到上海却被全国网友说成老赖lt;/dc:titlegt;lt;upnp:storageMediumgt;UNKNOWNlt;/upnp:storageMediumgt;lt;upnp:writeStatusgt;UNKNOWNlt;/upnp:writeStatusgt;lt;upnp:longDescriptiongt;8ZPKtX3aKq2ata7O0mJBw8X-jfpm6X7rvBzgi6zEJLdfXlUYjO_zomV6xIkLrNS8Y97j6EBY4i-BmrgA2WWIqDn9Gd6p83nZCVDTdbhDOos4x_QGAByL3FXnYPdYvFYaHluTvVQ5MKCckyKCQIKKxdYatoPoEqsgeVHUPN4WtaAChNi5UaHQfwtwRNaZO_SSa9jnMEYFDhN4kpzC4A0TmmqKUbUQvdIR9fh7acHLpKz3Rt7XmnEu-o9jfV7km3nwBpPT8il6Ycxp5Gw0k-kEa192ccOzzA0V11Bg7EntZzm7aJKn2QVwqg5t8hFPxpdS32W6yzM1W-7usjfm2SOESC4guqmfBlaOxdVL8hnG3mdl_BGU3kmcVhKz8u0u79UsDTWEElD1088XFetEb--WevzgLqcQF53VlmwPzCF1hoIXID5L4UaTmLu6cSzi3dKWlt;/upnp:longDescriptiongt;lt;res protocolInfohttp-get:*:video/mp4:DLNA.ORG_PNMPEG4_P2_SP_AAC;DLNA.ORG_OP01;DLNA.ORG_CI0;DLNA.ORG_FLAGS01500000000000000000000000000000gt;http://183.131.147.16/upgcxcode/09/78/1255627809/1255627809-1-192.mp4?eig8euxZM2rNcNbNBhWdVhwdlhbU1hwdVhoNvNC8BqJIzNbfqXBvEuENvNC8aNEVEtEvE9IMvXBvE2ENvNCImNEVEIj0Y2J_aug859r1qXg8gNEVE5XREto8z5JZC2X2gkX5L5F1eTX1jkXlsTXHeux_f2o859IMvNC8xNbLEkF6MuwLStj8fqJ0EkX1ftx7Sqr_aio8_amp;amp;uatvprojamp;amp;uipk5amp;amp;nbs1amp;amp;deadline1693797974amp;amp;genplayurlv2amp;amp;osbcacheamp;amp;oi3728755326amp;amp;trid00002c92df64c42a43fa9d53a3d4ca9263eaTamp;amp;mid434537794amp;amp;upsiga446d61c8f79924e141b2973d94a0715amp;amp;uparamse%2Cua%2Cuipk%2Cnbs%2Cdeadline%2Cgen%2Cos%2Coi%2Ctrid%2Cmidamp;amp;cdnid22222amp;amp;bvcvodamp;amp;nettype0amp;amp;bw251536amp;amp;orderid0%2C3amp;amp;buvidY947C4998063ABA44C5595C15C70401D62DFamp;amp;build70100100amp;amp;mobi_appiphoneamp;amp;fT_0_0amp;amp;logo80000000amp;amp;_nva_ext_lt;/resgt;lt;upnp:classgt;object.item.videoItemlt;/upnp:classgt;lt;/itemgt;lt;/DIDL-Litegt;/CurrentURIMetaData/u:SetAVTransportURI/s:Body/s:Envelope 这就是发过来的视频连接适当解析后就可以正常播放了 4.监听播放与控制进度
手机端会不断给电视端发送实时数据比如音量啊进度啊这些手机监听到系统音量后会给DLNA也发一条关于音量的数据电视机拿到就处理一下即可 大部分视频厂家都是可以正常上报,优酷和芒果倒是加了些机制需要电视机端给设备发消息说我已经开始播放了然后优酷才会继续播不然播了两秒优酷没有收到电视机的消息会立刻把链接关掉云云.. //获取body信息private void getBody(HttpServletRequest request, Request baseRequest, HttpServletResponse response) throws IOException {Log.e(getBody, ------getBody分割线start------);BufferedReader br request.getReader();String str, wholeStr ;while ((str br.readLine()) ! null) {wholeStr str;}Log.e(getBody, wholeStr);//拿到播放的链接if (wholeStr.contains(u:SetAVTransportURI)) {Log.e(55m5m53, wholeStr);String temp wholeStr.substring(0, wholeStr.indexOf(/CurrentURI));PlayerURL temp.substring(temp.indexOf(CurrentURI)).replace(CurrentURI, ).replace(amp;, ).replace(quot;, \).replace(lt;, ).replace(gt;, ).replace(nbsp;, ).replace(apos;, );Log.e(55m5m54, PlayerURL);PlayState PLAYING;//Response响应一下baseRequest.setHandled(true);ServletMsg.SetAVTransportURIResponseMsg(response);//发送并打开页面给播放器
// Intent intent new Intent(myactivity, PlayerActivity.class);
// intent.putExtra(PlayerURL, PlayerURL);
// myactivity.startActivity(intent);//芒果TV一次性发了5个
// if (!PlayerURL.equals(PlayerURLTemp)) {
// PlayerURLTemp PlayerURL;countDownTimer.start();
// intent.putExtra(PlayerURL, PlayerURL);
// myactivity.startActivity(intent);
// }} else if (wholeStr.contains(u:SetVolume)) {//摘取出要我设置的音量String Volume wholeStr.substring(0, wholeStr.indexOf(/DesiredVolume));DesiredVolume Volume.substring(Volume.indexOf(DesiredVolume)).replace(DesiredVolume, );Log.e(SetVolume, DesiredVolume);//Response响应一下baseRequest.setHandled(true);ServletMsg.SetVolumeResponseMsg(response);//到这边的意思就是和播放器说要控制一下设备的音频SendBroadcast(Volume# DesiredVolume);} else if (wholeStr.contains(u:Seek)) {//摘取出要我设置的进度String Target wholeStr.substring(0, wholeStr.indexOf(/Target));PlayTarget Target.substring(Target.indexOf(Target)).replace(Target, );Log.e(PlayTarget, PlayTarget);SendBroadcast(Seek# PlayTarget);//告诉播放器我要设置进度了} else if (wholeStr.contains(u:GetTransportInfo)) {//告诉控制端我还活着Log.e(u:GetTransportInfo, 电视系统的State: PlayState);baseRequest.setHandled(true);ServletMsg.GetTransportInfoMsg(response, PlayState, OK);} else if (wholeStr.contains(u:GetVolume)) {//腾讯bilibili会要求获取一下音量就给呗//bilibili给了后会一直索要好奇怪Log.e(u:GetVolume, 电视系统的Volume: PlayerActivity.playerData.get_volume());baseRequest.setHandled(true);ServletMsg.GetVolumeMsg(response, PlayerActivity.playerData.get_volume());} else if (wholeStr.contains(u:GetPositionInfo)) {//告诉控制端我当前的播放状态GetPositionInfo(response, baseRequest);} else if (wholeStr.contains(u:GetMediaInfo)) {//爱奇艺单独列了这个,尝试发了但没反应找了几个数据应该是正确的Log.e(getBody, 我拿到GetMediaInfo了);GetMediaInfo(response, baseRequest);} else if (wholeStr.contains(u:Stop)) {//有些东西需要响应的就响应一下Log.e(getBody, 我拿到Stop了);baseRequest.setHandled(true);ServletMsg.StopResponseMsg(response);}Log.e(getBody, ------getBody分割线end------);}private CountDownTimer countDownTimer new CountDownTimer(1000, 1000) {Overridepublic void onTick(long millisUntilFinished) {}Overridepublic void onFinish() {//PlayerURLTempFowlet;if(isTopActivity(Constants.PLAYACTIVITY)){SendBroadcast(State#Stop);}Intent intent new Intent(myactivity, PlayerActivity.class);//Intent intent new Intent(myactivity, Player2Activity.class);intent.putExtra(PlayerURL, PlayerURL);myactivity.startActivity(intent);}};//获取的头部信息private void getHeader(HttpServletRequest request, Request baseRequest, HttpServletResponse response) {Log.e(getHeader, ------getHeader分割线start------);EnumerationString em request.getHeaderNames();while (em.hasMoreElements()) {String name (String) em.nextElement();String value request.getHeader(name);//System.out.println(name value);Log.e(getHeader, name value);if (name.equals(SOAPAction) | name.equals(SOAPACTION)) {String[] split value.split(#);String result split[1];//获取头部的一些快捷操作如果说有一些要参数要回调的话还是要走bodyString Transport result.substring(0, result.indexOf(\));Log.e(yongge, Transport);if (Transport.equals(Play)) {//播放PlayState PLAYING;Log.e(Header__, 我拿到Play了: PlayState);//开始播放了就让播放器开始收集信息//SendBroadcast(GetInfo#Transport);//暂停播放也会用的到SendBroadcast(State# Transport);} else if (Transport.equals(Stop)) {//停止PlayState STOPPED;Log.e(Header, 我拿到Stop了: PlayState);SendBroadcast(State# Transport);} else if (Transport.equals(Pause)) {//暂停PlayState PAUSED_PLAYBACK;Log.e(Header__, 我拿到Pause了: PlayState);SendBroadcast(State# Transport);} else if (Transport.equals(SetVolume)) {//设置音量Log.e(Header, 我拿到SetVolume了 DesiredVolume);} else if (Transport.equals(GetPositionInfo)) {SendBroadcast(GetInfo# Transport);//告诉播放器你要开始收集播放器信息了//获取进度Log.e(Header, 我拿到GetPositionInfo了);} else if (Transport.equals(GetTransportInfo)) {//获取传输状态//安卓端的腾讯是拿的GetTransportInfoLog.e(Header, 我拿到GetTransportInfo了);} else if (Transport.equals(SetMute)) {//静音/取消Log.e(Header, 我拿到SetMute了);} else if (Transport.equals(Seek)) {//设置进度Log.e(Header, 我拿到Seek了 PlayerActivity.playerData.get_seek());} else if (Transport.equals(GetMediaInfo)) {//爱奇艺单独列了这个Log.e(Header, 我拿到GetMediaInfo了);} else if (Transport.equals(GetVolume)) {Log.e(Header, 我拿到GetVolume了);}}//优酷是要走订阅if (name.equals(CALLBACK)) {String callbackUrl value.replace(, ).replace(, );Log.e(callbackUrl, callbackUrl);String str[] callbackUrl.replace(/,).split(:);try {Socket socket new Socket(str[1], Integer.parseInt(str[2]));Log.e(callbackUrl2, str[1]---str[2]);BufferedWriter bw new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write(HTTP/1.1 200 OK\r\n);bw.write(Server: Linux/3.14.29 UPnP/1.0 Cling/2.0\r\n);bw.write(SID: uuid:MainActivity._UUID\r\n);bw.write(Content-Type: text/html; charset\utf-8\\r\n);bw.write(TIMEOUT: Second-3600\r\n\r\n);bw.flush();BufferedReader br new BufferedReader(new InputStreamReader(socket.getInputStream()));while(true) {String readLine null;if ((readLine br.readLine()) ! null) {Log.e(socket, readLine);} else {break;}}} catch (IOException e) {e.printStackTrace();}
// YoukuMsg youkuMsg new YoukuMsg();
// //要返回一下订阅成功
// baseRequest.setHandled(true);
// ServletMsg.VendorMsg(response, youkuMsg.toString());// SubResponse subRespons new SubResponse();
// subRespons.getTest(callbackUrl,MainActivity._UUID);// onUnicastSend(172.16.0.121, 49152);//源IP和源端口
//
// response.setStatus(SC_OK);
// response.addHeader(Server: ,Linux/3.14.29 UPnP/1.0 Cling/2.0);
// response.addHeader(SID: ,uuid:MainActivity._UUID);
// //response.addHeader(Content-Type: ,text/html; charset\utf-8\);
// response.addHeader(TIMEOUT: ,Second-3600);
// response.setContentType(text/html; charset\utf-8\);
//
// baseRequest.setHandled(true);
// ServletMsg.VendorMsg(response, TencentMsg.RenderingControl_desc);
// baseRequest.setHandled(true);
// ServletMsg.VendorMsg(response, );// response.setHeader();}}Log.e(getHeader, ------getHeader分割线end: PlayState ------);}
处理返回的进度