首页 > TI IPNC Web网页之网页修改教程

TI IPNC Web网页之网页修改教程

web网页程序修改

打开gStudio之后,点击菜单栏中Help->Contents。先把这个诡异的编程语言看一遍吧。这里搬一些东西出来。

GoDB简介

161208408232554.png

161208559954686.png

从第一副图片中,我们可以看出,从源文件到可执行文件的过程。

从第二幅图我们可以了解到GoDB是如何跨平台的。

编程语言的话:

GBasic is a variant of the Basic Language included in GoDB platform as the scripting language.

这种编程语言大小写不区分,真是编程界的一朵奇葩。

GBasic编程语言

GBasic程序通常是有一个frm文件和与之名字相同的bas文件。比如说当HOME.frm被加载时,HOME.bas就会被执行。

  • GBasic中的字符串变量名以$结尾。
  • 如果没有$的变量则认为是数值变量
  • GBasic有两种对象(object),一种是Form Field Objects(可理解为Visual Studio中的rc文件),另外一种是数据库(专业版才能使用)
  • 在GBasic中调用From对象方式是用#对象名
  • GBasic中有三种变量范围,全局,局部和当前页面。

    其中变量名以~开头的是全局变量,你可以在工程的任何地方访问这个变量。没有以~开头的变量并且声明在Sub或者Function之外的,则为页面变量,声明在Sub或者Function内部的则是局部变量。
  • 像其它任何语言一样,GBasic也可以将针对多行代码进行封装。GBasic提供Subroutines和Functions这两种方式,前者没有返回值,后者需要返回值。如果要直接调用Subroutines,你需要在其前面家call,比如说call subname,而Functions的调用则不用。
  • Functions返回值的做法是赋值给函数名。

GBasic自带了很多封装好的函数,这些函数如何用,请看Help。

GStudio这个IDE像Visual Stduio一样,支持控件的事件触发(按钮按下,编辑框改变etc.)这些事件称之为控件的事件,还有另外一种事件叫做Global Event

具体查看Help中的GBasic Reference->Global Event。这里稍微介绍下

form_load当frm文件加载首先执行的是这个Sub。

form_notify当收到服务器反馈时自动调用,此时在用GetMessage获取消息内容..

form_timer,定时器触发时调用此Sub,通常使用settimer(1000)来使能定时触发。

所以一般阅读GoDB工程代码一般先从每个bas文件的form_load开始看起。

Debug

  • 在GUI的下拉菜单中,保证当前选中的是"DEBUG"模式;
  • 按F7编译工程,按F5会编译和启动项目;
  • 保证你在"Win32_Debug"文件夹中有"ip.txt"文件,这个是用于用户登录的。

Release

  • Debug/ Release下拉菜单中选中Release
  • 在View-option在选项卡Build中在Make Read-Only BDB选中
  • 在文件夹Win32_Release中找到gIPNC-ro.bdb
  • 使用以下命令生成gIPNC-ro.gz

gIPNC工程分析

我们做的是在原有基础上添加东西,修修补补,谁要是有精力就从头开始弄吧。

bas和frm和inc一一对应,bas为执行文件,inc为头文件,frm为form文件,相当于Visual Studio中的rc文件。

还是先分析流程吧。

首先当然是登录界面,那就是对应logon.bas和auth.bas,首次登录执行的auth.bas文件,而用户刷新过程中执行的logon.bas文件。

关于logon.bas

readprofile("PAGENAME",pageName$)
...
readprofile("IPNC",loginDet$)
...

首先读取变量名为IPNC和PAGENAME的profile,如果读取得到那么就将读取到的用户名和密码填充到用户名编辑框和登录编辑框(这就是记住密码功能),调用dwnldIniFile函数进行Http请求,请求内容是ip地址/ini.htm,这样如果认证通过,那么此时已经把所有关键字及其内容保存到~responseData$全局环境变量上。获取失败则弹出用户或密码错误。读取profile错误,则加载auth.frm。

我们知道,TI IPNC除了用户验证登录之外还有区分用户权限,所以在成功调用dwnldIniFile调用getUserAuthority获取当前用户的权限,获取失败弹出错误信息,成功并且PAGENAME的值为LOGIN则加载auth.frm,否则直接进入liveVideo.frm

温馨提示,仔细研究下writeprofile函数,你就知道如何做到真正的记住密码。

