1. 需求

    1. 获取视频流并在内嵌video标签进行播放
    2. 操控监控进行转向、调焦操作
  2. 解决流程

    1. 官方SDK一个赛一个的不靠谱,尤其是mac开发无法加载dll文件,就算可以加载,服务器也加载不了,果断pass
    2. 从onvif入手,写一个全通用的微服务
    3. 不需要依赖,但需要如下几个包

    org.oasis_open.docs
    org.onvif.ver10
    org.onvif.ver20
    org.w3._2004_08.xop.include
    org.w3._2005
    org.xmlsoap.schemas.soap.envelope

    1. 需要写一些实体类ImagingDeviceInitialDeviceMediaDeviceOnvifDevicePtzDeviceSoap,这里就只列一下主要的
    import java.io.IOException;
    import java.net.ConnectException;
    import java.net.InetSocketAddress;
    import java.net.Socket;
    import java.net.SocketAddress;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.text.SimpleDateFormat;
    import java.util.*;
    import javax.xml.soap.SOAPException;
    import com.hjly.tour.common.core.exception.TourBizException;
    import lombok.Cleanup;
    import lombok.Data;
    import com.hjly.tour.edi.onvif.api.org.onvif.ver10.schema.Capabilities;/*** @author Mr. Cui*/
    @Data
    public class OnvifDevice {private final String HOST_IP;private String originalIp;private boolean isProxy;private final String username;private final String password;private String nonce;private String utcTime;private final String serverDeviceUri;private String serverPtzUri;private String serverMediaUri;private String serverImagingUri;private String serverEventsUri;private final SOAP soap;private final InitialDevices initialDevices;private final PtzDevices ptzDevices;private final MediaDevices mediaDevices;private final ImagingDevices imagingDevices;public OnvifDevice(String hostIp, String user, String password) throws ConnectException, SOAPException {this.HOST_IP = hostIp;if (!isOnline()) {throw new ConnectException("Host not available.");}this.serverDeviceUri = "http://" + HOST_IP + "/onvif/device_service";this.username = user;this.password = password;this.soap = new SOAP(this);this.initialDevices = new InitialDevices(this);this.ptzDevices = new PtzDevices(this);this.mediaDevices = new MediaDevices(this);this.imagingDevices = new ImagingDevices(this);init();}public OnvifDevice(String hostIp) throws ConnectException, SOAPException {this(hostIp, null, null);}private boolean isOnline() {String port = HOST_IP.contains(":") ? HOST_IP.substring(HOST_IP.indexOf(':') + 1) : "80";String ip = HOST_IP.contains(":") ? HOST_IP.substring(0, HOST_IP.indexOf(':')) : HOST_IP;try {SocketAddress sockaddr = new InetSocketAddress(ip, new Integer(port));@Cleanup Socket socket = new Socket();socket.connect(sockaddr, 5000);}catch (NumberFormatException | IOException e) {return false;}return true;}protected void init() throws ConnectException, SOAPException {Capabilities capabilities = getInitialDevices().getCapabilities();if (capabilities == null) {throw new TourBizException("Capabilities not reachable.");}String localDeviceUri = capabilities.getDevice().getXAddr();if (localDeviceUri.startsWith("http://")) {originalIp = localDeviceUri.replace("http://", "");originalIp = originalIp.substring(0, originalIp.indexOf('/'));}else {throw new TourBizException("Unknown/Not implemented local procotol!");}if (!originalIp.equals(HOST_IP)) {isProxy = true;}if (capabilities.getMedia() != null && capabilities.getMedia().getXAddr() != null) {serverMediaUri = replaceLocalIpWithProxyIp(capabilities.getMedia().getXAddr());}if (capabilities.getPTZ() != null && capabilities.getPTZ().getXAddr() != null) {serverPtzUri = replaceLocalIpWithProxyIp(capabilities.getPTZ().getXAddr());}if (capabilities.getImaging() != null && capabilities.getImaging().getXAddr() != null) {serverImagingUri = replaceLocalIpWithProxyIp(capabilities.getImaging().getXAddr());}if (capabilities.getMedia() != null && capabilities.getEvents().getXAddr() != null) {serverEventsUri = replaceLocalIpWithProxyIp(capabilities.getEvents().getXAddr());}}public String replaceLocalIpWithProxyIp(String original) {if (original.startsWith("http:///")) {original.replace("http:///", "http://"+HOST_IP);}if (isProxy) {return original.replace(originalIp, HOST_IP);}return original;}public String getUsername() {return username;}public String getEncryptedPassword() {return encryptPassword();}public String encryptPassword() {String nonce = getNonce();String timestamp = getUTCTime();String beforeEncryption = nonce + timestamp + password;byte[] encryptedRaw;try {encryptedRaw = sha1(beforeEncryption);}catch (NoSuchAlgorithmException e) {e.printStackTrace();return null;}return Base64.getEncoder().encodeToString(encryptedRaw);}private static byte[] sha1(String s) throws NoSuchAlgorithmException {MessageDigest SHA1;SHA1 = MessageDigest.getInstance("SHA1");SHA1.reset();SHA1.update(s.getBytes());return SHA1.digest();}private String getNonce() {if (nonce == null) {createNonce();}return nonce;}public String getEncryptedNonce() {if (nonce == null) {createNonce();}return Base64.getEncoder().encodeToString(nonce.getBytes());}public void createNonce() {Random generator = new Random();nonce = "" + generator.nextInt();}public String getUTCTime() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-d'T'HH:mm:ss'Z'");sdf.setTimeZone(new SimpleTimeZone(SimpleTimeZone.UTC_TIME, "UTC"));Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));String utcTime = sdf.format(cal.getTime());this.utcTime = utcTime;return utcTime;}public Date getDate() {return initialDevices.getDate();}public String getName() {return initialDevices.getDeviceInformation().getModel();}
    }
    
    import java.net.ConnectException;
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.JAXBException;
    import javax.xml.bind.Marshaller;
    import javax.xml.bind.UnmarshalException;
    import javax.xml.bind.Unmarshaller;
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.parsers.ParserConfigurationException;
    import javax.xml.soap.MessageFactory;
    import javax.xml.soap.SOAPConnection;
    import javax.xml.soap.SOAPConnectionFactory;
    import javax.xml.soap.SOAPConstants;
    import javax.xml.soap.SOAPElement;
    import javax.xml.soap.SOAPEnvelope;
    import javax.xml.soap.SOAPException;
    import javax.xml.soap.SOAPHeader;
    import javax.xml.soap.SOAPMessage;
    import javax.xml.soap.SOAPPart;import com.hjly.tour.common.core.exception.TourBizException;
    import lombok.Cleanup;
    import lombok.Data;
    import org.w3c.dom.Document;@Data
    public class SOAP {private OnvifDevice onvifDevice;public SOAP(OnvifDevice onvifDevice) {super();this.onvifDevice = onvifDevice;}public Object createSOAPDeviceRequest(Object soapRequestElem, Object soapResponseElem) throws SOAPException,ConnectException {return createSOAPRequest(soapRequestElem, soapResponseElem, onvifDevice.getServerDeviceUri());}public Object createSOAPPtzRequest(Object soapRequestElem, Object soapResponseElem) throws SOAPException, ConnectException {return createSOAPRequest(soapRequestElem, soapResponseElem, onvifDevice.getServerPtzUri());}public Object createSOAPMediaRequest(Object soapRequestElem, Object soapResponseElem) throws SOAPException, ConnectException {return createSOAPRequest(soapRequestElem, soapResponseElem, onvifDevice.getServerMediaUri());}public Object createSOAPImagingRequest(Object soapRequestElem, Object soapResponseElem) throws SOAPException,ConnectException {return createSOAPRequest(soapRequestElem, soapResponseElem, onvifDevice.getServerImagingUri());}public Object createSOAPRequest(Object soapRequestElem, Object soapResponseElem, String soapUri) {try {SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();@Cleanup SOAPConnection soapConnection = soapConnectionFactory.createConnection();SOAPMessage soapMessage = createSoapMessage(soapRequestElem);SOAPMessage soapResponse = soapConnection.call(soapMessage, soapUri);if (soapResponseElem == null) {throw new NullPointerException("Improper SOAP Response Element given (is null).");}Unmarshaller unmarshaller = JAXBContext.newInstance(soapResponseElem.getClass()).createUnmarshaller();try {try {soapResponseElem = unmarshaller.unmarshal(soapResponse.getSOAPBody().extractContentAsDocument());}catch (SOAPException e) {soapResponseElem = unmarshaller.unmarshal(soapResponse.getSOAPBody().extractContentAsDocument());}}catch (UnmarshalException e) {throw new TourBizException("Could not unmarshal, ended in SOAP fault.");}return soapResponseElem;} catch (SOAPException e) {throw new TourBizException("Unexpected response. Response should be from class " + soapResponseElem.getClass());}catch (ParserConfigurationException | JAXBException e) {throw new TourBizException("Unhandled exception: " + e.getMessage());}}protected SOAPMessage createSoapMessage(Object soapRequestElem) throws SOAPException, ParserConfigurationException,JAXBException {MessageFactory messageFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);SOAPMessage soapMessage = messageFactory.createMessage();Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();Marshaller marshaller = JAXBContext.newInstance(soapRequestElem.getClass()).createMarshaller();marshaller.marshal(soapRequestElem, document);soapMessage.getSOAPBody().addDocument(document);// if (needAuthentification)createSoapHeader(soapMessage);soapMessage.saveChanges();return soapMessage;}protected void createSoapHeader(SOAPMessage soapMessage) throws SOAPException {onvifDevice.createNonce();String encrypedPassword = onvifDevice.getEncryptedPassword();if (encrypedPassword != null && onvifDevice.getUsername() != null) {SOAPPart sp = soapMessage.getSOAPPart();SOAPEnvelope se = sp.getEnvelope();SOAPHeader header = soapMessage.getSOAPHeader();se.addNamespaceDeclaration("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");se.addNamespaceDeclaration("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd");SOAPElement securityElem = header.addChildElement("Security", "wsse");// securityElem.setAttribute("SOAP-ENV:mustUnderstand", "1");SOAPElement usernameTokenElem = securityElem.addChildElement("UsernameToken", "wsse");SOAPElement usernameElem = usernameTokenElem.addChildElement("Username", "wsse");usernameElem.setTextContent(onvifDevice.getUsername());SOAPElement passwordElem = usernameTokenElem.addChildElement("Password", "wsse");passwordElem.setAttribute("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest");passwordElem.setTextContent(encrypedPassword);SOAPElement nonceElem = usernameTokenElem.addChildElement("Nonce", "wsse");nonceElem.setAttribute("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");nonceElem.setTextContent(onvifDevice.getEncryptedNonce());SOAPElement createdElem = usernameTokenElem.addChildElement("Created", "wsu");createdElem.setTextContent(onvifDevice.getUTCTime());}}
    }
    
    1. 工具类
    import com.hjly.tour.common.core.exception.TourBizException;
    import com.hjly.tour.edi.onvif.api.dto.GetVSDTO;
    import com.hjly.tour.edi.onvif.api.dto.TurnDTO;
    import com.hjly.tour.edi.onvif.api.entity.MediaDevices;
    import com.hjly.tour.edi.onvif.api.entity.OnvifDevice;
    import com.hjly.tour.edi.onvif.api.entity.PtzDevices;
    import com.hjly.tour.edi.onvif.api.org.onvif.ver10.schema.PTZNode;
    import com.hjly.tour.edi.onvif.api.org.onvif.ver10.schema.PTZVector;import javax.xml.soap.SOAPException;
    import java.net.ConnectException;
    import java.util.HashMap;
    import java.util.Map;/*** @author: Mr. Cui*/public class OnvifUtil {public static final Map<String,PTZNode> ptzNodeMap = new HashMap<>();public static final Map<String,PtzDevices> ptzDevicesMap = new HashMap<>();public static final Map<String,String> profileTokenMap = new HashMap<>();public static final Map<String,OnvifDevice> deviceMap = new HashMap<>();public static void connect(String ip, String user, String password) {try {if(deviceMap.get(ip) != null) return;final OnvifDevice onvifDevice = new OnvifDevice(ip, user, password);deviceMap.put(ip,onvifDevice);String profileToken = onvifDevice.getInitialDevices().getProfiles().get(0).getToken();PtzDevices ptzDevices = new PtzDevices(onvifDevice);ptzNodeMap.put(ip, ptzDevices.getNode(profileToken));ptzDevicesMap.put(ip , ptzDevices);profileTokenMap.put(ip , profileToken);} catch (ConnectException | SOAPException e) {e.printStackTrace();}}public static boolean turn(TurnDTO turnDTO) {String ip = turnDTO.getTerminal();connect(ip,turnDTO.getUsername(),turnDTO.getPassword());PtzDevices ptzDevices = ptzDevicesMap.get(ip);PTZVector position = ptzDevices.getStatus(profileTokenMap.get(ip)).getPosition();turnDTO.setX(turnDTO.getX()+position.getPanTilt().getX());turnDTO.setY(turnDTO.getY()+position.getPanTilt().getY());turnDTO.setZoom(turnDTO.getZoom()+position.getZoom().getX());try {return deviceMap.get(ip).getPtzDevices().absoluteMove(ptzNodeMap.get(ip), profileTokenMap.get(ip), turnDTO.getX(), turnDTO.getY(), turnDTO.getZoom());} catch (SOAPException e) {e.printStackTrace();return false;}}public static String getVS(GetVSDTO getVSDTO){String ip = getVSDTO.getTerminal();connect(ip,getVSDTO.getUsername(),getVSDTO.getPassword());final MediaDevices mediaDevices = deviceMap.get(ip).getMediaDevices();try {return mediaDevices.getHTTPStreamUri(profileTokenMap.get(ip));} catch (ConnectException | SOAPException e) {e.printStackTrace();throw new TourBizException("获取视频流失败");}}public static String getSnapshot(GetVSDTO getVSDTO){String ip = getVSDTO.getTerminal();final MediaDevices mediaDevices = deviceMap.get(ip).getMediaDevices();try {return mediaDevices.getSnapshotUri(profileTokenMap.get(ip));} catch (ConnectException | SOAPException e) {e.printStackTrace();throw new TourBizException("获取视频截图失败");}}
    }
    
    1. 截止到现在,已经可以控制视频的转向、焦距,以及获取rtsp的视频流
  3. 追加解决问题

    1. 问题来源:前端说rtsp视频流无法在video标签进行播放,必须用插件,太麻烦,需要我解析成rtmp
    2. rtmp可以通过ffmpeg进行推流转换,需要nginx支持rtmp直播流,很遗憾,我弄了一下午也没弄出来,服务器是centos,无法下载相应的环境

    当然如果有朋友在centos下的nginx配置了rtmp视频直播流,请告诉我,让我好好学习一下,万分感谢

    1. 转换成rtmp被pass后,考虑在前端获取视频流的时候,将视频分片截取放在服务器中,定时的清除缓存文件,在关闭的时候进行请求,结束视频的截取,删除对应的所有文件
    2. 先出ffmpeg命令,并在服务器上进行运行,可以成功,并且与前端沟通可以进行实时访问
    ffmpeg -rtsp_transport tcp -i 'rtsp://admin:1234566@218.28.112.3:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif' -fflags flush_packets -max_delay 1 -an -flags -global_header -hls_time 1 -hls_list_size 3 -hls_wrap 3 -vcodec copy -s 216x384 -b 1024k -y '/usr/local/nginx/document/my.m3u8'
    
    1. 命令成功接下来就是Java代码
    import cn.hutool.core.thread.ThreadUtil;
    import cn.hutool.core.util.IdUtil;
    import cn.hutool.core.util.RuntimeUtil;
    import cn.hutool.core.util.StrUtil;
    import com.hjly.tour.edi.onvif.api.dto.GetVSDTO;
    import com.hjly.tour.edi.onvif.api.dto.TurnDTO;
    import com.hjly.tour.edi.onvif.api.util.OnvifUtil;
    import com.hjly.tour.edi.onvif.biz.service.IOnvifService;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.scheduling.annotation.EnableScheduling;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Service;/*** @author: Mr. Cui**/
    @Service
    @RequiredArgsConstructor
    @EnableScheduling
    @Slf4j
    public class OnvifServiceImpl implements IOnvifService {@Value("${document.dir}")private String documentDir;@Value("${document.url-prefix}")private String documentUrlPrefix;@Overridepublic Boolean turn(TurnDTO turnDTO) {return OnvifUtil.turn(turnDTO);}@Scheduled(cron = "${task.clean-ts.cron}")public void cleanTs(){log.info("定时删除.ts文件");RuntimeUtil.execForStr("rm -rf " + documentDir+"*.ts");}@Overridepublic String getVS(GetVSDTO getVSDTO) {String rtsp = OnvifUtil.getVS(getVSDTO);rtsp = StrUtil.replace(rtsp, "rtsp://", "rtsp://" + getVSDTO.getUsername() + ":" + getVSDTO.getPassword() + "@");log.info("rtsp {} ", rtsp);final String filePrefix = IdUtil.simpleUUID();final String m3u8FileName = filePrefix + ".m3u8";StringBuilder command = StrUtil.builder();command.append("ffmpeg -rtsp_transport tcp -i '");command.append(rtsp);command.append("' -fflags flush_packets -max_delay 1 -an -flags -global_header -hls_time 1 -hls_list_size 3 -hls_wrap 3 -vcodec copy -s 216x384 -b 1024k -y '");command.append(documentDir).append(m3u8FileName);command.append("'");ThreadUtil.execute(() -> {try {String[] cmd = new String[]{"sh", "-c", command.toString()};Process ffmpeg = Runtime.getRuntime().exec(cmd);int exitValue = ffmpeg.waitFor();if (0 != exitValue)System.err.println("转换视频流失败");} catch (Throwable e) {System.err.println("转换视频流失败");}});log.info("command {} ", command);return documentUrlPrefix + m3u8FileName;}public Boolean closeVS(String m3u8Url) {m3u8Url = m3u8Url.replace(documentUrlPrefix, "");try {String[] cmd = new String[]{"sh", "-c", "ps -ef | grep ffmpeg | grep " + m3u8Url + " | grep -v grep | awk '{print $2}' | xargs kill -9"};Process killFfmpeg = Runtime.getRuntime().exec(cmd);int exitValue = killFfmpeg.waitFor();if (0 != exitValue)log.info("杀死ffmpeg失败");} catch (Throwable e) {log.info("杀死ffmpeg失败");}RuntimeUtil.execForStr("rm -rf " + documentDir + m3u8Url);log.info("删除文件结束");return Boolean.TRUE;}
    }
    
  4. 问题解决,对接结束

