libonvif-1.4.4/0000755000175000017500000000000014355557521013242 5ustar stephenstephenlibonvif-1.4.4/onvif-gui/0000755000175000017500000000000014355557026015145 5ustar stephenstephenlibonvif-1.4.4/onvif-gui/CMakeLists.txt0000644000175000017500000000453314355557026017712 0ustar stephenstephen#******************************************************************************* # onvif-gui/CMakeLists.txt # # Copyright (c) 2022 Stephen Rhodes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # #******************************************************************************/ cmake_minimum_required(VERSION 3.17) project(onvif-gui) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__STDC_CONSTANT_MACROS") list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) find_package(Qt5 COMPONENTS Widgets REQUIRED) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) add_executable(onvif-gui src/admintab.cpp src/camera.cpp src/cameradialogtab.cpp src/cameralistmodel.cpp src/cameralistview.cpp src/camerapanel.cpp src/discovery.cpp src/filepanel.cpp src/imagetab.cpp src/logindialog.cpp src/main.cpp src/mainwindow.cpp src/messagepanel.cpp src/networktab.cpp src/onvifmanager.cpp src/ptztab.cpp src/settingspanel.cpp src/stylepanel.cpp src/videotab.cpp include/admintab.h include/camera.h include/cameradialogtab.h include/cameralistmodel.h include/cameralistview.h include/camerapanel.h include/discovery.h include/filepanel.h include/imagetab.h include/logindialog.h include/mainwindow.h include/messagepanel.h include/networktab.h include/onvifmanager.h include/ptztab.h include/settingspanel.h include/stylepanel.h include/videotab.h resources/resources.qrc ) target_link_libraries(onvif-gui PRIVATE Qt5::Widgets onvif avio ) target_include_directories(onvif-gui PUBLIC libonvif/include include ) libonvif-1.4.4/onvif-gui/resources/0000755000175000017500000000000014355557026017157 5ustar stephenstephenlibonvif-1.4.4/onvif-gui/resources/apply_hi.png0000644000175000017500000000036514355557026021476 0ustar stephenstephenPNG  IHDRJLsRGBgAMA a pHYsodIDATHK F#sAtWV ď/!-x5qZcݼז;p3">QbE*Y9Y!!$$B|I3 .(̧Kv_ 5$/CpNL 9IENDB`libonvif-1.4.4/onvif-gui/resources/stop_hi.png0000644000175000017500000000065114355557026021334 0ustar stephenstephenPNG  IHDR1qcgAMA a cHRMz&u0`:pQ<tRNSv8bKGD݊ pHYsodtIME .)uIDATc`* B!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T16:11:49+00:00M(tEXtdate:timestamp2022-12-09T16:46:27+00:00σtEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/record_lo.png0000644000175000017500000000065114355557026021637 0ustar stephenstephenPNG  IHDRJLsRGBgAMA a pHYsod>IDATHKM05 Q>VvffV5g.: N ;K;o^Ă26r_lRGB D[2&%\4 9[7Qb$z, "` BvJ+#R-RtEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/mute_hi.png0000644000175000017500000000076514355557026021327 0ustar stephenstephenPNG  IHDR1qcgAMA a cHRMz&u0`:pQ<tRNSv8bKGD݊ pHYsodtIME &6sEy_IDAT̡0pu8A` Dm*8Jy"= X$agbU/FͲf5162_W"Q"h] J!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:38:54+00:00\7QtEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/onvif-gui.png0000644000175000017500000013200414355557026021570 0ustar stephenstephenPNG  IHDR\rfgAMA a cHRMz&u0`:pQ<bKGD pHYsodIDATxy^y9rbA\.NZ,+Ų#8yg:I'qd"H,L =N4Nzjtg&Ӂ([2]"E mzs:u]pQx./U߻TZVkZV^\+nydͩLtdqHhwJD:XޢtgE"K/G?z~kk&/~sQmھ7ڝtrv!۝=N.I пܠu vb/=w< 'K 4ϟiwzker DE {om3gMFG)ʿrZu][.@!$yQ-@$+'/xx[<_xZq5xCG-{[n|춽Y9A)Ujt-9Tw.VW|nw@?)HrDs܉@/ TnfqQf/BCT}z 껎-")HOxbͿ׷zEkގ[@*NRGpbm3t \hk кfR".<>q+Gx5xˡ#Gfwoyz4jtRV;^~U@Q*|Xo4%+((hpq͜ѱG5S(5(E2_>ڝڼw(3_{VCzs^DiZ羃HFhK' 9v-ʕk*=]7_]"zO 7t­{u 0N´킱)3K\ѱg~k%X+\92vy`uM{s#+{.agZ<*GkL@ $BO̽pO5Քk CG̦n;|P|868:ӆvi.:Yo+QK'>|+e{lOonxH:9wVZc0}̃ ]s=;jو@Yf&ƘʖOn?R\,>{oy-7Ȥ{_UvU9%վ6d_fE>qP iXqk~Qlzt~Ħ;N=K]O :jby?nRdW4 )`HFJWO?5Tq45ZT,7ҝ?v;ẂuA>,Ar{O̝ѷ:\@ys{v2U}W\5>+nǮ 0qk4JʯƋ?VKm$QI¨V`y z?~CGn+[Wj{OTs쭓p;j}T*AblJ%](yѣoAG5(ˡ}#~d'=; u^?@Xn`4 ४OPWqUa"PxYH2֥?x6ow@eU >ԫj2K7ֳ6v",ƻγXG1 e ـĤ~oD4?wNtfɇ1vN# m5q⊨;cbW$F^kC(V>~dFB"l'p[92^+(w{ !/8T_NBC,\/$O ]l𱣋cZ_}L61K63y[={y݉Y^ Q~f?37Dk.nhJ@ׯQ~q5`ۦ5,,6SG?_ojz'o. SƐSϥ!o"|rF;X 벃&$ įV3*SK'v=Ico 8twYVS$^_`8zH'W]]`q2_(p5؉"tpXŬƧH!f7~O?c|Xٺul}Oxr-T)tWُ1@c#-HD#n`ٯU.?psȥ[h?X{V}E_֛c|d*tX-eeP'! P c[A15UAAE*vϯJX?utbny&o|B[4+ 9 tqx@@pϿz~g~k ~]˛x=㽸Ec;*10_.T_/w"2o[`WBr;hE L'QvZ:#t &FkI$@LDMH!  'ζQN!$ϟw ˛={.}߱nZ96c&P=w8F:;;jAGT".\]L LŅ7OtM5bL6b$aSS`S3t@6t&oY 5Nw>dX'@cp0bYgO o:x~B}#A~hw< |\@@-b)Ў%BB `"ZR)#1O61ی`PNUԷ^/g9B^mUN= p7.xS#Gf^ F%Ol\PƖ7J>J~Mz\,EvLFZqva`#la*)hؒuOZ! Q}}AQ1 cV +߁@ <7OMjsjoץ;-!Z*/&A0B?T?GPH,n@"e"0n`%%kb(c\ #pCxl.G@U+՘2Fr"(R`?n5@X$OXyo 8tlG/oY81h7u00~X y8kGC2  U+rY5HHݼ2X˗Q^2t zٚaGrj݊m9e@ __=q)xuJz?8W.3>O]OQO|{Q+kf2f@"#MZ2B3iF~fq FZ+P /Z>Y_)G"Q\$o!l<"Ub'*2J_J~+"ERKHiT S4u!r2䔁eT9h`!&mJʁaNCi \~,ˡgkGy~9 (ߘ t4+E@C"VH`k[`g#YgBLA^a7h B2yTfQy\ ~9JU+".gR]K+hHH1ĐR Q CdJ_s ] ’wW$SKnQ_N'Iܳm3;nޱxEx=go{)Ɏʭ,x!ڏ xa?Lk߻Ж `k+{M)fɤH7M)ad5Om;*vjXX.\J 4@֌.2 SaG[fV$#z >mq D_oҜJRB6e"#ʺ*̫P x.m}U7,o88t[D'w.*\W jT-7μ1!Y؄f$ fK@}%F,W?UHV_U/)DE.e\^j2bZƀ2*t3j?^ =|kxC#Gf]O!:Jo\B u#wi@EѠs7M%l3TSb&)(XS{G*Ud/߰ؖSZj+KXN`} U"X.yewe>Fgdc d[fM8 N9pcg%0~E+%*P@rK &?>o~W%HWQy<nWc;<#$R`k; 6QFffyWfgN=]<yyyAzfE-[ c5[Z9\#U3OS'U=ȖrnW<, @ YRjAd2ðʌC맅䁈='BTe&|?3=j 5x.b7~'?WZBuC?⎛*>aߍۇP:<ݗq}&.@Tg%W\}"Ҋ-`S B2g >T}\I/BQ^ 롫V/),fW=dj0![S"\N Ӄx;A3Rr a (}V}\y) xCύ X([!X\@]q=es]xpȑً o?|u6ϭ` m`VRWloJ7N&n`aK;B!cfWPz;ϻX^A7뢗4K)rz+">{nqTiM".]2J ;v[h۩s  d0>cGz npxP3?AU7fr@ n'M"6c,6D.p4z*۰q(L~l^bz/ i\G2tg=Pzuyb\Hq(eX6B|;`*5"Dig3/Paós q "" !'cIܙ“{*\n~1ʩ_bБ#k?NSԽjv`\O0H\׊1,rZĞJAGXE =E25.yWxC\|XL6mv[gk~ =9|OcL^[75:BP"Dpyu{"C+bрÓ09 @r].z6ތw: `'{y/k'K:|o@WW^y^Z wqos]d,cs3–n}XH=s! [/n^K9 T/3XJ/Yxf|KoFオk`=ȵ v/VqI/>OF?砟n\$`S/}oAC6&@C,ǥn==#`eTÏ<);)khSQ,E1>'f- :M4c5 F@ ;b9[K8Obr(YU @Ugn=yKT.~Y<ςCB "aL(fa?/L|gצO5clwc*xo"<{eiap~io{L92{{wHi|X Hc&"Ħf'p\rjaOf~}/SCtU >ֲ<{?\&. !Sd28T['hsЯgC(""c! P`}\I#+=i8xMS=g/\yoόh[?֚[xmޫ+h"$ ܳf1V؊u(rjA0b)מ^ #URˣbvv>v&#zp*-"(t͋j^ӵW=ى?w$'(M"@(D!@an:d%ݰ=3 H'iݯ0"B3I"t7hs[8Gy xБ#x0SVCZYꍿڑĖf;mibaj̙g*xPR5@?uqixugZ*^匪펝ϴ;vj o 9xkxVvZ8"'&ApD_SuKR"A$_5LX‚y~F姣Y$eHԹ"E?Wpہ gؾΣX7fy`:>aEt_J$f7vZxIljKX{Am+>S)ypw l @@Kn 9tw\@vP8̊FZ$! }G`hfa),B#d/.@S>9!,K[ю&9zYV$TE x1vБ#_i^x~A%06~Xk h&jLGhD_o9\7.B2C5ĥY|gkx,V%Kμ{B]F9kb_a!+xa8Nڝ% c@ P@7`فU8 |-eTpaV9Yg@a PT61h[m^),!0,$-m۟+8b5]LunE%G'wMp\ I4c4eT:b=#1CR ʐWҋ|i,W_ SQ4| ^j}gJ/̀JXЯ@sOi!က3/r?׮8! i~FBrB#|`u>֧4`6aO$][g/`)+C`)Tx{~j呗W}{^~<ӝ5L h -)m_>g'1h*-\Ab:bzlϮ}^Rv|yiӛ 9᷐)0 h;?(/| LBx#7]043(>JͯsL #Tޕcrd%^GtTRvAL4Iu•<5dh(k)Vv}PXU=Nˑ~Y~ڟPIc!gM6Nъ40¢H>%<Ӌ88  +V?(m0βr=࿣"(Cro9@od J\mhA LsV@"TQ) zŔf@\Bq/g-W箬]feBw0DQ&.K{)7y.pr/KZ3 :_n zl?=9/f?Ï׾Jz3~ lc/ʏ5B+ `SM/օ3G8h!L@[_ "v8 X[g@߁"m|'j"zx [gbI摈c!b)ι+X{ }Sa9#g=59hU+o6valͪj|L r8;88./0})޲/sA7! =0z\{ɏ۟H2 vLXЂNwtXΰ J0 dKք5=)/Ür]R\#t>Ȕʀ4waoLmL?0q볉_ؽ {f'pLBH&xmr )<]Z2,BQ'-^w8dE#X>9 T_!CHa6  @YUzt1s 2ژ$4r1h]d8oNh9ɕ³WV3MvNƏWXJ/zkyJs>Z~rC0ѿF=bE `٪!c Es:opAyB邀yDa8Q~T&=P +,& {72P RZC?_u,G( }0Sȹ +XЛ۲?͵jd?5bgvOqiD(__Nwq'?30P=޳_/*\wW4?[_FkO"(OQg1oåN zNq 2ɟ^WL"žt$._L@;A2'^gGp@lN 0߶Cw;'U gGyl~Б#g9'PĞ&nL:m&_ڑ'2)8Nwo}gϖyt" nI%o{rͯ_U`gCCt=EԟX f-u44PO&8-G5qkG"Lb&9'rC`ie$!/P 0-ϙMD~pA T!/;o<#PDp_6"ici,LYK D>zn+R<<3Ӹ08o}?@XכݹJS`*Bwh\ v~2¡UI"kpã-+_m6+`T[ U|G :4y]= | SѬ8C?mjP }l_ul (apqc^Q8tr*(eC$R⺉nLb)lnJ{t)][2^3oWZ~ jG;ss3-˲5ٍmp: Ϗ`e2s֕[iK riv#Gf2mח# ?Dk~wOcSiuoJ3Qeet5\\g_1,'PԸRW(ݩӰ_Urəu`=+ s,aGGt@J޽J/<i_feS=6r!HBxnlߘ:d::<,7 VVi Z=9 xЧtkj/bV4 L7˰ tnt g+OwW"N׬eZ |;N?ۏ[L&X y/sya£2<"U!9(LK-p >,}Ri5* `mX8{"hɶh 6paAaM$wŅv@?SX۴ ^/'"L%1jMjG4n'1dLAK×0>2JHSes`|}AGX `~C- `_qǓJGhw돦XAڎU+z$ @(c:(͔`7>ܝ {6,h}n/Vi'"R^V6ԾsCGnI'pLID H!LO?@Pexc F_uQOk*^﬒WWۀcR{M mڭ#ή«ƪ]Z0G8t JQ[]Ǟg!ʏi͜gr%͞E6V8sgC K zY xm@m?J0hFDFj .ϗ3Wq>_2迥QM>N?KÞB]<7e т^_(v &v -Yvoq* >0ӎLUͪf+5HX6}DyY0|1ĶuKp~a 8Mi}y _ŮXHfCG̪LQ!gwR5X . _ɵb1#܀/8 a9BG0ÇC+?1r{[&%pw Y0m.3WLH݊vxSG P)\NDXe_)1lnsd ! ^(@)َ!˯IVͲ6ziaCO&س]*4W "r?`u1fN=F)%QPKOETJu,/_k]nR>A% hYDEk z4?h oÀ᜷Va]~m!D$"'JvGf)w@@20AORWӞ Q66g)Z4^8;/>F fB H\߼ wNǞ Pϝó]}(ȴLHsR< whGM)}9V+0<~gp%{ z;¦SZ!gC*gH{T-azqXn\+Z#!1"# Qur::٤R&'%O Z̒lt!)2]ZHEM{^kHULȐEfF^hMfBRf]P n0qUL#GPfX|FDE Q[Д- DB|WCyⱭC>  ,R"4H>}\ʰ;5#Db>rWI6!@Hg ~z/R ,3CsrM*g(<4~ʚZK.7XXhsJ hjq99M㪩NdWM2ݤcB Aw\] >oaCz+ ("Ca|q&DS4vN:1|*|ꡇNdj2( #J C"v1u# A(l71ca-E3`c!HAHDh6f 'rRpۊ)+:iNXaWl{nEP+6 ~x%u#Jo:?̳L΁a@G_k|3pډ|b!ib̞Ž8%ٶﱏ/'~h 래I%gSF&$HE [~6K^];!W^t8ct,b<¯C `Gr#~~W&~'//^v'YL),E"| lߐ_`)3TXvswZ= t~˫U r sU4|ʖ?B#1$;Η;bc3kG}PCS×ՕB".2 (ȵ/\qN `5XӲ ()3Λ@Ďf~ ?-NӋ+xۃ iX= pe?bx6Ğ>\53Q+e|B_-^VamjGAg WwhrN끀-*oԂĈˤ#xyy@"hO\ܙfʣ>_;^!!C9"C(:S1oF\m)w &L.f8 C\C.̇(h*SW9j#Ż.g@N'fa\ pߋI@O-d{x- =a ,@Y<@M>űW@iJp)JNGu %,pV*p2(5Cʖɿۨ¤ D1|" iwpK뎃Gx#^?h.lkl?ю6!aZ%4`l;u }76œ Bq!_#J)t)lݺ|i+ߔb7Dތz8<ρ.G:Jsg2SHM2`N %|Zj~OQy.ᴓ›p_1aЊ:6o?ǟ6y3GoxwHoGhb2AAce};]m?Vĺ[T]SE$b*ĖKk=Y׾;X9 y L,w1|R}Qdnn#z ?Ѳ\V3P^=/S]46_s`6m p!oDng2@EQ} YPA#&eA~$nlt+6śO69$r hCDLVxs [ r l6n|7k߀e7yF98d;sEUJj8Y a7YzfWl}Р>(t^uڸYN6![#ډxqYUAF(K?՘S;zgV+wҍ4?/6 1H) ,rJDؒS?lUO2ʹiW.]R]k_J¢@Xgqn @HAPV}@s_H{vxeqjrrq%Q}ΡvyZ3jb{{牶8O>S PZ<r: DaB$#L$!;{ۿ:.&rH ) D!=٩;hPA x/a:bVճ1ȋ'j xo=W`5>H&!HBN2EK7h x>ozB٪C~BUG5_|nz9tNq)G7Ro'(X4bFR h A> 6t fC^ԕ<ܭ=@#}[TwZB J08CVw<x=v0j8DacŜ[ٰdaXM™Mk/D ӭiרʣ"9 BeD@3=3ΤuŴg4E wO4n \BB3MbzGWv{v=w|ofXdHӴ?@Ȏi}k^W=/În1)zEu_9/D+i{֎̺V˶{ 6" E B3`>`qˏHĸu $sf>FY\S*;C'=W 4?Ɔ`lԟS @qP-^8,,#`Mnǒzj/X'QfZgo[>CsCfru,d$W8PxH,Zᖩъ?BY_}"#}5NB'gA2SulxGCX2}=\ 1]wPZhrrMfX $J5(hx,&$(C lAc#_ĭ{1̰Q:Ls+ȵ RO&}me:$Dti}np"DPIuT5{ Lϋ,y> h:SjX@~N$0ZV͸N6ĆZ }ob,'#$~`d-q{wԘ7G r_^7^ZWN |e?O !т`7bLsq>;5}>;NUVſ.rXp?Xusr'J ADHFkl:?Ը|_'"B8-Hf?tXޙu$}g_"~̋ʁ#\8agǓš޽\td 2} pa d9ŋڹ+?# m`gn:! 3! ] rgl2G6l0P_oolxZCpMΥ߬^#n`"81n_+)3o2\l-;se.L3Ee ;_9sK| O oX? $ xMfTQ 2p}trػ|w_.<@^&%wAe'iP^3&hgkeT )2,g]rRk RJ$2u[{qhE!s^pyV,¯ ve7*e͇6Gp~nAYw8̡ǿO',͇D:m"Lȉ_ĹV^2K9 NZ'emp^t[qhD^'T"/?gV"IYU2 |f{a"[ Jʎ;S/( ԮvwsʊG*˺঒0&/nbTy^gs @ӁG"B3m_+㔆R'e@iCH@ y2}o}^} 5)hJ`3^5h2e*؀ `Gд <VkR]6*6t'pe9?lFRS=tJR22N??0Mmw=s; H \ gg.tF@`vUy6s?B.3ug3jd[]}w Άn&`8O3 /d8Olծ b#E 2&4"&].H[gn#_?{+i Q6(Sz:89 S & nD n^k8aSu8^#߰|Q9W(" S~Rs*)#DYV6P!~Qk/^\ sc?/ڟS@ i_V Uu:ҳhA~cc,`QJnxr-;ۄ*'2"-q?,R DCI.hx@$c4 5{hRFњVۓ,W_oa)bƱep zfd*:zp>n-EH1ͽF헄%XkCp,_ s%%  hyk%T$E! d7M_=S0yijjEŸ_"~py/uX6s2[_aD2Pw ni926HR:s!eZk/QyQݵM#msbI!D%`@Tc7ᮕ_ES6n7DF%h@?T..⹥5\4B# @giuA(eTJ7`tl~L}b&~TMaw6p' `e[xWd i` c$ "NprEIgD 7w6c D2 &E}Fpi} W/GW)ObnS꨺˨g*^4|(q6>EkA' Q ټa .Cٵja.L(Тf @ 40n^+/6v6woEh 2 |P4'<=^A Bn 1#<'$E044MMX/u/q"/C{Ti:ɓ )~ν\3' s9K'HDZٰ09g.U2tqbtC#wz Pq8vukn腮ۈ%EG:L&mF"ş^\ es&_g04i(䭩t`Uu/9G(a LTZO~ XgFq7n*A?8!?rLPEsȡٍ~ikX"29HJn.yޅ% 38s; m[BT\:|~ oh1:lȵ' ;о BvD@k~rb&pgxB3" DQ# 13 $D,#2\ K8K}\& ScvHhj/o{{jO jC&@I(x$?k !WqՃseFm{g?6\1^ֻ )X]?n_+ԾF QrL{Vp7,& H,TEQ=F<|q {J峍2 DYcl3Z$aVJhj:fzшm!L!%90)H('YK x[ebێXI$BH,28w_9}߽a9Msys#"FlTޔ@8_@w>$J+3TmN/!5:}`tb_fB^7F',x#BU6re7 vBKد+3jiV/+/O_KamvTwHẊR<ޜ[&Ӱ lpS=tJ @ 3yOB}O5Q $5%(UAzԿ2缴A92\N?n]_+%۱@wǕ>w/ӗ_ٕ~15B) Dx?޶PGd 9ky;ڱX_aC@ b*Z!I#‚WNqzNejXF,t8yBBfC0y8 @'g~l((f'r 1UŸܙ\?cJ]{z\#j)OUEQ^K3o4y_xFZqp+.N.+] r,E柝N㓳 !agxK=U9ۈuѝ/q=[{&~nb-[1!gwQhr<0|`(y?<)`@@ArэbC4Ϡ K!z on@hV|.R Կ-5̄gÎX"'kl @`*ڄ=`chFB+!0?#Fy'A U9SFZ92;Xp+U$3p?BTU;W}`4uCO'̪/H!' 9yvN"6ԢR x+kނhS|ݼPoX곲(q~T}r9 iBQd'3sP~^Å6>`c%ټ`jb_PTXZ2l 0fBNӁaw>/IX'nĄ>;h(X0L4c<|O1`p\!W "lJvݘ@tҡ9Uk (>W, 5ݾ)=5궲 d@ !.)Hˡ#Gfo!O1+Sxy (?2.WΔ_zHL'8zl9pOc6Rz`= !JSs N*K oÍj; Nx/oQ죥/`87s~C\:rdV'b Y(*>``pA=C |H p ksdc?^j @3ʆ[/$6%q`.V4$ *+?v{(R1jc`R11xC3m2, ;.{Glj%y,+rUVLk?^lk{wC4u,_qBĺc\˴?@1hR'BoZCI!W "lk=S?-uHD3hXOaUoAXD(m7 pkTԻ޻m*G;wLYn~M5D@۬໎>c&䫓 UHZCWMQߗ ߖLkc.ۊ#$\ 䊐SQ؍wϼvQj񗶿Pe^P"={, -_.KnРف9zyg{?F'< zCa#jw'xZ_v)1dx=t˷rU8`M*wX {# $ 4#: @3KO,(cHw-a@)`RZX'`9g)6Ys?< ygg=>6?ۓ}ϱti4#(%AKc>BX[4LcZ(Ҋ}sw& 8}ݷz ̪Z6R T4wN9}/-WԏwUm|P|L.`Qa ?<5N~@iĆjt'Ox?h[G{lcX- Ì碠Th}Q$>3ԕ@9١UfO* TvݰQ/7 2lWwRDbFd`l㶉w=3z!]c&.jI8EG΅?@07оeXKpjoY&cIhoo0 UrR?G y9HT~_ qItpz<$Z~DRU}9W k% P_!Xh߆_ x7]q Y% b^pgf|XT#|.Xi [4 0C,L* `e'7)B G0qh(F ޅ%C\@Cw47yxǸ^(w 7GC+BDHTz ZYx ?@+@T) lo;g=S?I9ST 6 *DERw)!7 p3qF L Z緼pW]vͷ4𛸿W=9F_yc, ]idQnTJAiƋu@*=\;!U YOBbSwM4#1tƭzu`7ߊ:4pg 99gAattrtN%vȑrۦT$ *hb1LtbOpJ,WuԌ 0^W4dmH`S9O=Щ,K)zy^ ]^>L4'n/v2>8똎6U$\lRPզ Z!^5kx=z6!Hr18/.\j\y=ocJ:2B[$hJEP /ʯ% xa\u;{w^e5HkgR\+f6e V&ޱs<!D+`. BIB"܅6L&Ӹ88?^_aXcM2Sɢ v|}D.[B,Cܹ 'evEӡokSHyb/C}SW^orȑٹ;o;p><ێ;- ň)B,"$HTI* h-ڼe{rN ,\֦u`$;v7V8 S(؀ى&pبp\@s7U(B"%&:Z6}HĘK6c]ռ BH+ Zwx<;Ԫ^YV^SA n`#\oҚe]Q^>5We/\m}+9Z~_}*O6 \m ۨeUF"l2͍7%&1T*\H,_yD> 㼛 pf@.t?(k|}.w~_WێVy߳wQ wO¦x Z?1b$DHĐ"B;I0'Ղ鱗AGc xԟFؘ9VpDn,| i_Fg/jf0?\;$ڇ3U4ǩ$(lh!4)2"ʧ9Sվ;{p4N ~Uh5̭$b]4HISn;{ԏZ;[.dbZ]V5g{lw@4T*B `nG9{?8<߸u592۾y|wc/!V &Ph}B9k&1vN[7qr.h[/{Z\&w{Jm;$*p0_#/s-jzm`:_\ ?^a-ϰ)왙ĤK] a:fcgVZO3XVW QI9$rdVacB0ԢQpQG,@me7(m8u+2R⠋a&plsfa ;?M(u*4| :[qcv~'{ǹǀzPahNIIRVYe |Kw& Df B5hI| ѕNS?zhOgX| GN$ DnگvѼ;WH>l\ 2(&"Hĥ *Ew0{V76:ɜrW.[:闚*{fa 'iӗVD| 7q@ff r>~槰eFӄsMm۱؂B'۱iv+ڷp.=ϡ誕"?턢sQ8pY. #˼v$l{( =/PD-;1╡U80Ǎ ҙ6 :b @RHlkƖNӸsa1XTG#reD@iwس8`VB01m#e5 cB)(!R HՅ41(Yfh.F$D2ŷ!s L+#Jȵ*<y(7߆*}D#M &l,.(8h')7qo}U_qyvF9(}zN[&O>>^C+g_BUVR W)~z+uD /qt]q=^3N4blKE9mNE4qZs@cUpG0AX$e9 WAYDbHb":HΛo-#`Bu;mT8;x.:_9e~g`kJ^99iUU5WU bߌ#lNԱ%(/`k="ZsΔtj+{L "늠e BV\׼ wN]S_gb5 ) f) @p6l }fWUC )-um r"fD n'g<:kFNbW18+o@T␯e ϖ+&Z3Ⱦy&Opσ+ A7ʾ]oaaf8FEf|c"BCD܉5~}b5_׾SjAIvۺsRJA3m|Π,C[0ʂy NC1i*ERXPK JVKX֖ep'm%R_0ԟ?\8*(@ʚ rՉ@ї}`]gd 99PhDZIZӕA)4Y,"4-܃[b:C)Hi`w>ͯ_71|}m}r 5&$!}S2T0 j+C[Cݹv]h=>S w~+iniw) 6vN8,vڔ_|~/1/ g*A=!v7M_02W}ދ+"p1E֌:Q9 ٵ!9RG—R/10tevYv4mݸub/v4-~G vђ~h m;\ %{ۘuKԱ}=1c}$ RY ݼ&nG[6Fd3B'ig~N@ >r)<6)i\ XAF궩x#"\"!k82C_:bx,SGSAx3d<ǥa^X`a;称e$;L'+/:i(Д-!pSt'vn,~}+j )3tj۫wyr݌FkcuO/Jkm) 1l9e7s2jݒ@~ `T|ɶݐFൠ'c|owf~ vK0A0}˪P~UD̜O5"mGGWї3\l9_tꋜݜp⅕)&-`;ͨd7c{z4&R"hXسk,7,`e$ wגߋ'@lM=FGg=G 4mE(7m0k_ދ+)Ȉ< &|U3Էö2 pNpGEk퟈£D<DR` S/8+`Oʡ4+I10I0D C,9^\bt o2v,Ezl@\zƤRfܵt4`};v0Y<N5RMY)H,-YuB$B:sxZ*Tm~He6~_{(*F߷gqӄWnOmsvUS|ك\ܰBBh:GEi|_c<Э2IDAT+ G l1UGbi,,@Id xK4q;Ď޹eVBڱٺH-0}X4ЈLc׷oE?q[8})ligOĎVN"D5}?@XCs3H 9dUt(3PĮ5sC|gOl?&fP!*&xX Lu3!7< gOjshw~pֻ_@[הx"0K\b֟io_*chpQ(L|ѱdY٪Xf hL&\ٕ>N 1݈0E86O&o)\eq7f['Dx+6%۰bf)Yx-|KE>s#dԛg;41-tzJ?G%PAmfWB9{=C{V=`;T> ޥ6 zmL\cuEA $vNzЩW/n[Rr 賦:(nx\ղbJ3 z)Cd9Ј#"=0x59T?VS,[[[76 eZ+#WhSK^|)Pͧ;#- 4cOxoChveॕ䅋&Zo\ЁC=@캜3>g5k,mc=;;ؙ?8, X;b9)R^%% 0$+l)\ L)xٕ sٶvIa(E(L}]A#m\׼[7qN\\ċgq9?+yKO#>șόmԟy:(lxco·AD<ءVyEȽ3.u?僞r5?,0_M )D(Et›@ N>2ʫ~iu9u)<.S 2`H@,dAD@mVfSMVl/`{z,#i܎hZ-[_{BqW_\Ws 9i{-#(o ire l\m6׾>CL=Xg]  /A SmNZކp="_*K}86ʰrdhhwQ)|6`&Ylo w`{:\׺ ;7bGr&c"FN)g̵4~bm50;͑9wĜ -Sx/XY$1_={ٻƅ_w<1Be?x`<㎱n3/!~m" 6Mc8_j.@X;|6ݟ+t)*tT@BʲQ\P{C\ m nyz3&&Ӎ&"DUAq#v6 F/4^=×У5Na1/a̮'v఩Op'fY?i:@o;szF_c/5Ld9Z_q PAR_$$Į /_:_e,_ ljZ]D 6vWVSr|o!yId%r'[U2PCeЊ&0 Ι͓wa []ؑ܀ M!UCfur?z5oo[3BBZ{g?h}.DV3`o&3p O+ԟi{pN]LY"I?zȯ6PLkvbq[ck/-(:7P_r{FBnu+ApaS9RCCE BR^~3/k65Íwwbavh^x+E) _ sBnmdI}bs`M uCni d25K5Tm:o??23YȿNa߀HIʾE, n<_+T^E$2U9LIH y냅` )\xn9x~mWPQY'Y1.' L,n {ڷ`>ށ͘іlҠ¹.z+C_9r~ m{?h2 ߵ^Z0=A6>@8?G k0@P@hHXL Iܼevq*翮҆_g,Nrw~ROz. ]|e߰uQ8p΍d1fCJt1ni㶹 lh"I,NوoӚٯI8XcvM-٨ϕ|g4Ӌ8) #q@@:_-Ksr1|] \T~o_ZC#484[HbfJ!rGOʇ1!;lJщp^tf[9f+8JZII@gCv$t1L0*~ “dQnvqzuo_Ǝ&nmcb,IR f9TڄjW#ag@~D4̫@; 5Y|姠A,g|i\KHiZ+xP y1"oCZr2Thv߾115~\^ҫ?JA% JΉD#c~sz=QET8=,]A *j"o^0 Ba,g reEXNs\fxvX>vNmv9QT8DɂrB@@_I2 =&? CM*Xy|;r{0T<^AXVp9%SHTa0LeZYU$Q0Y'CP+/bpM CpZ nA/P?ߟCX \77ɻ_-y|Mǿ@Ks;G쓅ObY;s r'_su^qb@v,ю#"T@#i%1{BYM(YiF;5Y39 Y*" Wpnx 30|zrZ)t[=d ~_|kZoWo >7|\P+(xLٔHl۷~WK_sˍix"ʉMiDg(zzAW4=WAY|M*$,iz\0W(nFet͗1ĘLbiaL[ ZqbASZ?d lxg#="4uNh8N" ^ou  P JٔߨR ('e?WU#f99(ӡl:Bduy(VPbkY`YB?#RD8ff0Ljl+\+\K`$l# ^|wgi B\jSz}v#Ɩ%*t?d}\m!$LIKW~M%)DB )(f7O.uG~h5U(k\ˠH#P\cfUgg縂˴'i >E(a<8@U (gO.<XVL RIJH$N"6"$*qv ln5k4 m5fJeہ#,XCPkY?>WX;)yXg e~6೨$nnzq ҃nҋ}I5&uXG fs}0*rC}w=l?~}~vn&tO\3T"asVĀnnaDcx˅6L__Y/㛧tX>; =VsaI>Z)j(ߦn,|}ܽ @{urCԣ E%/Bf qJNpM-*{kbt;ZZ_)'XaY?a|7yxYXD,1MT?}'ѐƆqC? f a[k@&@R[jWʘT1~Էal{(3 l<0# !eOZ>(Mia>4Z2@1TI6#4"MV1ܷUm_~4u龻o=Ծ"Q"ǧ/E!n:6+_k{Ø7=l>2g7_BHMK/9( C)9ߧ9>6&9Fl a* 5ufv,`2ɱFCFHbF$N$10KDMJ3*%T5uy>_N6M4d_*?  _q<7zȸ~EP$s=އzk-oo8c@VaF9&ஃpcr)!pDs>tFDHq$H M)ЊbȒ9Tl}Dϟ/_w\U[ ( `~%@H:>Iw7|k![~yCꃏ?waCQ's {}ހ9b\A^q.LZ2чFMM0KlJHd@TNv˂7$%+RyJq /1p" ;{?ZŦ>z/W{sn>q|!qЁF#a!gbJ wxY'}ƣӶ>vsm+`3ɞ&~$@HN,c')@J"L%&cXb:!L']InCGz݄ʇ|j}{>&# ^Z'd#[g=?x݄x hʗtޯ^D 4 2p?QsQ{!>Ӂ&יVܓ}`s0}WǴ$Flj+y8x,AkbhB,0å~N-VsnQ9 &$ Mqna@e XR~3xq-bO,|eN-3ۭjO~L~q A +nD|U]<Ǟ9wpm z/ ~8U ֈR`)ҳ30j O39=z}Kѯ|yC|?x/*ޯLӾ.VgP@ӱ~QR^w4BΟ9 䃈:}瞊<ؘ3n ]Z3WVl!(`U_As_AD.GX_1Ihn:;>=OS:KJ8F_zBr[7LJ~h^ z 0R:,"(pU(&8 ?o虵0g$n, 1x_̾ T.vރ9c!Ј"Y31|jZ7? Nv 7Z0p0P;o?1<wZտlPhӑ'KUJ|k*w/?x7 w?}k9'l" 5(:$#M6D3ﯱ] 9C &cLSـgWjJCZFЫ?/̳' ŗ|=G:XT ve\aJwzB.=onEZס98F1P! P:!nьǫ[^Yq|Cb  ïѧSCWU1Ǧ6o݇~u Օ7l,ۇf|>4FQ|Mہ6  TTC,y7 c N?iByMgaP  Rcʼn82o#OeP?_ܽ q;lFޡ*5Z?hN?"':J`jo~q4nK~uLYa3)CV*~',<\C,Q)A0|M #gm+j~ZgWAa/O0Pe>g[fL,A|n3LJl{ls'z0 c9g`@# 2RXͳD!_^_0adދL,q\`L (ur/UgW迯KP5 xV~8[6%>ϿNpaUɽ|P!O q2 Iu><' @ p GU5+8X0}Lᙆ/7L$B3p]gjiS땟_*0e ӸfYqY-oCBϷ z T츠9  9pM[_bge?e`' `sG }ծoBTӐ$ƍ[oR>uרD||}K*1 EG mN{@aڿĎqwTY_ h- XgF́*(^-= ӑbH0Y|>;\ow@>9 ?*C|Rv1R#L6ܶsbg ?JHOؒhZE\R?T)U x_04X,:߿j@@/m ӱ6"fN-1&=r_$  Van>19_d`Ք7uzС#O q@MYtyqꌬw>/ 0`eNf[)H;z5Orxvb|HOp_~:o;> o{ 1. ('wP ,vLO,޺y葇˪7@]<l_d fDT^k 8S>m&Qmg~JޕHGvarNE6Q )(uivS'C 9t{X%Y%kp]I+$!K> Rf4`BC0a!D<[cw*~ul!Vw ^ʋxuR\)~[40}.׎|?sfSb'!BchQ ZvVw.YdݶO2^` |6 ]}Afr}Z%~ݮQPHy}(E'?o6ݲm5Э0~>ø4aҸ7`νDO/\A]4aZ> Ѐ[cGmz}7e/sݲʣi):F-pk&<>w3B`` {LG*EҜny$@KMu3`v B ,'߶osm`Ң]{J-g! >9x x,>?E x/ڃ}2CbVGB>K3z,7,b$#x9QqeGλi\:w\D0V05qTO]s2/# 4N Tts~0rAB=G[tn4a2dQd{eqzdK#Gqb`[p{`s-pIF~0Vqj`gz!4w)"Rq@-ZVs"!ziAK)/TM^h!4a(_&z 0w\ݑco'$swn}BQ f9vEvH|Ն5iZN- vBMjymGvᴊc=qwJvB?)7[{]$ |x᳏Xoh9}.o{'v;xX^>k.*\Ph53]ZjU}BS(Y;\8? ѯ~miCA} hQvdGԗd=Ix$xkVK|>PϪϕn#!]#4N/ךQir.5f3k{;W/$p_ײn?^CcCb~R( 8:3϶._%ziڠsOɦe٩c1<'v3PϹGi' P b fԅ ~zvgr 2]7[hYAA`$ h;3$Î0R𳷋ȨQWaݴ0:o P\%nn+&V][Ϛpp@&xvz,vD2s:i?+@u`"BTCQړTzN7kZ1tm SI!c @lSi•)ۆ*TM1RRmnY^N~ok&jMdj$ (wyضF!X)3rj ) y\P aSdzIg_6[Ŧ:~=DH7ܽaG!Ys DH &65f&|fْfN|gI$e~L>nBqHa|Jv3"TeI+>K FLQä͖M!jtBB1vs I[vJR>jU)TBbo.8 S&&2- lP|ar vFV 4aFZFFA5&f)%F*ziLŠaEkhVlТ  ʈDr*#ix $H A $H GS\5!tEXtCopyright(c) Stephen Rhodes 2023 %tEXtdate:create2023-01-04T19:57:16+00:00\nl%tEXtdate:modify2023-01-04T22:47:12+00:00h{tEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/recording_hi.png0000644000175000017500000000066314355557026022326 0ustar stephenstephenPNG  IHDRJLsRGBgAMA a pHYsodHIDATHKAN@)d bBB@'-]xMX{,4(!!1,414v6o3L:L{c*j­=㲳ϟaz%#F0{&&_ۻ,CѸjx[OtdLpuٚE N#R'ZvCJkV~w/N%ʉtb'K1jK (%Q*:AQJRPۛQBY::WA(F0hTgVIENDB`libonvif-1.4.4/onvif-gui/resources/unchecked.png0000644000175000017500000000067514355557026021626 0ustar stephenstephenPNG  IHDR +tgAMA a cHRMz&u0`:pQ< PLTEtRNS@fbKGDf |d pHYsodtIME .4]IDATc ep``$ ofR!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:46:52+00:00~'[tEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/apply_lo.png0000644000175000017500000000036414355557026021507 0ustar stephenstephenPNG  IHDRJLsRGBgAMA a pHYsodIDATHKcJS ]{?ɐl7 JZD6k0bLNBxQE9` -\AlZBϐO @I Fe= @l` (GܞbIIENDB`libonvif-1.4.4/onvif-gui/resources/unchecked_lo.png0000644000175000017500000000067514355557026022320 0ustar stephenstephenPNG  IHDR +tgAMA a cHRMz&u0`:pQ< PLTEvַtRNS@fbKGDf |d pHYsodtIME /$o IDATc ep``$ ofR!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:47:07+00:00bdmtEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/checked_lo.png0000644000175000017500000000075014355557026021747 0ustar stephenstephenPNG  IHDR [AgAMA a cHRMz&u0`:pQ<PLTE› tRNSkk`bKGDo pHYsodtIME &)3IDATcp `S" L19)#H bQhpROf0!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:38:26+00:00^/atEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/checked.png0000644000175000017500000000076414355557026021262 0ustar stephenstephenPNG  IHDR [AgAMA a cHRMz&u0`:pQ<PLTE߿*tRNS⨨6 XbKGDޕz pHYsodtIME & hD3IDATc( `SL1)s\IC)H:[bWh(R; }!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:38:09+00:00kXtEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/record.png0000644000175000017500000000061314355557026021143 0ustar stephenstephenPNG  IHDRJLsRGBgAMA a pHYsod IDATHK0 :uCA`Cgг.މIJRoB= Wg yL*r:$*dnc>G:Y,;bG.d2O ӕ3*РU:L'dzKxn? ٴArl!«F `l> a:Yu= 9> >8BQb:썬]^~qs A!s[pc_T޴lOMͥ[D3b a4IENDB`libonvif-1.4.4/onvif-gui/resources/discover.png0000644000175000017500000000161614355557026021507 0ustar stephenstephenPNG  IHDRJLsRGBgAMA a pHYsod#IDATHKVKR@R/S7AY@e,5g, U|WSe~= \뗷O~a>#Qʰo6nSgtn-JDKyVN֚ ڒ "_቙ah=OmBpMI][-٬e;|9|ȂwS7I " tr$i9w:z~suQWԡ<=P=^!e(+Ϻ>6.u}]9lKqXa9`Rbs1' yK2=s?lD3.y*iٔ߿T|i]X!^6uEG ?0mɣ$p"M"CeY%Ke/sw( D,DY_ 10%,t@nh04p^e\Eqְ38{$D@&a .6&_t3/Ջc 0|ݰt0Ni?Z rIENDB`libonvif-1.4.4/onvif-gui/resources/audio_lo.png0000644000175000017500000000076114355557026021464 0ustar stephenstephenPNG  IHDR$]gAMA a cHRMz&u0`:pQ< PLTE9tRNS@fbKGDf |d pHYsodtIME $!<HIDATוϻ 3 dVWQ§8`"JSbejJbC Ӛ=]yrUCxNG!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:36:33+00:00]0tEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/stop_lo.png0000644000175000017500000000067614355557026021355 0ustar stephenstephenPNG  IHDR$]gAMA a cHRMz&u0`:pQ< PLTE9tRNS@fbKGDf |d pHYsodtIME .$9IDATc` : MAy!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T16:12:10+00:00o(tEXtdate:timestamp2022-12-09T16:46:36+00:00^ tEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/unchecked_hi.png0000644000175000017500000000065014355557026022277 0ustar stephenstephenPNG  IHDR ~aJgAMA a cHRMz&u0`:pQ<tRNSv8bKGD݊ pHYsodtIME .;IDATcA? =`C!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:46:59+00:00ystEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/pause_hi.png0000644000175000017500000000065114355557026021464 0ustar stephenstephenPNG  IHDR1qcgAMA a cHRMz&u0`:pQ<tRNSv8bKGD݊ pHYsodtIME )DIDATc`~* mApA!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T16:10:58+00:00R(tEXtdate:timestamp2022-12-09T16:41:08+00:002mtEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/radio-off_hi.png0000644000175000017500000000075414355557026022221 0ustar stephenstephenPNG  IHDR |lgAMA a cHRMz&u0`:pQ<bKGD̿ pHYsodtIME +  dIDATu0 4A#)$Ж#>ă9}wvSհSmzÍ jD.p\>~gl9ދUO1 S B!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:43:09+00:00;tEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/discover_lo.png0000644000175000017500000000165114355557026022200 0ustar stephenstephenPNG  IHDRJLsRGBgAMA a pHYsod>IDATHKVQnA "!G܃D+$M@$hW/$"5#{Dcl~xRx=i8_s3ˏ{/\{A̫a/,f΁{DSAz=,1_NNN K:D1db(e >gBe[s TJ#qAՠ!dH\Rٗ|"6rSPpHY=ΕƐ/<ڕO|"HX#cT"JIЙCH!E@Q;DAzPU2z1lș82,>PI}R\}O*P[e/0 s b.86H%ȴ⪈~(OfeS栧FIΔT2hAILBYh.R6Tm@l05p [98jSx=XS5i#ʄh&{s!b4 1"j }^o[K?@|JSȶejoV"fiBFDxrFBR"Bv ;Jԁlj>h6LP1^ʫ"ՆV+J*dFeךh=fԩb{Mo1Ҩ:@qRv|q !D6dU'd+:cA[6WDbAd*DwTt2߬ Bxƈѫ،:s)p*hΣ\6WBѴXt&"tV9YYįm$i._z˳fav FH Mw2C>}IENDB`libonvif-1.4.4/onvif-gui/resources/mute_lo.png0000644000175000017500000000100314355557026021323 0ustar stephenstephenPNG  IHDR$]gAMA a cHRMz&u0`:pQ< PLTE9tRNS@fbKGDf |d pHYsodtIME '4SfZIDAT-ϱ 0DC }7W β ( ZڊrAI(w<2e&(La/7^`r;;T!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:39:52+00:00ONiUtEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/small_arrow_up_lo.png0000644000175000017500000000070714355557026023411 0ustar stephenstephenPNG  IHDR l\VgAMA a cHRMz&u0`:pQ< PLTEҘtRNSbKGD L pHYsodtIME .ZIDATc` \+VE$ !tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:46:00+00:00H8tEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/small_arrow_left.png0000644000175000017500000000072514355557026023225 0ustar stephenstephenPNG  IHDR @gAMA a cHRMz&u0`:pQ<PLTE說=[tRNS T)bKGDo pHYsodtIME ,; -N IDATc` vq``b(79++!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:44:59+00:00tEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/LICENSE0000644000175000017500000000336214355557026020170 0ustar stephenstephen/******************************************************************************* * * The files in this directory are copyright protected by the following license * * apply.png * apply_hi.png * apply_lo.png * audio.png * audio_hi.png * audio_lo.png * branch_closed.png * branch_closed_hi.png * branch_closed_lo.png * branch_open.png * branch_open_hi.png * branch_open_lo.png * checked.png * checked_hi.png * checked_lo.png * discover.png * discover_hi.png * discover_lo.png * mute.png * mute_hi.png * mute_lo.png * onvif-gui.png * pause.png * pause_hi.png * pause_lo.png * play.png * play_hi.png * play_lo.png * radio-off.png * radio-off_hi.png * radio-off_lo.png * radio-on.png * radio-on_hi.png * radio-on_lo.png * small_arrow_left.png * small_arrow_left_hi.png * small_arrow_left_lo.png * small_arrow_up.png * small_arrow_up_hi.png * small_arrow_up_lo.png * stop.png * stop_hi.png * stop_lo.png * unchecked.png * unchecked_hi.png * unchecked_lo.png * * Copyright (c) 2022 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ libonvif-1.4.4/onvif-gui/resources/branch_closed_hi.png0000644000175000017500000000101714355557026023132 0ustar stephenstephenPNG  IHDR &DgAMA a cHRMz&u0`:pQ<9PLTE4tRNS 9bbKGDa pHYsodtIME % >U/IDATc`F09x4/?/-ɱ򱁕e!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:37:13+00:00s\tEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/play.png0000644000175000017500000000074714355557026020642 0ustar stephenstephenPNG  IHDR[dQgAMA a cHRMz&u0`:pQ<PLTE4]tRNS@bKGDhQ pHYsodtIME )8St6IDATc`0@pPxƆ(<k…&c3i!Nf-:!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T16:24:23+00:00W(tEXtdate:timestamp2022-12-09T16:41:56+00:001tEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/small_arrow_left_lo.png0000644000175000017500000000072514355557026023717 0ustar stephenstephenPNG  IHDR @gAMA a cHRMz&u0`:pQ<PLTE說X+tRNS T)bKGDo pHYsodtIME -) IDATc` vq``b(79++!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:45:17+00:00;tEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/discover_hi.png0000644000175000017500000000152114355557026022162 0ustar stephenstephenPNG  IHDRJLsRGBgAMA a pHYsodIDATHK?z@w}R&7abaAnPN࠼hG^ IE v]¿XIZ&TTii-#@, W$oZ$*.bS\`c 7%9C=*oA*$9mnb=:ɘ( _1cfh:ھͷѨ*:b͘Bw[oS-E֓Щ~wwYot@)y@ mkLTqFIg=YC&ufPlwt2IR (r3nQpظ}g=8qv_!ClIENDB`libonvif-1.4.4/onvif-gui/resources/apply.png0000644000175000017500000000037414355557026021016 0ustar stephenstephenPNG  IHDRJLsRGBgAMA a pHYsodIDATHKcJS ?`7 JZD6k0b[LNTAxQE@K)[K ` .dhs5jiP Ul>C>%%1gɛ@Iu M4F/վIENDB`libonvif-1.4.4/onvif-gui/resources/small_arrow_left_hi.png0000644000175000017500000000072214355557026023702 0ustar stephenstephenPNG  IHDR @gAMA a cHRMz&u0`:pQ<PLTE說wetRNS T)bKGDhQ pHYsodtIME -Y IDATc` vq``b(79++!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:45:07+00:00ftEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/recording_lo.png0000644000175000017500000000102414355557026022330 0ustar stephenstephenPNG  IHDRJLsRGBgAMA a pHYsodIDATHK1K@]cb(m:M\DAs'qwig'w nUpppE:9i@Ӧ;s3x 6Q=˕9T AP0|G^~W]BH @c$TVcuj8 %el% W`c+W$YѭٶC92RAq(28ĘDc&/`iD61sc3zQ s3 Q^(bQ ?LATMT#Pv[scs#h˴xafE.F6wl3,=)}Ivu0Xr"jl4x^2bHE$$vXA8 g1ͣNnc+F>cWjC''/ p~-! Y aZIENDB`libonvif-1.4.4/onvif-gui/resources/radio-on_hi.png0000644000175000017500000000077114355557026022062 0ustar stephenstephenPNG  IHDR |lgAMA a cHRMz&u0`:pQ<bKGD̿ pHYsodtIME +$dJ|qIDATu 0ǩ#pnca;9} XLUƆiX@8 darkstyle.qss apply.png apply_hi.png apply_lo.png audio.png audio_hi.png audio_lo.png branch_closed.png branch_closed_hi.png branch_closed_lo.png branch_open.png branch_open_hi.png branch_open_lo.png checked.png checked_hi.png checked_lo.png discover.png discover_hi.png discover_lo.png mute.png mute_hi.png mute_lo.png pause.png pause_hi.png pause_lo.png play.png play_hi.png play_lo.png radio-off.png radio-off_hi.png radio-off_lo.png radio-on.png radio-on_hi.png radio-on_lo.png record.png record_hi.png record_lo.png recording.png recording_hi.png recording_lo.png small_arrow_left.png small_arrow_left_hi.png small_arrow_left_lo.png small_arrow_up.png small_arrow_up_hi.png small_arrow_up_lo.png stop.png stop_hi.png stop_lo.png unchecked.png unchecked_hi.png unchecked_lo.png onvif-gui.png libonvif-1.4.4/onvif-gui/resources/radio-on_lo.png0000644000175000017500000000102014355557026022060 0ustar stephenstephenPNG  IHDR [AgAMA a cHRMz&u0`:pQ<PLTEݝžtRNSU9q}bKGD ٥ pHYsodtIME +.bKIDATc``h```Q`4s``+```W`fhf0d0d0bhhQ Aa&dV#F&TYV!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:43:46+00:00IȒtEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/branch_open.png0000644000175000017500000000076114355557026022147 0ustar stephenstephenPNG  IHDR gAMA a cHRMz&u0`:pQ<*PLTE>dtRNS 2{bKGD a pHYsodtIME %&!IDATc` Z]@mX}Ox!tEXtCopyright(c) Stephen Rhodes 2022 +%tEXtdate:create2022-12-09T14:29:58+00:00l%tEXtdate:modify2022-12-09T14:29:58+00:00}O(tEXtdate:timestamp2022-12-09T16:37:38+00:003/tEXtLicenseGPL-2+cIENDB`libonvif-1.4.4/onvif-gui/resources/recording.png0000644000175000017500000000063014355557026021640 0ustar stephenstephenPNG  IHDRJLsRGBgAMA a pHYsod-IDATHKq0E7 =-@p"$'G P?k ,+7ތG+ZiI*/|hq7oϸ>ּV,&8 "?>w4uj ,UW&6iy+ŕ$TFW"9\ !Fa_\$s{\/]6!+i=?B٩ 5栽hl^) pU[0K ),qkv3|mA{} "ntpKE jQ=O#']@=IENDB`libonvif-1.4.4/onvif-gui/docs/0000755000175000017500000000000014355414521016065 5ustar stephenstephenlibonvif-1.4.4/onvif-gui/docs/manpage.txt0000644000175000017500000001217714355414521020246 0ustar stephenstephenNAME onvif-gui - query and adjust onvif compatible cameras SYNOPSIS onvif-gui DESCRIPTION GUI program to view and set parameters on onvif compatible IP cameras. Double clidcking the camera name in the list will display the camera video output. Camera parameters are available on the tabs on the lower right side of the application. Once a parameter has been changed, the Apply button will be enabled, which can be used to commit the change to the camera. It may be necessary to re-start the video output stream in order to see the changes. Video: Resolution - The drop down bos is used to select the setting. Frame Rate - The number of frames per second in the video output. Gov Length - This is the distance between key frames in the stream. Bitrate - The maxmimum number of bits per second to transmit. Image: All values are set using the sliders Brightness Saturation Contrast Sharpness Network: If the DHCP is enabled, all fields are set by the server, if DHCP is disabled, other network settings may be completed manually. IP Address Subnet Mask Gateway Primary DNS PTZ: Settings pertain to preset selections or current camera position. The arrow keys, Zoom In and Zoom out control the position and zoom. The numbered buttons on the left correspond to preset positions. The blank text box may be used to address presets numbered higher than 5. To set a preset, position the camera, then check Set Preset, then click the numbered preset button. Admin: Camera Name - Sets the application display name of the camera based on the camera mfgr and serial number. Set admin Password - Can be used to change the password for the camera. Sync Time - Will reset the camera's current time without regard to time zone. Browser - Will launch a browser session with the camera for advanced maintenance. Enable Reboot - Will enable the reboot button for use. Enable Reset - Will enable the reset button for use. Use with caution, all camera settings will be reset. Config: Auto Discovery - When checked, the application will automatcally start discovery upon launch. Multi Broadcast - When checked will repeat the broadcast message a number of times. Common Username - Default username used during discover. Common Password - Default password used during discover. EXAMPLES To change the video resolution of a camera output, Double cick on the camera name in the list. The camera video output should display in the viewer. Select the Video tab and use the drop down box labelled Resolution. Upon changing the selection, the Apply button will be enabled. Click the apply button to make the change. The stream will stop and may be re-started by double clicking on the camera name. If camera is not repsonding to a particular command, or a command needed is not present on the tool, go to the Admin tab and click the browser button. This will launch the browser using the camera IP address. Log into the camera and settings should be avialable in native format for the camera configuration. COPYRIGHT Copyright (c) 2020 Stephen Rhodes This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. SEE ALSO There is a command line version of this program included with the libonvif package which will implement most of the same commands. It may be invoked using the 'onvif-util' command. NOTES Camera compliance with the onvif standard is often incomplete and in some cases incorrect. Success with the onvif-util may be limited in many cases. Cameras made by Hikvision will have the greatest level of compatibility with onvif-util. Cameras made by Dahua will have a close degree of compatibility with some notable exceptions regarding gateway and DNS settings. Time settings may not be reliable in some cases. If the time is set without the zone flag, the time appearing in the camera feed will be synced to the computer time. If the time zone flag is used, the displayed time may be set to an offset from the computer time based on the timezone setting of the camera. If the camera DNS setting is properly onvif compliant, the IP address may be reliably set. Some cameras may not respond to the DNS setting requested by onvif-gui due to non compliance. Note that the camera may reboot automatically under some conditions if the DNS setting is changed from off to on.libonvif-1.4.4/onvif-gui/docs/onvif-gui.10000644000175000017500000001256114355414521020057 0ustar stephenstephen.\" Text automatically generated by txt2man .TH onvif-gui 1 "09 November 2022" "" "" .SH NAME onvif-gui - query and adjust onvif compatible cameras .SH SYNOPSIS .nf .fam C \fBonvif-gui\fP .fam T .fi .fam T .fi .SH DESCRIPTION GUI program to view and set parameters on onvif compatible IP cameras. Double clidcking the camera name in the list will display the camera video output. .PP Camera parameters are available on the tabs on the lower right side of the application. Once a parameter has been changed, the Apply button will be enabled, which can be used to commit the change to the camera. It may be necessary to re-start the video output stream in order to see the changes. .PP Video: .PP .nf .fam C Resolution - The drop down bos is used to select the setting. Frame Rate - The number of frames per second in the video output. Gov Length - This is the distance between key frames in the stream. Bitrate - The maxmimum number of bits per second to transmit. .fam T .fi Image: .PP .nf .fam C All values are set using the sliders Brightness Saturation Contrast Sharpness .fam T .fi Network: .PP .nf .fam C If the DHCP is enabled, all fields are set by the server, if DHCP is disabled, other network settings may be completed manually. IP Address Subnet Mask Gateway Primary DNS .fam T .fi PTZ: .PP .nf .fam C Settings pertain to preset selections or current camera position. The arrow keys, Zoom In and Zoom out control the position and zoom. The numbered buttons on the left correspond to preset positions. The blank text box may be used to address presets numbered higher than 5. To set a preset, position the camera, then check Set Preset, then click the numbered preset button. .fam T .fi Admin: .PP .nf .fam C Camera Name - Sets the application display name of the camera based on the camera mfgr and serial number. Set admin Password - Can be used to change the password for the camera. Sync Time - Will reset the camera's current time without regard to time zone. Browser - Will launch a browser session with the camera for advanced maintenance. Enable Reboot - Will enable the reboot button for use. Enable Reset - Will enable the reset button for use. Use with caution, all camera settings will be reset. .fam T .fi Config: .PP .nf .fam C Auto Discovery - When checked, the application will automatcally start discovery upon launch. Multi Broadcast - When checked will repeat the broadcast message a number of times. Common Username - Default username used during discover. Common Password - Default password used during discover. .fam T .fi .SH EXAMPLES To change the video resolution of a camera output, Double cick on the camera name in the list. The camera video output should display in the viewer. Select the Video tab and use the drop down box labelled Resolution. Upon changing the selection, the Apply button will be enabled. Click the apply button to make the change. The stream will stop and may be re-started by double clicking on the camera name. .PP If camera is not repsonding to a particular command, or a command needed is not present on the tool, go to the Admin tab and click the browser button. This will launch the browser using the camera IP address. Log into the camera and settings should be avialable in native format for the camera configuration. .SH COPYRIGHT Copyright (c) 2020 Stephen Rhodes .PP This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. .SH SEE ALSO There is a command line version of this program included with the libonvif package which will implement most of the same commands. It may be invoked using the 'onvif-util' command. .SH NOTES Camera compliance with the onvif standard is often incomplete and in some cases incorrect. Success with the onvif-util may be limited in many cases. Cameras made by Hikvision will have the greatest level of compatibility with onvif-util. Cameras made by Dahua will have a close degree of compatibility with some notable exceptions regarding gateway and DNS settings. Time settings may not be reliable in some cases. If the time is set without the zone flag, the time appearing in the camera feed will be synced to the computer time. If the time zone flag is used, the displayed time may be set to an offset from the computer time based on the timezone setting of the camera. .PP If the camera DNS setting is properly onvif compliant, the IP address may be reliably set. Some cameras may not respond to the DNS setting requested by \fBonvif-gui\fP due to non compliance. Note that the camera may reboot automatically under some conditions if the DNS setting is changed from off to on. libonvif-1.4.4/onvif-gui/src/0000755000175000017500000000000014355557026015734 5ustar stephenstephenlibonvif-1.4.4/onvif-gui/src/mainwindow.cpp0000644000175000017500000001112014355557026020607 0ustar stephenstephen/******************************************************************************* * mainwindow.cpp * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include #include "mainwindow.h" #include #include #include #include #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { Q_INIT_RESOURCE(resources); QIcon icon(":/onvif-gui.png"); setWindowIcon(icon); setWindowTitle("onvif-gui version 1.4.4"); settings = new QSettings("libonvif", "onvif"); //settings->clear(); messagePanel = new MessagePanel(this); glWidget = new avio::GLWidget(); connect(glWidget, SIGNAL(msg(const QString&)), this, SLOT(msg(const QString&))); styleDialog = new StyleDialog(this); settingsPanel = new SettingsPanel(this); cameraPanel = new CameraPanel(this); filePanel = new FilePanel(this); tabWidget= new QTabWidget(); tabWidget->addTab(cameraPanel, "Cameras"); tabWidget->addTab(filePanel, "Files"); tabWidget->addTab(settingsPanel, "Settings"); tabWidget->addTab(messagePanel, "Messages"); setMinimumWidth(840); QWidget* layoutPanel = new QWidget(); QGridLayout* layout = new QGridLayout(layoutPanel); split = new QSplitter; split->addWidget(glWidget); split->addWidget(tabWidget); split->restoreState(settings->value(splitKey).toByteArray()); connect(split, SIGNAL(splitterMoved(int, int)), this, SLOT(onSplitterMoved(int, int))); layout->addWidget(split, 0, 0, 1, 1); setCentralWidget(layoutPanel); QRect savedGeometry = settings->value("geometry").toRect(); if (savedGeometry.isValid()) { setGeometry(savedGeometry); } else { QList screens = QGuiApplication::screens(); QSize screenSize = screens[0]->size(); int x = (screenSize.width() - width()) / 2; int y = (screenSize.height() - height()) / 2; move(x, y); } StylePanel *stylePanel = (StylePanel*)styleDialog->panel; const ColorProfile profile = stylePanel->getProfile(); applyStyle(profile); } MainWindow::~MainWindow() { } void MainWindow::closeEvent(QCloseEvent* e) { Q_UNUSED(e); glWidget->stop(); settings->setValue("geometry", geometry()); } void MainWindow::msg(const QString& str) { std::cout << str.toLatin1().data() << std::endl; messagePanel->msg->append(str); } void MainWindow::onSplitterMoved(int pos, int index) { settings->setValue(splitKey, split->saveState()); } QString MainWindow::getButtonStyle(const QString& name) const { if (styleDialog->panel->useSystemGui->isChecked()) { return QString("QPushButton {image:url(:/%1_lo.png);}").arg(name); } else { return QString("QPushButton {image:url(:/%1.png);} QPushButton:!enabled {image:url(:/%1_lo.png);} QPushButton:hover {image:url(:/%1_hi.png);} QPushButton:pressed {image:url(:/%1.png);}").arg(name); } } void MainWindow::applyStyle(const ColorProfile& profile) { if (styleDialog->panel->useSystemGui->isChecked()) { setStyleSheet(""); return; } QFile f(":/darkstyle.qss"); if (!f.exists()) { msg("Error: MainWindow::getThemes() Style sheet not found"); } else { f.open(QFile::ReadOnly | QFile::Text); style = QString(f.readAll()); style.replace("background_light", profile.bl); style.replace("background_medium", profile.bm); style.replace("background_dark", profile.bd); style.replace("foreground_light", profile.fl); style.replace("foreground_medium", profile.fm); style.replace("foreground_dark", profile.fd); style.replace("selection_light", profile.sl); style.replace("selection_medium", profile.sm); style.replace("selection_dark", profile.sd); setStyleSheet(style); } } libonvif-1.4.4/onvif-gui/src/discovery.cpp0000644000175000017500000001717314355557026020460 0ustar stephenstephen/******************************************************************************* * discovery.cpp * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include #include "discovery.h" #include "camerapanel.h" Discovery::Discovery(QWidget *cameraPanel, SettingsPanel* settingsPanel) : cameraPanel(cameraPanel), settingsPanel(settingsPanel) { thread = new QThread; moveToThread(thread); connect(this, SIGNAL(starting()), thread, SLOT(start())); connect(this, SIGNAL(stopping()), thread, SLOT(quit())); connect(thread, SIGNAL(started()), this, SLOT(run())); connect(this, SIGNAL(msg(const QString&)), CP->mainWindow, SLOT(msg(const QString&))); running = false; loginDialog = new LoginDialog(cameraPanel); connect(this, SIGNAL(login(Credential*)), cameraPanel, SLOT(showLoginDialog(Credential*))); connect(this, SIGNAL(found(OnvifData*)), cameraPanel, SLOT(receiveOnvifData(OnvifData*))); } Discovery::~Discovery() { if (running) stop(); thread->wait(); } void Discovery::start() { mutex.lock(); running = true; mutex.unlock(); emit starting(); } void Discovery::stop() { mutex.lock(); running = false; mutex.unlock(); emit stopping(); } void Discovery::resume() { waitCondition.wakeAll(); } bool Discovery::isRunning() { return running; } void Discovery::run() { discover(); } void Discovery::discover() { int nb_loops = 1; //if (settingsPanel->multiBroadcast->isChecked()) // nb_loops = settingsPanel->broadcastRepeat->value(); for (int k=0; konvif_session; QString str = "Discovery started\n"; /**/ char buffer[1024]; settingsPanel->getCurrentlySelectedIP(buffer); str.append(QString("currently selected IP for broadcast - %1\n").arg(buffer)); strcpy(onvif_session->preferred_network_address, buffer); std::cout << "onvif_session->preferred_network_address: " << onvif_session->preferred_network_address << std::endl; /**/ int number_of_cameras = broadcast(onvif_session); str.append(QString("libonvif found %1 cameras\n").arg(QString::number(number_of_cameras))); emit msg(str); for (int i=0; icamera_name, onvif_data->xaddrs)); QString username = settingsPanel->commonUsername->text(); QString password = settingsPanel->commonPassword->text(); strncpy(onvif_data->username, username.toLatin1(), username.length()); strncpy(onvif_data->password, password.toLatin1(), password.length()); bool loggedIn = alreadyLoggedIn(onvif_data); if (loggedIn) { emit msg(QString("Duplicate discovery packet for camera %1\n").arg(onvif_data->camera_name)); } while (!loggedIn) { if (fillRTSP(onvif_data) == 0) { loggedIn = true; addCamera(onvif_data); } else { QString error_msg = onvif_data->last_error; if (error_msg.contains("ter:NotAuthorized") || error_msg.contains("Unauthorized")) { memset(&credential, 0, sizeof(credential)); strncpy(credential.camera_name, onvif_data->camera_name, sizeof(credential.camera_name)-1); strncpy(credential.host_name, onvif_data->xaddrs, sizeof(credential.host_name) - 1); emit login(&credential); emit msg("starting login"); mutex.lock(); waitCondition.wait(&mutex); mutex.unlock(); if (credential.accept_requested) { strncpy(onvif_data->username, credential.username, sizeof(onvif_data->username)); onvif_data->username[sizeof(onvif_data->username)-1]=0; strncpy(onvif_data->password, credential.password, sizeof(onvif_data->password)); onvif_data->username[sizeof(onvif_data->password)-1]=0; if (fillRTSP(onvif_data) == 0) { loggedIn = true; addCamera(onvif_data); } else { emit msg(QString("Login failure for camera %1\n").arg(onvif_data->camera_name)); } } else { emit msg(QString("Login cancelled for camera %1\n").arg(onvif_data->camera_name)); break; } } else { emit msg(QString("ONVIF error %1\n").arg(onvif_data->last_error)); break; } } } } else { break; } } thread->msleep((k+1) * 200); } stop(); } void Discovery::addCamera(OnvifData *onvif_data) { getProfile(onvif_data); getDeviceInformation(onvif_data); QString str; str.append(QString("%1\n").arg(onvif_data->stream_uri)); str.append(QString("serial number: %1\nmfgr name: %2\n").arg(onvif_data->serial_number, onvif_data->camera_name)); QString key = onvif_data->serial_number; QString alias = cameraAlias.value(key); if (alias.length() > 0) { strncpy(onvif_data->camera_name, alias.toLatin1().data(), alias.length()); onvif_data->camera_name[alias.length()] = '\0'; } emit found(onvif_data); str.append(QString("display name: %1\n").arg(onvif_data->camera_name)); emit msg(str); } bool Discovery::alreadyLoggedIn(OnvifData *onvif_data) { bool result = false; QVector cameras = CP->cameraList->cameraListModel->cameras; for (int i = 0; i < cameras.size(); i++) { Camera *camera = cameras[i]; QString currentXaddrs = onvif_data->xaddrs; QString cameraXaddrs = camera->onvif_data->xaddrs; if (currentXaddrs == cameraXaddrs) result = true; } return result; } libonvif-1.4.4/onvif-gui/src/camera.cpp0000644000175000017500000000235014355414521017660 0ustar stephenstephen/******************************************************************************* * camera.cpp * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include "camera.h" Camera::Camera(OnvifData *arg) { onvif_data = arg; } Camera::~Camera() { free(onvif_data); } QString Camera::getCameraName() { return QString(onvif_data->camera_name); } bool Camera::hasPTZ() { if (strcmp(onvif_data->ptz_service, "") == 0) return false; else return true; } libonvif-1.4.4/onvif-gui/src/messagepanel.cpp0000644000175000017500000000302514355414521021074 0ustar stephenstephen/******************************************************************************* * messagepanel.cpp * * Copyright (c) 2022 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include "messagepanel.h" #include "mainwindow.h" #include #include MessagePanel::MessagePanel(QMainWindow *parent) { mainWindow = parent; cp = QGuiApplication::clipboard(); msg = new QTextEdit(); btnCopy = new QPushButton("Copy"); connect(btnCopy, SIGNAL(clicked()), this, SLOT(onBtnCopyClicked())); QGridLayout *layout = new QGridLayout(this); layout->addWidget(msg, 0, 0, 1, 1); layout->addWidget(btnCopy, 1, 0, 1, 1, Qt::AlignRight); } void MessagePanel::onBtnCopyClicked() { //std::cout << "test" << std::endl; cp->setText(msg->toPlainText()); } libonvif-1.4.4/onvif-gui/src/cameralistmodel.cpp0000644000175000017500000000645114355414521021603 0ustar stephenstephen/******************************************************************************* * cameralistmodel.cpp * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include "cameralistmodel.h" #include "mainwindow.h" CameraListModel::CameraListModel(QMainWindow *parent) { mainWindow = parent; } int CameraListModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent) return cameras.size(); } QVariant CameraListModel::data(const QModelIndex &index, int role) const { if (index.isValid() && (role == Qt::DisplayRole || role == Qt::EditRole)) { Camera *tmp = (Camera *)cameras[index.row()]; return tmp->getCameraName(); } return QVariant(); } bool CameraListModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (index.isValid() && role == Qt::EditRole) { Camera *camera = (Camera *)cameras[index.row()]; strncpy(camera->onvif_data->camera_name, value.toString().toLatin1(), value.toString().length()); camera->onvif_data->camera_name[value.toString().length()] = '\0'; MW->cameraPanel->adminTab->textCameraName->setText(camera->onvif_data->camera_name); MW->cameraPanel->cameraNames->setValue(camera->onvif_data->serial_number, camera->onvif_data->camera_name); return true; } else { return false; } } Qt::ItemFlags CameraListModel::flags(const QModelIndex &index) const { return Qt::ItemIsEditable | QAbstractListModel::flags(index); } void CameraListModel::beginInsertItems(int start, int end) { beginInsertRows(QModelIndex(), start, end); } void CameraListModel::endInsertItems() { endInsertRows(); } void CameraListModel::pushCamera(OnvifData *onvif_data) { bool found = false; for (int i = 0; i < cameras.size(); i++) { if (!strcmp(onvif_data->xaddrs, cameras[i]->onvif_data->xaddrs)) found = true; } if (!found) { Camera *camera = new Camera(onvif_data); cameras.push_back(camera); emit dataChanged(QModelIndex(), QModelIndex()); } } Camera * CameraListModel::getCameraAt(int index) { return cameras[index]; } void CameraListModel::onSelectedItemsChanged(QItemSelection selected, QItemSelection deselected) { Q_UNUSED(deselected) if (!selected.empty()) { int index = selected.first().indexes().first().row(); Camera *camera = cameras[index]; MW->cameraPanel->camera = camera; if (camera->onvif_data_read) { emit showCameraData(); } else { emit getCameraData(); } } } libonvif-1.4.4/onvif-gui/src/cameralistview.cpp0000644000175000017500000000647214355557026021470 0ustar stephenstephen/******************************************************************************* * cameralistview.cpp * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include "cameralistview.h" #include "mainwindow.h" #include CameraListView::CameraListView(QMainWindow *parent) { mainWindow = parent; cameraListModel = new CameraListModel(mainWindow); setModel(cameraListModel); setFrameStyle(QFrame::Panel | QFrame::Sunken); connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), cameraListModel, SLOT(onSelectedItemsChanged(QItemSelection, QItemSelection))); } void CameraListView::mouseDoubleClickEvent(QMouseEvent *event) { Q_UNUSED(event); MW->cameraPanel->cameraListDoubleClicked(); } void CameraListView::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Return) MW->cameraPanel->cameraListDoubleClicked(); QListView::keyPressEvent(event); } QModelIndex CameraListView::previousIndex() const { QModelIndex previous; QModelIndex index = currentIndex(); if (index.isValid()) { if (index.row() > 0) { QRect rect = rectForIndex(index); QPoint previous_center = QPoint(rect.center().x(), rect.center().y() - rect.height()); previous = indexAt(previous_center); } } return previous; } QModelIndex CameraListView::nextIndex() const { QModelIndex next; QModelIndex index = currentIndex(); if (index.isValid()) { if (index.row() + 1 < model()->rowCount()) { QRect rect = rectForIndex(index); QPoint next_center = QPoint(rect.center().x(), rect.center().y() + rect.height()); next = indexAt(next_center); } } return next; } void CameraListView::setCurrentCamera(const QString& cameraName) { int row = -1; for (int i = 0; i < model()->rowCount(); i++) { Camera *camera = ((CameraListModel*)model())->getCameraAt(i); if (camera->getCameraName() == cameraName) { row = i; break; } } if (row > -1) { QModelIndex index = model()->index(row, 0); setCurrentIndex(index); } } Camera *CameraListView::getCurrentCamera() { if (currentIndex().isValid()) return (Camera*)((CameraListModel*)model())->cameras[currentIndex().row()]; else return NULL; } void CameraListView::refresh() { for (int i = 0; i < model()->rowCount(); i++) { Camera *camera = ((CameraListModel*)model())->getCameraAt(i); } model()->emit dataChanged(QModelIndex(), QModelIndex()); } libonvif-1.4.4/onvif-gui/src/filepanel.cpp0000644000175000017500000003771614355557026020415 0ustar stephenstephen/******************************************************************************* * filepanel.cpp * * Copyright (c) 2022 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include #include #include #include #include #include #include #include #include "filepanel.h" #include "mainwindow.h" FilePanel::FilePanel(QMainWindow *parent) : QWidget(parent) { mainWindow = parent; directorySetter = new DirectorySetter(mainWindow, ""); model = new QFileSystemModel(); model->setReadOnly(false); tree = new TreeView(this); tree->setModel(model); btnPlay = new QPushButton(); btnPlay->setStyleSheet(MW->getButtonStyle("play")); connect(btnPlay, SIGNAL(clicked()), this, SLOT(onBtnPlayClicked())); btnStop = new QPushButton(); btnStop->setStyleSheet(MW->getButtonStyle("stop")); connect(btnStop, SIGNAL(clicked()), this, SLOT(onBtnStopClicked())); btnMute = new QPushButton(); MW->glWidget->setMute(MW->settings->value(muteKey, false).toBool()); if (MW->glWidget->isMute()) btnMute->setStyleSheet(MW->getButtonStyle("mute")); else btnMute->setStyleSheet(MW->getButtonStyle("audio")); connect(btnMute, SIGNAL(clicked()), this, SLOT(onBtnMuteClicked())); sldVolume = new QSlider(Qt::Horizontal, this); sldVolume->setValue(MW->settings->value(volumeKey, 80).toInt()); connect(sldVolume, SIGNAL(sliderMoved(int)), this, SLOT(onSldVolumeMoved(int))); sldProgress = new ProgressSlider(Qt::Horizontal, this); sldProgress->setMaximum(1000); connect(MW->glWidget, SIGNAL(progress(float)), this, SLOT(progress(float))); connect(MW->glWidget, SIGNAL(mediaPlayingFinished()), this, SLOT(mediaPlayingFinished())); connect(MW->glWidget, SIGNAL(mediaPlayingStarted()), this, SLOT(mediaPlayingStarted())); //connect(sldProgress, SIGNAL(seek(float)), MW->glWidget, SLOT(seek(float))); lblProgress = new QLabel("0:00", this); lblProgress->setFont(QFont("courier", 12, QFont::Bold)); lblDuration = new QLabel("-:--", this); lblDuration->setFont(QFont("courier", 12, QFont::Bold)); lblSeek = new ProgressLabel(this); lblSeek->setFont(QFont("courier", 12, QFont::Bold)); QWidget *progressPanel = new QWidget(this); QGridLayout *progressLayout = new QGridLayout(progressPanel); progressLayout->addWidget(lblSeek, 0, 1, 1, 7); progressLayout->addWidget(lblProgress, 1, 0, 1, 1); progressLayout->addWidget(sldProgress, 1, 1, 1, 7); progressLayout->addWidget(lblDuration, 1, 8, 1, 1); progressLayout->setContentsMargins(0, 0, 0, 0); progressLayout->setColumnStretch(1, 20); QWidget *controlPanel = new QWidget(this); QGridLayout *controlLayout = new QGridLayout(controlPanel); controlLayout->addWidget(btnPlay, 0, 0, 1, 1); controlLayout->addWidget(btnStop, 0, 1, 1, 1); controlLayout->addWidget(btnMute, 0, 3, 1, 1); controlLayout->addWidget(sldVolume, 0, 4, 1, 2); controlLayout->addWidget(progressPanel, 1, 0, 1, 8); QGridLayout *layout = new QGridLayout(this); layout->addWidget(directorySetter, 0, 0, 1, 1); layout->addWidget(tree, 1, 0, 1, 1); layout->addWidget(controlPanel, 2, 0, 1, 1); layout->setRowStretch(1, 20); QStringList list = QStandardPaths::standardLocations(QStandardPaths::MoviesLocation); QString path = MW->settings->value(dirKey, list[0]).toString(); directorySetter->setPath(path); model->setRootPath(path); tree->setRootIndex(model->index(path)); connect(directorySetter, SIGNAL(directorySet(const QString&)), this, SLOT(setDirectory(const QString&))); tree->header()->restoreState(MW->settings->value(headerKey).toByteArray()); tree->setContextMenuPolicy(Qt::CustomContextMenu); connect(tree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&))); connect(tree, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(doubleClicked(const QModelIndex&))); connect(tree->header(), SIGNAL(sectionResized(int, int, int)), this, SLOT(headerChanged(int, int, int))); connect(tree->header(), SIGNAL(sectionMoved(int, int, int)), this, SLOT(headerChanged(int, int, int))); menu = new QMenu("Context Menu", this); QAction *remove = new QAction("Delete", this); QAction *rename = new QAction("Rename", this); QAction *info = new QAction("Info", this); QAction *play = new QAction("Play", this); connect(remove, SIGNAL(triggered()), this, SLOT(onMenuRemove())); connect(rename, SIGNAL(triggered()), this, SLOT(onMenuRename())); connect(info, SIGNAL(triggered()), this, SLOT(onMenuInfo())); connect(play, SIGNAL(triggered()), this, SLOT(onMenuPlay())); menu->addAction(remove); menu->addAction(rename); menu->addAction(info); menu->addAction(play); disableToolTips(MW->settingsPanel->hideToolTips->isChecked()); connect(this, SIGNAL(msg(const QString&)), mainWindow, SLOT(msg(const QString&))); } void FilePanel::disableToolTips(bool arg) { if (!arg) { btnPlay->setToolTip("Play"); btnStop->setToolTip("Stop"); btnMute->setToolTip("Mute"); } else { btnPlay->setToolTip(""); btnStop->setToolTip(""); btnMute->setToolTip(""); } } void FilePanel::setDirectory(const QString& path) { directorySetter->setPath(path); model->setRootPath(path); tree->setRootIndex(model->index(path)); MW->settings->setValue(dirKey, path); } void FilePanel::onBtnPlayClicked() { if (MW->glWidget->process) { MW->glWidget->togglePaused(); if (MW->glWidget->isPaused()) btnPlay->setStyleSheet(MW->getButtonStyle("play")); else btnPlay->setStyleSheet(MW->getButtonStyle("pause")); } else { QModelIndex index = tree->currentIndex(); if (index.isValid()) { QFileInfo fileInfo = model->fileInfo(index); std::cout << fileInfo.filePath().toLatin1().data() << std::endl; MW->currentStreamingMediaName = fileInfo.fileName(); MW->glWidget->play(fileInfo.filePath()); } } } void FilePanel::doubleClicked(const QModelIndex& index) { if (index.isValid()) { QFileInfo fileInfo = model->fileInfo(index); if (fileInfo.isDir()) { bool expanded = tree->isExpanded(index); tree->setExpanded(index, !expanded); } else { MW->glWidget->play(fileInfo.filePath()); MW->currentStreamingMediaName = fileInfo.fileName(); } } } void FilePanel::mediaPlayingFinished() { progress(0); btnPlay->setStyleSheet(MW->getButtonStyle("play")); } void FilePanel::mediaPlayingStarted() { btnPlay->setStyleSheet(MW->getButtonStyle("pause")); int duration_in_seconds = MW->glWidget->media_duration / 1000; int hours = duration_in_seconds / 3600; int minutes = (duration_in_seconds - (hours * 3600)) / 60; int seconds = (duration_in_seconds - (hours * 3600) - (minutes * 60)); char buf[32] = {0}; if (hours > 0) sprintf(buf, "%02d:%02d:%02d", hours, minutes, seconds); else sprintf(buf, "%d:%02d", minutes, seconds); QString output(buf); lblDuration->setText(output); } void FilePanel::progress(float pct) { sldProgress->setValue(sldProgress->maximum() * pct); double position = 0; if (MW->glWidget->media_duration) position = pct * MW->glWidget->media_duration; int position_in_seconds = position / 1000; int hours = position_in_seconds / 3600; int minutes = (position_in_seconds - (hours * 3600)) / 60; int seconds = (position_in_seconds - (hours * 3600) - (minutes * 60)); char buf[32] = {0}; if (hours > 0) sprintf(buf, "%02d:%02d:%02d", hours, minutes, seconds); else sprintf(buf, "%d:%02d", minutes, seconds); QString output(buf); lblProgress->setText(output); } void FilePanel::headerChanged(int arg1, int arg2, int arg3) { MW->settings->setValue(headerKey, tree->header()->saveState()); } void FilePanel::onBtnStopClicked() { MW->glWidget->stop(); btnPlay->setStyleSheet(MW->getButtonStyle("play")); lblProgress->setText("0:00"); } void FilePanel::onBtnMuteClicked() { if (MW->glWidget->isMute()) { btnMute->setStyleSheet(MW->getButtonStyle("audio")); MW->cameraPanel->btnMute->setStyleSheet(MW->getButtonStyle("audio")); } else { btnMute->setStyleSheet(MW->getButtonStyle("mute")); MW->cameraPanel->btnMute->setStyleSheet(MW->getButtonStyle("mute")); } MW->glWidget->setMute(!MW->glWidget->isMute()); MW->settings->setValue(muteKey, MW->glWidget->isMute()); } void FilePanel::onSldVolumeMoved(int value) { MW->glWidget->setVolume(value); MW->settings->setValue(volumeKey, value); MW->cameraPanel->volumeSlider->setValue(value); } void FilePanel::showContextMenu(const QPoint &pos) { QModelIndex index = tree->indexAt(pos); if (index.isValid()) { menu->exec(mapToGlobal(pos)); } } void FilePanel::onMenuPlay() { doubleClicked(tree->currentIndex()); } void FilePanel::onMenuRemove() { QModelIndex index = tree->currentIndex(); if (!index.isValid()) return; int ret = QMessageBox::warning(this, "onvif-gui", "You are about to delete this file.\n" "Are you sure you want to continue?", QMessageBox::Ok | QMessageBox::Cancel); if (ret == QMessageBox::Ok) QFile::remove(model->filePath(tree->currentIndex()).toLatin1().data()); } void FilePanel::onMenuRename() { QModelIndex index = tree->currentIndex(); if (index.isValid()) tree->edit(index); } void FilePanel::onMenuInfo() { QString str; QModelIndex idx = tree->currentIndex(); if (idx.isValid()) { QFileInfo info = model->fileInfo(idx); str += "Filename: " + info.absoluteFilePath(); str += "\nLast Modified: " + info.lastModified().toString(); avio::Reader reader(info.absoluteFilePath().toLatin1().data()); long duration = reader.duration(); int time_in_seconds = duration / 1000; int hours = time_in_seconds / 3600; int minutes = (time_in_seconds - (hours * 3600)) / 60; int seconds = (time_in_seconds - (hours * 3600) - (minutes * 60)); char buf[32] = {0}; if (hours > 0) sprintf(buf, "%02d:%02d:%02d", hours, minutes, seconds); else sprintf(buf, "%d:%02d", minutes, seconds); str += "\nDuration: " + QString(buf); if (reader.has_video()) { str += "\n\nVideo Stream:"; str += "\n\tResolution: " + QString::number(reader.width()) + " x " + QString::number(reader.height()); str += "\n\tFrame Rate: " + QString::number((float)reader.frame_rate().num / (float)reader.frame_rate().den); str += "\n\tVideo Codec: " + QString(reader.str_video_codec()); str += "\n\tPixel Format: " + QString(reader.str_pix_fmt()); } if (reader.has_audio()) { str += "\n\nAudio Stream:"; str += "\n\tChannel Layout: " + QString(reader.str_channel_layout().c_str()); str += "\n\tAudio Codec: " + QString(reader.str_audio_codec()); str += "\n\tSample Rate: " + QString::number(reader.sample_rate()); } } else { str = "Invalid Index"; } QMessageBox msgBox(this); msgBox.setText(str); msgBox.exec(); } TreeView::TreeView(QWidget *parent) : QTreeView(parent) { } void TreeView::keyPressEvent(QKeyEvent *event) { switch (event->key()) { case (Qt::Key_Delete): ((FilePanel*)parent())->onMenuRemove(); break; case (Qt::Key_Return): ((FilePanel*)parent())->doubleClicked(currentIndex()); break; case (Qt::Key_Space): ((FilePanel*)parent())->onBtnPlayClicked(); break; case (Qt::Key_Escape): ((FilePanel*)parent())->onBtnStopClicked(); break; default: QTreeView::keyPressEvent(event); } } void TreeView::mouseDoubleClickEvent(QMouseEvent *event) { emit doubleClicked(indexAt(event->pos())); } DirectorySetter::DirectorySetter(QMainWindow *parent, const QString& labelText) { mainWindow = parent; label = new QLabel(labelText); text = new QLineEdit(); button = new QPushButton("..."); button->setMaximumWidth(30); connect(button, SIGNAL(clicked()), this, SLOT(selectDirectory())); QGridLayout *layout = new QGridLayout; layout->setContentsMargins(0, 0, 0, 0); if (label->text() != "") layout->addWidget(label, 0, 0, 1, 1); layout->addWidget(text, 0, 1, 1, 1); layout->addWidget(button, 0, 2, 1, 1); layout->setContentsMargins(0, 0, 0, 0); setLayout(layout); setContentsMargins(0, 0, 0, 0); } void DirectorySetter::setPath(const QString& path) { directory = path; text->setText(path); } void DirectorySetter::selectDirectory() { QString path = QFileDialog::getExistingDirectory(mainWindow, label->text(), directory, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if (path.length() > 0) { directory = path; text->setText(directory); emit directorySet(directory); } } ProgressSlider::ProgressSlider(Qt::Orientation o, QWidget *parent) : QSlider(o, parent) { setMouseTracking(true); filePanel = parent; } bool ProgressSlider::event(QEvent *e) { if (e->type() == QEvent::Leave) ((FilePanel*)filePanel)->lblSeek->setText(""); return QSlider::event(e); } void ProgressSlider::mousePressEvent(QMouseEvent *event) { float pct = event->pos().x() / (float)width(); avio::GLWidget* glWidget = ((MainWindow*)((FilePanel*)filePanel)->mainWindow)->glWidget; avio::Reader* reader = glWidget->get_reader(); if (reader) { if (reader->running) { glWidget->seek(pct); } } } void ProgressSlider::mouseMoveEvent(QMouseEvent *e) { MainWindow* mainWindow = (MainWindow*)(((FilePanel*)filePanel)->mainWindow); if (mainWindow->glWidget->media_duration) { double percentage = e->pos().x() / (double)width(); double position = percentage * MW->glWidget->media_duration; int position_in_seconds = position / 1000; int hours = position_in_seconds / 3600; int minutes = (position_in_seconds - (hours * 3600)) / 60; int seconds = (position_in_seconds - (hours * 3600) - (minutes * 60)); char buf[32] = {0}; if (hours > 0) sprintf(buf, "%02d:%02d:%02d", hours, minutes, seconds); else sprintf(buf, "%d:%02d", minutes, seconds); QString output(buf); ((FilePanel*)filePanel)->lblSeek->x_pos = e->pos().x(); ((FilePanel*)filePanel)->lblSeek->setText(output); } } ProgressLabel::ProgressLabel(QWidget *parent) : QLabel(parent) { } void ProgressLabel::paintEvent(QPaintEvent *event) { QPainter painter(this); QFontMetrics metrics = fontMetrics(); QRect rect = metrics.boundingRect(text()); int x = std::min(width() - rect.width(), x_pos); painter.drawText(QPoint(x, height()), text()); }libonvif-1.4.4/onvif-gui/src/main.cpp0000644000175000017500000000226614355557026017372 0ustar stephenstephen/******************************************************************************* * main.cpp * * Copyright (c) 2022 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include #include #include "mainwindow.h" #include Q_DECLARE_METATYPE(std::string); int main(int argc, char *argv[]) { qRegisterMetaType(); QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); } libonvif-1.4.4/onvif-gui/src/logindialog.cpp0000644000175000017500000000446414355414521020730 0ustar stephenstephen/******************************************************************************* * logindialog.cpp * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include #include "logindialog.h" #include "camerapanel.h" #include #include LoginDialog::LoginDialog(QWidget *parent) { cameraPanel = parent; setWindowTitle("Login"); cameraIP = new QLabel(); cameraName = new QLabel("Camera Name:"); buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); username = new QLineEdit; password = new QLineEdit; QGridLayout *layout = new QGridLayout; layout->addWidget(cameraIP, 0, 0, 1, 2); layout->addWidget(cameraName, 1, 0, 1, 2); layout->addWidget(new QLabel("Username"), 2, 0, 1, 1); layout->addWidget(username, 2, 1, 1, 1); layout->addWidget(new QLabel("Password"), 3, 0, 1, 1); layout->addWidget(password, 3, 1, 1, 1); layout->addWidget(buttonBox, 4, 0, 1, 2); setLayout(layout); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); } int LoginDialog::exec() { QRect rect = CP->geometry(); setMinimumWidth(300); QPoint global = CP->mapToGlobal(QPoint(rect.x(), rect.y())); int x = global.x() + rect.width() / 2 - 150; int y = global.y() + rect.height() / 2 - 150; move(x, y); username->setText(""); password->setText(""); username->setFocus(); return QDialog::exec(); } libonvif-1.4.4/onvif-gui/src/ptztab.cpp0000644000175000017500000001552414355414521017743 0ustar stephenstephen/******************************************************************************* * ptztab.cpp * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ //#include "camerapanel.h" //#include "mainwindow.h" #include "onvif.h" #include "ptztab.h" #include #include PTZTab::PTZTab(QWidget *parent) { cameraPanel = parent; const int BUTTON_SIZE = 30; for (int i = 0; i < 10; i++) speed[i] = 0.09 * (i+1); labelSpeed = new QLabel("PTZ speed"); labelSpeed->setAlignment(Qt::AlignRight | Qt::AlignVCenter); comboSpeed = new QComboBox(); comboSpeed->addItem(tr("1")); comboSpeed->addItem(tr("2")); comboSpeed->addItem(tr("3")); comboSpeed->addItem(tr("4")); comboSpeed->addItem(tr("5")); comboSpeed->addItem(tr("6")); comboSpeed->addItem(tr("7")); comboSpeed->addItem(tr("8")); comboSpeed->addItem(tr("9")); comboSpeed->addItem(tr("10")); comboSpeed->setMaximumWidth(40); comboSpeed->setCurrentIndex(4); textPreset = new QLineEdit; textPreset->setMaximumWidth(35); buttonPreset = new QPushButton(tr("Go"), this); buttonPreset->setMaximumWidth(35); connect(buttonPreset, SIGNAL(clicked()), this, SLOT(userPreset())); button1 = new QPushButton(tr("1"), this); button2 = new QPushButton(tr("2"), this); button3 = new QPushButton(tr("3"), this); button4 = new QPushButton(tr("4"), this); button5 = new QPushButton(tr("5"), this); button1->setMaximumWidth(BUTTON_SIZE); button2->setMaximumWidth(BUTTON_SIZE); button3->setMaximumWidth(BUTTON_SIZE); button4->setMaximumWidth(BUTTON_SIZE); button5->setMaximumWidth(BUTTON_SIZE); connect(button1, &QPushButton::clicked, [=] {preset(0);}); connect(button2, &QPushButton::clicked, [=] {preset(1);}); connect(button3, &QPushButton::clicked, [=] {preset(2);}); connect(button4, &QPushButton::clicked, [=] {preset(3);}); connect(button5, &QPushButton::clicked, [=] {preset(4);}); checkPreset = new QCheckBox(tr("Set Preset"), this); buttonUp = new QPushButton(tr("^"), this); buttonDown = new QPushButton(tr("v"), this); buttonLeft = new QPushButton(tr("<"), this); buttonRight = new QPushButton(tr(">"), this); buttonZoomIn = new QPushButton(tr("Zoom In"), this); buttonZoomOut = new QPushButton(tr("Zoom Out"), this); buttonUp->setMaximumWidth(45); buttonDown->setMaximumWidth(45); buttonLeft->setMaximumWidth(45); buttonRight->setMaximumWidth(45); buttonZoomIn->setMaximumWidth(90); buttonZoomOut->setMaximumWidth(90); connect(buttonUp, &QPushButton::pressed, [=] {move(0.0, speed[comboSpeed->currentIndex()], 0.0);}); connect(buttonDown, &QPushButton::pressed, [=] {move(0.0, speed[comboSpeed->currentIndex()] * -1, 0.0);}); connect(buttonLeft, &QPushButton::pressed, [=] {move(speed[comboSpeed->currentIndex()] * -1, 0.0, 0.0);}); connect(buttonRight, &QPushButton::pressed, [=] {move(speed[comboSpeed->currentIndex()], 0.0, 0.0);}); connect(buttonZoomIn, &QPushButton::pressed, [=] {move(0.0, 0.0, speed[comboSpeed->currentIndex()]);}); connect(buttonZoomOut, &QPushButton::pressed, [=] {move(0.0, 0.0, speed[comboSpeed->currentIndex()] * -1);}); connect(buttonUp, SIGNAL(released()), this, SLOT(stopPanTilt())); connect(buttonDown, SIGNAL(released()), this, SLOT(stopPanTilt())); connect(buttonLeft, SIGNAL(released()), this, SLOT(stopPanTilt())); connect(buttonRight, SIGNAL(released()), this, SLOT(stopPanTilt())); connect(buttonZoomIn, SIGNAL(released()), this, SLOT(stopZoom())); connect(buttonZoomOut, SIGNAL(released()), this, SLOT(stopZoom())); QGridLayout *layout = new QGridLayout( this ); layout->addWidget(textPreset, 1, 2, 1, 3); layout->addWidget(buttonPreset, 1, 3, 1, 1); layout->addWidget(checkPreset, 1, 4, 1, 2); layout->addWidget(button1, 1, 1, 1, 1); layout->addWidget(button2, 2, 1, 1, 1); layout->addWidget(button3, 3, 1, 1, 1); layout->addWidget(button4, 4, 1, 1, 1); layout->addWidget(button5, 5, 1, 1, 1); layout->addWidget(buttonLeft, 3, 2, 1, 1); layout->addWidget(buttonUp, 2, 3, 1, 1); layout->addWidget(buttonRight, 3, 4, 1, 1); layout->addWidget(buttonDown, 4, 3, 1, 1); layout->addWidget(buttonZoomIn, 2, 5, 1, 1); layout->addWidget(buttonZoomOut, 3, 5, 1, 1); layout->addWidget(labelSpeed, 5, 2, 1, 2); layout->addWidget(comboSpeed, 5, 4, 1, 2); ptzMover = new PTZMover(cameraPanel); ptzStopper = new PTZStopper(cameraPanel); ptzGoto = new PTZGoto(cameraPanel); ptzSetPreset = new PTZSetPreset(cameraPanel); } void PTZTab::update() { } void PTZTab::setActive(bool active) { checkPreset->setEnabled(active); textPreset->setEnabled(active); labelSpeed->setEnabled(active); comboSpeed->setEnabled(active); button1->setEnabled(active); button2->setEnabled(active); button3->setEnabled(active); button4->setEnabled(active); button5->setEnabled(active); buttonUp->setEnabled(active); buttonDown->setEnabled(active); buttonLeft->setEnabled(active); buttonRight->setEnabled(active); buttonZoomIn->setEnabled(active); buttonZoomOut->setEnabled(active); buttonPreset->setEnabled(active); } bool PTZTab::hasBeenEdited() { return false; } void PTZTab::preset(int arg) { if (checkPreset->isChecked()) { ptzSetPreset->set(arg); QThreadPool::globalInstance()->tryStart(ptzSetPreset); checkPreset->setChecked(false); } else { ptzGoto->set(arg); QThreadPool::globalInstance()->tryStart(ptzGoto); } } void PTZTab::userPreset() { bool ok; int arg = textPreset->text().toInt(&ok); if (ok) { preset(arg); } } void PTZTab::move(float x, float y, float z) { ptzMover->set(x, y, z); QThreadPool::globalInstance()->tryStart(ptzMover); } void PTZTab::stopPanTilt() { ptzStopper->set(PAN_TILT_STOP); QThreadPool::globalInstance()->tryStart(ptzStopper); } void PTZTab::stopZoom() { ptzStopper->set(ZOOM_STOP); QThreadPool::globalInstance()->tryStart(ptzStopper); } libonvif-1.4.4/onvif-gui/src/cameradialogtab.cpp0000644000175000017500000000224214355414521021527 0ustar stephenstephen/******************************************************************************* * cameradialogtab.cpp * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include "cameradialogtab.h" CameraDialogTab::CameraDialogTab() { } void CameraDialogTab::update() { } void CameraDialogTab::clear() { } void CameraDialogTab::setActive(bool active) { } bool CameraDialogTab::hasBeenEdited() { return false; } libonvif-1.4.4/onvif-gui/src/imagetab.cpp0000644000175000017500000001224114355414521020201 0ustar stephenstephen/******************************************************************************* * imagetab.cpp * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include "camerapanel.h" #include #include #include ImageTab::ImageTab(QWidget *parent) { cameraPanel = parent; sliderBrightness = new QSlider(Qt::Horizontal); sliderSaturation = new QSlider(Qt::Horizontal); sliderContrast = new QSlider(Qt::Horizontal); sliderSharpness = new QSlider(Qt::Horizontal); lblBrightness = new QLabel("Brightness"); lblSaturation = new QLabel("Saturation"); lblContrast = new QLabel("Contrast"); lblSharpness = new QLabel("Sharpness"); QGridLayout *layout = new QGridLayout(); layout->addWidget(lblBrightness, 0, 0, 1, 1); layout->addWidget(sliderBrightness, 0, 1, 1, 1); layout->addWidget(lblSaturation, 1, 0, 1, 1); layout->addWidget(sliderSaturation, 1, 1, 1, 1); layout->addWidget(lblContrast, 2, 0, 1, 1); layout->addWidget(sliderContrast, 2, 1, 1, 1); layout->addWidget(lblSharpness, 3, 0, 1, 1); layout->addWidget(sliderSharpness, 3, 1, 1, 1); setLayout(layout); connect(sliderBrightness, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int))); connect(sliderSaturation, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int))); connect(sliderContrast, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int))); connect(sliderSharpness, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int))); updater = new ImageUpdater(cameraPanel); connect(updater, SIGNAL(done()), this, SLOT(initialize())); } void ImageTab::update() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; onvif_data->brightness = sliderBrightness->value(); onvif_data->saturation = sliderSaturation->value(); onvif_data->contrast = sliderContrast->value(); onvif_data->sharpness = sliderSharpness->value(); setImagingSettings(onvif_data); QThreadPool::globalInstance()->tryStart(updater); } void ImageTab::clear() { sliderBrightness->setMinimum(0); sliderBrightness->setMaximum(0); sliderSaturation->setMinimum(0); sliderSaturation->setMaximum(0); sliderContrast->setMinimum(0); sliderContrast->setMaximum(0); sliderSharpness->setMinimum(0); sliderSharpness->setMaximum(0); sliderBrightness->setValue(0); sliderSaturation->setValue(0); sliderContrast->setValue(0); sliderSharpness->setValue(0); } void ImageTab::setActive(bool active) { sliderBrightness->setEnabled(active); sliderSaturation->setEnabled(active); sliderContrast->setEnabled(active); sliderSharpness->setEnabled(active); lblBrightness->setEnabled(active); lblSaturation->setEnabled(active); lblContrast->setEnabled(active); lblSharpness->setEnabled(active); } bool ImageTab::hasBeenEdited() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; bool result = false; if (sliderBrightness->value() != onvif_data->brightness) result = true; if (sliderSaturation->value() != onvif_data->saturation) result = true; if (sliderContrast->value() != onvif_data->contrast) result = true; if (sliderSharpness->value() != onvif_data->sharpness) result = true; return result; } void ImageTab::initialize() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; sliderBrightness->setMinimum(onvif_data->brightness_min); sliderBrightness->setMaximum(onvif_data->brightness_max); sliderSaturation->setMinimum(onvif_data->saturation_min); sliderSaturation->setMaximum(onvif_data->saturation_max); sliderContrast->setMinimum(onvif_data->contrast_min); sliderContrast->setMaximum(onvif_data->contrast_max); sliderSharpness->setMinimum(onvif_data->sharpness_min); sliderSharpness->setMaximum(onvif_data->sharpness_max); sliderBrightness->setValue(onvif_data->brightness); sliderSaturation->setValue(onvif_data->saturation); sliderContrast->setValue(onvif_data->contrast); sliderSharpness->setValue(onvif_data->sharpness); ((CameraPanel *)cameraPanel)->applyButton->setEnabled(false); } void ImageTab::onValueChanged(int value) { Q_UNUSED(value); if (hasBeenEdited()) ((CameraPanel *)cameraPanel)->applyButton->setEnabled(true); else ((CameraPanel *)cameraPanel)->applyButton->setEnabled(false); } libonvif-1.4.4/onvif-gui/src/videotab.cpp0000644000175000017500000001503614355414521020232 0ustar stephenstephen/******************************************************************************* * videotab.cpp * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include "camerapanel.h" #include #include #include SpinBox::SpinBox(QLineEdit *editor) { setLineEdit(editor); } VideoTab::VideoTab(QWidget *parent) { cameraPanel = parent; comboResolutions = new QComboBox(); QLineEdit *textBitrate = new QLineEdit(); spinBitrate = new SpinBox(textBitrate); QLineEdit *textFrameRate = new QLineEdit(); spinFrameRate = new SpinBox(textFrameRate); QLineEdit *textGovLength = new QLineEdit(); spinGovLength = new SpinBox(textGovLength); lblResolutions = new QLabel("Resolution"); lblFrameRate = new QLabel("Frame Rate"); lblGovLength = new QLabel("Gov Length"); lblBitrate = new QLabel("Bitrate"); QGridLayout *layout = new QGridLayout(); layout->addWidget(lblResolutions, 0, 0, 1, 1); layout->addWidget(comboResolutions, 0, 1, 1, 1); layout->addWidget(lblFrameRate, 1, 0, 1, 1); layout->addWidget(spinFrameRate, 1, 1, 1, 1); layout->addWidget(lblGovLength, 2, 0, 1, 1); layout->addWidget(spinGovLength, 2, 1, 1, 1); layout->addWidget(lblBitrate, 3, 0, 1, 1); layout->addWidget(spinBitrate, 3, 1, 1, 1); setLayout(layout); connect(comboResolutions, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int))); connect(spinFrameRate, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int))); connect(spinGovLength, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int))); connect(spinBitrate, SIGNAL(valueChanged(int)), this, SLOT(onValueChanged(int))); updater = new VideoUpdater(cameraPanel); connect(updater, SIGNAL(done()), this, SLOT(initialize())); } void VideoTab::update() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; onvif_data->frame_rate = spinFrameRate->value(); onvif_data->gov_length = spinGovLength->value(); onvif_data->bitrate = spinBitrate->value(); char * resolution_buf = comboResolutions->currentText().toLatin1().data(); char * mark = strstr(resolution_buf, "x"); int length = strlen(resolution_buf); int point = mark - resolution_buf; char width_buf[128] = {0}; char height_buf[128] = {0}; for (int i=0; iwidth = atoi(width_buf); onvif_data->height = atoi(height_buf); QThreadPool::globalInstance()->tryStart(updater); } void VideoTab::clear() { comboResolutions->clear(); spinFrameRate->setValue(0); spinGovLength->setValue(0); spinBitrate->setValue(0); spinGovLength->setMinimum(0); spinGovLength->setMaximum(0); spinFrameRate->setMinimum(0); spinFrameRate->setMaximum(0); spinBitrate->setMinimum(0); spinBitrate->setMaximum(0); } void VideoTab::setActive(bool active) { comboResolutions->setEnabled(active); spinFrameRate->setEnabled(active); spinGovLength->setEnabled(active); spinBitrate->setEnabled(active); lblResolutions->setEnabled(active); lblFrameRate->setEnabled(active); lblGovLength->setEnabled(active); lblBitrate->setEnabled(active); } void VideoTab::initialize() { OnvifData *onvif_data = CP->camera->onvif_data; comboResolutions->clear(); int size = 0; bool found_size = false; while (!found_size) { if (strlen(onvif_data->resolutions_buf[size]) == 0) { found_size = true; } else { size++; if (size > 15) found_size = true; } } if (resolutions) delete resolutions; resolutions = new QListWidget(this); QStringList args; for (int i=0; iresolutions_buf[i]); } resolutions->addItems(args); comboResolutions->setModel(resolutions->model()); comboResolutions->setView(resolutions); spinGovLength->setMinimum(onvif_data->gov_length_min); spinGovLength->setMaximum(onvif_data->gov_length_max); spinFrameRate->setMinimum(onvif_data->frame_rate_min); spinFrameRate->setMaximum(onvif_data->frame_rate_max); spinBitrate->setMinimum(onvif_data->bitrate_min); spinBitrate->setMaximum(onvif_data->bitrate_max); char res[128] = {0}; sprintf(res, "%d x %d", onvif_data->width, onvif_data->height); comboResolutions->setCurrentText(tr(res)); spinGovLength->setValue(onvif_data->gov_length); spinFrameRate->setValue(onvif_data->frame_rate); spinBitrate->setValue(onvif_data->bitrate); CP->applyButton->setEnabled(false); } bool VideoTab::hasBeenEdited() { OnvifData *onvif_data = CP->camera->onvif_data; bool result = false; if (strcmp(comboResolutions->currentText().toLatin1().data(), "") != 0) { if (spinGovLength->value() != onvif_data->gov_length) result = true; if (spinBitrate->value() != onvif_data->bitrate) result = true; if (spinFrameRate->value() != onvif_data->frame_rate) result = true; char resolution[128] = {0}; sprintf(resolution, "%d x %d", onvif_data->width, onvif_data->height); if (strcmp(comboResolutions->currentText().toLatin1().data(), resolution) != 0) result = true; } return result; } void VideoTab::onCurrentIndexChanged(int index) { Q_UNUSED(index); if (hasBeenEdited()) CP->applyButton->setEnabled(true); else CP->applyButton->setEnabled(false); } void VideoTab::onValueChanged(int index) { Q_UNUSED(index); if (hasBeenEdited()) CP->applyButton->setEnabled(true); else CP->applyButton->setEnabled(false); } libonvif-1.4.4/onvif-gui/src/stylepanel.cpp0000644000175000017500000001671014355414521020615 0ustar stephenstephen/******************************************************************************* * stylepanel.cpp * * Copyright (c) 2022 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include #include #include #include "stylepanel.h" #include "mainwindow.h" StylePanel::StylePanel(QMainWindow *parent) : QWidget(parent) { mainWindow = parent; QLabel *lblBL = new QLabel("Background Light"); QLabel *lblBM = new QLabel("Background Medium"); QLabel *lblBD = new QLabel("Background Dark"); QLabel *lblFL = new QLabel("Foreground Light"); QLabel *lblFM = new QLabel("Foreground Medium"); QLabel *lblFD = new QLabel("Foreground Dark"); QLabel *lblSL = new QLabel("Selection Light"); QLabel *lblSM = new QLabel("Selection Medium"); QLabel *lblSD = new QLabel("Selection Dark"); bl = new ColorButton(mainWindow, "background_light", blDefault); bm = new ColorButton(mainWindow, "background_medium", bmDefault); bd = new ColorButton(mainWindow, "background_dark", bdDefault); fl = new ColorButton(mainWindow, "foreground_light", flDefault); fm = new ColorButton(mainWindow, "foreground_medium", fmDefault); fd = new ColorButton(mainWindow, "foreground_dark", fdDefault); sl = new ColorButton(mainWindow, "selection_light", slDefault); sm = new ColorButton(mainWindow, "selection_medium", smDefault); sd = new ColorButton(mainWindow, "selection_dark", sdDefault); QWidget *colorPanel = new QWidget(); QGridLayout *colorLayout = new QGridLayout(colorPanel); colorLayout->addWidget(lblBL, 0, 0, 1, 1); colorLayout->addWidget(bl, 0, 1, 1, 1); colorLayout->addWidget(lblBM, 1, 0, 1, 1); colorLayout->addWidget(bm, 1, 1, 1, 1); colorLayout->addWidget(lblBD, 2, 0, 1, 1); colorLayout->addWidget(bd, 2, 1, 1, 1); colorLayout->addWidget(lblFL, 0, 2, 1, 1); colorLayout->addWidget(fl, 0, 3, 1, 1); colorLayout->addWidget(lblFM, 1, 2, 1, 1); colorLayout->addWidget(fm, 1, 3, 1, 1); colorLayout->addWidget(lblFD, 2, 2, 1, 1); colorLayout->addWidget(fd, 2, 3, 1, 1); colorLayout->addWidget(lblSL, 0, 4, 1, 1); colorLayout->addWidget(sl, 0, 5, 1, 1); colorLayout->addWidget(lblSM, 1, 4, 1, 1); colorLayout->addWidget(sm, 1, 5, 1, 1); colorLayout->addWidget(lblSD, 2, 4, 1, 1); colorLayout->addWidget(sd, 2, 5, 1, 1); colorLayout->setContentsMargins(0, 0, 0, 0); btnDefaults = new QPushButton("Defaults"); connect(btnDefaults, SIGNAL(clicked()), this, SLOT(onBtnDefaultsClicked())); btnCancel = new QPushButton("Cancel"); btnApply = new QPushButton("Apply"); connect(btnApply, SIGNAL(clicked()), this, SLOT(onBtnApplyClicked())); useSystemGui = new QCheckBox("Use System GUI"); connect(useSystemGui, SIGNAL(clicked(bool)), this, SLOT(sysGuiEnabled(bool))); useSystemGui->setChecked(MW->settings->value(sysGuiKey, false).toBool()); sysGuiEnabled(useSystemGui->isChecked()); btnApply->setEnabled(false); QWidget *controlPanel = new QWidget(); QGridLayout *controlLayout = new QGridLayout(controlPanel); controlLayout->addWidget(useSystemGui, 0, 0, 1, 1, Qt::AlignLeft); controlLayout->addWidget(btnCancel, 0, 2, 1, 1, Qt::AlignRight); controlLayout->addWidget(btnDefaults, 0, 3, 1, 1, Qt::AlignRight); controlLayout->addWidget(btnApply, 0, 4, 1, 1, Qt::AlignRight); controlLayout->setColumnStretch(1, 20); QGridLayout *layout = new QGridLayout(); layout->addWidget(colorPanel, 0, 0, 1, 4); layout->addWidget(controlPanel, 1, 0, 1, 4); setLayout(layout); } ColorProfile StylePanel::getProfile() const { ColorProfile profile; profile.bl = bl->color.name(); profile.bm = bm->color.name(); profile.bd = bd->color.name(); profile.fl = fl->color.name(); profile.fm = fm->color.name(); profile.fd = fd->color.name(); profile.sl = sl->color.name(); profile.sm = sm->color.name(); profile.sd = sd->color.name(); return profile; } void StylePanel::setTempProfile(const ColorProfile& profile) { bl->setTempColor(profile.bl); bm->setTempColor(profile.bm); bd->setTempColor(profile.bd); fl->setTempColor(profile.fl); fm->setTempColor(profile.fm); fd->setTempColor(profile.fd); sl->setTempColor(profile.sl); sm->setTempColor(profile.sm); sd->setTempColor(profile.sd); } void StylePanel::sysGuiEnabled(bool arg) { bl->setEnabled(!arg); bm->setEnabled(!arg); bd->setEnabled(!arg); fl->setEnabled(!arg); fm->setEnabled(!arg); fd->setEnabled(!arg); sl->setEnabled(!arg); sm->setEnabled(!arg); sd->setEnabled(!arg); btnDefaults->setEnabled(!arg); btnApply->setEnabled(true); } void StylePanel::onBtnApplyClicked() { MW->settings->setValue(sysGuiKey, useSystemGui->isChecked()); MW->applyStyle(getProfile()); bl->writeSettings(); bm->writeSettings(); bd->writeSettings(); fl->writeSettings(); fm->writeSettings(); fd->writeSettings(); sl->writeSettings(); sm->writeSettings(); sd->writeSettings(); btnApply->setEnabled(false); } void StylePanel::onBtnDefaultsClicked() { bl->setColor(blDefault); bm->setColor(bmDefault); bd->setColor(bdDefault); fl->setColor(flDefault); fm->setColor(fmDefault); fd->setColor(fdDefault); sl->setColor(slDefault); sm->setColor(smDefault); sd->setColor(sdDefault); btnApply->setEnabled(true); } ColorButton::ColorButton(QMainWindow *parent, const QString& qss_name, const QString& color_name) { mainWindow = parent; settingsKey = qss_name; QString thingy = MW->settings->value(settingsKey).toString(); color.setNamedColor(MW->settings->value(settingsKey, color_name).toString()); button = new QPushButton(); connect(button, SIGNAL(clicked()), this, SLOT(clicked())); button->setStyleSheet(getStyle()); QGridLayout *layout = new QGridLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(button, 0, 0, 1, 1); } void ColorButton::setTempColor(const QString& color_name) { color.setNamedColor(color_name); button->setStyleSheet(getStyle()); } void ColorButton::setColor(const QString& color_name) { color.setNamedColor(color_name); button->setStyleSheet(getStyle()); } QString ColorButton::getStyle() const { return QString("QPushButton {background-color: %1;}").arg(color.name()); } void ColorButton::writeSettings() { MW->settings->setValue(settingsKey, color.name()); } void ColorButton::clicked() { QColor result = QColorDialog::getColor(color, this, "playqt"); if (result.isValid()) { setColor(result.name()); MW->styleDialog->panel->btnApply->setEnabled(true); } } libonvif-1.4.4/onvif-gui/src/admintab.cpp0000644000175000017500000001600714355414521020213 0ustar stephenstephen/******************************************************************************* * admintab.cpp * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include "camerapanel.h" #include #include #include #include #include #include AdminTab::AdminTab(QWidget *parent) { cameraPanel = parent; textCameraName = new QLineEdit(); textCameraName->setMaximumWidth(240); textAdminPassword = new QLineEdit(); textAdminPassword->setMaximumWidth(240); buttonReboot = new QPushButton(tr("Reboot Camera"), this); buttonReboot->setMaximumWidth(160); buttonReboot->setEnabled(false); buttonSyncTime = new QPushButton(tr("Sync Time"), this); buttonSyncTime->setMaximumWidth(100); buttonLaunchBrowser = new QPushButton(tr("Browser"), this); buttonLaunchBrowser->setMaximumWidth(100); buttonHardReset = new QPushButton(tr("Hard Reset"), this); buttonHardReset->setMaximumWidth(160); buttonHardReset->setEnabled(false); checkEnableReboot = new QCheckBox("Enable Reboot"); checkEnableReset = new QCheckBox("Enable Reset"); lblCameraName = new QLabel("Camera Name"); lblAdminPassword = new QLabel("Set admin Password"); QGridLayout *layout = new QGridLayout(); layout->addWidget(lblCameraName, 0, 0, 1, 1); layout->addWidget(textCameraName, 0, 1, 1, 2); layout->addWidget(lblAdminPassword, 1, 0, 1, 1); layout->addWidget(textAdminPassword, 1, 1, 1, 2); layout->addWidget(buttonReboot, 2, 0, 1, 1); layout->addWidget(buttonSyncTime, 2, 1, 1, 1); layout->addWidget(buttonHardReset, 2, 2, 1, 1); layout->addWidget(checkEnableReboot, 3, 0, 1, 1); layout->addWidget(buttonLaunchBrowser, 3, 1, 1, 1); layout->addWidget(checkEnableReset, 3, 2, 1, 1); setLayout(layout); connect(buttonLaunchBrowser, SIGNAL(clicked()), this, SLOT(launchBrowserClicked())); connect(checkEnableReboot, SIGNAL(clicked()), this, SLOT(enableRebootChecked())); connect(checkEnableReset, SIGNAL(clicked()), this, SLOT(enableResetChecked())); connect(buttonReboot, SIGNAL(clicked()), this, SLOT(rebootClicked())); connect(buttonHardReset, SIGNAL(clicked()), this, SLOT(hardResetClicked())); connect(buttonSyncTime, SIGNAL(clicked()), this, SLOT(syncTimeClicked())); connect(textCameraName, SIGNAL(textChanged(const QString &)), this, SLOT(onTextChanged(const QString&))); connect(textAdminPassword, SIGNAL(textChanged(const QString &)), this, SLOT(onTextChanged(const QString&))); rebooter = new Rebooter(cameraPanel); connect(rebooter, SIGNAL(done()), this, SLOT(doneRebooting())); resetter = new Resetter(cameraPanel); connect(resetter, SIGNAL(done()), this, SLOT(doneResetting())); timesetter = new Timesetter(cameraPanel); } void AdminTab::update() { OnvifData *onvif_data = CP->camera->onvif_data; QString camera_name = textCameraName->text(); strncpy(onvif_data->camera_name, camera_name.toLatin1(), camera_name.length()); onvif_data->camera_name[camera_name.length()] = '\0'; setUser(textAdminPassword->text().toLatin1().data(), onvif_data); CP->applyButton->setEnabled(false); CP->refreshList(); CP->cameraNames->setValue(onvif_data->serial_number, onvif_data->camera_name); QString password = textAdminPassword->text(); strncpy(onvif_data->password, password.toLatin1(), password.length()); } void AdminTab::clear() { textCameraName->setText(""); textAdminPassword->setText(""); checkEnableReboot->setChecked(false); checkEnableReset->setChecked(false); } void AdminTab::setActive(bool active) { textCameraName->setEnabled(active); textAdminPassword->setEnabled(active); buttonLaunchBrowser->setEnabled(active); buttonSyncTime->setEnabled(active); checkEnableReboot->setEnabled(active); checkEnableReset->setEnabled(active); lblCameraName->setEnabled(active); lblAdminPassword->setEnabled(active); } bool AdminTab::hasBeenEdited() { bool result = false; OnvifData *onvif_data = CP->camera->onvif_data; QString camera_name = onvif_data->camera_name; if (camera_name != textCameraName->text()) result = true; if (textAdminPassword->text().length() > 0) result = true; return result; } void AdminTab::initialize() { OnvifData *onvif_data = CP->camera->onvif_data; textCameraName->setText(tr(onvif_data->camera_name)); buttonReboot->setEnabled(false); buttonHardReset->setEnabled(false); checkEnableReboot->setChecked(false); checkEnableReset->setChecked(false); } void AdminTab::launchBrowserClicked() { OnvifData *onvif_data = CP->camera->onvif_data; char host[128]; extractHost(onvif_data->xaddrs, host); #ifdef _WIN32 QString cmd("\"C:\\Program Files\\Internet Explorer\\iexplore.exe\""); #else QString cmd("xdg-open"); #endif QStringList args = { QString("http://") + host}; process.start(cmd, args); } void AdminTab::enableRebootChecked() { buttonReboot->setEnabled(checkEnableReboot->isChecked()); } void AdminTab::enableResetChecked() { buttonHardReset->setEnabled(checkEnableReset->isChecked()); } void AdminTab::rebootClicked() { QMessageBox::StandardButton result = QMessageBox::question(this, "onvif-gui", "You are about to reboot the camera\nAre you sure you want to do this"); if (result == QMessageBox::Yes) QThreadPool::globalInstance()->tryStart(rebooter); } void AdminTab::hardResetClicked() { QMessageBox::StandardButton result = QMessageBox::question(this, "onvif-gui", "You are about to HARD RESET the camera\nAll settings will be returned to default factory configuration\nAre you sure you want to do this"); if (result == QMessageBox::Yes) QThreadPool::globalInstance()->tryStart(resetter); } void AdminTab::syncTimeClicked() { QThreadPool::globalInstance()->tryStart(timesetter); } void AdminTab::doneRebooting() { buttonReboot->setEnabled(false); checkEnableReboot->setChecked(false); } void AdminTab::doneResetting() { buttonHardReset->setEnabled(false); checkEnableReset->setChecked(false); } void AdminTab::onTextChanged(const QString &) { if (hasBeenEdited()) CP->applyButton->setEnabled(true); else CP->applyButton->setEnabled(false); } libonvif-1.4.4/onvif-gui/src/camerapanel.cpp0000644000175000017500000003070614355557026020716 0ustar stephenstephen/******************************************************************************* * camerapanel.cpp * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include #include "camerapanel.h" #include "mainwindow.h" #include #include #include #include #include #include #include CameraPanel::CameraPanel(QMainWindow *parent) { mainWindow = parent; tabWidget = new QTabWidget(); videoTab = new VideoTab(this); tabWidget->addTab(videoTab, "Video"); imageTab = new ImageTab(this); tabWidget->addTab(imageTab, "Image"); networkTab = new NetworkTab(this); tabWidget->addTab(networkTab, "Network"); ptzTab = new PTZTab(this); tabWidget->addTab(ptzTab, "PTZ"); adminTab = new AdminTab(this); tabWidget->addTab(adminTab, "Admin"); QList screens = QGuiApplication::screens(); QSize screenSize = screens[0]->size(); tabWidget->setMaximumHeight(screenSize.height() * 0.2); applyButton = new QPushButton(this); applyButton->setStyleSheet(MW->getButtonStyle("apply")); connect(applyButton, SIGNAL(clicked()), this, SLOT(applyButtonClicked())); discoverButton = new QPushButton(this); discoverButton->setStyleSheet(MW->getButtonStyle("discover")); connect(discoverButton, SIGNAL(clicked()), this, SLOT(discoverButtonClicked())); playButton = new QPushButton(this); playButton->setStyleSheet(MW->getButtonStyle("play")); connect(playButton, SIGNAL(clicked()), this, SLOT(playButtonClicked())); recordButton = new QPushButton(this); recordButton->setStyleSheet(MW->getButtonStyle("record")); connect(recordButton, SIGNAL(clicked()), this, SLOT(recordButtonClicked())); recordButton->setEnabled(false); volumeSlider = new QSlider(Qt::Horizontal, this); volumeSlider->setMaximumHeight(16); int volume = MW->settings->value(volumeKey, 100).toInt(); volumeSlider->setValue(volume); MW->glWidget->setVolume(volume); connect(volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(adjustVolume(int))); btnMute = new QPushButton(); MW->glWidget->setMute(MW->settings->value(muteKey, false).toBool()); if (MW->glWidget->isMute()) btnMute->setStyleSheet(MW->getButtonStyle("mute")); else btnMute->setStyleSheet(MW->getButtonStyle("audio")); connect(btnMute, SIGNAL(clicked()), this, SLOT(onBtnMuteClicked())); QWidget *controlPanel = new QWidget(this); QGridLayout* controlLayout = new QGridLayout(controlPanel); controlLayout->addWidget(playButton, 0, 0, 1, 1); controlLayout->addWidget(recordButton, 0, 1, 1, 1); controlLayout->addWidget(btnMute, 0, 2, 1, 1); controlLayout->addWidget(volumeSlider, 0, 3, 1, 1); controlLayout->addWidget(discoverButton, 0, 4, 1, 1); controlLayout->addWidget(applyButton, 0, 5, 1 ,1); controlPanel->setMaximumHeight(60); cameraList = new CameraListView(mainWindow); QGridLayout *layout = new QGridLayout(); layout->addWidget(cameraList, 0, 0, 1, 1); layout->addWidget(tabWidget, 1, 0, 1, 1); layout->addWidget(controlPanel, 2, 0, 1, 1); layout->setRowStretch(0, 10); setLayout(layout); filler = new Filler(this); connect(filler, SIGNAL(done()), this, SLOT(showData())); videoTab->setActive(false); imageTab->setActive(false); networkTab->setActive(false); ptzTab->setActive(false); adminTab->setActive(false); applyButton->setEnabled(false); connect(this, SIGNAL(msg(const QString&)), mainWindow, SLOT(msg(const QString&))); connect(MW->glWidget, SIGNAL(timerStart()), this, SLOT(streamStarting())); connect(MW->glWidget, SIGNAL(cameraTimeout()), this, SLOT(cameraTimeout())); connect(MW->glWidget, SIGNAL(connectFailed(const QString&)), this, SLOT(connectFailed(const QString&))); connect(MW->glWidget, SIGNAL(openWriterFailed(const std::string&)), this, SLOT(openWriterFailed(const std::string&))); CameraListModel *cameraListModel = cameraList->cameraListModel; connect(cameraListModel, SIGNAL(showCameraData()), this, SLOT(showData())); connect(cameraListModel, SIGNAL(getCameraData()), this, SLOT(fillData())); onvif_session = (OnvifSession*)calloc(sizeof(OnvifSession), 1); initializeSession(onvif_session); discovery = new Discovery(this, MW->settingsPanel); connect(discovery, SIGNAL(stopping()), this, SLOT(discoveryFinished())); cameraNames = new QSettings("Onvif", "Camera Names"); foreach(QString key, cameraNames->allKeys()) { discovery->cameraAlias.insert(key, cameraNames->value(key).toString()); } disableToolTips(MW->settingsPanel->hideToolTips->isChecked()); if (MW->settingsPanel->autoDiscovery->isChecked()) { discovery->start(); } } CameraPanel::~CameraPanel() { closeSession(onvif_session); free(onvif_session); } void CameraPanel::receiveOnvifData(OnvifData *onvif_data) { cameraList->cameraListModel->pushCamera(onvif_data); } void CameraPanel::discoverButtonClicked() { discovery->start(); } void CameraPanel::onBtnMuteClicked() { if (MW->glWidget->isMute()) { btnMute->setStyleSheet(MW->getButtonStyle("audio")); MW->filePanel->btnMute->setStyleSheet(MW->getButtonStyle("audio")); } else { btnMute->setStyleSheet(MW->getButtonStyle("mute")); MW->filePanel->btnMute->setStyleSheet(MW->getButtonStyle("mute")); } MW->glWidget->setMute(!MW->glWidget->isMute()); MW->settings->setValue(muteKey, MW->glWidget->isMute()); } void CameraPanel::cameraListDoubleClicked() { if (connecting) { std::cout << "currently attempting to connect to " << MW->currentStreamingMediaName.toLatin1().data() << " please wait" << std::endl; } else { MW->currentStreamingMediaName = cameraList->getCurrentCamera()->getCameraName(); std::cout << "attempting to connect to " << MW->currentStreamingMediaName.toLatin1().data() << std::endl; std::stringstream ss_uri; OnvifData* onvif_data = cameraList->getCurrentCamera()->onvif_data; std::string uri(onvif_data->stream_uri); ss_uri << uri.substr(0, 7) << onvif_data->username << ":" << onvif_data->password << "@" << uri.substr(7); uri = ss_uri.str(); connecting = true; if (MW->settingsPanel->lowLatency->isChecked()) { MW->glWidget->vpq_size = 1; MW->glWidget->apq_size = 1; } else { MW->glWidget->vpq_size = 100; MW->glWidget->apq_size = 100; } MW->glWidget->disable_audio = MW->settingsPanel->disableAudio->isChecked(); MW->glWidget->mediaShortName = MW->currentStreamingMediaName.toLatin1().data(); MW->glWidget->play(uri.c_str()); MW->setWindowTitle("connecting to " + MW->currentStreamingMediaName); playButton->setStyleSheet(MW->getButtonStyle("stop")); } } void CameraPanel::playButtonClicked() { if (MW->glWidget->process){ if (recording) recordButtonClicked(); recordButton->setEnabled(false); MW->glWidget->stop(); playButton->setStyleSheet(MW->getButtonStyle("play")); } else { if (cameraList->getCurrentCamera()) { QString name = cameraList->getCurrentCamera()->getCameraName(); cameraListDoubleClicked(); } } } void CameraPanel::showLoginDialog(Credential *credential) { if (loginDialog == nullptr) loginDialog = new LoginDialog(this); loginDialog->setStyleSheet(MW->style); QString host = QString(credential->host_name); int start = host.indexOf("//") + 2; int stop = host.indexOf("/", start); int len = stop - start; QString ip = host.mid(start, len); loginDialog->cameraIP->setText(QString("Camera IP: ").append(ip)); loginDialog->cameraName->setText(QString("Camera Name: ").append(credential->camera_name)); if (loginDialog->exec()) { QString username = loginDialog->username->text(); strncpy(credential->username, username.toLatin1(), username.length()); QString password = loginDialog->password->text(); strncpy(credential->password, password.toLatin1(), password.length()); credential->accept_requested = true; } else { emit msg("login cancelled"); memset(credential->username, 0, 128); memset(credential->password, 0, 128); credential->accept_requested = false; } discovery->resume(); } void CameraPanel::applyButtonClicked() { CameraDialogTab *tab = (CameraDialogTab *)tabWidget->currentWidget(); tab->update(); } void CameraPanel::recordButtonClicked() { std::cout << "record button clicked - use environment variable QT_FILESYSTEMMODEL_WATCH_FILES for file size updates" << std::endl; recording = !recording; if (recording) recordButton->setStyleSheet(MW->getButtonStyle("recording")); else recordButton->setStyleSheet(MW->getButtonStyle("record")); QString filename = MW->filePanel->directorySetter->directory; if (MW->settingsPanel->generateFilename->isChecked()) filename.append("/").append(QDateTime::currentDateTime().toString("yyyyMMddhhmmss")).append(".mp4"); else filename.append("/").append("out.mp4"); MW->glWidget->toggle_pipe_out(filename.toLatin1().data()); } void CameraPanel::disableToolTips(bool arg) { if (!arg) { applyButton->setToolTip("Apply"); discoverButton->setToolTip("Discover"); playButton->setToolTip("Play"); recordButton->setToolTip("Record"); btnMute->setToolTip("Mute"); } else { applyButton->setToolTip(""); discoverButton->setToolTip(""); playButton->setToolTip(""); recordButton->setToolTip(""); btnMute->setToolTip(""); } } void CameraPanel::fillData() { videoTab->clear(); imageTab->clear(); networkTab->clear(); adminTab->clear(); videoTab->setActive(false); imageTab->setActive(false); networkTab->setActive(false); adminTab->setActive(false); applyButton->setEnabled(false); QThreadPool::globalInstance()->tryStart(filler); } void CameraPanel::showData() { videoTab->initialize(); imageTab->initialize(); networkTab->initialize(); adminTab->initialize(); videoTab->setActive(true); imageTab->setActive(true); networkTab->setActive(true); adminTab->setActive(true); ptzTab->setActive(camera->hasPTZ()); camera->onvif_data_read = true; applyButton->setEnabled(false); } void CameraPanel::discoveryFinished() { emit msg("discovery is completed"); } void CameraPanel::refreshList() { cameraList->refresh(); } void CameraPanel::adjustVolume(int value) { MW->glWidget->setVolume(value); MW->settings->setValue(volumeKey, value); MW->filePanel->sldVolume->setValue(value); } void CameraPanel::streamStarting() { connecting = false; MW->glWidget->setVolume(volumeSlider->value()); MW->setWindowTitle("Streaming from " + MW->currentStreamingMediaName); recordButton->setEnabled(true); } void CameraPanel::cameraTimeout() { QMessageBox msgBox(this); msgBox.setText("Camera has timed out"); msgBox.exec(); } void CameraPanel::connectFailed(const QString& str) { connecting = false; QString title = "connection failed - "; title += MW->currentStreamingMediaName; MW->setWindowTitle(title); QMessageBox msgBox(this); msgBox.setText(str); msgBox.exec(); } void CameraPanel::openWriterFailed(const std::string& str) { QMessageBox msgBox(this); QString msg = "Writer open failure: "; msg.append(str.c_str()); msgBox.setText(msg); msgBox.exec(); recording = false; recordButton->setStyleSheet(MW->getButtonStyle("record")); }libonvif-1.4.4/onvif-gui/src/settingspanel.cpp0000644000175000017500000003525114355557026021326 0ustar stephenstephen/******************************************************************************* * settingspanel.cpp * * Copyright (c) 2022 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifdef _WIN32 #include #include #pragma comment(lib, "iphlpapi.lib") #else #include #include #include #include #include #include #include #include #endif #include #include #include #include #include #include #include #include #include "settingspanel.h" #include "mainwindow.h" #define EulerConstant = 2.71828 SettingsPanel::SettingsPanel(QMainWindow* parent) { mainWindow = parent; connect(this, SIGNAL(msg(const QString&)), MW, SLOT(msg(const QString&))); networkInterfaces = new QComboBox(); networkInterfaces->setMaximumWidth(300); interfaces = new QListWidget(this); QLabel *lbl03 = new QLabel("Network Interface"); getActiveNetworkInterfaces(); QString netIntf = MW->settings->value(netIntfKey, "").toString(); if (netIntf.length() > 0) networkInterfaces->setCurrentText(netIntf); connect(networkInterfaces, SIGNAL(currentTextChanged(const QString&)), this, SLOT(netIntfChanged(const QString&))); autoDiscovery = new QCheckBox("Auto Discovery"); connect(autoDiscovery, SIGNAL(clicked(bool)), this, SLOT(autoDiscoveryClicked(bool))); autoDiscovery->setChecked(MW->settings->value(autoDiscKey, false).toBool()); QLabel *lbl01 = new QLabel("Common Username"); commonUsername = new QLineEdit(this); commonUsername->setText(MW->settings->value(usernameKey, "").toString()); connect(commonUsername, SIGNAL(editingFinished()), this, SLOT(usernameUpdated())); QLabel *lbl02 = new QLabel("Common Password"); commonPassword = new QLineEdit(this); commonPassword->setText(MW->settings->value(passwordKey, "").toString()); connect(commonPassword, SIGNAL(editingFinished()), this, SLOT(passwordUpdated())); lowLatency = new QCheckBox("Low Latency Buffering"); connect(lowLatency, SIGNAL(clicked()), this, SLOT(lowLatencyClicked())); lowLatency->setChecked(MW->settings->value(lowLatencyKey, false).toBool()); lowLatencyClicked(); disableAudio = new QCheckBox("Disable Audio"); disableAudio->setChecked(MW->settings->value(disAudioKey, false).toBool()); connect(disableAudio, SIGNAL(clicked(bool)), this, SLOT(disableAudioClicked(bool))); hideToolTips = new QCheckBox("Hide Tool Tips"); hideToolTips->setChecked(MW->settings->value(hideTipsKey, false).toBool()); connect(hideToolTips, SIGNAL(clicked()), this, SLOT(hideToolTipsClicked())); keyframeCount = new QSpinBox(this); keyframeCount->setRange(1, 100); keyframeCount->setMaximumWidth(140); lblKeyframeCount = new QLabel("Write Cache Size"); keyframeCount->setValue(MW->settings->value(keyCountKey, 1).toInt()); connect(keyframeCount, SIGNAL(valueChanged(int)), this, SLOT(keyframeCountChanged(int))); QStringList decoders = { "NONE", "CUDA", "VAAPI", "VDPAU", "DXVA2", "D3D11VA", "QSV", "DRM", "OPENCL", }; listDecoders = new QListWidget(this); listDecoders->addItems(decoders); lblDecoders = new QLabel("Hardware Decoder"); hardwareDecoders = new QComboBox(this); hardwareDecoders->setModel(listDecoders->model()); hardwareDecoders->setView(listDecoders); connect(hardwareDecoders, SIGNAL(currentTextChanged(const QString&)), this, SLOT(decoderChanged(const QString&))); hardwareDecoders->setCurrentText(MW->settings->value(decoderKey, "NONE").toString()); generateFilename = new QRadioButton("Generate Unique Filename"); generateFilename->setChecked(MW->settings->value(genFileKey, true).toBool()); connect(generateFilename, SIGNAL(clicked(bool)), this, SLOT(generateFilenameClicked(bool))); defaultFilename = new QRadioButton("Use Default Filename"); defaultFilename->setChecked(MW->settings->value(defFileKey, false).toBool()); connect(defaultFilename, SIGNAL(clicked(bool)), this, SLOT(defaultFilenameClicked(bool))); QLabel *lbl10 = new QLabel("Zoom"); zoom = new QSlider(Qt::Vertical); zoom->setValue(0); zoom->setStyleSheet(MW->style); connect(zoom, SIGNAL(valueChanged(int)), this, SLOT(zoomMoved(int))); QLabel *lbl11 = new QLabel("Pan X"); panX = new QSlider(Qt::Vertical); panX->setValue(50); connect(panX, SIGNAL(valueChanged(int)), this, SLOT(panXMoved(int))); QLabel *lbl12 = new QLabel("Pan Y"); panY = new QSlider(Qt::Vertical); panY->setValue(50); connect(panY, SIGNAL(valueChanged(int)), this, SLOT(panYMoved(int))); reset = new QPushButton("Reset"); connect(reset, SIGNAL(clicked()), this, SLOT(resetClicked())); style = new QPushButton("Style Colors"); connect(style, SIGNAL(clicked()), this, SLOT(styleClicked())); clear = new QPushButton("Clear Settings"); connect(clear, SIGNAL(clicked()), this, SLOT(clearClicked())); QLabel *title = new QLabel("Digital Zoom"); QFrame *sliderFrame = new QFrame(this); sliderFrame->setFrameShape(QFrame::Panel); sliderFrame->setFrameShadow(QFrame::Plain); QGridLayout *frameLayout = new QGridLayout(sliderFrame); frameLayout->addWidget(title, 0, 0, 1, 4, Qt::AlignRight); frameLayout->addWidget(zoom, 1, 0, 1, 1, Qt::AlignHCenter); frameLayout->addWidget(panX, 1, 1, 1, 1, Qt::AlignHCenter); frameLayout->addWidget(panY, 1, 2, 1, 1, Qt::AlignHCenter); frameLayout->addWidget(lbl10, 2, 0, 1, 1, Qt::AlignCenter); frameLayout->addWidget(lbl11, 2, 1, 1, 1, Qt::AlignCenter); frameLayout->addWidget(lbl12, 2, 2, 1, 1, Qt::AlignCenter); frameLayout->addWidget(reset, 1, 3, 1, 1); QGroupBox *groupBox = new QGroupBox("Record Filename", this); QGridLayout *groupLayout = new QGridLayout(groupBox); groupLayout->addWidget(generateFilename, 0, 0, 1, 1); groupLayout->addWidget(defaultFilename, 1, 0, 1, 1); QGridLayout *layout = new QGridLayout(this); layout->addWidget(lbl03, 0, 0, 1, 1); layout->addWidget(networkInterfaces, 0, 1, 1, 1); layout->addWidget(autoDiscovery, 1, 0, 1, 2); layout->addWidget(lbl01, 3, 0, 1, 1); layout->addWidget(commonUsername, 3, 1, 1, 1); layout->addWidget(lbl02, 4, 0, 1, 1); layout->addWidget(commonPassword, 4, 1, 1, 1); layout->addWidget(lowLatency, 5, 0, 1, 3); layout->addWidget(disableAudio, 6, 0, 1, 3); layout->addWidget(hideToolTips, 7, 0, 1, 3); layout->addWidget(lblKeyframeCount, 8, 0, 1, 1); layout->addWidget(keyframeCount, 8, 1, 1, 1); layout->addWidget(lblDecoders, 9, 0, 1, 1); layout->addWidget(hardwareDecoders, 9, 1, 1, 2); layout->addWidget(groupBox, 10, 0, 1, 3); layout->addWidget(clear, 11, 0, 1, 1, Qt::AlignCenter); layout->addWidget(style, 11, 1, 1, 1, Qt::AlignCenter); layout->addWidget(sliderFrame, 12, 0, 2, 3); setLayout(layout); autoDiscoveryClicked(autoDiscovery->isChecked()); MW->glWidget->keyframe_cache_size = keyframeCount->value(); } void SettingsPanel::autoDiscoveryClicked(bool checked) { MW->settings->setValue(autoDiscKey, autoDiscovery->isChecked()); } void SettingsPanel::keyframeCountChanged(int value) { MW->glWidget->keyframe_cache_size = value; MW->settings->setValue(keyCountKey, value); } void SettingsPanel::usernameUpdated() { MW->settings->setValue(usernameKey, commonUsername->text()); } void SettingsPanel::passwordUpdated() { MW->settings->setValue(passwordKey, commonPassword->text()); } void SettingsPanel::lowLatencyClicked() { MW->settings->setValue(lowLatencyKey, lowLatency->isChecked()); if (lowLatency->isChecked()) { MW->glWidget->vpq_size = 1; MW->glWidget->apq_size = 1; } else { MW->glWidget->vpq_size = 100; MW->glWidget->apq_size = 100; } } void SettingsPanel::hideToolTipsClicked() { MW->settings->setValue(hideTipsKey, hideToolTips->isChecked()); MW->cameraPanel->disableToolTips(hideToolTips->isChecked()); MW->filePanel->disableToolTips(hideToolTips->isChecked()); } void SettingsPanel::disableAudioClicked(bool clicked) { MW->settings->setValue(disAudioKey, clicked); } void SettingsPanel::generateFilenameClicked(bool clicked) { MW->settings->setValue(genFileKey, clicked); MW->settings->setValue(defFileKey, !clicked); } void SettingsPanel::defaultFilenameClicked(bool clicked) { MW->settings->setValue(defFileKey, clicked); MW->settings->setValue(genFileKey, !clicked); } void SettingsPanel::zoomMoved(int arg) { MW->glWidget->setZoomFactor(1 - (float)arg / 100.0f); float scale = 10 * pow(10, MW->glWidget->zoom_factor() * -2.108); float range_x = (50.0f - (float)panX->value()) / 50.0f; MW->glWidget->setPanX(range_x * scale); float range_y = (50.0f - (float)panY->value()) / 50.0f; MW->glWidget->setPanY(range_y * scale); } void SettingsPanel::panXMoved(int arg) { float scale = 10 * pow(10, MW->glWidget->zoom_factor() * -2.108); float range = (50.0f - (float)arg) / 50.0f; MW->glWidget->setPanX(range * scale); } void SettingsPanel::panYMoved(int arg) { float scale = 10 * pow(10, MW->glWidget->zoom_factor() * -2.108); float range = (50.0f - (float)arg) / 50.0f; MW->glWidget->setPanY(range * scale); } void SettingsPanel::resetClicked() { zoom->setValue(0); panX->setValue(50); panY->setValue(50); } void SettingsPanel::styleClicked() { MW->styleDialog->exec(); } void SettingsPanel::clearClicked() { QMessageBox::StandardButton result = QMessageBox::question(this, "onvif-gui", "You are about to delete all saved program settings\nAre you sure you want to do this"); if (result == QMessageBox::Yes) MW->settings->clear(); } void SettingsPanel::decoderChanged(const QString& name) { AVHWDeviceType result = AV_HWDEVICE_TYPE_NONE; if (name == "VDPAU") result = AV_HWDEVICE_TYPE_VDPAU; else if (name == "CUDA") result = AV_HWDEVICE_TYPE_CUDA; else if (name == "VAAPI") result = AV_HWDEVICE_TYPE_VAAPI; else if (name == "DXVA2") result = AV_HWDEVICE_TYPE_DXVA2; else if (name == "QSV") result = AV_HWDEVICE_TYPE_QSV; else if (name == "VIDEOTOOLBOX") result = AV_HWDEVICE_TYPE_VIDEOTOOLBOX; else if (name == "D3D11VA") result = AV_HWDEVICE_TYPE_D3D11VA; else if (name == "DRM") result = AV_HWDEVICE_TYPE_DRM; else if (name == "OPENCL") result = AV_HWDEVICE_TYPE_OPENCL; else if (name == "MEDIACODEC") result = AV_HWDEVICE_TYPE_MEDIACODEC; MW->settings->setValue(decoderKey, name); MW->glWidget->hardwareDecoder = result; } void SettingsPanel::netIntfChanged(const QString& arg) { MW->settings->setValue(netIntfKey, arg); } void SettingsPanel::getActiveNetworkInterfaces() { #ifdef _WIN32 PIP_ADAPTER_INFO pAdapterInfo; PIP_ADAPTER_INFO pAdapter = NULL; DWORD dwRetVal = 0; QStringList args; ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO); pAdapterInfo = (IP_ADAPTER_INFO *) malloc(sizeof (IP_ADAPTER_INFO)); if (pAdapterInfo == NULL) { emit msg("Error allocating memory needed to call GetAdaptersinfo"); return; } if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) { free(pAdapterInfo); pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); if (pAdapterInfo == NULL) { emit msg("Error allocating memory needed to call GetAdaptersinfo"); return; } } if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) { pAdapter = pAdapterInfo; while (pAdapter) { if (strcmp(pAdapter->IpAddressList.IpAddress.String, "0.0.0.0")) { char interface_info[1024]; sprintf(interface_info, "%s - %s", pAdapter->IpAddressList.IpAddress.String, pAdapter->Description); emit msg(QString("Network interface info %1").arg(interface_info)); args.push_back(interface_info); } pAdapter = pAdapter->Next; } interfaces->addItems(args); networkInterfaces->setModel(interfaces->model()); networkInterfaces->setView(interfaces); } else { emit msg(QString("GetAdaptersInfo failed with error: %1").arg(dwRetVal)); } if (pAdapterInfo) free(pAdapterInfo); #else struct ifaddrs *ifaddr; int family, s; char host[NI_MAXHOST]; if (getifaddrs(&ifaddr) == -1) { emit msg(QString("Error: getifaddrs failed - %1").arg(strerror(errno))); return; } QStringList args; for (struct ifaddrs *ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL) continue; family = ifa->ifa_addr->sa_family; if (family == AF_INET ) { s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); if (s != 0) { emit msg(QString("getnameinfo() failed: %1").arg(gai_strerror(s))); continue; } if (strcmp(ifa->ifa_name, "lo")) { args.push_back(QString("%1 - %2").arg(host, ifa->ifa_name)); } } } interfaces->addItems(args); networkInterfaces->setModel(interfaces->model()); networkInterfaces->setView(interfaces); freeifaddrs(ifaddr); #endif } void SettingsPanel::getCurrentlySelectedIP(char *buffer) { QString selected = networkInterfaces->currentText(); int index = selected.indexOf(" - "); int i = 0; for (i = 0; i < index; i++) { buffer[i] = selected.toLatin1().data()[i]; } buffer[i] = '\0'; } libonvif-1.4.4/onvif-gui/src/networktab.cpp0000644000175000017500000001462514355557026020630 0ustar stephenstephen/******************************************************************************* * networktab.cpp * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include "camerapanel.h" #include #include #include NetworkTab::NetworkTab(QWidget *parent) { cameraPanel = parent; checkDHCP = new QCheckBox(tr("DHCP Enabled"), this); textIPAddress = new QLineEdit(); textIPAddress->setMaximumWidth(200); textSubnetMask = new QLineEdit(); textSubnetMask->setMaximumWidth(200); textDefaultGateway = new QLineEdit(); textDefaultGateway->setMaximumWidth(200); textDNS = new QLineEdit(); textDNS->setMaximumWidth(200); lblIPAddress = new QLabel("IP Address"); lblSubnetMask = new QLabel("Subnet Mask"); lblDefaultGateway = new QLabel("Gateway"); lblDNS = new QLabel("Primary DNS"); QGridLayout *layout = new QGridLayout(); layout->addWidget(checkDHCP, 0, 1, 1, 1); layout->addWidget(lblIPAddress, 1, 0, 1, 1); layout->addWidget(textIPAddress, 1, 1, 1, 1); layout->addWidget(lblSubnetMask, 2, 0, 1, 1); layout->addWidget(textSubnetMask, 2, 1, 1, 1); layout->addWidget(lblDefaultGateway, 3, 0, 1, 1); layout->addWidget(textDefaultGateway, 3, 1, 1, 1); layout->addWidget(lblDNS, 4, 0, 1, 1); layout->addWidget(textDNS, 4, 1, 1, 1); setLayout(layout); connect(checkDHCP, SIGNAL(clicked()), this, SLOT(dhcpChecked())); connect(textIPAddress, SIGNAL(textChanged(const QString &)), this, SLOT(onTextChanged(const QString &))); connect(textDefaultGateway, SIGNAL(textChanged(const QString &)), this, SLOT(onTextChanged(const QString &))); connect(textDNS, SIGNAL(textChanged(const QString &)), this, SLOT(onTextChanged(const QString &))); connect(textSubnetMask, SIGNAL(textChanged(const QString &)), this, SLOT(onTextChanged(const QString &))); updater = new NetworkUpdater(cameraPanel); connect(updater, SIGNAL(done()), this, SLOT(doneUpdating())); } void NetworkTab::update() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; onvif_data->dhcp_enabled = checkDHCP->isChecked(); QString ip_address = textIPAddress->text(); strncpy(onvif_data->ip_address_buf, ip_address.toLatin1(), ip_address.length()); onvif_data->ip_address_buf[ip_address.length()] = '\0'; QString default_gateway = textDefaultGateway->text(); strncpy(onvif_data->default_gateway_buf, default_gateway.toLatin1(), default_gateway.length()); onvif_data->default_gateway_buf[default_gateway.length()] = '\0'; QString dns = textDNS->text(); strncpy(onvif_data->dns_buf, dns.toLatin1(), dns.length()); onvif_data->dns_buf[dns.length()] = '\0'; onvif_data->prefix_length = mask2prefix(textSubnetMask->text().toLatin1().data()); QThreadPool::globalInstance()->tryStart(updater); } void NetworkTab::clear() { checkDHCP->setChecked(false); textIPAddress->setText(""); textSubnetMask->setText(""); textDefaultGateway->setText(""); textDNS->setText(""); } void NetworkTab::setActive(bool active) { checkDHCP->setEnabled(active); textIPAddress->setEnabled(active); textSubnetMask->setEnabled(active); textDefaultGateway->setEnabled(active); textDNS->setEnabled(active); lblIPAddress->setEnabled(active); lblSubnetMask->setEnabled(active); lblDefaultGateway->setEnabled(active); lblDNS->setEnabled(active); } bool NetworkTab::hasBeenEdited() { bool result = false; OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; if (strcmp(textIPAddress->text().toLatin1().data(), "") != 0) { if (checkDHCP->isChecked() != onvif_data->dhcp_enabled) result = true; if (strcmp(textIPAddress->text().toLatin1().data(), onvif_data->ip_address_buf) != 0) result = true; if (mask2prefix(textSubnetMask->text().toLatin1().data()) != onvif_data->prefix_length) result = true; if (strcmp(textDefaultGateway->text().toLatin1().data(), onvif_data->default_gateway_buf) != 0) result = true; if (strcmp(textDNS->text().toLatin1().data(), onvif_data->dns_buf) != 0) result = true; } return result; } void NetworkTab::initialize() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; textIPAddress->setText(tr(onvif_data->ip_address_buf)); char mask_buf[128] = {0}; prefix2mask(onvif_data->prefix_length, mask_buf); textSubnetMask->setText(tr(mask_buf)); textDNS->setText(tr(onvif_data->dns_buf)); textDefaultGateway->setText(tr(onvif_data->default_gateway_buf)); setDHCP(onvif_data->dhcp_enabled); } void NetworkTab::setDHCP(bool used) { checkDHCP->setChecked(used); textIPAddress->setEnabled(!used); textSubnetMask->setEnabled(!used); textDefaultGateway->setEnabled(!used); textDNS->setEnabled(!used); } void NetworkTab::dhcpChecked() { bool used = checkDHCP->isChecked(); textIPAddress->setEnabled(!used); textSubnetMask->setEnabled(!used); textDefaultGateway->setEnabled(!used); textDNS->setEnabled(!used); if (hasBeenEdited()) ((CameraPanel *)cameraPanel)->applyButton->setEnabled(true); else ((CameraPanel *)cameraPanel)->applyButton->setEnabled(false); } void NetworkTab::onTextChanged(const QString &) { if (hasBeenEdited()) ((CameraPanel *)cameraPanel)->applyButton->setEnabled(true); else ((CameraPanel *)cameraPanel)->applyButton->setEnabled(false); } void NetworkTab::doneUpdating() { fprintf(stderr, "done updating\n"); ((CameraPanel *)cameraPanel)->applyButton->setEnabled(false); } libonvif-1.4.4/onvif-gui/src/onvifmanager.cpp0000644000175000017500000001110514355414521021102 0ustar stephenstephen/******************************************************************************* * onvifmanager.cpp * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include #include "onvifmanager.h" #include "camerapanel.h" Filler::Filler(QWidget *parent) { cameraPanel = parent; setAutoDelete(false); } void Filler::run() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; getCapabilities(onvif_data); getNetworkInterfaces(onvif_data); getNetworkDefaultGateway(onvif_data); getDNS(onvif_data); getVideoEncoderConfigurationOptions(onvif_data); getVideoEncoderConfiguration(onvif_data); getOptions(onvif_data); getImagingSettings(onvif_data); emit done(); } VideoUpdater::VideoUpdater(QWidget *parent) { cameraPanel = parent; setAutoDelete(false); } void VideoUpdater::run() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; setVideoEncoderConfiguration(onvif_data); getProfile(onvif_data); getVideoEncoderConfigurationOptions(onvif_data); getVideoEncoderConfiguration(onvif_data); emit done(); } ImageUpdater::ImageUpdater(QWidget *parent) { cameraPanel = parent; setAutoDelete(false); } void ImageUpdater::run() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; setImagingSettings(onvif_data); getOptions(onvif_data); getImagingSettings(onvif_data); emit done(); } NetworkUpdater::NetworkUpdater(QWidget *parent) { cameraPanel = parent; setAutoDelete(false); } void NetworkUpdater::run() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; setNetworkInterfaces(onvif_data); setDNS(onvif_data); setNetworkDefaultGateway(onvif_data); emit done(); } Rebooter::Rebooter(QWidget *parent) { cameraPanel = parent; setAutoDelete(false); } void Rebooter::run() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; rebootCamera(onvif_data); emit done(); } Resetter::Resetter(QWidget *parent) { cameraPanel = parent; setAutoDelete(false); } void Resetter::run() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; hardReset(onvif_data); emit done(); } Timesetter::Timesetter(QWidget *parent) { cameraPanel = parent; setAutoDelete(false); } void Timesetter::run() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; setSystemDateAndTime(onvif_data); emit done(); } PTZMover::PTZMover(QWidget *parent) { cameraPanel = parent; setAutoDelete(false); } void PTZMover::set(float x_arg, float y_arg, float z_arg) { x = x_arg; y = y_arg; z = z_arg; } void PTZMover::run() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; continuousMove(x, y, z, onvif_data); } PTZStopper::PTZStopper(QWidget *parent) { cameraPanel = parent; setAutoDelete(false); } void PTZStopper::set(int type_arg) { type = type_arg; } void PTZStopper::run() { OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; moveStop(type, onvif_data); } PTZGoto::PTZGoto(QWidget *parent) { cameraPanel = parent; setAutoDelete(false); } void PTZGoto::set(int position_arg) { position = position_arg; } void PTZGoto::run() { char pos[128] = {0}; sprintf(pos, "%d", position); OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; gotoPreset(pos, onvif_data); } PTZSetPreset::PTZSetPreset(QWidget *parent) { cameraPanel = parent; setAutoDelete(false); } void PTZSetPreset::set(int position_arg) { position = position_arg; } void PTZSetPreset::run() { char pos[128] = {0}; sprintf(pos, "%d", position); OnvifData *onvif_data = ((CameraPanel *)cameraPanel)->camera->onvif_data; setPreset(pos, onvif_data); } libonvif-1.4.4/onvif-gui/include/0000755000175000017500000000000014355557026016570 5ustar stephenstephenlibonvif-1.4.4/onvif-gui/include/filepanel.h0000644000175000017500000000711614355557026020705 0ustar stephenstephen/******************************************************************************* * filepanel.h * * Copyright (c) 2022 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef FILEPANEL_H #define FILEPANEL_H #include #include #include #include #include #include #include #include #include #include #include class ProgressLabel : public QLabel { Q_OBJECT public: ProgressLabel(QWidget *parent); void paintEvent(QPaintEvent *event) override; QWidget *filePanel; int x_pos; }; class ProgressSlider : public QSlider { Q_OBJECT public: ProgressSlider(Qt::Orientation o, QWidget *parent); bool event(QEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; QWidget* filePanel; int last_position_x; signals: void seek(float); }; class DirectorySetter : public QWidget { Q_OBJECT public: DirectorySetter(QMainWindow *parent, const QString& labelText); void setPath(const QString& path); QLabel *label; QLineEdit *text; QPushButton *button; QString directory; QMainWindow *mainWindow; signals: void directorySet(const QString&); public slots: void selectDirectory(); }; class TreeView : public QTreeView { public: TreeView(QWidget *parent); void keyPressEvent(QKeyEvent *event) override; void mouseDoubleClickEvent(QMouseEvent *event) override; }; class FilePanel : public QWidget { Q_OBJECT public: FilePanel(QMainWindow *mainWindow); QMainWindow *mainWindow; DirectorySetter *directorySetter; QFileSystemModel *model; TreeView *tree; QMenu *menu; QPushButton *btnMute; QPushButton *btnPlay; QPushButton *btnStop; QIcon icnAudioOn; QIcon icnAudioOff; QIcon icnPlay; QIcon icnPause; QIcon icnStop; ProgressLabel *lblSeek; ProgressSlider *sldProgress; QLabel *lblProgress; QLabel *lblDuration; QSlider *sldVolume; const QString dirKey = "FilePanel/dir"; const QString headerKey = "FilePanel/header"; const QString volumeKey = "Application/volume"; const QString muteKey = "Application/mute"; signals: void msg(const QString&); public slots: void setDirectory(const QString&); void doubleClicked(const QModelIndex&); void showContextMenu(const QPoint&); void headerChanged(int, int, int); void onMenuRemove(); void onMenuRename(); void onMenuInfo(); void onMenuPlay(); void onBtnPlayClicked(); void onBtnStopClicked(); void onBtnMuteClicked(); void onSldVolumeMoved(int); void progress(float); void mediaPlayingFinished(); void mediaPlayingStarted(); void disableToolTips(bool); }; #endif // FILEPANEL_H libonvif-1.4.4/onvif-gui/include/admintab.h0000644000175000017500000000415114355414521020511 0ustar stephenstephen/******************************************************************************* * admintab.h * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef ADMINTAB_H #define ADMINTAB_H #include "cameradialogtab.h" #include "onvifmanager.h" #include #include #include #include #include class AdminTab : public CameraDialogTab { Q_OBJECT public: AdminTab(QWidget *parent); QLineEdit *textCameraName; QLineEdit *textAdminPassword; QPushButton *buttonReboot; QPushButton *buttonHardReset; QPushButton *buttonLaunchBrowser; QPushButton *buttonSyncTime; QCheckBox *checkEnableReboot; QCheckBox *checkEnableReset; QLabel *lblCameraName; QLabel *lblAdminPassword; QWidget *cameraPanel; Rebooter *rebooter; Resetter *resetter; Timesetter *timesetter; QProcess process; void update() override; void clear() override; void setActive(bool active) override; bool hasBeenEdited()override; void initialize(); public slots: void doneRebooting(); void doneResetting(); private slots: void launchBrowserClicked(); void enableRebootChecked(); void enableResetChecked(); void rebootClicked(); void hardResetClicked(); void syncTimeClicked(); void onTextChanged(const QString &); }; #endif // ADMINTAB_H libonvif-1.4.4/onvif-gui/include/onvifmanager.h0000644000175000017500000000652714355414521021417 0ustar stephenstephen/******************************************************************************* * onvifmanager.h * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef ONVIFMANAGER_H #define ONVIFMANAGER_H #include #include #include #define ZOOM_FACTOR 16 class Filler : public QObject, public QRunnable { Q_OBJECT public: Filler(QWidget *parent); void run() override; private: QWidget *cameraPanel; signals: void done(); }; class VideoUpdater : public QObject, public QRunnable { Q_OBJECT public: VideoUpdater(QWidget *parent); void run() override; private: QWidget *cameraPanel; signals: void done(); }; class ImageUpdater : public QObject, public QRunnable { Q_OBJECT public: ImageUpdater(QWidget *parent); void run() override; private: QWidget *cameraPanel; signals: void done(); }; class NetworkUpdater : public QObject, public QRunnable { Q_OBJECT public: NetworkUpdater(QWidget *parent); void run() override; private: QWidget *cameraPanel; signals: void done(); }; class Rebooter : public QObject, public QRunnable { Q_OBJECT public: Rebooter(QWidget *parent); void run() override; private: QWidget *cameraPanel; signals: void done(); }; class Resetter : public QObject, public QRunnable { Q_OBJECT public: Resetter(QWidget *parent); void run() override; private: QWidget *cameraPanel; signals: void done(); }; class Timesetter : public QObject, public QRunnable { Q_OBJECT public: Timesetter(QWidget *parent); void run() override; private: QWidget *cameraPanel; signals: void done(); }; class PTZMover : public QObject, public QRunnable { Q_OBJECT public: PTZMover(QWidget *parent); void run() override; void set(float x_arg, float y_arg, float z_arg); private: QWidget *cameraPanel; float x; float y; float z; }; class PTZStopper : public QObject, public QRunnable { Q_OBJECT public: PTZStopper(QWidget *parent); void run() override; void set(int type_arg); private: QWidget *cameraPanel; int type; }; class PTZGoto : public QObject, public QRunnable { Q_OBJECT public: PTZGoto(QWidget *parent); void run() override; void set(int position_arg); private: QWidget *cameraPanel; int position; }; class PTZSetPreset : public QObject, public QRunnable { Q_OBJECT public: PTZSetPreset(QWidget *parent); void run() override; void set(int position_arg); private: QWidget *cameraPanel; int position; }; #endif // ONVIFMANAGER_H libonvif-1.4.4/onvif-gui/include/settingspanel.h0000644000175000017500000000677514355557026021640 0ustar stephenstephen/******************************************************************************* * settingspanel.h * * Copyright (c) 2022 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef SETTINGSPANEL_H #define SETTINGSPANEL_H #include #include #include #include #include #include #include #include #include #include #include #include class SettingsPanel : public QWidget { Q_OBJECT public: SettingsPanel(QMainWindow *parent); void getActiveNetworkInterfaces(); void getCurrentlySelectedIP(char *buffer); QMainWindow *mainWindow; QCheckBox *autoDiscovery; QPushButton *style; QPushButton *clear; QLineEdit *commonUsername; QLineEdit *commonPassword; QCheckBox *lowLatency; QCheckBox *hideToolTips; QComboBox *hardwareDecoders; QRadioButton *generateFilename; QRadioButton *defaultFilename; QCheckBox *disableAudio; QSlider *zoom; QSlider *panX; QSlider *panY; QPushButton *reset; QListWidget *interfaces; QComboBox *networkInterfaces; QSpinBox *keyframeCount; QLabel *lblKeyframeCount; QStringList decoders; QListWidget *listDecoders; QLabel *lblDecoders; const QString usernameKey = "SettingsPanel/username"; const QString passwordKey = "SettingsPanel/password"; const QString playerKey = "SettingsPanel/player"; const QString autoDiscKey = "SettingsPanel/autoDiscovery"; const QString multiBroadKey = "SettingsPanel/multiBroadcast"; const QString broadRepKey = "SettingsPanel/brodacastRepeat"; const QString lowLatencyKey = "SettingsPanel/lowLatency"; const QString decoderKey = "SettingsPanel/decoder"; const QString netIntfKey = "SettingsPanel/networkInterface"; const QString genFileKey = "SettingsPanel/generateFIlename"; const QString defFileKey = "SettingsPanel/defaultFilename"; const QString disAudioKey = "SettingsPanel/disableAudio"; const QString keyCountKey = "SettingsPanel/keyframeCount"; const QString hideTipsKey = "SettingsPanel/hideToolTips"; signals: void msg(const QString&); public slots: void usernameUpdated(); void passwordUpdated(); void autoDiscoveryClicked(bool); void lowLatencyClicked(); void hideToolTipsClicked(); void decoderChanged(const QString&); void zoomMoved(int); void panXMoved(int); void panYMoved(int); void resetClicked(); void styleClicked(); void clearClicked(); void generateFilenameClicked(bool); void defaultFilenameClicked(bool); void disableAudioClicked(bool); void netIntfChanged(const QString&); void keyframeCountChanged(int); }; #endif // SETTINGSPANEL_Hlibonvif-1.4.4/onvif-gui/include/cameralistmodel.h0000644000175000017500000000372214355414521022102 0ustar stephenstephen/******************************************************************************* * cameralistmodel.h * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef LISTMODEL_H #define LISTMODEL_H #include "camera.h" #include #include #include #include #include class CameraListModel : public QAbstractListModel { Q_OBJECT public: CameraListModel(QMainWindow *parent); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int rolw = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex &index) const override; void pushCamera(OnvifData *onvif_data); Camera * getCameraAt(int index); int current_index = -1; QVector cameras; QMainWindow *mainWindow; signals: void getCameraData(); void showCameraData(); public slots: void onSelectedItemsChanged(QItemSelection selected, QItemSelection deselected); private slots: void beginInsertItems(int start, int end); void endInsertItems(); }; #endif // LISTMODEL_H libonvif-1.4.4/onvif-gui/include/camerapanel.h0000644000175000017500000000623314355557026021215 0ustar stephenstephen/******************************************************************************* * camerapanel.h * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef CAMERAPANEL_H #define CAMERAPANEL_H #include "videotab.h" #include "imagetab.h" #include "networktab.h" #include "ptztab.h" #include "admintab.h" #include "onvifmanager.h" #include "camera.h" #include "cameralistview.h" #include "discovery.h" #include "logindialog.h" #include #include #include #include #include #include #define CP dynamic_cast(cameraPanel) class CameraPanel : public QWidget { Q_OBJECT public: CameraPanel(QMainWindow *parent); ~CameraPanel(); void refreshList(); void saveUsername(); void savePassword(); void saveAutoDiscovery(); void saveMultiBroadcast(); void saveNetIntf(const QString& name); void autoLoadClicked(bool checked); void autoCameraChanged(int index); void saveBroadcastRepeat(int value); Camera *camera; QTabWidget *tabWidget; QSlider* volumeSlider; QPushButton *applyButton; QPushButton *discoverButton; QPushButton *recordButton; QPushButton *playButton; VideoTab *videoTab; ImageTab *imageTab; NetworkTab *networkTab; PTZTab *ptzTab; AdminTab *adminTab; QMainWindow *mainWindow; Filler *filler; CameraListView *cameraList; Discovery *discovery; LoginDialog *loginDialog = nullptr; QSettings *cameraNames; OnvifSession *onvif_session; QPushButton *btnMute; const QString volumeKey = "Application/volume"; const QString muteKey = "Application/mute"; //QString MW->currentStreamingMediaName; bool connecting = false; bool recording = false; std::string uri; char buf[256]; signals: void msg(QString str); public slots: void fillData(); void showData(); void receiveOnvifData(OnvifData*); void showLoginDialog(Credential*); void applyButtonClicked(); void discoverButtonClicked(); void cameraListDoubleClicked(); void recordButtonClicked(); void playButtonClicked(); void discoveryFinished(); void adjustVolume(int); void streamStarting(); void onBtnMuteClicked(); void cameraTimeout(); void connectFailed(const QString&); void openWriterFailed(const std::string&); void disableToolTips(bool); }; #endif // CAMERAPANEL_H libonvif-1.4.4/onvif-gui/include/stylepanel.h0000644000175000017500000000636514355414521021123 0ustar stephenstephen/******************************************************************************* * stylepanel.h * * Copyright (c) 2022 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef STYLEPANEL_H #define STYLEPANEL_H #include #include #include #include #include struct ColorProfile { QString bl; QString bm; QString bd; QString fl; QString fm; QString fd; QString sl; QString sm; QString sd; }; class ColorButton : public QWidget { Q_OBJECT public: ColorButton(QMainWindow *parent, const QString& qss_name, const QString& color_name); QString getStyle() const; void setColor(const QString& color_name); void writeSettings(); void setTempColor(const QString& color_name); QMainWindow *mainWindow; QString name; QColor color; QPushButton *button; QString settingsKey; public slots: void clicked(); }; class StylePanel : public QWidget { Q_OBJECT public: StylePanel(QMainWindow *parent); ColorProfile getProfile() const; void setTempProfile(const ColorProfile& profile); QMainWindow *mainWindow; const QString blDefault = "#566170"; const QString bmDefault = "#3E4754"; const QString bdDefault = "#283445"; const QString flDefault = "#C6D9F2"; const QString fmDefault = "#9DADC2"; const QString fdDefault = "#808D9E"; const QString slDefault = "#FFFFFF"; const QString smDefault = "#DDEEFF"; const QString sdDefault = "#306294"; ColorButton *bl; ColorButton *bm; ColorButton *bd; ColorButton *fl; ColorButton *fm; ColorButton *fd; ColorButton *sl; ColorButton *sm; ColorButton *sd; QCheckBox *useSystemGui; QPushButton *btnDefaults; QPushButton *btnApply; QPushButton *btnCancel; const QString sysGuiKey = "StylePanel/useSystemGui"; public slots: void onBtnDefaultsClicked(); void onBtnApplyClicked(); void sysGuiEnabled(bool); }; class StyleDialog : public QDialog { Q_OBJECT public: StyleDialog(QMainWindow *parent) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint) { mainWindow = parent; setWindowTitle("Themes"); panel = new StylePanel(mainWindow); connect(panel->btnCancel, SIGNAL(clicked()), this, SLOT(close())); QVBoxLayout *layout = new QVBoxLayout(this); layout->addWidget(panel); } QMainWindow *mainWindow; StylePanel *panel; }; #endif // STYLEPANEL_H libonvif-1.4.4/onvif-gui/include/discovery.h0000644000175000017500000000371714355414521020750 0ustar stephenstephen/******************************************************************************* * discovery.h * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef DISCOVERY_H #define DISCOVERY_H #include #include #include #include #include #include #include "onvif.h" #include "logindialog.h" #include "settingspanel.h" class Discovery : public QObject { Q_OBJECT public: Discovery(QWidget *camerPanel, SettingsPanel *settingsPanel); ~Discovery(); void start(); void stop(); void resume(); void discover(); bool isRunning(); bool alreadyLoggedIn(OnvifData *onvif_data); void addCamera(OnvifData *onvif_data); QWidget *cameraPanel; SettingsPanel *settingsPanel; char *username; char *password; QHash cameraAlias; LoginDialog *loginDialog; Credential credential; private: bool running; QThread *thread; QMutex mutex; QWaitCondition waitCondition; signals: void starting(); void stopping(); void found(OnvifData *onvif_data); void login(Credential*); void msg(const QString&); public slots: void run(); }; #endif // DISCOVERY_H libonvif-1.4.4/onvif-gui/include/imagetab.h0000644000175000017500000000322114355414521020500 0ustar stephenstephen/******************************************************************************* * imagetab.h * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef IMAGETAB_H #define IMAGETAB_H #include "cameradialogtab.h" #include "onvifmanager.h" #include #include class ImageTab : public CameraDialogTab { Q_OBJECT public: ImageTab(QWidget *parent); QSlider *sliderBrightness; QSlider *sliderSaturation; QSlider *sliderContrast; QSlider *sliderSharpness; QLabel *lblBrightness; QLabel *lblSaturation; QLabel *lblContrast; QLabel *lblSharpness; QWidget *cameraPanel; ImageUpdater *updater; void update() override; void clear() override; void setActive(bool active) override; bool hasBeenEdited() override; public slots: void initialize(); private slots: void onValueChanged(int value); }; #endif // IMAGETAB_H libonvif-1.4.4/onvif-gui/include/videotab.h0000644000175000017500000000356214355414521020534 0ustar stephenstephen/******************************************************************************* * videotab.h * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef VIDEOTAB_H #define VIDEOTAB_H #include "cameradialogtab.h" #include "onvifmanager.h" #include #include #include #include class SpinBox : public QSpinBox { public: SpinBox(QLineEdit *editor); }; class VideoTab : public CameraDialogTab { Q_OBJECT public: VideoTab(QWidget *parent); void update() override; void clear() override; void setActive(bool active) override; bool hasBeenEdited() override; QWidget *cameraPanel; QListWidget *resolutions = nullptr; QComboBox *comboResolutions; SpinBox *spinFrameRate; SpinBox *spinGovLength; SpinBox *spinBitrate; QLabel *lblResolutions; QLabel *lblFrameRate; QLabel *lblGovLength; QLabel *lblBitrate; VideoUpdater *updater; bool updating = false; public slots: void initialize(); private slots: void onCurrentIndexChanged(int index); void onValueChanged(int value); }; #endif // VIDEOTAB_H libonvif-1.4.4/onvif-gui/include/ptztab.h0000644000175000017500000000415514355414521020242 0ustar stephenstephen/******************************************************************************* * ptztab.h * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef PTZTAB_H #define PTZTAB_H #include "cameradialogtab.h" #include "onvifmanager.h" #include #include #include #include #include #include class PTZTab : public CameraDialogTab { Q_OBJECT public: PTZTab(QWidget *parent); QCheckBox *checkPreset; QCheckBox *checkDigitalPTZ; QLineEdit *textPreset; QComboBox *comboSpeed; QPushButton *button1; QPushButton *button2; QPushButton *button3; QPushButton *button4; QPushButton *button5; QPushButton *buttonUp; QPushButton *buttonDown; QPushButton *buttonLeft; QPushButton *buttonRight; QPushButton *buttonZoomIn; QPushButton *buttonZoomOut; QPushButton *buttonPreset; QLabel *labelZoom; QLabel *labelSpeed; float speed[10]; QWidget *cameraPanel; PTZMover *ptzMover; PTZStopper *ptzStopper; PTZGoto *ptzGoto; PTZSetPreset *ptzSetPreset; void update() override; void setActive(bool active) override; bool hasBeenEdited() override; private slots: void preset(int); void userPreset(); void move(float, float, float); void stopPanTilt(); void stopZoom(); }; #endif // PTZTAB_H libonvif-1.4.4/onvif-gui/include/messagepanel.h0000644000175000017500000000246314355414521021402 0ustar stephenstephen/******************************************************************************* * messagepanel.h * * Copyright (c) 2022 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef MESSAGEPANEL_H #define MESSAGEPANEL_H #include #include #include #include class MessagePanel : public QWidget { Q_OBJECT public: MessagePanel(QMainWindow *parent); QMainWindow *mainWindow; QTextEdit *msg; QPushButton *btnCopy; QClipboard* cp; public slots: void onBtnCopyClicked(); }; #endif // MESSAGEPANEL_Hlibonvif-1.4.4/onvif-gui/include/camera.h0000644000175000017500000000231114355414521020156 0ustar stephenstephen/******************************************************************************* * camera.h * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef CAMERA_H #define CAMERA_H #include "onvif.h" #include class Camera : public QObject { Q_OBJECT public: Camera(OnvifData *arg); ~Camera(); QString getCameraName(); bool hasPTZ(); OnvifData *onvif_data; bool onvif_data_read = false; }; #endif // CAMERA_H libonvif-1.4.4/onvif-gui/include/networktab.h0000644000175000017500000000342514355557026021125 0ustar stephenstephen /******************************************************************************* * networktab.h * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef NETWORKTAB_H #define NETWORKTAB_H #include "cameradialogtab.h" #include "onvifmanager.h" #include #include #include class NetworkTab : public CameraDialogTab { Q_OBJECT public: NetworkTab(QWidget *parent); QCheckBox *checkDHCP; QLineEdit *textIPAddress; QLineEdit *textSubnetMask; QLineEdit *textDefaultGateway; QLineEdit *textDNS; QLabel *lblIPAddress; QLabel *lblSubnetMask; QLabel *lblDefaultGateway; QLabel *lblDNS; QWidget *cameraPanel; NetworkUpdater *updater; void update() override; void clear() override; void setActive(bool active) override; bool hasBeenEdited() override; void initialize(); void setDHCP(bool used); private slots: void dhcpChecked(); void onTextChanged(const QString &); void doneUpdating(); }; #endif // NETWORKTAB_H libonvif-1.4.4/onvif-gui/include/mainwindow.h0000644000175000017500000000456614355557026021130 0ustar stephenstephen/******************************************************************************* * mainwindow.h * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include #include #include #include #include #include #include #include "camerapanel.h" #include "settingspanel.h" #include "messagepanel.h" #include "stylepanel.h" #include "filepanel.h" #include #include #include "GLWidget.h" #define MW dynamic_cast(mainWindow) class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); void showVersion() { std::cout << "1.4.4" << std::endl; } QString getButtonStyle(const QString& name) const; void applyStyle(const ColorProfile& profile); void closeEvent(QCloseEvent* event) override; CameraPanel* cameraPanel; SettingsPanel* settingsPanel; MessagePanel* messagePanel; QTabWidget* tabWidget; StyleDialog* styleDialog; FilePanel* filePanel; QSettings* settings; avio::GLWidget* glWidget; QSplitter* split; const QString splitKey = "MainWindow/splitKey"; QString style; QString currentStreamingMediaName; std::function initPy = nullptr; std::function runPy = nullptr; public slots: void msg(const QString&); void onSplitterMoved(int pos, int index); }; #endif // MAINWINDOW_H libonvif-1.4.4/onvif-gui/include/cameradialogtab.h0000644000175000017500000000236514355414521022036 0ustar stephenstephen/******************************************************************************* * cameradialogtab.h * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef CAMERADIALOGTAB_H #define CAMERADIALOGTAB_H #include #include class CameraDialogTab : public QWidget { Q_OBJECT public: CameraDialogTab(); virtual void update(); virtual void clear(); virtual void setActive(bool active); virtual bool hasBeenEdited(); }; #endif // CAMERADIALOGTAB_H libonvif-1.4.4/onvif-gui/include/logindialog.h0000644000175000017500000000311614355414521021222 0ustar stephenstephen/******************************************************************************* * logindialog.h * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef LOGINDIALOG_H #define LOGINDIALOG_H #include #include #include #include #include #include #include class Credential { public: char camera_name[1024]; char username[128]; char password[128]; char host_name[1024]; bool accept_requested; }; class LoginDialog : public QDialog { Q_OBJECT public: LoginDialog(QWidget *parent); int exec() override; QWidget *cameraPanel; QLabel *cameraIP; QLabel *cameraName; QDialogButtonBox *buttonBox; QLineEdit *username; QLineEdit *password; Credential credential; }; #endif // LOGINDIALOG_H libonvif-1.4.4/onvif-gui/include/cameralistview.h0000644000175000017500000000315714355414521021756 0ustar stephenstephen/******************************************************************************* * cameralistview.h * * Copyright (c) 2020 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #ifndef LISTVIEW_H #define LISTVIEW_H #include "camera.h" #include "cameralistmodel.h" #include #include #include class CameraListView : public QListView { Q_OBJECT public: CameraListView(QMainWindow *parent); Camera *getCurrentCamera(); void setCurrentCamera(const QString& cameraName); void refresh(); QModelIndex previousIndex() const; QModelIndex nextIndex() const; void mouseDoubleClickEvent(QMouseEvent *event) override; void keyPressEvent(QKeyEvent *event) override; QMainWindow *mainWindow; CameraListModel *cameraListModel; QModelIndex index; signals: void connectToCamera(QModelIndex); }; #endif // LISTVIEW_H libonvif-1.4.4/CMakeLists.txt0000644000175000017500000000473114355557026016007 0ustar stephenstephen#******************************************************************************* # libonvif/CMakeLists.txt # # Copyright (c) 2022 Stephen Rhodes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # #******************************************************************************/ cmake_minimum_required(VERSION 3.17) project(libonvif-pkg VERSION 1.4.4) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) if(WIN32) add_compile_options("/EHsc") set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) set(BUILD_SHARED_LIBS TRUE) else() set(CMAKE_CXX_FLAGS "-D_FORTIFY_SOURCE=2 -fstack-protector-strong -Wformat -Werror=format-security") endif() include(GNUInstallDirs) set(CMAKE_INSTALL_INCLUDEDIR "include/${CMAKE_LIBRARY_ARCHITECTURE}") add_subdirectory(libonvif) add_subdirectory(onvif-util) if (BUILD_GUI) add_subdirectory(libavio) add_subdirectory(onvif-gui) install(TARGETS onvif-gui DESTINATION bin) if (WIN32) install(TARGETS avio LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin COMPONENT library ) else() install(TARGETS avio DESTINATION "${CMAKE_INSTALL_LIBDIR}/") endif() endif() install(TARGETS onvif-util DESTINATION bin) if (WIN32) install(TARGETS onvif LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin COMPONENT library ) else() install(TARGETS onvif DESTINATION "${CMAKE_INSTALL_LIBDIR}/") endif() install(FILES libonvif/include/onvif.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") INSTALL(FILES onvif-util/docs/onvif-util.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1) INSTALL(FILES onvif-gui/docs/onvif-gui.1 DESTINATION ${CMAKE_INSTALL_PREFIX}/share/man/man1) libonvif-1.4.4/onvif-util/0000755000175000017500000000000014355557026015336 5ustar stephenstephenlibonvif-1.4.4/onvif-util/CMakeLists.txt0000644000175000017500000000235414355557026020102 0ustar stephenstephen#******************************************************************************* # libonvif/onvif-util/CMakeLists.txt # # Copyright (c) 2022 Stephen Rhodes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # #******************************************************************************/ cmake_minimum_required(VERSION 3.17) project(onvif-util VERSION 1.1.0) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) add_executable(onvif-util src/onvif-util.cpp ) target_link_libraries(onvif-util PRIVATE onvif ) target_include_directories(onvif-util PUBLIC libonvif/include ) libonvif-1.4.4/onvif-util/docs/0000755000175000017500000000000014355414521016256 5ustar stephenstephenlibonvif-1.4.4/onvif-util/docs/manpage.txt0000644000175000017500000001456214355414521020437 0ustar stephenstephenNAME onvif-util - query and adjust onvif compatible cameras SYNOPSIS onvif-util [-ah] [-u ] [-p ] [ip_address] DESCRIPTION View and set parameters on onvif compatible IP cameras. The command may be used to find and identify cameras, and then to create an interactive session that can be used to query and set camera properties. -a, --all show all cameras on the network -h, --help show the help for this command -u, --user set the username for the camera login -p, --password set the password for the camera login To view all cameras on the network: onvif-util -a To login to a particular camera: onvif-util -u username -p password ip_address To login to a camera with safe mode disabled: onvif-util -s -u username -p password ip_address Once logged into the camera you can view data using the 'get' command followed by the data requested. The (n) indicates an optional profile index to apply the setting, otherwise the current profile is used Data Retrieval Commands (start with get) get rtsp 'pass'(optional) (n) - Get rtsp uri for camera, with optional password credential get capabilities get time get profiles get profile (n) get video (n) get video options (n) get imaging get imaging options get network Parameter Setting Commands (start with set) set resolution (n) - Resolution setting in the format widthxheight, must match option set framerate (n) set gov_length (n) set bitrate (n) set bightness value(required) set contrast value(required) set saturation value(required) set sharpness value(required) set ip_address value(required) set default_gateway value(required) set dns value(required) set dhcp value(required) - Accepted settings are 'on' and off' set password value(required) Maintenance Commands help safe - set safe mode on. Viewer and browser are disabled unsafe - set safe mode off. Viewer and browser are enabled browser - Use browser to access camera configurations view (n) - View the camera output using ffplay (ffplay must be installed in the path) view player (n) - View the camera output with user specified player e.g. view vlc sync_time 'zone'(optional) - Sync the camera time to the computer dump - Full set of raw data from camera configuration reboot To Exit Camera Session quit EXAMPLES A typical session would begin by finding the cameras on the network > onvif-util -a Looking for cameras on the network... Found 8 cameras 192.168.1.18 localhost(TV TV-IP319PI) 192.168.1.7 (IPC-BO IPC-122) 192.168.1.14 IPC(Dahua IPC-HDW4631C-A) 192.168.1.6 IPC(Amcrest IP2M-841EB) 192.168.1.12 (AXIS M1065-LW) 192.168.1.12 (AXIS M1065-LW) 192.168.1.2 IPC(Amcrest IP3M-HX2W) 192.168.1.11 R2(IPC-model) To start a session with a camera, use the login credentials > onvif-util -u admin -p admin123 192.168.1.12 found host: 192.168.1.12 successfully connected to host name: AXIS M1065-LW serial: ACCC8E99C915 Get current settings for video > get video Profile set to profile_1_h264 Resolution: 1920 x 1080 Frame Rate: 25 Gov Length: 30 Bit Rate: 4096 Get available video settings > get video options Available Resolutions 1920 x 1080 1280 x 720 640 x 480 320 x 240 Min Gov Length: 1 Max Gov Length: 32767 Min Frame Rate: 1 Max Frame Rate: 30 Min Bit Rate: 1 Max Bit Rate: 2147483647 Set video resolution > set resolution 1280x720 Resolution was set to 1280 x 720 Exit session > quit COPYRIGHT Copyright (c) 2020 Stephen Rhodes This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. SEE ALSO There is a gui version of this program included with the libonvif package which will implement most of the same commands. It may be invoked using the 'onvif' command. The gui has the ability to view camera video output using a player such as ffplay, provided that the player executable is installed in the computer path. NOTES Camera compliance with the onvif standard is often incomplete and in some cases incorrect. Success with the onvif-util may be limited in many cases. Cameras made by Hikvision will have the greatest level of compatibility with onvif-util. Cameras made by Dahua will have a close degree of compatibility with some notable exceptions regarding gateway and DNS settings. Time settings may not be reliable in some cases. If the time is set without the zone flag, the time appearing in the camera feed will be synced to the computer time. If the time zone flag is used, the displayed time may be set to an offset from the computer time based on the timezone setting of the camera. If the camera DNS setting is properly onvif compliant, the IP address may be reliably set using onvif-util. Some cameras may not respond to the DNS setting requested by onvif-util due to non compliance. Note that the camera may reboot automatically under some conditions if the DNS setting is changed from off to on. Video settings are reliable. The Admin Password setting is reliable, as well as sync_time and the reboot command. If there is an issue with a particular setting, it is recommended to connect to the camera using the browser command, as most cameras will have a web interface that will allow you to make the changes reliably. The gui version has a button on the Admin tab that will launch the web browser with the camera ip address automatically.libonvif-1.4.4/onvif-util/docs/onvif-util.10000644000175000017500000001540214355414521020436 0ustar stephenstephen.\" Text automatically generated by txt2man .TH ONVIF-UTIL 1 "09 November 2022" "" "" .SH NAME onvif-util - query and adjust onvif compatible cameras .SH SYNOPSIS .nf .fam C \fBonvif-util\fP [\fB-ah\fP] [\fB-u\fP ] [\fB-p\fP ] [\fIip_address\fP] .fam T .fi .fam T .fi .SH DESCRIPTION View and set parameters on onvif compatible IP cameras. The command may be used to find and identify cameras, and then to create an interactive session that can be used to query and set camera properties. .PP \fB-a\fP, \fB--all\fP show all cameras on the network .PP \fB-h\fP, \fB--help\fP show the help for this command .PP \fB-u\fP, \fB--user\fP set the username for the camera login .PP \fB-p\fP, \fB--password\fP set the password for the camera login .PP To view all cameras on the network: \fBonvif-util\fP \fB-a\fP .PP To login to a particular camera: \fBonvif-util\fP \fB-u\fP username \fB-p\fP password \fIip_address\fP .PP To login to a camera with safe mode disabled: \fBonvif-util\fP \fB-s\fP \fB-u\fP username \fB-p\fP password \fIip_address\fP .PP Once logged into the camera you can view data using the 'get' command followed by the data requested. The (n) indicates an optional profile index to apply the setting, otherwise the current profile is used .PP .nf .fam C Data Retrieval Commands (start with get) get rtsp 'pass'(optional) (n) - Get rtsp uri for camera, with optional password credential get capabilities get time get profiles get profile (n) get video (n) get video options (n) get imaging get imaging options get network Parameter Setting Commands (start with set) set resolution (n) - Resolution setting in the format widthxheight, must match option set framerate (n) set gov_length (n) set bitrate (n) set bightness value(required) set contrast value(required) set saturation value(required) set sharpness value(required) set ip_address value(required) set default_gateway value(required) set dns value(required) set dhcp value(required) - Accepted settings are 'on' and off' set password value(required) Maintenance Commands help safe - set safe mode on. Viewer and browser are disabled unsafe - set safe mode off. Viewer and browser are enabled browser - Use browser to access camera configurations view (n) - View the camera output using ffplay (ffplay must be installed in the path) view player (n) - View the camera output with user specified player e.g. view vlc sync_time 'zone'(optional) - Sync the camera time to the computer dump - Full set of raw data from camera configuration reboot To Exit Camera Session quit .fam T .fi .SH EXAMPLES A typical session would begin by finding the cameras on the network .PP > \fBonvif-util\fP \fB-a\fP .PP .nf .fam C Looking for cameras on the network\.\.\. Found 8 cameras 192.168.1.18 localhost(TV TV-IP319PI) 192.168.1.7 (IPC-BO IPC-122) 192.168.1.14 IPC(Dahua IPC-HDW4631C-A) 192.168.1.6 IPC(Amcrest IP2M-841EB) 192.168.1.12 (AXIS M1065-LW) 192.168.1.12 (AXIS M1065-LW) 192.168.1.2 IPC(Amcrest IP3M-HX2W) 192.168.1.11 R2(IPC-model) .fam T .fi To start a session with a camera, use the login credentials .PP > \fBonvif-util\fP \fB-u\fP admin \fB-p\fP admin123 192.168.1.12 .PP .nf .fam C found host: 192.168.1.12 successfully connected to host name: AXIS M1065-LW serial: ACCC8E99C915 .fam T .fi Get current settings for video .PP > get video .PP .nf .fam C Profile set to profile_1_h264 Resolution: 1920 x 1080 Frame Rate: 25 Gov Length: 30 Bit Rate: 4096 .fam T .fi Get available video settings .PP > get video options .PP .nf .fam C Available Resolutions 1920 x 1080 1280 x 720 640 x 480 320 x 240 Min Gov Length: 1 Max Gov Length: 32767 Min Frame Rate: 1 Max Frame Rate: 30 Min Bit Rate: 1 Max Bit Rate: 2147483647 .fam T .fi Set video resolution .PP > set resolution 1280x720 .PP .nf .fam C Resolution was set to 1280 x 720 .fam T .fi Exit session .PP > quit .SH COPYRIGHT Copyright (c) 2020 Stephen Rhodes .PP This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. .SH SEE ALSO There is a gui version of this program included with the libonvif package which will implement most of the same commands. It may be invoked using the 'onvif' command. The gui has the ability to view camera video output using a player such as ffplay, provided that the player executable is installed in the computer path. .SH NOTES Camera compliance with the onvif standard is often incomplete and in some cases incorrect. Success with the \fBonvif-util\fP may be limited in many cases. Cameras made by Hikvision will have the greatest level of compatibility with \fBonvif-util\fP. Cameras made by Dahua will have a close degree of compatibility with some notable exceptions regarding gateway and DNS settings. Time settings may not be reliable in some cases. If the time is set without the zone flag, the time appearing in the camera feed will be synced to the computer time. If the time zone flag is used, the displayed time may be set to an offset from the computer time based on the timezone setting of the camera. .PP If the camera DNS setting is properly onvif compliant, the IP address may be reliably set using \fBonvif-util\fP. Some cameras may not respond to the DNS setting requested by \fBonvif-util\fP due to non compliance. Note that the camera may reboot automatically under some conditions if the DNS setting is changed from off to on. .PP Video settings are reliable. The Admin Password setting is reliable, as well as sync_time and the reboot command. If there is an issue with a particular setting, it is recommended to connect to the camera using the browser command, as most cameras will have a web interface that will allow you to make the changes reliably. The gui version has a button on the Admin tab that will launch the web browser with the camera ip address automatically. libonvif-1.4.4/onvif-util/src/0000755000175000017500000000000014355414521016115 5ustar stephenstephenlibonvif-1.4.4/onvif-util/src/onvif-util.cpp0000644000175000017500000007454414355414521020733 0ustar stephenstephen/******************************************************************************* * onvif-util.c * * Copyright (c) 2022 Stephen Rhodes * Code contributions by Brian D Scott and Petter Reinholdtsen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *******************************************************************************/ #include #include #include #include #include #include #include #include "onvif.h" #ifdef _WIN32 #include "getopt-win.h" #else #include #endif int longopt = 0; static struct option longopts[] = { { "user", required_argument, NULL, 'u'}, { "password", required_argument, NULL, 'p'}, { "all", no_argument, NULL, 'a'}, { "safe_off", no_argument, NULL, 's'}, { "help", required_argument, NULL, 'h'}, { NULL, 0, NULL, 0 } }; static const char *username = nullptr; static const char *password = nullptr; static void usage() { std::cout << "Usage: onvif-util [-ahs] [-u ] [-p ] [command]" << std::endl; } static void showAll() { std::cout << "Looking for cameras on the network..." << std::endl; struct OnvifSession *onvif_session = (struct OnvifSession*)calloc(sizeof(struct OnvifSession), 1); struct OnvifData *onvif_data = (struct OnvifData*)malloc(sizeof(struct OnvifData)); initializeSession(onvif_session); int n = broadcast(onvif_session); std::cout << "Found " << n << " cameras" << std::endl; for (int i = 0; i < n; i++) { prepareOnvifData(i, onvif_session, onvif_data); char host[128]; extractHost(onvif_data->xaddrs, host); getHostname(onvif_data); printf("%s %s(%s)\n",host, onvif_data->host_name, onvif_data->camera_name); } closeSession(onvif_session); free(onvif_session); free(onvif_data); } static void showHelp() { std::cout << "\n onvif-util help\n\n" << " Usage: onvif-util [-ahs] [-u ] [-p ] [command]\n\n" << " options\n" << " -a poll all cameras on network and reply with host name\n" << " -u username\n" << " -p password\n" << " -s safe mode off, enable applications for viewer and browser to run\n\n" << " To view all cameras on the network:\n" << " onvif-util -a\n\n" << " To login to a particular camera:\n" << " onvif-util -u username -p password ip_address\n\n" << " To login to a camera with safe mode disabled:\n" << " onvif-util -s -u username -p password ip_address\n\n" << " Once logged into the camera you can view data using the 'get' command followed by the data requested.\n" << " The (n) indicates an optional profile index to apply the setting, otherwise the current profile is used.\n\n" << " Data Retrieval Commands (start with get)\n\n" << " get rtsp 'pass'(optional) (n) - Get rtsp uri for camera, with optional password credential\n" << " get capabilities\n" << " get time\n" << " get profiles\n" << " get profile (n)\n" << " get video (n)\n" << " get video options (n)\n" << " get imaging\n" << " get imaging options\n" << " get network\n\n" << " Parameter Setting Commands (start with set)\n\n" << " set resolution (n) - Resolution setting in the format widthxheight, must match the video option\n" << " set framerate (n)\n" << " set gov_length (n)\n" << " set bitrate (n)\n" << " set bightness value(required)\n" << " set contrast value(required)\n" << " set saturation value(required)\n" << " set sharpness value(required)\n" << " set ip_address value(required)\n" << " set default_gateway value(required)\n" << " set dns value(required)\n" << " set dhcp value(required) - Accepted settings are 'on' and off'\n" << " set password value(required)\n\n" << " Maintenance Commands\n\n" << " help\n" << " safe - set safe mode on. Viewer and browser are disabled\n" << " unsafe - set safe mode off. Viewer and browser are enabled\n" << " browser - Use browser to access camera configurations\n" << " view (n) - View the camera output using ffplay (this assmumes you have ffplay installed in the path\n" << " view player (n) - View the camera output with user specified player e.g. view vlc\n" << " dump - Full set of raw data from camera configuration\n" << " sync_time 'zone'(optional) - Sync the camera time to the computer. Optionally adjusts based on camera time zone\n" << " reboot\n\n" << " To Exit Camera Session\n\n" << " quit\n" << std::endl; } const std::string cat(const char* arg1, const char* arg2) { std::string result(arg1); result += arg2; return result; } std::string uri_with_pass(OnvifData* onvif_data) { std::string uri(onvif_data->stream_uri); std::stringstream ss; ss << uri.substr(0, 7) << onvif_data->username << ":" << onvif_data->password << "@" << uri.substr(7); return ss.str(); } void show(const std::vector& args) { std::cout << "args size: " << args.size() << std::endl; for (int i = 0; i < args.size(); i++) { std::cout << "args[" << i << "] = " << args[i] << std::endl; } } void profileCheck(OnvifData* onvif_data, const std::vector& args) { int index = 0; if (args.size() > 1) { index = std::stoi(args[1]); if (getProfileToken(onvif_data, index)) throw std::runtime_error(cat("get profile token - ", onvif_data->last_error)); if (strlen(onvif_data->profileToken) == 0) throw std::runtime_error(cat("invalid profile token - ", (char*)std::to_string(index).c_str()).data()); std::cout << " Profile set to " << onvif_data->profileToken << "\n" << std::endl; } else { if (!strcmp(onvif_data->profileToken, "")) { if (getProfileToken(onvif_data, index)) throw std::runtime_error(cat("get profile token - ", onvif_data->last_error)); if (strlen(onvif_data->profileToken) == 0) throw std::runtime_error(cat("invalid profile token - ", (char*)std::to_string(index).c_str()).data()); std::cout << " Profile set to " << onvif_data->profileToken << "\n" << std::endl; } else { std::cout << std::endl; } } if (getProfile(onvif_data)) throw std::runtime_error(cat("get profile - ", onvif_data->last_error)); } int main(int argc, char **argv) { bool safe_mode = true; int ch; while ((ch = getopt_long(argc, argv, "u:p:ahs", longopts, NULL)) != -1) { switch (ch) { case 'u': username = optarg; break; case 'p': password = optarg; break; case 'a': showAll(); exit(0); case 'h': usage(); showHelp(); exit(0); case 's': safe_mode = false; break; case 0: std::cout << optarg << std::endl; break; default: usage(); exit(1); } } argc -= optind; argv += optind; if (argc < 1) { usage(); exit(1); } char *wanted = argv++[0]; struct OnvifSession *onvif_session = (struct OnvifSession*)calloc(sizeof(struct OnvifSession), 1); struct OnvifData *onvif_data = (struct OnvifData*)malloc(sizeof(struct OnvifData)); initializeSession(onvif_session); int n = broadcast(onvif_session); for (int i = 0; i < n; i++) { prepareOnvifData(i, onvif_session, onvif_data); char host[128]; extractHost(onvif_data->xaddrs, host); getHostname(onvif_data); if (!strcmp(host, wanted)) { std::cout << " found host: " << host << std::endl; if (username) strcpy(onvif_data->username, username); if (password) strcpy(onvif_data->password, password); if (getDeviceInformation(onvif_data) == 0) { std::cout << " successfully connected to host" << "\n"; std::cout << " name: " << onvif_data->camera_name << "\n"; std::cout << " serial: " << onvif_data->serial_number << "\n" << std::endl; // Initializing the session properly with the camera requires calling getCapabilities if (getCapabilities(onvif_data)) { std::cout << "ERROR: get capabilities - " << onvif_data->last_error << "\n" << std::endl; exit(1); } break; } else { std::cout << "ERROR: get device information - " << onvif_data->last_error << "\n" << std::endl; exit(1); } } if (i == n - 1) { std::cout << "ERROR: camera " << wanted << " not found" << "\n" << std::endl; exit(1); } } char kybd_buf[128] = {0}; while (strcmp(kybd_buf, "quit")) { memset(kybd_buf, 0, 128); fgets(kybd_buf, 128, stdin); kybd_buf[strcspn(kybd_buf, "\r\n")] = 0; std::string cmd(kybd_buf); if (cmd.length() == 0) continue; std::string arg; std::vector args; std::stringstream ss(cmd); while (ss >> arg) args.push_back(arg); try { if (args[0] == "get") { args.erase(args.begin()); if (args[0] == "rtsp") { bool add_pass = false; if (args.size() > 1) { if (args[1] == "pass") { args.erase(args.begin()); add_pass = true; } } profileCheck(onvif_data, args); if (getStreamUri(onvif_data)) throw std::runtime_error(cat("get stream uri - ", onvif_data->last_error)); std::string uri(onvif_data->stream_uri); if (add_pass) { uri = uri_with_pass(onvif_data); } std::cout << " " << uri << "\n" << std::endl; } else if (args[0] == "capabilities") { std::cout << " event_service: " << onvif_data->event_service << "\n"; std::cout << " imaging_service: " << onvif_data->imaging_service << "\n"; std::cout << " media_service: " << onvif_data->imaging_service << "\n"; std::cout << " ptz_service: " << onvif_data->imaging_service << "\n" << std::endl; } else if (args[0] == "profiles") { int index = 0; bool looking = true; while (looking) { memset(onvif_data->profileToken, 0, 128); if (getProfileToken(onvif_data, index)) throw std::runtime_error(cat("get profile token - ", onvif_data->last_error)); if (strlen(onvif_data->profileToken) == 0) looking = false; else std::cout << " Token " << index << ": " << onvif_data->profileToken << "\n"; index++; } std::cout << std::endl; } else if (args[0] == "profile") { profileCheck(onvif_data, args); std::cout << " Width: " << onvif_data->width << "\n"; std::cout << " Height: " << onvif_data->height << "\n"; std::cout << " Frame Rate: " << onvif_data->frame_rate << "\n"; std::cout << " Gov Length: " << onvif_data->gov_length << "\n"; std::cout << " Bitrate: " << onvif_data->bitrate << "\n" << std::endl; } else if (args[0] == "time") { if (getTimeOffset(onvif_data)) throw std::runtime_error(cat("get time offset - ", onvif_data->last_error)); std::cout << " Time Offset: " << onvif_data->time_offset << " seconds" << "\n"; std::cout << " Timezone: " << onvif_data->timezone << "\n"; std::cout << " DST: " << (onvif_data->dst ? "Yes" : "No") << "\n"; std::cout << " Time Set By: " << ((onvif_data->datetimetype == 'M') ? "Manual" : "NTP") << "\n"; if (onvif_data->datetimetype != 'M') { if (getNTP(onvif_data)) throw std::runtime_error(cat("get NTP - ", onvif_data->last_error)); std::cout << " NTP Server: " << onvif_data->ntp_addr << "\n"; } std::cout << std::endl; } else if (args[0] == "video") { if (args[1] == "options") { args.erase(args.begin()); profileCheck(onvif_data, args); if(getVideoEncoderConfigurationOptions(onvif_data)) throw std::runtime_error(cat("get video encoder configuration options - ", onvif_data->last_error)); int size = 0; bool found_size = false; while (!found_size) { if (strlen(onvif_data->resolutions_buf[size]) == 0) { found_size = true; } else { size++; if (size > 15) found_size = true; } } std::cout << " Available Resolutions" << std::endl; for (int i=0; iresolutions_buf[i] << std::endl; } std::cout << " Min Gov Length: " << onvif_data->gov_length_min << "\n"; std::cout << " Max Gov Length: " << onvif_data->gov_length_max << "\n"; std::cout << " Min Frame Rate: " << onvif_data->frame_rate_min << "\n"; std::cout << " Max Frame Rate: " << onvif_data->frame_rate_max << "\n"; std::cout << " Min Bit Rate: " << onvif_data->bitrate_min << "\n"; std::cout << " Max Bit Rate: " << onvif_data->bitrate_max << "\n" << std::endl; } else { profileCheck(onvif_data, args); if(getVideoEncoderConfigurationOptions(onvif_data)) throw std::runtime_error(cat("get video encoder configuration options - ", onvif_data->last_error)); std::cout << " Resolution: " << onvif_data->width << " x " << onvif_data->height << "\n"; std::cout << " Frame Rate: " << onvif_data->frame_rate << "\n"; std::cout << " Gov Length: " << onvif_data->gov_length << "\n"; std::cout << " Bit Rate: " << onvif_data->bitrate << "\n" << std::endl; } } else if (args[0] == "imaging") { if (args[1] == "options") { args.erase(args.begin()); profileCheck(onvif_data, args); if (getOptions(onvif_data)) throw std::runtime_error(cat("get options - ", onvif_data->last_error)); std::cout << " Min Brightness: " << onvif_data->brightness_min << "\n"; std::cout << " Max Brightness: " << onvif_data->brightness_max << "\n"; std::cout << " Min ColorSaturation: " << onvif_data->saturation_min << "\n"; std::cout << " Max ColorSaturation: " << onvif_data->saturation_max << "\n"; std::cout << " Min Contrast: " << onvif_data->contrast_min << "\n"; std::cout << " Max Contrast: " << onvif_data->contrast_max << "\n"; std::cout << " Min Sharpness: " << onvif_data->sharpness_min << "\n"; std::cout << " Max Sharpness: " << onvif_data->sharpness_max << "\n" << std::endl; } else { profileCheck(onvif_data, args); if (getImagingSettings(onvif_data)) throw std::runtime_error(cat("get imaging settings - ", onvif_data->last_error)); std::cout << " Brightness: " << onvif_data->brightness << "\n"; std::cout << " Contrast: " << onvif_data->contrast << "\n"; std::cout << " Saturation: " << onvif_data->saturation << "\n"; std::cout << " Sharpness: " << onvif_data->sharpness << "\n" << std::endl; } } else if (args[0] == "network") { profileCheck(onvif_data, args); if (getNetworkInterfaces(onvif_data)) throw std::runtime_error(cat("get network interfaces - ", onvif_data->last_error)); if (getNetworkDefaultGateway(onvif_data)) throw std::runtime_error(cat("get network default gateway - ", onvif_data->last_error)); if (getDNS(onvif_data)) throw std::runtime_error(cat("get DNS - ", onvif_data->last_error)); std::cout << " IP Address: " << onvif_data->ip_address_buf << "\n"; std::cout << " Gateway: " << onvif_data->default_gateway_buf << "\n"; std::cout << " DNS: " << onvif_data->dns_buf << "\n"; std::cout << " DHCP: " << (onvif_data->dhcp_enabled ? "YES" : "NO") << "\n" << std::endl; } else { std::cout << " Unrecognized command, use onvif-util -h to see help\n" << std::endl; } } else if (args[0] == "set") { args.erase(args.begin()); if (args[0] == "brightness") { if (args.size() > 1) { args.erase(args.begin()); profileCheck(onvif_data, args); if (getImagingSettings(onvif_data)) throw std::runtime_error(cat("get imaging settings - ", onvif_data->last_error)); int value = stoi(args[0]); onvif_data->brightness = value; if (setImagingSettings(onvif_data)) throw std::runtime_error(cat("set brightness - ", onvif_data->last_error)); std::cout << " Brightness was set to " << value << "\n" << std::endl; } else { std::cout << " Missing value for brightness\n" << std::endl; } } else if (args[0] == "contrast") { if (args.size() > 1) { args.erase(args.begin()); profileCheck(onvif_data, args); if (getImagingSettings(onvif_data)) throw std::runtime_error(cat("get imaging settings - ", onvif_data->last_error)); int value = stoi(args[0]); onvif_data->contrast = value; if (setImagingSettings(onvif_data)) throw std::runtime_error(cat("set contrast - ", onvif_data->last_error)); std::cout << " Contrast was set to " << value << "\n" << std::endl; } else { std::cout << " Missing value for contrast\n" << std::endl; } } else if (args[0] == "saturation") { if (args.size() > 1) { args.erase(args.begin()); profileCheck(onvif_data, args); if (getImagingSettings(onvif_data)) throw std::runtime_error(cat("get imaging settings - ", onvif_data->last_error)); int value = stoi(args[0]); onvif_data->saturation = value; if (setImagingSettings(onvif_data)) throw std::runtime_error(cat("set saturation - ", onvif_data->last_error)); std::cout << " Saturation was set to " << value << "\n" << std::endl; } else { std::cout << " Missing value for saturation\n" << std::endl; } } else if (args[0] == "sharpness") { if (args.size() > 1) { args.erase(args.begin()); profileCheck(onvif_data, args); if (getImagingSettings(onvif_data)) throw std::runtime_error(cat("get imaging settings - ", onvif_data->last_error)); int value = stoi(args[0]); onvif_data->sharpness = value; if (setImagingSettings(onvif_data)) throw std::runtime_error(cat("set sharpness - ", onvif_data->last_error)); std::cout << " Sharpness was set to " << value << "\n" << std::endl; } else { std::cout << " Missing value for sharpness\n" << std::endl; } } else if (args[0] == "resolution") { if (args.size() > 1) { args.erase(args.begin()); profileCheck(onvif_data, args); std::string delim = "x"; std::size_t found = args[0].find(delim); if (found != std::string::npos) { if(getVideoEncoderConfiguration(onvif_data)) throw std::runtime_error(cat("get video encoder configuration - ", onvif_data->last_error)); onvif_data->width = stoi(args[0].substr(0, found)); onvif_data->height = stoi(args[0].substr(found+1)); if (setVideoEncoderConfiguration(onvif_data)) throw std::runtime_error(cat("set video encoder configuration - ", onvif_data->last_error)); std::cout << " Resolution was set to " << onvif_data->width << " x " << onvif_data->height << "\n" << std::endl; } else { std::cout << " Syntax error, proper format for the argument is widthxheight e.g. 1280x720" << std::endl; } } } else if (args[0] == "gov_length") { if (args.size() > 1) { args.erase(args.begin()); profileCheck(onvif_data, args); if(getVideoEncoderConfiguration(onvif_data)) throw std::runtime_error(cat("get video encoder configuration - ", onvif_data->last_error)); int value = stoi(args[0]); onvif_data->gov_length = value; if (setVideoEncoderConfiguration(onvif_data)) throw std::runtime_error(cat("set video encoder configuration - ", onvif_data->last_error)); std::cout << " Gov Length was set to " << value << "\n" << std::endl; } else { std::cout << " Missing value for Gov Length\n" << std::endl; } } else if (args[0] == "framerate") { if (args.size() > 1) { args.erase(args.begin()); profileCheck(onvif_data, args); if(getVideoEncoderConfiguration(onvif_data)) throw std::runtime_error(cat("get video encoder configuration - ", onvif_data->last_error)); int value = stoi(args[0]); onvif_data->frame_rate = value; if (setVideoEncoderConfiguration(onvif_data)) throw std::runtime_error(cat("set video encoder configuration - ", onvif_data->last_error)); std::cout << " Frame Rate was set to " << value << "\n" << std::endl; } else { std::cout << " Missing value for Frame Rate\n" << std::endl; } } else if (args[0] == "bitrate") { if (args.size() > 1) { args.erase(args.begin()); profileCheck(onvif_data, args); if(getVideoEncoderConfiguration(onvif_data)) throw std::runtime_error(cat("get video encoder configuration - ", onvif_data->last_error)); int value = stoi(args[0]); onvif_data->bitrate = value; if (setVideoEncoderConfiguration(onvif_data)) throw std::runtime_error(cat("set video encoder configuration - ", onvif_data->last_error)); std::cout << " Bitrate was set to " << value << "\n" << std::endl; } else { std::cout << " Missing value for Bitrate\n" << std::endl; } } else if (args[0] == "dhcp") { if (args.size() > 1) { args.erase(args.begin()); if (args[0] == "on") { profileCheck(onvif_data, args); if (getNetworkInterfaces(onvif_data)) throw std::runtime_error(cat("get network interfaces - ", onvif_data->last_error)); if (onvif_data->dhcp_enabled) { std::cout << " DHCP is already enabled\n" << std::endl; } else { onvif_data->dhcp_enabled = true; if (setNetworkInterfaces(onvif_data)) throw std::runtime_error(cat("set network interfaces - ", onvif_data->last_error)); std::cout << " DHCP was enabled successfully\n\n" << " Camera may or may not reboot depending on settings\n" << " Session is being terminated.\n" << std::endl; exit(0); } } else if (args[0] == "off") { profileCheck(onvif_data, args); if (getNetworkInterfaces(onvif_data)) throw std::runtime_error(cat("get network interfaces - ", onvif_data->last_error)); if (!onvif_data->dhcp_enabled) { std::cout << " DHCP is already disabled\n" << std::endl; } else { onvif_data->dhcp_enabled = false; if (setNetworkInterfaces(onvif_data)) throw std::runtime_error(cat("set network interfaces - ", onvif_data->last_error)); std::cout << " DHCP disabled\n" << std::endl; } } else { std::cout << " Invalid value for DHCP, use either 'on' or 'off\n" << std::endl; } } else { std::cout << " Missing value for DHCP\n" << std::endl; } } else if (args[0] == "ip_address") { if (args.size() > 1) { args.erase(args.begin()); profileCheck(onvif_data, args); if (getNetworkInterfaces(onvif_data)) throw std::runtime_error(cat("get network interfaces - ", onvif_data->last_error)); if (onvif_data->dhcp_enabled) { std::cout << " Camera DHCP is enabled, IP address may not be set manually\n" << std::endl; } else { strcpy(onvif_data->ip_address_buf, args[0].c_str()); if (setNetworkInterfaces(onvif_data)) throw std::runtime_error(cat("set network interfaces - ", onvif_data->last_error)); std::cout << " IP Address has been changed, session will need to be restarted\n" << std::endl; exit(0); } } else { std::cout << " Missing value for IP address\n" << std::endl; } } else if (args[0] == "default_gateway") { if (args.size() > 1) { args.erase(args.begin()); profileCheck(onvif_data, args); if (getNetworkDefaultGateway(onvif_data)) throw std::runtime_error(cat("get network default gateway - ", onvif_data->last_error)); if (onvif_data->dhcp_enabled) { std::cout << " Camera DHCP is enabled, default gateway may not be set manually\n" << std::endl; } else { strcpy(onvif_data->default_gateway_buf, args[0].c_str()); if (setNetworkDefaultGateway(onvif_data)) throw std::runtime_error(cat("set default gateway - ", onvif_data->last_error)); std::cout << " Default gateway has been changed\n" << std::endl; } } else { std::cout << " Missing value for default gateway\n" << std::endl; } } else if (args[0] == "dns") { if (args.size() > 1) { args.erase(args.begin()); profileCheck(onvif_data, args); if (getDNS(onvif_data)) throw std::runtime_error(cat("get DNS - ", onvif_data->last_error)); if (onvif_data->dhcp_enabled) { std::cout << " Camera DHCP is enabled, DNS may not be set manually\n" << std::endl; } else { strcpy(onvif_data->dns_buf, args[0].c_str()); if (setDNS(onvif_data)) throw std::runtime_error(cat("set DNS - ", onvif_data->last_error)); std::cout << " DNS has been changed\n" << std::endl; } } else { std::cout << " Missing value for DNS\n" << std::endl; } } else if (args[0] == "password") { if (args.size() > 1) { args.erase(args.begin()); profileCheck(onvif_data, args); char buf[128] = {0}; strcpy(buf, args[0].c_str()); if (setUser(buf, onvif_data)) throw std::runtime_error(cat("set user - ", onvif_data->last_error)); std::cout << " Admin password has been reset\n" << std::endl; } else { std::cout << " Missing value for admin password\n" << std::endl; } } else { std::cout << " Unrecognized command, use onvif-util -h to see help\n" << std::endl; } } else if (args[0] == "reboot") { std::cout << " Are you sure you want to reboot? Type yes to confirm\n" << std::endl; memset(kybd_buf, 0, 128); fgets(kybd_buf, 128, stdin); kybd_buf[strcspn(kybd_buf, "\r\n")] = 0; std::string reply(kybd_buf); if (reply == "yes") { if (rebootCamera(onvif_data)) throw std::runtime_error(cat("reboot camera - ", onvif_data->last_error)); std::cout << " Camera is rebooting...\n" << " Session will be terminated" << std::endl; } else { std::cout << " Confirmation not received, reboot cancelled\n" << std::endl; } } else if (args[0] == "sync_time") { if (args.size() > 1) { args.erase(args.begin()); profileCheck(onvif_data, args); if (args[0] == "zone") { profileCheck(onvif_data, args); if (setSystemDateAndTimeUsingTimezone(onvif_data)) throw std::runtime_error(cat("set system date and time using timezone - ", onvif_data->last_error)); std::cout << " Camera date and time has been synchronized using the camera timezone\n" << std::endl; } } else { profileCheck(onvif_data, args); if (setSystemDateAndTime(onvif_data)) throw std::runtime_error(cat("set system date and time - ", onvif_data->last_error)); std::cout << " Camera date and time has been synchronized without regard to camera timezone\n" << std::endl; } } else if (args[0] == "dump") { dumpConfigAll (onvif_data); std::cout << std::endl; } else if (args[0] == "safe") { safe_mode = true; std::cout << " Safe mode is on\n" << std::endl; } else if (args[0] == "unsafe") { safe_mode = false; std::cout << " Safe mode has been turned off, run only known safe apps and cameras\n" << std::endl; } else if (args[0] == "view") { if (safe_mode) { std::cout << " SAFE MODE ON, use 'unsafe' command to disable safe mode, or -s option from command line\n" << std::endl; continue; } std::string player("ffplay"); if (args.size() > 1) { args.erase(args.begin()); profileCheck(onvif_data, args); player = args[0]; } else { profileCheck(onvif_data, args); } if (getStreamUri(onvif_data)) throw std::runtime_error(cat("get stream uri - ", onvif_data->last_error)); std::stringstream ss; #ifdef _WIN32 ss << "start " << player << " \"" << uri_with_pass(onvif_data) << "\""; #else ss << player << " \"" << uri_with_pass(onvif_data) << "\""; #endif std::system(ss.str().c_str()); } else if (args[0] == "browser") { if (safe_mode) { std::cout << " SAFE MODE ON, use 'unsafe' command to disable safe mode, or -s option from command line\n" << std::endl; continue; } profileCheck(onvif_data, args); if (getNetworkInterfaces(onvif_data)) throw std::runtime_error(cat("get network interfaces - ", onvif_data->last_error)); std::stringstream ss; #ifdef _WIN32 ss << "start http://" << onvif_data->ip_address_buf; #else ss << "xdg-open http://" << onvif_data->ip_address_buf; #endif std::system(ss.str().c_str()); } else if (args[0] == "help") { showHelp(); } else { if (strcmp(kybd_buf, "quit")) std::cout << " Unrecognized command, use onvif-util -h to see help\n" << std::endl; } } catch (std::exception& e) { std::cout << " ERROR: " << e.what() << "\n" << std::endl; } } } /* else if (args[0] == "ntp") { if (args.size() > 1) { args.erase(args.begin()); if (args[0] == "manual") { profileCheck(onvif_data, args); if (getHostname(onvif_data)) throw std::runtime_error(cat("get host name - ", onvif_data->last_error)); if (getTimeOffset(onvif_data)) throw std::runtime_error(cat("get time offset - ", onvif_data->last_error)); onvif_data->datetimetype = 'M'; if (setSystemDateAndTime(onvif_data)) throw std::runtime_error(cat("set NTP - ", onvif_data->last_error)); std::cout << " NTP set to manual\n" << std::endl; } else { std::cout << "DHCP NTP" << std::endl; profileCheck(onvif_data, args); if (getHostname(onvif_data)) throw std::runtime_error(cat("get host name - ", onvif_data->last_error)); if (getTimeOffset(onvif_data)) throw std::runtime_error(cat("get time offset - ", onvif_data->last_error)); onvif_data->datetimetype = 'N'; onvif_data->ntp_dhcp = false; strcpy(onvif_data->ntp_addr, "192.168.1.1"); strcpy(onvif_data->ntp_type, "IPv4"); if (setSystemDateAndTime(onvif_data)) throw std::runtime_error(cat("set NTP - ", onvif_data->last_error)); if (setNTP(onvif_data)) throw std::runtime_error(cat("set ntp - ", onvif_data->last_error)); } } else { std::cout << " Missing value for NTP\n" << std::endl; } } */ libonvif-1.4.4/libavio/0000755000175000017500000000000014355557026014667 5ustar stephenstephenlibonvif-1.4.4/libavio/CMakeLists.txt0000644000175000017500000000342114355566436017434 0ustar stephenstephen#******************************************************************************* # libavio/CMakeLists.txt # # Copyright (c) 2022 Stephen Rhodes # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # #******************************************************************************/ cmake_minimum_required(VERSION 3.17) project(libavio VERSION 1.1.0) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D__STDC_CONSTANT_MACROS") list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) if(WIN32) add_compile_options("/EHsc") # find_package(Iconv REQUIRED) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) set(BUILD_SHARED_LIBS TRUE) endif() find_package(SDL2 REQUIRED) find_package(FFmpeg REQUIRED) find_package(Qt5 COMPONENTS Widgets REQUIRED) set(CMAKE_AUTOMOC ON) add_library(avio SHARED src/Reader.cpp src/Exception.cpp src/Frame.cpp src/Decoder.cpp src/Filter.cpp src/Clock.cpp src/Display.cpp src/Encoder.cpp src/Writer.cpp src/Pipe.cpp src/GLWidget.cpp include/GLWidget.h ) target_link_libraries(avio PRIVATE Qt5::Widgets FFmpeg::FFmpeg SDL2::SDL2 pthread ) set_target_properties(avio PROPERTIES SOVERSION 1 ) target_include_directories(avio PUBLIC include )libonvif-1.4.4/libavio/cmake/0000755000175000017500000000000014355557026015747 5ustar stephenstephenlibonvif-1.4.4/libavio/cmake/FindFFmpeg.cmake0000644000175000017500000000650214355557026020721 0ustar stephenstephen#******************************************************************************* # FindFFmpeg.cmake # # Copyright (c) 2022 Stephen Rhodes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # #******************************************************************************/ macro(find_component_library var library_name) find_library(${var} NAME ${library_name} HINTS $ENV{CONDA_PREFIX} ) endmacro() macro(find_component_include_dir var header_name) find_path(${var} ${header_name} HINTS $ENV{CONDA_PREFIX} ) endmacro() find_package(PkgConfig QUIET) if (PKG_CONFIG_FOUND) pkg_check_modules(FFMPEG libavcodec libavfilter libavformat libavutil libswscale libswresample ) else() find_component_library(LIBAVCODEC_LIBRARY avcodec) find_component_include_dir(LIBAVCODEC_INCLUDE_DIR libavcodec/avcodec.h) find_component_library(LIBAVDEVICE_LIBRARY avdevice) find_component_include_dir(LIBAVDEVICE_INCLUDE_DIR libavdevice/avdevice.h) find_component_library(LIBAVFILTER_LIBRARY avfilter) find_component_include_dir(LIBAVFILTER_INCLUDE_DIR libavfilter/avfilter.h) find_component_library(LIBAVFORMAT_LIBRARY avformat) find_component_include_dir(LIBAVFORMAT_INCLUDE_DIR libavformat/avformat.h) find_component_library(LIBAVUTIL_LIBRARY avutil) find_component_include_dir(LIBAVUTIL_INCLUDE_DIR libavutil/avutil.h) find_component_library(LIBSWRESAMPLE_LIBRARY swresample) find_component_include_dir(LIBSWRESAMPLE_INCLUDE_DIR libswresample/swresample.h) find_component_library(LIBSWSCALE_LIBRARY swscale) find_component_include_dir(LIBSWSCALE_INCLUDE_DIR libswscale/swscale.h) set(FFMPEG_LINK_LIBRARIES ${LIBAVCODEC_LIBRARY} ${LIBAVDEVICE_LIBRARY} ${LIBAVFILTER_LIBRARY} ${LIBAVFORMAT_LIBRARY} ${LIBAVUTIL_LIBRARY} ${LIBSWRESAMPLE_LIBRARY} ${LIBSWSCALE_LIBRARY} ) set(FFMPEG_INCLUDE_DIRS ${LIBAVCODEC_INCLUDE_DIR} ${LIBAVDEVICE_INCLUDE_DIR} ${LIBAVFILTER_INCLUDE_DIR} ${LIBAVFORMAT_INCLUDE_DIR} ${LIBVUTIL_INCLUDE_DIRS} ${LIBSWRESAMPLE_INCLUDE_DIR} ${LIBSWSCALE_INCLUDE_DIR} ) endif() include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(FFmpeg REQUIRED_VARS FFMPEG_INCLUDE_DIRS FFMPEG_LINK_LIBRARIES VERSION_VAR FFMPEG_VERSION_STRING ) if (FFMPEG_FOUND) add_library(FFmpeg::FFmpeg INTERFACE IMPORTED) set_target_properties(FFmpeg::FFmpeg PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIRS}" INTERFACE_LINK_LIBRARIES "${FFMPEG_LINK_LIBRARIES}") endif() libonvif-1.4.4/libavio/LICENSE0000644000175000017500000002613514355414521015673 0ustar stephenstephen Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. libonvif-1.4.4/libavio/src/0000755000175000017500000000000014355557026015456 5ustar stephenstephenlibonvif-1.4.4/libavio/src/Frame.cpp0000644000175000017500000001206514355414521017210 0ustar stephenstephen/******************************************************************** * libavio/src/Frame.cpp * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #include "Frame.h" namespace avio { Frame::Frame() : m_frame(NULL), m_rts(0) { } Frame::Frame(const Frame& other) : m_frame(copyFrame(other.m_frame)), m_rts(other.m_rts) { } Frame::Frame(Frame&& other) noexcept : m_frame(copyFrame(other.m_frame)), m_rts(other.m_rts) { } Frame::Frame(AVFrame* src) { av_frame_free(&m_frame); m_frame = copyFrame(src); } Frame::Frame(int width, int height, AVPixelFormat pix_fmt) { m_rts = AV_NOPTS_VALUE; m_frame = av_frame_alloc(); m_frame->width = width; m_frame->height = height; m_frame->format = pix_fmt; av_frame_get_buffer(m_frame, 0); av_frame_make_writable(m_frame); int data_size = width * height; switch (pix_fmt) { case AV_PIX_FMT_YUV420P: memset(m_frame->data[0], 0, data_size); memset(m_frame->data[1], 128, data_size >> 2); memset(m_frame->data[2], 128, data_size >> 2); break; case AV_PIX_FMT_BGR24: memset(m_frame->data[0], 0, data_size * 3); break; case AV_PIX_FMT_BGRA: memset(m_frame->data[0], 0, data_size * 4); break; default: std::cout << "Error: unsupported pix fmt" << std::endl; } } Frame::~Frame() { av_frame_free(&m_frame); } Frame& Frame::operator=(const Frame& other) { if (other.isValid()) { m_rts = other.m_rts; av_frame_free(&m_frame); m_frame = av_frame_clone(other.m_frame); av_frame_make_writable(m_frame); m_faded = other.m_faded; } else { invalidate(); } return *this; } Frame& Frame::operator=(Frame&& other) noexcept { if (other.isValid()) { m_rts = other.m_rts; av_frame_free(&m_frame); m_frame = other.m_frame; av_frame_make_writable(m_frame); other.m_frame = NULL; m_faded = other.m_faded; } else { invalidate(); } return *this; } void Frame::invalidate() { av_frame_free(&m_frame); m_frame = NULL; m_rts = 0; } AVFrame* Frame::copyFrame(AVFrame* src) { if (!src) return NULL; AVFrame* dst = av_frame_alloc(); dst->format = src->format; dst->channel_layout = src->channel_layout; dst->sample_rate = src->sample_rate; dst->nb_samples = src->nb_samples; dst->channels = src->channels; dst->width = src->width; dst->height = src->height; av_frame_get_buffer(dst, 0); av_frame_make_writable(dst); av_frame_copy_props(dst, src); av_frame_copy(dst, src); return dst; } AVMediaType Frame::mediaType() const { AVMediaType result = AVMEDIA_TYPE_UNKNOWN; if (isValid()) { if (m_frame->width > 0 && m_frame->height > 0) result = AVMEDIA_TYPE_VIDEO; else if (m_frame->nb_samples > 0 && m_frame->sample_rate > 0) result = AVMEDIA_TYPE_AUDIO; } return result; } std::string Frame::description() const { std::stringstream str; if (isValid()) { if (mediaType() == AVMEDIA_TYPE_VIDEO) { const char* pix_fmt_name = av_get_pix_fmt_name((AVPixelFormat)m_frame->format); str << "VIDEO FRAME, width: " << m_frame->width << " height: " << m_frame->height << " format: " << (pix_fmt_name ? pix_fmt_name : "unknown pixel format") << " pts: " << m_frame->pts << " m_rts: " << m_rts; } else if (mediaType() == AVMEDIA_TYPE_AUDIO) { const char* sample_fmt_name = av_get_sample_fmt_name((AVSampleFormat)m_frame->format); char buf[256]; av_get_channel_layout_string(buf, 256, m_frame->channels, m_frame->channel_layout); str << "AUDIO FRAME, nb_samples: " << m_frame->nb_samples << ", channels: " << m_frame->channels << ", format: " << (sample_fmt_name ? sample_fmt_name : "unknown sample format") << ", sample_rate: " << m_frame->sample_rate << ", channel_layout: " << buf << ", extended_data: " << (m_frame->extended_data[0] ? "yes" : "no") << ", pts: " << m_frame->pts << ", m_rts: " << m_rts; } else { str << "UNKNOWN MEDIA TYPE"; } } else { str << "INVALID FRAME"; } return str.str(); } void Frame::set_rts(AVStream* stream) { if (isValid()) { if (m_frame->pts == AV_NOPTS_VALUE) { m_rts = 0; } else { double factor = 1000 * av_q2d(stream->time_base); uint64_t start_time = (stream->start_time == AV_NOPTS_VALUE ? 0 : stream->start_time); m_rts = (pts() - start_time) * factor; } } } void Frame::set_pts(AVStream* stream) { if (isValid()) { double factor = av_q2d(stream->time_base); m_frame->pts = m_rts / factor / 1000; } } } libonvif-1.4.4/libavio/src/Display.cpp0000644000175000017500000004714514355557026017602 0ustar stephenstephen/******************************************************************** * libavio/src/Display.cpp * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #include #include "Display.h" #include "avio.h" namespace avio { void Display::init() { try { if (P->audioFilter) initAudio(P->audioFilter->sample_rate(), P->audioFilter->sample_format(), P->audioFilter->channels(), P->audioFilter->channel_layout(), P->audioFilter->frame_size()); else if (P->audioDecoder) initAudio(P->audioDecoder->sample_rate(), P->audioDecoder->sample_format(), P->audioDecoder->channels(), P->audioDecoder->channel_layout(), P->audioDecoder->frame_size()); } catch (const Exception& e) { ex.msg(e.what(), MsgPriority::CRITICAL, "Display constructor exception: "); } } Display::~Display() { if (SDL_WasInit(SDL_INIT_AUDIO)) { SDL_LockAudioDevice(audioDeviceID); SDL_CloseAudioDevice(audioDeviceID); } if (swr_ctx) swr_free(&swr_ctx); if (swr_buffer) delete[] swr_buffer; if (texture) SDL_DestroyTexture(texture); if (renderer) SDL_DestroyRenderer(renderer); if (window) SDL_DestroyWindow(window); if (screen) SDL_FreeSurface(screen); SDL_Quit(); } int Display::initVideo(int width, int height, AVPixelFormat pix_fmt) { int ret = 0; if (P->glWidget) return ret; try { Uint32 sdl_format = 0; int w = width; int h = height; switch (pix_fmt) { case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUVJ420P: sdl_format = SDL_PIXELFORMAT_IYUV; break; case AV_PIX_FMT_RGB24: sdl_format = SDL_PIXELFORMAT_RGB24; break; case AV_PIX_FMT_RGBA: sdl_format = SDL_PIXELFORMAT_RGBA32; break; case AV_PIX_FMT_BGR24: sdl_format = SDL_PIXELFORMAT_BGR24; break; case AV_PIX_FMT_BGRA: sdl_format = SDL_PIXELFORMAT_BGRA32; break; default: const char* pix_fmt_name = av_get_pix_fmt_name(pix_fmt); std::stringstream str; str << "unsupported pix fmt: " << (pix_fmt_name ? pix_fmt_name : std::to_string(pix_fmt)); throw Exception(str.str()); } if (!SDL_WasInit(SDL_INIT_VIDEO)) if (SDL_Init(SDL_INIT_VIDEO)) throw Exception(std::string("SDL video init error: ") + SDL_GetError()); if (!window) { const char* pix_fmt_name = av_get_pix_fmt_name(pix_fmt); std::stringstream str; str << "initializing video display | width: " << width << " height: " << height << " pixel format: " << pix_fmt_name ? pix_fmt_name : "unknown pixel format"; ex.msg(str.str()); window = SDL_CreateWindow("window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, 0); if (!window) throw Exception(std::string("SDL_CreateWindow") + SDL_GetError()); if (fullscreen) SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN); renderer = SDL_CreateRenderer(window, -1, 0); if (!renderer) throw Exception(std::string("SDL_CreateRenderer") + SDL_GetError()); SDL_RenderSetLogicalSize(renderer, w, h); texture = SDL_CreateTexture(renderer, sdl_format, SDL_TEXTUREACCESS_STREAMING, w, h); if (!texture) throw Exception(std::string("SDL_CreateTexture") + SDL_GetError()); } else { if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN)) { int window_width; int window_height; SDL_GetWindowSize(window, &window_width, &window_height); if (!(window_width == w && window_height == h)) { SDL_SetWindowSize(window, w, h); SDL_DisplayMode DM; SDL_GetCurrentDisplayMode(0, &DM); auto Width = DM.w; auto Height = DM.h; int x = (Width - w) / 2; int y = (Height - h) / 2; SDL_SetWindowPosition(window, x, y); if (texture) SDL_DestroyTexture(texture); texture = SDL_CreateTexture(renderer, sdl_format, SDL_TEXTUREACCESS_STREAMING, w, h); } } } } catch (const Exception& e) { ex.msg(e.what(), MsgPriority::CRITICAL, "Display::initVideo exception: "); ret = -1; } return ret; } void Display::videoPresentation() { if (!P->glWidget) { if (f.m_frame->format == AV_PIX_FMT_YUV420P) { ex.ck(SDL_UpdateYUVTexture(texture, NULL, f.m_frame->data[0], f.m_frame->linesize[0], f.m_frame->data[1], f.m_frame->linesize[1], f.m_frame->data[2], f.m_frame->linesize[2]), SDL_GetError()); } else { ex.ck(SDL_UpdateTexture(texture, NULL, f.m_frame->data[0], f.m_frame->linesize[0]), SDL_GetError()); } SDL_RenderClear(renderer); ex.ck(SDL_RenderCopy(renderer, texture, NULL, NULL), SDL_GetError()); SDL_RenderPresent(renderer); } else { if (P->glWidget->media_duration) { float pct = (float)f.m_rts / (float)P->glWidget->media_duration; P->glWidget->emit progress(pct); } } } void Display::clearInputQueues() { if (vfq_in) while (vfq_in->size() > 0) vfq_in->pop(); if (afq_in) while (afq_in->size() > 0) afq_in->pop(); } PlayState Display::getEvents(std::vector* events) { PlayState state = PlayState::PLAY; SDL_Event event; while (SDL_PollEvent(&event)) events->push_back(event); if (events->empty()) { SDL_Event user_event = { 0 }; user_event.type = SDL_USEREVENT; events->push_back(user_event); } for (int i = 0; i < events->size(); i++) { SDL_Event event = events->at(i); if (event.type == SDL_QUIT) state = PlayState::QUIT; else if (event.type == SDL_KEYDOWN) { if (event.key.keysym.sym == SDLK_ESCAPE) { state = PlayState::QUIT; } else if (event.key.keysym.sym == SDLK_SPACE) { state = PlayState::PAUSE; } } } return state; } bool Display::display() { bool playing = true; while (true) { if (P->glWidget) { if (!P->glWidget->running) { playing = false; break; } } std::vector events; PlayState state = getEvents(&events); if (state == PlayState::QUIT) { reader->request_break = true; break; } else if (state == PlayState::PAUSE) { togglePause(); } if (paused) { f = paused_frame; bool flag_out = true; while (reader->seeking()) { if (afq_in) { while (afq_in->size() > 0) afq_in->pop(); } if (vfq_in->size() > 0) vfq_in->pop(f); if (f.m_frame != nullptr) { if (f.m_frame->pts == reader->seek_found_pts) { reader->seek_found_pts = AV_NOPTS_VALUE; flag_out = false; } } else { std::cout << "Display received invalid frame during seek" << std::endl; playing = false; break; } } videoPresentation(); SDL_Delay(SDL_EVENT_LOOP_WAIT); if (flag_out) break; } try { if (vfq_in) { vfq_in->pop(f); if (!f.isValid()) { playing = false; break; } } else { SDL_Delay(SDL_EVENT_LOOP_WAIT); f = Frame(640, 480, AV_PIX_FMT_YUV420P); f.m_rts = rtClock.stream_time(); } paused_frame = f; if (!P->glWidget) ex.ck(initVideo(f.m_frame->width, f.m_frame->height, (AVPixelFormat)f.m_frame->format), "initVideo"); SDL_Delay(rtClock.update(f.m_rts - reader->start_time())); videoPresentation(); reader->last_video_pts = f.m_frame->pts; if (vfq_out) { if (vfq_out->size() == 0) vfq_out->push(f); } } catch (const QueueClosedException& e) { playing = false; ex.msg(e.what(), MsgPriority::INFO, "Display::display exception: "); break; } catch (const Exception& e) { ex.msg(e.what(), MsgPriority::CRITICAL, "Display::display exception: "); ex.msg(std::string("last frame description: ") + f.description()); playing = false; break; } } return playing; } int Display::initAudio(int stream_sample_rate, AVSampleFormat stream_sample_format, int stream_channels, uint64_t stream_channel_layout, int stream_nb_samples) { int ret = 0; try { if (stream_nb_samples == 0) { int audio_frame_size = av_samples_get_buffer_size(NULL, stream_channels, 1, sdl_sample_format, 1); int bytes_per_second = av_samples_get_buffer_size(NULL, stream_channels, stream_sample_rate, sdl_sample_format, 1); stream_nb_samples = bytes_per_second * 0.02f / audio_frame_size; } if (!stream_channel_layout || stream_channels != av_get_channel_layout_nb_channels(stream_channel_layout)) { stream_channel_layout = av_get_default_channel_layout(stream_channels); stream_channel_layout &= ~AV_CH_LAYOUT_STEREO_DOWNMIX; } stream_channels = av_get_channel_layout_nb_channels(stream_channel_layout); sdl.channels = stream_channels; sdl.freq = stream_sample_rate; sdl.silence = 0; sdl.samples = stream_nb_samples; sdl.userdata = this; sdl.callback = AudioCallback; switch (sdl_sample_format) { case AV_SAMPLE_FMT_FLT: sdl.format = AUDIO_F32; break; case AV_SAMPLE_FMT_S16: sdl.format = AUDIO_S16; break; case AV_SAMPLE_FMT_U8: sdl.format = AUDIO_U8; break; default: const char* result = "unknown sample format"; const char* name = av_get_sample_fmt_name(sdl_sample_format); if (name) result = name; std::cout << "ERROR: incompatible sample format: " << result << std::endl; std::cout << "supported formats: AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_U8" << std::endl; std::exit(0); } audio_buffer_len = av_samples_get_buffer_size(NULL, sdl.channels, sdl.samples, sdl_sample_format, 1); sdl_buffer.set_max_size(audio_buffer_len * 10); ex.ck(swr_ctx = swr_alloc_set_opts(NULL, stream_channel_layout, sdl_sample_format, stream_sample_rate, stream_channel_layout, stream_sample_format, stream_sample_rate, 0, NULL), SASO); ex.ck(swr_init(swr_ctx), SI); if (!SDL_WasInit(SDL_INIT_AUDIO)) { if (SDL_Init(SDL_INIT_AUDIO)) throw Exception(std::string("SDL audio init error: ") + SDL_GetError()); } audioDeviceID = SDL_OpenAudioDevice(NULL, 0, &sdl, &have, 0); if (audioDeviceID == 0) { throw Exception(std::string("SDL_OpenAudioDevice exception: ") + SDL_GetError()); } SDL_PauseAudioDevice(audioDeviceID, 0); } catch (const Exception& e) { ex.msg(e.what(), MsgPriority::CRITICAL, "Display::initAudio exception: "); std::exit(0); } return ret; } void Display::AudioCallback(void* userdata, uint8_t* audio_buffer, int len) { Display* d = (Display*)userdata; memset(audio_buffer, 0, len); uint8_t* temp = (uint8_t*)malloc(len); memset(temp, 0, len); Frame f; if (d->paused) return; try { while (len > 0) { if (d->sdl_buffer.size() < d->audio_buffer_len) { d->afq_in->pop(f); if (d->afq_out) d->afq_out->push(f); if (f.isValid()) { uint64_t channels = f.m_frame->channels; int nb_samples = f.m_frame->nb_samples; const uint8_t** data = (const uint8_t**)&f.m_frame->data[0]; int frame_buffer_size = av_samples_get_buffer_size(NULL, channels, nb_samples, d->sdl_sample_format, 0); if (frame_buffer_size != d->swr_buffer_size) { if (d->swr_buffer) delete[] d->swr_buffer; d->swr_buffer = new uint8_t[frame_buffer_size]; d->swr_buffer_size = frame_buffer_size; } swr_convert(d->swr_ctx, &d->swr_buffer, nb_samples, data, nb_samples); for (int i = 0; i < d->swr_buffer_size; i++) d->sdl_buffer.push(d->swr_buffer[i]); } else { std::cout << "audio callback revcd null eof" << std::endl; SDL_PauseAudioDevice(d->audioDeviceID, true); if (!d->vfq_in) { SDL_PauseAudioDevice(d->audioDeviceID, true); len = -1; d->audio_eof = true; SDL_Event event; event.type = SDL_QUIT; SDL_PushEvent(&event); } return; } } while (d->sdl_buffer.size() > 0 && len > 0) { temp[d->audio_buffer_len - len] = d->sdl_buffer.pop(); len--; } if (!d->mute) SDL_MixAudioFormat(audio_buffer, temp, d->sdl.format, d->audio_buffer_len, SDL_MIX_MAXVOLUME * d->volume); d->rtClock.sync(f.m_rts); d->reader->seek_found_pts = AV_NOPTS_VALUE; } } catch (const QueueClosedException& e) { } free(temp); } bool Display::isPaused() { return paused; } void Display::togglePause() { paused = !paused; rtClock.pause(paused); } void Display::toggleRecord() { if (!P->writer && !reader->pipe_out) { std::cout << "Error: no writer specified" << std::endl; return; } recording = !recording; if (P->writer) { if (prepend_recent_write && recording) { for (int i = 0; i < recent.size() - 1; i++) vfq_out->push(recent[i]); } P->writer->enabled = recording; } else if (reader->pipe_out) { reader->request_pipe_write = recording; } } std::string Display::audioDeviceStatus() const { std::stringstream str; bool output = false; int count = SDL_GetNumAudioDevices(output); for (int i = 0; i < count; i++) str << "audio device: " << i << " name: " << SDL_GetAudioDeviceName(i, output) << "\n"; str << "selected audio device ID (+2): " << audioDeviceID << "\n"; str << "CHANNELS want: " << (int)sdl.channels << "\n have: " << (int)have.channels << "\n"; str << "FREQUENCY want: " << sdl.freq << "\n have: " << have.freq << "\n"; str << "SAMPLES want: " << sdl.samples << "\n have: " << have.samples << "\n"; str << "FORMAT want: " << sdlAudioFormatName(sdl.format) << "\n have: " << sdlAudioFormatName(have.format) << "\n"; str << "SIZE want: " << sdl.size << "\n have: " << have.size << "\n"; return str.str(); } const char* Display::sdlAudioFormatName(SDL_AudioFormat format) const { /* Note: SDL does not support planar format audio AV_SAMPLE_FMT_NONE = -1, AV_SAMPLE_FMT_U8, ///< unsigned 8 bits AV_SAMPLE_FMT_S16, ///< signed 16 bits AV_SAMPLE_FMT_S32, ///< signed 32 bits AV_SAMPLE_FMT_FLT, ///< float AV_SAMPLE_FMT_DBL, ///< double */ switch (format) { case AUDIO_S8: return "AUDIO_S8"; break; case AUDIO_U8: return "AUDIO_U8"; break; case AUDIO_S16: return "AUDIO_S16"; break; case AUDIO_U16: return "AUDIO_U16"; break; case AUDIO_S32: return "AUDIO_S32"; break; case AUDIO_F32: return "AUDIO_F32"; break; default: return "UNKNOWN"; } } void Display::snapshot() { std::cout << "Display::snapshot" << std::endl; AVCodecContext* codec_ctx = NULL; AVFormatContext* fmt_ctx = NULL; AVStream* stream = NULL; AVCodec* codec = NULL; try { const char* out_name = "filename.jpg"; int width = f.m_frame->width; int height = f.m_frame->height; ex.ck(fmt_ctx = avformat_alloc_context(), AAC); fmt_ctx->oformat = av_guess_format("mjpeg", NULL, NULL); ex.ck(avio_open(&fmt_ctx->pb, out_name, AVIO_FLAG_READ_WRITE), AO); ex.ck(stream = avformat_new_stream(fmt_ctx, 0), ANS); AVCodecParameters *parameters = stream->codecpar; parameters->codec_id = fmt_ctx->oformat->video_codec; parameters->codec_type = AVMEDIA_TYPE_VIDEO; parameters->format = AV_PIX_FMT_YUVJ420P; parameters->width = width; parameters->height = height; ex.ck(codec = (AVCodec *)avcodec_find_encoder(stream->codecpar->codec_id), AFE); ex.ck(codec_ctx = avcodec_alloc_context3(codec), AAC3); ex.ck(avcodec_parameters_to_context(codec_ctx, stream->codecpar), APTC); codec_ctx->time_base = av_make_q(1, 25); ex.ck(avcodec_open2(codec_ctx, codec, NULL), AO2); ex.ck(avformat_write_header(fmt_ctx, NULL), AWH); AVPacket pkt; av_new_packet(&pkt, width * height * 3); ex.ck(avcodec_send_frame(codec_ctx, f.m_frame), ASF); ex.ck(avcodec_receive_packet(codec_ctx, &pkt), ARP); ex.ck(av_write_frame(fmt_ctx, &pkt), AWF); av_packet_unref(&pkt); ex.ck(av_write_trailer(fmt_ctx), AWT); } catch (Exception& e) { std::cout << "Display::snapshot exception: " << e.what() << std::endl; } if (codec_ctx) avcodec_close(codec_ctx); if (fmt_ctx) { avio_close(fmt_ctx->pb); avformat_free_context(fmt_ctx); } } } libonvif-1.4.4/libavio/src/Clock.cpp0000644000175000017500000000421514355414521017207 0ustar stephenstephen/******************************************************************** * libavio/src/Clock.cpp * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #include "Clock.h" #include constexpr auto MAX_CLOCK_DIFF = 1000; namespace avio { uint64_t Clock::milliseconds() { if (!started) { play_start = clock.now(); started = true; } auto current = clock.now(); return std::chrono::duration_cast(current - play_start).count(); } uint64_t Clock::stream_time() { if (!started) { play_start = clock.now(); started = true; } return milliseconds() - correction; } uint64_t Clock::update(uint64_t rts) { if (!started) { play_start = clock.now(); started = true; } uint64_t current = milliseconds() - correction; if (current > rts) { uint64_t diff = current - rts; if (diff > MAX_CLOCK_DIFF) { correction -= (rts - current); } return 0; } else { uint64_t diff = rts - current; if (diff > MAX_CLOCK_DIFF) { correction += (current - rts); diff = 0; } return diff; } } int Clock::sync(uint64_t rts) { if (!started) { play_start = clock.now(); started = true; } uint64_t current = milliseconds() - correction; int diff = rts - current; correction -= diff; return diff; } void Clock::pause(bool paused) { if (paused) { pause_start = clock.now(); } else { auto current = clock.now(); correction += std::chrono::duration_cast(current - pause_start).count(); } } }libonvif-1.4.4/libavio/src/GLWidget.cpp0000644000175000017500000003034714355557026017637 0ustar stephenstephen/******************************************************************** * libavio/src/GLWidget.cpp * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #include "GLWidget.h" #include #include #include #include #include "avio.h" #define VERTEX_ATTRIBUTE 0 #define TEXCOORD_ATTRIBUTE 1 namespace avio { GLWidget::GLWidget() { timer = new QTimer(this); timer->setInterval(poll_interval); connect(timer, SIGNAL(timeout()), this, SLOT(poll())); connect(this, SIGNAL(timerStart()), timer, SLOT(start())); connect(this, SIGNAL(timerStop()), timer, SLOT(stop())); } GLWidget::~GLWidget() { emit timerStop(); makeCurrent(); vbo.destroy(); texture->release(); texture->destroy(); delete texture; program->release(); program->removeAllShaders(); delete vshader; delete fshader; delete program; doneCurrent(); delete timer; } QSize GLWidget::sizeHint() const { return QSize(640, 480); } void GLWidget::initializeGL() { initializeOpenGLFunctions(); vshader = new QOpenGLShader(QOpenGLShader::Vertex, this); const char *vsrc = "attribute highp vec4 vertex;\n" "attribute mediump vec4 texCoord;\n" "varying mediump vec4 texc;\n" "uniform mediump mat4 matrix;\n" "void main(void)\n" "{\n" " gl_Position = matrix * vertex;\n" " texc = texCoord;\n" "}\n"; vshader->compileSourceCode(vsrc); fshader = new QOpenGLShader(QOpenGLShader::Fragment, this); const char *fsrc = "uniform sampler2D texture;\n" "varying mediump vec4 texc;\n" "void main(void)\n" "{\n" " gl_FragColor = texture2D(texture, texc.st);\n" "}\n"; fshader->compileSourceCode(fsrc); program = new QOpenGLShaderProgram; program->addShader(vshader); program->addShader(fshader); program->bindAttributeLocation("vertex", VERTEX_ATTRIBUTE); program->bindAttributeLocation("texCoord", TEXCOORD_ATTRIBUTE); program->link(); program->bind(); program->setUniformValue("texture", 0); static const int coords[4][3] = { { +1, -1, -1 }, { -1, -1, -1 }, { -1, +1, -1 }, { +1, +1, -1 } }; QVector vertData; for (int j = 0; j < 4; ++j) { vertData.append(coords[j][0]); vertData.append(coords[j][1]); vertData.append(coords[j][2]); vertData.append(j == 0 || j == 3); vertData.append(j == 0 || j == 1); } vbo.create(); vbo.bind(); vbo.allocate(vertData.constData(), vertData.count() * sizeof(GLfloat)); } void GLWidget::paintGL() { try { QColor clearColor(0, 0, 0); glClearColor(clearColor.redF(), clearColor.greenF(), clearColor.blueF(), clearColor.alphaF()); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); float B[4] = { (-1.0f + pan_x) * zoom * factor * aspect, (+1.0f + pan_x) * zoom * factor * aspect, (+1.0f + pan_y) * zoom * factor / aspect, (-1.0f + pan_y) * zoom * factor / aspect }; QMatrix4x4 m; m.ortho(B[0], B[1], B[2], B[3], -4.0f, 15.0f); m.translate(0.0f, 0.0f, -10.0f); program->setUniformValue("matrix", m); program->enableAttributeArray(VERTEX_ATTRIBUTE); program->enableAttributeArray(TEXCOORD_ATTRIBUTE); program->setAttributeBuffer(VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3, 5 * sizeof(GLfloat)); program->setAttributeBuffer(TEXCOORD_ATTRIBUTE, GL_FLOAT, 3 * sizeof(GLfloat), 2, 5 * sizeof(GLfloat)); if (texture) { if (texture->width() != tex_width || texture->height() != tex_height) { texture->release(); texture->destroy(); delete texture; texture = nullptr; } } if (!texture) { texture = new QOpenGLTexture(QOpenGLTexture::Target2D); texture->setSize(tex_width, tex_height); texture->setFormat(QOpenGLTexture::RGB8_UNorm); texture->allocateStorage(QOpenGLTexture::RGB, QOpenGLTexture::UInt8); if (tex_width && tex_height) updateAspectRatio(); texture->bind(); } glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } catch (const std::runtime_error& e) { std::cout << "GLWidget paintGL error: " << e.what() << std::endl; } } void GLWidget::updateAspectRatio() { if (maintain_aspect_ratio) { if (texture) { float imageAspect = (float)texture->width() / (float)texture->height(); float widgetAspect = (float)width() / (float)height(); float ratio = imageAspect / widgetAspect; aspect = pow(ratio, -0.5); zoom = (ratio > 1.0 ? pow(ratio, 0.5) : pow(ratio, -0.5)); texture->bind(); } } } void GLWidget::resizeGL(int width, int height) { if (maintain_aspect_ratio) updateAspectRatio(); } void GLWidget::setZoomFactor(float arg) { factor = arg; update(); } void GLWidget::setPanX(float arg) { pan_x = arg; update(); } void GLWidget::setPanY(float arg) { pan_y = arg; update(); } void GLWidget::setFormat(QImage::Format arg) { fmt = arg; } void GLWidget::setVolume(int arg) { volume = arg; if (process) { if (((Process*)process)->display) { ((Process*)process)->display->volume = (float)arg / 100.0f; } } } void GLWidget::setMute(bool arg) { mute = arg; if (process) { if (((Process*)process)->display) { ((Process*)process)->display->mute = arg; } } } void GLWidget::togglePaused() { if (process) { if (((Process*)process)->display) { ((Process*)process)->display->togglePause(); } } } bool GLWidget::isPaused() { bool result = false; if (process) { if (((Process*)process)->display) { result = ((Process*)process)->display->paused; } } return result; } void GLWidget::poll() { if (!running) return; if (vfq_in) { try { if (vfq_in->size() > 0) { vfq_in->pop(f); if (f.isValid()) { if (f.m_frame->width == texture->width() && f.m_frame->height == texture->height()) { QImage img(f.m_frame->data[0], texture->width(), texture->height(), fmt); texture->setData(QOpenGLTexture::RGB, QOpenGLTexture::UInt8, (const void*)img.bits()); } } update(); } } catch (const QueueClosedException& e) { } catch (const std::runtime_error& e) { std::cout << "GLWidget poll error: " << e.what() << std::endl; } } } void GLWidget::play(const QString& arg) { try { stop(); memset(uri, 0, 1024); strcpy(uri, arg.toLatin1().data()); std::thread process_thread(start, this); process_thread.detach(); } catch (const std::runtime_error& e) { std::cout << "GLWidget play error: " << e.what() << std::endl; } } void GLWidget::toggle_pipe_out(const std::string& filename) { if (process) { Reader* reader = ((Process*)process)->reader; if (reader) { reader->pipe_out_filename = filename; reader->request_pipe_write = !reader->request_pipe_write; } } } void GLWidget::seek(float arg) { if (process) { if (((Process*)process)->reader) { ((Process*)process)->reader->request_seek(arg); } } } Reader* GLWidget::get_reader() { Reader* result = nullptr; if (process) { result = ((Process*)process)->reader; } return result; } void GLWidget::stop() { running = false; while (process) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } emit progress(0); } void GLWidget::showStreamParameters(avio::Reader* reader) { std::stringstream str; str << "\n" << mediaShortName; if (reader->has_video()) { str << "\nVideo Stream Parameters" << "\n Video Codec: " << reader->str_video_codec() << "\n Pixel Format: " << reader->str_pix_fmt() << "\n Resolution: " << reader->width() << " x " << reader->height() << "\n Frame Rate: " << av_q2d(reader->frame_rate()); } else { str << "\nNo Video Stream Found"; } if (reader->has_audio()) { str << "\nAudio Stream Parameters" << "\n Audio Codec: " << reader->str_audio_codec() << "\n Sample Format: " << reader->str_sample_format() << "\n Channels: " << reader->str_channel_layout(); } else { str << "\nNo Audio Stream Found"; } emit msg(str.str().c_str()); } bool GLWidget::checkForStreamHeader(const char* name) { QString str = QString(name).toLower(); if (str.startsWith("rtsp://")) return true; if (str.startsWith("http://")) return true; if (str.startsWith("https://")) return true; return false; } void GLWidget::start(void * parent) { GLWidget* widget = (GLWidget*)parent; try { avio::Process process; avio::Reader reader(widget->uri); widget->showStreamParameters(&reader); if (widget->checkForStreamHeader(widget->uri)) { if (widget->vpq_size) reader.apq_max_size = widget->vpq_size; if (widget->apq_size) reader.vpq_max_size = widget->vpq_size; } else { reader.apq_max_size = 1; reader.vpq_max_size = 1; } widget->tex_width = reader.width(); widget->tex_height = reader.height(); reader.set_video_out("vpq_reader"); widget->media_duration = reader.duration(); widget->media_start_time = reader.start_time(); avio::Decoder videoDecoder(reader, AVMEDIA_TYPE_VIDEO, (AVHWDeviceType)widget->hardwareDecoder); videoDecoder.set_video_in(reader.video_out()); videoDecoder.set_video_out("vfq_decoder"); avio::Filter videoFilter(videoDecoder, "format=rgb24,vflip"); videoFilter.set_video_in(videoDecoder.video_out()); videoFilter.set_video_out("vfq_filter"); avio::Display display(reader); display.set_video_in(videoFilter.video_out()); display.set_video_out("vfq_display"); widget->set_video_in(display.video_out()); avio::Decoder* audioDecoder = nullptr; if (reader.has_audio() && !widget->disable_audio) { reader.set_audio_out("apq_reader"); audioDecoder = new avio::Decoder(reader, AVMEDIA_TYPE_AUDIO); audioDecoder->set_audio_in(reader.audio_out()); audioDecoder->set_audio_out("afq_decoder"); display.set_audio_in(audioDecoder->audio_out()); display.volume = widget->volume; display.mute = widget->isMute(); process.add_decoder(*audioDecoder); } process.add_reader(reader); process.add_decoder(videoDecoder); process.add_filter(videoFilter); process.add_display(display); process.add_widget(widget); widget->running = true; widget->emit mediaPlayingStarted(); process.run(); if (audioDecoder) delete audioDecoder; } catch (const Exception& e) { std::stringstream str; str << "GLWidget process error: " << e.what() << "\n"; std::cout << str.str() << std::endl; widget->emit connectFailed(str.str().c_str()); } widget->process = nullptr; widget->media_duration = 0; widget->emit mediaPlayingFinished(); } }libonvif-1.4.4/libavio/src/Decoder.cpp0000644000175000017500000001747714355557026017547 0ustar stephenstephen/******************************************************************** * libavio/src/Decoder.cpp * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #include "Decoder.h" AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE; const char * good = "good"; const char * bad = "bad"; AVPixelFormat get_hw_format(AVCodecContext* ctx, const AVPixelFormat* pix_fmts) { const AVPixelFormat* p; for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) { if (*p == hw_pix_fmt) { ctx->opaque = (void*)good; return *p; } } fprintf(stderr, "Failed to get HW surface format.\n"); ctx->opaque = (void*)bad; return AV_PIX_FMT_NONE; } namespace avio { Decoder::Decoder(Reader& reader, AVMediaType mediaType, AVHWDeviceType hw_device_type) : reader(&reader), mediaType(mediaType) { const char* str = av_get_media_type_string(mediaType); strMediaType = (str ? str : "UNKNOWN MEDIA TYPE"); stream_index = av_find_best_stream(reader.fmt_ctx, mediaType, -1, -1, NULL, 0); if (stream_index < 0) { std::stringstream str; str << "Error opening stream, unable to find " << strMediaType << " stream"; throw Exception(str.str()); } stream = reader.fmt_ctx->streams[stream_index]; dec = avcodec_find_decoder(stream->codecpar->codec_id); if (!dec) { std::stringstream str; str << "avcodec_find_decoder could not find " << avcodec_get_name(stream->codecpar->codec_id); throw Exception(str.str()); } ex.ck(dec_ctx = avcodec_alloc_context3(dec), AAC3); ex.ck(avcodec_parameters_to_context(dec_ctx, stream->codecpar), APTC); dec_ctx->opaque = nullptr; if (mediaType == AVMEDIA_TYPE_VIDEO && dec_ctx->pix_fmt != AV_PIX_FMT_YUV420P) { ex.ck(sws_ctx = sws_getContext(dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, dec_ctx->width, dec_ctx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL), SGC); cvt_frame = av_frame_alloc(); cvt_frame->width = dec_ctx->width; cvt_frame->height = dec_ctx->height; cvt_frame->format = AV_PIX_FMT_YUV420P; av_frame_get_buffer(cvt_frame, 0); } this->hw_device_type = hw_device_type; ex.ck(frame = av_frame_alloc(), AFA); if (hw_device_type != AV_HWDEVICE_TYPE_NONE) { ex.ck(sw_frame = av_frame_alloc(), AFA); for (int i = 0;; i++) { const AVCodecHWConfig* config; config = avcodec_get_hw_config(dec, i); if (!config) { std::stringstream str; str << strMediaType << " Decoder " << dec->name << " does not support device type " << av_hwdevice_get_type_name(hw_device_type); throw Exception(str.str()); } if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == hw_device_type) { hw_pix_fmt = config->pix_fmt; break; } } ex.ck(av_hwdevice_ctx_create(&hw_device_ctx, hw_device_type, NULL, NULL, 0), AHCC); dec_ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx); dec_ctx->get_format = get_hw_format; const char* hw_pix_fmt_name; hw_pix_fmt_name = av_get_pix_fmt_name(hw_pix_fmt); ex.msg(hw_pix_fmt_name, MsgPriority::INFO, "using hw pix fmt: "); ex.ck(sws_ctx = sws_getContext(dec_ctx->width, dec_ctx->height, AV_PIX_FMT_NV12, dec_ctx->width, dec_ctx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL), SGC); cvt_frame = av_frame_alloc(); cvt_frame->width = dec_ctx->width; cvt_frame->height = dec_ctx->height; cvt_frame->format = AV_PIX_FMT_YUV420P; av_frame_get_buffer(cvt_frame, 0); } ex.ck(avcodec_open2(dec_ctx, dec, NULL), AO2); } Decoder::~Decoder() { close(); } void Decoder::close() { av_frame_free(&frame); av_frame_free(&sw_frame); av_frame_free(&cvt_frame); avcodec_free_context(&dec_ctx); av_buffer_unref(&hw_device_ctx); sws_freeContext(sws_ctx); } void Decoder::flush() { if (!dec_ctx) throw Exception("dec_ctx null"); avcodec_flush_buffers(dec_ctx); } int Decoder::decode(AVPacket* pkt) { if (!dec_ctx) throw Exception("dec_ctx null"); if (dec_ctx->opaque && pkt) { const char* status = (const char *)dec_ctx->opaque; if (strcmp("good", status)) throw Exception("incompatible hardware decoder"); } int ret = 0; try { int width = dec_ctx->width; int height = dec_ctx->height; ex.ck(ret = avcodec_send_packet(dec_ctx, pkt), ASP); while (ret >= 0) { ret = avcodec_receive_frame(dec_ctx, frame); if (ret < 0) { if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { return 0; } else if (ret < 0) { ex.ck(ret, "error during decoding"); } } if (frame->width != width || frame->height != height) { if (sw_frame) { av_frame_free(&sw_frame); ex.ck(sw_frame = av_frame_alloc(), AFA); } if (cvt_frame) { av_frame_free(&cvt_frame); ex.ck(cvt_frame = av_frame_alloc(), AFA); cvt_frame->width = dec_ctx->width; cvt_frame->height = dec_ctx->height; cvt_frame->format = AV_PIX_FMT_YUV420P; av_frame_get_buffer(cvt_frame, 0); } if (sws_ctx) { sws_freeContext(sws_ctx); ex.ck(sws_ctx = sws_getContext(dec_ctx->width, dec_ctx->height, AV_PIX_FMT_NV12, dec_ctx->width, dec_ctx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL), SGC); } } Frame f; if (frame->format == hw_pix_fmt) { ex.ck(ret = av_hwframe_transfer_data(sw_frame, frame, 0), AHTD); ex.ck(av_frame_copy_props(sw_frame, frame)); ex.ck(sws_scale(sws_ctx, sw_frame->data, sw_frame->linesize, 0, dec_ctx->height, cvt_frame->data, cvt_frame->linesize), SS); cvt_frame->pts = sw_frame->pts; f = Frame(cvt_frame); } else { if (sws_ctx) { ex.ck(sws_scale(sws_ctx, frame->data, frame->linesize, 0, dec_ctx->height, cvt_frame->data, cvt_frame->linesize), SS); cvt_frame->pts = frame->pts; f = Frame(cvt_frame); } else { f = Frame(frame); } } f.set_rts(stream); if (show_frames) std::cout << strMediaType << " decoder " << f.description() << std::endl; frame_q->push(f); } } catch (const Exception& e) { std::cout << strMediaType << " Decoder::decode exception: " << e.what() << std::endl; ret = -1; } return ret; } } libonvif-1.4.4/libavio/src/Encoder.cpp0000644000175000017500000003027014355414521017533 0ustar stephenstephen/******************************************************************** * libavio/src/Encoder.cpp * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #include "Encoder.h" namespace avio { Encoder::Encoder(Writer& writer, AVMediaType mediaType) : mediaType(mediaType), writer(&writer) { if (mediaType == AVMEDIA_TYPE_VIDEO) this->writer->videoEncoder = this; if (mediaType == AVMEDIA_TYPE_AUDIO) this->writer->audioEncoder = this; } void Encoder::init() { const char* str = av_get_media_type_string(mediaType); strMediaType = (str ? str : "UNKNOWN MEDIA TYPE"); switch (mediaType) { case AVMEDIA_TYPE_VIDEO: openVideoStream(); break; case AVMEDIA_TYPE_AUDIO: openAudioStream(); break; default: ex.msg("Encoder constructor failed: unknown media type", MsgPriority::CRITICAL); } } Encoder::~Encoder() { close(); } void Encoder::close() { if (enc_ctx) avcodec_free_context(&enc_ctx); if (pkt) av_packet_free(&pkt); if (hw_frame) av_frame_free(&hw_frame); if (hw_device_ctx) av_buffer_unref(&hw_device_ctx); if (cvt_frame) av_frame_free(&cvt_frame); opened = false; } void Encoder::openVideoStream() { if (!writer->fmt_ctx) writer->init(); AVFormatContext* fmt_ctx = writer->fmt_ctx; AVCodecID codec_id = writer->fmt_ctx->oformat->video_codec; first_pass = true; pts_offset = 0; try { const AVCodec* codec; ex.ck(pkt = av_packet_alloc(), CmdTag::APA); if (hw_device_type != AV_HWDEVICE_TYPE_NONE) { codec = avcodec_find_encoder_by_name(hw_video_codec_name.c_str()); std::stringstream str; str << "avcodec_find_encoder_by_name: " << hw_video_codec_name; if (!codec) throw Exception(str.str()); } else { codec = avcodec_find_encoder(fmt_ctx->oformat->video_codec); if (!codec) throw Exception("avcodec_find_encoder"); } ex.msg(std::string("encoder opened codec ") + codec->long_name); ex.ck(stream = avformat_new_stream(fmt_ctx, NULL), CmdTag::ANS); stream->id = fmt_ctx->nb_streams - 1; writer->video_stream_id = stream->id; ex.ck(enc_ctx = avcodec_alloc_context3(codec), CmdTag::AAC3); enc_ctx->codec_id = codec_id; enc_ctx->bit_rate = video_bit_rate; enc_ctx->width = width; enc_ctx->height = height; stream->time_base = av_make_q(1, frame_rate); enc_ctx->time_base = stream->time_base; enc_ctx->gop_size = gop_size; cvt_frame = av_frame_alloc(); cvt_frame->width = enc_ctx->width; cvt_frame->height = enc_ctx->height; cvt_frame->format = AV_PIX_FMT_YUV420P; av_frame_get_buffer(cvt_frame, 0); av_frame_make_writable(cvt_frame); if (hw_device_type != AV_HWDEVICE_TYPE_NONE) { enc_ctx->pix_fmt = hw_pix_fmt; if (!profile.empty()) av_opt_set(enc_ctx->priv_data, "profile", profile.c_str(), 0); ex.ck(av_hwdevice_ctx_create(&hw_device_ctx, hw_device_type, NULL, NULL, 0), CmdTag::AHCC); ex.ck(hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx), CmdTag::AHCA); AVHWFramesContext* frames_ctx = (AVHWFramesContext*)(hw_frames_ref->data); frames_ctx->format = hw_pix_fmt; frames_ctx->sw_format = sw_pix_fmt; frames_ctx->width = width; frames_ctx->height = height; frames_ctx->initial_pool_size = 20; ex.ck(av_hwframe_ctx_init(hw_frames_ref), CmdTag::AHCI); ex.ck(enc_ctx->hw_frames_ctx = av_buffer_ref(hw_frames_ref), CmdTag::ABR); av_buffer_unref(&hw_frames_ref); ex.ck(hw_frame = av_frame_alloc(), CmdTag::AFA); ex.ck(av_hwframe_get_buffer(enc_ctx->hw_frames_ctx, hw_frame, 0), CmdTag::AHGB); } else { enc_ctx->pix_fmt = pix_fmt; } if (enc_ctx->codec_id == AV_CODEC_ID_MPEG2VIDEO) enc_ctx->max_b_frames = 2; if (enc_ctx->codec_id == AV_CODEC_ID_MPEG1VIDEO) enc_ctx->mb_decision = 2; if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; ex.ck(avcodec_open2(enc_ctx, codec, &opts), CmdTag::AO2); ex.ck(avcodec_parameters_from_context(stream->codecpar, enc_ctx), CmdTag::APFC); std::stringstream str; str << "Encoder video stream width: " << enc_ctx->width << " height: " << enc_ctx->height; ex.msg(str.str()); opened = true; } catch (const Exception& e) { ex.msg(e.what(), MsgPriority::CRITICAL, "Encoder video stream constructor exception: "); close(); } } void Encoder::openAudioStream() { if (!writer->fmt_ctx) writer->init(); AVFormatContext* fmt_ctx = writer->fmt_ctx; AVCodecID codec_id = writer->fmt_ctx->oformat->audio_codec; first_pass = true; pts_offset = 0; try { const AVCodec* codec; codec = avcodec_find_encoder(codec_id); if (codec) ex.msg(std::string("Encoder opened audio stream codec ") + codec->long_name); else throw Exception(std::string("avcodec_find_decoder could not find ") + avcodec_get_name(codec_id)); ex.ck(pkt = av_packet_alloc(), CmdTag::APA); ex.ck(stream = avformat_new_stream(fmt_ctx, NULL), CmdTag::ANS); stream->id = fmt_ctx->nb_streams - 1; writer->audio_stream_id = stream->id; ex.ck(enc_ctx = avcodec_alloc_context3(codec), CmdTag::AAC3); enc_ctx->sample_fmt = sample_fmt; enc_ctx->bit_rate = audio_bit_rate; enc_ctx->sample_rate = sample_rate; enc_ctx->channel_layout = channel_layout; enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout); enc_ctx->frame_size = nb_samples; stream->time_base = av_make_q(1, enc_ctx->sample_rate); cvt_frame = av_frame_alloc(); cvt_frame->channels = enc_ctx->channels; cvt_frame->channel_layout = enc_ctx->channel_layout; cvt_frame->format = sample_fmt; cvt_frame->nb_samples = nb_samples; av_frame_get_buffer(cvt_frame, 0); av_frame_make_writable(cvt_frame); if (fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; ex.ck(avcodec_open2(enc_ctx, codec, NULL), CmdTag::AO2); ex.ck(avcodec_parameters_from_context(stream->codecpar, enc_ctx), CmdTag::APFC); opened = true; } catch (const Exception& e) { ex.msg(e.what(), MsgPriority::CRITICAL, "AudioStream constructor exception: "); close(); } } bool Encoder::cmpFrame(AVFrame* frame) { if (mediaType == AVMEDIA_TYPE_VIDEO) return (frame->width == width && frame->height == height && frame->format == pix_fmt); if (mediaType == AVMEDIA_TYPE_AUDIO) return (frame->channels == channels && frame->channel_layout == channel_layout && frame->nb_samples == nb_samples && frame->format == sample_fmt); return false; } int Encoder::encode(Frame& f) { int ret = 0; try { if (!pkt_q) throw Exception("no packet queue"); f.set_pts(stream); AVFrame* frame = f.m_frame; if (frame) { if (mediaType == AVMEDIA_TYPE_VIDEO && !cmpFrame(frame)) { if (!sws_ctx) { ex.ck(sws_ctx = sws_getContext(frame->width, frame->height, (AVPixelFormat)frame->format, enc_ctx->width, enc_ctx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL), CmdTag::SGC); } ex.ck(sws_scale(sws_ctx, frame->data, frame->linesize, 0, enc_ctx->height, cvt_frame->data, cvt_frame->linesize), CmdTag::SS); //cvt_frame->pts = frame->pts; cvt_frame->pts = av_rescale_q(frame->pts, video_time_base, enc_ctx->time_base); frame = cvt_frame; Frame qf(cvt_frame); } if (mediaType == AVMEDIA_TYPE_AUDIO && !cmpFrame(frame)) { if (!swr_ctx) { ex.ck(swr_ctx = swr_alloc(), CmdTag::SA); av_opt_set_int(swr_ctx, "in_channel_count", frame->channels, 0); av_opt_set_int(swr_ctx, "out_channel_count", channels, 0); av_opt_set_channel_layout(swr_ctx, "in_channel_layout", frame->channel_layout, 0); av_opt_set_channel_layout(swr_ctx, "out_channel_layout", channel_layout, 0); av_opt_set_int(swr_ctx, "in_sample_rate", frame->sample_rate, 0); av_opt_set_int(swr_ctx, "out_sample_rate", sample_rate, 0); av_opt_set_sample_fmt(swr_ctx, "in_sample_fmt", (AVSampleFormat)frame->format, 0); av_opt_set_sample_fmt(swr_ctx, "out_sample_fmt", sample_fmt, 0); ex.ck(swr_init(swr_ctx), CmdTag::SI); } ex.ck(swr_convert(swr_ctx, cvt_frame->data, cvt_frame->nb_samples, (const uint8_t**)frame->data, frame->nb_samples), CmdTag::SC); //cvt_frame->pts = frame->pts; cvt_frame->pts = av_rescale_q(total_samples, av_make_q(1, enc_ctx->sample_rate), enc_ctx->time_base); cvt_frame->sample_rate = sample_rate; total_samples += nb_samples; frame = cvt_frame; Frame qf(cvt_frame); } if (hw_device_type != AV_HWDEVICE_TYPE_NONE) { av_frame_copy_props(hw_frame, frame); ex.ck(av_hwframe_transfer_data(hw_frame, frame, 0), CmdTag::AHTD); ex.ck(avcodec_send_frame(enc_ctx, hw_frame), CmdTag::ASF); } else { ex.ck(avcodec_send_frame(enc_ctx, frame), "hw avcodec_send_frame"); } } else { ex.ck(ret = avcodec_send_frame(enc_ctx, NULL), "avcodec_send_frame"); } while (ret >= 0) { ret = avcodec_receive_packet(enc_ctx, pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; else if (ret < 0) ex.ck(ret, CmdTag::ARP); pkt->stream_index = stream->index; AVPacket* tmp = av_packet_alloc(); // needed to prevent corruption in dts if (pkt->dts > pkt->pts) pkt->dts = pkt->pts - 2; tmp = av_packet_clone(pkt); if (first_pass) { pts_offset = tmp->pts; first_pass = false; } tmp->pts -= pts_offset; tmp->dts -= pts_offset; /* std::stringstream str; str << " index: " << tmp->stream_index << " flags: " << tmp->flags << " pts: " << tmp->pts << " dts: " << tmp->dts << " size: " << tmp->size << " duration: " << tmp->duration; std::cout << strMediaType << " " << str.str() << std::endl; */ writer->write(tmp); // are we missing av_packet_free? } } catch (const Exception& e) { if (!strcmp(e.what(), "End of file")) std::cout << strMediaType << "Encode::encode exception: " << e.what(); } return ret; } } libonvif-1.4.4/libavio/src/Writer.cpp0000644000175000017500000000674314355414521017440 0ustar stephenstephen/******************************************************************** * libavio/src/Writer.cpp * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #include "Writer.h" #include "Encoder.h" namespace avio { Writer::Writer(const std::string& format) : m_format(format) { } Writer::~Writer() { } void Writer::init() { ex.ck(avformat_alloc_output_context2(&fmt_ctx, NULL, m_format.c_str(), NULL), AAOC2); } void Writer::open(const std::string& filename) { std::unique_lock lock(mutex); try { if (getEncoderState() == EncoderState::OPEN && !opened) { if (!(fmt_ctx->oformat->flags & AVFMT_NOFILE)) ex.ck(avio_open(&fmt_ctx->pb, filename.c_str(), AVIO_FLAG_WRITE), AO); ex.ck(avformat_write_header(fmt_ctx, NULL), AWH); opened = true; } } catch (const Exception& e) { std::cout << "Writer::open exception: " << e.what() << std::endl; } } void Writer::write(AVPacket* pkt) { std::unique_lock lock(mutex); try { ex.ck(av_interleaved_write_frame(fmt_ctx, pkt), AIWF); } catch (const Exception& e) { std::cout << "Writer::write exception: " << e.what() << std::endl; } } void Writer::close() { std::unique_lock lock(mutex); try { if (getEncoderState() == EncoderState::CLOSED && opened) { ex.ck(av_write_trailer(fmt_ctx), AWT); if (!(fmt_ctx->oformat->flags & AVFMT_NOFILE)) ex.ck(avio_closep(&fmt_ctx->pb), AC); avformat_free_context(fmt_ctx); fmt_ctx = NULL; opened = false; } } catch (const Exception& e) { std::cout << "Writer::close exception: " << e.what() << std::endl; } } EncoderState Writer::getEncoderState() { bool videoEncoderOpen = false; bool videoEncoderClosed = false; if (videoEncoder) { if (((Encoder*)videoEncoder)->opened) videoEncoderOpen = true; else videoEncoderClosed = true; } bool audioEncoderOpen = false; bool audioEncoderClosed = false; if (audioEncoder) { if (((Encoder*)audioEncoder)->opened) audioEncoderOpen = true; else audioEncoderClosed = true; } if (videoEncoderOpen && audioEncoderOpen) return EncoderState::OPEN; if (videoEncoderOpen && !audioEncoder) return EncoderState::OPEN; if (!videoEncoder && audioEncoderOpen) return EncoderState::OPEN; if (videoEncoderClosed && audioEncoderClosed) return EncoderState::CLOSED; if (videoEncoderClosed && !audioEncoder) return EncoderState::CLOSED; if (!videoEncoder && audioEncoderClosed) return EncoderState::CLOSED; return EncoderState::MIXED; } } libonvif-1.4.4/libavio/src/Pipe.cpp0000644000175000017500000001413614355557026017064 0ustar stephenstephen/******************************************************************** * libavio/src/Pipe.cpp * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #include "Pipe.h" #include "avio.h" namespace avio { Pipe::Pipe(Reader& reader) : reader(&reader) { } Pipe::~Pipe() { //std::cout << "write operation finished (pipe)" << std::endl; if (fmt_ctx) avformat_free_context(fmt_ctx); if (video_ctx) avcodec_free_context(&video_ctx); } AVCodecContext* Pipe::getContext(AVMediaType mediaType) { AVCodecContext* enc_ctx = NULL; std::string strMediaType = "unknown"; try { int stream_index = -1; if (mediaType == AVMEDIA_TYPE_VIDEO) { strMediaType = "video"; stream_index = reader->video_stream_index; } else if (mediaType == AVMEDIA_TYPE_AUDIO) { strMediaType = "audio"; stream_index = reader->audio_stream_index; } if (stream_index < 0) throw Exception("invalid stream index from reader"); AVStream* stream = reader->fmt_ctx->streams[stream_index]; const AVCodec* enc = avcodec_find_encoder(stream->codecpar->codec_id); if (!enc) throw Exception("could not find encoder"); ex.ck(enc_ctx = avcodec_alloc_context3(enc), AAC3); ex.ck(avcodec_parameters_to_context(enc_ctx, stream->codecpar), APTC); } catch (const Exception& e) { if (strcmp(e.what(), "invalid stream index from reader")) std::cout << strMediaType << " Pipe::getContext exception: " << e.what() << std::endl; else std::cout << "no " << strMediaType << " found in stream" << std::endl; } return enc_ctx; } bool Pipe::open(const std::string& filename) { try { opened = false; ex.ck(avformat_alloc_output_context2(&fmt_ctx, NULL, NULL, filename.c_str()), AAOC2); video_ctx = getContext(AVMEDIA_TYPE_VIDEO); if (video_ctx) { ex.ck(video_stream = avformat_new_stream(fmt_ctx, NULL), ANS); if (video_ctx == NULL) throw Exception("no video reference context"); ex.ck(avcodec_parameters_from_context(video_stream->codecpar, video_ctx), APFC); video_stream->time_base = reader->fmt_ctx->streams[reader->video_stream_index]->time_base; } audio_ctx = getContext(AVMEDIA_TYPE_AUDIO); if (audio_ctx) { ex.ck(audio_stream = avformat_new_stream(fmt_ctx, NULL), ANS); if (audio_ctx == NULL) throw Exception("no audio reference context"); ex.ck(avcodec_parameters_from_context(audio_stream->codecpar, audio_ctx), APFC); audio_stream->time_base = reader->fmt_ctx->streams[reader->audio_stream_index]->time_base; } //show_ctx(); ex.ck(avio_open(&fmt_ctx->pb, filename.c_str(), AVIO_FLAG_WRITE), AO); opened = true; ex.ck(avformat_write_header(fmt_ctx, NULL), AWH); video_next_pts = 0; audio_next_pts = 0; std::cout << "pipe opened write file " << filename.c_str() << std::endl; } catch (const Exception& e) { std::stringstream str; str << "Pipe::open exception: " << e.what(); reader->request_pipe_write = false; if (opened) ex.ck(avio_closep(&fmt_ctx->pb), ACP); if (P->glWidget) P->glWidget->emit openWriterFailed(str.str()); return false; } return true; } void Pipe::adjust_pts(AVPacket* pkt) { if (pkt->stream_index == reader->video_stream_index) { pkt->stream_index = video_stream->index; pkt->dts = pkt->pts = video_next_pts; video_next_pts += pkt->duration; } else if (pkt->stream_index == reader->audio_stream_index) { pkt->stream_index = audio_stream->index; pkt->dts = pkt->pts = audio_next_pts; audio_next_pts += pkt->duration; } } void Pipe::write(AVPacket* pkt) { adjust_pts(pkt); std::unique_lock lock(mutex); try { ex.ck(av_interleaved_write_frame(fmt_ctx, pkt), AIWF); } catch (const Exception& e) { std::cout << "Pipe::write exception: " << e.what() << std::endl; } } void Pipe::close() { try { ex.ck(av_write_trailer(fmt_ctx), AWT); ex.ck(avio_closep(&fmt_ctx->pb), ACP); } catch (const Exception& e) { std::cout << "Pipe::close exception: " << e.what() << std::endl; } std::cout << "pipe closed file " << std::endl; } void Pipe::show_ctx() { for (int i = 0; i < fmt_ctx->nb_streams; i++) { AVStream* stream = fmt_ctx->streams[i]; enum AVMediaType media_type = stream->codecpar->codec_type; switch (media_type) { case AVMEDIA_TYPE_VIDEO: std::cout << "Video Stream" << std::endl; break; case AVMEDIA_TYPE_AUDIO: std::cout << "Audio Stream" << std::endl; std::cout << "sample rate: " << stream->codecpar->sample_rate << std::endl; std::cout << "sample channels: " << stream->codecpar->channels << std::endl; std::cout << "sample frame_size: " << stream->codecpar->frame_size << std::endl; std::cout << "sample format: " << stream->codecpar->format << std::endl; break; } std::cout << "stream time base: " << stream->time_base.num << " / " << stream->time_base.den << std::endl; } } } libonvif-1.4.4/libavio/src/Filter.cpp0000644000175000017500000001471214355414521017404 0ustar stephenstephen/******************************************************************** * libavio/src/Filter.cpp * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #include "../include/Filter.h" #include namespace avio { Filter::Filter(Decoder& decoder, const char* description) : decoder(&decoder), desc(description) { switch (decoder.mediaType) { case AVMEDIA_TYPE_VIDEO: initVideo(); break; case AVMEDIA_TYPE_AUDIO: initAudio(); break; default: std::cout << "Filter constructor error, unknown media type" << std::endl; break; } } void Filter::initVideo() { const AVFilter* buffersrc = avfilter_get_by_name("buffer"); const AVFilter* buffersink = avfilter_get_by_name("buffersink"); AVFilterInOut* outputs = avfilter_inout_alloc(); AVFilterInOut* inputs = avfilter_inout_alloc(); char args[512]; snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", decoder->dec_ctx->width, decoder->dec_ctx->height, decoder->dec_ctx->pix_fmt, decoder->stream->time_base.num, decoder->stream->time_base.den, decoder->dec_ctx->sample_aspect_ratio.num, decoder->dec_ctx->sample_aspect_ratio.den); try { ex.ck(frame = av_frame_alloc(), AFA); ex.ck(graph = avfilter_graph_alloc(), AGA); ex.ck(avfilter_graph_create_filter(&src_ctx, buffersrc, "in", args, NULL, graph), AGCF); ex.ck(avfilter_graph_create_filter(&sink_ctx, buffersink, "out", NULL, NULL, graph), AGCF); outputs->name = av_strdup("in"); outputs->filter_ctx = src_ctx; outputs->pad_idx = 0; outputs->next = NULL; inputs->name = av_strdup("out"); inputs->filter_ctx = sink_ctx; inputs->pad_idx = 0; inputs->next = NULL; ex.ck(avfilter_graph_parse_ptr(graph, desc.c_str(), &inputs, &outputs, NULL), AGPP); ex.ck(avfilter_graph_config(graph, NULL), AGC); } catch (const Exception& e) { ex.msg(e.what(), MsgPriority::CRITICAL, "Video Filter constructor exception: "); } avfilter_inout_free(&inputs); avfilter_inout_free(&outputs); } void Filter::initAudio() { AVFilterInOut* outputs = avfilter_inout_alloc(); AVFilterInOut* inputs = avfilter_inout_alloc(); const AVFilter* buf_src = avfilter_get_by_name("abuffer"); const AVFilter* buf_sink = avfilter_get_by_name("abuffersink"); //static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_U8, AV_SAMPLE_FMT_NONE }; //static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_S16, AV_SAMPLE_FMT_NONE }; static const enum AVSampleFormat sample_fmts[] = { AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_NONE }; try { if (decoder->dec_ctx->channel_layout && av_get_channel_layout_nb_channels(decoder->dec_ctx->channel_layout) == decoder->dec_ctx->channels) m_channel_layout = decoder->dec_ctx->channel_layout; std::stringstream str; str << "sample_rate=" << decoder->dec_ctx->sample_rate << ":" << "sample_fmt=" << av_get_sample_fmt_name(decoder->dec_ctx->sample_fmt) << ":" << "channels=" << decoder->dec_ctx->channels << ":" << "time_base=" << decoder->stream->time_base.num << "/" << decoder->stream->time_base.den; if (m_channel_layout) str << ":channel_layout=0x" << std::hex << m_channel_layout; ex.ck(frame = av_frame_alloc(), AFA); ex.ck(graph = avfilter_graph_alloc(), AGA); ex.ck(avfilter_graph_create_filter(&src_ctx, buf_src, "buf_src", str.str().c_str(), NULL, graph), AGCF); ex.ck(avfilter_graph_create_filter(&sink_ctx, buf_sink, "buf_sink", NULL, NULL, graph), AGCF); ex.ck(av_opt_set_int_list(sink_ctx, "sample_fmts", sample_fmts, AV_SAMPLE_FMT_NONE, AV_OPT_SEARCH_CHILDREN), AOSIL); ex.ck(av_opt_set_int(sink_ctx, "all_channel_counts", 1, AV_OPT_SEARCH_CHILDREN), AOSI); if (desc.c_str()) { if (!outputs || !inputs) throw Exception("avfilter_inout_alloc"); outputs->name = av_strdup("in"); outputs->filter_ctx = src_ctx; outputs->pad_idx = 0; outputs->next = NULL; inputs->name = av_strdup("out"); inputs->filter_ctx = sink_ctx; inputs->pad_idx = 0; inputs->next = NULL; ex.ck(avfilter_graph_parse_ptr(graph, desc.c_str(), &inputs, &outputs, NULL), AGPP); } else { ex.ck(avfilter_link(src_ctx, 0, sink_ctx, 0), AL); } ex.ck(avfilter_graph_config(graph, NULL), AGC); } catch (const Exception& e) { ex.msg(e.what(), MsgPriority::CRITICAL, "Audio Filter constructor exception: "); } avfilter_inout_free(&outputs); avfilter_inout_free(&inputs); } Filter::~Filter() { avfilter_free(sink_ctx); avfilter_free(src_ctx); avfilter_graph_free(&graph); av_frame_free(&frame); } void Filter::filter(const Frame& f) { int ret = 0; try { ex.ck(av_buffersrc_add_frame_flags(src_ctx, f.m_frame, AV_BUFFERSRC_FLAG_KEEP_REF), ABAFF); while (true) { ret = av_buffersink_get_frame(sink_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; if (ret < 0) throw Exception("av_buffersink_get_frame"); tmp = Frame(frame); tmp.m_rts = tmp.pts() * 1000 * av_q2d(av_buffersink_get_time_base(sink_ctx)); if (show_frames) std::cout << "filter " << f.description() << std::endl; frame_out_q->push(tmp); av_frame_unref(frame); } } catch (const Exception& e) { ex.msg(e.what(), MsgPriority::CRITICAL, "VideoFilter::filter exception: "); } } } libonvif-1.4.4/libavio/src/Reader.cpp0000644000175000017500000002565214355557026017376 0ustar stephenstephen/******************************************************************** * libavio/src/Reader.cpp * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ extern "C" { #include } #include #include #include "Reader.h" #include "avio.h" #define MAX_TIMEOUT 5 time_t timeout_start = time(NULL); static int interrupt_callback(void *ctx) { avio::Reader* reader = (avio::Reader*)ctx; time_t diff = time(NULL) - timeout_start; if (diff > MAX_TIMEOUT || reader->request_break) { return 1; } return 0; } namespace avio { Reader::Reader(const char* filename) { AVDictionary* opts = NULL; if (LIBAVFORMAT_VERSION_INT > AV_VERSION_INT(59, 0, 0)) av_dict_set(&opts, "timeout", "5000000", 0); else av_dict_set(&opts, "stimeout", "5000000", 0); ex.ck(avformat_open_input(&fmt_ctx, filename, NULL, &opts), CmdTag::AOI); av_dict_free(&opts); timeout_start = time(NULL); AVIOInterruptCB cb = { interrupt_callback, this }; fmt_ctx->interrupt_callback = cb; ex.ck(avformat_find_stream_info(fmt_ctx, NULL), CmdTag::AFSI); video_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); if (video_stream_index < 0) ex.msg("did not find video stream", MsgPriority::INFO); audio_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); if (audio_stream_index < 0) ex.msg("did not find audio stream", MsgPriority::INFO); //if (video_codec() == AV_CODEC_ID_HEVC) throw Exception("HEVC compression is not supported by default configuration"); } Reader::~Reader() { avformat_close_input(&fmt_ctx); } AVPacket* Reader::read() { if (closed) return NULL; int ret = 0; AVPacket* pkt = av_packet_alloc(); try { if (!fmt_ctx) throw Exception("fmt_ctx null"); timeout_start = time(NULL); ex.ck(ret = av_read_frame(fmt_ctx, pkt), CmdTag::ARF); timeout_start = time(NULL); } catch (const Exception& e) { if (ret != AVERROR_EOF) { ex.msg(e.what(), MsgPriority::CRITICAL, "Reader::read exception: "); if (ret == AVERROR_EXIT || ret == AVERROR(ETIMEDOUT)) { std::cout << "Camera connection timed out" << std::endl; if (P->glWidget) { P->glWidget->emit cameraTimeout(); } } } av_packet_free(&pkt); closed = true; } return pkt; } AVPacket* Reader::seek() { int flags = AVSEEK_FLAG_FRAME; if (seek_target_pts < last_video_pts) flags |= AVSEEK_FLAG_BACKWARD; try { ex.ck(av_seek_frame(fmt_ctx, seek_stream_index(), seek_target_pts, flags), CmdTag::ASF); } catch (const Exception& e) { std::cout << "Reader seek exception: " << e.what() << std::endl; return NULL; } AVPacket* pkt = NULL; while (pkt = read()) { if (pkt->stream_index == seek_stream_index()) { seek_found_pts = pkt->pts; break; } } seek_target_pts = AV_NOPTS_VALUE; return pkt; } bool Reader::isPaused() { return ((Process*)process)->display->paused; } void Reader::request_seek(float pct) { seek_target_pts = (start_time() + (pct * duration()) / av_q2d(fmt_ctx->streams[seek_stream_index()]->time_base)) / 1000; } bool Reader::seeking() { return seek_target_pts != AV_NOPTS_VALUE || seek_found_pts != AV_NOPTS_VALUE; } void Reader::start_from(int milliseconds) { request_seek((start_time() + milliseconds) / (float)duration()); } void Reader::end_at(int milliseconds) { stop_play_at_pts = ((start_time() + milliseconds) / av_q2d(fmt_ctx->streams[seek_stream_index()]->time_base)) / 1000; } int Reader::seek_stream_index() { return (video_stream_index >= 0 ? video_stream_index : audio_stream_index); } int64_t Reader::start_time() { int64_t start_pts = (fmt_ctx->start_time == AV_NOPTS_VALUE ? 0 : fmt_ctx->start_time); return start_pts * AV_TIME_BASE / 1000000000; } int64_t Reader::duration() { return fmt_ctx->duration * AV_TIME_BASE / 1000000000; } int64_t Reader::bit_rate() { return fmt_ctx->bit_rate; } bool Reader::has_video() { return video_stream_index >= 0; } int Reader::width() { int result = -1; if (video_stream_index >= 0) result = fmt_ctx->streams[video_stream_index]->codecpar->width; return result; } int Reader::height() { int result = -1; if (video_stream_index >= 0) result = fmt_ctx->streams[video_stream_index]->codecpar->height; return result; } AVRational Reader::frame_rate() { AVRational result = av_make_q(0, 0); if (video_stream_index >= 0) result = fmt_ctx->streams[video_stream_index]->avg_frame_rate; return result; } AVPixelFormat Reader::pix_fmt() { AVPixelFormat result = AV_PIX_FMT_NONE; if (video_stream_index >= 0) result = (AVPixelFormat)fmt_ctx->streams[video_stream_index]->codecpar->format; return result; } const char* Reader::str_pix_fmt() { const char* result = "unknown pixel format"; if (video_stream_index >= 0) { const char* name = av_get_pix_fmt_name((AVPixelFormat)fmt_ctx->streams[video_stream_index]->codecpar->format); if (name) result = name; } return result; } AVCodecID Reader::video_codec() { AVCodecID result = AV_CODEC_ID_NONE; if (video_stream_index >= 0) result = fmt_ctx->streams[video_stream_index]->codecpar->codec_id; return result; } const char* Reader::str_video_codec() { const char* result = "unknown codec"; if (video_stream_index >= 0) { result = avcodec_get_name(fmt_ctx->streams[video_stream_index]->codecpar->codec_id); } return result; } int64_t Reader::video_bit_rate() { int64_t result = -1; if (video_stream_index >= 0) result = fmt_ctx->streams[video_stream_index]->codecpar->bit_rate; return result; } AVRational Reader::video_time_base() { AVRational result = av_make_q(0, 0); if (video_stream_index >= 0) result = fmt_ctx->streams[video_stream_index]->time_base; return result; } bool Reader::has_audio() { return audio_stream_index >= 0; } int Reader::channels() { int result = -1; if (audio_stream_index >= 0) result = fmt_ctx->streams[audio_stream_index]->codecpar->channels; return result; } int Reader::sample_rate() { int result = -1; if (audio_stream_index >= 0) result = fmt_ctx->streams[audio_stream_index]->codecpar->sample_rate; return result; } int Reader::frame_size() { int result = -1; if (audio_stream_index >= 0) result = fmt_ctx->streams[audio_stream_index]->codecpar->frame_size; return result; } uint64_t Reader::channel_layout() { uint64_t result = 0; if (audio_stream_index >= 0) result = fmt_ctx->streams[audio_stream_index]->codecpar->channel_layout; return result; } std::string Reader::str_channel_layout() { char result[256] = { 0 }; if (audio_stream_index >= 0) { uint64_t cl = fmt_ctx->streams[audio_stream_index]->codecpar->channel_layout; av_get_channel_layout_string(result, 256, channels(), cl); } return std::string(result); } AVSampleFormat Reader::sample_format() { AVSampleFormat result = AV_SAMPLE_FMT_NONE; if (audio_stream_index >= 0) result = (AVSampleFormat)fmt_ctx->streams[video_stream_index]->codecpar->format; return result; } const char* Reader::str_sample_format() { const char* result = "unknown sample format"; if (audio_stream_index >= 0) { const char* name = av_get_sample_fmt_name((AVSampleFormat)fmt_ctx->streams[audio_stream_index]->codecpar->format); if (name) result = name; } return result; } AVCodecID Reader::audio_codec() { AVCodecID result = AV_CODEC_ID_NONE; if (audio_stream_index >= 0) result = fmt_ctx->streams[audio_stream_index]->codecpar->codec_id; return result; } const char* Reader::str_audio_codec() { const char* result = "unknown codec"; if (audio_stream_index >= 0) { result = avcodec_get_name(fmt_ctx->streams[audio_stream_index]->codecpar->codec_id); } return result; } int64_t Reader::audio_bit_rate() { int64_t result = -1; if (audio_stream_index >= 0) result = fmt_ctx->streams[audio_stream_index]->codecpar->bit_rate; return result; } AVRational Reader::audio_time_base() { AVRational result = av_make_q(0, 0); if (audio_stream_index >= 0) result = fmt_ctx->streams[audio_stream_index]->time_base; return result; } int Reader::keyframe_cache_size() { int result = 1; if (P->glWidget) result = P->glWidget->keyframe_cache_size; return result; } void Reader::clear_stream_queues() { PKT_Q_MAP::iterator pkt_q; for (pkt_q = P->pkt_queues.begin(); pkt_q != P->pkt_queues.end(); ++pkt_q) { while (pkt_q->second->size() > 0) { AVPacket* tmp = pkt_q->second->pop(); av_packet_free(&tmp); } } FRAME_Q_MAP::iterator frame_q; for (frame_q = P->frame_queues.begin(); frame_q != P->frame_queues.end(); ++frame_q) { while (frame_q->second->size() > 0) { Frame f; frame_q->second->pop(f); } } } void Reader::clear_decoders() { if (P->videoDecoder) P->videoDecoder->flush(); if (P->audioDecoder) P->audioDecoder->flush(); } void Reader::signal_eof() { if (P->glWidget) P->glWidget->running = false; } /* std::string Reader::get_pipe_out_filename() { std::string filename; if (pipe_out_filename.empty()) { std::time_t t = std::time(nullptr); std::tm tm = *std::localtime(&t); std::stringstream str; str << std::put_time(&tm, "%y%m%d%H%M%S"); //filename = str.str() + extension; } else { filename = pipe_out_filename; } if (!pipe_out_dir.empty()) filename = pipe_out_dir + "/" + filename; return filename; } */ } libonvif-1.4.4/libavio/src/Exception.cpp0000644000175000017500000001623314355557026020125 0ustar stephenstephen/******************************************************************** * libavio/src/Exception.cpp * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #include "Exception.h" namespace avio { Exception::Exception(const char* msg) { buffer = msg; } Exception::Exception(const std::string& msg) { if (!tmp) tmp = (char*)malloc(256); strcpy(tmp, msg.c_str()); buffer = tmp; } Exception::~Exception() { free(tmp); } void ExceptionHandler::ck(int ret) { if (ret < 0) throw Exception("an AV exception has occurred"); } void ExceptionHandler::ck(int ret, CmdTag cmd_tag) { if (ret < 0) { char av_str[256]; av_strerror(ret, av_str, 256); throw Exception(tag(cmd_tag) + std::string(" has failed with error: ") + av_str); } } void ExceptionHandler::ck(int ret, const std::string& msg) { if (ret < 0) { char av_str[256]; av_strerror(ret, av_str, 256); throw Exception(msg + std::string(" ") + av_str); } } void ExceptionHandler::ck(void* arg, CmdTag cmd_tag) { if (arg == NULL) throw getNullException(cmd_tag); } /* void av::ExceptionHandler::ck(void* arg, const std::string& msg) { if (arg == NULL) throw Exception(msg); } void av::ExceptionHandler::ck(const void* arg, CmdTag cmd_tag) { if (arg == NULL) throw getNullException(cmd_tag); } void av::ExceptionHandler::ck(const void* arg, const std::string& msg) { if (arg == NULL) throw Exception(msg); } */ const Exception ExceptionHandler::getNullException(CmdTag cmd_tag) { if (cmd_tag == CmdTag::NONE) { return Exception("a NULL exception has occurred"); } else { return Exception(tag(cmd_tag) + std::string(" has failed with NULL value")); } } void ExceptionHandler::msg(const std::string& msg, MsgPriority priority, const std::string& qualifier) { if (fnMsgOut) fnMsgOut(msg, priority, qualifier); else { std::stringstream str; switch (priority) { case INFO: str << "I: "; break; case CRITICAL: str << "CRITICAL ERROR! "; break; case DEBUG: str << "D: "; break; } str << qualifier << msg; std::cout << str.str() << std::endl; } } const char* ExceptionHandler::tag(CmdTag cmd_tag) { switch (cmd_tag) { case CmdTag::AO2: return "avcodec_open2"; case CmdTag::AOI: return "avformat_open_input"; case CmdTag::ACI: return "avformat_close_input"; case CmdTag::AFSI: return "avformat_find_stream_info"; case CmdTag::AFBS: return "av_find_best_stream"; case CmdTag::APTC: return "avcodec_parameters_to_context"; case CmdTag::APFC: return "avcodec_parameters_from_context"; case CmdTag::AWH: return "avformat_write_header"; case CmdTag::AWT: return "avformat_write_trailer"; case CmdTag::AO: return "avio_open"; case CmdTag::AC: return "avio_close"; case CmdTag::ACP: return "avio_closep"; case CmdTag::AAOC2: return "avformat_alloc_output_context2"; case CmdTag::AFMW: return "av_frame_make_writable"; case CmdTag::AFGB: return "av_frame_get_buffer"; case CmdTag::AHCC: return "av_hwdevice_ctx_create"; case CmdTag::AWF: return "av_write_frame"; case CmdTag::ASP: return "avcodec_send_packet"; case CmdTag::ASF: return "av_seek_frame"; case CmdTag::AEV2: return "avcodec_encode_video2"; case CmdTag::ARF: return "av_read_frame"; case CmdTag::ADV2: return "av_decode_video2"; case CmdTag::ARP: return "avcodec_recieve_packet"; case CmdTag::AIWF: return "av_interleaved_write_frame"; case CmdTag::AFE: return "avcodec_find_encoder"; case CmdTag::AFD: return "avcodec_find_decoder"; case CmdTag::AAC3: return "avcodec_alloc_context3"; case CmdTag::AFA: return "av_frame_alloc"; case CmdTag::AAC: return "avformat_alloc_context"; case CmdTag::AFC: return "av_frame_copy"; case CmdTag::ABR: return "av_buffer_ref"; case CmdTag::AGF: return "av_guess_format"; case CmdTag::AGA: return "avfilter_graph_alloc"; case CmdTag::AGC: return "avfilter_graph_config"; case CmdTag::AL: return "avfilter_link"; case CmdTag::AGPP: return "avfilter_graph_parse_ptr"; case CmdTag::AGCF: return "avfilter_graph_create_filter"; case CmdTag::AHCA: return "avcodec_find_encoder_by_name"; case CmdTag::AHCI: return "av_hwframe_ctx_init"; case CmdTag::AHGB: return "av_hwframe_get_buffer"; case CmdTag::AFEBN: return "avcodec_find_encoder_by_name"; case CmdTag::AICTB: return "av_image_copy_to_buffer"; case CmdTag::APFDG: return "av_pix_fmt_desc_get"; case CmdTag::AGPFN: return "av_get_pix_fmt_name"; case CmdTag::ABAFF: return "av_buffersrc_add_frame_flags"; case CmdTag::AHFTBN: return "av_hwdevice_find_type_by_name"; case CmdTag::AOSI: return "av_opt_set_init"; case CmdTag::AOSIL: return "av_opt_set_int_list"; case CmdTag::AFDBN: return "avcodec_find_decoder_by_name"; case CmdTag::ACLFM: return "av_channel_layout_from_mask"; case CmdTag::ACLD: return "av_channel_layout_describe"; case CmdTag::AGHC: return "avcodec_get_hw_config"; case CmdTag::AHTD: return "av_hwframe_transfer_data"; case CmdTag::ANS: return "avformat_new_stream"; case CmdTag::AFR: return "av_frame_ref"; case CmdTag::AFCP: return "av_frame_copy_props"; case CmdTag::SGC: return "sws_getContext"; case CmdTag::AFIF: return "av_find_input_format"; case CmdTag::APA: return "av_packet_alloc"; case CmdTag::ADC: return "av_dict_copy"; case CmdTag::AIA: return "av_image_alloc"; case CmdTag::AM: return "av_malloc"; case CmdTag::SASO: return "swr_alloc_set_opts"; case CmdTag::SA: return "swr_alloc"; case CmdTag::SI: return "swr_init"; case CmdTag::SC: return "swr_convert"; case CmdTag::SS: return "sws_scale"; default: return ""; } } } libonvif-1.4.4/libavio/include/0000755000175000017500000000000014355557026016312 5ustar stephenstephenlibonvif-1.4.4/libavio/include/Decoder.h0000644000175000017500000000601414355557026020031 0ustar stephenstephen/******************************************************************** * libavio/include/Decoder.h * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #ifndef DECODER_H #define DECODER_H #include "Exception.h" #include "Queue.h" #include "Frame.h" #include "Reader.h" namespace avio { class Decoder { public: Decoder(Reader& reader, AVMediaType mediaType, AVHWDeviceType hw_device_type = AV_HWDEVICE_TYPE_NONE); ~Decoder(); int decode(AVPacket* pkt); void close(); void flush(); int sample_rate() { return dec_ctx->sample_rate; } int channels() { return dec_ctx->channels; } int frame_size() { return reader->fmt_ctx->streams[stream_index]->codecpar->frame_size; } uint64_t channel_layout() { return dec_ctx->channel_layout; } AVSampleFormat sample_format() { return dec_ctx->sample_fmt; } int bit_rate() { return dec_ctx->bit_rate; } int width() { return dec_ctx->width; } int height() { return dec_ctx->height; } AVPixelFormat pix_fmt() { return dec_ctx->pix_fmt; } int64_t nb_frames() { return reader->fmt_ctx->streams[stream_index]->nb_frames; } int64_t duration() { return reader->fmt_ctx->streams[stream_index]->duration; } AVRational time_base() { return reader->fmt_ctx->streams[stream_index]->time_base; } AVMediaType mediaType; std::string strMediaType; AVFrame* frame = NULL; AVFrame* sw_frame = NULL; AVFrame* cvt_frame = NULL; AVStream* stream = NULL; const AVCodec* dec = NULL; AVCodecContext* dec_ctx = NULL; AVBufferRef* hw_device_ctx = NULL; SwsContext* sws_ctx = NULL; AVHWDeviceType hw_device_type; int stream_index; Reader* reader; bool show_frames = false; Queue* frame_q = nullptr; Queue* pkt_q = nullptr; std::string frame_q_name; std::string pkt_q_name; std::string video_in() const { return std::string(pkt_q_name); } std::string audio_in() const { return std::string(pkt_q_name); } std::string video_out() const { return std::string(frame_q_name); } std::string audio_out() const { return std::string(frame_q_name); } void set_video_in(const std::string& name) { pkt_q_name = std::string(name); } void set_audio_in(const std::string& name) { pkt_q_name = std::string(name); } void set_video_out(const std::string& name) { frame_q_name = std::string(name); } void set_audio_out(const std::string& name) { frame_q_name = std::string(name); } ExceptionHandler ex; }; } #endif // DECODER_Hlibonvif-1.4.4/libavio/include/Clock.h0000644000175000017500000000234314355414521017510 0ustar stephenstephen/******************************************************************** * libavio/include/Clock.h * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #ifndef CLOCK_H #define CLOCK_H #include namespace avio { class Clock { public: uint64_t milliseconds(); uint64_t update(uint64_t rts); uint64_t stream_time(); int sync(uint64_t rts); void pause(bool paused); long long correction = 0; private: std::chrono::steady_clock clock; bool started = false; std::chrono::steady_clock::time_point play_start; std::chrono::steady_clock::time_point pause_start; }; } #endif // CLOCK_Hlibonvif-1.4.4/libavio/include/Display.h0000644000175000017500000001012214355557026020064 0ustar stephenstephen/******************************************************************** * libavio/include/Display.h * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #ifndef DISPLAY_H #define DISPLAY_H #define SDL_MAIN_HANDLED #include #include #include #include #include "Exception.h" #include "Queue.h" #include "Frame.h" #include "Clock.h" #include "Decoder.h" #include "Filter.h" #include "Reader.h" #include "Writer.h" #include "Encoder.h" #include "GLWidget.h" #define SDL_EVENT_LOOP_WAIT 10 namespace avio { enum class PlayState { PLAY, PAUSE, QUIT }; class Display { public: Display(Reader& reader) : reader(&reader) { } ~Display(); void* process; Reader* reader; void init(); int initAudio(int sample_rate, AVSampleFormat sample_fmt, int channels, uint64_t channel_layout, int stream_nb_samples); int initVideo(int width, int height, AVPixelFormat pix_fmt); static void AudioCallback(void* userdata, uint8_t* stream, int len); void videoPresentation(); PlayState getEvents(std::vector* events); bool display(); void pin_osd(bool arg); void enable_status(bool arg); void clearInputQueues(); void snapshot(); bool paused = false; Frame paused_frame; bool isPaused(); void togglePause(); bool single_step = false; bool reverse_step = false; int recent_idx = -1; bool recording = false; void toggleRecord(); Frame f; std::string audioDeviceStatus() const; const char* sdlAudioFormatName(SDL_AudioFormat format) const; SDL_Window* window = NULL; SDL_Renderer* renderer = NULL; SDL_Texture* texture = NULL; SDL_Surface* screen = NULL; SDL_AudioSpec sdl = { 0 }; SDL_AudioSpec have = { 0 }; std::string vfq_in_name; std::string afq_in_name; std::string vfq_out_name; std::string afq_out_name; Queue* vfq_in = nullptr; Queue* afq_in = nullptr; Queue* vfq_out = nullptr; Queue* afq_out = nullptr; std::string video_in() const { return std::string(vfq_in_name); } std::string audio_in() const { return std::string(afq_in_name); } std::string video_out() const { return std::string(vfq_out_name); } std::string audio_out() const { return std::string(afq_out_name); } void set_video_in(const std::string& name) { vfq_in_name = std::string(name); } void set_audio_in(const std::string& name) { afq_in_name = std::string(name); } void set_video_out(const std::string& name) { vfq_out_name = std::string(name); } void set_audio_out(const std::string& name) { afq_out_name = std::string(name); } bool fullscreen = false; SDL_AudioDeviceID audioDeviceID; Clock rtClock; SwrContext* swr_ctx = nullptr; AVSampleFormat sdl_sample_format = AV_SAMPLE_FMT_S16; uint8_t* swr_buffer = nullptr; int swr_buffer_size = 0; Queue sdl_buffer; int audio_buffer_len = 0; bool audio_eof = false; float volume = 1.0f; bool mute = false; int width = 0; int height = 0; AVPixelFormat pix_fmt = AV_PIX_FMT_NONE; uint64_t start_time; uint64_t duration; std::chrono::steady_clock clock; std::deque recent; bool request_recent_clear = false; int recent_q_size = 200; bool prepend_recent_write = false; ExceptionHandler ex; }; } #endif // DISPLAY_Hlibonvif-1.4.4/libavio/include/Filter.h0000644000175000017500000000605314355414521017704 0ustar stephenstephen/******************************************************************** * libavio/include/Filter.h * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #ifndef FILTER_H #define FILTER_H extern "C" { #include "libavcodec/avfft.h" #include "libavfilter/avfilter.h" #include "libswresample/swresample.h" #include "libavutil/avstring.h" #include "libavutil/opt.h" #include "libavfilter/buffersink.h" #include "libavfilter/buffersrc.h" #include "libavformat/avformat.h" } #include "Exception.h" #include "Decoder.h" namespace avio { class Filter { public: Filter(Decoder& decoder, const char* description); void initVideo(); void initAudio(); ~Filter(); void filter(const Frame& f); AVMediaType mediaType() { return decoder->mediaType; } Decoder* decoder = NULL; AVFilterContext* sink_ctx = NULL; AVFilterContext* src_ctx = NULL; AVFilterGraph* graph = NULL; AVFrame* frame = NULL; Frame tmp; std::string desc; uint64_t m_channel_layout = 0; int width() { return av_buffersink_get_w(sink_ctx); } int height() { return av_buffersink_get_h(sink_ctx); } AVPixelFormat pix_fmt() { return (AVPixelFormat)av_buffersink_get_format(sink_ctx); } AVRational time_base() { return av_buffersink_get_time_base(sink_ctx); } AVRational frame_rate() { return av_buffersink_get_frame_rate(sink_ctx); } int sample_rate() { return av_buffersink_get_sample_rate(sink_ctx); } int channels() { return av_buffersink_get_channels(sink_ctx); } int64_t channel_layout() { return av_buffersink_get_channel_layout(sink_ctx); } AVSampleFormat sample_format() { return (AVSampleFormat)av_buffersink_get_format(sink_ctx); } int frame_size() { return decoder->frame_size(); } Queue* frame_out_q = nullptr; std::string q_in_name; std::string q_out_name; bool show_frames = false; std::string video_in() const { return std::string(q_in_name); } std::string video_out() const { return std::string(q_out_name); } std::string audio_in() const { return std::string(q_in_name); } std::string audio_out() const { return std::string(q_out_name); } void set_audio_in(const std::string& name) { q_in_name = std::string(name); } void set_audio_out(const std::string& name) { q_out_name = std::string(name); } void set_video_in(const std::string& name) { q_in_name = std::string(name); } void set_video_out(const std::string& name) { q_out_name = std::string(name); } ExceptionHandler ex; }; } #endif // FILTER_Hlibonvif-1.4.4/libavio/include/GLWidget.h0000644000175000017500000001002114355557026020123 0ustar stephenstephen/******************************************************************** * libavio/include/GLWidget.h * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #ifndef GLWIDGET_H #define GLWIDGET_H #include #include #include #include #include #include #include #include "Queue.h" #include "Frame.h" #include "Reader.h" QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram) QT_FORWARD_DECLARE_CLASS(QOpenGLTexture) namespace avio { //QT_FORWARD_DECLARE_CLASS(Process) class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT public: GLWidget(); ~GLWidget(); void setZoomFactor(float); void setPanX(float); void setPanY(float); void setFormat(QImage::Format); void setVolume(int arg); void setMute(bool arg); bool isMute() { return mute; } void togglePaused(); bool isPaused(); void updateAspectRatio(); void play(const QString& arg); void stop(); float zoom_factor() { return factor; } void showStreamParameters(avio::Reader* reader); void toggle_pipe_out(const std::string& filename); bool checkForStreamHeader(const char*); static void start(void * parent); QSize sizeHint() const override; std::string vfq_in_name; std::string vfq_out_name; Queue* vfq_in = nullptr; Queue* vfq_out = nullptr; std::string video_in() const { return std::string(vfq_in_name); } std::string video_out() const { return std::string(vfq_out_name); } void set_video_in(const std::string& name) { vfq_in_name = std::string(name); } void set_video_out(const std::string& name) { vfq_out_name = std::string(name); } int poll_interval = 1; int tex_width = 0; int tex_height = 0; bool maintain_aspect_ratio = true; long media_duration = 0; long media_start_time = 0; bool running = false; bool disable_audio = false; int keyframe_cache_size = 1; int vpq_size = 0; int apq_size = 0; std::string mediaShortName; AVHWDeviceType hardwareDecoder = AV_HWDEVICE_TYPE_NONE; void* process = nullptr; Reader* get_reader(); bool python_enabled = false; std::string python_dir; std::string python_file; std::string python_class; std::string python_args; std::function initPy = nullptr; std::function runPy = nullptr; bool pyInitialized = false; signals: void timerStart(); void timerStop(); void cameraTimeout(); void connectFailed(const QString&); void openWriterFailed(const std::string&); void msg(const QString&); void progress(float); void mediaPlayingFinished(); void mediaPlayingStarted(); public slots: void poll(); void seek(float); protected: void initializeGL() override; void paintGL() override; void resizeGL(int width, int height) override; private: QOpenGLTexture *texture = nullptr; QOpenGLShaderProgram *program = nullptr; QOpenGLBuffer vbo; QOpenGLShader *vshader; QOpenGLShader *fshader; Frame f; std::mutex mutex; float zoom = 1.0f; float factor = 1.0f; float aspect = 1.0f; float pan_x = 0.0f; float pan_y = 0.0f; int volume = 100; bool mute = false; QImage::Format fmt = QImage::Format_RGB888; char uri[1024]; QTimer *timer; }; } #endif libonvif-1.4.4/libavio/include/Encoder.h0000644000175000017500000000730614355414521020040 0ustar stephenstephen/******************************************************************** * libavio/include/Encoder.h * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #ifndef ENCODER_H #define ENCODER_H extern "C" { #include #include #include #include #include #include #include #include #include } #include "Exception.h" #include "Queue.h" #include "Frame.h" #include "Writer.h" namespace avio { class Encoder { public: Encoder(Writer& writer, AVMediaType mediaType); ~Encoder(); void openVideoStream(); void openAudioStream(); void init(); int encode(Frame& f); void close(); bool cmpFrame(AVFrame* frame); bool opened = false; Writer* writer; AVMediaType mediaType; std::string strMediaType; AVStream* stream = NULL; AVCodecContext* enc_ctx = NULL; AVPacket* pkt = NULL; SwsContext* sws_ctx = NULL; SwrContext* swr_ctx = NULL; AVFrame* cvt_frame = NULL; AVBufferRef* hw_frames_ref = NULL; AVBufferRef* hw_device_ctx = NULL; AVFrame* hw_frame = NULL; AVHWDeviceType hw_device_type = AV_HWDEVICE_TYPE_NONE; std::string hw_video_codec_name; std::string profile; AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE; AVPixelFormat sw_pix_fmt = AV_PIX_FMT_NONE; AVPixelFormat pix_fmt = AV_PIX_FMT_NONE; int width = 0; int height = 0; int video_bit_rate = 0; int frame_rate = 0; int gop_size = 0; AVRational video_time_base = av_make_q(0, 0); AVDictionary* opts = NULL; AVSampleFormat sample_fmt = AV_SAMPLE_FMT_NONE; uint64_t channel_layout = 0; int audio_bit_rate = 0; int sample_rate = 0; int nb_samples = 0; int channels = 0; AVRational audio_time_base = av_make_q(0, 0); void set_channel_layout_mono() { channel_layout = AV_CH_LAYOUT_MONO; } void set_channel_layout_stereo() { channel_layout = AV_CH_LAYOUT_STEREO; } int64_t total_samples = 0; bool show_frames = false; int64_t pts_offset = 0; bool first_pass = true; Queue* frame_q = nullptr; Queue* pkt_q = nullptr; std::string frame_q_name; std::string pkt_q_name; int frame_q_max_size = 0; std::string video_in() const { return std::string(frame_q_name); } std::string audio_in() const { return std::string(frame_q_name); } std::string video_out() const { return std::string(pkt_q_name); } std::string audio_out() const { return std::string(pkt_q_name); } void set_video_in(const std::string& name) { frame_q_name = std::string(name); } void set_audio_in(const std::string& name) { frame_q_name = std::string(name); } void set_video_out(const std::string& name) { pkt_q_name = std::string(name); } void set_audio_out(const std::string& name) { pkt_q_name = std::string(name); } ExceptionHandler ex; }; } #endif // ENCODER_Hlibonvif-1.4.4/libavio/include/Frame.h0000644000175000017500000000356314355414521017514 0ustar stephenstephen/******************************************************************** * libavio/include/Frame.h * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #ifndef FRAME_H #define FRAME_H extern "C" { #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavutil/imgutils.h" #include "libavutil/pixdesc.h" #include "libavcodec/avcodec.h" #include "libavutil/avassert.h" } #include "Exception.h" namespace avio { class Frame { public: Frame(); ~Frame(); Frame(const Frame& other); Frame(Frame&& other) noexcept; Frame(AVFrame* src); Frame(int width, int height, AVPixelFormat pix_fmt); Frame& operator=(const Frame& other); Frame& operator=(Frame&& other) noexcept; AVFrame* copyFrame(AVFrame* src); bool isValid() const { return m_frame ? true : false; } void invalidate(); AVMediaType mediaType() const; uint64_t pts() { return m_frame ? m_frame->pts : AV_NOPTS_VALUE; } void set_rts(AVStream* stream); // called from Decoder::decode void set_pts(AVStream* stream); // called from Encoder::encode std::string description() const; AVFrame* m_frame = NULL; uint64_t m_rts; bool m_faded = false; // used by osd to avoid duplicate background fade ExceptionHandler ex; }; } #endif // FRAME_Hlibonvif-1.4.4/libavio/include/Pipe.h0000644000175000017500000000367514355557026017373 0ustar stephenstephen/******************************************************************** * libavio/include/Pipe.h * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #ifndef PIPE_H #define PIPE_H extern "C" { #include #include #include #include #include #include #include #include #include } #include #include "Reader.h" #include "Exception.h" namespace avio { class Pipe { public: Pipe(Reader& reader); ~Pipe(); void* process; AVCodecContext* getContext(AVMediaType mediaType); bool open(const std::string& filename); void close(); void adjust_pts(AVPacket* pkt); void write(AVPacket* pkt); void show_ctx(); std::string m_filename; Reader* reader; AVFormatContext* fmt_ctx = NULL; AVCodecContext* video_ctx = NULL; AVCodecContext* audio_ctx = NULL; AVStream* video_stream = NULL; AVStream* audio_stream = NULL; int64_t video_next_pts = 0; int64_t audio_next_pts = 0; int last_video_pkt_duration = 0; bool opened = false; std::mutex mutex; ExceptionHandler ex; }; } #endif // PIPE_Hlibonvif-1.4.4/libavio/include/Exception.h0000644000175000017500000000635114355414521020416 0ustar stephenstephen/******************************************************************** * libavio/include/Exception.h * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #pragma warning(disable: 26812) // unscoped enum #ifndef EXCEPTION_H #define EXCEPTION_H extern "C" { #include #include #include #include #include #include #include #include } #include #include #include #include enum CmdTag { NONE, AO2, AOI, ACI, AFSI, APTC, APFC, AWH, AWT, AO, AC, ACP, AAOC2, AFMW, AFGB, AHCC, AFBS, AWF, ASP, ASF, AEV2, ARF, ADV2, ARP, AIWF, AFE, AFD, AAC3, AFA, AAC, AFC, ABR, AGF, AGA, AGC, AL, AGPP, AGCF, AHCA, AHCI, AHGB, AFEBN, AICTB, AGPFN, ABAFF, APFDG, AHFTBN, AOSI, AOSIL, AFDBN, ACLFM, ACLD, AGHC, AHTD, ANS, AFCP, SGC, AFIF, APA, ADC, AIA, AFR, AM, SASO, SA, SI, SC, SS }; enum MsgPriority { CRITICAL, DEBUG, INFO }; namespace avio { class Exception : public std::exception { public: Exception(const char* msg); Exception(const std::string& msg); ~Exception(); const char* what() const throw () { return buffer; } char* tmp = nullptr; const char* buffer; }; class ExceptionHandler { public: void ck(int ret); void ck(int ret, CmdTag cmd_tag); void ck(int ret, const std::string& msg); void ck(void* arg, CmdTag cmd_tag = CmdTag::NONE); const Exception getNullException(CmdTag cmd_tag); const char* tag(CmdTag cmd_tag); std::function fnMsgOut = nullptr; void msg(const std::string& msg, MsgPriority priority = MsgPriority::INFO, const std::string& qualifier = ""); }; } #endif // EXCEPTION_H libonvif-1.4.4/libavio/include/Writer.h0000644000175000017500000000374414355414521017737 0ustar stephenstephen/******************************************************************** * libavio/include/Writer.h * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #ifndef WRITER_H #define WRITER_H extern "C" { #include #include #include #include #include #include #include #include #include } #include #include "Exception.h" namespace avio { enum class EncoderState { MIXED, OPEN, CLOSED }; class Writer { public: Writer(const std::string& format); ~Writer(); void open(const std::string& filename); void write(AVPacket* pkt); void close(); void init(); EncoderState getEncoderState(); AVFormatContext* fmt_ctx = NULL; int video_stream_id = AVERROR_STREAM_NOT_FOUND; int audio_stream_id = AVERROR_STREAM_NOT_FOUND; std::string m_format; std::string write_dir; std::string filename; void* videoEncoder = nullptr; void* audioEncoder = nullptr; bool enabled = false; bool opened = false; bool show_video_pkts = false; bool show_audio_pkts = false; std::mutex mutex; ExceptionHandler ex; }; } #endif // WRITER_Hlibonvif-1.4.4/libavio/include/Queue.h0000644000175000017500000000746714355557026017565 0ustar stephenstephen/******************************************************************** * libavio/include/Queue.h * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #ifndef QUEUE_H #define QUEUE_H #include #include #include #include #include #include #include namespace avio { class QueueClosedException : public std::exception { public: const char* what() const throw() { return "attempt to access closed queue"; } }; template class Queue { public: Queue(size_t max_size = 1); void push(T const&); T pop(); void pop(T&); T peek(); int size(); void close(); bool full() { return m_size == m_max_size; } bool empty() { return m_front == -1; } bool closed() { return m_closed; } void clear() { m_front = m_rear = -1; } void set_max_size(int arg); private: std::vector m_data; int m_max_size; int m_front = -1; int m_rear = -1; std::mutex n_mutex; std::condition_variable m_cond_push, m_cond_pop; bool m_closed = false; int m_size = 0; }; template Queue::Queue(size_t max_size) : m_max_size(max_size) { m_data.reserve(max_size); } template void Queue::set_max_size(int arg) { m_max_size = arg; m_data.reserve(m_max_size); } template void Queue::push(T const& element) { std::unique_lock lock(n_mutex); while (full()) { if (m_closed) break; m_cond_push.wait(lock); } if (m_closed) throw QueueClosedException(); if (m_front == -1) m_front = m_rear = 0; else if (m_rear == m_max_size - 1 && m_front != 0) m_rear = 0; else m_rear++; if (m_data.size() < m_rear + 1) m_data.push_back(element); else m_data[m_rear] = element; m_size++; m_cond_pop.notify_one(); } template T Queue::pop() { std::unique_lock lock(n_mutex); while (empty()) { if (m_closed) break; m_cond_pop.wait(lock); } if (m_closed) throw QueueClosedException(); T& result = m_data[m_front]; if (m_front == m_rear) m_front = m_rear = -1; else if (m_front == m_max_size - 1) m_front = 0; else m_front++; m_size--; m_cond_push.notify_one(); return result; } template void Queue::pop(T& arg) { std::unique_lock lock(n_mutex); while (empty()) { if (m_closed) break; m_cond_pop.wait(lock); } if (m_closed) throw QueueClosedException(); arg = m_data[m_front]; if (m_front == m_rear) m_front = m_rear = -1; else if (m_front == m_max_size - 1) m_front = 0; else m_front++; m_size--; m_cond_push.notify_one(); } template T Queue::peek() { std::unique_lock lock(n_mutex); while (empty()) { if (m_closed) break; m_cond_pop.wait(lock); } if (m_closed) throw QueueClosedException(); T result = T(m_data[m_front]); return result; } template int Queue::size() { std::lock_guard lock(n_mutex); return m_size; } template void Queue::close() { std::unique_lock lock(n_mutex); m_closed = true; m_cond_push.notify_all(); m_cond_pop.notify_all(); } } #endif // QUEUE_Hlibonvif-1.4.4/libavio/include/avio.h0000644000175000017500000004634014355557026017430 0ustar stephenstephen/******************************************************************** * libavio/include/avio.h * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #ifndef AVIO_H #define AVIO_H #include "Exception.h" #include "Queue.h" #include "Reader.h" #include "Decoder.h" #include "Encoder.h" #include "Writer.h" #include "Pipe.h" #include "Filter.h" #include "Display.h" #include "GLWidget.h" #define P ((Process*)process) namespace avio { static void show_pkt(AVPacket* pkt) { std::stringstream str; str << " index: " << pkt->stream_index << " flags: " << pkt->flags << " pts: " << pkt->pts << " dts: " << pkt->dts << " size: " << pkt->size << " duration: " << pkt->duration; std::cout << str.str() << std::endl; } static void read(Reader* reader, Queue* vpq, Queue* apq) { if (reader->vpq_max_size > 0 && vpq) vpq->set_max_size(reader->vpq_max_size); if (reader->apq_max_size > 0 && apq) apq->set_max_size(reader->apq_max_size); Pipe* pipe = nullptr; std::deque pkts; int keyframe_count = 0; int keyframe_marker = 0; try { while (true) { AVPacket* pkt = reader->read(); if (!pkt) break; reader->running = true; if (reader->request_break) { reader->clear_stream_queues(); break; } if (reader->seek_target_pts != AV_NOPTS_VALUE) { AVPacket* tmp = reader->seek(); while (pkts.size() > 0) { AVPacket* jnk = pkts.front(); pkts.pop_front(); av_packet_free(&jnk); } if (tmp) { av_packet_free(&pkt); pkt = tmp; reader->clear_stream_queues(); } else { break; } } if (reader->request_pipe_write) { // pkts queue caches recent packets based off key frame packet if (!pipe) { pipe = new Pipe(*reader); pipe->process = reader->process; if (pipe->open(reader->pipe_out_filename)) { while (pkts.size() > 0) { AVPacket* tmp = pkts.front(); pkts.pop_front(); pipe->write(tmp); av_packet_free(&tmp); } } else { delete pipe; pipe = nullptr; } } // verify pipe was opened successfully before continuing if (pipe) { AVPacket* tmp = av_packet_clone(pkt); pipe->write(tmp); av_packet_free(&tmp); } } else { if (pipe) { pipe->close(); delete pipe; pipe = nullptr; } if (pkt->stream_index == reader->video_stream_index) { if (pkt->flags) { // key frame packet found in stream if (++keyframe_count >= reader->keyframe_cache_size()) { while (pkts.size() > keyframe_marker) { AVPacket* tmp = pkts.front(); pkts.pop_front(); av_packet_free(&tmp); } keyframe_count--; } keyframe_marker = pkts.size(); } } AVPacket* tmp = av_packet_clone(pkt); pkts.push_back(tmp); } if (reader->stop_play_at_pts != AV_NOPTS_VALUE && pkt->stream_index == reader->seek_stream_index()) { if (pkt->pts > reader->stop_play_at_pts) { av_packet_free(&pkt); break; } } if (pkt->stream_index == reader->video_stream_index) { if (reader->show_video_pkts) show_pkt(pkt); if (vpq) vpq->push(pkt); else av_packet_free(&pkt); } else if (pkt->stream_index == reader->audio_stream_index) { if (reader->show_audio_pkts) show_pkt(pkt); if (apq) apq->push(pkt); else av_packet_free(&pkt); } } if (vpq) vpq->push(NULL); if (apq) apq->push(NULL); } catch (const QueueClosedException& e) {} catch (const Exception& e) { std::cout << " reader failed: " << e.what() << std::endl; } reader->signal_eof(); reader->running = false; } static void decode(Decoder* decoder, Queue* pkt_q, Queue* frame_q) { decoder->frame_q = frame_q; decoder->pkt_q = pkt_q; try { while (AVPacket* pkt = pkt_q->pop()) { decoder->decode(pkt); av_packet_free(&pkt); } decoder->decode(NULL); decoder->frame_q->push(Frame(nullptr)); } catch (const QueueClosedException& e) { } catch (const Exception& e) { std::stringstream str; str << decoder->strMediaType << " decoder failed: " << e.what(); std::cout << str.str() << std::endl; decoder->reader->exit_error_msg = str.str(); decoder->decode(NULL); decoder->frame_q->push(Frame(nullptr)); } } static void filter(Filter* filter, Queue* q_in, Queue* q_out) { try { Frame f; filter->frame_out_q = q_out; while (true) { q_in->pop(f); filter->filter(f); if (!f.isValid()) break; } filter->frame_out_q->push(Frame(nullptr)); } catch (const QueueClosedException& e) {} } static void write(Writer* writer, Encoder* encoder) { try { Frame f; while (true) { encoder->frame_q->pop(f); if (encoder->show_frames) std::cout << f.description() << std::endl; if (writer->enabled) { if (!encoder->opened) encoder->init(); if (!writer->opened) { std::string filename; if (!writer->write_dir.empty()) filename = writer->write_dir + "/"; if (writer->filename.empty()) { std::time_t t = std::time(nullptr); std::tm tm = *std::localtime(&t); std::stringstream str; str << std::put_time(&tm, "%y%m%d%H%M%S"); filename += str.str() + "." + writer->m_format; } else { filename += writer->filename; } writer->open(filename); } if (writer->opened && encoder->opened) encoder->encode(f); } else { if (writer->opened) { if (encoder->opened) { Frame tmp(nullptr); encoder->encode(tmp); encoder->close(); } writer->close(); } } } } catch (const QueueClosedException& e) { if (writer->opened) { if (encoder->opened) { Frame tmp(nullptr); encoder->encode(tmp); encoder->close(); } writer->close(); } } } static void pkt_drain(Queue* pkt_q) { try { while (AVPacket* pkt = pkt_q->pop()) { av_packet_free(&pkt); } } catch (const QueueClosedException& e) {} } static void frame_drain(Queue* frame_q) { Frame f; try { while (true) { frame_q->pop(f); if (!f.isValid()) break; } } catch (const QueueClosedException& e) {} } typedef std::map*> PKT_Q_MAP; typedef std::map*> FRAME_Q_MAP; class Process { public: Reader* reader = nullptr; Decoder* videoDecoder = nullptr; Decoder* audioDecoder = nullptr; Filter* videoFilter = nullptr; Filter* audioFilter = nullptr; Writer* writer = nullptr; Encoder* videoEncoder = nullptr; Encoder* audioEncoder = nullptr; Display* display = nullptr; GLWidget* glWidget = nullptr; PKT_Q_MAP pkt_queues; FRAME_Q_MAP frame_queues; std::vector pkt_q_names; std::vector frame_q_names; std::map display_q_names; std::vector frame_q_drain_names; std::vector pkt_q_drain_names; std::vector merge_filenames; int interleaved_q_size = 0; std::string interleaved_q_name; bool muxing = false; std::string mux_video_q_name; std::string mux_audio_q_name; std::vector ops; Process() { av_log_set_level(AV_LOG_PANIC); } ~Process() { } void key_event(int keyCode) { SDL_Event event; event.type = SDL_KEYDOWN; event.key.keysym.sym = keyCode; SDL_PushEvent(&event); } void add_reader(Reader& reader_in) { reader_in.process = (void*)this; reader = &reader_in; if (!reader_in.vpq_name.empty()) pkt_q_names.push_back(reader_in.vpq_name); if (!reader_in.apq_name.empty()) pkt_q_names.push_back(reader_in.apq_name); } void add_decoder(Decoder& decoder_in) { if (decoder_in.mediaType == AVMEDIA_TYPE_VIDEO) videoDecoder = &decoder_in; if (decoder_in.mediaType == AVMEDIA_TYPE_AUDIO) audioDecoder = &decoder_in; pkt_q_names.push_back(decoder_in.pkt_q_name); frame_q_names.push_back(decoder_in.frame_q_name); } void add_filter(Filter& filter_in) { if (filter_in.mediaType() == AVMEDIA_TYPE_VIDEO) videoFilter = &filter_in; if (filter_in.mediaType() == AVMEDIA_TYPE_AUDIO) audioFilter = &filter_in; frame_q_names.push_back(filter_in.q_in_name); frame_q_names.push_back(filter_in.q_out_name); } void add_encoder(Encoder& encoder_in) { if (encoder_in.mediaType == AVMEDIA_TYPE_VIDEO) { videoEncoder = &encoder_in; writer = videoEncoder->writer; } if (encoder_in.mediaType == AVMEDIA_TYPE_AUDIO) { audioEncoder = &encoder_in; writer = audioEncoder->writer; } pkt_q_names.push_back(encoder_in.pkt_q_name); frame_q_names.push_back(encoder_in.frame_q_name); } void add_display(Display& display_in) { display_in.process = (void*)this; display = &display_in; if (!display->vfq_out_name.empty()) frame_q_names.push_back(display->vfq_out_name); if (!display->afq_out_name.empty()) frame_q_names.push_back(display->afq_out_name); } void add_widget(GLWidget* widget_in) { widget_in->process = (void*)this; glWidget = widget_in; } void add_frame_drain(const std::string& frame_q_name) { frame_q_drain_names.push_back(frame_q_name); } void add_packet_drain(const std::string& pkt_q_name) { pkt_q_drain_names.push_back(pkt_q_name); } void cleanup() { for (PKT_Q_MAP::iterator q = pkt_queues.begin(); q != pkt_queues.end(); ++q) { if (q->second) { while (q->second->size() > 0) { AVPacket* pkt = q->second->pop(); av_packet_free(&pkt); } q->second->close(); } } for (FRAME_Q_MAP::iterator q = frame_queues.begin(); q != frame_queues.end(); ++q) { if (q->second) { while (q->second->size() > 0) { Frame f; q->second->pop(f); } q->second->close(); } } for (int i = 0; i < ops.size(); i++) { ops[i]->join(); delete ops[i]; } for (PKT_Q_MAP::iterator q = pkt_queues.begin(); q != pkt_queues.end(); ++q) { if (q->second) delete q->second; } for (FRAME_Q_MAP::iterator q = frame_queues.begin(); q != frame_queues.end(); ++q) { if (q->second) delete q->second; } } void run() { for (const std::string& name : pkt_q_names) { if (!name.empty()) { if (pkt_queues.find(name) == pkt_queues.end()) pkt_queues.insert({ name, new Queue() }); } } for (const std::string& name : frame_q_names) { if (!name.empty()) { if (frame_queues.find(name) == frame_queues.end()) frame_queues.insert({ name, new Queue() }); } } if (reader) { ops.push_back(new std::thread(read, reader, reader->has_video() ? pkt_queues[reader->vpq_name] : nullptr, reader->has_audio() ? pkt_queues[reader->apq_name] : nullptr)); } if (videoDecoder) { ops.push_back(new std::thread(decode, videoDecoder, pkt_queues[videoDecoder->pkt_q_name], frame_queues[videoDecoder->frame_q_name])); } if (videoFilter) { ops.push_back(new std::thread(filter, videoFilter, frame_queues[videoFilter->q_in_name], frame_queues[videoFilter->q_out_name])); } if (glWidget) { if (!glWidget->vfq_in_name.empty()) glWidget->vfq_in = frame_queues[glWidget->vfq_in_name]; if (!glWidget->vfq_out_name.empty()) glWidget->vfq_out = frame_queues[glWidget->vfq_out_name]; } if (audioDecoder) { ops.push_back(new std::thread(decode, audioDecoder, pkt_queues[audioDecoder->pkt_q_name], frame_queues[audioDecoder->frame_q_name])); } if (audioFilter) { ops.push_back(new std::thread(filter, audioFilter, frame_queues[audioFilter->q_in_name], frame_queues[audioFilter->q_out_name])); } if (videoEncoder) { videoEncoder->pkt_q = pkt_queues[videoEncoder->pkt_q_name]; videoEncoder->frame_q = frame_queues[videoEncoder->frame_q_name]; if (videoEncoder->frame_q_max_size > 0) videoEncoder->frame_q->set_max_size(videoEncoder->frame_q_max_size); if (writer->enabled) videoEncoder->init(); ops.push_back(new std::thread(write, videoEncoder->writer, videoEncoder)); } if (audioEncoder) { audioEncoder->pkt_q = pkt_queues[audioEncoder->pkt_q_name]; audioEncoder->frame_q = frame_queues[audioEncoder->frame_q_name]; if (audioEncoder->frame_q_max_size > 0) audioEncoder->frame_q->set_max_size(audioEncoder->frame_q_max_size); if (writer->enabled) audioEncoder->init(); ops.push_back(new std::thread(write, audioEncoder->writer, audioEncoder)); } for (const std::string& name : frame_q_drain_names) ops.push_back(new std::thread(frame_drain, frame_queues[name])); for (const std::string& name : pkt_q_drain_names) ops.push_back(new std::thread(pkt_drain, pkt_queues[name])); if (display) { if (!display->vfq_in_name.empty()) display->vfq_in = frame_queues[display->vfq_in_name]; if (!display->afq_in_name.empty()) display->afq_in = frame_queues[display->afq_in_name]; if (!display->vfq_out_name.empty()) display->vfq_out = frame_queues[display->vfq_out_name]; if (!display->afq_out_name.empty()) display->afq_out = frame_queues[display->afq_out_name]; display->init(); if (glWidget) glWidget->emit timerStart(); while (display->display()) {} if (glWidget) glWidget->emit timerStop(); // reader shutdown routine if downstream module shuts down process // there is probably a better way to handle this situation if (!reader->exit_error_msg.empty()) { int count = 0; std::cout << "reader attempting shutdown" << std::endl; reader->request_break = true; while (reader->running) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); if (count++ > 1000) { if (!reader->apq_name.empty()) { Queue* q = pkt_queues[reader->apq_name]; if (q) { while (q->size() > 0) { AVPacket* pkt = q->pop(); av_packet_free(&pkt); } } } if (!reader->vpq_name.empty()) { Queue* q = pkt_queues[reader->vpq_name]; if (q) { while (q->size() > 0) { AVPacket* pkt = q->pop(); av_packet_free(&pkt); } } } break; } } throw Exception(reader->exit_error_msg); } if (writer) { while (!display->audio_eof) SDL_Delay(1); writer->enabled = false; } } cleanup(); } }; } #endif // AVIO_Hlibonvif-1.4.4/libavio/include/Reader.h0000644000175000017500000000575314355557026017677 0ustar stephenstephen/******************************************************************** * libavio/include/Reader.h * * Copyright (c) 2022 Stephen Rhodes * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *********************************************************************/ #ifndef READER_H #define READER_H #include #include "Exception.h" #include "Queue.h" namespace avio { class Reader { public: Reader() {} Reader(const char* filename); ~Reader(); AVPacket* read(); void* process; void request_seek(float pct); int64_t seek_target_pts = AV_NOPTS_VALUE; int64_t seek_found_pts = AV_NOPTS_VALUE; int64_t stop_play_at_pts = AV_NOPTS_VALUE; int seek_stream_index(); AVPacket* seek(); bool seeking(); void start_from(int milliseconds); void end_at(int milliseconds); int64_t start_time(); int64_t duration(); int64_t bit_rate(); bool has_video(); int width(); int height(); AVRational frame_rate(); const char* str_pix_fmt(); AVPixelFormat pix_fmt(); const char* str_video_codec(); AVCodecID video_codec(); int64_t video_bit_rate(); AVRational video_time_base(); bool has_audio(); int channels(); int sample_rate(); int frame_size(); uint64_t channel_layout(); std::string str_channel_layout(); AVSampleFormat sample_format(); const char* str_sample_format(); AVCodecID audio_codec(); const char* str_audio_codec(); int64_t audio_bit_rate(); AVRational audio_time_base(); int keyframe_cache_size(); bool request_pipe_write = false; bool pipe_out = false; bool pipe_out_enabled = false; std::string pipe_out_dir; std::string pipe_out_filename; AVFormatContext* fmt_ctx = NULL; int video_stream_index = -1; int audio_stream_index = -1; bool closed = false; int64_t last_video_pts = 0; int64_t last_audio_pts = 0; bool show_video_pkts = false; bool show_audio_pkts = false; std::string vpq_name; std::string apq_name; int vpq_max_size = 0; int apq_max_size = 0; std::string video_out() const { return std::string(vpq_name); } std::string audio_out() const { return std::string(apq_name); } void set_video_out(const std::string& name) { vpq_name = std::string(name); } void set_audio_out(const std::string& name) { apq_name = std::string(name); } bool request_break = false; bool running = false; std::string exit_error_msg; void clear_stream_queues(); bool isPaused(); void clear_decoders(); void signal_eof(); ExceptionHandler ex; }; } #endif // READER_Hlibonvif-1.4.4/README.md0000644000175000017500000004642314355557521014532 0ustar stephenstephen libonvif ======== A client side implementation of the ONVIF specification. Introduction ------------ libonvif is a multi platform library implementing the client side of the ONVIF specification for communicating with IP enabled compatible cameras. It will compile on Linux and Windows. An utility program is included with libonvif that can be used as a maintenance tool and will discover compatible cameras on the local network and may be used to query each of them for device configuration such as RSTP connection uri information or video settings. Additionally, there is a comprehensive GUI sample program that includes the discovery functionality as well as controls for adjusting camera parameters and PTZ operations. The GUI program has a record function that will write the camera stream to file and includes some basic media file management tools. The GUI sample is written in Qt and can be compiled with cmake. The utility program is invoked using the 'onvif-util' command. The GUI interface may be invoked using the 'onvif-gui' command. To Install From Source ---------------------- BUILD ON LINUX ```bash sudo apt install libxml2-dev sudo apt install qtbase5-dev sudo apt install libavcodec-dev sudo apt install libavdevice-dev sudo apt install libsdl2-dev git clone https://github.com/sr99622/libonvif.git cd libonvif mkdir build cd build cmake -DBUILD_GUI=ON .. make sudo make install sudo ldconfig ``` BUILD ON WINDOWS The recommended method for building libonvif on Windows is to use a conda environment to install dependencies. To install anaconda on Windows, please refer to the link https://docs.anaconda.com/anaconda/install/windows/. Once anaconda has been installed, launch a conda prompt and then use the following commands to build. You will need to have Microsoft Visual Studio installed with the C++ compiler, as well as git and cmake. The cmake installer will integrate the executables and development files into the conda environment. The conda environment must be active when running the executables. ```bash conda create --name onvif -c conda-forge qt libxml2 ffmpeg sdl2 conda activate onvif git clone https://github.com/sr99622/libonvif.git cd libonvif mkdir build cd build cmake -DBUILD_GUI=ON -DCMAKE_INSTALL_PREFIX=%CONDA_PREFIX%\Library .. cmake --build . --config Release cmake --install . ``` ALTERNATE WINDOWS BUILD If you are only interested in the libonvif library and command line utility, it is possible to build on Windows without using conda. You will need to get the source code for libxml2 from https://github.com/GNOME/libxml2. Upon completion of the build for libxml2, you will need Adminstrator privileges to install the library. You will also need to set the PATH environment variable to include the libxml2.dll path. The instructions below will build a stripped down version of libxml2 which is fine for onvif. To get an administrator privileged command prompt, use the Windows search bar for cmd and right click on the command prompt icon to select Run as Administrator. To make a permanent change to the PATH environment variable, use the Settings->About->Advanced System Settings->Environment Variables configuration screen. For Windows, use the commands following from an Administrator privileged command prompt. To make a permanent change to the PATH environment variable, use the Settings->About->Advanced System Settings->Environment Variables configuration screen. ```bash git clone https://github.com/GNOME/libxml2.git cd libxml2 mkdir build cd build cmake -DLIBXML2_WITH_PYTHON=OFF -DLIBXML2_WITH_ICONV=OFF -DLIBXML2_WITH_LZMA=OFF -DLIBXML2_WITH_ZLIB=OFF .. cmake --build . --config Release cmake --install . set PATH=%PATH%;"C:\Program Files (x86)\libxml2\bin" git clone https://github.com/sr99622/libonvif.git cd libonvif mkdir build cd build cmake .. cmake --build . --config Release cmake --install . set PATH=%PATH%;"C:\Program Files (x86)\libonvif\bin" ``` Utility Program Commands ---------------------- SYNOPSIS onvif-util [-ahs] [-u ] [-p ] [host_ip_address] DESCRIPTION View and set parameters on onvif compatible IP cameras. The command may be used to find and identify cameras, and then to create an interactive session that can be used to query and set camera properties. -a, --all show all cameras on the network -h, --help show the help for this command -u, --user set the username for the camera login -p, --password set the password for the camera login To view all cameras on the network: onvif-util -a To login to a particular camera: onvif-util -u username -p password ip_address To login to a camera with safe mode disabled: onvif-util -s -u username -p password ip_address Once logged into the camera you can view data using the 'get' command followed by the data requested. The (n) indicates an optional profile index to apply the setting, otherwise the current profile is used Data Retrieval Commands (start with get) get rtsp 'pass'(optional) (n) - Get rtsp uri for camera, with optional password credential get capabilities get time get profiles get profile (n) get video (n) get video options (n) get imaging get imaging options get network Parameter Setting Commands (start with set) set resolution (n) - Resolution setting in the format widthxheight, must match option set framerate (n) set gov_length (n) set bitrate (n) set bightness value(required) set contrast value(required) set saturation value(required) set sharpness value(required) set ip_address value(required) set default_gateway value(required) set dns value(required) set dhcp value(required) - Accepted settings are 'on' and off' set password value(required) Maintenance Commands help safe - set safe mode on. Viewer and browser are disabled unsafe - set safe mode off. Viewer and browser are enabled browser - Use browser to access camera configurations view (n) - View the camera output using ffplay (ffplay must be installed in the path) view player (n) - View the camera output with user specified player e.g. view vlc sync_time 'zone'(optional) - Sync the camera time to the computer dump - Full set of raw data from camera configuration reboot To Exit Camera Session quit EXAMPLES A typical session would begin by finding the cameras on the network > onvif-util -a Looking for cameras on the network... Found 8 cameras 192.168.1.18 localhost(TV TV-IP319PI) 192.168.1.7 (IPC-BO IPC-122) 192.168.1.14 IPC(Dahua IPC-HDW4631C-A) 192.168.1.6 IPC(Amcrest IP2M-841EB) 192.168.1.12 (AXIS M1065-LW) 192.168.1.12 (AXIS M1065-LW) 192.168.1.2 IPC(Amcrest IP3M-HX2W) 192.168.1.11 R2(IPC-model) To start a session with a camera, use the login credentials > onvif-util -u admin -p admin123 192.168.1.12 found host: 192.168.1.12 successfully connected to host name: AXIS M1065-LW serial: ACCC8E99C915 Get current settings for video > get video Profile set to profile_1_h264 Resolution: 1920 x 1080 Frame Rate: 25 Gov Length: 30 Bit Rate: 4096 Get available video settings > get video options Available Resolutions 1920 x 1080 1280 x 720 640 x 480 320 x 240 Min Gov Length: 1 Max Gov Length: 32767 Min Frame Rate: 1 Max Frame Rate: 30 Min Bit Rate: 1 Max Bit Rate: 2147483647 Set video resolution > set resolution 1280x720 Resolution was set to 1280 x 720 Exit session > quit SEE ALSO There is a GUI version of this program included with the libonvif package which will implement most of the same commands. It may be invoke using the 'onvif-gui' command. Onvif GUI Program ----------------- NAME onvif-gui SYNOPSIS onvif-gui DESCRIPTION GUI program to view and set parameters on onvif compatible IP cameras. Double clidcking the camera name in the list will display the camera video output. Camera parameters are available on the tabs on the lower right side of the application. Once a parameter has been changed, the Apply button will be enabled, which can be used to commit the change to the camera. It may be necessary to re-start the video output stream in order to see the changes. Video: Resolution - The drop down box is used to select the setting. Frame Rate - The number of frames per second in the video output. Gov Length - This is the distance between key frames in the stream. Bitrate - The maxmimum number of bits per second to transmit. Image: All values are set using the sliders Brightness Saturation Contrast Sharpness Network: If the DHCP is enabled, all fields are set by the server, if DHCP is disabled, other network settings may be completed manually. IP Address Subnet Mask Gateway Primary DNS PTZ: Settings pertain to preset selections or current camera position. The arrow keys, Zoom In and Zoom out control the position and zoom. The numbered buttons on the left correspond to preset positions. The blank text box may be used to address presets numbered higher than 5. To set a preset, position the camera, then check Set Preset, then click the numbered preset button. Admin: Camera Name - Sets the application display name of the camera based on the camera mfgr and serial number. Set admin Password - Can be used to change the password for the camera. Sync Time - Will reset the camera's current time without regard to time zone. Browser - Will launch a browser session with the camera for advanced maintenance. Enable Reboot - Will enable the reboot button for use. Enable Reset - Will enable the reset button for use. Use with caution, all camera settings will be reset. Config: Auto Discovery - When checked, the application will automatcally start discovery upon launch, otherwise use the Discover button. Multi Broadcast - When checked will repeat the broadcast message the number of times in the Broadcast Repeate spin box. Common Username - Default username used during discover. Common Password - Default password used during discover. EXAMPLES To change the video resolution of a camera output, Double click on the camera name in the list. The camera video output should display in the viewer. Select the Video tab and use the drop down box labelled Resolution. Upon changing the selection, the Apply button will be enabled. Click the apply button to make the change. The stream will stop and may be re-started by double clicking on the camera name. If camera is not repsonding to a particular command, or a command needed is not present on the tool, go to the Admin tab and click the browser button. This will launch the browser using the camera IP address. Log into the camera and settings should be avialable in native format for the camera configuration. SEE ALSO There is a command line version of this program included with the libonvif package which will implement most of the same commands. It may be invoked using the 'onvif-util' command. NOTES Camera compliance with the onvif standard is often incomplete and in some cases incorrect. Success with the onvif-util may be limited in many cases. Cameras made by Hikvision will have the greatest level of compatibility with onvif-util. Cameras made by Dahua will have a close degree of compaiability with some notable exceptions regarding gateway and DNS settings. Time settings may not be reliable in some cases. If the time is set without the zone flag, the time appearing in the camera feed will be synced to the computer time. If the time zone flag is used, the displayed time may be set to an offset from the computer time based on the timezone setting of the camera. If the camera DNS setting is properly onvif compliant, the IP address may be reliably set. Some cameras may not respond to the DNS setting requested by onvif-gui due to non compliance. Note that the camera may reboot automatically under some conditions if the DNS setting is changed from off to on. Video settings are reliable. The Admin Password setting is reliable, as well as the reboot command. If there is an issue with a particular setting, it is recommended to connect to the camera with a web browser, at most cameras will have a web interface that will allow you to make the changes reliably. The gui version has a button on the Admin tab that will launch the web browser with the camera ip address automatically. License ------- Copyright (c) 2018, 2020, 2022 Stephen Rhodes License: GPL-2+ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ---------- libavio Copyright (c) 2022 Stephen Rhodes License: Apache Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ---------- getopt-win.h (originally getopt.h) Copyright (c) 2002 Todd C. Miller and Copyright (c) 2000 The NetBSD Foundation, Inc. License: BSD-2-Clause-NETBSD Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. . THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------- cencode.h, cencode.c in Public Domain by Chris Venter : chris.venter[anti-spam]gmail.com License: public-domain1 Copyright-Only Dedication (based on United States law) or Public Domain Certification The person or persons who have associated work with this document (the "Dedicator" or "Certifier") hereby either (a) certifies that, to the best of his knowledge, the work of authorship identified is in the public domain of the country from which the work is published, or (b) hereby dedicates whatever copyright the dedicators holds in the work of authorship identified below (the "Work") to the public domain. A certifier, moreover, dedicates any copyright interest he may have in the associated work, and for these purposes, is described as a "dedicator" below. A certifier has taken reasonable steps to verify the copyright status of this work. Certifier recognizes that his good faith efforts may not shield him from liability if in fact the work certified is not in the public domain. Dedicator makes this dedication for the benefit of the public at large and to the detriment of the Dedicator's heirs and successors. Dedicator intends this dedication to be an overt act of relinquishment in perpetuity of all present and future rights under copyright law, whether vested or contingent, in the Work. Dedicator understands that such relinquishment of all rights includes the relinquishment of all rights to enforce (by lawsuit or otherwise) those copyrights in the Work. Dedicator recognizes that, once placed in the public domain, the Work may be freely reproduced, distributed, transmitted, used, modified, built upon, or otherwise exploited by anyone for any purpose, commercial or non-commercial, and in any way, including by methods that have not yet been invented or conceived. ---------- sha1.h, sha1.c in Public Domain by By Steve Reid License: public-domain2 100% Public Domain. ---------- debian folder Copyright: 2022 Petter Reinholdtsen License: GPL-2+ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ---------- docker/Dockerfile Copyright (c) 2022 Vladislav Visarro License: GPL-2+ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. libonvif-1.4.4/COPYING0000644000175000017500000004325414355414521014275 0ustar stephenstephen GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. libonvif-1.4.4/docker/0000755000175000017500000000000014355414521014501 5ustar stephenstephenlibonvif-1.4.4/docker/Dockerfile0000644000175000017500000000326514355414521016501 0ustar stephenstephen#******************************************************************************* # libonvif/docker/Dockerfile # # Copyright (c) 2022 Vladislav Vlsarro # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # #*******************************************************************************/ FROM ubuntu:18.04 # Setup build environment RUN export DEBIAN_FRONTEND=noninteractive; \ export DEBCONF_NONINTERACTIVE_SEEN=true; \ apt-get update -qqy && apt-get install -qqy --option Dpkg::Options::="--force-confnew" --no-install-recommends \ autoconf automake build-essential pkgconf libtool libxml2-dev git ca-certificates wget && \ apt-get --quiet autoremove --yes && \ apt-get --quiet --yes clean && \ rm -rf /var/lib/apt/lists/* # install cmake 3.17 RUN wget -qO- "https://cmake.org/files/v3.17/cmake-3.17.0-Linux-x86_64.tar.gz" | tar --strip-components=1 -xz -C /usr/local COPY . /libonvif RUN cd /libonvif && \ mkdir build && \ cd build && \ cmake .. && \ make && \ make install && \ ldconfig CMD ["/bin/bash"] libonvif-1.4.4/cmake/0000755000175000017500000000000014355557026014322 5ustar stephenstephenlibonvif-1.4.4/cmake/FindLibOnvif.cmake0000644000175000017500000000336114355557026017640 0ustar stephenstephen#******************************************************************************* # FindLibOnvif.cmake # # Copyright (c) 2022 Stephen Rhodes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # #******************************************************************************/ set(win_path "C:/Program Files (x86)") find_path(LIBONVIF_INCLUDE_DIR onvif.h HINTS /usr/local/include /usr/include $ENV{CONDA_PREFIX}/include $ENV{CONDA_PREFIX}/Library/include ${win_path}/libonvif/include ) find_library(LIBONVIF_LIBRARY NAMES onvif HINTS /usr/local/lib /usr/lib/x86_64-linux-gnu/lib $ENV{CONDA_PREFIX}/lib $ENV{CONDA_PREFIX}/Library/lib ${win_path}/libonvif/lib ) set(LIBONVIF_INCLUDE_DIRS ${LIBONVIF_INCLUDE_DIR}) set(LIBONVIF_LIBRARIES ${LIBONVIF_LIBRARY}) set(LIBONVIF_VERSION_STRING 1.4.4) include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibOnvif REQUIRED_VARS LIBONVIF_INCLUDE_DIRS LIBONVIF_LIBRARIES VERSION_VAR LIBONVIF_VERSION_STRING ) mark_as_advanced(LIBONVIF_INCLUDE_DIR LIBONVIF_LIBRARY) libonvif-1.4.4/libonvif/0000755000175000017500000000000014355557026015052 5ustar stephenstephenlibonvif-1.4.4/libonvif/CMakeLists.txt0000644000175000017500000000270114355557026017612 0ustar stephenstephen#******************************************************************************* # libonvif/libonvif/CMakeLists.txt # # Copyright (c) 2020 Stephen Rhodes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # #******************************************************************************/ cmake_minimum_required(VERSION 3.17) project(libonvif VERSION 1.4.4) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) if(WIN32) find_package(Iconv REQUIRED) endif() find_package(LibXml2 REQUIRED) add_library(onvif SHARED src/onvif.c src/cencode.c src/sha1.c ) target_link_libraries(onvif PRIVATE ${LIBXML2_LIBRARIES} ) set_target_properties(onvif PROPERTIES SOVERSION 1 ) target_include_directories(onvif PUBLIC include ${Iconv_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIRS} ) libonvif-1.4.4/libonvif/src/0000755000175000017500000000000014355557026015641 5ustar stephenstephenlibonvif-1.4.4/libonvif/src/onvif.c0000644000175000017500000037276314355557026017150 0ustar stephenstephen/******************************************************************************* * onvif.c * * copyright 2018 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *******************************************************************************/ #include #include #include #include #include #include #include #include "libxml/xpathInternals.h" #ifdef _WIN32 #include #include #include #include #include #else #include #include #include #include #include #include #include #include #include #endif #include "onvif.h" #include "sha1.h" #include "cencode.h" #include #ifdef _WIN32 #pragma comment(lib, "iphlpapi.lib") #pragma comment(lib, "ws2_32.lib") #endif #define INT_TO_ADDR(_addr) \ (_addr & 0xFF), \ (_addr >> 8 & 0xFF), \ (_addr >> 16 & 0xFF), \ (_addr >> 24 & 0xFF) xmlDocPtr sendCommandToCamera(char * cmd, char * xaddrs); void getBase64(unsigned char * buffer, int chunk_size, unsigned char * result); void getUUID(char uuid_buf[47]); void addUsernameDigestHeader(xmlNodePtr root, xmlNsPtr ns_env, char * user, char * password, time_t offset); void addHttpHeader(xmlDocPtr doc, xmlNodePtr root, char * xaddrs, char * post_type, char cmd[], int cmd_length); int checkForXmlErrorMsg(xmlDocPtr doc, char error_msg[1024]); int getXmlValue(xmlDocPtr doc, xmlChar *xpath, char buf[], int buf_length); int getNodeAttributen (xmlDocPtr doc, xmlChar *xpath, xmlChar *attribute, char buf[], int buf_length, int profileIndex); #define getNodeAttribute(doc,xpath,attribute,buf,buf_length) getNodeAttributen(doc,xpath,attribute,buf,buf_length,0) xmlXPathObjectPtr getNodeSet (xmlDocPtr doc, xmlChar *xpath); const int SHA1_DIGEST_SIZE = 20; char preferred_network_address[16]; static bool dump_reply = false; static void dumpReply(xmlDocPtr reply); int getNetworkInterfaces(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_tds, BAD_CAST "GetNetworkInterfaces", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlChar *xpath = BAD_CAST "//s:Body//tds:GetNetworkInterfacesResponse//tds:NetworkInterfaces"; xmlNodeSetPtr nodeset; xmlChar *enabled = NULL; xmlXPathObjectPtr xml_result = getNodeSet(reply, xpath); if (xml_result) { nodeset = xml_result->nodesetval; for (int i=0; inodeNr; i++) { xmlNodePtr cur = nodeset->nodeTab[i]; xmlChar *token = xmlGetProp(cur, BAD_CAST "token"); xmlDocPtr temp_doc = xmlNewDoc(BAD_CAST "1.0"); xmlDocSetRootElement(temp_doc, cur); bool dhcp = false; char isDHCP[128]; xpath = BAD_CAST "//tds:NetworkInterfaces//tt:IPv4//tt:Config//tt:DHCP"; if (getXmlValue(temp_doc, xpath, isDHCP, 128) == 0) { if (strcmp(isDHCP, "true") == 0) { dhcp = true; } onvif_data->dhcp_enabled = dhcp; } xmlChar *xpath_address; xmlChar *xpath_prefix; if (dhcp) { xpath_address = BAD_CAST "//tds:NetworkInterfaces//tt:IPv4//tt:Config//tt:FromDHCP//tt:Address"; xpath_prefix = BAD_CAST "//tds:NetworkInterfaces//tt:IPv4//tt:Config//tt:FromDHCP//tt:PrefixLength"; } else { xpath_address = BAD_CAST "//tds:NetworkInterfaces//tt:IPv4//tt:Config//tt:Manual//tt:Address"; xpath_prefix = BAD_CAST "//tds:NetworkInterfaces//tt:IPv4//tt:Config//tt:Manual//tt:PrefixLength"; } char ip_address_buf[128]; if (getXmlValue(temp_doc, xpath_address, ip_address_buf, 128) == 0) { char host[128] = {0}; extractHost(onvif_data->xaddrs, host); if (strcmp(ip_address_buf, host) == 0) { strcpy(onvif_data->ip_address_buf, ip_address_buf); strcpy(onvif_data->networkInterfaceToken, (char*) token); char prefix_length_buf[128]; if (getXmlValue(temp_doc, xpath_prefix, prefix_length_buf, 128) == 0) { onvif_data->prefix_length = atoi(prefix_length_buf); } xpath = BAD_CAST "//tds:NetworkInterfaces//tt:Info//tt:Name"; getXmlValue(temp_doc, xpath, onvif_data->networkInterfaceName, 128); i = nodeset->nodeNr; } } xmlFreeDoc(temp_doc); } } if (enabled != NULL) { free(enabled); } xmlXPathFreeObject(xml_result); result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int setNetworkInterfaces(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlNsPtr ns_tt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/schema", BAD_CAST "tt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr setNetworkInterfaces = xmlNewTextChild(body, ns_tds, BAD_CAST "SetNetworkInterfaces", NULL); xmlNewTextChild(setNetworkInterfaces, ns_tt, BAD_CAST "InterfaceToken", BAD_CAST onvif_data->networkInterfaceName); xmlNodePtr networkInterface = xmlNewTextChild(setNetworkInterfaces, ns_tt, BAD_CAST "NetworkInterface", NULL); xmlNodePtr ipv4 = xmlNewTextChild(networkInterface, ns_tt, BAD_CAST "IPv4", NULL); if (onvif_data->dhcp_enabled) { xmlNewTextChild(ipv4, ns_tt, BAD_CAST "DHCP", BAD_CAST "true"); } else { xmlNewTextChild(ipv4, ns_tt, BAD_CAST "DHCP", BAD_CAST "false"); xmlNodePtr manual = xmlNewTextChild(ipv4, ns_tt, BAD_CAST "Manual", NULL); xmlNewTextChild(manual, ns_tt, BAD_CAST "Address" , BAD_CAST onvif_data->ip_address_buf); char prefix_length_buf[128]; sprintf(prefix_length_buf, "%d", onvif_data->prefix_length); xmlNewTextChild(manual, ns_tt, BAD_CAST "PrefixLength", BAD_CAST prefix_length_buf); } char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlChar *xpath = BAD_CAST "//s:Body//tds:SetNetworkInterfacesResponse//tds:RebootNeeded"; char rebootNeeded[128]; if (getXmlValue(reply, xpath, rebootNeeded, 128) == 0) { if (strcmp(rebootNeeded, "true") == 0) { rebootCamera(onvif_data); } } result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int getNetworkDefaultGateway(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_tds, BAD_CAST "GetNetworkDefaultGateway", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlChar *xpath = BAD_CAST "//s:Body//tds:GetNetworkDefaultGatewayResponse//tds:NetworkGateway//tt:IPv4Address"; getXmlValue(reply, xpath, onvif_data->default_gateway_buf, 128); result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int setNetworkDefaultGateway(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlNsPtr ns_tt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/schema", BAD_CAST "tt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr setNetworkDefaultGateway = xmlNewTextChild(body, ns_tds, BAD_CAST "SetNetworkDefaultGateway", NULL); xmlNewTextChild(setNetworkDefaultGateway, ns_tt, BAD_CAST "IPv4Address", BAD_CAST onvif_data->default_gateway_buf); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int getDNS(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_tds, BAD_CAST "GetDNS", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlChar *xpath = BAD_CAST "//s:Body//tds:GetDNSResponse//tds:DNSInformation//tt:FromDHCP"; char fromDHCP[128]; if (getXmlValue(reply, xpath, fromDHCP, 128) == 0) { if (strcmp(fromDHCP, "true") == 0) { xpath = BAD_CAST "//s:Body//tds:GetDNSResponse//tds:DNSInformation//tt:DNSFromDHCP//tt:IPv4Address"; if (getXmlValue(reply, xpath, onvif_data->dns_buf, 128) == 0) {} } else { xpath = BAD_CAST "//s:Body//tds:GetDNSResponse//tds:DNSInformation//tt:DNSManual//tt:IPv4Address"; if (getXmlValue(reply, xpath, onvif_data->dns_buf, 128) == 0) {} } } result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int setDNS(struct OnvifData *onvif_data) { int result = 0; char fromDHCP_buf[128]; if (onvif_data->dhcp_enabled) { strcpy(fromDHCP_buf, "true"); } else { strcpy(fromDHCP_buf, "false"); } xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlNsPtr ns_tt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/schema", BAD_CAST "tt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr setDNS = xmlNewTextChild(body, ns_tds, BAD_CAST "SetDNS", NULL); if (!onvif_data->dhcp_enabled) { xmlNodePtr dnsManual = xmlNewTextChild(setDNS, ns_tds, BAD_CAST "DNSManual", NULL); xmlNewTextChild(dnsManual, ns_tt, BAD_CAST "Type", BAD_CAST "IPv4"); xmlNewTextChild(dnsManual, ns_tt, BAD_CAST "IPv4Address", BAD_CAST onvif_data->dns_buf); } else { xmlNewTextChild(setDNS, ns_tds, BAD_CAST "FromDHCP", BAD_CAST fromDHCP_buf); } char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int getNTP(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_tds, BAD_CAST "GetNTP", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlChar *xpath = BAD_CAST "//s:Body//tds:GetNTPResponse//tt:FromDHCP"; char ntp_buf[128]; getXmlValue(reply, xpath, ntp_buf, 128); if (strcmp(ntp_buf,"true") == 0) { onvif_data->ntp_dhcp = true; xpath = BAD_CAST "//s:Body//tds:GetNTPResponse//tt:NTPFromDHCP//tt:Type"; getXmlValue(reply, xpath, onvif_data->ntp_type, 128); if (strcmp(onvif_data->ntp_type,"IPv4") == 0) xpath = BAD_CAST "//s:Body//tds:GetNTPResponse//tt:NTPFromDHCP//tt:IPv4Address"; else if (strcmp(onvif_data->ntp_type,"IPv4") == 0) xpath = BAD_CAST "//s:Body//tds:GetNTPResponse//tt:NTPFromDHCP//tt:IPv6Address"; else xpath = BAD_CAST "//s:Body//tds:GetNTPResponse//tt:NTPFromDHCP//tt:DNSname"; getXmlValue(reply, xpath, onvif_data->ntp_addr, 128); } else { onvif_data->ntp_dhcp = false; xpath = BAD_CAST "//s:Body//tds:GetNTPResponse//tt:NTPManual//tt:Type"; getXmlValue(reply, xpath, onvif_data->ntp_type, 128); if (strcmp(onvif_data->ntp_type,"IPv4") == 0) xpath = BAD_CAST "//s:Body//tds:GetNTPResponse//tt:NTPManual//tt:IPv4Address"; else if (strcmp(onvif_data->ntp_type,"IPv4") == 0) xpath = BAD_CAST "//s:Body//tds:GetNTPResponse//tt:NTPManual//tt:IPv6Address"; else xpath = BAD_CAST "//s:Body//tds:GetNTPResponse//tt:NTPManual//tt:DNSname"; getXmlValue(reply, xpath, onvif_data->ntp_addr, 128); } } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int setNTP(struct OnvifData *onvif_data) { int result = 0; char fromDHCP_buf[128]; if (onvif_data->ntp_dhcp) { strcpy(fromDHCP_buf, "true"); } else { strcpy(fromDHCP_buf, "false"); } xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlNsPtr ns_tt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/schema", BAD_CAST "tt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr setNTP = xmlNewTextChild(body, ns_tds, BAD_CAST "SetNTP", NULL); xmlNewTextChild(setNTP, ns_tds, BAD_CAST "FromDHCP", BAD_CAST fromDHCP_buf); if (!onvif_data->ntp_dhcp) { xmlNodePtr ntpManual = xmlNewTextChild(setNTP, ns_tds, BAD_CAST "NTPManual", NULL); xmlNewTextChild(ntpManual, ns_tt, BAD_CAST "Type", BAD_CAST onvif_data->ntp_type); if (strcmp(onvif_data->ntp_type,"IPv4") == 0) xmlNewTextChild(ntpManual, ns_tt, BAD_CAST "IPv4Address", BAD_CAST onvif_data->ntp_addr); else if (strcmp(onvif_data->ntp_type,"IPv6") == 0) xmlNewTextChild(ntpManual, ns_tt, BAD_CAST "IPv6Address", BAD_CAST onvif_data->ntp_addr); else xmlNewTextChild(ntpManual, ns_tt, BAD_CAST "DNSName", BAD_CAST onvif_data->ntp_addr); } char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int getHostname(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_tds, BAD_CAST "GetHostname", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlChar *xpath = BAD_CAST "//s:Body//tds:GetHostnameResponse//tds:HostnameInformation//tt:FromDHCP"; xpath = BAD_CAST "//s:Body//tds:GetHostnameResponse//tds:HostnameInformation//tt:Name"; getXmlValue(reply, xpath, onvif_data->host_name, 128); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int setHostname(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlNsPtr ns_tt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/schema", BAD_CAST "tt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); if (onvif_data->host_name[0]) { xmlNodePtr setHostname = xmlNewTextChild(body, ns_tds, BAD_CAST "SetHostname", NULL); xmlNewTextChild(setHostname, ns_tds, BAD_CAST "Name", BAD_CAST onvif_data->host_name); /* Do I also need to set FromDHCP to false ? */ } else { xmlNodePtr setHostname = xmlNewTextChild(body, ns_tds, BAD_CAST "SetHostnameFromDHCP", NULL); xmlNewTextChild(setHostname, ns_tds, BAD_CAST "FromDHCP", BAD_CAST "true"); } char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { /* Should check for RebootNeeded=true from setHostnameFronDHCP */ result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int getCapabilities(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr capabilities = xmlNewTextChild(body, ns_tds, BAD_CAST "GetCapabilities", NULL); xmlNewTextChild(capabilities, ns_tds, BAD_CAST "Category", BAD_CAST "All"); char cmd[4096] = {0}; strcpy(onvif_data->device_service, onvif_data->xaddrs); extractOnvifService(onvif_data->device_service, true); addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlChar *xpath; xpath = BAD_CAST "//s:Body//tds:GetCapabilitiesResponse//tds:Capabilities//tt:Events//tt:XAddr"; strcpy(onvif_data->event_service, ""); if (getXmlValue(reply, xpath, onvif_data->event_service, 128) == 0) { extractOnvifService(onvif_data->event_service, true); } xpath = BAD_CAST "//s:Body//tds:GetCapabilitiesResponse//tds:Capabilities//tt:Imaging//tt:XAddr"; strcpy(onvif_data->imaging_service, ""); if (getXmlValue(reply, xpath, onvif_data->imaging_service, 128) == 0) extractOnvifService(onvif_data->imaging_service, true); xpath = BAD_CAST "//s:Body//tds:GetCapabilitiesResponse//tds:Capabilities//tt:Media//tt:XAddr"; strcpy(onvif_data->media_service, ""); if (getXmlValue(reply, xpath, onvif_data->media_service, 128) == 0) extractOnvifService(onvif_data->media_service, true); xpath = BAD_CAST "//s:Body//tds:GetCapabilitiesResponse//tds:Capabilities//tt:PTZ//tt:XAddr"; strcpy(onvif_data->ptz_service, ""); if (getXmlValue(reply, xpath, onvif_data->ptz_service, 128) == 0) extractOnvifService(onvif_data->ptz_service, true); result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int getVideoEncoderConfigurationOptions(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_trt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/media/wsdl", BAD_CAST "trt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr getVideoEncoderConfigurationOptions = xmlNewTextChild(body, ns_trt, BAD_CAST "GetVideoEncoderConfigurationOptions", NULL); if (onvif_data->videoEncoderConfigurationToken[0]) xmlNewTextChild(getVideoEncoderConfigurationOptions, ns_trt, BAD_CAST "ConfigurationToken", BAD_CAST onvif_data->videoEncoderConfigurationToken); if (onvif_data->profileToken[0]) xmlNewTextChild(getVideoEncoderConfigurationOptions, ns_trt, BAD_CAST "ProfileToken", BAD_CAST onvif_data->profileToken); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->media_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlChar *width = NULL; xmlChar *height = NULL; xmlChar *xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationOptionsResponse//trt:Options//tt:H264//tt:ResolutionsAvailable"; xmlNodeSetPtr nodeset; xmlXPathObjectPtr xml_result = getNodeSet(reply, xpath); int k = 0; if (xml_result) { nodeset = xml_result->nodesetval; for (int i=0; inodeNr; i++) { xmlNodePtr cur = nodeset->nodeTab[i]->children; while(cur != NULL) { if ((!xmlStrcmp(cur->name, (const xmlChar *) "Width"))) { width = xmlNodeListGetString(reply, cur->xmlChildrenNode, 1); } else if ((!xmlStrcmp(cur->name, (const xmlChar *) "Height"))) { height = xmlNodeListGetString(reply, cur->xmlChildrenNode, 1); } cur = cur->next; } char tmp[128] = {0}; if ((strlen((char *)width) + strlen((char *)height)) > 124) { fprintf(stderr, "xmlNodeListString return buffer overflow %zu\n", strlen((char *)width) + strlen((char *)height)); } else { sprintf(tmp, "%s x %s", width, height); } int size = 0; bool found_size = false; while (!found_size) { if (strlen(onvif_data->resolutions_buf[size]) == 0) { found_size = true; } else { size++; if (size > 15) found_size = true; } } bool duplicate = false; for (int n=0; nresolutions_buf[n], tmp) == 0) { duplicate = true; } } if (!duplicate) { strcpy(onvif_data->resolutions_buf[size], tmp); k++; } if (width != NULL) free(width); if (height != NULL) free(height); } xmlXPathFreeObject(xml_result); } char temp_buf[128]; xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationOptionsResponse//trt:Options//tt:H264//tt:GovLengthRange//tt:Min"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->gov_length_min = atoi(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationOptionsResponse//trt:Options//tt:H264//tt:GovLengthRange//tt:Max"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->gov_length_max = atoi(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationOptionsResponse//trt:Options//tt:H264//tt:FrameRateRange//tt:Min"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->frame_rate_min = atoi(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationOptionsResponse//trt:Options//tt:H264//tt:FrameRateRange//tt:Max"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->frame_rate_max = atoi(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationOptionsResponse//trt:Options//tt:Extension//tt:H264//tt:BitrateRange//tt:Min"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->bitrate_min = atoi(temp_buf); else onvif_data->bitrate_min = 128; xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationOptionsResponse//trt:Options//tt:Extension//tt:H264//tt:BitrateRange//tt:Max"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->bitrate_max = atoi(temp_buf); else onvif_data->bitrate_max = 16384; result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int getVideoEncoderConfiguration(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_trt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/media/wsdl", BAD_CAST "trt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr getVideoEncoderConfiguration = xmlNewTextChild(body, ns_trt, BAD_CAST "GetVideoEncoderConfiguration", NULL); xmlNewTextChild(getVideoEncoderConfiguration, ns_trt, BAD_CAST "ConfigurationToken", BAD_CAST onvif_data->videoEncoderConfigurationToken); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->media_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlChar *xpath; char temp_buf[128] = {0}; xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:Name"; getXmlValue(reply, xpath, onvif_data->video_encoder_name_buf, 128); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:UseCount"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->use_count = atoi(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:GuaranteedFrameRate"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) { if (strcmp(temp_buf, "true") == 0) onvif_data->guaranteed_frame_rate = true; else onvif_data->guaranteed_frame_rate = false; } xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:Encoding"; getXmlValue(reply, xpath, onvif_data->encoding, 128); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:Resolution//tt:Width"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->conf_width = atof(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:Resolution//tt:Height"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->conf_height = atof(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:Quality"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->quality = atof(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:RateControl//tt:FrameRateLimit"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->conf_frame_rate_limit = atoi(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:RateControl//tt:EncodingInterval"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->conf_encoding_interval = atoi(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:RateControl//tt:BitrateLimit"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->conf_bitrate_limit = atoi(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:H264//tt:H264Profile"; getXmlValue(reply, xpath, onvif_data->h264_profile_buf, 128); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:Multicast//tt:Address//tt:Type"; getXmlValue(reply, xpath, onvif_data->multicast_address_type_buf, 128); if (strcmp(onvif_data->multicast_address_type_buf,"IPv6") == 0) xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:Multicast//tt:Address//tt:IPv6Address"; else xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:Multicast//tt:Address//tt:IPv4Address"; getXmlValue(reply, xpath, onvif_data->multicast_address_buf, 128); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:Multicast//tt:Port"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->multicast_port = atoi(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:Multicast//tt:TTL"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->multicast_ttl = atoi(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:Multicast//tt:AutoStart"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) { if (strcmp(temp_buf, "true") == 0) onvif_data->autostart = true; else onvif_data->autostart = false; } xpath = BAD_CAST "//s:Body//trt:GetVideoEncoderConfigurationResponse//trt:Configuration//tt:SessionTimeout"; getXmlValue(reply, xpath, onvif_data->session_time_out_buf, 128); result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int setVideoEncoderConfiguration(struct OnvifData *onvif_data) { int result = 0; char frame_rate_buf[128] = {0}; char gov_length_buf[128] = {0}; char bitrate_buf[128] = {0}; char width_buf[128] = {0}; char height_buf[128] = {0}; char use_count_buf[128] = {0}; char quality_buf[128] = {0}; char multicast_port_buf[128] = {0}; char multicast_ttl_buf[128] = {0}; char autostart_buf[128] = {0}; char encoding_interval_buf[128] = {0}; sprintf(frame_rate_buf, "%d", onvif_data->frame_rate); sprintf(gov_length_buf, "%d", onvif_data->gov_length); sprintf(bitrate_buf, "%d", onvif_data->bitrate); sprintf(use_count_buf, "%d", onvif_data->use_count); sprintf(width_buf, "%d", onvif_data->width); sprintf(height_buf, "%d", onvif_data->height); sprintf(quality_buf, "%f", onvif_data->quality); sprintf(multicast_port_buf, "%d", onvif_data->multicast_port); sprintf(multicast_ttl_buf, "%d", onvif_data->multicast_ttl); if (onvif_data->autostart) strcpy(autostart_buf, "true"); else strcpy(autostart_buf, "false"); sprintf(encoding_interval_buf, "%d", onvif_data->conf_encoding_interval); xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_trt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/media/wsdl", BAD_CAST "trt"); xmlNsPtr ns_tt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/schema", BAD_CAST "tt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr setVideoEncoderConfiguration = xmlNewTextChild(body, ns_trt, BAD_CAST "SetVideoEncoderConfiguration", NULL); xmlNodePtr configuration = xmlNewTextChild(setVideoEncoderConfiguration, ns_trt, BAD_CAST "Configuration", NULL); xmlNewProp(configuration, BAD_CAST "token", BAD_CAST onvif_data->videoEncoderConfigurationToken); xmlNewTextChild(configuration, ns_tt, BAD_CAST "Name", BAD_CAST onvif_data->video_encoder_name_buf); xmlNewTextChild(configuration, ns_tt, BAD_CAST "UseCount", BAD_CAST use_count_buf); #ifdef ONVIF19060 /* Sad, but not supported until 19.06 release - crashes my older camera */ xmlNewTextChild(configuration, ns_tt, BAD_CAST "GuaranteedFrameRate", onvif_data->guaranteed_frame_rate?BAD_CAST "true":BAD_CAST "false"); #endif xmlNewTextChild(configuration, ns_tt, BAD_CAST "Encoding", onvif_data->encoding[0]?BAD_CAST onvif_data->encoding:BAD_CAST "H264"); xmlNodePtr resolution = xmlNewTextChild(configuration, ns_tt, BAD_CAST "Resolution", NULL); xmlNewTextChild(resolution, ns_tt, BAD_CAST "Width", BAD_CAST width_buf); xmlNewTextChild(resolution, ns_tt, BAD_CAST "Height", BAD_CAST height_buf); xmlNewTextChild(configuration, ns_tt, BAD_CAST "Quality", BAD_CAST quality_buf); xmlNodePtr rateControl = xmlNewTextChild(configuration, ns_tt, BAD_CAST "RateControl", NULL); xmlNewTextChild(rateControl, ns_tt, BAD_CAST "FrameRateLimit", BAD_CAST frame_rate_buf); xmlNewTextChild(rateControl, ns_tt, BAD_CAST "EncodingInterval", BAD_CAST encoding_interval_buf); xmlNewTextChild(rateControl, ns_tt, BAD_CAST "BitrateLimit", BAD_CAST bitrate_buf); xmlNodePtr h264 = xmlNewTextChild(configuration, ns_tt, BAD_CAST "H264", NULL); xmlNewTextChild(h264, ns_tt, BAD_CAST "GovLength", BAD_CAST gov_length_buf); xmlNewTextChild(h264, ns_tt, BAD_CAST "H264Profile", BAD_CAST onvif_data->h264_profile_buf); xmlNodePtr multicast = xmlNewTextChild(configuration, ns_tt, BAD_CAST "Multicast", NULL); xmlNodePtr address = xmlNewTextChild(multicast, ns_tt, BAD_CAST "Address", NULL); xmlNewTextChild(address, ns_tt, BAD_CAST "Type", BAD_CAST onvif_data->multicast_address_type_buf); xmlNewTextChild(address, ns_tt, BAD_CAST "IPv4Address", BAD_CAST onvif_data->multicast_address_buf); xmlNewTextChild(multicast, ns_tt, BAD_CAST "Port", BAD_CAST multicast_port_buf); xmlNewTextChild(multicast, ns_tt, BAD_CAST "TTL", BAD_CAST multicast_ttl_buf); xmlNewTextChild(multicast, ns_tt, BAD_CAST "AutoStart", BAD_CAST autostart_buf); xmlNewTextChild(configuration, ns_tt, BAD_CAST "SessionTimeout", BAD_CAST onvif_data->session_time_out_buf); xmlNewTextChild(setVideoEncoderConfiguration, ns_trt, BAD_CAST "ForcePersistence", BAD_CAST "true"); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->media_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int getProfile(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_trt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/media/wsdl", BAD_CAST "trt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr getProfile = xmlNewTextChild(body, ns_trt, BAD_CAST "GetProfile", NULL); xmlNewTextChild(getProfile, ns_trt, BAD_CAST "ProfileToken", BAD_CAST onvif_data->profileToken); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->media_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { char temp_buf[128]; xmlChar *xpath; xpath = BAD_CAST "//s:Body//trt:GetProfileResponse//trt:Profile//tt:VideoEncoderConfiguration//tt:Resolution//tt:Width"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->width = atoi(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetProfileResponse//trt:Profile//tt:VideoEncoderConfiguration//tt:Resolution//tt:Height"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->height = atoi(temp_buf); xpath = BAD_CAST "//s:Body//trt:GetProfileResponse//trt:Profile//tt:VideoEncoderConfiguration//tt:RateControl//tt:FrameRateLimit"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) { onvif_data->frame_rate = atoi(temp_buf); } xpath = BAD_CAST "//s:Body//trt:GetProfileResponse//trt:Profile//tt:VideoEncoderConfiguration//tt:RateControl//tt:BitrateLimit"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) { onvif_data->bitrate = atoi(temp_buf); } else { onvif_data->bitrate = 0; } xpath = BAD_CAST "//s:Body//trt:GetProfileResponse//trt:Profile//tt:VideoEncoderConfiguration//tt:H264//tt:GovLength"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) { onvif_data->gov_length = atoi(temp_buf); } xpath = BAD_CAST "//s:Body//trt:GetProfileResponse//trt:Profile//tt:VideoEncoderConfiguration"; getNodeAttribute(reply, xpath, BAD_CAST "token", onvif_data->videoEncoderConfigurationToken, 128); xpath = BAD_CAST "//s:Body//trt:GetProfileResponse//trt:Profile//tt:VideoSourceConfiguration//tt:SourceToken"; getXmlValue(reply, xpath, onvif_data->videoSourceConfigurationToken, 128); result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int getOptions(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_timg = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver20/imaging/wsdl", BAD_CAST "timg"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr getOptions = xmlNewTextChild(body, ns_timg, BAD_CAST "GetOptions", NULL); xmlNewTextChild(getOptions, ns_timg, BAD_CAST "VideoSourceToken", BAD_CAST onvif_data->videoSourceConfigurationToken); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->imaging_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlChar *xpath; char temp_buf[128]; xpath = BAD_CAST "//s:Body//timg:GetOptionsResponse//timg:ImagingOptions//tt:Brightness//tt:Min"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->brightness_min = atoi(temp_buf); xpath = BAD_CAST "//s:Body//timg:GetOptionsResponse//timg:ImagingOptions//tt:Brightness//tt:Max"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->brightness_max = atoi(temp_buf); xpath = BAD_CAST "//s:Body//timg:GetOptionsResponse//timg:ImagingOptions//tt:ColorSaturation//tt:Min"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->saturation_min = atoi(temp_buf); xpath = BAD_CAST "//s:Body//timg:GetOptionsResponse//timg:ImagingOptions//tt:ColorSaturation//tt:Max"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->saturation_max = atoi(temp_buf); xpath = BAD_CAST "//s:Body//timg:GetOptionsResponse//timg:ImagingOptions//tt:Contrast//tt:Min"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->contrast_min = atoi(temp_buf); xpath = BAD_CAST "//s:Body//timg:GetOptionsResponse//timg:ImagingOptions//tt:Contrast//tt:Max"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->contrast_max = atoi(temp_buf); xpath = BAD_CAST "//s:Body//timg:GetOptionsResponse//timg:ImagingOptions//tt:Sharpness//tt:Min"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->sharpness_min = atoi(temp_buf); xpath = BAD_CAST "//s:Body//timg:GetOptionsResponse//timg:ImagingOptions//tt:Sharpness//tt:Max"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->sharpness_max = atoi(temp_buf); result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int getImagingSettings(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_timg = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver20/imaging/wsdl", BAD_CAST "timg"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr getImagingSettings = xmlNewTextChild(body, ns_timg, BAD_CAST "GetImagingSettings", NULL); xmlNewTextChild(getImagingSettings, ns_timg, BAD_CAST "VideoSourceToken", BAD_CAST onvif_data->videoSourceConfigurationToken); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->imaging_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlChar *xpath; char temp_buf[128]; xpath = BAD_CAST "//s:Body//timg:GetImagingSettingsResponse//timg:ImagingSettings//tt:Brightness"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->brightness = atoi(temp_buf); xpath = BAD_CAST "//s:Body//timg:GetImagingSettingsResponse//timg:ImagingSettings//tt:ColorSaturation"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->saturation = atoi(temp_buf); xpath = BAD_CAST "//s:Body//timg:GetImagingSettingsResponse//timg:ImagingSettings//tt:Contrast"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->contrast = atoi(temp_buf); xpath = BAD_CAST "//s:Body//timg:GetImagingSettingsResponse//timg:ImagingSettings//tt:Sharpness"; if (getXmlValue(reply, xpath, temp_buf, 128) == 0) onvif_data->sharpness = atoi(temp_buf); result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int setImagingSettings(struct OnvifData *onvif_data) { int result = 0; char brightness_buf[128] = {0}; char saturation_buf[128] = {0}; char contrast_buf[128] = {0}; char sharpness_buf[128] = {0}; sprintf(brightness_buf, "%d", onvif_data->brightness); sprintf(saturation_buf, "%d", onvif_data->saturation); sprintf(contrast_buf, "%d", onvif_data->contrast); sprintf(sharpness_buf, "%d", onvif_data->sharpness); xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_timg = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver20/imaging/wsdl", BAD_CAST "timg"); xmlNsPtr ns_tt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/schema", BAD_CAST "tt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr setImagingSettings = xmlNewTextChild(body, ns_timg, BAD_CAST "SetImagingSettings", NULL); xmlNewTextChild(setImagingSettings, ns_timg, BAD_CAST "VideoSourceToken", BAD_CAST onvif_data->videoSourceConfigurationToken); xmlNodePtr imagingSettings = xmlNewTextChild(setImagingSettings, ns_timg, BAD_CAST "ImagingSettings", NULL); xmlNewTextChild(imagingSettings, ns_tt, BAD_CAST "Brightness", BAD_CAST brightness_buf); xmlNewTextChild(imagingSettings, ns_tt, BAD_CAST "ColorSaturation", BAD_CAST saturation_buf); xmlNewTextChild(imagingSettings, ns_tt, BAD_CAST "Contrast", BAD_CAST contrast_buf); xmlNewTextChild(imagingSettings, ns_tt, BAD_CAST "Sharpness", BAD_CAST sharpness_buf); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->imaging_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int continuousMove(float x, float y, float z, struct OnvifData *onvif_data) { int result = 0; char pan_tilt_string[128] = {0}; char zoom_string[128] = {0}; sprintf(pan_tilt_string, "PanTilt x=\"%.*f\" y=\"%.*f\"", 2, x, 2, y); sprintf(zoom_string, "Zoom x=\"%.*f\"", 2, z); xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_ptz = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver20/ptz/wsdl", BAD_CAST "ptz"); xmlNsPtr ns_tt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/schema", BAD_CAST "tt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr continuousMove = xmlNewTextChild(body, ns_ptz, BAD_CAST "ContinuousMove", NULL); xmlNewTextChild(continuousMove, ns_ptz, BAD_CAST "ProfileToken", BAD_CAST onvif_data->profileToken); xmlNodePtr velocity = xmlNewTextChild(continuousMove, ns_ptz, BAD_CAST "Velocity", BAD_CAST NULL); xmlNewTextChild(velocity, ns_tt, BAD_CAST pan_tilt_string, NULL); xmlNewTextChild(velocity, ns_tt, BAD_CAST zoom_string, NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->ptz_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int moveStop(int type, struct OnvifData *onvif_data) { int result = 0; char pan_tilt_flag[128] = {0}; char zoom_flag[128] = {0}; if (type == PAN_TILT_STOP) { strcpy(pan_tilt_flag, "true"); strcpy(zoom_flag, "false"); } else if (type == ZOOM_STOP) { strcpy(pan_tilt_flag, "false"); strcpy(zoom_flag, "true"); } xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_ptz = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver20/ptz/wsdl", BAD_CAST "ptz"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr stop = xmlNewTextChild(body, ns_ptz, BAD_CAST "Stop", NULL); xmlNewTextChild(stop, ns_ptz, BAD_CAST "ProfileToken", BAD_CAST onvif_data->profileToken); xmlNewTextChild(stop, ns_ptz, BAD_CAST "PanTilt", BAD_CAST pan_tilt_flag); xmlNewTextChild(stop, ns_ptz, BAD_CAST "Zoom", BAD_CAST zoom_flag); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->ptz_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int setPreset(char *arg, struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_ptz = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver20/ptz/wsdl", BAD_CAST "ptz"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr setPreset = xmlNewTextChild(body, ns_ptz, BAD_CAST "SetPreset", NULL); xmlNewTextChild(setPreset, ns_ptz, BAD_CAST "ProfileToken", BAD_CAST onvif_data->profileToken); xmlNewTextChild(setPreset, ns_ptz, BAD_CAST "PresetToken", BAD_CAST arg); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->ptz_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int gotoPreset(char *arg, struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_ptz = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver20/ptz/wsdl", BAD_CAST "ptz"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr gotoPreset = xmlNewTextChild(body, ns_ptz, BAD_CAST "GotoPreset", NULL); xmlNewTextChild(gotoPreset, ns_ptz, BAD_CAST "ProfileToken", BAD_CAST onvif_data->profileToken); xmlNewTextChild(gotoPreset, ns_ptz, BAD_CAST "PresetToken", BAD_CAST arg); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->ptz_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int setUser(char *new_password, struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlNsPtr ns_tt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/schema", BAD_CAST "tt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr setUser = xmlNewTextChild(body, ns_tds, BAD_CAST "SetUser", NULL); xmlNodePtr user = xmlNewTextChild(setUser, ns_tds, BAD_CAST "User", NULL); xmlNewTextChild(user, ns_tt, BAD_CAST "Username", BAD_CAST "admin"); xmlNewTextChild(user, ns_tt, BAD_CAST "Password", BAD_CAST new_password); xmlNewTextChild(user, ns_tt, BAD_CAST "UserLevel", BAD_CAST "Administrator"); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int setSystemDateAndTime(struct OnvifData *onvif_data) { int result = 0; time_t rawtime; time(&rawtime); struct tm *UTCTime = localtime(&rawtime); char dst_flag_buf[128]; if (UTCTime->tm_isdst == 1) strcpy(dst_flag_buf, "true"); else strcpy(dst_flag_buf, "false"); char hour_buf[128]; char minute_buf[128]; char second_buf[128]; char year_buf[128]; char month_buf[128]; char day_buf[128]; sprintf(hour_buf, "%d", UTCTime->tm_hour); sprintf(minute_buf, "%d", UTCTime->tm_min); sprintf(second_buf, "%d", UTCTime->tm_sec); sprintf(year_buf, "%d", UTCTime->tm_year + 1900); sprintf(month_buf, "%d", UTCTime->tm_mon + 1); sprintf(day_buf, "%d", UTCTime->tm_mday); xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlNsPtr ns_tt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/schema", BAD_CAST "tt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr setSystemDateAndTime = xmlNewTextChild(body, ns_tds, BAD_CAST "SetSystemDateAndTime", NULL); xmlNewTextChild(setSystemDateAndTime, ns_tds, BAD_CAST "DateTimeType", BAD_CAST "Manual"); xmlNewTextChild(setSystemDateAndTime, ns_tds, BAD_CAST "DaylightSavings", BAD_CAST dst_flag_buf); xmlNodePtr timeZone = xmlNewTextChild(setSystemDateAndTime, ns_tds, BAD_CAST "TimeZone", NULL); xmlNewTextChild(timeZone, ns_tt, BAD_CAST "TZ", BAD_CAST "UTC0"); xmlNodePtr utcDateTime = xmlNewTextChild(setSystemDateAndTime, ns_tds, BAD_CAST "UTCDateTime", NULL); xmlNodePtr cameraTime = xmlNewTextChild(utcDateTime, ns_tt, BAD_CAST "Time", NULL); xmlNewTextChild(cameraTime, ns_tt, BAD_CAST "Hour", BAD_CAST hour_buf); xmlNewTextChild(cameraTime, ns_tt, BAD_CAST "Minute", BAD_CAST minute_buf); xmlNewTextChild(cameraTime, ns_tt, BAD_CAST "Second", BAD_CAST second_buf); xmlNodePtr cameraDate = xmlNewTextChild(utcDateTime, ns_tt, BAD_CAST "Date", NULL); xmlNewTextChild(cameraDate, ns_tt, BAD_CAST "Year", BAD_CAST year_buf); xmlNewTextChild(cameraDate, ns_tt, BAD_CAST "Month", BAD_CAST month_buf); xmlNewTextChild(cameraDate, ns_tt, BAD_CAST "Day", BAD_CAST day_buf); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int setSystemDateAndTimeUsingTimezone(struct OnvifData *onvif_data) { int result = 0; time_t rawtime; time(&rawtime); bool special = false; struct tm *UTCTime = localtime(&rawtime); char dst_flag_buf[128]; if (UTCTime->tm_isdst == 1) strcpy(dst_flag_buf, "true"); else strcpy(dst_flag_buf, "false"); if (strcmp(onvif_data->timezone,"UTC0") == 0) { special = true; } else { if (!onvif_data->timezone[0]) { #ifndef _WIN32 // work out a timezone to use on the camera int h = -(UTCTime->tm_gmtoff/3600); int m = (UTCTime->tm_gmtoff + 3600 * h)/60; if (m) sprintf(onvif_data->timezone,"%s%d:%02d:00%s",tzname[0],h,m,tzname[1]); else sprintf(onvif_data->timezone,"%s%d%s",tzname[0],h,tzname[1]); #else int h = _timezone/3600; int m = (_timezone - 3600 * h)/60; if (m) sprintf(onvif_data->timezone,"%s%d:%02d:00%s",_tzname[0],h,m,_tzname[1]); else sprintf(onvif_data->timezone,"%s%d%s",_tzname[0],h,_tzname[1]); #endif } UTCTime = gmtime(&rawtime); } if (!onvif_data->datetimetype) onvif_data->datetimetype = 'M'; // manual char hour_buf[128]; char minute_buf[128]; char second_buf[128]; char year_buf[128]; char month_buf[128]; char day_buf[128]; sprintf(hour_buf, "%d", UTCTime->tm_hour); sprintf(minute_buf, "%d", UTCTime->tm_min); sprintf(second_buf, "%d", UTCTime->tm_sec); sprintf(year_buf, "%d", UTCTime->tm_year + 1900); sprintf(month_buf, "%d", UTCTime->tm_mon + 1); sprintf(day_buf, "%d", UTCTime->tm_mday); xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlNsPtr ns_tt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/schema", BAD_CAST "tt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr setSystemDateAndTime = xmlNewTextChild(body, ns_tds, BAD_CAST "SetSystemDateAndTime", NULL); xmlNewTextChild(setSystemDateAndTime, ns_tds, BAD_CAST "DateTimeType", BAD_CAST "Manual"); xmlNewTextChild(setSystemDateAndTime, ns_tds, BAD_CAST "DaylightSavings", BAD_CAST dst_flag_buf); xmlNodePtr timeZone = xmlNewTextChild(setSystemDateAndTime, ns_tds, BAD_CAST "TimeZone", NULL); xmlNewTextChild(timeZone, ns_tt, BAD_CAST "TZ", BAD_CAST onvif_data->timezone); xmlNodePtr utcDateTime = xmlNewTextChild(setSystemDateAndTime, ns_tds, BAD_CAST "UTCDateTime", NULL); xmlNodePtr cameraTime = xmlNewTextChild(utcDateTime, ns_tt, BAD_CAST "Time", NULL); xmlNewTextChild(cameraTime, ns_tt, BAD_CAST "Hour", BAD_CAST hour_buf); xmlNewTextChild(cameraTime, ns_tt, BAD_CAST "Minute", BAD_CAST minute_buf); xmlNewTextChild(cameraTime, ns_tt, BAD_CAST "Second", BAD_CAST second_buf); xmlNodePtr cameraDate = xmlNewTextChild(utcDateTime, ns_tt, BAD_CAST "Date", NULL); xmlNewTextChild(cameraDate, ns_tt, BAD_CAST "Year", BAD_CAST year_buf); xmlNewTextChild(cameraDate, ns_tt, BAD_CAST "Month", BAD_CAST month_buf); xmlNewTextChild(cameraDate, ns_tt, BAD_CAST "Day", BAD_CAST day_buf); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); if (result == 0 && onvif_data->datetimetype == 'N') { // switch back to NTP after we have nudged it to correct time_t newtime; time(&newtime); if (newtime != rawtime) { // save a little effort if we are within a second of the previous check if (special) UTCTime = localtime(&newtime); else UTCTime = gmtime(&newtime); sprintf(hour_buf, "%d", UTCTime->tm_hour); sprintf(minute_buf, "%d", UTCTime->tm_min); sprintf(second_buf, "%d", UTCTime->tm_sec); sprintf(year_buf, "%d", UTCTime->tm_year + 1900); sprintf(month_buf, "%d", UTCTime->tm_mon + 1); sprintf(day_buf, "%d", UTCTime->tm_mday); } doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlNsPtr ns_tt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/schema", BAD_CAST "tt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr setSystemDateAndTime = xmlNewTextChild(body, ns_tds, BAD_CAST "SetSystemDateAndTime", NULL); xmlNewTextChild(setSystemDateAndTime, ns_tds, BAD_CAST "DateTimeType", BAD_CAST "NTP"); xmlNewTextChild(setSystemDateAndTime, ns_tds, BAD_CAST "DaylightSavings", BAD_CAST dst_flag_buf); xmlNodePtr timeZone = xmlNewTextChild(setSystemDateAndTime, ns_tds, BAD_CAST "TimeZone", NULL); xmlNewTextChild(timeZone, ns_tt, BAD_CAST "TZ", BAD_CAST onvif_data->timezone); // Need to include date/time even though the specs say it should be ignored xmlNodePtr utcDateTime = xmlNewTextChild(setSystemDateAndTime, ns_tds, BAD_CAST "UTCDateTime", NULL); xmlNodePtr cameraTime = xmlNewTextChild(utcDateTime, ns_tt, BAD_CAST "Time", NULL); xmlNewTextChild(cameraTime, ns_tt, BAD_CAST "Hour", BAD_CAST hour_buf); xmlNewTextChild(cameraTime, ns_tt, BAD_CAST "Minute", BAD_CAST minute_buf); xmlNewTextChild(cameraTime, ns_tt, BAD_CAST "Second", BAD_CAST second_buf); xmlNodePtr cameraDate = xmlNewTextChild(utcDateTime, ns_tt, BAD_CAST "Date", NULL); xmlNewTextChild(cameraDate, ns_tt, BAD_CAST "Year", BAD_CAST year_buf); xmlNewTextChild(cameraDate, ns_tt, BAD_CAST "Month", BAD_CAST month_buf); xmlNewTextChild(cameraDate, ns_tt, BAD_CAST "Day", BAD_CAST day_buf); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } } } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int getProfileToken(struct OnvifData *onvif_data, int profileIndex) { int result; onvif_data->profileToken[0] = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_trt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/media/wsdl", BAD_CAST "trt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_trt, BAD_CAST "GetProfiles", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->media_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { getNodeAttributen(reply, BAD_CAST "//s:Body//trt:GetProfilesResponse//trt:Profiles", BAD_CAST "token", onvif_data->profileToken, 128, profileIndex); result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int getTimeOffset(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlSetNs(root, ns_env); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_tds, BAD_CAST "GetSystemDateAndTime", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { char hour_buf[16]; char min_buf[16]; char sec_buf[16]; char year_buf[16]; char month_buf[16]; char day_buf[16]; char dst_buf[16]; getXmlValue(reply, BAD_CAST "//s:Body//tds:GetSystemDateAndTimeResponse//tds:SystemDateAndTime//tt:UTCDateTime//tt:Time//tt:Hour", hour_buf, 16); getXmlValue(reply, BAD_CAST "//s:Body//tds:GetSystemDateAndTimeResponse//tds:SystemDateAndTime//tt:UTCDateTime//tt:Time//tt:Minute", min_buf, 16); getXmlValue(reply, BAD_CAST "//s:Body//tds:GetSystemDateAndTimeResponse//tds:SystemDateAndTime//tt:UTCDateTime//tt:Time//tt:Second", sec_buf, 16); getXmlValue(reply, BAD_CAST "//s:Body//tds:GetSystemDateAndTimeResponse//tds:SystemDateAndTime//tt:UTCDateTime//tt:Date//tt:Year", year_buf, 16); getXmlValue(reply, BAD_CAST "//s:Body//tds:GetSystemDateAndTimeResponse//tds:SystemDateAndTime//tt:UTCDateTime//tt:Date//tt:Month", month_buf, 16); getXmlValue(reply, BAD_CAST "//s:Body//tds:GetSystemDateAndTimeResponse//tds:SystemDateAndTime//tt:UTCDateTime//tt:Date//tt:Day", day_buf, 16); getXmlValue(reply, BAD_CAST "//s:Body//tds:GetSystemDateAndTimeResponse//tds:SystemDateAndTime//tt:DaylightSavings", dst_buf, 16); onvif_data->dst = false; int is_dst = 0; if (strcmp(dst_buf, "true") == 0) { is_dst = 1; onvif_data->dst = true; } getXmlValue(reply, BAD_CAST "//s:Body//tds:GetSystemDateAndTimeResponse//tds:SystemDateAndTime//tt:TimeZone//tt:TZ", onvif_data->timezone, 128); char dttype[16]; getXmlValue(reply, BAD_CAST "//s:Body//tds:GetSystemDateAndTimeResponse//tds:SystemDateAndTime//tt:DateTimeType", dttype, 16); onvif_data->datetimetype = dttype[0]; /* M == Manual, N == NTP */ time_t now = time(NULL); time_t utc_time_here = now; bool special = false; if (strcmp(onvif_data->timezone,"UTC0") == 0) { /* special case - camera is running on local time believing it is UTC */ special = true; struct tm *utc_here = gmtime(&now); utc_here->tm_isdst = -1; utc_time_here = mktime(utc_here); } struct tm *utc_there = localtime(&now); utc_there->tm_year = atoi(year_buf) - 1900; utc_there->tm_mon = atoi(month_buf) - 1; utc_there->tm_mday = atoi(day_buf); utc_there->tm_hour = atoi(hour_buf); utc_there->tm_min = atoi(min_buf); utc_there->tm_sec = atoi(sec_buf); utc_there->tm_isdst = is_dst; time_t utc_time_there; if (special) utc_time_there = mktime(utc_there); else #ifndef _WIN32 utc_time_there = timegm(utc_there); #else utc_time_there = _mkgmtime(utc_there); #endif onvif_data->time_offset = utc_time_there - utc_time_here; result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int getStreamUri(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_trt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/media/wsdl", BAD_CAST "trt"); xmlNsPtr ns_tt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/schema", BAD_CAST "tt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr getStreamUri = xmlNewTextChild(body, ns_trt, BAD_CAST "GetStreamUri", NULL); xmlNodePtr streamSetup = xmlNewTextChild(getStreamUri, ns_trt, BAD_CAST "StreamSetup", NULL); xmlNewTextChild(streamSetup, ns_tt, BAD_CAST "Stream", BAD_CAST "RTP-Unicast"); xmlNodePtr transport = xmlNewTextChild(streamSetup, ns_tt, BAD_CAST "Transport", NULL); xmlNewTextChild(transport, ns_tt, BAD_CAST "Protocol", BAD_CAST "RTSP"); xmlNewTextChild(getStreamUri, ns_trt, BAD_CAST "ProfileToken", BAD_CAST onvif_data->profileToken); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->media_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { getXmlValue(reply, BAD_CAST "//s:Body//trt:GetStreamUriResponse//trt:MediaUri//tt:Uri", onvif_data->stream_uri, 1024); result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int getDeviceInformation(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_tds, BAD_CAST "GetDeviceInformation", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { getXmlValue(reply, BAD_CAST "//s:Body//tds:GetDeviceInformationResponse//tds:SerialNumber", onvif_data->serial_number, 128); result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } void getDiscoveryXml2(char buffer[], int buf_size) { char *xml_string = "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probeuuid:6bbdae2d-f229-42c8-a27b-93880fb80826http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymousurn:schemas-xmlsoap-org:ws:2005:04:discoverydp0:Device"; strcpy(buffer, xml_string); } void getDiscoveryXml(char buffer[], int buf_size, char uuid[47]) { for (int i=0; iusername, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_tds, BAD_CAST "SystemReboot", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } int hardReset(struct OnvifData *onvif_data) { int result = 0; xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNodePtr setSystemFactoryDefault = xmlNewTextChild(body, ns_tds, BAD_CAST "SetSystemFactoryDefault", NULL); xmlNewTextChild(setSystemFactoryDefault, ns_tds, BAD_CAST "FactoryDefault", BAD_CAST "Hard"); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { result = checkForXmlErrorMsg(reply, onvif_data->last_error); xmlFreeDoc(reply); } else { result = -1; strcpy(onvif_data->last_error, "No XML reply"); } return result; } void saveSystemDateAndTime(char *filename, struct OnvifData *onvif_data) { xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_tds, BAD_CAST "GetSystemDateAndTime", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlSaveFormatFile(filename, reply, 1); xmlFreeDoc(reply); } } void saveScopes(char *filename, struct OnvifData *onvif_data) { xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_tds, BAD_CAST "GetScopes", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlSaveFormatFile(filename, reply, 1); xmlFreeDoc(reply); } } void saveDeviceInformation(char *filename, struct OnvifData *onvif_data) { xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_tds, BAD_CAST "GetDeviceInformation", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlSaveFormatFile(filename, reply, 1); xmlFreeDoc(reply); } } void saveCapabilities(char *filename, struct OnvifData *onvif_data) { xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_tds = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/device/wsdl", BAD_CAST "tds"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_tds, BAD_CAST "GetCapabilities", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->device_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlSaveFormatFile(filename, reply, 1); xmlFreeDoc(reply); } } void saveProfiles(char *filename, struct OnvifData *onvif_data) { xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_trt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/media/wsdl", BAD_CAST "trt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_trt, BAD_CAST "GetProfiles", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->media_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlSaveFormatFile(filename, reply, 1); xmlFreeDoc(reply); } } void saveServiceCapabilities(char *filename, struct OnvifData *onvif_data) { xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); xmlNodePtr root = xmlNewDocNode(doc, NULL, BAD_CAST "Envelope", NULL); xmlDocSetRootElement(doc, root); xmlNsPtr ns_env = xmlNewNs(root, BAD_CAST "http://www.w3.org/2003/05/soap-envelope", BAD_CAST "SOAP-ENV"); xmlNsPtr ns_trt = xmlNewNs(root, BAD_CAST "http://www.onvif.org/ver10/media/wsdl", BAD_CAST "trt"); xmlSetNs(root, ns_env); addUsernameDigestHeader(root, ns_env, onvif_data->username, onvif_data->password, onvif_data->time_offset); xmlNodePtr body = xmlNewTextChild(root, ns_env, BAD_CAST "Body", NULL); xmlNewTextChild(body, ns_trt, BAD_CAST "GetServiceCapabilities", NULL); char cmd[4096] = {0}; addHttpHeader(doc, root, onvif_data->xaddrs, onvif_data->media_service, cmd, 4096); xmlDocPtr reply = sendCommandToCamera(cmd, onvif_data->xaddrs); if (reply != NULL) { xmlSaveFormatFile(filename, reply, 1); xmlFreeDoc(reply); } } int getXmlValue(xmlDocPtr doc, xmlChar *xpath, char buf[], int buf_length) { xmlChar *keyword = NULL; xmlXPathContextPtr context = xmlXPathNewContext(doc); if (context == NULL) { return -1; } xmlXPathRegisterNs(context, BAD_CAST "s", BAD_CAST "http://www.w3.org/2003/05/soap-envelope"); xmlXPathRegisterNs(context, BAD_CAST "trt", BAD_CAST "http://www.onvif.org/ver10/media/wsdl"); xmlXPathRegisterNs(context, BAD_CAST "tt", BAD_CAST "http://www.onvif.org/ver10/schema"); xmlXPathRegisterNs(context, BAD_CAST "tds", BAD_CAST "http://www.onvif.org/ver10/device/wsdl"); xmlXPathRegisterNs(context, BAD_CAST "timg", BAD_CAST "http://www.onvif.org/ver20/imaging/wsdl"); xmlXPathRegisterNs(context, BAD_CAST "wsa5", BAD_CAST "http://www.w3.org/2005/08/addressing"); xmlXPathRegisterNs(context, BAD_CAST "wsnt", BAD_CAST "http://docs.oasis-open.org/wsn/b-2"); xmlXPathRegisterNs(context, BAD_CAST "d", BAD_CAST "http://schemas.xmlsoap.org/ws/2005/04/discovery"); xmlXPathRegisterNs(context, BAD_CAST "ter", BAD_CAST "http://www.onvif.org/ver10/error"); xmlXPathRegisterNs(context, BAD_CAST "a", BAD_CAST "http://schemas.xmlsoap.org/ws/2004/08/addressing"); xmlXPathObjectPtr result = xmlXPathEvalExpression(xpath, context); xmlXPathFreeContext(context); if (result == NULL) { return -2; } if (xmlXPathNodeSetIsEmpty(result->nodesetval)) { if ((strcmp((char*) xpath, "//s:Body//s:Fault//s:Code//s:Subcode//s:Value") != 0) && (strcmp((char*) xpath, "//s:Body//s:Fault//s:Reason//s:Text") != 0)) { } return -3; } if (result) { keyword = xmlNodeListGetString(doc, result->nodesetval->nodeTab[0]->xmlChildrenNode, 1); if (keyword != NULL) { if (strlen((char*) keyword) > buf_length-1) { xmlXPathFreeObject(result); free(keyword); return -4; } else { for (int i=0; inodesetval)) { return -3; } if (result) { if( profileIndex >= result->nodesetval->nodeNr ) return -5; keyword = xmlGetProp(result->nodesetval->nodeTab[profileIndex], attribute); if (keyword != NULL) { if (strlen((char*) keyword) > buf_length-1) { xmlXPathFreeObject(result); free(keyword); return -4; } else { for (int i=0; inodesetval)){ xmlXPathFreeObject(result); return NULL; } return result; } xmlDocPtr sendCommandToCamera(char *cmd, char *xaddrs) { int sock = 0, valread, flags; const int buffer_size = 4096; struct sockaddr_in serv_addr; char buffer[4096] = {0}; char tmp[128] = {0}; char *mark = strstr(xaddrs, "//"); int start = mark-xaddrs+2; int tmp_len = strlen(xaddrs); int j; for (j=0; j 1024) return NULL; char http_header[1024]; for (i=0; i 1024) return NULL; char str_xml_length[1024]; for (i=length_start; i 65536) return NULL; char xml_reply[65536]; for (i=0; i 8191) { fprintf(stderr, "xmlOutputBufferGetSize too big %d\n", size); strncat(xml, (char*)xmlOutputBufferGetContent(outputbuffer), 8191); } else { strcpy(xml, (char*)xmlOutputBufferGetContent(outputbuffer)); } xmlOutputBufferFlush(outputbuffer); xmlOutputBufferClose(outputbuffer); xmlFreeDoc(doc); char c_xml_size[6]; sprintf(c_xml_size, "%d", size); int xml_size_length = strlen(c_xml_size)+1; char tmp[128] = {0}; char * mark = strstr(xaddrs, "//"); int start = mark-xaddrs+2; int tmp_len = strlen(xaddrs); int j; for (j=0; jpreferred_network_address); struct sockaddr_in broadcast_address; int broadcast_socket; char broadcast_message[1024] = {0}; unsigned int address_size; int error_code; if (onvif_session->discovery_msg_id == 1) getDiscoveryXml(broadcast_message, 1024, onvif_session->uuid); else if (onvif_session->discovery_msg_id == 2) getDiscoveryXml2(broadcast_message, 1024); int broadcast_message_length = strlen(broadcast_message); broadcast_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); setSocketOptions(broadcast_socket); for (int k=0; k<128; k++) { for (int j=0; j<8192; j++) { onvif_session->buf[k][j] = '\0'; } } memset((char *) &broadcast_address, 0, sizeof(broadcast_address)); broadcast_address.sin_family = AF_INET; broadcast_address.sin_port = htons(3702); broadcast_address.sin_addr.s_addr = inet_addr("239.255.255.250"); int status = sendto(broadcast_socket, broadcast_message, broadcast_message_length, 0, (struct sockaddr*)&broadcast_address, sizeof(broadcast_address)); if (status < 0) { //error } int i = 0; unsigned char looping = 1; address_size = sizeof(broadcast_address); while(looping) { onvif_session->len[i] = recvfrom(broadcast_socket, onvif_session->buf[i], sizeof(onvif_session->buf[i]), 0, (struct sockaddr*) &broadcast_address, &address_size); if (onvif_session->len[i] > 0) { onvif_session->buf[i][onvif_session->len[i]] = '\0'; i++; } else { looping = 0; if (onvif_session->len[i] < 0) { //error } } } #ifdef _WIN32 closesocket(broadcast_socket); #else close(broadcast_socket); #endif return i; } void getIPAddress(char buf[128]) { #ifdef _WIN32 PMIB_IPADDRTABLE pIPAddrTable; DWORD dwSize = 0; DWORD dwRetVal = 0; IN_ADDR IPAddr; pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof(MIB_IPADDRTABLE)); if (pIPAddrTable) { if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { free(pIPAddrTable); pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize); } if (pIPAddrTable == NULL) { return; } } if ((dwRetVal = GetIpAddrTable(pIPAddrTable, &dwSize, 0)) != NO_ERROR) { return; } int p = 0; while (p < (int)pIPAddrTable->dwNumEntries) { if (pIPAddrTable->table[p].dwAddr != inet_addr("127.0.0.1") && pIPAddrTable->table[p].dwMask == inet_addr("255.255.255.0")) { IPAddr.S_un.S_addr = (u_long)pIPAddrTable->table[p].dwAddr; strcpy(buf, inet_ntoa(IPAddr)); p = (int)pIPAddrTable->dwNumEntries; } p++; } if (pIPAddrTable) { free(pIPAddrTable); pIPAddrTable = NULL; } #else #if defined(__APPLE__) || defined(__FreeBSD__) char *address; struct ifaddrs *interfaces = NULL; struct ifaddrs *temp_addr = NULL; int success = 0; success = getifaddrs(&interfaces); if (success == 0) { temp_addr = interfaces; while (temp_addr != NULL) { address = inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr); if (strcmp(address, "127.0.0.1") != 0) strcpy(buf, address); } temp_addr = temp_addr->ifa_next; } freeifaddrs(interfaces); #else struct ifconf ifc; struct ifreq ifr[10]; int sd, ifc_num, addr,mask, i; sd = socket(PF_INET, SOCK_DGRAM, 0); if (sd > 0) { ifc.ifc_len = sizeof(ifr); ifc.ifc_ifcu.ifcu_buf = (caddr_t)ifr; if (ioctl(sd, SIOCGIFCONF, &ifc) == 0) { ifc_num = ifc.ifc_len / sizeof(struct ifreq); for (i = 0; i < ifc_num; ++i) { if (ifr[i].ifr_addr.sa_family != AF_INET) { continue; } if (ioctl(sd, SIOCGIFNETMASK, &ifr[i]) == 0) { mask = ((struct sockaddr_in *)(&ifr[i].ifr_netmask))->sin_addr.s_addr; char mask_buf[128]; sprintf(mask_buf, "%d.%d.%d.%d", INT_TO_ADDR(mask)); if (strcmp(mask_buf, "255.255.255.0") == 0) { if (ioctl(sd, SIOCGIFADDR, &ifr[i]) == 0) { addr = ((struct sockaddr_in *)(&ifr[i].ifr_addr))->sin_addr.s_addr; char addr_buf[128]; sprintf(addr_buf, "%d.%d.%d.%d", INT_TO_ADDR(addr)); if (strcmp(addr_buf, "127.0.0.1") != 0) { printf("-----------------------------------------------%s\n", addr_buf); strcpy(buf, addr_buf); } } } } } } } close(sd); #endif /* not __APPLE__ || __FreeBSD__ */ #endif /* not _WIN32 */ } int mask2prefix(char *mask_buf) { struct in_addr mask; inet_pton(AF_INET, mask_buf, &mask); uint32_t number = ntohl(mask.s_addr); int count = 0; unsigned int step = 0; while (number > 0) { if (number & 1) { step = 1; count++; } else { if (step) { return -1; } } number >>=1; } return count; } void prefix2mask(int prefix, char mask_buf[128]) { struct in_addr mask; uint32_t number; if (prefix) { number = htonl(~((1 << (32-prefix)) - 1)); } else { number = htonl(0); } mask.s_addr = number; inet_ntop(AF_INET, &mask, mask_buf, 128); } int setSocketOptions(int socket) { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = 500000; int broadcast = 500; char loopch = 0; int status = 0; struct in_addr localInterface; #ifdef _WIN32 PMIB_IPADDRTABLE pIPAddrTable; DWORD dwSize = 0; DWORD dwRetVal = 0; IN_ADDR IPAddr; pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof(MIB_IPADDRTABLE)); if (pIPAddrTable) { if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { free(pIPAddrTable); pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize); } if (pIPAddrTable == NULL) { printf("Memory allocation failed for GetIpAddrTable\n"); return -1; } } if ((dwRetVal = GetIpAddrTable(pIPAddrTable, &dwSize, 0)) != NO_ERROR) { printf("GetIpAddrTable failed with error %d\n", dwRetVal); return -1; } int p = 0; while (p < (int)pIPAddrTable->dwNumEntries) { IPAddr.S_un.S_addr = (u_long)pIPAddrTable->table[p].dwAddr; IPAddr.S_un.S_addr = (u_long)pIPAddrTable->table[p].dwMask; if (pIPAddrTable->table[p].dwAddr != inet_addr("127.0.0.1") && pIPAddrTable->table[p].dwMask == inet_addr("255.255.255.0")) { if (strlen(preferred_network_address) > 0) { localInterface.s_addr = inet_addr(preferred_network_address); //printf("using preferred network address for broadcast: %s\n", preferred_network_address); } else { localInterface.s_addr = pIPAddrTable->table[p].dwAddr; //printf("using default network address for broadcast\n"); } status = setsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&localInterface, sizeof(localInterface)); if (status < 0) printf("ip_multicast_if error"); p = (int)pIPAddrTable->dwNumEntries; } p++; } if (pIPAddrTable) { free(pIPAddrTable); pIPAddrTable = NULL; } status = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char *)&broadcast, sizeof(broadcast)); #else if (strlen(preferred_network_address) > 0) { printf("preferred network address: %s\n", preferred_network_address); localInterface.s_addr = inet_addr(preferred_network_address); status = setsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&localInterface, sizeof(localInterface)); if (status < 0) printf("ip_multicast_if error"); } status = setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval)); #endif status = setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loopch, sizeof(loopch)); return 0; } #ifdef __MINGW32__ int inet_pton(int af, const char *src, void *dst) { struct sockaddr_storage ss; int size = sizeof(ss); char src_copy[INET6_ADDRSTRLEN+1]; ZeroMemory(&ss, sizeof(ss)); strncpy (src_copy, src, INET6_ADDRSTRLEN+1); src_copy[INET6_ADDRSTRLEN] = 0; if (WSAStringToAddress(src_copy, af, NULL, (struct sockaddr *)&ss, &size) == 0) { switch(af) { case AF_INET: *(struct in_addr *)dst = ((struct sockaddr_in *)&ss)->sin_addr; return 1; case AF_INET6: *(struct in6_addr *)dst = ((struct sockaddr_in6 *)&ss)->sin6_addr; return 1; } } return 0; } const char *inet_ntop(int af, const void *src, char *dst, socklen_t size) { struct sockaddr_storage ss; unsigned long s = size; ZeroMemory(&ss, sizeof(ss)); ss.ss_family = af; switch(af) { case AF_INET: ((struct sockaddr_in *)&ss)->sin_addr = *(struct in_addr *)src; break; case AF_INET6: ((struct sockaddr_in6 *)&ss)->sin6_addr = *(struct in6_addr *)src; break; default: return NULL; } return (WSAAddressToString((struct sockaddr *)&ss, sizeof(ss), NULL, dst, &s) == 0)?dst : NULL; } #endif void extractOnvifService(char service[1024], bool post) { int length = strlen(service); char *sub = strstr(service, "//"); if (sub != NULL) { int mark = sub - service; mark = mark+2; int i; for (i=0; ibuf[ordinal], onvif_session->len[ordinal]); for(int i=0; i<1024; i++) onvif_data->camera_name[i] = '\0'; char scopes[8192]; getXmlValue(xml_input, BAD_CAST "//s:Body//d:ProbeMatches//d:ProbeMatch//d:Scopes", scopes, 8192); char temp_mfgr[1024] = {0}; char temp_hdwr[1024] = {0}; getScopeField(scopes, "onvif://www.onvif.org/name/", temp_mfgr); getScopeField(scopes, "onvif://www.onvif.org/hardware/", temp_hdwr); if (strlen(temp_mfgr) > 0) { strcat(onvif_data->camera_name, temp_mfgr); } if (strlen(temp_hdwr) > 0) { if (strstr(temp_mfgr, temp_hdwr) == NULL) { strcat(onvif_data->camera_name, " "); strcat(onvif_data->camera_name, temp_hdwr); } } if (strlen(onvif_data->camera_name) == 0) strcpy(onvif_data->camera_name, "UNKNOWN CAMERA"); xmlFreeDoc(xml_input); } void extractXAddrs(int ordinal, struct OnvifSession *onvif_session, struct OnvifData *onvif_data) { xmlDocPtr xml_input = xmlParseMemory(onvif_session->buf[ordinal], onvif_session->len[ordinal]); if (getXmlValue(xml_input, BAD_CAST "//s:Body//d:ProbeMatches//d:ProbeMatch//d:XAddrs", onvif_data->xaddrs, 1024) == 0) { char *sub = strstr(onvif_data->xaddrs, " "); if (sub != NULL) { int mark = sub - onvif_data->xaddrs; onvif_data->xaddrs[mark] = '\0'; } strcpy(onvif_data->device_service, onvif_data->xaddrs); } xmlFreeDoc(xml_input); } void clearData(struct OnvifData *onvif_data) { for (int i=0; i<16; i++) { for (int j=0; j<128; j++) { onvif_data->resolutions_buf[i][j] = '\0'; } } for (int i=0; i<128; i++) { onvif_data->videoEncoderConfigurationToken[i] = '\0'; onvif_data->networkInterfaceToken[i] = '\0'; onvif_data->networkInterfaceName[i] = '\0'; onvif_data->ip_address_buf[i] = '\0'; onvif_data->default_gateway_buf[i] = '\0'; onvif_data->dns_buf[i] = '\0'; onvif_data->videoSourceConfigurationToken[i] = '\0'; onvif_data->video_encoder_name_buf[i] = '\0'; onvif_data->h264_profile_buf[i] = '\0'; onvif_data->multicast_address_type_buf[i] = '\0'; onvif_data->multicast_address_buf[i] = '\0'; onvif_data->session_time_out_buf[i] = '\0'; onvif_data->media_service[i] = '\0'; onvif_data->imaging_service[i] = '\0'; onvif_data->ptz_service[i] = '\0'; onvif_data->event_service[i] = '\0'; onvif_data->profileToken[i] = '\0'; onvif_data->username[i] = '\0'; onvif_data->password[i] = '\0'; onvif_data->encoding[i] = '\0'; onvif_data->timezone[i] = '\0'; onvif_data->ntp_type[i] = '\0'; onvif_data->ntp_addr[i] = '\0'; } for (int i=0; i<1024; i++) { onvif_data->xaddrs[i] = '\0'; onvif_data->device_service[i] = '\0'; onvif_data->stream_uri[i] = '\0'; onvif_data->camera_name[i] = '\0'; onvif_data->host_name[i] = '\0'; } onvif_data->gov_length_min = 0; onvif_data->gov_length_max = 0; onvif_data->frame_rate_min = 0; onvif_data->frame_rate_max = 0; onvif_data->bitrate_min = 0; onvif_data->bitrate_max = 0; onvif_data->width = 0; onvif_data->height = 0; onvif_data->gov_length = 0; onvif_data->frame_rate = 0; onvif_data->bitrate = 0; onvif_data->use_count = 0; onvif_data->quality = 0; onvif_data->multicast_port = 0; onvif_data->multicast_ttl = 0; onvif_data->autostart = false; onvif_data->prefix_length = 0; onvif_data->dhcp_enabled = false; onvif_data->brightness_min = 0; onvif_data->brightness_max = 0; onvif_data->saturation_min = 0; onvif_data->saturation_max = 0; onvif_data->contrast_min = 0; onvif_data->contrast_max = 0; onvif_data->sharpness_min = 0; onvif_data->sharpness_max = 0; onvif_data->brightness = 0; onvif_data->saturation = 0; onvif_data->contrast = 0; onvif_data->sharpness = 0; onvif_data->time_offset = 0; onvif_data->event_listen_port = 0; onvif_data->guaranteed_frame_rate = false; onvif_data->conf_width = 0; onvif_data->conf_height = 0; onvif_data->conf_frame_rate_limit = 0; onvif_data->conf_encoding_interval = 0; onvif_data->conf_bitrate_limit = 0; onvif_data->datetimetype = '\0'; onvif_data->dst = false; onvif_data->ntp_dhcp = false; } void initializeSession(struct OnvifSession *onvif_session) { getUUID(onvif_session->uuid); onvif_session->discovery_msg_id = 1; xmlInitParser (); #ifdef _WIN32 WSADATA wsaData; WSAStartup(MAKEWORD(2,2), &wsaData); #endif strcpy(preferred_network_address, onvif_session->preferred_network_address); } void closeSession(struct OnvifSession *onvif_session) { #ifdef _WIN32 WSACleanup(); #endif xmlCleanupParser (); } void prepareOnvifData(int ordinal, struct OnvifSession *onvif_session, struct OnvifData *onvif_data) { clearData(onvif_data); getCameraName(ordinal, onvif_session, onvif_data); extractXAddrs(ordinal, onvif_session, onvif_data); extractOnvifService(onvif_data->device_service, true); getTimeOffset(onvif_data); } int fillRTSPn(struct OnvifData *onvif_data, int profileIndex) { int result = 0; result = getCapabilities(onvif_data); if (result == 0) { result = getProfileToken(onvif_data, profileIndex); if (result == 0) { result = getStreamUri(onvif_data); } } return result; } void dumpXmlNode (xmlDocPtr doc, xmlNodePtr cur_node, char *prefix) { const char *name; const char *value; char new_prefix[1024]; char attr[128]; xmlAttrPtr prop; /* Traverse the tree */ for (; cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { name = (char *)(cur_node->name); value = (const char *)xmlNodeListGetString(doc, cur_node->xmlChildrenNode, 1); if (value) { printf("%s%s=%s\n", prefix ? prefix : "", name, value); } else { sprintf(new_prefix, "%s%s.", prefix ? prefix : "", name); for (prop = cur_node->properties; prop; prop = prop->next) { if (prop->children && prop->children->content) { printf("%s%s=%s\n", new_prefix, prop->name, prop->children->content); } } } } dumpXmlNode(doc, cur_node->children, new_prefix); } } /* Dump xml document */ void dumpReply(xmlDocPtr reply) { if (reply != NULL) { xmlChar *xpath = BAD_CAST "//s:Body/*"; xmlXPathObjectPtr body = getNodeSet(reply, xpath); if (body) { xmlNodeSetPtr nodeset = body->nodesetval; for (int i=0; inodeNr; i++) { xmlNodePtr cur = nodeset->nodeTab[i]; /* Skip error return */ if (strcmp((char *)cur->name, "Fault") != 0) { printf("[%s]\n", cur->name); dumpXmlNode(reply, cur->children, NULL); } } } } } /* Dump all available onvif device configuration */ void dumpConfigAll (struct OnvifData *onvif_data) { xmlDocPtr reply; dump_reply = true; getNetworkInterfaces(onvif_data); getNetworkDefaultGateway(onvif_data); getDNS(onvif_data); getCapabilities(onvif_data); getVideoEncoderConfigurationOptions(onvif_data); getVideoEncoderConfiguration(onvif_data); getProfile(onvif_data); getOptions(onvif_data); getImagingSettings(onvif_data); getFirstProfileToken(onvif_data); getTimeOffset(onvif_data); getNTP(onvif_data); getHostname(onvif_data); getStreamUri(onvif_data); getDeviceInformation(onvif_data); dump_reply = false; } libonvif-1.4.4/libonvif/src/sha1.c0000644000175000017500000002232714355414521016637 0ustar stephenstephen/* SHA-1 in C By Steve Reid 100% Public Domain The person or persons who have associated work with this document (the "Dedicator" or "Certifier") hereby either (a) certifies that, to the best of his knowledge, the work of authorship identified is in the public domain of the country from which the work is published, or (b) hereby dedicates whatever copyright the dedicators holds in the work of authorship identified below (the "Work") to the public domain. A certifier, moreover, dedicates any copyright interest he may have in the associated work, and for these purposes, is described as a "dedicator" below. A certifier has taken reasonable steps to verify the copyright status of this work. Certifier recognizes that his good faith efforts may not shield him from liability if in fact the work certified is not in the public domain. Dedicator makes this dedication for the benefit of the public at large and to the detriment of the Dedicator's heirs and successors. Dedicator intends this dedication to be an overt act of relinquishment in perpetuity of all present and future rights under copyright law, whether vested or contingent, in the Work. Dedicator understands that such relinquishment of all rights includes the relinquishment of all rights to enforce (by lawsuit or otherwise) those copyrights in the Work. Dedicator recognizes that, once placed in the public domain, the Work may be freely reproduced, distributed, transmitted, used, modified, built upon, or otherwise exploited by anyone for any purpose, commercial or non-commercial, and in any way, including by methods that have not yet been invented or conceived. CC0 for Public Domain Dedication This tool is based on United States law and may not be applicable outside the US. For dedicating new works to the public domain, we recommend CC0. Test Vectors (from FIPS PUB 180-1) "abc" A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 A million repetitions of "a" 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ /* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ /* #define SHA1HANDSOFF * Copies data before messing with it. */ #define SHA1HANDSOFF #include #include /* for uint32_t */ #include #include "sha1.h" #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #if BYTE_ORDER == LITTLE_ENDIAN #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ |(rol(block->l[i],8)&0x00FF00FF)) #elif BYTE_ORDER == BIG_ENDIAN #define blk0(i) block->l[i] #else #error "Endianness not defined!" #endif #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); /* Hash a single 512-bit block. This is the core of the algorithm. */ void SHA1Transform( uint32_t state[5], const unsigned char buffer[64] ) { uint32_t a, b, c, d, e; typedef union { unsigned char c[64]; uint32_t l[16]; } CHAR64LONG16; #ifdef SHA1HANDSOFF CHAR64LONG16 block[1]; /* use array to appear as a pointer */ memcpy(block, buffer, 64); #else /* The following had better never be used because it causes the * pointer-to-const buffer to be cast into a pointer to non-const. * And the result is written through. I threw a "const" in, hoping * this will cause a diagnostic. */ CHAR64LONG16 *block = (const CHAR64LONG16 *) buffer; #endif /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a, b, c, d, e, 0); R0(e, a, b, c, d, 1); R0(d, e, a, b, c, 2); R0(c, d, e, a, b, 3); R0(b, c, d, e, a, 4); R0(a, b, c, d, e, 5); R0(e, a, b, c, d, 6); R0(d, e, a, b, c, 7); R0(c, d, e, a, b, 8); R0(b, c, d, e, a, 9); R0(a, b, c, d, e, 10); R0(e, a, b, c, d, 11); R0(d, e, a, b, c, 12); R0(c, d, e, a, b, 13); R0(b, c, d, e, a, 14); R0(a, b, c, d, e, 15); R1(e, a, b, c, d, 16); R1(d, e, a, b, c, 17); R1(c, d, e, a, b, 18); R1(b, c, d, e, a, 19); R2(a, b, c, d, e, 20); R2(e, a, b, c, d, 21); R2(d, e, a, b, c, 22); R2(c, d, e, a, b, 23); R2(b, c, d, e, a, 24); R2(a, b, c, d, e, 25); R2(e, a, b, c, d, 26); R2(d, e, a, b, c, 27); R2(c, d, e, a, b, 28); R2(b, c, d, e, a, 29); R2(a, b, c, d, e, 30); R2(e, a, b, c, d, 31); R2(d, e, a, b, c, 32); R2(c, d, e, a, b, 33); R2(b, c, d, e, a, 34); R2(a, b, c, d, e, 35); R2(e, a, b, c, d, 36); R2(d, e, a, b, c, 37); R2(c, d, e, a, b, 38); R2(b, c, d, e, a, 39); R3(a, b, c, d, e, 40); R3(e, a, b, c, d, 41); R3(d, e, a, b, c, 42); R3(c, d, e, a, b, 43); R3(b, c, d, e, a, 44); R3(a, b, c, d, e, 45); R3(e, a, b, c, d, 46); R3(d, e, a, b, c, 47); R3(c, d, e, a, b, 48); R3(b, c, d, e, a, 49); R3(a, b, c, d, e, 50); R3(e, a, b, c, d, 51); R3(d, e, a, b, c, 52); R3(c, d, e, a, b, 53); R3(b, c, d, e, a, 54); R3(a, b, c, d, e, 55); R3(e, a, b, c, d, 56); R3(d, e, a, b, c, 57); R3(c, d, e, a, b, 58); R3(b, c, d, e, a, 59); R4(a, b, c, d, e, 60); R4(e, a, b, c, d, 61); R4(d, e, a, b, c, 62); R4(c, d, e, a, b, 63); R4(b, c, d, e, a, 64); R4(a, b, c, d, e, 65); R4(e, a, b, c, d, 66); R4(d, e, a, b, c, 67); R4(c, d, e, a, b, 68); R4(b, c, d, e, a, 69); R4(a, b, c, d, e, 70); R4(e, a, b, c, d, 71); R4(d, e, a, b, c, 72); R4(c, d, e, a, b, 73); R4(b, c, d, e, a, 74); R4(a, b, c, d, e, 75); R4(e, a, b, c, d, 76); R4(d, e, a, b, c, 77); R4(c, d, e, a, b, 78); R4(b, c, d, e, a, 79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; #ifdef SHA1HANDSOFF memset(block, '\0', sizeof(block)); #endif } /* SHA1Init - Initialize new context */ void SHA1Init( SHA1_CTX * context ) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ void SHA1Update( SHA1_CTX * context, const unsigned char *data, uint32_t len ) { uint32_t i; uint32_t j; j = context->count[0]; if ((context->count[0] += len << 3) < j) context->count[1]++; context->count[1] += (len >> 29); j = (j >> 3) & 63; if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64 - j)); SHA1Transform(context->state, context->buffer); for (; i + 63 < len; i += 64) { SHA1Transform(context->state, &data[i]); } j = 0; } else i = 0; memcpy(&context->buffer[j], &data[i], len - i); } /* Add padding and return the message digest. */ void SHA1Final( unsigned char digest[20], SHA1_CTX * context ) { unsigned i; unsigned char finalcount[8]; unsigned char c; #if 0 /* untested "improvement" by DHR */ /* Convert context->count to a sequence of bytes * in finalcount. Second element first, but * big-endian order within element. * But we do it all backwards. */ unsigned char *fcp = &finalcount[8]; for (i = 0; i < 2; i++) { uint32_t t = context->count[i]; int j; for (j = 0; j < 4; t >>= 8, j++) *--fcp = (unsigned char) t} #else for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ } #endif c = 0200; SHA1Update(context, &c, 1); while ((context->count[0] & 504) != 448) { c = 0000; SHA1Update(context, &c, 1); } SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ for (i = 0; i < 20; i++) { digest[i] = (unsigned char) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); } /* Wipe variables */ memset(context, '\0', sizeof(*context)); memset(&finalcount, '\0', sizeof(finalcount)); } void SHA1( char *hash_out, const char *str, int len) { SHA1_CTX ctx; unsigned int ii; SHA1Init(&ctx); for (ii=0; iistep = step_A; state_in->result = 0; state_in->stepcount = 0; } char base64_encode_value(char value_in) { static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; if (value_in > 63) return '='; return encoding[(int)value_in]; } int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) { const char* plainchar = plaintext_in; const char* const plaintextend = plaintext_in + length_in; char* codechar = code_out; char result; char fragment; result = state_in->result; switch (state_in->step) { while (1) { case step_A: if (plainchar == plaintextend) { state_in->result = result; state_in->step = step_A; return codechar - code_out; } fragment = *plainchar++; result = (fragment & 0x0fc) >> 2; *codechar++ = base64_encode_value(result); result = (fragment & 0x003) << 4; case step_B: if (plainchar == plaintextend) { state_in->result = result; state_in->step = step_B; return codechar - code_out; } fragment = *plainchar++; result |= (fragment & 0x0f0) >> 4; *codechar++ = base64_encode_value(result); result = (fragment & 0x00f) << 2; case step_C: if (plainchar == plaintextend) { state_in->result = result; state_in->step = step_C; return codechar - code_out; } fragment = *plainchar++; result |= (fragment & 0x0c0) >> 6; *codechar++ = base64_encode_value(result); result = (fragment & 0x03f) >> 0; *codechar++ = base64_encode_value(result); ++(state_in->stepcount); if (state_in->stepcount == CHARS_PER_LINE/4) { //*codechar++ = '\n'; state_in->stepcount = 0; } } } /* control should not reach here */ return codechar - code_out; } int base64_encode_blockend(char* code_out, base64_encodestate* state_in) { char* codechar = code_out; switch (state_in->step) { case step_B: *codechar++ = base64_encode_value(state_in->result); *codechar++ = '='; *codechar++ = '='; break; case step_C: *codechar++ = base64_encode_value(state_in->result); *codechar++ = '='; break; case step_A: break; } //*codechar++ = '\n'; return codechar - code_out; } libonvif-1.4.4/libonvif/include/0000755000175000017500000000000014355557026016475 5ustar stephenstephenlibonvif-1.4.4/libonvif/include/sha1.h0000644000175000017500000000466714355414521017507 0ustar stephenstephen/* SHA-1 in C By Steve Reid 100% Public Domain The person or persons who have associated work with this document (the "Dedicator" or "Certifier") hereby either (a) certifies that, to the best of his knowledge, the work of authorship identified is in the public domain of the country from which the work is published, or (b) hereby dedicates whatever copyright the dedicators holds in the work of authorship identified below (the "Work") to the public domain. A certifier, moreover, dedicates any copyright interest he may have in the associated work, and for these purposes, is described as a "dedicator" below. A certifier has taken reasonable steps to verify the copyright status of this work. Certifier recognizes that his good faith efforts may not shield him from liability if in fact the work certified is not in the public domain. Dedicator makes this dedication for the benefit of the public at large and to the detriment of the Dedicator's heirs and successors. Dedicator intends this dedication to be an overt act of relinquishment in perpetuity of all present and future rights under copyright law, whether vested or contingent, in the Work. Dedicator understands that such relinquishment of all rights includes the relinquishment of all rights to enforce (by lawsuit or otherwise) those copyrights in the Work. Dedicator recognizes that, once placed in the public domain, the Work may be freely reproduced, distributed, transmitted, used, modified, built upon, or otherwise exploited by anyone for any purpose, commercial or non-commercial, and in any way, including by methods that have not yet been invented or conceived. CC0 for Public Domain Dedication This tool is based on United States law and may not be applicable outside the US. For dedicating new works to the public domain, we recommend CC0. */ #ifndef SHA1_H #define SHA1_H #ifdef __cplusplus extern "C" { #endif #include "stdint.h" typedef struct { uint32_t state[5]; uint32_t count[2]; unsigned char buffer[64]; } SHA1_CTX; void SHA1Transform( uint32_t state[5], const unsigned char buffer[64] ); void SHA1Init( SHA1_CTX * context ); void SHA1Update( SHA1_CTX * context, const unsigned char *data, uint32_t len ); void SHA1Final( unsigned char digest[20], SHA1_CTX * context ); void SHA1( char *hash_out, const char *str, int len); #ifdef __cplusplus } #endif #endif /* SHA1_H */ libonvif-1.4.4/libonvif/include/cencode.h0000644000175000017500000000502414355414521020237 0ustar stephenstephen/* cencode.h - c header for a base64 encoding algorithm This is part of the libb64 project, and has been placed in the public domain. For details, see http://sourceforge.net/projects/libb64 The person or persons who have associated work with this document (the "Dedicator" or "Certifier") hereby either (a) certifies that, to the best of his knowledge, the work of authorship identified is in the public domain of the country from which the work is published, or (b) hereby dedicates whatever copyright the dedicators holds in the work of authorship identified below (the "Work") to the public domain. A certifier, moreover, dedicates any copyright interest he may have in the associated work, and for these purposes, is described as a "dedicator" below. A certifier has taken reasonable steps to verify the copyright status of this work. Certifier recognizes that his good faith efforts may not shield him from liability if in fact the work certified is not in the public domain. Dedicator makes this dedication for the benefit of the public at large and to the detriment of the Dedicator's heirs and successors. Dedicator intends this dedication to be an overt act of relinquishment in perpetuity of all present and future rights under copyright law, whether vested or contingent, in the Work. Dedicator understands that such relinquishment of all rights includes the relinquishment of all rights to enforce (by lawsuit or otherwise) those copyrights in the Work. Dedicator recognizes that, once placed in the public domain, the Work may be freely reproduced, distributed, transmitted, used, modified, built upon, or otherwise exploited by anyone for any purpose, commercial or non-commercial, and in any way, including by methods that have not yet been invented or conceived. CC0 for Public Domain Dedication This tool is based on United States law and may not be applicable outside the US. For dedicating new works to the public domain, we recommend CC0. */ #ifndef BASE64_CENCODE_H #define BASE64_CENCODE_H #ifdef __cplusplus extern "C" { #endif typedef enum { step_A, step_B, step_C } base64_encodestep; typedef struct { base64_encodestep step; char result; int stepcount; } base64_encodestate; void base64_init_encodestate(base64_encodestate* state_in); char base64_encode_value(char value_in); int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in); int base64_encode_blockend(char* code_out, base64_encodestate* state_in); #ifdef __cplusplus } #endif #endif /* BASE64_CENCODE_H */ libonvif-1.4.4/libonvif/include/onvif.h0000644000175000017500000002150614355557026017773 0ustar stephenstephen /******************************************************************************* * onvif.h * * copyright 2018 Stephen Rhodes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * *******************************************************************************/ #ifndef ONVIF_H #define ONVIF_H #include #include #ifdef __cplusplus extern "C" { #endif #ifndef _WIN32 #include #endif #ifdef __MINGW32__ #include #endif #ifdef LIBONVIFDLL_EXPORTS #define LIBRARY_API __declspec(dllexport) #else #define LIBRARY_API extern #endif static const int PAN_TILT_STOP = 0; static const int ZOOM_STOP = 1; struct OnvifData { /*video*/ char videoEncoderConfigurationToken[128]; char resolutions_buf[16][128]; int gov_length_min; int gov_length_max; int frame_rate_min; int frame_rate_max; int bitrate_min; int bitrate_max; int width; int height; int gov_length; int frame_rate; int bitrate; char video_encoder_name_buf[128]; int use_count; float quality; char h264_profile_buf[128]; char multicast_address_type_buf[128]; char multicast_address_buf[128]; int multicast_port; int multicast_ttl; bool autostart; char session_time_out_buf[128]; bool guaranteed_frame_rate; char encoding[128]; int conf_width; int conf_height; int conf_frame_rate_limit; int conf_encoding_interval; int conf_bitrate_limit; /*network*/ char networkInterfaceToken[128]; char networkInterfaceName[128]; bool dhcp_enabled; char ip_address_buf[128]; char default_gateway_buf[128]; char dns_buf[128]; int prefix_length; /*image*/ char videoSourceConfigurationToken[128]; int brightness_min; int brightness_max; int saturation_min; int saturation_max; int contrast_min; int contrast_max; int sharpness_min; int sharpness_max; int brightness; int saturation; int contrast; int sharpness; /*service*/ char device_service[1024]; char media_service[1024]; char imaging_service[1024]; char ptz_service[1024]; char event_service[1024]; /*event*/ char subscription_reference[128]; int event_listen_port; /*general*/ char xaddrs[1024]; char profileToken[128]; char username[128]; char password[128]; time_t time_offset; char stream_uri[1024]; char camera_name[1024]; char serial_number[128]; char host_name[1024]; /*error*/ char last_error[1024]; /*date/time*/ char datetimetype; bool dst; char timezone[128]; bool ntp_dhcp; char ntp_type[128]; char ntp_addr[128]; }; struct OnvifSession { char buf[128][8192]; int len[128]; char uuid[47]; int discovery_msg_id; char preferred_network_address[16]; }; LIBRARY_API void initializeSession(struct OnvifSession *onvif_session); LIBRARY_API void closeSession(struct OnvifSession *onvif_session); LIBRARY_API int broadcast(struct OnvifSession *onvif_session); LIBRARY_API void prepareOnvifData(int ordinal, struct OnvifSession *onvif_session, struct OnvifData *onvif_data); LIBRARY_API int fillRTSPn(struct OnvifData *onvif_data, int profileIndex); #define fillRTSP(a) fillRTSPn(a,0) LIBRARY_API void clearData(struct OnvifData *onvif_data); LIBRARY_API int getCapabilities(struct OnvifData *onvif_data); LIBRARY_API int getProfile(struct OnvifData *onvif_data); LIBRARY_API int getNetworkInterfaces(struct OnvifData *onvif_data); LIBRARY_API int setNetworkInterfaces(struct OnvifData *onvif_data); LIBRARY_API int getNetworkDefaultGateway(struct OnvifData *onvif_data); LIBRARY_API int setNetworkDefaultGateway(struct OnvifData *onvif_data); LIBRARY_API int getDNS(struct OnvifData *onvif_data); LIBRARY_API int setDNS(struct OnvifData *onvif_data); LIBRARY_API int getNTP(struct OnvifData *onvif_data); LIBRARY_API int setNTP(struct OnvifData *onvif_data); LIBRARY_API int getHostname(struct OnvifData *onvif_data); LIBRARY_API int setHostname(struct OnvifData *onvif_data); LIBRARY_API int getVideoEncoderConfigurationOptions(struct OnvifData *onvif_data); LIBRARY_API int getVideoEncoderConfiguration(struct OnvifData *onvif_data); LIBRARY_API int setVideoEncoderConfiguration(struct OnvifData *onvif_data); LIBRARY_API int getOptions(struct OnvifData *onvif_data); LIBRARY_API int getImagingSettings(struct OnvifData *onvif_data); LIBRARY_API int setImagingSettings(struct OnvifData *onvif_data); LIBRARY_API int continuousMove(float x, float y, float z, struct OnvifData *onvif_data); LIBRARY_API int moveStop(int type, struct OnvifData *onvif_data); LIBRARY_API int setPreset(char * arg, struct OnvifData *onvif_data); LIBRARY_API int gotoPreset(char * arg, struct OnvifData *onvif_data); LIBRARY_API int setUser(char * new_password, struct OnvifData *onvif_data); LIBRARY_API int setSystemDateAndTime(struct OnvifData *onvif_data); LIBRARY_API int setSystemDateAndTimeUsingTimezone(struct OnvifData *onvif_data); LIBRARY_API int getTimeOffset(struct OnvifData *onvif_data); LIBRARY_API int getProfileToken(struct OnvifData *onvif_data, int profileIndex); #define getFirstProfileToken(a) getProfileToken(a,0) LIBRARY_API int getStreamUri(struct OnvifData *onvif_data); LIBRARY_API int getDeviceInformation(struct OnvifData *onvif_data); LIBRARY_API int rebootCamera(struct OnvifData *onvif_data); LIBRARY_API int hardReset(struct OnvifData *onvif_data); LIBRARY_API void saveSystemDateAndTime(char * filename, struct OnvifData *onvif_data); LIBRARY_API void saveScopes(char * filename, struct OnvifData *onvif_data); LIBRARY_API void saveDeviceInformation(char * filename, struct OnvifData *onvif_data); LIBRARY_API void saveCapabilities(char * filename, struct OnvifData *onvif_data); LIBRARY_API void saveProfiles(char * filename, struct OnvifData *onvif_data); LIBRARY_API void saveServiceCapabilities(char * filename, struct OnvifData *onvif_data); LIBRARY_API int eventSubscribe(struct OnvifData *onvif_data); LIBRARY_API int eventRenew(struct OnvifData *onvif_data); /* LIBRARY_API xmlDocPtr sendCommandToCamera(char * cmd, char * xaddrs); LIBRARY_API void getBase64(unsigned char * buffer, int chunk_size, unsigned char * result); LIBRARY_API void getUUID(char uuid_buf[47]); LIBRARY_API void addUsernameDigestHeader(xmlNodePtr root, xmlNsPtr ns_env, char * user, char * password, time_t offset); LIBRARY_API void addHttpHeader(xmlDocPtr doc, xmlNodePtr root, char * xaddrs, char * post_type, char cmd[], int cmd_length); LIBRARY_API void getDiscoveryXml(char buffer[], int buf_size, char uuid[47]); LIBRARY_API void getDiscoveryXml2(char buffer[], int buf_size); LIBRARY_API void getScopeField(char *, char *, char[1024]); LIBRARY_API void getCameraName(int ordinal, struct OnvifSession *onvif_session, struct OnvifData *onvif_data); LIBRARY_API void extractXAddrs(int ordinal, struct OnvifSession *onvif_session, struct OnvifData *onvif_data); LIBRARY_API void extractOnvifService(char service[1024], bool post); LIBRARY_API void extractHost(char * xaddrs, char host[128]); LIBRARY_API int checkForXmlErrorMsg(xmlDocPtr doc, char error_msg[1024]); LIBRARY_API int getXmlValue(xmlDocPtr doc, xmlChar *xpath, char buf[], int buf_length); LIBRARY_API int getNodeAttributen (xmlDocPtr doc, xmlChar *xpath, xmlChar *attribute, char buf[], int buf_length, int profileIndex); #define getNodeAttribute(doc,xpath,attribute,buf,buf_length) getNodeAttributen(doc,xpath,attribute,buf,buf_length,0) LIBRARY_API xmlXPathObjectPtr getNodeSet (xmlDocPtr doc, xmlChar *xpath); */ LIBRARY_API void getDiscoveryXml(char buffer[], int buf_size, char uuid[47]); LIBRARY_API void getDiscoveryXml2(char buffer[], int buf_size); LIBRARY_API void getScopeField(char *, char *, char[1024]); LIBRARY_API void getCameraName(int ordinal, struct OnvifSession *onvif_session, struct OnvifData *onvif_data); LIBRARY_API void extractXAddrs(int ordinal, struct OnvifSession *onvif_session, struct OnvifData *onvif_data); LIBRARY_API void extractOnvifService(char service[1024], bool post); LIBRARY_API void extractHost(char * xaddrs, char host[128]); LIBRARY_API int setSocketOptions(int socket); LIBRARY_API void prefix2mask(int prefix, char mask_buf[128]); LIBRARY_API int mask2prefix(char * mask_buf); LIBRARY_API void getIPAddress(char buf[128]); LIBRARY_API void dumpConfigAll (struct OnvifData *onvif_data); #ifdef __MINGW32__ int inet_pton(int af, const char *src, void *dst); const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); #endif #ifdef __cplusplus } #endif #endif libonvif-1.4.4/libonvif/include/getopt-win.h0000644000175000017500000004410114355414521020733 0ustar stephenstephen/** * DISCLAIMER * This file is part of the mingw-w64 runtime package. * * The mingw-w64 runtime package and its code is distributed in the hope that it * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ /* * Copyright (c) 2002 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifndef __GETOPT_H__ #define __GETOPT_H__ #pragma warning(disable:4996) /* All the headers include this file. */ #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ #ifdef REPLACE_GETOPT int opterr = 1; /* if error message should be printed */ int optind = 1; /* index into parent argv vector */ int optopt = '?'; /* character checked for validity */ #undef optreset /* see getopt.h */ #define optreset __mingw_optreset int optreset; /* reset getopt */ char *optarg; /* argument associated with option */ #endif //extern int optind; /* index of first non-option in argv */ //extern int optopt; /* single option character, as parsed */ //extern int opterr; /* flag to enable built-in diagnostics... */ // /* (user may set to zero, to suppress) */ // //extern char *optarg; /* pointer to argument of current option */ #define PRINT_ERROR ((opterr) && (*options != ':')) #define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ #define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ #define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ /* return values */ #define BADCH (int)'?' #define BADARG ((*options == ':') ? (int)':' : (int)'?') #define INORDER (int)1 #ifndef __CYGWIN__ #define __progname __argv[0] #else extern char __declspec(dllimport) *__progname; #endif #ifdef __CYGWIN__ static char EMSG[] = ""; #else #define EMSG "" #endif static int getopt_internal(int, char * const *, const char *, const struct option *, int *, int); static int parse_long_options(char * const *, const char *, const struct option *, int *, int); static int gcd(int, int); static void permute_args(int, int, int, char * const *); static char *place = EMSG; /* option letter processing */ /* XXX: set optreset to 1 rather than these two */ static int nonopt_start = -1; /* first non option argument (for permute) */ static int nonopt_end = -1; /* first option after non options (for permute) */ /* Error messages */ static const char recargchar[] = "option requires an argument -- %c"; static const char recargstring[] = "option requires an argument -- %s"; static const char ambig[] = "ambiguous option -- %.*s"; static const char noarg[] = "option doesn't take an argument -- %.*s"; static const char illoptchar[] = "unknown option -- %c"; static const char illoptstring[] = "unknown option -- %s"; static void _vwarnx(const char *fmt,va_list ap) { (void)fprintf(stderr,"%s: ",__progname); if (fmt != NULL) (void)vfprintf(stderr,fmt,ap); (void)fprintf(stderr,"\n"); } static void warnx(const char *fmt,...) { va_list ap; va_start(ap,fmt); _vwarnx(fmt,ap); va_end(ap); } /* * Compute the greatest common divisor of a and b. */ static int gcd(int a, int b) { int c; c = a % b; while (c != 0) { a = b; b = c; c = a % b; } return (b); } /* * Exchange the block from nonopt_start to nonopt_end with the block * from nonopt_end to opt_end (keeping the same order of arguments * in each block). */ static void permute_args(int panonopt_start, int panonopt_end, int opt_end, char * const *nargv) { int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; char *swap; /* * compute lengths of blocks and number and size of cycles */ nnonopts = panonopt_end - panonopt_start; nopts = opt_end - panonopt_end; ncycle = gcd(nnonopts, nopts); cyclelen = (opt_end - panonopt_start) / ncycle; for (i = 0; i < ncycle; i++) { cstart = panonopt_end+i; pos = cstart; for (j = 0; j < cyclelen; j++) { if (pos >= panonopt_end) pos -= nnonopts; else pos += nopts; swap = nargv[pos]; /* LINTED const cast */ ((char **) nargv)[pos] = nargv[cstart]; /* LINTED const cast */ ((char **)nargv)[cstart] = swap; } } } #ifdef REPLACE_GETOPT /* * getopt -- * Parse argc/argv argument vector. * * [eventually this will replace the BSD getopt] */ int getopt(int nargc, char * const *nargv, const char *options) { /* * We don't pass FLAG_PERMUTE to getopt_internal() since * the BSD getopt(3) (unlike GNU) has never done this. * * Furthermore, since many privileged programs call getopt() * before dropping privileges it makes sense to keep things * as simple (and bug-free) as possible. */ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); } #endif /* REPLACE_GETOPT */ //extern int getopt(int nargc, char * const *nargv, const char *options); #ifdef _BSD_SOURCE /* * BSD adds the non-standard `optreset' feature, for reinitialisation * of `getopt' parsing. We support this feature, for applications which * proclaim their BSD heritage, before including this header; however, * to maintain portability, developers are advised to avoid it. */ # define optreset __mingw_optreset extern int optreset; #endif #ifdef __cplusplus } #endif /* * POSIX requires the `getopt' API to be specified in `unistd.h'; * thus, `unistd.h' includes this header. However, we do not want * to expose the `getopt_long' or `getopt_long_only' APIs, when * included in this manner. Thus, close the standard __GETOPT_H__ * declarations block, and open an additional __GETOPT_LONG_H__ * specific block, only when *not* __UNISTD_H_SOURCED__, in which * to declare the extended API. */ #endif /* !defined(__GETOPT_H__) */ #if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) #define __GETOPT_LONG_H__ #ifdef __cplusplus extern "C" { #endif struct option /* specification for a long form option... */ { const char *name; /* option name, without leading hyphens */ int has_arg; /* does it take an argument? */ int *flag; /* where to save its status, or NULL */ int val; /* its associated status value */ }; enum /* permitted values for its `has_arg' field... */ { no_argument = 0, /* option never takes an argument */ required_argument, /* option always requires an argument */ optional_argument /* option may take an argument */ }; /* * parse_long_options -- * Parse long options in argc/argv argument vector. * Returns -1 if short_too is set and the option does not match long_options. */ static int parse_long_options(char * const *nargv, const char *options, const struct option *long_options, int *idx, int short_too) { char *current_argv, *has_equal; size_t current_argv_len; int i, ambiguous, match; #define IDENTICAL_INTERPRETATION(_x, _y) \ (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ long_options[(_x)].flag == long_options[(_y)].flag && \ long_options[(_x)].val == long_options[(_y)].val) current_argv = place; match = -1; ambiguous = 0; optind++; if ((has_equal = strchr(current_argv, '=')) != NULL) { /* argument found (--option=arg) */ current_argv_len = has_equal - current_argv; has_equal++; } else current_argv_len = strlen(current_argv); for (i = 0; long_options[i].name; i++) { /* find matching long option */ if (strncmp(current_argv, long_options[i].name, current_argv_len)) continue; if (strlen(long_options[i].name) == current_argv_len) { /* exact match */ match = i; ambiguous = 0; break; } /* * If this is a known short option, don't allow * a partial match of a single character. */ if (short_too && current_argv_len == 1) continue; if (match == -1) /* partial match */ match = i; else if (!IDENTICAL_INTERPRETATION(i, match)) ambiguous = 1; } if (ambiguous) { /* ambiguous abbreviation */ if (PRINT_ERROR) warnx(ambig, (int)current_argv_len, current_argv); optopt = 0; return (BADCH); } if (match != -1) { /* option found */ if (long_options[match].has_arg == no_argument && has_equal) { if (PRINT_ERROR) warnx(noarg, (int)current_argv_len, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; return (BADARG); } if (long_options[match].has_arg == required_argument || long_options[match].has_arg == optional_argument) { if (has_equal) optarg = has_equal; else if (long_options[match].has_arg == required_argument) { /* * optional argument doesn't use next nargv */ optarg = nargv[optind++]; } } if ((long_options[match].has_arg == required_argument) && (optarg == NULL)) { /* * Missing argument; leading ':' indicates no error * should be generated. */ if (PRINT_ERROR) warnx(recargstring, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; --optind; return (BADARG); } } else { /* unknown option */ if (short_too) { --optind; return (-1); } if (PRINT_ERROR) warnx(illoptstring, current_argv); optopt = 0; return (BADCH); } if (idx) *idx = match; if (long_options[match].flag) { *long_options[match].flag = long_options[match].val; return (0); } else return (long_options[match].val); #undef IDENTICAL_INTERPRETATION } /* * getopt_internal -- * Parse argc/argv argument vector. Called by user level routines. */ static int getopt_internal(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx, int flags) { char *oli; /* option letter list index */ int optchar, short_too; static int posixly_correct = -1; if (options == NULL) return (-1); /* * XXX Some GNU programs (like cvs) set optind to 0 instead of * XXX using optreset. Work around this braindamage. */ if (optind == 0) optind = optreset = 1; /* * Disable GNU extensions if POSIXLY_CORRECT is set or options * string begins with a '+'. * * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or * optreset != 0 for GNU compatibility. */ if (posixly_correct == -1 || optreset != 0) posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); if (*options == '-') flags |= FLAG_ALLARGS; else if (posixly_correct || *options == '+') flags &= ~FLAG_PERMUTE; if (*options == '+' || *options == '-') options++; optarg = NULL; if (optreset) nonopt_start = nonopt_end = -1; start: if (optreset || !*place) { /* update scanning pointer */ optreset = 0; if (optind >= nargc) { /* end of argument vector */ place = EMSG; if (nonopt_end != -1) { /* do permutation, if we have to */ permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } else if (nonopt_start != -1) { /* * If we skipped non-options, set optind * to the first of them. */ optind = nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } if (*(place = nargv[optind]) != '-' || (place[1] == '\0' && strchr(options, '-') == NULL)) { place = EMSG; /* found non-option */ if (flags & FLAG_ALLARGS) { /* * GNU extension: * return non-option as argument to option 1 */ optarg = nargv[optind++]; return (INORDER); } if (!(flags & FLAG_PERMUTE)) { /* * If no permutation wanted, stop parsing * at first non-option. */ return (-1); } /* do permutation */ if (nonopt_start == -1) nonopt_start = optind; else if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); nonopt_start = optind - (nonopt_end - nonopt_start); nonopt_end = -1; } optind++; /* process next argument */ goto start; } if (nonopt_start != -1 && nonopt_end == -1) nonopt_end = optind; /* * If we have "-" do nothing, if "--" we are done. */ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { optind++; place = EMSG; /* * We found an option (--), so if we skipped * non-options, we have to permute. */ if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } } /* * Check long options if: * 1) we were passed some * 2) the arg is not just "-" * 3) either the arg starts with -- we are getopt_long_only() */ if (long_options != NULL && place != nargv[optind] && (*place == '-' || (flags & FLAG_LONGONLY))) { short_too = 0; if (*place == '-') place++; /* --foo long option */ else if (*place != ':' && strchr(options, *place) != NULL) short_too = 1; /* could be short option too */ optchar = parse_long_options(nargv, options, long_options, idx, short_too); if (optchar != -1) { place = EMSG; return (optchar); } } if ((optchar = (int)*place++) == (int)':' || (optchar == (int)'-' && *place != '\0') || (oli = (char*)strchr(options, optchar)) == NULL) { /* * If the user specified "-" and '-' isn't listed in * options, return -1 (non-option) as per POSIX. * Otherwise, it is an unknown option character (or ':'). */ if (optchar == (int)'-' && *place == '\0') return (-1); if (!*place) ++optind; if (PRINT_ERROR) warnx(illoptchar, optchar); optopt = optchar; return (BADCH); } if (long_options != NULL && optchar == 'W' && oli[1] == ';') { /* -W long-option */ if (*place) /* no space */ /* NOTHING */; else if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else /* white space */ place = nargv[optind]; optchar = parse_long_options(nargv, options, long_options, idx, 0); place = EMSG; return (optchar); } if (*++oli != ':') { /* doesn't take argument */ if (!*place) ++optind; } else { /* takes (optional) argument */ optarg = NULL; if (*place) /* no white space */ optarg = place; else if (oli[1] != ':') { /* arg not optional */ if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else optarg = nargv[optind]; } place = EMSG; ++optind; } /* dump back option letter */ return (optchar); } /* * getopt_long -- * Parse argc/argv argument vector. */ int getopt_long(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE)); } /* * getopt_long_only -- * Parse argc/argv argument vector. */ int getopt_long_only(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE|FLAG_LONGONLY)); } //extern int getopt_long(int nargc, char * const *nargv, const char *options, // const struct option *long_options, int *idx); //extern int getopt_long_only(int nargc, char * const *nargv, const char *options, // const struct option *long_options, int *idx); /* * Previous MinGW implementation had... */ #ifndef HAVE_DECL_GETOPT /* * ...for the long form API only; keep this for compatibility. */ # define HAVE_DECL_GETOPT 1 #endif #ifdef __cplusplus } #endif #endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */libonvif-1.4.4/recipe/0000755000175000017500000000000014355557026014511 5ustar stephenstephenlibonvif-1.4.4/recipe/build.sh0000644000175000017500000000207314355557026016146 0ustar stephenstephen#******************************************************************************* # libovnif/recipe/build.sh # # Copyright (c) 2022 Stephen Rhodes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # #******************************************************************************/ echo "BUILD.SH" mkdir build cd build cmake -DCMAKE_INSTALL_PREFIX:PATH=${PREFIX} -DCMAKE_BUILD_TYPE:STRING=Release -DBUILD_GUI=ON .. make install libonvif-1.4.4/recipe/meta.yaml0000644000175000017500000000257214355557026016331 0ustar stephenstephen#******************************************************************************* # libovnif/recipe/meta.yaml # # Copyright (c) 2022 Stephen Rhodes # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # #******************************************************************************/ {% set name = "libonvif" %} {% set version = "1.4.4" %} package: name: {{ name }} version: {{ version }} source: path: .. build: number: 0 requirements: build: - {{ compiler('cxx') }} - cmake - make # [unix] host: - libxml2=2.10.2 # [win] - libxml2 # [linux] - qt run: - libxml2=2.10.2 # [win] - libxml2 # [linux] - qt test: commands: - echo test about: summary: client side implementation of ONVIF libonvif-1.4.4/recipe/bld.bat0000644000175000017500000000217114355557026015743 0ustar stephenstephenREM******************************************************************************* REM libovnif/recipe/meta.yaml REM REM Copyright (c) 2022 Stephen Rhodes REM REM This program is free software; you can redistribute it and/or modify REM it under the terms of the GNU General Public License as published by REM the Free Software Foundation; either version 2 of the License, or REM (at your option) any later version. REM REM This program is distributed in the hope that it will be useful, REM but WITHOUT ANY WARRANTY; without even the implied warranty of REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the REM GNU General Public License for more details. REM REM You should have received a copy of the GNU General Public License along REM with this program; if not, write to the Free Software Foundation, Inc., REM 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. REM REM******************************************************************************/ mkdir build cd build cmake -G "NMake Makefiles" -DCMAKE_INSTALL_PREFIX:PATH="%LIBRARY_PREFIX%" -DCMAKE_BUILD_TYPE:STRING=Release -DBUILD_GUI=ON .. nmake nmake install