From 6196c19805b8ce8bf8d8eee338b4c525f1be5c85 Mon Sep 17 00:00:00 2001 From: Sebastien Vincent Date: Fri, 12 Feb 2010 16:57:09 +0000 Subject: [PATCH] Implement desktop streaming capture device as a PullBufferCaptureDevice (rather than a PushBuffer ones). Reduce memory allocation by refactoring screencapture JNI. --- lib/native/freebsd-64/libscreencapture.so | Bin 9484 -> 9580 bytes lib/native/freebsd/libscreencapture.so | Bin 7025 -> 7073 bytes lib/native/linux-64/libscreencapture.so | Bin 6784 -> 6784 bytes lib/native/linux/libscreencapture.so | Bin 4848 -> 4912 bytes lib/native/mac/libscreencapture.jnilib | Bin 41776 -> 41776 bytes lib/native/windows-64/screencapture.dll | Bin 38780 -> 39292 bytes lib/native/windows/screencapture.dll | Bin 57169 -> 57169 bytes src/native/screencapture/Makefile | 2 +- ...eomedia_imgstreaming_NativeScreenCapture.c | 102 ++- ...eomedia_imgstreaming_NativeScreenCapture.h | 6 +- .../device/VideoMediaDeviceSession.java | 10 +- .../imgstreaming/DesktopInteract.java | 34 + .../imgstreaming/DesktopInteractImpl.java | 84 +- .../imgstreaming/ImageStreamingUtils.java | 22 +- .../imgstreaming/NativeScreenCapture.java | 45 +- .../AbstractPullBufferCaptureDevice.java | 721 ++++++++++++++++++ .../protocol/AbstractPullBufferStream.java | 241 ++++++ .../protocol/imgstreaming/DataSource.java | 18 +- .../protocol/imgstreaming/ImageStream.java | 234 +++--- 19 files changed, 1274 insertions(+), 245 deletions(-) create mode 100644 src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferCaptureDevice.java create mode 100644 src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferStream.java diff --git a/lib/native/freebsd-64/libscreencapture.so b/lib/native/freebsd-64/libscreencapture.so index a592d3a57ac845654e6ca16002e7553fdf045093..cc172d350faf9ab15236c8320afdc1c3876bb333 100755 GIT binary patch delta 2724 zcmdT`Z){Ul6u+-sx3$pqb=~sTQMNX`K?7!jnfnt$Xr+s9*kpz51K^)@8s;FVXgW8| zq=SO;@)&g9gb-tNA%^q=(ZO&TiqP>N63Cce#1Cem39rnAE+&R3`aI|MJ$%3tKl_rt zdw;+4=bn4+c|CXE+F#eEgO*_B(9*^gd7iMx7zrs59I(akGx$7T;PXR6OWYZE!6@S{ zkeQ(?OWdmN>ESraaUWUQf;wHWD7>v?K53kjcM`+=QT;NKr#GwuM<`cRY2`@BCtBSkywmV#b%?vsE;p^gQ+E+D)?aRt;nRpM zh_eijA+{nOVfahLMTmzPK7tt5Be^ugpOx}S(v6wS4KU&$Dqxm!+Zo=E7|$mcXZSL;(3UdGyEpv`H1}tcOiBn<^jV0D;I>+lVH~-pNCj_bWg9Nr)3t}?i7-Cn9G;IZUalgX^4ubCxFBa#M|J- z!*XU}F9;zSteB-#zZlmNi=@GX7%Rjjff$xrhA1wjG%aCMD}ZYQ8}=s=mG6YL4lxe6 zfxH0A&8!Wd#F7fjs(dpl-ZWN;0H z-ysI2!H!);c=*9^>H-&8xlQyXysdkF-;tM;ZK7YYZWDvwVo167AveZ8A-)FuBL2;H z=uaab2Vs^hZ15$RLS3IkdtA-`NO9kX;vUCZo>}@IN@zc0!!Hfod@Al`<54jUV^sM< zJjBMM;xHReh#6>0AUGwC03q!>kA~;y=dOCLonCjHbr@|SXA zz%e|RS*)k3McCRckPeIn7Bq4H^D9Q%!h6pc87yp*ko7R(=D5hj8IKnky0xa>Yvc+G z7N3vd$B{B}pB}4u{?+ArCYcCc$CZwigbh(~p;=V}86O1p@4nv^7w_!iZ6KN`~QY+6~tu delta 2590 zcmdT`?{8C87(TaMySAh4ZP(Fu8(WKa{BoHa30(=9tWx9*t8`>Bs0*12%S?#QqzlAg zx-p2mUPkUXUlzYOKN%AZ$czc7xCyvH#2`j9_5r72ymcCeUnC%0-}l_!<%;_UJjuQH zJkN99_q^xFJ@5H_`_b*{8ryVeWL=_36$vlqmk@&xfOW!S@~UX?>d46I01X&MG+>Z) zenz zu-J#inVkg5%#wUL(U#;xWMN6Z0{U($sbI%SUK1#DJ9Y@i*)xt;gduj(vCTH^XF+EJ zfRr;Feb7NWOqWP*>FW5a&K)+GR3lDTF-{YYKETfv<2b->xx&%@a>ELzz1}iH7mK^Y zQL(6&f)cT)5!McB%%WCW)HQ4X)JBWC!lEYGhoH7v)VM{Z>_beo*?kim3 zcsF7j;t7tQMr=oXlH;ckmmnVF_;JM0A1SCDKk5?mqzC7;(9ekkHo#dd^m5#Q7+0#$ z#qlb{Zp2xRYY=-7w{pB3aT(%9j(v!iA&znEMC?T@0){VP2g-bV@0G&p_HCkrEk9!| zS4EYUKTAZSrJ&Wkmkv(2sO->y(XUd)@dH$-BGMyQ`)+*cm;e)j&tP}A3NA$MA`0!^ zh8jIGk@M2wmb*?iUzW&^LYHmwefeAYa#~JHp|qTlM5>gg<*!rz6AQXJ3e(djPl1{L zun(rPO>v&VI5YD7jQp3b&fs!^WdvIEm8CvMt?&!10rc@6wAWhh>pHhSiKINGeT4*A zB!SKQ55I-snP+8{g8%O$%<6&kQy^ePpd+!(@IwoFik}|^?sg<>QE2@&(ELgV5 z=Wz%{Uqk5Yroei=;qG-%m#4s#@{yl~$cXkGkT{BL8@xp5&?=6CIF*)mVM{uAP|E7L zhv;xlYBeHz31sN%VMJLvsOve0wt@ZO4L`|$<@xQY)3`fQvL;{4$UjjfCp8+DANd2Vd?grn|d8snFSBR3_Rl9oBDA) zjQ0A*SZ5%IS>kG7w{0OfzCKtZ2z0!?e18dKlZ)X;JZ2_@HnvD2U?xEGdy2K!l(WuA zwR_0O1CWjeBF%0yAeBuK zvNO?`$IOjdkl88|9y6mY1lePi3HMO(T!#=UH$&2!tlaFrQx0CCO!Gjit0Wj#s%on26kVLhD+Rk$(-1Z12NM;R;K?cwKM{-fC{DubGUxw$f4K&{jd;kIpPi{aCaA0}wjUN@GKN=` pKpd|s195&;xfE|`GGjEoj=?b+fJ=(G)F%Hi@W$n3?aD3oe*n)9m0AD* diff --git a/lib/native/freebsd/libscreencapture.so b/lib/native/freebsd/libscreencapture.so index 97049f50f5bea8c042bef434d713b722446c4ef0..8c1b59cd1b17a7dbc65e3d78f8e32d28726c452d 100755 GIT binary patch delta 1853 zcmd5-UuauZ82?W0ZJTsWlQeA;Nz$fS==@)GZbe1Jk_#1mQ0JY9%qk91*-IrAi^f>) zie4^d9jC)FCQj%>A2zDAmM~Ji?Sct2U}p26&_O8`QX6Uq1uL^&zwe|8+rW1ZB){+f z{l0V0{hb?!zv>+i+rsYCkw_@#1fX9X;Bo^n1Yrvyo_@pUD5A57O7WW!pDih)MoWTy z0^K#eN$5F@q$NScQK;$7!cLWFN#GJV(IEhJd^WE0HQgih$(r6mdJ9a|3|Mk1Mp)4}*eLeyA5)fj+0l^{nQ6?_6?4=a zavQ41eV1w-!&)?eC8jZrKG`+hcQ+Q1vodZi?wuqOSE^GY%4DZ*ri5$FP))theP@87 ztWeDH-G0l;DkaL%5^Gkx8{4A#h9depgf+0Kdkkew+p*$y%3)~3qS-!iq-pwi+w_aUpKX@abhBG3{+NWQ%>Y^W*h!>-3^oJ#F}eL+{spCpzb9&yc? z3oCB@>ca1C*WAyJ0)5ezesyFOEC5GAPby_YDeDc|4@}F-PYn5orVS-MCV#CfX-!En z15cWoihH$$A3okR60=ew{T)4jUqUVXzoSJ@b(O8joOt!Mfn!(3RsFZ4M@qXl`Bc+G>{0$} z)Aw~m5Eu9t&Hxe1W#@xzimy9QvEs~db3kGle#X;TPy9$;Po$ox(e31`o`}fIaYxJk zI^x3ue;6gfsr(g`hyy412uj3$=TY);v!$tyxNnkQLpw3n6#ok);<9bt@W7j>Px9++ogSKxULJ#1ID?qy z&i0aD@>!dNChPqYsp5nadsB7f3H*TkTYu8Db{ axF$Azw_g+U$Sb#A6HBP?%sk+aN&f=a>JbM3 delta 1808 zcmd5-|7%-S6hAlbrA@Y^`L-n)Y140^v(v4jxH6qTEapLB6E^jUAf+4jhyE~ON}}Bl zA=_RHKHjRCcS3(CxI%tV*qEgoifps4(L{|{Fi4Ov0!~P6lop0m?0lYc(}ZsL2Y4a* zoOAEzoOjO2&F9SyuI*2ESyo*adU`shodEQ)0xmZILlAo%h!;)->=``Fpp%(98?dB` z4&nhwuw7uK>DP^EWYUrV!*E?{`fb9eOtd6$2^txgS2tVHRz2tjgyt&~M zge-r4XNbq77|U>7dXmlYo6=#H<#u_%QgraA<#2a)GsF#R<~9s!r0}4B4ExQ`pGIp_aG5Xy@Cjibgy0$(djLKod<;OVIjYC8zUr&<(N;TS1N?|>g#ExT+XhowZA>4& zrzh6+$m?kEx?)k?wpQ235#KeuyaYI)s zwDl8h-8RllVAzy==F5sKojXs@IBL_?GSAq%+A9=NzHge#?;_byEBMsvL6M@wEA}9} z%m1>6Q-byl(sZSWwYWElZ&P4D22%B=Xr*XGW!Kf{Bh+tB#)0C*+#;~rXc1ASeR}kD zaa1$Zq8@cWOfZxxao)H;WSTi;m14AH&5ZX=DR=Ah6m>;02NuZEo7!JR}fO4uH2y!iZ-=m3G_eX z9gRNfmAvwIvyUre)+>HfnDy%?B*Rd%RMA7X(1Abg%D%=u-AX+4AIx0+q8dux7`|sD z){W$bUK?IaKe$x=*OyPFftHX-uA@b=Hp=JFB0)QeR)DX1 z9afUFIA^|ID~ZrJ??;Q|Da()fcB5*Y#_#=n*0FOe-6cUs#n zo*kmmc~MaDL2w1ZO0`(mOQO}rf*>xGRiws(IJL6W7h9qMRP`l-FYFD!;i+Bey&X||>n+zwy0HjM8g{{m8UuS1)Xnmghf)h6A% z|D9Ioy%N|I(9ZWxROB`L7I>Pt;D7U{jr_66EgE5+cW6%1>s`P431QA$=G;&gk=-67 zdhr+2N9Jlv$UgCb?|WYPj1P5;5ifnZ&VxT#%?C9+|OL8cEeOc7OWqBXe45E%Ue1=wGs!u_NehrgcO375HQ<@(z>Xlnnbu>ZH@x{t5?U)1+Oh#})awQ9^cM~_2g z&Q+R)w46)yjFMOBMI~?0Kb5>gdmvfIfW$q_&YY@W7=OEsWtQyL!dRRS70e0laLv#P zZKJy{wA9x#?Vbp&oZp7pC$XPoPblXOgkGHv$MH6737?#!{o}q_yr!~Heq#!5P4&9U a;j%XD?tu2~-Eq%Cn=1RJdS6su_I(9{IKkim delta 1072 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 diff --git a/lib/native/mac/libscreencapture.jnilib b/lib/native/mac/libscreencapture.jnilib index 0633cdf4abfa548198825f372c2f5f1b8b327b7d..1e00a5344dd0099119ad59ff8a4fa70ece8ae2fd 100755 GIT binary patch delta 2238 zcma)-eM}Q)7{H%?07VOTC{7e{ht|a@I@HXx3a&-Yov}I>S7pHoi!Nc)1+qa_wyQlT zIBy4vsed@90T(1L(b%NUFk`g!gQ6o59WyRt6Q?Fc-G(|pvgvNmb61T2xcH`b@9*B{ z`+c9ghPsR){8h6fLTLbbMu1RScq6;a$Z~T=e3iF)+sOOt8%*b~T%8y0AJsR)9|s&I zYG?^+D$*NxR|oVpMS4wp^2pa$ty~efwLx1hRcf*VJ`R|MD+aXiSQMk;;pDe$W@I_j zrnc-ytLDsa$uD!zGr@6!*?M?bHCAl1kl=}lU$^m^02(7q!xmJO`9m@O8Uiqf`D>f` z*j;8CKjAKu!S^F|5nQ6BOrky_mZ%qfXOVB5GZQmQUKGApHro`wPcFtJ`Aaxi6uuuP zYy5yeg909HjMS7W0d5p(0|_WkPWNRdAI*VBl$qHUuR{Z^YE-eJ1RoAz^_C|U=>>^c zP$!88qol(hCEK|a4Cj}%pq+$%M&pR$>BP;8C(~1`F;czg7#S;kM6o8zO9v!h45}wF zhojK}Rz4fO!7_(0r1s(ggDRh``wdip)Lxk%Q1FNOm}dvN%#H`298Uy3$x@zLgl{y& zQ`7U$|K9?r^qWaq9f4CDO4pQ~IT@_`Xl$^xEF)~@lDJnpvy;=od>|*yR$v*KzG@`u zgWKuoPTBf+QAf(x847%!xSS2}02qlUCi1h9}Djv;GsBRf8LXt^!pVY73666d z5tJHMZ7E3tC~D3;Kv}q$gMHM}wVHzI2(6)(pB3cGYRgbEK2al=qIj{~72|slsF8a% z^R0JW%(PQ?uva^YP7{TQVnqAyU?=Y&Y7-Q^usd+xCs4a0ga}xKPJ%2guGS;RDL~vm zO$!*q;59{plEh%)f?_$sq3{@K2+f5b;~cl(O;AkV;S#vzJ5->S`9sWHZ`cmfmSgdr z#7t2Z%cp5+kA|n*@}Kctx)kG87o&zBcSy0mBrolje67#Zs@)3T?UK!N1?*G-TQ%yI z)p$8NCFhIC`LmLwY7$K;D0r+29;*_(u_oO-9!0_XYo?WBD~v-PJo;Ewk;f2+biR$& zgY7#1LcBv~+K|B}o$1>(DCvBLxLW7E#1+VimtfZ9dH6u%4@!!L_O(c7HyO?N0f4k* zb9VS{*)o=SJTCQt;jtlEA1T4*Ra0|^TYvb?FymW7!-*Gz4o8H)tooHnb>lnL1YmWC zC25zD{i`MAa^&9VO@*J&-Ba*m>-}(EfJ+T-Z_3UH`D9avXf=5YQN6Cov_BYWn(P;S z@0&W7NoG#87(0$#QTtmAVA-ClE-z|@B!Ar4A%2$a6+5SU#h>PQ#lPo!eG?aYeFaNu zUG;`r(n37ihO6GX`s94_j(&1iIoWw4+`j0SvcR?5&@Gjs&f1^7q-Sr#$o0vOhIR8y z*cj-jUG!yp?EnT0ZvWy6*bBLZ`2}SF(XUO#l@SSWef73nDCssp$s?$k`UoJa*8uKH zrzTs6vT~G_KP3|?IZd_!Wfdr^cuH1W37V{Wn?Dz-P+0Yp&?&tOPN@!xr9B`>QE=M_ zz~I15cW~HtU^p+ny95TTjH865B2%?=Leo3*bmjiNH@)@k5a(Xj)sG2*A$$Z$a1nChX@dnTD@61NzjK*CGRd3&Z~J(&H05~pjo+O5Ze-xIzI8=oUf z114diZ0)%aLZ=qdV(K`8y%O%*GT)Gc9cX>#70%!;GC^b8>z08m*4As3jKMeCCe1>9 jn4$jG(kfz<(lu52(}8e|xySt;zTwlAF$g delta 1970 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 z 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 EzmDL%;Q#;t diff --git a/src/native/screencapture/Makefile b/src/native/screencapture/Makefile index 7948f893c..cd576e9dd 100644 --- a/src/native/screencapture/Makefile +++ b/src/native/screencapture/Makefile @@ -4,7 +4,7 @@ # \author Sebastien Vincent CC = gcc -CFLAGS = -std=c99 -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600 -Wall -Wextra -pedantic -Wstrict-prototypes -Wredundant-decls +CFLAGS = -std=c99 -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600 -Wall -Wextra -pedantic -Wstrict-prototypes -Wredundant-decls -O3 # uncomment to compile on Linux LDFLAGS = diff --git a/src/native/screencapture/net_java_sip_communicator_impl_neomedia_imgstreaming_NativeScreenCapture.c b/src/native/screencapture/net_java_sip_communicator_impl_neomedia_imgstreaming_NativeScreenCapture.c index 39e375cdb..a80e54a85 100644 --- a/src/native/screencapture/net_java_sip_communicator_impl_neomedia_imgstreaming_NativeScreenCapture.c +++ b/src/native/screencapture/net_java_sip_communicator_impl_neomedia_imgstreaming_NativeScreenCapture.c @@ -14,6 +14,7 @@ #include #include +#include #if defined(_WIN32) || defined(_WIN64) @@ -22,6 +23,8 @@ #include typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int8 uint8_t; #elif defined(__APPLE__) @@ -56,7 +59,7 @@ typedef __int32 int32_t; * \param h capture height * \return 0 if success, -1 otherwise */ -static int windows_grab_screen(int32_t* data, int32_t x, int32_t y, int32_t w, int32_t h) +static int windows_grab_screen(jbyte* data, int32_t x, int32_t y, int32_t w, int32_t h) { static const RGBQUAD redColor = {0x00, 0x00, 0xFF, 0x00}; static const RGBQUAD greenColor = {0x00, 0xFF, 0x00, 0x00}; @@ -72,6 +75,9 @@ static int windows_grab_screen(int32_t* data, int32_t x, int32_t y, int32_t w, i BITMAPINFOHEADER* bitmap_hdr = NULL; RGBQUAD *pixels = NULL; size_t i = 0; + size_t off = 0; + uint32_t test = 1; + int little_endian = *((uint8_t*)&test); /* get handle to the entire screen of Windows */ desktop = GetDC(NULL); @@ -186,8 +192,20 @@ static int windows_grab_screen(int32_t* data, int32_t x, int32_t y, int32_t w, i for(i = 0 ; i < size ; i++) { - RGBQUAD* pixel = &pixels[i]; - data[i] = 0xFF000000 | pixel->rgbRed << 16 | pixel->rgbGreen << 8 | pixel->rgbBlue; + RGBQUAD* quad = &pixels[i]; + uint32_t pixel = 0xFF000000 | quad->rgbRed << 16 | quad->rgbGreen << 8 | quad->rgbBlue; + + if(little_endian) + { + uint8_t r = (pixel >> 16) & 0xff; + uint8_t g = (pixel >> 8) & 0xff; + uint8_t b = pixel & 0xff; + + pixel = 0xff << 24 | b << 16 | g << 8 | r; + } + + memcpy(data + off, &pixel, 4); + off += 4; } /* cleanup */ @@ -211,7 +229,7 @@ static int windows_grab_screen(int32_t* data, int32_t x, int32_t y, int32_t w, i * \param h capture height * \return 0 if success, -1 otherwise */ -static int quartz_grab_screen(int32_t* data, int32_t x, int32_t y, int32_t w, int32_t h) +static int quartz_grab_screen(jbyte* data, int32_t x, int32_t y, int32_t w, int32_t h) { CGImageRef img = NULL; CGDataProviderRef provider = NULL; @@ -221,6 +239,8 @@ static int quartz_grab_screen(int32_t* data, int32_t x, int32_t y, int32_t w, in size_t off = 0; size_t i = 0; CGRect rect; + uint32_t test_endian = 1; + int little_endian = *((uint8_t*)&test_endian); rect = CGRectMake(x, y, w, h); img = CGWindowListCreateImage(rect, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowImageDefault); @@ -242,8 +262,17 @@ static int quartz_grab_screen(int32_t* data, int32_t x, int32_t y, int32_t w, in { uint32_t pixel = *((uint32_t*)&pixels[i]); - pixel |= 0xff000000; /* ARGB */ - data[off++] = pixel; + if(little_endian) + { + uint8_t r = (pixel >> 16) & 0xff; + uint8_t g = (pixel >> 8) & 0xff; + uint8_t b = pixel & 0xff; + + pixel = 0xff << 24 | b << 16 | g << 8 | r; + } + + memcpy(data + off, &pixel, 4); + off += 4; } /* cleanup */ @@ -264,7 +293,7 @@ static int quartz_grab_screen(int32_t* data, int32_t x, int32_t y, int32_t w, in * \param h capture height * \return 0 if success, -1 otherwise */ -static int x11_grab_screen(const char* x11display, int32_t* data, int32_t x, int32_t y, int32_t w, int32_t h) +static int x11_grab_screen(const char* x11display, jbyte* data, int32_t x, int32_t y, int32_t w, int32_t h) { const char* display_str; /* display string */ Display* display = NULL; /* X11 display */ @@ -281,13 +310,9 @@ static int x11_grab_screen(const char* x11display, int32_t* data, int32_t x, int int i = 0; int j = 0; size_t size = 0; - - if(!data) - { - /* fprintf(stderr, "data is NULL!\n"); */ - return -1; - } - + uint32_t test_endian = 1; + int little_endian = *((uint8_t*)&test_endian); + display_str = x11display ? x11display : getenv("DISPLAY"); if(!display_str) @@ -388,7 +413,7 @@ static int x11_grab_screen(const char* x11display, int32_t* data, int32_t x, int } } - /* convert to Java ARGB */ + /* convert to bytes but keep ARGB */ for(j = 0 ; j < h ; j++) { for(i = 0 ; i < w ; i++) @@ -398,8 +423,17 @@ static int x11_grab_screen(const char* x11display, int32_t* data, int32_t x, int */ uint32_t pixel = (uint32_t)XGetPixel(img, i, j); - pixel |= 0xff000000; /* ARGB */ - data[off++] = pixel; + if(little_endian) + { + uint8_t r = (pixel >> 16) & 0xff; + uint8_t g = (pixel >> 8) & 0xff; + uint8_t b = pixel & 0xff; + + pixel = 0xff << 24 | b << 16 | g << 8 | r; + } + + memcpy(data + off, &pixel, 4); + off += 4; } } @@ -428,29 +462,27 @@ static int x11_grab_screen(const char* x11display, int32_t* data, int32_t x, int * \param y y position to start capture * \param width capture width * \param height capture height - * \return array of ARGB pixels (jint) + * \param output output buffer, screen bytes will be stored in + * \return true if success, false otherwise */ -JNIEXPORT jintArray JNICALL Java_net_java_sip_communicator_impl_neomedia_imgstreaming_NativeScreenCapture_grabScreen - (JNIEnv* env, jclass obj, jint x, jint y, jint width, jint height) +JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_neomedia_imgstreaming_NativeScreenCapture_grabScreen + (JNIEnv* env, jclass obj, jint x, jint y, jint width, jint height, jbyteArray output) { - int32_t* data = NULL; /* jint is always four-bytes signed integer */ - size_t size = width * height; - jintArray ret = NULL; + jint size = width * height * 4; + jbyte* data = NULL; obj = obj; /* not used */ - ret = (*env)->NewIntArray(env, size); - - if(!ret) + if(!output || (*env)->GetArrayLength(env, output) < size) { - return NULL; + return JNI_FALSE; } - data = (*env)->GetIntArrayElements(env, ret, NULL); - + data = (*env)->GetPrimitiveArrayCritical(env, output, 0); + if(!data) { - return NULL; + return JNI_FALSE; } #if defined (_WIN32) || defined(_WIN64) @@ -461,13 +493,11 @@ JNIEXPORT jintArray JNICALL Java_net_java_sip_communicator_impl_neomedia_imgstre if(x11_grab_screen(NULL, data, x, y, width, height) == -1) #endif { - (*env)->ReleaseIntArrayElements(env, ret, data, 0); - return NULL; + (*env)->ReleasePrimitiveArrayCritical(env, output, data, 0); + return JNI_FALSE; } - /* updates array with data's content */ - (*env)->ReleaseIntArrayElements(env, ret, data, 0); - - return ret; + (*env)->ReleasePrimitiveArrayCritical(env, output, data, 0); + return JNI_TRUE; } diff --git a/src/native/screencapture/net_java_sip_communicator_impl_neomedia_imgstreaming_NativeScreenCapture.h b/src/native/screencapture/net_java_sip_communicator_impl_neomedia_imgstreaming_NativeScreenCapture.h index 7c9d4dcde..ed9d8b1ea 100644 --- a/src/native/screencapture/net_java_sip_communicator_impl_neomedia_imgstreaming_NativeScreenCapture.h +++ b/src/native/screencapture/net_java_sip_communicator_impl_neomedia_imgstreaming_NativeScreenCapture.h @@ -10,10 +10,10 @@ extern "C" { /* * Class: net_java_sip_communicator_impl_neomedia_imgstreaming_NativeScreenCapture * Method: grabScreen - * Signature: (IIII)[I + * Signature: (IIII[B)Z */ -JNIEXPORT jintArray JNICALL Java_net_java_sip_communicator_impl_neomedia_imgstreaming_NativeScreenCapture_grabScreen - (JNIEnv *, jclass, jint, jint, jint, jint); +JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_impl_neomedia_imgstreaming_NativeScreenCapture_grabScreen + (JNIEnv *, jclass, jint, jint, jint, jint, jbyteArray); #ifdef __cplusplus } diff --git a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java index 069e91eec..de1341faf 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java @@ -132,13 +132,13 @@ protected DataSource createCaptureDevice() } /* - * FIXME There is no video in calls when using the QuickTime/QTKit - * CaptureDevice so the local video support is disabled for it. + * FIXME Cloning a Desktop streaming capture device no longer works + * since it becames a PullBufferCaptureDevice */ -// if (!QuickTimeAuto.LOCATOR_PROTOCOL.equals(protocol)) + if (!ImageStreamingUtils.LOCATOR_PROTOCOL.equals(protocol)) { - DataSource cloneableDataSource - = Manager.createCloneableDataSource(captureDevice); + DataSource cloneableDataSource = + Manager.createCloneableDataSource(captureDevice); if (cloneableDataSource != null) captureDevice = cloneableDataSource; diff --git a/src/net/java/sip/communicator/impl/neomedia/imgstreaming/DesktopInteract.java b/src/net/java/sip/communicator/impl/neomedia/imgstreaming/DesktopInteract.java index d93a7ce4f..e45f53d40 100644 --- a/src/net/java/sip/communicator/impl/neomedia/imgstreaming/DesktopInteract.java +++ b/src/net/java/sip/communicator/impl/neomedia/imgstreaming/DesktopInteract.java @@ -16,6 +16,40 @@ */ public interface DesktopInteract { + /** + * Capture the full desktop screen using native grabber. + * + * Contrary to other captureScreen method, it only returns raw bytes + * and not BufferedImage. It is done in order to limit + * slow operation such as converting ARGB images (uint32_t) to bytes + * especially for big big screen. For example a 1920x1200 desktop consumes + * 9 MB of memory for grabbing and another 9 MB array for convertion operation. + * + * @param output output buffer to store bytes in. + * Be sure that output length is sufficient + * @return true if success, false if JNI error or output length too short + */ + public boolean captureScreen(byte output[]); + + /** + * Capture a part of the desktop screen using native grabber. + * + * Contrary to other captureScreen method, it only returns raw bytes + * and not BufferedImage. It is done in order to limit + * slow operation such as converting ARGB images (uint32_t) to bytes + * especially for big big screen. For example a 1920x1200 desktop consumes + * 9 MB of memory for grabbing and another 9 MB array for convertion operation. + * + * @param x x position to start capture + * @param y y position to start capture + * @param width capture width + * @param height capture height + * @param output output buffer to store bytes in. + * Be sure that output length is sufficient + * @return true if success, false if JNI error or output length too short + */ + public boolean captureScreen(int x, int y, int width, int height, byte output[]); + /** * Capture the full desktop screen. * diff --git a/src/net/java/sip/communicator/impl/neomedia/imgstreaming/DesktopInteractImpl.java b/src/net/java/sip/communicator/impl/neomedia/imgstreaming/DesktopInteractImpl.java index 2e8628f49..7267038bb 100644 --- a/src/net/java/sip/communicator/impl/neomedia/imgstreaming/DesktopInteractImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/imgstreaming/DesktopInteractImpl.java @@ -48,7 +48,56 @@ public DesktopInteractImpl() throws AWTException, SecurityException } /** - * Capture the full desktop screen. + * Capture the full desktop screen using native grabber. + * + * Contrary to other captureScreen method, it only returns raw bytes + * and not BufferedImage. It is done in order to limit + * slow operation such as converting ARGB images (uint32_t) to bytes + * especially for big big screen. For example a 1920x1200 desktop consumes + * 9 MB of memory for grabbing and another 9 MB array for convertion operation. + * + * @param output output buffer to store bytes in. + * Be sure that output length is sufficient + * @return true if success, false if JNI error or output length too short + */ + public boolean captureScreen(byte output[]) + { + Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); + + return captureScreen(0, 0, (int)dim.getWidth(), (int)dim.getHeight(), output); + } + + /** + * Capture a part of the desktop screen using native grabber. + * + * Contrary to other captureScreen method, it only returns raw bytes + * and not BufferedImage. It is done in order to limit + * slow operation such as converting ARGB images (uint32_t) to bytes + * especially for big big screen. For example a 1920x1200 desktop consumes + * 9 MB of memory for grabbing and another 9 MB array for convertion operation. + * + * @param x x position to start capture + * @param y y position to start capture + * @param width capture width + * @param height capture height + * @param output output buffer to store bytes in. + * Be sure that output length is sufficient + * @return true if success, false if JNI error or output length too short + */ + public boolean captureScreen(int x, int y, int width, int height, byte output[]) + { + if(OSUtils.IS_LINUX || OSUtils.IS_FREEBSD || OSUtils.IS_WINDOWS + || OSUtils.IS_MAC) + { + return NativeScreenCapture.grabScreen( + x, y, width, height, output); + } + + return false; + } + + /** + * Capture the full desktop screen using java.awt.Robot. * * @return BufferedImage of the desktop screen */ @@ -60,7 +109,7 @@ public BufferedImage captureScreen() } /** - * Capture a part of the desktop screen. + * Capture a part of the desktop screen using java.awt.Robot. * * @param x x position to start capture * @param y y position to start capture @@ -72,31 +121,18 @@ public BufferedImage captureScreen() public BufferedImage captureScreen(int x, int y, int width, int height) { BufferedImage img = null; - - if(OSUtils.IS_LINUX || OSUtils.IS_FREEBSD || OSUtils.IS_WINDOWS || OSUtils.IS_MAC) + Rectangle rect = null; + + if(robot == null) { - img = NativeScreenCapture.captureScreen(x, y, width, height); + /* Robot has not been created so abort */ + return null; } - /* in case native screen grabber is not available - * for the current OS or if it has failed, - * fallback to Java AWT Robot - */ - if(img == null) - { - Rectangle rect = null; - - if(robot == null) - { - /* Robot has not been created so abort */ - return null; - } - - logger.info("Begin capture: " + System.nanoTime()); - rect = new Rectangle(x, y, width, height); - img = robot.createScreenCapture(rect); - logger.info("End capture: " + System.nanoTime()); - } + logger.info("Begin capture: " + System.nanoTime()); + rect = new Rectangle(x, y, width, height); + img = robot.createScreenCapture(rect); + logger.info("End capture: " + System.nanoTime()); return img; } diff --git a/src/net/java/sip/communicator/impl/neomedia/imgstreaming/ImageStreamingUtils.java b/src/net/java/sip/communicator/impl/neomedia/imgstreaming/ImageStreamingUtils.java index 4a2c5a13f..78dbf3d91 100644 --- a/src/net/java/sip/communicator/impl/neomedia/imgstreaming/ImageStreamingUtils.java +++ b/src/net/java/sip/communicator/impl/neomedia/imgstreaming/ImageStreamingUtils.java @@ -60,10 +60,12 @@ public static BufferedImage getScaledImage(BufferedImage src, * Get raw bytes from ARGB BufferedImage. * * @param src ARGB + * @param output output buffer, if not null and if its length is at least + * image's (width * height) * 4, method will put bytes in it. * @return raw bytes or null if src is not an ARGB - * BufferedImage + * BufferedImage */ - public static byte[] getImageBytes(BufferedImage src) + public static byte[] getImageBytes(BufferedImage src, byte output[]) { if(src.getType() != BufferedImage.TYPE_INT_ARGB) throw new IllegalArgumentException("src.type"); @@ -71,11 +73,21 @@ public static byte[] getImageBytes(BufferedImage src) WritableRaster raster = src.getRaster(); int width = src.getWidth(); int height = src.getHeight(); - - /* allocate our bytes array */ - byte[] data = new byte[width * height * 4]; + int size = width * height * 4; int off = 0; int pixel[] = new int[4]; + byte data[] = null; + + if(output == null || output.length < size) + { + /* allocate our bytes array */ + data = new byte[size]; + } + else + { + /* use output */ + data = output; + } for(int y = 0 ; y < height ; y++) for(int x = 0 ; x < width ; x++) diff --git a/src/net/java/sip/communicator/impl/neomedia/imgstreaming/NativeScreenCapture.java b/src/net/java/sip/communicator/impl/neomedia/imgstreaming/NativeScreenCapture.java index 9090849cf..66f170ff9 100644 --- a/src/net/java/sip/communicator/impl/neomedia/imgstreaming/NativeScreenCapture.java +++ b/src/net/java/sip/communicator/impl/neomedia/imgstreaming/NativeScreenCapture.java @@ -23,51 +23,14 @@ public class NativeScreenCapture } /** - * Capture desktop screen. + * Grab desktop screen and get raw bytes. * * @param x x position to start capture * @param y y position to start capture * @param width capture width * @param height capture height - * @return BufferedImage of the desktop screen + * @param output output buffer to store screen bytes + * @return true if grab success, false otherwise */ - public static BufferedImage captureScreen(int x, - int y, - int width, - int height) - { - DirectColorModel model - = new DirectColorModel(32, 0xFF0000, 0x00FF00, 0xFF, 0xFF000000); - int masks[] = {0xFF0000, 0xFF00, 0xFF, 0xFF000000}; - WritableRaster raster = null; - DataBufferInt buffer = null; - BufferedImage image = null; - int data[] = null; - - data = grabScreen(x, y, width, height); - - if(data == null) - { - return null; - } - - buffer = new DataBufferInt(data, data.length); - raster - = Raster.createPackedRaster( - buffer, width, height, width, masks, null); - image = new BufferedImage(model, raster, false, null); - - return image; - } - - /** - * Grab desktop screen and get ARGB pixels. - * - * @param x x position to start capture - * @param y y position to start capture - * @param width capture width - * @param height capture height - * @return array of ARGB pixels - */ - private static native int[] grabScreen(int x, int y, int width, int height); + public static native boolean grabScreen(int x, int y, int width, int height, byte output[]); } diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferCaptureDevice.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferCaptureDevice.java new file mode 100644 index 000000000..dd3b4a99e --- /dev/null +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferCaptureDevice.java @@ -0,0 +1,721 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.neomedia.jmfext.media.protocol; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import javax.media.*; +import javax.media.control.*; +import javax.media.protocol.*; + +import net.java.sip.communicator.impl.neomedia.control.*; +import net.java.sip.communicator.util.*; + +/** + * Provides a base implementation of PullBufferDataSource and + * CaptureDevice in order to facilitate implementers by taking care of + * boilerplate in the most common cases. + * + * @author Lubomir Marinov + */ +public abstract class AbstractPullBufferCaptureDevice + extends PullBufferDataSource + implements CaptureDevice +{ + + /** + * The Logger used by the AbstractPullBufferCaptureDevice + * class and its instances for logging output. + */ + private static final Logger logger + = Logger.getLogger(AbstractPullBufferCaptureDevice.class); + + /** + * The value of the formatControls property of + * AbstractPullBufferCaptureDevice which represents an empty array + * of FormatControls. Explicitly defined in order to reduce + * unnecessary allocations. + */ + protected static final FormatControl[] EMPTY_FORMAT_CONTROLS + = new FormatControl[0]; + + /** + * The value of the streams property of + * AbstractPullBufferCaptureDevice which represents an empty array + * of PullBufferStreams. Explicitly defined in order to reduce + * unnecessary allocations. + */ + protected static final PullBufferStream[] EMPTY_STREAMS + = new PullBufferStream[0]; + + /** + * The indicator which determines whether a connection to the media source + * specified by the MediaLocator of this DataSource has + * been opened. + */ + private boolean connected = false; + + /** + * The array of FormatControl instances each one of which can be + * used before {@link #connect()} to get and set the capture Format + * of each one of the capture streams. + */ + private FormatControl[] formatControls; + + /** + * The indicator which determines whether the transfer of media data from + * this DataSource has been started. + */ + private boolean started = false; + + /** + * The PullBufferStreams through which this + * PullBufferDataSource gives access to its media data. + *

+ * Warning: Caution is advised when directly using the field and access to + * it is to be synchronized with sync root this. + *

+ */ + protected AbstractPullBufferStream[] streams; + + /** + * Initializes a new AbstractPullBufferCaptureDevice instance. + */ + protected AbstractPullBufferCaptureDevice() + { + } + + /** + * Initializes a new AbstractPullBufferCaptureDevice instance from + * a specific MediaLocator. + * + * @param locator the MediaLocator to create the new instance from + */ + protected AbstractPullBufferCaptureDevice(MediaLocator locator) + { + setLocator(locator); + } + + /** + * Opens a connection to the media source specified by the + * MediaLocator of this DataSource. + * + * @throws IOException if anything goes wrong while opening the connection + * to the media source specified by the MediaLocator of this + * DataSource + */ + public synchronized void connect() + throws IOException + { + if (!connected) + { + doConnect(); + connected = true; + } + } + + /** + * Creates a new FormatControl instance which is to be associated + * with a PullBufferStream at a specific zero-based index in the + * list of streams of this PullBufferDataSource. As the + * FormatControls of a PullBufferDataSource can be + * requested before {@link #connect()}, its PullBufferStreams may + * not exist at the time of the request for the creation of the + * FormatControl. + * + * @param streamIndex the zero-based index of the PullBufferStream + * in the list of streams of this PullBufferDataSource which is to + * be associated with the new FormatControl instance + * @return a new FormatControl instance which is to be associated + * with a PullBufferStream at the specified streamIndex in + * the list of streams of this PullBufferDataSource + */ + protected FormatControl createFormatControl(final int streamIndex) + { + return + new AbstractFormatControl() + { + /** + * The Format of this FormatControl and, + * respectively, of the media data of its owner. + */ + private Format format; + + /** + * Gets the Format of the media data of the owner of + * this FormatControl. + * + * @return the Format of the media data of the owner of + * this FormatControl + */ + public Format getFormat() + { + format + = AbstractPullBufferCaptureDevice.this + .internalGetFormat(streamIndex, format); + return format; + } + + /** + * Gets the Formats in which the owner of this + * FormatControl is capable of providing media data. + * + * @return an array of Formats in which the owner of + * this FormatControl is capable of providing media + * data + */ + public Format[] getSupportedFormats() + { + return + AbstractPullBufferCaptureDevice.this + .getSupportedFormats(streamIndex); + } + + /** + * Implements {@link FormatControl#setFormat(Format)}. Attempts + * to set the Format in which the owner of this + * FormatControl is to provide media data. + * + * @param format the Format to be set on this instance + * @return the currently set Format after the attempt + * to set it on this instance if format is supported by + * this instance and regardless of whether it was actually set; + * null if format is not supported by this + * instance + */ + @Override + public Format setFormat(Format format) + { + Format setFormat = super.setFormat(format); + + if (setFormat != null) + { + setFormat + = AbstractPullBufferCaptureDevice.this + .internalSetFormat( + streamIndex, + setFormat, + format); + if (setFormat != null) + this.format = setFormat; + } + return setFormat; + } + }; + } + + /** + * Creates the FormatControls of this CaptureDevice. + * + * @return an array of the FormatControls of this + * CaptureDevice + */ + protected FormatControl[] createFormatControls() + { + FormatControl formatControl = createFormatControl(0); + + return + (formatControl == null) + ? EMPTY_FORMAT_CONTROLS + : new FormatControl[] { formatControl }; + } + + /** + * Create a new PullBufferStream which is to be at a specific + * zero-based index in the list of streams of this + * PullBufferDataSource. The Format-related information of + * the new instance is to be abstracted by a specific + * FormatControl. + * + * @param streamIndex the zero-based index of the PullBufferStream + * in the list of streams of this PullBufferDataSource + * @param formatControl the FormatControl which is to abstract the + * Format-related information of the new instance + * @return a new PullBufferStream which is to be at the specified + * streamIndex in the list of streams of this + * PullBufferDataSource and which has its Format-related + * information abstracted by the specified formatControl + */ + protected abstract AbstractPullBufferStream createStream( + int streamIndex, + FormatControl formatControl); + + /** + * Closes the connection to the media source specified by the + * MediaLocator of this DataSource. If such a connection + * has not been opened, the call is ignored. + */ + public synchronized void disconnect() + { + try + { + stop(); + } + catch (IOException ioex) + { + logger.error("Failed to stop " + getClass().getSimpleName(), ioex); + } + + if (connected) + { + doDisconnect(); + connected = false; + } + } + + /** + * Opens a connection to the media source specified by the + * MediaLocator of this DataSource. Allows extenders to + * override and be sure that there will be no request to open a connection + * if the connection has already been opened. + * + * @throws IOException if anything goes wrong while opening the connection + * to the media source specified by the MediaLocator of this + * DataSource + */ + protected synchronized void doConnect() + throws IOException + { + } + + /** + * Closes the connection to the media source specified by the + * MediaLocator of this DataSource. Allows extenders to + * override and be sure that there will be no request to close a connection + * if the connection has not been opened yet. + */ + protected synchronized void doDisconnect() + { + /* + * While it is not clear whether the streams can be released upon + * disconnect, com.imb.media.protocol.SuperCloneableDataSource gets the + * streams of the DataSource it adapts (i.e. this DataSource when + * SourceCloneable support is to be created for it) before #connect(). + * Unfortunately, it means that it isn't clear when the streams are to + * be disposed. + */ +// if (streams != null) +// try +// { +// for (AbstractPullBufferStream stream : streams) +// stream.close(); +// } +// finally +// { +// streams = null; +// } + } + + /** + * Starts the transfer of media data from this DataSource. Allows + * extenders to override and be sure that there will be no request to start + * the transfer of media data if it has already been started. + * + * @throws IOException if anything goes wrong while starting the transfer of + * media data from this DataSource + */ + protected synchronized void doStart() + throws IOException + { + if (streams != null) + for (AbstractPullBufferStream stream : streams) + stream.start(); + } + + /** + * Stops the transfer of media data from this DataSource. Allows + * extenders to override and be sure that there will be no request to stop + * the transfer of media data if it has not been started yet. + * + * @throws IOException if anything goes wrong while stopping the transfer of + * media data from this DataSource + */ + protected synchronized void doStop() + throws IOException + { + if (streams != null) + for (AbstractPullBufferStream stream : streams) + stream.stop(); + } + + /** + * Gets the CaptureDeviceInfo of this CaptureDevice which + * describes it. + * + * @return the CaptureDeviceInfo of this CaptureDevice + * which describes it + */ + public CaptureDeviceInfo getCaptureDeviceInfo() + { + return getCaptureDeviceInfo(this); + } + + /** + * Gets the CaptureDeviceInfo of a specific CaptureDevice + * by locating its registration in JMF using its MediaLocator. + * + * @param captureDevice the CaptureDevice to gets the + * CaptureDeviceInfo of + * @return the CaptureDeviceInfo of the specified + * CaptureDevice as registered in JMF + */ + public static CaptureDeviceInfo getCaptureDeviceInfo( + DataSource captureDevice) + { + /* + * TODO The implemented search for the CaptureDeviceInfo of this + * CaptureDevice by looking for its MediaLocator is inefficient. + */ + @SuppressWarnings("unchecked") + Vector captureDeviceInfos + = (Vector) + CaptureDeviceManager.getDeviceList(null); + MediaLocator locator = captureDevice.getLocator(); + + for (CaptureDeviceInfo captureDeviceInfo : captureDeviceInfos) + if (captureDeviceInfo.getLocator().equals(locator)) + return captureDeviceInfo; + return null; + } + + /** + * Gets the content type of the media represented by this instance. The + * AbstractPullBufferCaptureDevice implementation always returns + * {@link ContentDescriptor#RAW}. + * + * @return the content type of the media represented by this instance + */ + public String getContentType() + { + return ContentDescriptor.RAW; + } + + /** + * Gets the control of the specified type available for this instance. + * + * @param controlType the type of the control available for this instance to + * be retrieved + * @return an Object which represents the control of the specified + * type available for this instance if such a control is indeed available; + * otherwise, null + */ + public Object getControl(String controlType) + { + return AbstractControls.getControl(this, controlType); + } + + /** + * Implements {@link Controls#getControls()}. Gets the controls available + * for this instance. + * + * @return an array of Objects which represent the controls + * available for this instance + */ + public Object[] getControls() + { + FormatControl[] formatControls = internalGetFormatControls(); + + if ((formatControls == null) || (formatControls.length == 0)) + return ControlsAdapter.EMPTY_CONTROLS; + else + { + Object[] controls = new Object[formatControls.length]; + + System.arraycopy( + formatControls, + 0, + controls, + 0, + formatControls.length); + return controls; + } + } + + /** + * Gets the duration of the media represented by this instance. The + * AbstractPullBufferCaptureDevice always returns + * {@link #DURATION_UNBOUNDED}. + * + * @return the duration of the media represented by this instance + */ + public Time getDuration() + { + return DURATION_UNBOUNDED; + } + + /** + * Gets the Format to be reported by the FormatControl of + * a PullBufferStream at a specific zero-based index in the list of + * streams of this PullBufferDataSource. The + * PullBufferStream may not exist at the time of requesting its + * Format. Allows extenders to override the default behavior which + * is to report any last-known format or the first Format from the + * list of supported formats as defined in the JMF registration of this + * CaptureDevice. + * + * @param streamIndex the zero-based index of the PullBufferStream + * the Format of which is to be retrieved + * @param oldValue the last-known Format for the + * PullBufferStream at the specified streamIndex + * @return the Format to be reported by the FormatControl + * of the PullBufferStream at the specified streamIndex in + * the list of streams of this PullBufferDataSource. + */ + protected Format getFormat(int streamIndex, Format oldValue) + { + if (oldValue != null) + return oldValue; + + Format[] supportedFormats = getSupportedFormats(streamIndex); + + return + ((supportedFormats == null) || (supportedFormats.length < 1)) + ? null + : supportedFormats[0]; + } + + /** + * Gets an array of FormatControl instances each one of which can + * be used before {@link #connect()} to get and set the capture + * Format of each one of the capture streams. + * + * @return an array of FormatControl instances each one of which + * can be used before {@link #connect()} to get and set the capture + * Format of each one of the capture streams + */ + public FormatControl[] getFormatControls() + { + return AbstractFormatControl.getFormatControls(this); + } + + /** + * Gets the PullBufferStreams through which this + * PullBufferDataSource gives access to its media data. + * + * @return an array of the PullBufferStreams through which this + * PullBufferDataSource gives access to its media data + */ + public synchronized PullBufferStream[] getStreams() + { + if (streams == null) + { + FormatControl[] formatControls = internalGetFormatControls(); + + if (formatControls != null) + { + int formatControlCount = formatControls.length; + + streams = new AbstractPullBufferStream[formatControlCount]; + for (int i = 0; i < formatControlCount; i++) + streams[i] = createStream(i, formatControls[i]); + + /* + * Start the streams if this DataSource has already been + * started. + */ + if (started) + for (AbstractPullBufferStream stream : streams) + try + { + stream.start(); + } + catch (IOException ioex) + { + throw new UndeclaredThrowableException(ioex); + } + } + } + if (streams == null) + return EMPTY_STREAMS; + else + { + PullBufferStream[] clone = new PullBufferStream[streams.length]; + + System.arraycopy(streams, 0, clone, 0, streams.length); + return clone; + } + } + + /** + * Gets the Formats which are to be reported by a + * FormatControl as supported formats for a + * PullBufferStream at a specific zero-based index in the list of + * streams of this PullBufferDataSource. + * + * @param streamIndex the zero-based index of the PullBufferStream + * for which the specified FormatControl is to report the list of + * supported Formats + * @return an array of Formats to be reported by a + * FormatControl as the supported formats for the + * PullBufferStream at the specified streamIndex in the + * list of streams of this PullBufferDataSource + */ + protected Format[] getSupportedFormats(int streamIndex) + { + CaptureDeviceInfo captureDeviceInfo = getCaptureDeviceInfo(); + + return + (captureDeviceInfo == null) ? null : captureDeviceInfo.getFormats(); + } + + /** + * Gets the Format to be reported by the FormatControl of + * a PullBufferStream at a specific zero-based index in the list of + * streams of this PullBufferDataSource. The + * PullBufferStream may not exist at the time of requesting its + * Format. + * + * @param streamIndex the zero-based index of the PullBufferStream + * the Format of which is to be retrieved + * @param oldValue the last-known Format for the + * PullBufferStream at the specified streamIndex + * @return the Format to be reported by the FormatControl + * of the PullBufferStream at the specified streamIndex in + * the list of streams of this PullBufferDataSource. + */ + private Format internalGetFormat(int streamIndex, Format oldValue) + { + synchronized (this) + { + if (streams != null) + { + AbstractPullBufferStream stream = streams[streamIndex]; + + if (stream != null) + { + Format streamFormat = stream.internalGetFormat(); + + if (streamFormat != null) + return streamFormat; + } + } + } + return getFormat(streamIndex, oldValue); + } + + /** + * Gets an array of FormatControl instances each one of which can + * be used before {@link #connect()} to get and set the capture + * Format of each one of the capture streams. + * + * @return an array of FormatControl instances each one of which + * can be used before {@link #connect()} to get and set the capture + * Format of each one of the capture streams + */ + private synchronized FormatControl[] internalGetFormatControls() + { + if (formatControls == null) + formatControls = createFormatControls(); + return formatControls; + } + + /** + * Attempts to set the Format to be reported by the + * FormatControl of a PullBufferStream at a specific + * zero-based index in the list of streams of this + * PullBufferDataSource. + * + * @param streamIndex the zero-based index of the PullBufferStream + * the Format of which is to be set + * @param oldValue the last-known Format for the + * PullBufferStream at the specified streamIndex + * @param newValue the Format which is to be set + * @return the Format to be reported by the FormatControl + * of the PullBufferStream at the specified streamIndex + * in the list of streams of this PullBufferStream or null + * if the attempt to set the Format did not success and any + * last-known Format is to be left in effect + */ + private Format internalSetFormat( + int streamIndex, + Format oldValue, + Format newValue) + { + synchronized (this) + { + if (streams != null) + { + AbstractPullBufferStream stream = streams[streamIndex]; + + if (stream != null) + return stream.internalSetFormat(newValue); + } + } + return setFormat(streamIndex, oldValue, newValue); + } + + /** + * Attempts to set the Format to be reported by the + * FormatControl of a PullBufferStream at a specific + * zero-based index in the list of streams of this + * PullBufferDataSource. The PullBufferStream does not + * exist at the time of the attempt to set its Format. Allows + * extenders to override the default behavior which is to not attempt to set + * the specified Format so that they can enable setting the + * Format prior to creating the PullBufferStream. If + * setting the Format of an existing PullBufferStream is + * desired, AbstractPullBufferStream#doSetFormat(Format) should be + * overridden instead. + * + * @param streamIndex the zero-based index of the PullBufferStream + * the Format of which is to be set + * @param oldValue the last-known Format for the + * PullBufferStream at the specified streamIndex + * @param newValue the Format which is to be set + * @return the Format to be reported by the FormatControl + * of the PullBufferStream at the specified streamIndex + * in the list of streams of this PullBufferStream or null + * if the attempt to set the Format did not success and any + * last-known Format is to be left in effect + */ + protected Format setFormat( + int streamIndex, + Format oldValue, + Format newValue) + { + return oldValue; + } + + /** + * Starts the transfer of media data from this DataSource + * + * @throws IOException if anything goes wrong while starting the transfer of + * media data from this DataSource + */ + public synchronized void start() + throws IOException + { + if (!started) + { + if (!connected) + throw + new IOException( + getClass().getSimpleName() + " not connected"); + + doStart(); + started = true; + } + } + + /** + * Stops the transfer of media data from this DataSource. + * + * @throws IOException if anything goes wrong while stopping the transfer of + * media data from this DataSource + */ + public synchronized void stop() + throws IOException + { + if (started) + { + doStop(); + started = false; + } + } +} diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferStream.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferStream.java new file mode 100644 index 000000000..d46f40396 --- /dev/null +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferStream.java @@ -0,0 +1,241 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.neomedia.jmfext.media.protocol; + +import java.io.*; + +import javax.media.*; +import javax.media.control.*; +import javax.media.protocol.*; + +import net.java.sip.communicator.impl.neomedia.control.*; +import net.java.sip.communicator.util.*; + +/** + * Provides a base implementation of PullBufferStream in order to + * facilitate implementers by taking care of boilerplate in the most common + * cases. + * + * @author Lubomir Marinov + */ +public abstract class AbstractPullBufferStream + extends AbstractControls + implements PullBufferStream +{ + + /** + * The Logger used by the AbstractPullBufferStream class + * and its instances for logging output. + */ + private static final Logger logger + = Logger.getLogger(AbstractPullBufferStream.class); + + /** + * The (default) ContentDescriptor of the + * AbstractPullBufferStream instances. + */ + private static final ContentDescriptor CONTENT_DESCRIPTOR + = new ContentDescriptor(ContentDescriptor.RAW); + + /** + * The FormatControl which gives access to the Format of + * the media data provided by this SourceStream and which, + * optionally, allows setting it. + */ + private final FormatControl formatControl; + + /** + * Initializes a new AbstractPullBufferStream instance which is to + * have its Format-related information abstracted by a specific + * FormatControl. + * + * @param formatControl the FormatControl which is to abstract the + * Format-related information of the new instance + */ + protected AbstractPullBufferStream(FormatControl formatControl) + { + this.formatControl = formatControl; + } + + /** + * Releases the resources used by this instance throughout its existence and + * makes it available for garbage collection. This instance is considered + * unusable after closing. + */ + public void close() + { + try + { + stop(); + } + catch (IOException ioex) + { + logger.error("Failed to stop " + getClass().getSimpleName(), ioex); + } + } + + /** + * Gets the Format of this PullBufferStream as directly + * known by it. Allows extenders to override the Format known to + * the PullBufferDataSource which created this instance and + * possibly provide more details on the currently set Format. + * + * @return the Format of this PullBufferStream as directly + * known by it or null if this PullBufferStream does not + * directly know its Format and it relies on the + * PullBufferDataSource which created it to report its + * Format + */ + protected Format doGetFormat() + { + return null; + } + + /** + * Attempts to set the Format of this PullBufferStream. + * Allows extenders to enable setting the Format of an existing + * PullBufferStream (in contract to setting it before the + * PullBufferStream is created by the PullBufferDataSource + * which will provide it). + * + * @param format the Format to be set as the format of this + * PullBufferStream + * @return the Format of this PullBufferStream or + * null if the attempt to set the Format did not succeed + * and any last-known Format is to be left in effect + */ + protected Format doSetFormat(Format format) + { + return null; + } + + /** + * Determines whether the end of this SourceStream has been + * reached. The AbstractPullBufferStream implementation always + * returns false. + * + * @return true if the end of this SourceStream has been + * reached; otherwise, false + */ + public boolean endOfStream() + { + return false; + } + + /** + * Determines if read will block. + * + * @return true if read block, false otherwise + */ + public boolean willReadBlock() + { + return true; + } + + /** + * Gets a ContentDescriptor which describes the type of the content + * made available by this SourceStream. The + * AbstractPullBufferStream implementation always returns a + * ContentDescriptor with content type equal to + * ContentDescriptor#RAW. + * + * @return a ContentDescriptor which describes the type of the + * content made available by this SourceStream + */ + public ContentDescriptor getContentDescriptor() + { + return CONTENT_DESCRIPTOR; + } + + /** + * Gets the length in bytes of the content made available by this + * SourceStream. The AbstractPullBufferStream + * implementation always returns LENGTH_UNKNOWN. + * + * @return the length in bytes of the content made available by this + * SourceStream if it is known; otherwise, LENGTH_UKNOWN + */ + public long getContentLength() + { + return LENGTH_UNKNOWN; + } + + /** + * Implements {@link Controls#getControls()}. Gets the controls available + * for this instance. + * + * @return an array of Objects which represent the controls + * available for this instance + */ + public Object[] getControls() + { + if (formatControl != null) + return new Object[] { formatControl }; + else + return ControlsAdapter.EMPTY_CONTROLS; + } + + /** + * Gets the Format of the media data made available by this + * PullBufferStream. + * + * @return the Format of the media data made available by this + * PullBufferStream + */ + public Format getFormat() + { + return (formatControl == null) ? null : formatControl.getFormat(); + } + + /** + * Gets the Format of this PullBufferStream as directly + * known by it. + * + * @return the Format of this PullBufferStream as directly + * known by it + */ + Format internalGetFormat() + { + return doGetFormat(); + } + + /** + * Attempts to set the Format of this PullBufferStream. + * + * @param format the Format to be set as the format of this + * PullBufferStream + * @return the Format of this PullBufferStream or + * null if the attempt to set the Format did not succeed + * and any last-known Format is to be left in effect + */ + Format internalSetFormat(Format format) + { + return doSetFormat(format); + } + + /** + * Starts the transfer of media data from this PullBufferStream. + * + * @throws IOException if anything goes wrong while starting the transfer of + * media data from this PullBufferStream + */ + public void start() + throws IOException + { + } + + /** + * Stops the transfer of media data from this PullBufferStream. + * + * @throws IOException if anything goes wrong while stopping the transfer of + * media data from this PullBufferStream + */ + public void stop() + throws IOException + { + } +} diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/DataSource.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/DataSource.java index 222b3bd48..271b671fb 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/DataSource.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/DataSource.java @@ -23,7 +23,7 @@ * @author Damian Minkov */ public class DataSource - extends AbstractPushBufferCaptureDevice + extends AbstractPullBufferCaptureDevice { /** @@ -61,23 +61,23 @@ public DataSource(MediaLocator locator) } /** - * Create a new PushBufferStream which is to be at a specific + * Create a new PullBufferStream which is to be at a specific * zero-based index in the list of streams of this - * PushBufferDataSource. The Format-related information of + * PullBufferDataSource. The Format-related information of * the new instance is to be abstracted by a specific * FormatControl. * - * @param streamIndex the zero-based index of the PushBufferStream - * in the list of streams of this PushBufferDataSource + * @param streamIndex the zero-based index of the PullBufferStream + * in the list of streams of this PullBufferDataSource * @param formatControl the FormatControl which is to abstract the * Format-related information of the new instance - * @return a new PushBufferStream which is to be at the specified + * @return a new PullBufferStream which is to be at the specified * streamIndex in the list of streams of this - * PushBufferDataSource and which has its Format-related + * PullBufferDataSource and which has its Format-related * information abstracted by the specified formatControl - * @see AbstractPushBufferCaptureDevice#createStream(int, FormatControl) + * @see AbstractPullBufferCaptureDevice#createStream(int, FormatControl) */ - protected AbstractPushBufferStream createStream( + protected AbstractPullBufferStream createStream( int streamIndex, FormatControl formatControl) { diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/ImageStream.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/ImageStream.java index b7f1b8581..bf270927f 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/ImageStream.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/ImageStream.java @@ -29,8 +29,7 @@ * @author Damian Minkov */ public class ImageStream - extends AbstractPushBufferStream - implements Runnable + extends AbstractPullBufferStream { /** * The Logger @@ -42,11 +41,6 @@ public class ImageStream */ private long seqNo = 0; - /** - * Capture thread reference. - */ - private Thread captureThread = null; - /** * If stream is started or not. */ @@ -83,164 +77,162 @@ public class ImageStream public void read(Buffer buffer) throws IOException { - synchronized(buf) + //System.out.println(System.currentTimeMillis()); + byte data[] = (byte[])buffer.getData(); + int dataLength = (data != null) ? data.length : 0; + long begin = System.currentTimeMillis(); + /* maximum time allowed for a capture to respect frame rate */ + long maxTime = 1000 / 10; + int wait = 0; + + if((data != null) || (dataLength != 0)) { - try + byte buf[] = readScreen(data); + + if(buf != data) + { + /* readScreen returns us a different buffer than JMF ones, + * it means that JMF's initial buffer was too short. + */ + //System.out.println("use our own buffer"); + buffer.setData(buf); + } + + buffer.setOffset(0); + buffer.setLength(buf.length); + buffer.setFormat(getFormat()); + buffer.setHeader(null); + buffer.setTimeStamp(System.nanoTime()); + buffer.setSequenceNumber(seqNo); + buffer.setFlags(Buffer.FLAG_SYSTEM_TIME | Buffer.FLAG_LIVE_DATA); + seqNo++; + } + + wait = (int)(maxTime - (System.currentTimeMillis() - begin)); + + try + { + /* sleep to respect as much as possible the + * frame rate + */ + if(wait > 0) { - Object bufData = buf.getData(); - int bufLength = buf.getLength(); - - if ((bufData != null) || (bufLength != 0)) - { - buffer.setData(bufData); - buffer.setOffset(0); - buffer.setLength(bufLength); - buffer.setFormat(buf.getFormat()); - buffer.setHeader(null); - buffer.setTimeStamp(buf.getTimeStamp()); - buffer.setSequenceNumber(buf.getSequenceNumber()); - buffer.setFlags(buf.getFlags()); - - /* clear buf so JMF will not get twice the same image */ - buf.setData(null); - buf.setLength(0); - } + Thread.sleep(wait); } - catch (Exception e) + else { + /* yield a little bit to not use all the + * CPU + */ + Thread.yield(); } } + catch(Exception e) + { + } } /** * Start desktop capture stream. * - * @see AbstractPushBufferStream#start() + * @see AbstractPullBufferStream#start() */ @Override public void start() { - if(captureThread == null || !captureThread.isAlive()) + if(desktopInteract == null) { - logger.info("Start stream"); - captureThread = new Thread(this); - - /* - * Set the started indicator before calling Thread#start() because - * the Thread may exist upon start if Thread#run() starts executing - * before setting the started indicator. - */ - started = true; - captureThread.start(); + try + { + desktopInteract = new DesktopInteractImpl(); + } + catch(Exception e) + { + logger.warn("Cannot create DesktopInteract object!"); + started = false; + return; + } } + + started = true; } /** * Stop desktop capture stream. * - * @see AbstractPushBufferStream#stop() + * @see AbstractPullBufferStream#stop() */ @Override public void stop() { logger.info("Stop stream"); started = false; - captureThread = null; } /** - * Thread entry point. + * Read screen. + * + * @param output output buffer for screen bytes + * @return raw bytes, it could be equal to output or not. Take care in the caller + * to check if output is the returned value. */ - public void run() + public byte[] readScreen(byte output[]) { VideoFormat format = (VideoFormat) getFormat(); Dimension formatSize = format.getSize(); int width = (int) formatSize.getWidth(); int height = (int) formatSize.getHeight(); - - if(desktopInteract == null) + BufferedImage scaledScreen = null; + BufferedImage screen = null; + byte data[] = null; + int size = width * height * 4; + + /* check if output buffer can hold all the screen + * if not allocate our own buffer + */ + if(output.length < size) { - try - { - desktopInteract = new DesktopInteractImpl(); - } - catch(Exception e) - { - logger.warn("Cannot create DesktopInteract object!"); - started = false; - return; - } + output = null; + output = new byte[size]; } - while(started) + /* get desktop screen via native grabber if available */ + if(desktopInteract.captureScreen(output)) { - byte data[] = null; - BufferedImage scaledScreen = null; - BufferedImage screen = null; - - /* get desktop screen and resize it */ - screen = desktopInteract.captureScreen(); + return output; + } - if(screen.getType() == BufferedImage.TYPE_INT_ARGB) - { - /* with our native screencapture we - * automatically create BufferedImage in - * ARGB format so no need to rescale/convert - * to ARGB - */ - scaledScreen = screen; - } - else - { - /* convert to ARGB BufferedImage */ - scaledScreen - = ImageStreamingUtils - .getScaledImage( - screen, - width, - height, - BufferedImage.TYPE_INT_ARGB); - } + System.out.println("failed to grab with native! " + output.length); + + /* OK native grabber failed or is not available, + * try with AWT Robot and convert it to the right format + * + * Note that it is very memory consuming since memory are allocated + * to capture screen (via Robot) and then for converting to raw bytes + * + * Normally not of our supported platform (Windows (x86, x64), + * Linux (x86, x86-64), Mac OS X (i386, x86-64, ppc) and + * FreeBSD (x86, x86-64) should go here. + */ + screen = desktopInteract.captureScreen(); + + if(screen != null) + { + /* convert to ARGB BufferedImage */ + scaledScreen + = ImageStreamingUtils + .getScaledImage( + screen, + width, + height, + BufferedImage.TYPE_INT_ARGB); /* get raw bytes */ - data = ImageStreamingUtils.getImageBytes(scaledScreen); - - /* notify JMF that new data is available */ - synchronized (buf) - { - buf.setData(data); - buf.setOffset(0); - buf.setLength(data.length); - buf.setFormat(format); - buf.setHeader(null); - buf.setTimeStamp(System.nanoTime()); - buf.setSequenceNumber(seqNo++); - buf.setFlags(Buffer.FLAG_LIVE_DATA | Buffer.FLAG_SYSTEM_TIME); - } - - /* pass to JMF handler */ - BufferTransferHandler transferHandler = this.transferHandler; - - if(transferHandler != null) - { - transferHandler.transferData(this); - Thread.yield(); - } - - /* cleanup */ - screen = null; - scaledScreen = null; - data = null; - - try - { - /* 100 ms */ - Thread.sleep(100); - } - catch(InterruptedException e) - { - /* do nothing */ - } + data = ImageStreamingUtils.getImageBytes(scaledScreen, output); } + + screen = null; + scaledScreen = null; + return data; } }