记录一次Java对接监控(大华、海康、onvif)相关推荐

  1. java GB28181 大华 海康摄像机国标对接源码源代码程序

    java GB28181 大华 海康摄像机国标对接源码源代码程序 本人亲测说明:首先此套程序我测试了可以用,但是不能同时多客户同时看一路视频,视频打开速度也很慢.仅拿来参考是可以的,还凑合吧. WEB ...

  2. 传统大华海康宇视安防摄像头RTSP流如何转webrtc直播低延时无插件浏览器视频播放

    传统大华海康宇视安防摄像头RTSP流如何转webrtc直播低延时无插件浏览器视频播放 1.问题场景 2.WEBRTC延时对比 3.LiveNVR支持WEBRTC输出 4.RTSP/HLS/FLV/RT ...

  3. 大华海康摄像头人家自己是怎么在web上播放视频的

    最近处理安防视频,怎么把摄像头视频在web上展示费了很大功夫,当然这一篇不是讲解我是怎么显示的,而是回答当时领导问我的一个问题,人家大华自己是怎么显示的? 我们知道大华海康大部分摄像头只对外提供rts ...

  4. 大华海康摄像头视频拉流

    流程 技术 海康&大华&DSS获取RTSP 实时流 海康: rtsp://[username]:[password]@[ip]:[port]/[codec]/[channel]/[su ...

  5. 【直播、摄像头】 Http 流媒体服务器 对接摄像机(大华海康)使用 nginx 和 ffmpeg 对rtsp进行转码获取

    http搭建流媒体推送平台 简述 通过http来搭建流媒体的推流平台(对接摄像头使用,通过获取摄像头的rtsp流通过ffmpeg进行转码推送到 nginx 转成 rtmp 最后将 rtmp 转换成 f ...

  6. 学习笔记(16):C++编程FFMpeg(QT5+OpenCV)实战--实时美颜直播推流-推流基于rtsp协议的大华海康相机...

    立即学习:https://edu.csdn.net/course/play/5694/106864?utm_source=blogtoedu rtsp 设置

  7. 大华sdk对接php,大华网络摄像机SDK对接

    1)演示程序\Mfc分类Demo中有04.远程抓图19.枪球联动等功能说明 关于视音频的数据格式有两种 // 编码格式, 包括音频和视频 struct AV_CFG_EncodeFormat { AV ...

  8. (全剧终)C/C++ 与 嵌入式软件开发招聘记录(华为、中兴、联发科、海康、大华、oppo、vivo、地平线、科大讯飞、广联达、绿盟、CVTE、诺瓦等)

    从2020年11月到2021年11月,用了一年时间,秋招终于结束了,因为平台.岗位.薪资和地域等各方面原因,最终签约信息如下: 华为 base:西安 部门:消费者BG 岗位:通用软件开发岗 级别:15 ...

  9. 浙江计算机机房监控,大华机房动环监控解决方案

    大华机房动环监控解决方案 方案背景 机房的动力环境如何能够满足苛刻机房建设和运维要求?居高不下的运维费用究竟是否用到了必要的地方?如此大量的机房,如何能够做到最少的人员,最高的运维效率? 机房建设和维 ...

