前言:

很久没有打理博客了。最近有点烦,teamviewer用的挺习惯的,突然不香了。虽然改用mstsc+加自己云服务器的方式也还行。但突然就萌发了为啥不自己试着写一个的想法。刚好项目空档几天,于是便有了该项目。至于为什么叫Weekday?大概因为热爱工作的缘故吧。由于只是写着玩的,所以没有大量的去做压力测试。基本功能还可以,代码风格符合本人习惯。技术涉及抓屏截图,图像处理,视频编码,网络推流,视频解码,远程控制等,学习价值大于实用价值。
看以前的博客下面有朋友留言讨论,很抱歉没有回复,因为确实没有经常关注博客,有兴趣的朋友可以加微信好友讨论技术或索要源码学习。

一、功能说明和测试

Weekday目前只支持局域网下的两台windows电脑进行远程协助(当然也可以自己用公网服务器进行双向代理实现外网远控,不懂的也可以咨询本人,这不是本文重点)。后续可以扩展一些办公的小功能,截图、录屏等等。有兴趣的朋友也可以自己扩展添加。

编译好的Weekday软件链接,感兴趣的可以免费下载测试:
https://download.csdn.net/download/u013752202/21463939

下面是release后的测试截图:
需要源码或编译好的ffmpeg+libx264动态库的可以扫描下面测试界面的二维码加微信,留言可能不能及时回复,请见谅。

  1. server启动后等待连接
  2. 客户端连接
    3. client端显示server的桌面
    本地控制本地,当client和server位于同一台电脑的时候会出现“套娃”现象,这是正常的。client和server不在同一台电脑不会出现。

    client和server不在同一台电脑,没有“套娃”现象。图中红点表示鼠标。可以点击操控。

二、技术原理

1. 通信流程
简单的通信流程大致如下:

#mermaid-svg-MG3Tc4mnLK12em8Z .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .label text{fill:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .node rect,#mermaid-svg-MG3Tc4mnLK12em8Z .node circle,#mermaid-svg-MG3Tc4mnLK12em8Z .node ellipse,#mermaid-svg-MG3Tc4mnLK12em8Z .node polygon,#mermaid-svg-MG3Tc4mnLK12em8Z .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-MG3Tc4mnLK12em8Z .node .label{text-align:center;fill:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .node.clickable{cursor:pointer}#mermaid-svg-MG3Tc4mnLK12em8Z .arrowheadPath{fill:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-MG3Tc4mnLK12em8Z .flowchart-link{stroke:#333;fill:none}#mermaid-svg-MG3Tc4mnLK12em8Z .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-MG3Tc4mnLK12em8Z .edgeLabel rect{opacity:0.9}#mermaid-svg-MG3Tc4mnLK12em8Z .edgeLabel span{color:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-MG3Tc4mnLK12em8Z .cluster text{fill:#333}#mermaid-svg-MG3Tc4mnLK12em8Z div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-MG3Tc4mnLK12em8Z .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-MG3Tc4mnLK12em8Z text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-MG3Tc4mnLK12em8Z .actor-line{stroke:grey}#mermaid-svg-MG3Tc4mnLK12em8Z .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-MG3Tc4mnLK12em8Z #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .sequenceNumber{fill:#fff}#mermaid-svg-MG3Tc4mnLK12em8Z #sequencenumber{fill:#333}#mermaid-svg-MG3Tc4mnLK12em8Z #crosshead path{fill:#333;stroke:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .messageText{fill:#333;stroke:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-MG3Tc4mnLK12em8Z .labelText,#mermaid-svg-MG3Tc4mnLK12em8Z .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-MG3Tc4mnLK12em8Z .loopText,#mermaid-svg-MG3Tc4mnLK12em8Z .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-MG3Tc4mnLK12em8Z .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-MG3Tc4mnLK12em8Z .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-MG3Tc4mnLK12em8Z .noteText,#mermaid-svg-MG3Tc4mnLK12em8Z .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-MG3Tc4mnLK12em8Z .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-MG3Tc4mnLK12em8Z .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-MG3Tc4mnLK12em8Z .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-MG3Tc4mnLK12em8Z .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-MG3Tc4mnLK12em8Z .section{stroke:none;opacity:0.2}#mermaid-svg-MG3Tc4mnLK12em8Z .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-MG3Tc4mnLK12em8Z .section2{fill:#fff400}#mermaid-svg-MG3Tc4mnLK12em8Z .section1,#mermaid-svg-MG3Tc4mnLK12em8Z .section3{fill:#fff;opacity:0.2}#mermaid-svg-MG3Tc4mnLK12em8Z .sectionTitle0{fill:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .sectionTitle1{fill:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .sectionTitle2{fill:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .sectionTitle3{fill:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-MG3Tc4mnLK12em8Z .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-MG3Tc4mnLK12em8Z .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-MG3Tc4mnLK12em8Z .grid path{stroke-width:0}#mermaid-svg-MG3Tc4mnLK12em8Z .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-MG3Tc4mnLK12em8Z .task{stroke-width:2}#mermaid-svg-MG3Tc4mnLK12em8Z .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-MG3Tc4mnLK12em8Z .taskText:not([font-size]){font-size:11px}#mermaid-svg-MG3Tc4mnLK12em8Z .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-MG3Tc4mnLK12em8Z .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-MG3Tc4mnLK12em8Z .task.clickable{cursor:pointer}#mermaid-svg-MG3Tc4mnLK12em8Z .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-MG3Tc4mnLK12em8Z .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-MG3Tc4mnLK12em8Z .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-MG3Tc4mnLK12em8Z .taskText0,#mermaid-svg-MG3Tc4mnLK12em8Z .taskText1,#mermaid-svg-MG3Tc4mnLK12em8Z .taskText2,#mermaid-svg-MG3Tc4mnLK12em8Z .taskText3{fill:#fff}#mermaid-svg-MG3Tc4mnLK12em8Z .task0,#mermaid-svg-MG3Tc4mnLK12em8Z .task1,#mermaid-svg-MG3Tc4mnLK12em8Z .task2,#mermaid-svg-MG3Tc4mnLK12em8Z .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-MG3Tc4mnLK12em8Z .taskTextOutside0,#mermaid-svg-MG3Tc4mnLK12em8Z .taskTextOutside2{fill:#000}#mermaid-svg-MG3Tc4mnLK12em8Z .taskTextOutside1,#mermaid-svg-MG3Tc4mnLK12em8Z .taskTextOutside3{fill:#000}#mermaid-svg-MG3Tc4mnLK12em8Z .active0,#mermaid-svg-MG3Tc4mnLK12em8Z .active1,#mermaid-svg-MG3Tc4mnLK12em8Z .active2,#mermaid-svg-MG3Tc4mnLK12em8Z .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-MG3Tc4mnLK12em8Z .activeText0,#mermaid-svg-MG3Tc4mnLK12em8Z .activeText1,#mermaid-svg-MG3Tc4mnLK12em8Z .activeText2,#mermaid-svg-MG3Tc4mnLK12em8Z .activeText3{fill:#000 !important}#mermaid-svg-MG3Tc4mnLK12em8Z .done0,#mermaid-svg-MG3Tc4mnLK12em8Z .done1,#mermaid-svg-MG3Tc4mnLK12em8Z .done2,#mermaid-svg-MG3Tc4mnLK12em8Z .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-MG3Tc4mnLK12em8Z .doneText0,#mermaid-svg-MG3Tc4mnLK12em8Z .doneText1,#mermaid-svg-MG3Tc4mnLK12em8Z .doneText2,#mermaid-svg-MG3Tc4mnLK12em8Z .doneText3{fill:#000 !important}#mermaid-svg-MG3Tc4mnLK12em8Z .crit0,#mermaid-svg-MG3Tc4mnLK12em8Z .crit1,#mermaid-svg-MG3Tc4mnLK12em8Z .crit2,#mermaid-svg-MG3Tc4mnLK12em8Z .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-MG3Tc4mnLK12em8Z .activeCrit0,#mermaid-svg-MG3Tc4mnLK12em8Z .activeCrit1,#mermaid-svg-MG3Tc4mnLK12em8Z .activeCrit2,#mermaid-svg-MG3Tc4mnLK12em8Z .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-MG3Tc4mnLK12em8Z .doneCrit0,#mermaid-svg-MG3Tc4mnLK12em8Z .doneCrit1,#mermaid-svg-MG3Tc4mnLK12em8Z .doneCrit2,#mermaid-svg-MG3Tc4mnLK12em8Z .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-MG3Tc4mnLK12em8Z .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-MG3Tc4mnLK12em8Z .milestoneText{font-style:italic}#mermaid-svg-MG3Tc4mnLK12em8Z .doneCritText0,#mermaid-svg-MG3Tc4mnLK12em8Z .doneCritText1,#mermaid-svg-MG3Tc4mnLK12em8Z .doneCritText2,#mermaid-svg-MG3Tc4mnLK12em8Z .doneCritText3{fill:#000 !important}#mermaid-svg-MG3Tc4mnLK12em8Z .activeCritText0,#mermaid-svg-MG3Tc4mnLK12em8Z .activeCritText1,#mermaid-svg-MG3Tc4mnLK12em8Z .activeCritText2,#mermaid-svg-MG3Tc4mnLK12em8Z .activeCritText3{fill:#000 !important}#mermaid-svg-MG3Tc4mnLK12em8Z .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-MG3Tc4mnLK12em8Z g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-MG3Tc4mnLK12em8Z g.classGroup text .title{font-weight:bolder}#mermaid-svg-MG3Tc4mnLK12em8Z g.clickable{cursor:pointer}#mermaid-svg-MG3Tc4mnLK12em8Z g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-MG3Tc4mnLK12em8Z g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-MG3Tc4mnLK12em8Z .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-MG3Tc4mnLK12em8Z .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-MG3Tc4mnLK12em8Z .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-MG3Tc4mnLK12em8Z .dashed-line{stroke-dasharray:3}#mermaid-svg-MG3Tc4mnLK12em8Z #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-MG3Tc4mnLK12em8Z #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-MG3Tc4mnLK12em8Z #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-MG3Tc4mnLK12em8Z #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-MG3Tc4mnLK12em8Z #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-MG3Tc4mnLK12em8Z #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-MG3Tc4mnLK12em8Z #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-MG3Tc4mnLK12em8Z #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-MG3Tc4mnLK12em8Z .commit-id,#mermaid-svg-MG3Tc4mnLK12em8Z .commit-msg,#mermaid-svg-MG3Tc4mnLK12em8Z .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-MG3Tc4mnLK12em8Z .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-MG3Tc4mnLK12em8Z .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-MG3Tc4mnLK12em8Z g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-MG3Tc4mnLK12em8Z g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-MG3Tc4mnLK12em8Z g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-MG3Tc4mnLK12em8Z g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-MG3Tc4mnLK12em8Z g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-MG3Tc4mnLK12em8Z g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-MG3Tc4mnLK12em8Z .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-MG3Tc4mnLK12em8Z .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-MG3Tc4mnLK12em8Z .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-MG3Tc4mnLK12em8Z .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-MG3Tc4mnLK12em8Z .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-MG3Tc4mnLK12em8Z .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-MG3Tc4mnLK12em8Z .edgeLabel text{fill:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-MG3Tc4mnLK12em8Z .node circle.state-start{fill:black;stroke:black}#mermaid-svg-MG3Tc4mnLK12em8Z .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-MG3Tc4mnLK12em8Z #statediagram-barbEnd{fill:#9370db}#mermaid-svg-MG3Tc4mnLK12em8Z .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-MG3Tc4mnLK12em8Z .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-MG3Tc4mnLK12em8Z .statediagram-state .divider{stroke:#9370db}#mermaid-svg-MG3Tc4mnLK12em8Z .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-MG3Tc4mnLK12em8Z .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-MG3Tc4mnLK12em8Z .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-MG3Tc4mnLK12em8Z .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-MG3Tc4mnLK12em8Z .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-MG3Tc4mnLK12em8Z .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-MG3Tc4mnLK12em8Z .note-edge{stroke-dasharray:5}#mermaid-svg-MG3Tc4mnLK12em8Z .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-MG3Tc4mnLK12em8Z .error-icon{fill:#522}#mermaid-svg-MG3Tc4mnLK12em8Z .error-text{fill:#522;stroke:#522}#mermaid-svg-MG3Tc4mnLK12em8Z .edge-thickness-normal{stroke-width:2px}#mermaid-svg-MG3Tc4mnLK12em8Z .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-MG3Tc4mnLK12em8Z .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-MG3Tc4mnLK12em8Z .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-MG3Tc4mnLK12em8Z .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-MG3Tc4mnLK12em8Z .marker{fill:#333}#mermaid-svg-MG3Tc4mnLK12em8Z .marker.cross{stroke:#333}:root { --mermaid-font-family: "trebuchet ms", verdana, arial;}#mermaid-svg-MG3Tc4mnLK12em8Z {color: rgba(0, 0, 0, 0.75);font: ;}clientserver连接请求1. 抓屏 2. 编码屏幕码流解码显示控制数据应用控制数据clientserver