温馨提示,GetIPAddress()是进行调试的关键。

分析auth.bas不难发现,无非就是。先将读取到的IPNC变量的值进行填充,然后这个文件主要的逻辑就是cmdSubmit_Click这个按钮按下的事件处理函数了。先进行输入验证,然后是登录验证,也是调用dwnldIniFile进行认证getUserAuthority。最后成功加载livevideo.frm,并写PAGENAME的profile。

** 几个比较关键的文件 **

这个工程里面有几个文件是比较重要(特殊的文件)的那就是leftmenu.bas,functions.inc,defines.inc,common.inckeywords.inc

  1. 其中leftmenu.bas是网页侧边栏的执行文件,添加,删除子菜单就修改这个文件。
  2. functions.inc里面定义了众多函数,大部分是网页keyword获取和设置的函数。
  3. defines.inc文件定义了一些关键数字,比如说侧栏菜单(主菜单,子菜单)多少个,比如关键字最多有多少个,有时候一些莫名其妙的错误就是由于这个文件上的数字没对上引起的。
  4. common.inc定义了一些通用函数,比如编辑框可以输入哪些字符,不可以输入哪些字符(CheckKey),产生Request Header(generateauthHeader)。
  5. keywords.inc,这个仅在functions.inc中被包括。这个文件其实不太重要,作用仅仅是用于显示获取或设置关键字时的错误信息而已。

logon.bas和auth.bas他们都包括了functions.inc,defines.inc,common.inc。而其它的主页面除了这个三个文件之外,还包括leftmenu.bas,这样的效果就是将侧边栏和各个form的网页叠加起来。

GBasic是从头到尾顺序执行的,所以每个bas文件先执行Sub或者functions之外的语句之后,然后再执行form_load事件,由于先包括了leftmenu.bas,所以会先执行leftmenu.bas里面的内容,而每个frm页面都包括了leftmenu.frm文件。这样就加载了侧边栏。

leftmenu负责触发加载各个页面。

添加删除侧边栏菜单

161209198549595.png

这里只说怎么加,不讨论怎么建页面。

打开leftmenu.bas

dims ~menuArray$(MAXMAINMENU+MAXSUBMENU+3)
dims menuOffImg$(MAXMAINMENU) = ("!camera_Off.bin","!users_Off.bin","!settings_Off.bin","!maintanance_off.bin","!support_off.bin")
dims ~menuOnImg$(MAXMAINMENU) = ("!camera_On.bin","!users_On.bin","!settings_On.bin","!maintanance_on.bin","!support_on.bin")
dims ~urlMainArray$(MAXMAINMENU) = ("!liveVideo.frm", "!addusers.frm","!videoImageSettings.frm","!maintenance.frm","!supportScreen.frm")
dims ~urlSubArray$(MAXSUBMENU) = ("!videoImageSettings.frm","!videoAnalyticsSetting.frm","!DMVAeventMonitor.frm","!display_settings.frm", "!audioSetting.frm","!setDateTime.frm", "!NetworkSettings.frm", "!alarmSettings.frm", "!class.frm","!storageSetting.frm")dimi dmvaEnable#imgselected.hidden = 1 
loadMenuCaptions()
....

上面是我修改过的。其中比较关键的是几个宏定义的定义,如果你要修改,请算好侧边栏的个数,然后修改在defines.inc中的数值。bin文件是主菜单的对应的图片,关于图片后面才说,frm就是对应的页面啦,算好顺序...

  1. 如果要增加页面,则在leftmenu.frm中增加一个ReadOnly控件,然后NAME必须为rosubmenu,value为空,背景那些参照已有的..你会发现有好几个ReadOnly都叫做rosubmenu,然后这个ide就自动就其他们的name改为类似数组的样子..rosubmenu[0],而程序里面也是如此引用。
  2. 修改字符串数组~URLsUBaRRAY$,将建立好的FRM的文件名添加到末尾
  3. 修改子菜单数量maxsubmenu和leftmenuctrls(在DEFINE.INC中定义).各加1
  4. 修改子菜单名字,修改MENUcAPTIONS.LAN文件,这个文件前面5个是主菜单,后面是子菜单的名字
  5. BUG.将ARRmOUSEpOS(14, 4)改成ARRmOUSEpOS(maxmainmenu+maxsubmenu, 4),否则会出现数组越界
  6. 子菜单对应的页面的BAS文件必须包括两个函数savepage()和chkValueMismatch(),否则会报错..