最新文章

  1. 年中盘点:2021年炙手可热的10家数据科学和机器学习初创公司
  2. 关于案例教学大家都有些什么看法呢?
  3. 3.6-fdisk命令
  4. python如何读取excel的一个sheet_python pandas是如何读取excel表中的sheet的(四)
  5. node创建web服务器代码示例
  6. linux 逻辑卷扩展
  7. ArrayBlockingQueue原理分析-take方法
  8. JavaScript-操作DOM对象-更新dom节点
  9. 求逆矩阵计算器_991CN的矩阵运算
  10. mysql 查看锁等待
  11. mysql route mycat_mycat
  12. 08TensorFlow2.0基础--8.6tensoflow-gpu和cpu
  13. DP-代理模式(Proxy Pattern)
  14. 虚拟机(VMware Workstation)的使用方法(转)
  15. reset.css下载
  16. 长度游程编码的JAVA源代码_游程编码(Run Length Code)
  17. 2020-2022年最全湖南省矢量数据(路网含城市道路、铁路(包括地铁、轻轨)高速、国道、省道、县道、乡道+几百万个poi数据+省市县乡镇行政区划+河流水系网+建筑轮廓+30米dem等shp数据
  18. 3乘3魔方第四步_【三阶魔方 - 初学】LBL第四步:顶层朝向
  19. 厚着脸皮求领导写了一篇java小白进阶大牛之路!!!
  20. erp系统软件的三层定义

热门文章

  1. 关于打的 umd 包在使用时,报 require is not defined 错误的问题出处
  2. mysql front和navicat_NavicatforMySQL与MySQL-Front比较[图文]
  3. 未定义的标识符:IplImage
  4. SpringBoot yml文件数据读取
  5. 软件测试测试用例执行多少条,软件测试用例执行中有效的策略
  6. 复数抽象数据类型及其四则运算 (c++)
  7. 软考的网络工程师对就业有用吗?
  8. XP64G 补丁安装实录
  9. 淘宝天猫抓包评论地址
  10. OpenCV视频剪切