2. 抓屏截图
为了简单,直接使用Qt的抓屏api实现,所以帧率最快只能达到20-25帧。有志之士可以自己从网卡旁路。以 达到更高的帧率。
由于抓屏比较耗时,所以抓屏代码在WindowSource线程中运行。抓屏之后再跟进客户端需要进行缩放处理。对比了Qt的scaled和opencv的双线性差值,Qt的效率高三四倍,效果也是一样的。
抓屏代码:

void WindowSource::slotWork()
{while(!stopCmd){QPixmap vQPixmap=pQScreen->grabWindow(QApplication::desktop()->winId(),0,0,1920,1080);srcW=vQPixmap.width();srcH=vQPixmap.height();vQPixmap=vQPixmap.scaled(dstW,dstH,Qt::KeepAspectRatio);QImage vQImage=vQPixmap.toImage().convertToFormat(QImage::Format_RGB888,Qt::NoAlpha);vQImageVec.append(vQImage);}runFlag=0;
}

3. 编码压缩
由于网络带宽资源宝贵,获取到截图后,需要对截图进行编码,减少传输数据量。编码压缩也是十分耗时的操作,所以跟UI和抓屏都是分开线程操作。工程中应用ffmpeg+libx264进行编码。并对其进行了封装。
下面是获取屏幕截图,并进行编码:

    while(WeekdayPCState_Working==vWeekdayPCState){if(0==pWindowSource->vQImageVec.size()){QThread::usleep(20000);continue;}QImage vQImage=pWindowSource->vQImageVec[0];windowW=pWindowSource->srcW;windowH=pWindowSource->srcH;vTimeStamps.fmtChange1=TimeScaleGetMsec();WImageRGB242YUV420P((U8 *)vQImage.constBits(),imgw,imgh,windowNv12);streamBuf=NULL;vTimeStamps.encode=TimeScaleGetMsec();err=WFF_EncoderStreamGet(pEncoder,windowNv12,&streamBuf,&streamSz);if(err&&-11!=err){error(WeekdayPCError_Encoder);break;}if(streamBuf&&streamSz>0){//send to clientvWKDFrame.seq++;vWKDFrame.window.mouseX=QCursor::pos().x();vWKDFrame.window.mouseY=QCursor::pos().y();vWKDFrame.window.width=pWindowSource->srcW;vWKDFrame.window.height=pWindowSource->srcH;vWKDFrame.window.pStreamBuf=streamBuf;vWKDFrame.window.streamSz=streamSz;sendWindowFrame();WFF_EncoderStreamRelease(pEncoder);//QThread::usleep(50000);}pWindowSource->vQImageVec.remove(0);}

4. 通信协议
为了简单,这里采用基于TCP的私有协议。
协议格式如下:

#pragma pack(1)
//**********S->C
typedef struct{U32 byteRate;
}Statis;typedef struct{U16 mouseX;U16 mouseY;U16 width;U16 height;U32 streamSz;union{U8 streamBuf[0];U8 *pStreamBuf;};
}WKDWindow;typedef struct{U32 len;U32 seq;WKDFrameType_t type;Statis statis;WKDWindow window;
}WKDFrame;//**********C->S
typedef struct{U8 pressed;U8 val;
}WKDLKey;typedef struct{U16 x;U16 y;MouseAct_t act;
}WKDMouse;typedef struct{U16 delta;
}WKDWheel;typedef struct{U32 len;U32 seq;WKDCmdType_t type;U16 scaledW;U16 scaledH;U32 byteRate;WKDLKey key;WKDMouse mouse;WKDWheel wheel;
}WKDCmd;#pragma pack()

5. 解码显示
客户端接收到码流后,需要对码流进行解码然后显示到界面上。解码显示依然使用封装的ffmpeg接口进行。

            U8 naluType=pWKDFrame->window.streamBuf[4]&0x1f;if(7==naluType||8==naluType||5==naluType){spsPpsOk=1;}serverWindowWidth=pWKDFrame->window.width;serverWindowHeight=pWKDFrame->window.height;vWeekdayStatis.byteRate=pWKDFrame->statis.byteRate;vWKDCThreadCallback.statisComming(vWKDCThreadCallback.arg,vWeekdayStatis);if(spsPpsOk){if((err=WFF_DecoderDecode(pWFF_Decoder,pWKDFrame->window.streamBuf,pWKDFrame->window.streamSz))<0){error(WKD_ERR_Decoder);emit signalError((int)WKD_ERR_Decoder);}}dataFromServer=dataFromServer.mid(pWKDFrame->len);}

显示则直接使用Qt的painter进行绘制,没有使用opengl 2d,实际测试效率也还行。

void WeekdayView::paintEvent(QPaintEvent *ev)
{QPainter painter(this);mutex.lock();painter.drawImage(QPoint(0,0),image);painter.drawText(QRect(0,this->height()-20,this->width(),this->height()),text);QBrush brush(QColor(255,0,0));painter.setBrush(brush);painter.drawEllipse(QPoint(mouseX,mouseY),5,5);mutex.unlock();
}

6. 远程控制
最后就是远控了,client端重载鼠标键盘事件并按照协议发给server,server端根据协议解析出事件并应用即可。

    vWKDCmdType=WeekdayProtocol::WKDCmdGetPacType((U8 *)buf,len);pWKDCmd=(WKDCmd *)buf;if(WKDCmdType_START==vWKDCmdType){pWeekdayPC->grabWindowStart(pWKDCmd);}else if(WKDCmdType_STOP==vWKDCmdType){pWeekdayPC->grabWindowStop();}else if(WKDCmdType_Ctl==vWKDCmdType){devx=SYS_DEV_POS(pWKDCmd->mouse.x,pWeekdayPC->windowW);devy=SYS_DEV_POS(pWKDCmd->mouse.y,pWeekdayPC->windowH);SysDevice::mouseMoveTo(devx,devy);if(MouseAct_LeftDown==pWKDCmd->mouse.act){SysDevice::mouseLeftDown(devx,devy);}else if(MouseAct_LeftUp==pWKDCmd->mouse.act){SysDevice::mouseLeftUp(devx,devy);}else if(MouseAct_LeftDoubleClick==pWKDCmd->mouse.act){SysDevice::mouseLeftDoubleClick(devx,devy);}else if(MouseAct_RightDown==pWKDCmd->mouse.act){SysDevice::mouseRightDown(devx,devy);}else if(MouseAct_RightUp==pWKDCmd->mouse.act){SysDevice::mouseRightUp(devx,devy);}else if(MouseAct_RightDoubleClick==pWKDCmd->mouse.act){SysDevice::mouseRightDoubleClick(devx,devy);}else if(MouseAct_MiddleDown==pWKDCmd->mouse.act){SysDevice::mouseMiddleDown(devx,devy);}else if(MouseAct_MiddleUp==pWKDCmd->mouse.act){SysDevice::mouseMiddleUp(devx,devy);}if(pWKDCmd->key.pressed){SysDevice::keyPressed(pWKDCmd->key.val);}else{SysDevice::keyReleased(pWKDCmd->key.val);}}

三、技术展望

本工程为爱好、学习使用,很多地方实现的比较粗糙。大致需要优化的一些点为:

1. 抓拍截图

最好直接从网卡进行旁路,一方面可以达到更高帧率,另一方面可以获取到登录界面。

2. 编码压缩

可以对编码压缩的参数进行调优,以获得带宽更稳定的码流。或更换为H265编码,同清晰度下,降低网络传输压力。

3. 网络推流

使用UDP分片传输,在网络带宽不够的情况下,降低视频延时。

4. 解码显示

通过opengl 2d纹理进行绘制,降低CPU使用率。

四、功能扩展

待实现:

1. 本地或远程录屏

对编码后的码流进行本地和远程封装成MP4或flv等格式。增加录屏功能。

2. 本地和远程剪切板访问和同步

监听剪切板状态,实现本地和远程共享剪切板。方便复制和粘贴。

3. 关于非局域网应用

如果需要外网远程控制,则需要自己的公网服务器。通过代理工具双向代理server和client的端口即可实现外网 远程控制,至于P2P内网穿透,现在路由大部分都是对称NAT,基本上不用想了。工程中数据没有加密,如果外网远 控,建议使用openssl进行数据加密后进行测试。

五、项目依赖

项目依赖ffmpeg和libx264,需要自行下载或编译动态库。

编译好的动态库(也可加微信免费提供):

  • ffmpeg https://download.csdn.net/download/u013752202/21455589

  • libx264 https://download.csdn.net/download/u013752202/21411867

Qt+ffmpeg+x264远程协助软件Weekday技术原理及源码剖析相关推荐

  1. 一文搞懂主流的扫码登录技术原理(附源码)

    点击上方[全栈开发者社区]→右上角[...]→[设为星标⭐] 1.引言 扫码登录这个功能,最早应该是微信的PC端开始搞,虽然有点反人类的功能(不扫码也没别的方式登录),但不得不说还是很酷的. 下面这张 ...

  2. JVM CPU Profiler技术原理及源码深度解析

    本文介绍了JVM平台上CPU Profiler的实现原理,希望能帮助读者在使用类似工具的同时也能清楚其内部的技术实现. 引言 研发人员在遇到线上报警或需要优化系统性能时,常常需要分析程序运行行为和性能 ...

  3. Z-Wave技术与zipgateway源码剖析

    1 Z-Wave简介 1.1 什么是Z-Wave技术 Z-Wave是一种新兴的基于射频的.低成本.低功耗.高可靠.适于网络的短距离无线通信技术.工作频带为908.42MHz(美国)~868.42MHz ...

  4. 老狼远程协助软件编写教程共62节全(作为作者,应该是最晚发布的了)

    老狼远程协助软件编写教程 本教程将深入剖析Gh0st内核,深入了解远程管理类软件编写的全过程,老狼将带领学员在参考Gh0st源码的基础上,纯手工打造一个新的远程协助软件,自从Gh0st开源后,各种改改 ...

  5. 远程协助软件开发_这是我从事远程软件开发人员工作的主要技巧

    远程协助软件开发 by Colin Morgan 通过科林·摩根(Colin Morgan) 这是我从事远程软件开发人员工作的主要技巧 (Here are the top tips I've used ...

  6. 仿TeamViewer远程协助软件

    仿TeamViewer远程协助软件 远程协助软件三个版本(TCP版.UDP商业版.网页控制版(websocket版)) 主要技术:java  udp  tcp   websocket   webRTC ...

  7. 9款免费的Windows远程协助软件

    首先,如果网友和你在同一个局域网中你可以使用的就是Windows 的远程桌面功能.如果你不在局域网中,并且只有一个Internet 连接.你可以通过其他的远程协助软件从互联网中得到网友的帮助.这里我们 ...

  8. 远程协助软件推荐,有哪些远程协助工具?

    Win10.11自带远程协助工具-快速助手 Win10.11的快速助手使用非常简单.只要在左下角的搜索框搜索一下就可以找到了. 我们都知道,Windows带有远程桌面RDP功能,而快速助手是通过Win ...

  9. ToDesk(远程协助软件)官方中文版V2.1.5 | todesk远程软件下载

    ToDesk 是一款短小精悍神似TeamViewer且完全免费的新生代远程协助软件,是一家做游戏加速器的公司由于疫情期间对无休止无节操的TeamViewer商业行为提示忍无可忍索性就自己开发了tode ...

最新文章

  1. MAC和windows开发操作系统环境,解决Maven工程中报 Missing artifact jdk.tools:jdk.tools
  2. SAP公司间STO流程里外向交货单PGI后自动触发内向交货单的实现
  3. 【前沿技术】2021年AI将改变制造业的6大应用趋势
  4. 【MOSS】Sharepoint大附件上传
  5. 解析ThreadPoolExecutor类是如何保证线程池正确运行的
  6. 无人机在高楼区做倾斜摄影的地籍建模的项目报告
  7. hadoop2.7.1安装初上手
  8. python统计时间的次数的代码_python脚本实现统计日志文件中的ip访问次数代码分享...
  9. 编译器的差别gcc和VS
  10. 线性代数01:函数对向量、矩阵的梯度(向量、矩阵求导)
  11. 录入姓名完成座位表,学习前端的小伙伴可以关注一波,用js+html+css构成
  12. windows清理_一个Windows系统下好用的内存清理工具
  13. android手机图标 足球球星,C罗梅西最抢眼 足球巨星们都用什么手机
  14. Kitkat中对class core, class main, class late_start的简单分析
  15. 【开发利器】中国国内可用API合集
  16. 基于Acgis从全球.nc数据中提取中国地图并计算地区CO2值
  17. ROS远程连接Turtlebot3并进行简单的移动控制
  18. mysql workbench怎么_MySQL Workbench怎么用?MySQL Workbench详细使用教程
  19. ContentPlaceHolder
  20. 将exe和dll文件打包成单一的启动文件

热门文章

  1. oracle random io,Oracle ORION IO 测试工具
  2. 残差连接(skip connect)/(residual connections)
  3. ttkefu如何下载访客的访问明细
  4. 如何解决word中latex公式出现虚线小方框问题
  5. python源代码制作星空_用python画星空源代码是什么?
  6. pandas DataFrame数据重命名列名的几种方式
  7. Deep Neural Networks are Easily Fooled High Confidence Predictions for Unrecognizable Images
  8. delphi xe 10.3 firemonkey stringdrig 插入,删除,添加
  9. 计算机安装操作步骤,重新安装计算机系统的步骤,最简单,最安全的操作!
  10. 思科模拟器 --- 路由器RIP动态路由配置