当然leftmenu大部分菜单鼠标点击时鼠标的触发事件,也就是加载相应的页面。当然还有什么图标排列,音视频开关等处理...

另外值得注意的是:子菜单中有可能有一两个页面是默认隐藏的,某种条件触发下才显示。比如Live Video页面那个Example的下拉框就是一个触发条件,另外如果用户权限不同,子菜单显示的个数也有所不同,这些逻辑都在leftmenu.bas中。

子页面流程分析

IPNC网页默认几个页面,大部分都是传统的表单页面,无非就是些CheckBoxEditCommonBox之类的控件,加载页面的时候,就是获取服务器上对应的控件的值然后填充进去,保存页面的时候,则是发送http请求到web服务器进行保存然后重新获取更新。

一般来说一个控件对应一个关键字。

我们先从对应页面的form_load看起,然后看savepage,然后看各个控件触发事件..其它全局事件。

我们拿声音设置页面为例,对应的文件是audiosetting.basaudiosetting.frm文件。

Sub Form_Loaddimi retValretVal = loadIniValues()if ~maxPropIndex = 0 then msgbox("无法加载初始配置",0,"错误",3)loadurl("!auth.frm")endifcall displayControls(LabelName$,XPos,YPos,Wdh,height)call loadInitialValues()    showSubMenu(0,1)setfocus("rosubmenu[4]")selectSubMenu() setfocus("chkenableaudio")call chkenableAudio_Click                                   'TR-19getimagefile(sliderImage$,"!slider_but.jpg")#lblsuccessmessage$ = ""                                    'TR-35End Sub

retVal = loadIniValues()

这是个关键函数,这个函数里面请求http://ip/ini.htm获取所有关键字的值,如果获取失败了,就提示错误。在loadIniValues函数中,有个字符串数组非常关键,每次添加新的关键字都必须修改这个字符串在后面添加新的关键字。keywords$

网页在functions.inc中的loadIniValues()中解析得到这些值将这些属性和对应的放分别放到字符串数组~iniProperties$和iniPropValues$中.

值得注意的是在放入~iniProperties$之前,在每个属性名之前加了字母"g".

每个页面的form_load都会在一开始调用这个函数

call loadInitialValues()

这个函数,也是非常典型,几乎每个页面都会做类似的处理(可能函数名字不同或者没用函数),作用是获取当前页面关键字的值然后填充到控件里面。

然后这里基本上都用到functions.inc中"get"开头的一些函数,这些函数参数不同,都是处理流程一致,都是分析~iniPropValues$这个全局变量然后取出对应的关键字的值。

retVal = getAudioSetting(enableAudio,audioMode,inputVolume,_encoding,sampleRate,bitRate, _alarmLevel,outputVolume)
'functions.inc中定义
function getAudioSetting(byref dimi enableAudio, byref dimi audioMode, byref dimi inputVolume, _byref dimi encoding, byref dimi sampleRate, byref dimi bitRate, _byref dimi alarmLevel, byref dimi outputVolume)'TR-26                   dims varName$(8)  = ("enableAudio","audioMode","inputVolume", _"encoding","sampleRate","bitRate", _"alarmLevel","outputVolume") dims propName$(8) = ("gaudioenable","gaudiomode","gaudioinvolume", _"gencoding","gsamplerate","gaudiobitrate", _"galarmlevel","gaudiooutvolume")dims tempVal$(8)dimi i,retValretVal = getiniValues(propName$,tempVal$)   if retVal = 0 thenfor i= 0 to ubound(tempVal$)    {varName$(i)} = strtoint(tempVal$(i))       nextendifreturn retVal   End Function

所以当你添加新的关键字时,做法可以是在原有函数基础增加参数。也可以按照里面的这些函数的形式和逻辑重新定义一个新的函数...

下面介绍另外一个重要函数,那就是savepage函数,记住不管你页面是否需要保存,都必须存在这个函数。正如前面一小节所说......

savepage函数里面重要是使用functions.inc中"set"开头的一些函数。这些函数参数不尽相同,但是逻辑相同,都是通过发送http请求设置关键字的值。

savepage函数一般还会做控件输入值有效的检测,错误成功信息显示等处理...

