一、前言 在视频监控行业经常看到两个厂家广告打得比较厉害,一个是青犀视频对应easyplayer,一个是大牛直播,两个最初都是sdk免费,并提供调用示例源码,后面大牛直播的sdk以及示例都无法运行,目前就剩下免费的easyplayer可以用,亲测下来确实免费可用不需要授权秘钥之类的,功能还行,支持各种音视频文件、本地摄像头、网络视频流等,就是在录制H265视频的时候不行,直接崩溃,估计官方放出的版本不支持,内部的版本肯定是支持的。 easyplayer的设计总体上估计参照了mpv播放器的设计,所有属性做成了可读取和设置,通过调用EasyPlayerProSetparam设置属性,调用EasyPlayerProGetparam获取属性,参数传入对应枚举值EASYPARAMID即可,至于有哪些参数可以通过头文件枚举值查看,所有的参数可读取,不是所有的参数可设置。这种设计很巧妙和万能通用,不少优秀的库也都参照这种设计思路,这样后期增加功能只需要增加对应的枚举值即可。比如获取倍速调用EasyPlayerProGetparam(easyPlayer,EASYPARAMPLAYSPEED,speed);设置倍速调用EasyPlayerProSetparam(easyPlayer,EASYPARAMPLAYSPEED,value);看起来是不是很简洁通俗易懂。 二、效果图 includeeasyplayerthread。hincludevideohelper。hEasyPlayerThread::EasyPlayerThread(QObjectparent):VideoThread(parent){easyPlayerNULL;}voidEasyPlayerThread::setGeometry(){if(!easyPlayer){}设置波形频谱可视化效果intmode2;EasyPlayerProSetparam(easyPlayer,EASYPARAMVISUALEFFECT,mode);获取视频流编号intvideoId1;EasyPlayerProGetparam(easyPlayer,EASYPARAMVIDEOSTREAMCUR,videoId);获取音频流编号intaudioId1;EasyPlayerProGetparam(easyPlayer,EASYPARAMAUDIOSTREAMCUR,audioId);intwidthhwndWidgetwidth();intheighthwndWidgetheight();EasyPlayerProGetparam(easyPlayer,EASYPARAMVISUALEFFECT,mode);QStringtext空白;if(mode1){text波形;}elseif(mode2){text频谱;}debug(设置信息,QString(效果:1视频:2音频:3)。arg(text)。arg(videoId)。arg(audioId));设置了不显示可视化效果无视频无音频则不显示波形频谱if(modeEASYAUDIOVISUALEFFECTDISABLEvideoId0audioId0){EasyPlayerProResize(easyPlayer,0,0,0,width,height);EasyPlayerProResize(easyPlayer,1,0,0,width,height);}else{intpercent10;EasyPlayerProResize(easyPlayer,0,0,0,width,height(percent1)percent);EasyPlayerProResize(easyPlayer,1,0,height(percent1)percent,width,heightpercent);}}boolEasyPlayerThread::openVideo(){先检查地址是否正常(文件是否存在或者网络地址是否可达)if(!VideoHelper::checkUrl(this,videoType,videoUrl,connectTimeout)){}启动计时timer。start();安装事件过滤器识别尺寸变化if(videoModeVideoModeHwnd){hwndWidgetinstallEventFilter(this);}创建实例if(!easyPlayer){easyPlayerEasyPlayerProCreate();}创建失败则返回if(!easyPlayer){}准备参数QByteArrayurlDataVideoHelper::getRightUrl(videoType,videoUrl)。toUtf8();charurlurlData。data();HWNDwid(HWND)hwndWidgetwinId();EASYSTREAMLINKMODElinkMode(transporttcp?EASYSTREAMLINKTCP:EASYSTREAMLINKUDP);通过句柄的方式设置播放ifdefeasyplayerxeasyPlayerEasyPlayerProOpen(easyPlayer,url,wid,EASYVIDEORENDERTYPEGDI,EASYVIDEOMODESTRETCHED,linkMode,100,0);elseQStringkeyEasyPlayerisFr1ee!;intretEasyPlayerProAuthorize(key。toUtf8()。data());debug(剩余天数,QString(天数:1)。arg(ret));intparam1;EasyPlayerProSetparam(easyPlayer,EASYPARAMVDEVRENDERSHOW,m);easyPlayerEasyPlayerProOpen(easyPlayer,url,wid,EASYVIDEORENDERTYPEGDI,EASYVIDEOMODESTRETCHED,linkMode,100,0,10241024,10000000);endifisOemitrecorderStateChanged(RecorderStateStopped,fileName);lastTimeQDateTime::currentDateTime();inttimetimer。elapsed();debug(打开成功,QString(用时:1毫秒)。arg(time));只有获取到了宽高信息才算真正打开完成emitreceivePlayStart(time);returnisOk;}voidEasyPlayerThread::closeVideo(){先停止录制recordStop();搞个标志位判断是否需要调用父类的释放(可以防止重复调用)boolneedClose(easyPlayer);释放对象if(easyPlayer){EasyPlayerProClose(easyPlayer);EasyPlayerProRelease(easyPlayer);easyPlayerNULL;}if(needClose){VideoThread::closeVideo();}}voidEasyPlayerThread::readMediaInfo(){if(!easyPlayer){}if0EasyPlayerProGetparam(easyPlayer,EASYPARAMVIDEOWIDTH,videoWidth);EasyPlayerProGetparam(easyPlayer,EASYPARAMVIDEOHEIGHT,videoHeight);EasyPlayerProGetparam(easyPlayer,EASYPARAMVDEVFRAMERATE,frameRate);elseMediaInfomediaImemset(mediaInfo,0x00,sizeof(MediaInfo));EasyPlayerProGetparam(easyPlayer,EASYPARAMMEDIAINFO,mediaInfo);videoWidthmediaInfo。nWvideoHeightmediaInfo。nHframeRatemediaInfo。nFrameRendif纯音频则取父窗体的尺寸if(onlyAudio){QWidgetwidget(QWidget)hwndWidgetparent();videoWidthwidgetwidth();videoHeightwidgetheight();}if(videoWidth0videoHeight0){emitreceivePlayStart(timer。elapsed());QStringmsgQString(宽高:1x2帧率:3角度:4)。arg(videoWidth)。arg(videoHeight)。arg(frameRate)。arg(rotate);debug(媒体信息,msg);thissetGeometry();emitreceiveVolume(getVolume());}}qint64EasyPlayerThread::getDuration(){没有获取过才需要获取if(easyPlayerduration0getIsFile()){EasyPlayerProGetparam(easyPlayer,EASYPARAMMEDIADURATION,duration);emitreceiveDuration(duration);}}qint64EasyPlayerThread::getPosition(){qint64position0;if(easyPlayer){thisgetDuration();lastTimeQDateTime::currentDateTime();EasyPlayerProGetparam(easyPlayer,EASYPARAMMEDIAPOSITION,position);emitreceivePosition(position);如果设置了重复循环播放则快到了文件末尾重新设置位置即可if(duration0offset500){if(thisgetPlayRepeat()){QMetaObject::invokeMethod(this,setPosition,QARG(qint64,0));}else{thisstop2();}}}}voidEasyPlayerThread::setPosition(qint64position){文件才能指定播放位置(保存文件阶段不允许切换进度否则录制的文件错乱)if(easyPlayergetIsFile()!isRecord){EasyPlayerProSeek(easyPlayer,position);}}doubleEasyPlayerThread::getSpeed(){doublespeed1。0;if(easyPlayer){EasyPlayerProGetparam(easyPlayer,EASYPARAMPLAYSPEED,speed);speedspeed100;}}voidEasyPlayerThread::setSpeed(doublespeed){文件才能指定播放速度if(easyPlayergetIsFile()){intvaluespeed100;EasyPlayerProSetparam(easyPlayer,EASYPARAMPLAYSPEED,value);}}intEasyPlayerThread::getVolume(){声音范围是〔182,73〕需要转换成〔0,100〕intvolume0;if(easyPlayer){EasyPlayerProGetparam(easyPlayer,EASYPARAMAUDIOVOLUME,volume);volumeVideoHelper::getRangeValue(182,73,volume,0,100);}}voidEasyPlayerThread::setVolume(intvolume){emitreceiveVolume(volume);if(easyPlayer){volumeVideoHelper::getRangeValue(0,100,volume,182,73);EasyPlayerProSetparam(easyPlayer,EASYPARAMAUDIOVOLUME,volume);}}boolEasyPlayerThread::getMuted(){intmuted0;if(easyPlayer){EasyPlayerProGetparam(easyPlayer,EASYPARAMADEVMUTE,muted);}return(muted1);}voidEasyPlayerThread::setMuted(boolmuted){emitreceiveMuted(muted);if(easyPlayer){intvaluemuted?1:0;EasyPlayerProSetparam(easyPlayer,EASYPARAMADEVMUTE,value);}}voidEasyPlayerThread::pause(){if(easyPlayer!isPause){isPEasyPlayerProPause(easyPlayer);}}voidEasyPlayerThread::next(){if(easyPlayerisPause){isPEasyPlayerProPlay(easyPlayer);}}voidEasyPlayerThread::snap(constQStringsnapName){thissetSnapName(snapName);if(easyPlayer){intresultEasyPlayerProSnapshot(easyPlayer,snapName。toUtf8()。data(),0,0,200);if(result0){QMetaObject::invokeMethod(this,snapFinsh);}}}voidEasyPlayerThread::recordStart(constQStringfileName){thissetFileName(fileName);if(easyPlayer!isRecord){intresultEasyPlayerProRecord(easyPlayer,fileName。toUtf8()。data(),0);if(result0){isRemitrecorderStateChanged(RecorderStateRecording,fileName);debug(开始录制,QString(文件:1)。arg(fileName));}}}voidEasyPlayerThread::recordStop(){if(easyPlayerisRecord){intresultEasyPlayerProStoprecord(easyPlayer);if(result0){isRemitrecorderStateChanged(RecorderStateStopped,fileName);debug(结束录制,QString(文件:1)。arg(fileName));}}}