/*一般实在保存按钮的事件响应函数中调用savepage函数*/
Sub cmdsave_Click   if canReload = 1 thensavePage()      end if
End Sub
Sub savePage()'Validate user input controls valuesiff validatemodecontrols() = 0 then return  dimi retVal,i' TR-26 set values to cameraretVal = setAudioSetting(#chkenableaudio,#drpaudiomode,atol(#sldaudioinput$),_#drpencoding,#drpsamplerate,#drpbitrate,_#sldalarmlevel,atol(#sldaudiooutput$))saveSuccess = retValtempX = #lblsuccessmessage.x'Based on reload flag wait for the camera to restartif getReloadFlag() = 1 then                             'TR-45              #lblsuccessmessage.style = 128#lblsuccessmessage.x = #lblsuccessmessage.x + #lblsuccessmessage.w/3canReload = 0animateCount = 1call animateLabel("lblsuccessmessage","参数更新中")else // If Reload animation is not requiredcanReload = 1end ifif canReload = 1 Then   //Do the remaining actions after reload animation is donecall displaySaveStatus(saveSuccess)     end if  
End Sub
function setAudioSetting(dimi enableAudio, dimi audioMode, dimi inputVolume, _dimi encoding, dimi sampleRate, dimi bitRate, _dimi alarmLevel, dimi outputVolume)    dimi retdims value$dims responseData$' TR-26value$ = "audioenable="+enableAudio+"&audiomode="+audioMode+"&audioinvolume="+inputVolume+_"&encoding="+encoding+"&samplerate="+sampleRate+"&audiobitrate="+bitRate+_"&alarmlevel="+alarmLevel+"&audiooutvolume="+outputVolumepprint value$        ret = setProperties(value$, responseData$)  if ret > 0 thendims propName$(8) = ("gaudioenable","gaudiomode","gaudioinvolume", _"gencoding","gsamplerate","gaudiobitrate", _"galarmlevel","gaudiooutvolume")dims propVal$(8)propVal$(0) = enableAudiopropVal$(1) = audioModepropVal$(2) = inputVolumepropVal$(3) = encodingpropVal$(4) = sampleRatepropVal$(5) = bitRatepropVal$(6) = alarmLevelpropVal$(7) = outputVolume~errorKeywords$ = ""~errorKeywords$ =  chkRetStatusAndUpdate$(responseData$,propName$,propVal$)if len(~errorKeywords$) > 0 thenreturn -10elsereturn retendif       endif return ret              End Function

跟踪到里面去,不难发现,实际上就是发送这样形式的Http请求:

vb.htm?keyword1=xx&keyword2=xxx

当增加新的关键字时,可以仿照这些set开头函数重新定义一个新的函数,也可以在原有基础上扩充。

GoDB端添加关键字的流程

这里介绍在GoDB端添加关键字的流程。我们忽略boa端代码的修改,所以修改完之后,既不能获取到这个关键字也无法设置成功。

现在举添加rtmp功能的例子..

页面如下,在网络/端口设置页面添加一个checkbox和一个edit,这就对应了两个关键字,关键字名字分别为rtmp_enrtmp_url(随意)

161209510269972.png

还是从from_load开始,还是从加载控件默认值的函数开始...

call ShowDefaultValues()

在这个Sub内:

dimi rtmp_en

Dims rtmp_url$

然后我们选择扩充已有的函数

retVal=getSNTPRTSPDetails(sntpServer$, multiCast,rtmp_en,rtmp_url$)

/*在functions.inc中定义*/
function getSNTPRTSPDetails(byref dims sntpServer$,byref dimi multiCast,byref dimi rtmp_en,byref dims rtmp_url$)dims varName$(4)  = ("sntpServer$","multiCast","rtmp_en","rtmp_url$")dims propName$(4) = ("gsntpip","gmulticast","grtmp_en","grtmp_url")dims tempVal$(4)dimi i,retret=getiniValues(propName$,tempVal$)if ret>=0 thenfor i= 0 to ubound(tempVal$)if i=1 or i=2 then{varName$(i)} = strtoint(tempVal$(i))           else {varName$(i)} = tempVal$(i)             endifnextendifreturn ret End Function

你对比下原始函数就知道我修改了哪里..

接着我们修改functions.inc中的loadIniValues函数,将rtmp_enrtmp_url添加到keywords$字符串后面

/*....*/keywords$ +=",aviformat,aviformatname,aviduration,avidurationname,reloadtime,qpinit1,qpinit2,qpinit3"_",videocodeccombo,videocodeccomboname,streamname1,streamname2,streamname3"_",localdisplayname,sdinsert,reloadflag,dmvaenable,minnamelen,maxnamelen,minpwdlen,maxpwdlen,maxaccount"_",bkupfirmware,display_mode,ppt_sw_en,ppt_sw_sensitivity,ppt_sw_interval_time,channel_id,psname,piname,ch_id_name"_",file_size,video_long,brightness2,contrast2,saturation2,sharpness2,brightness3,contrast3,saturation3,sharpness3,brightness4,contrast4,saturation4,sharpness4"_",language,ftp_upload_option,ftp_upload_time,trackip,cloudip,serverenable,singlecodecres,rtmp_en,rtmp_url"

接着我们修改keywords.inc文件,将这两个关键字添加到cameraKeywords$关键字后面。"|"右面是错误提示信息,合理填写即可。

cameraKeywords$ += ",display_mode|Display mode,ppt_sw_en|PPT Switch Enable,ppt_sw_interval_time|PPT Switch interval Time,"_",ppt_sw_sensitivity|PPT Switch sensitivity,channel_id|main display channel,language|language,trackip|track ip,cloudip|cloudip,"_",serverenable|Upload to server,singlecodecres|resolution for single stream,rtmp_en|rtmp enable,rtmp_url|rtmp url"

接着,我们修改savepage函数。

retVal = setSNTPRTSPDetails(#txtsntpserver$, #chkrtspmulticast,#chkrtmp,#txtrtmpurl$)

这里传入的参数是直接将对象的值传入..

function setSNTPRTSPDetails(dims sntpServer$,dimi multiCast,dimi rtmp_en,dims rtmp_url$)dimi retdims SNTPRTSPDetails$dims responseData$SNTPRTSPDetails$="sntpip="+sntpServer$+"&multicast="+multiCast+"&rtmp_en="+rtmp_en+"&rtmp_url="+rtmp_url$       ret = setProperties(SNTPRTSPDetails$, responseData$)if ret >= 0 thendims propName$(4) = ("gsntpip","gmulticast","grtmp_en","grtmp_url")dims propVal$(4)propVal$(0) = sntpServer$propVal$(1) = multiCast     propVal$(2) = rtmp_enpropVal$(3) = rtmp_url$~errorKeywords$ = ""~errorKeywords$ =  chkRetStatusAndUpdate$(responseData$,propName$,propVal$)if len(~errorKeywords$) > 0 thenreturn -10elsereturn retendif       endif return ret          End Function

这样着,关键字添加完毕了,按照前面文章说的步骤编译生成吧..

boa端添加关键字的流程

只讲怎么添加。

请自行用工具定位下面变量,函数的位置。

先从SysInfo结构体开始修改,这个结构体将保存所有网页表单,关键字的值。

我们在末尾添加

typedef struct SysInfo{//....__u8 rtmp_enable; ///< rtmp_enablechar rtmp_url[MAX_RTMP_URL_LEN+1];
}SysInfo;

网页需要出厂默认值,所以在修改system_default.h文件添加两个宏定义。

#define RTMP_EANABLE_DEFAULT 0 //RTMP使能,0为不使能...
#define RTMP_URL_DEFAULT  "rtmp://192.168.1.120:1935/live/xxoo"

修改file_mng.c

修改全局静态结构体SysInfoDefault,往末尾添加

static SysInfo SysInfoDefault =
{//....RTMP_EANABLE_DEFAULT,RTMP_URL_DEFAULT
};

修改HttpOptionTable结构体。

HTTP_OPTION HttpOptionTable [] =
{//....{ "rtmp_en"         , set_rtmp_en               , AUTHORITY_OPERATOR, FALSE,  TRUE, NULL },{ "rtmp_url"                , set_rtmp_url                  , AUTHORITY_OPERATOR, FALSE,  TRUE, NULL },//...
}

接着在该表所在文件定义这两个函数,一个是整数,一个字符串,注意区别。

void set_rtmp_en(request *req, COMMAND_ARGUMENT *argm)
{__u8 value = atoi(argm->value);do {ControlSystemData(SFIELD_SET_RTMP_EN, (void *)&value, sizeof(value));req->buffer_end += sprintf(req_bufptr(req), OPTION_OK "%s
", argm->name);return;} while (0);req->buffer_end += sprintf(req_bufptr(req), OPTION_NG "%s
", argm->name);
}void set_rtmp_url(request *req, COMMAND_ARGUMENT *argm)
{do {if(ControlSystemData(SFIELD_SET_RTMP_URL, (void*)argm->value, strlen(argm->value)) < 0)break;req->buffer_end += sprintf(req_bufptr(req), OPTION_OK "%s
", argm->name);return;} while (0);req->buffer_end += sprintf(req_bufptr(req), OPTION_NG "%s
", argm->name);
}

sysctrl.h定义SFIELD_SET_RTMP_EN和SFIELD_SET_RTMP_URL

ControlSystemData函数中添加

        case SFIELD_SET_RTMP_URL:ret = SetSysCommon(data, len,SYS_MSG_SET_RTMP_URL);break;case SFIELD_SET_RTMP_EN:ret = SetSysCommon(data, len, SYS_MSG_SET_RTMP_EN);break;

Msg_Def.h中定义SYS_MSG_SET_RTMP_URLSYS_MSG_SET_RTMP_EN

ProcSysMsg函数添加

        case SYS_MSG_SET_RTMP_EN:{unsigned char value;DBG("SYS_MSG_SET_RTMP_EN
");if(pMsg->length != sizeof(value))break;ShareMemRead(pMsg->offset, &value, pMsg->length);if(SetRtmpEn(value) != 0){printf("
SystemServer:Fail at SYS_MSG_SET_MULTICAST
");break;}ret = 1;break;}case SYS_MSG_SET_RTMP_URL:{if(pMsg->length > SYSTEM_SERVER_BUFFER_LENGTH){printf("String length bigger then System Server Buffer
");break;}ShareMemRead(pMsg->offset, buffer, pMsg->length);if(SetRtmpUrl(buffer, pMsg->length) != 0){printf("Fail at SYS_MSG_SET_RTMP_URL
");break;}ret = 1;break;}

定义SetRtmpUrlSetRtmpEn函数(这里在system_control.c中定义)

这两个函数的内容就非常自由了,不过和其它相同的地方就是就是需要将关键字保存到sysenv.cfg文件中。

所以分别各自定义一个函数用于保存关键字。

fSetRtmpEnfSetRtmpUrl(file_msg_drv.c)中定义。

int fSetRtmpEn(unsigned char value)
{SysInfo *pSysInfo = (SysInfo *)pShareMem;if(pSysInfo == NULL)return -1;if (value == pSysInfo->rtmp_enable)return 0;memcpy(&pSysInfo->rtmp_enable, &value, sizeof(value));return SetSysInfo(0);
}
int fSetRtmpUrl(void *buf, int len)
{SysInfo *pSysInfo = (SysInfo *)pShareMem;if(pSysInfo == NULL)return -1;if(sizeof(pSysInfo->rtmp_url) < len + 1)return -1;memcpy(&pSysInfo->rtmp_url, (char*)buf, len);pSysInfo->rtmp_url[len] = '';return SetSysInfo(0);
}

int SetRtmpEn(unsigned char value)
{int ret = 0;SysInfo *pSysInfo = GetSysInfo();if(pSysInfo == NULL)return -1;if(value != pSysInfo->rtmp_enable){ret = fSetRtmpEn(value);if ( value == 1 )rtmp_start();elsertmp_stop();}return ret;
}

int SetRtmpUrl(void * buf, int length)
{int ret = 0;signed char* serverip=(signed char*)buf;SysInfo *pSysInfo = GetSysInfo();if(pSysInfo == NULL)return -1;if(strncmp(serverip,pSysInfo->rtmp_url,length) != 0){ret = fSetRtmpUrl(buf,length);rtmp_stop();if ( pSysInfo->rtmp_enable )rtmp_start();}return ret;
}

修改HttpArgument结构体,用于获取关键字的值

HTML_ARGUMENT HttpArgument [] =
{//...{ "rtmp_en"     , para_rtmp_en              , AUTHORITY_VIEWER , NULL },{ "rtmp_url"            ,   para_rtmp_url       , AUTHORITY_VIEWER , NULL },//...
};

int para_rtmp_en(char *data, char *arg)
{SysInfo* pSysInfo = GetSysInfo();if(pSysInfo == NULL)return -1;return sprintf(data, "%d", pSysInfo->rtmp_enable);
}int para_rtmp_url(char *data, char *arg)
{SysInfo* pSysInfo = GetSysInfo();if(pSysInfo == NULL)return -1;return sprintf(data, "%s", pSysInfo->rtmp_url);
}

以上就成功的加入了关键字了,在其它线程里面,你只需要调用

    SysInfo *pSysInfo = GetSysInfo();//pSysInfo->rtmp_en//pSysInfo->rtmp_url

转载于:https://www.cnblogs.com/tracyone/p/4431698.html

更多相关:

  • 草色新雨中, 松声晚窗里。之前我们学习 Power Query 都是用鼠标就完成了很多复杂的操作。虽然 PowerQuery 已经将大部分常用功能内置成到功能区。基本能完成我们大部分的报表自动化功能。但是总有些复杂的或者个性化的问题是开发团队没有预先想到的,这时我们就需要学习 M 语言。一、M 语言在哪里?M语言的函数公式有三个地...

  • 前言从2020年3月份开始,计划写一系列文档--《小白从零开始学编程》,记录自己从0开始学习的一些东西。第一个系列:python,计划从安装、环境搭建、基本语法、到利用Django和Flask两个当前最热的web框架完成一个小的项目第二个系列:可能会选择Go语言,也可能会选择Vue.js。具体情况待定,拭目以待吧。。。基本概念表达式表...

  • 1.1函数1.1.1什么是函数函数就是程序实现模块化的基本单元,一般实现某一功能的集合。函数名:就相当于是程序代码集合的名称参数:就是函数运算时需要参与运算的值被称作为参数函数体:程序的某个功能,进行一系列的逻辑运算return 返回值:函数的返回值能表示函数的运行结果或运行状态。1.1.2函数的作用函数是组织好的,可重复使用的,用来...

  • 原标题:基于Python建立深度神经网络!你学会了嘛?图1 神经网络构造的例子(符号说明:上标[l]表示与第l层;上标(i)表示第i个例子;下标i表示矢量第i项)单层神经网络图2 单层神经网络示例神经元模型是先计算一个线性函数(z=Wx+b),接着再计算一个激活函数。一般来说,神经元模型的输出值是a=g(Wx+b),其中g是激活函数(...

  • 在学习MySQL的时候你会发现,它有非常多的函数,在学习的时候没有侧重。小编刚开始学习的时候也会有这个感觉。不过,经过一段时间的学习之后,小编发现尽管函数有很多,但是常用的却只有那几个。今天小编就把常用的函数汇总一下,为大家能够能好的学习MySQL中的函数。MySQL常使用的函数大概有四类。时间函数、数学函数、字符函数、控制函数。让我...

  • 本文来自 运维人生 ,作者:fly是个稻草人链接:http://www.ywadmin.com/?id=76误删除linux系统文件了?不用急,本文将给你一个恢复linux文件的方法,让你轻松应对运维中的各风险问题。方法总比问题多~说在前面的话针对日常维护操作,难免会出现文件误删除的操作。大家熟知linux文件系统不同win有回收...

  • 原文来自SecIN社区—作者:WiHat0x00 什么是WebShell渗透测试工作的一个阶段性目标就是获取目标服务器的操作控制权限,于是WebShell便应运而生。Webshell中的WEB就是web服务,shell就是管理攻击者与操作系统之间的交互。Webshell被称为攻击者通过Web服务器端口对Web服务器有一定的操作权限,而...

  • 断电时文件系统发生了什么?硬盘又发生了什么?下一次开机时写到一半的文件在系统层面还在吗?在底层还在吗?更进一步的, 文件系统如何保证事务性, 会不会存在某种极端情况导致例如最后几个bit还没写完, 文件系统却认为它成功了的情况?回答不限任何文件系统,谢谢!下面是「北极」的回复分享断电的一瞬间,很多事情是无法确定的:1. 你无法确定...

  • 接到项目需求。需要搭建一个页面进行交互,慢慢来b (2).jpg使用python django框架进行页面的搭建在项目文件下打开窗口,输入命令;django-admin startproject helloword#在文件helloword/helloword/创建view.py在view.py文件中输入以代码from django....

  • 常见的错误集合解决方案(一)No.1提示错误'Microsoft.VC90.CRT,version="9.0.21022.8"把Microsoft.NET Framework 3.5.1下面的全部勾选上。No.2解决Qt Designer设计的图标但是VS生成不显示问题描述:在Qt designer中为菜单栏和工具栏设计的图标,但是...