mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-07 16:30:00 +01:00
New ScreenCapture separate from Device, with Window selection
For now only LINUX support for window selection. New icons for Loopback and for ScreenCapture. Important BugFix on DeviceSource and Device management.
This commit is contained in:
@@ -89,6 +89,11 @@ if(UNIX)
|
||||
find_package(GTK 3.0 REQUIRED)
|
||||
macro_log_feature(GTK_FOUND "GTK" "GTK cross-platform widget toolkit" "http://www.gtk.org" TRUE)
|
||||
|
||||
find_package(X11 REQUIRED COMPONENTS xcb)
|
||||
include_directories(
|
||||
${X11_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
endif()
|
||||
add_definitions(-DUNIX)
|
||||
elseif(WIN32)
|
||||
|
||||
Binary file not shown.
@@ -1,40 +1,32 @@
|
||||
ply
|
||||
format ascii 1.0
|
||||
comment Created by Blender 2.91.2 - www.blender.org
|
||||
element vertex 14
|
||||
comment Created by Blender 2.90.1 - www.blender.org
|
||||
element vertex 12
|
||||
property float x
|
||||
property float y
|
||||
property float z
|
||||
element face 16
|
||||
element face 10
|
||||
property list uchar uint vertex_indices
|
||||
end_header
|
||||
-0.100035 0.057779 0.000000
|
||||
-0.002114 0.090894 0.000000
|
||||
-0.002069 0.111204 0.000000
|
||||
0.095897 0.057200 0.000000
|
||||
-0.072133 0.052704 0.000000
|
||||
0.068377 0.052027 0.000000
|
||||
-0.100035 -0.050237 0.000000
|
||||
0.078085 0.037024 0.000000
|
||||
0.095897 -0.050237 0.000000
|
||||
-0.002025 0.014469 0.000000
|
||||
0.006837 -0.000988 0.000000
|
||||
0.078085 -0.039552 0.000000
|
||||
0.006837 -0.077564 0.000000
|
||||
-0.002069 -0.102495 0.000000
|
||||
-0.120212 -0.072047 0.000000
|
||||
-0.101239 0.089191 0.000000
|
||||
-0.120212 0.108160 0.000000
|
||||
0.119230 0.108160 0.000000
|
||||
0.100257 0.089191 0.000000
|
||||
0.119230 -0.072047 0.000000
|
||||
-0.101239 -0.053078 0.000000
|
||||
0.100257 -0.053078 0.000000
|
||||
-0.057945 -0.110557 0.000000
|
||||
0.031238 -0.082104 0.000000
|
||||
-0.033289 -0.082104 0.000000
|
||||
0.055894 -0.110557 0.000000
|
||||
3 0 1 2
|
||||
3 1 3 2
|
||||
3 0 4 1
|
||||
3 5 3 1
|
||||
3 6 4 0
|
||||
3 5 7 3
|
||||
3 7 8 3
|
||||
3 6 9 4
|
||||
3 9 7 5
|
||||
3 9 10 7
|
||||
3 11 8 7
|
||||
3 6 10 9
|
||||
3 6 12 10
|
||||
3 12 8 11
|
||||
3 13 12 6
|
||||
3 12 13 8
|
||||
3 1 4 3
|
||||
3 4 5 3
|
||||
3 0 6 1
|
||||
3 7 5 4
|
||||
3 0 7 6
|
||||
3 0 5 7
|
||||
3 8 9 10
|
||||
3 8 11 9
|
||||
|
||||
@@ -1,32 +1,788 @@
|
||||
ply
|
||||
format ascii 1.0
|
||||
comment Created by Blender 2.90.1 - www.blender.org
|
||||
element vertex 12
|
||||
comment Created by Blender 3.4.1 - www.blender.org
|
||||
element vertex 391
|
||||
property float x
|
||||
property float y
|
||||
property float z
|
||||
element face 10
|
||||
element face 387
|
||||
property list uchar uint vertex_indices
|
||||
end_header
|
||||
-0.120212 -0.072047 0.000000
|
||||
-0.101239 0.089191 0.000000
|
||||
-0.120212 0.108160 0.000000
|
||||
0.119230 0.108160 0.000000
|
||||
0.100257 0.089191 0.000000
|
||||
0.119230 -0.072047 0.000000
|
||||
-0.101239 -0.053078 0.000000
|
||||
0.100257 -0.053078 0.000000
|
||||
-0.057945 -0.110557 0.000000
|
||||
0.031238 -0.082104 0.000000
|
||||
-0.033289 -0.082104 0.000000
|
||||
0.055894 -0.110557 0.000000
|
||||
-0.074560 0.075085 0.000000
|
||||
-0.072225 0.075085 0.000000
|
||||
-0.073393 0.075159 0.000000
|
||||
-0.075712 0.074862 0.000000
|
||||
-0.071073 0.074862 0.000000
|
||||
-0.076833 0.074490 0.000000
|
||||
-0.069952 0.074490 0.000000
|
||||
-0.077908 0.073969 0.000000
|
||||
-0.068877 0.073969 0.000000
|
||||
-0.078921 0.073300 0.000000
|
||||
-0.067864 0.073300 0.000000
|
||||
-0.079857 0.072482 0.000000
|
||||
-0.066927 0.072482 0.000000
|
||||
-0.080612 0.071727 0.000000
|
||||
-0.066173 0.071727 0.000000
|
||||
-0.064086 0.069641 0.000000
|
||||
-0.082699 0.069641 0.000000
|
||||
-0.060934 0.066490 0.000000
|
||||
-0.085851 0.066490 0.000000
|
||||
-0.056983 0.062540 0.000000
|
||||
-0.089802 0.062540 0.000000
|
||||
-0.052499 0.058057 0.000000
|
||||
-0.094286 0.058057 0.000000
|
||||
-0.018788 0.061512 0.000000
|
||||
-0.015014 0.061709 0.000000
|
||||
-0.016840 0.061709 0.000000
|
||||
-0.009967 0.061709 0.000000
|
||||
-0.002343 0.061709 0.000000
|
||||
0.007215 0.061709 0.000000
|
||||
0.018060 0.061709 0.000000
|
||||
0.029551 0.061709 0.000000
|
||||
0.041041 0.061709 0.000000
|
||||
0.051887 0.061709 0.000000
|
||||
0.061444 0.061709 0.000000
|
||||
0.069068 0.061709 0.000000
|
||||
0.074115 0.061709 0.000000
|
||||
0.075941 0.061709 0.000000
|
||||
0.077182 0.061626 0.000000
|
||||
0.078372 0.061383 0.000000
|
||||
-0.020547 0.060953 0.000000
|
||||
0.079500 0.060991 0.000000
|
||||
0.080556 0.060461 0.000000
|
||||
-0.022096 0.060081 0.000000
|
||||
0.081528 0.059805 0.000000
|
||||
-0.023416 0.058943 0.000000
|
||||
0.082406 0.059032 0.000000
|
||||
0.083179 0.058154 0.000000
|
||||
-0.024486 0.057589 0.000000
|
||||
0.083836 0.057182 0.000000
|
||||
-0.047749 0.053308 0.000000
|
||||
-0.099036 0.053308 0.000000
|
||||
-0.025287 0.056066 0.000000
|
||||
0.084365 0.056126 0.000000
|
||||
0.084757 0.054998 0.000000
|
||||
-0.025798 0.054424 0.000000
|
||||
0.085000 0.053809 0.000000
|
||||
-0.025999 0.052710 0.000000
|
||||
0.085084 0.052568 0.000000
|
||||
-0.042999 0.048559 0.000000
|
||||
-0.103786 0.048558 0.000000
|
||||
-0.025870 0.050972 0.000000
|
||||
0.085084 0.050904 0.000000
|
||||
-0.025392 0.049260 0.000000
|
||||
0.085084 0.046302 0.000000
|
||||
-0.024543 0.047621 0.000000
|
||||
-0.038515 0.044076 0.000000
|
||||
-0.108270 0.044075 0.000000
|
||||
-0.023305 0.046104 0.000000
|
||||
0.085084 0.039350 0.000000
|
||||
-0.023185 0.045985 0.000000
|
||||
-0.022853 0.045653 0.000000
|
||||
-0.022353 0.045152 0.000000
|
||||
-0.021725 0.044525 0.000000
|
||||
-0.021012 0.043812 0.000000
|
||||
-0.034564 0.040126 0.000000
|
||||
-0.112221 0.040125 0.000000
|
||||
-0.020257 0.043057 0.000000
|
||||
-0.019502 0.042303 0.000000
|
||||
-0.018790 0.041590 0.000000
|
||||
-0.018162 0.040963 0.000000
|
||||
-0.017661 0.040462 0.000000
|
||||
-0.017330 0.040130 0.000000
|
||||
-0.017210 0.040010 0.000000
|
||||
-0.031412 0.036974 0.000000
|
||||
-0.115373 0.036974 0.000000
|
||||
-0.016769 0.039598 0.000000
|
||||
-0.016304 0.039217 0.000000
|
||||
0.060703 0.037334 0.000000
|
||||
0.085084 0.030635 0.000000
|
||||
-0.015817 0.038869 0.000000
|
||||
-0.015310 0.038555 0.000000
|
||||
-0.014785 0.038275 0.000000
|
||||
-0.014243 0.038030 0.000000
|
||||
-0.013687 0.037820 0.000000
|
||||
-0.013117 0.037647 0.000000
|
||||
-0.012536 0.037511 0.000000
|
||||
-0.011946 0.037413 0.000000
|
||||
-0.011348 0.037354 0.000000
|
||||
-0.010745 0.037334 0.000000
|
||||
0.060703 -0.032030 0.000000
|
||||
-0.029326 0.034888 0.000000
|
||||
-0.117460 0.034888 0.000000
|
||||
-0.028571 0.034134 0.000000
|
||||
-0.118214 0.034133 0.000000
|
||||
-0.061202 0.033570 0.000000
|
||||
-0.027753 0.033198 0.000000
|
||||
-0.119033 0.033197 0.000000
|
||||
-0.085583 0.033570 0.000000
|
||||
-0.061202 -0.035794 0.000000
|
||||
-0.045806 0.017272 0.000000
|
||||
-0.085886 0.033249 0.000000
|
||||
-0.085583 -0.051029 0.000000
|
||||
-0.086724 0.032362 0.000000
|
||||
-0.027083 0.032185 0.000000
|
||||
-0.119702 0.032184 0.000000
|
||||
-0.087989 0.031023 0.000000
|
||||
-0.026562 0.031110 0.000000
|
||||
-0.120223 0.031109 0.000000
|
||||
-0.026190 0.029989 0.000000
|
||||
-0.120595 0.029988 0.000000
|
||||
-0.089575 0.029344 0.000000
|
||||
0.085084 0.020746 0.000000
|
||||
-0.025967 0.028838 0.000000
|
||||
-0.120818 0.028837 0.000000
|
||||
-0.091375 0.027439 0.000000
|
||||
-0.025893 0.027670 0.000000
|
||||
-0.120892 0.027670 0.000000
|
||||
-0.025967 0.026503 0.000000
|
||||
-0.120818 0.026502 0.000000
|
||||
-0.093281 0.025420 0.000000
|
||||
-0.026190 0.025352 0.000000
|
||||
-0.120595 0.025351 0.000000
|
||||
-0.095188 0.023402 0.000000
|
||||
-0.026562 0.024231 0.000000
|
||||
-0.120223 0.024230 0.000000
|
||||
-0.027083 0.023156 0.000000
|
||||
-0.119702 0.023155 0.000000
|
||||
-0.096988 0.021497 0.000000
|
||||
-0.027753 0.022143 0.000000
|
||||
-0.119033 0.022142 0.000000
|
||||
-0.028571 0.021207 0.000000
|
||||
-0.118214 0.021206 0.000000
|
||||
-0.098574 0.019818 0.000000
|
||||
-0.028652 0.021126 0.000000
|
||||
-0.118133 0.021125 0.000000
|
||||
-0.028876 0.020901 0.000000
|
||||
-0.117909 0.020901 0.000000
|
||||
-0.029215 0.020563 0.000000
|
||||
-0.117570 0.020562 0.000000
|
||||
0.085084 0.010269 0.000000
|
||||
-0.029640 0.020138 0.000000
|
||||
-0.117146 0.020137 0.000000
|
||||
-0.030122 0.019656 0.000000
|
||||
-0.116664 0.019656 0.000000
|
||||
-0.099839 0.018478 0.000000
|
||||
-0.030632 0.019146 0.000000
|
||||
-0.116153 0.019145 0.000000
|
||||
-0.031143 0.018635 0.000000
|
||||
-0.115643 0.018635 0.000000
|
||||
-0.031625 0.018153 0.000000
|
||||
-0.115161 0.018153 0.000000
|
||||
-0.100677 0.017592 0.000000
|
||||
-0.032049 0.017729 0.000000
|
||||
-0.114736 0.017728 0.000000
|
||||
-0.032388 0.017390 0.000000
|
||||
-0.114397 0.017390 0.000000
|
||||
-0.100980 0.017271 0.000000
|
||||
-0.032612 0.017166 0.000000
|
||||
-0.114173 0.017165 0.000000
|
||||
-0.044874 0.016412 0.000000
|
||||
-0.101911 0.016411 0.000000
|
||||
-0.032693 0.017085 0.000000
|
||||
-0.114092 0.017084 0.000000
|
||||
-0.033649 0.016252 0.000000
|
||||
-0.113136 0.016251 0.000000
|
||||
-0.043859 0.015705 0.000000
|
||||
-0.102926 0.015704 0.000000
|
||||
-0.034684 0.015574 0.000000
|
||||
-0.112101 0.015574 0.000000
|
||||
-0.042777 0.015151 0.000000
|
||||
-0.104009 0.015150 0.000000
|
||||
-0.035782 0.015051 0.000000
|
||||
-0.111004 0.015050 0.000000
|
||||
-0.041643 0.014750 0.000000
|
||||
-0.105142 0.014749 0.000000
|
||||
-0.036926 0.014682 0.000000
|
||||
-0.109859 0.014682 0.000000
|
||||
-0.040475 0.014502 0.000000
|
||||
-0.106310 0.014502 0.000000
|
||||
-0.038100 0.014468 0.000000
|
||||
-0.108685 0.014468 0.000000
|
||||
-0.039289 0.014408 0.000000
|
||||
-0.107496 0.014408 0.000000
|
||||
0.085084 -0.000208 0.000000
|
||||
0.085084 -0.010097 0.000000
|
||||
0.085084 -0.018812 0.000000
|
||||
0.037601 -0.012928 0.000000
|
||||
0.039977 -0.012962 0.000000
|
||||
0.038790 -0.012868 0.000000
|
||||
0.105810 -0.012963 0.000000
|
||||
0.108185 -0.012929 0.000000
|
||||
0.106997 -0.012869 0.000000
|
||||
0.036427 -0.013142 0.000000
|
||||
0.109360 -0.013143 0.000000
|
||||
0.041144 -0.013210 0.000000
|
||||
0.104642 -0.013210 0.000000
|
||||
0.035283 -0.013511 0.000000
|
||||
0.110504 -0.013511 0.000000
|
||||
0.042278 -0.013611 0.000000
|
||||
0.103509 -0.013611 0.000000
|
||||
0.034185 -0.014034 0.000000
|
||||
0.111602 -0.014034 0.000000
|
||||
0.043360 -0.014165 0.000000
|
||||
0.102427 -0.014165 0.000000
|
||||
0.033150 -0.014712 0.000000
|
||||
0.112637 -0.014712 0.000000
|
||||
0.044375 -0.014872 0.000000
|
||||
0.101412 -0.014872 0.000000
|
||||
0.032195 -0.015545 0.000000
|
||||
0.113592 -0.015545 0.000000
|
||||
0.045307 -0.015732 0.000000
|
||||
0.100480 -0.015732 0.000000
|
||||
0.032113 -0.015626 0.000000
|
||||
0.113674 -0.015626 0.000000
|
||||
0.031889 -0.015850 0.000000
|
||||
0.113898 -0.015851 0.000000
|
||||
0.045610 -0.016052 0.000000
|
||||
0.085084 -0.032030 0.000000
|
||||
0.031550 -0.016189 0.000000
|
||||
0.114237 -0.016189 0.000000
|
||||
0.046447 -0.016939 0.000000
|
||||
0.031126 -0.016613 0.000000
|
||||
0.114661 -0.016614 0.000000
|
||||
0.030644 -0.017095 0.000000
|
||||
0.115143 -0.017096 0.000000
|
||||
0.047712 -0.018278 0.000000
|
||||
0.030133 -0.017606 0.000000
|
||||
0.115654 -0.017606 0.000000
|
||||
0.029622 -0.018116 0.000000
|
||||
0.116165 -0.018117 0.000000
|
||||
0.029140 -0.018598 0.000000
|
||||
0.116647 -0.018599 0.000000
|
||||
0.049298 -0.019957 0.000000
|
||||
0.028716 -0.019023 0.000000
|
||||
0.117071 -0.019023 0.000000
|
||||
0.085084 -0.025764 0.000000
|
||||
0.028377 -0.019362 0.000000
|
||||
0.117410 -0.019362 0.000000
|
||||
0.028153 -0.019586 0.000000
|
||||
0.117634 -0.019586 0.000000
|
||||
0.028072 -0.019667 0.000000
|
||||
0.117716 -0.019667 0.000000
|
||||
0.027253 -0.020603 0.000000
|
||||
0.118534 -0.020603 0.000000
|
||||
0.051098 -0.021862 0.000000
|
||||
0.026584 -0.021616 0.000000
|
||||
0.119203 -0.021617 0.000000
|
||||
0.026063 -0.022691 0.000000
|
||||
0.119724 -0.022691 0.000000
|
||||
0.053005 -0.023881 0.000000
|
||||
0.025691 -0.023812 0.000000
|
||||
0.120096 -0.023812 0.000000
|
||||
0.025468 -0.024963 0.000000
|
||||
0.120319 -0.024964 0.000000
|
||||
0.054911 -0.025899 0.000000
|
||||
0.025394 -0.026130 0.000000
|
||||
0.120393 -0.026131 0.000000
|
||||
0.085084 -0.030366 0.000000
|
||||
0.056711 -0.027804 0.000000
|
||||
0.025468 -0.027297 0.000000
|
||||
0.120319 -0.027298 0.000000
|
||||
0.025691 -0.028449 0.000000
|
||||
0.120096 -0.028450 0.000000
|
||||
0.058297 -0.029483 0.000000
|
||||
0.026063 -0.029570 0.000000
|
||||
0.119724 -0.029571 0.000000
|
||||
0.059562 -0.030823 0.000000
|
||||
0.026584 -0.030645 0.000000
|
||||
0.119203 -0.030646 0.000000
|
||||
0.027253 -0.031658 0.000000
|
||||
0.118533 -0.031659 0.000000
|
||||
0.060400 -0.031709 0.000000
|
||||
0.028072 -0.032594 0.000000
|
||||
0.117715 -0.032594 0.000000
|
||||
0.117715 -0.032594 0.000000
|
||||
0.117715 -0.032594 0.000000
|
||||
0.117715 -0.032594 0.000000
|
||||
0.117715 -0.032594 0.000000
|
||||
0.117715 -0.032594 0.000000
|
||||
0.117715 -0.032594 0.000000
|
||||
0.117715 -0.032594 0.000000
|
||||
0.117715 -0.032595 0.000000
|
||||
0.117715 -0.032595 0.000000
|
||||
0.117715 -0.032595 0.000000
|
||||
0.117715 -0.032595 0.000000
|
||||
0.117715 -0.032595 0.000000
|
||||
0.028826 -0.033349 0.000000
|
||||
0.079359 -0.070943 0.000000
|
||||
0.030913 -0.035435 0.000000
|
||||
0.034065 -0.038586 0.000000
|
||||
-0.059796 -0.035794 0.000000
|
||||
-0.055910 -0.035794 0.000000
|
||||
-0.050039 -0.035794 0.000000
|
||||
-0.042679 -0.035794 0.000000
|
||||
-0.034327 -0.035794 0.000000
|
||||
-0.025479 -0.035794 0.000000
|
||||
-0.016630 -0.035794 0.000000
|
||||
-0.008278 -0.035794 0.000000
|
||||
-0.000919 -0.035794 0.000000
|
||||
0.004953 -0.035794 0.000000
|
||||
0.008839 -0.035794 0.000000
|
||||
0.010245 -0.035794 0.000000
|
||||
0.010849 -0.035814 0.000000
|
||||
0.011447 -0.035873 0.000000
|
||||
0.012037 -0.035971 0.000000
|
||||
0.012618 -0.036107 0.000000
|
||||
0.013187 -0.036280 0.000000
|
||||
0.013744 -0.036490 0.000000
|
||||
0.014286 -0.036735 0.000000
|
||||
0.014811 -0.037015 0.000000
|
||||
0.015317 -0.037330 0.000000
|
||||
0.015804 -0.037678 0.000000
|
||||
0.016269 -0.038059 0.000000
|
||||
0.016710 -0.038471 0.000000
|
||||
0.016830 -0.038591 0.000000
|
||||
0.038016 -0.042536 0.000000
|
||||
0.017162 -0.038923 0.000000
|
||||
0.017662 -0.039424 0.000000
|
||||
0.018290 -0.040051 0.000000
|
||||
0.019003 -0.040764 0.000000
|
||||
0.019758 -0.041518 0.000000
|
||||
0.020513 -0.042273 0.000000
|
||||
0.021225 -0.042986 0.000000
|
||||
0.042500 -0.047019 0.000000
|
||||
0.021853 -0.043613 0.000000
|
||||
0.022354 -0.044114 0.000000
|
||||
0.022686 -0.044446 0.000000
|
||||
0.022806 -0.044566 0.000000
|
||||
0.024044 -0.046082 0.000000
|
||||
0.024892 -0.047721 0.000000
|
||||
0.047250 -0.051768 0.000000
|
||||
0.025371 -0.049433 0.000000
|
||||
0.025499 -0.051171 0.000000
|
||||
-0.085500 -0.052270 0.000000
|
||||
0.025298 -0.052885 0.000000
|
||||
0.052000 -0.056518 0.000000
|
||||
-0.085257 -0.053459 0.000000
|
||||
0.024788 -0.054527 0.000000
|
||||
-0.084865 -0.054587 0.000000
|
||||
0.023987 -0.056050 0.000000
|
||||
-0.084335 -0.055643 0.000000
|
||||
-0.083678 -0.056615 0.000000
|
||||
0.022917 -0.057404 0.000000
|
||||
0.056484 -0.061000 0.000000
|
||||
-0.082905 -0.057493 0.000000
|
||||
0.021597 -0.058542 0.000000
|
||||
-0.082027 -0.058266 0.000000
|
||||
-0.081055 -0.058922 0.000000
|
||||
0.020048 -0.059414 0.000000
|
||||
-0.079999 -0.059452 0.000000
|
||||
0.018289 -0.059973 0.000000
|
||||
-0.078871 -0.059844 0.000000
|
||||
-0.077681 -0.060087 0.000000
|
||||
0.016340 -0.060170 0.000000
|
||||
-0.076440 -0.060170 0.000000
|
||||
-0.074615 -0.060170 0.000000
|
||||
-0.069568 -0.060170 0.000000
|
||||
-0.061943 -0.060170 0.000000
|
||||
-0.052386 -0.060170 0.000000
|
||||
-0.041540 -0.060170 0.000000
|
||||
-0.030050 -0.060170 0.000000
|
||||
-0.018560 -0.060170 0.000000
|
||||
-0.007714 -0.060170 0.000000
|
||||
0.001843 -0.060170 0.000000
|
||||
0.009468 -0.060170 0.000000
|
||||
0.014515 -0.060170 0.000000
|
||||
0.060435 -0.064951 0.000000
|
||||
0.063587 -0.068102 0.000000
|
||||
0.065674 -0.070188 0.000000
|
||||
0.066429 -0.070943 0.000000
|
||||
0.067365 -0.071761 0.000000
|
||||
0.078422 -0.071761 0.000000
|
||||
0.068378 -0.072430 0.000000
|
||||
0.077409 -0.072430 0.000000
|
||||
0.069453 -0.072951 0.000000
|
||||
0.076334 -0.072951 0.000000
|
||||
0.070574 -0.073322 0.000000
|
||||
0.075213 -0.073322 0.000000
|
||||
0.071726 -0.073545 0.000000
|
||||
0.074061 -0.073545 0.000000
|
||||
0.072894 -0.073620 0.000000
|
||||
3 0 1 2
|
||||
3 1 3 2
|
||||
3 1 4 3
|
||||
3 4 5 3
|
||||
3 0 6 1
|
||||
3 7 5 4
|
||||
3 0 7 6
|
||||
3 0 5 7
|
||||
3 8 9 10
|
||||
3 8 11 9
|
||||
3 3 1 0
|
||||
3 3 4 1
|
||||
3 5 4 3
|
||||
3 5 6 4
|
||||
3 7 6 5
|
||||
3 7 8 6
|
||||
3 9 8 7
|
||||
3 9 10 8
|
||||
3 11 10 9
|
||||
3 11 12 10
|
||||
3 13 12 11
|
||||
3 13 14 12
|
||||
3 13 15 14
|
||||
3 16 15 13
|
||||
3 16 17 15
|
||||
3 18 17 16
|
||||
3 18 19 17
|
||||
3 20 19 18
|
||||
3 20 21 19
|
||||
3 22 21 20
|
||||
3 23 24 25
|
||||
3 23 26 24
|
||||
3 23 27 26
|
||||
3 23 28 27
|
||||
3 23 29 28
|
||||
3 23 30 29
|
||||
3 23 31 30
|
||||
3 23 32 31
|
||||
3 23 33 32
|
||||
3 23 34 33
|
||||
3 23 35 34
|
||||
3 23 36 35
|
||||
3 23 37 36
|
||||
3 23 38 37
|
||||
3 39 38 23
|
||||
3 39 40 38
|
||||
3 39 41 40
|
||||
3 42 41 39
|
||||
3 42 43 41
|
||||
3 44 43 42
|
||||
3 44 45 43
|
||||
3 44 46 45
|
||||
3 47 46 44
|
||||
3 47 48 46
|
||||
3 22 49 21
|
||||
3 50 49 22
|
||||
3 51 48 47
|
||||
3 51 52 48
|
||||
3 51 53 52
|
||||
3 54 53 51
|
||||
3 54 55 53
|
||||
3 56 55 54
|
||||
3 56 57 55
|
||||
3 50 58 49
|
||||
3 59 58 50
|
||||
3 60 57 56
|
||||
3 60 61 57
|
||||
3 62 61 60
|
||||
3 62 63 61
|
||||
3 64 63 62
|
||||
3 59 65 58
|
||||
3 66 65 59
|
||||
3 67 63 64
|
||||
3 67 68 63
|
||||
3 69 68 67
|
||||
3 70 68 69
|
||||
3 71 68 70
|
||||
3 72 68 71
|
||||
3 73 68 72
|
||||
3 66 74 65
|
||||
3 75 74 66
|
||||
3 76 68 73
|
||||
3 77 68 76
|
||||
3 78 68 77
|
||||
3 79 68 78
|
||||
3 80 68 79
|
||||
3 81 68 80
|
||||
3 82 68 81
|
||||
3 75 83 74
|
||||
3 84 83 75
|
||||
3 85 68 82
|
||||
3 86 68 85
|
||||
3 86 87 68
|
||||
3 87 88 68
|
||||
3 89 87 86
|
||||
3 90 87 89
|
||||
3 91 87 90
|
||||
3 92 87 91
|
||||
3 93 87 92
|
||||
3 94 87 93
|
||||
3 95 87 94
|
||||
3 96 87 95
|
||||
3 97 87 96
|
||||
3 98 87 97
|
||||
3 99 88 87
|
||||
3 84 100 83
|
||||
3 101 100 84
|
||||
3 101 102 100
|
||||
3 103 102 101
|
||||
3 103 104 102
|
||||
3 104 105 102
|
||||
3 106 107 103
|
||||
3 107 104 103
|
||||
3 107 108 104
|
||||
3 109 105 104
|
||||
3 106 110 107
|
||||
3 111 108 107
|
||||
3 106 112 110
|
||||
3 109 113 105
|
||||
3 114 112 106
|
||||
3 114 115 112
|
||||
3 109 116 113
|
||||
3 117 115 114
|
||||
3 109 118 116
|
||||
3 119 115 117
|
||||
3 119 120 115
|
||||
3 99 121 88
|
||||
3 109 122 118
|
||||
3 123 120 119
|
||||
3 123 124 120
|
||||
3 109 125 122
|
||||
3 126 124 123
|
||||
3 109 127 125
|
||||
3 128 124 126
|
||||
3 128 129 124
|
||||
3 109 130 127
|
||||
3 131 129 128
|
||||
3 131 132 129
|
||||
3 109 133 130
|
||||
3 134 132 131
|
||||
3 109 135 133
|
||||
3 136 132 134
|
||||
3 136 137 132
|
||||
3 109 138 135
|
||||
3 139 137 136
|
||||
3 109 140 138
|
||||
3 141 137 139
|
||||
3 141 142 137
|
||||
3 109 143 140
|
||||
3 144 142 141
|
||||
3 109 145 143
|
||||
3 146 142 144
|
||||
3 109 147 145
|
||||
3 148 142 146
|
||||
3 99 149 121
|
||||
3 109 150 147
|
||||
3 151 142 148
|
||||
3 109 152 150
|
||||
3 153 142 151
|
||||
3 153 154 142
|
||||
3 109 155 152
|
||||
3 156 154 153
|
||||
3 109 157 155
|
||||
3 158 154 156
|
||||
3 109 159 157
|
||||
3 160 154 158
|
||||
3 160 161 154
|
||||
3 109 162 159
|
||||
3 163 161 160
|
||||
3 109 164 162
|
||||
3 165 161 163
|
||||
3 165 166 161
|
||||
3 109 167 164
|
||||
3 168 166 165
|
||||
3 169 167 109
|
||||
3 168 170 166
|
||||
3 169 171 167
|
||||
3 172 170 168
|
||||
3 169 173 171
|
||||
3 174 170 172
|
||||
3 175 173 169
|
||||
3 174 176 170
|
||||
3 175 177 173
|
||||
3 178 176 174
|
||||
3 179 177 175
|
||||
3 178 180 176
|
||||
3 179 181 177
|
||||
3 182 180 178
|
||||
3 183 181 179
|
||||
3 182 184 180
|
||||
3 183 185 181
|
||||
3 186 184 182
|
||||
3 187 185 183
|
||||
3 186 188 184
|
||||
3 187 189 185
|
||||
3 190 188 186
|
||||
3 191 189 187
|
||||
3 190 192 188
|
||||
3 99 193 149
|
||||
3 99 194 193
|
||||
3 99 195 194
|
||||
3 196 197 198
|
||||
3 199 200 201
|
||||
3 202 197 196
|
||||
3 199 203 200
|
||||
3 202 204 197
|
||||
3 205 203 199
|
||||
3 206 204 202
|
||||
3 205 207 203
|
||||
3 206 208 204
|
||||
3 209 207 205
|
||||
3 210 208 206
|
||||
3 209 211 207
|
||||
3 210 212 208
|
||||
3 213 211 209
|
||||
3 214 212 210
|
||||
3 213 215 211
|
||||
3 214 216 212
|
||||
3 217 215 213
|
||||
3 218 216 214
|
||||
3 217 219 215
|
||||
3 218 220 216
|
||||
3 221 219 217
|
||||
3 222 220 218
|
||||
3 221 223 219
|
||||
3 224 220 222
|
||||
3 221 225 223
|
||||
3 224 226 220
|
||||
3 227 225 221
|
||||
3 228 226 224
|
||||
3 227 229 225
|
||||
3 228 230 226
|
||||
3 231 230 228
|
||||
3 227 232 229
|
||||
3 233 230 231
|
||||
3 227 234 232
|
||||
3 233 235 230
|
||||
3 236 235 233
|
||||
3 227 237 234
|
||||
3 238 235 236
|
||||
3 227 239 237
|
||||
3 240 235 238
|
||||
3 227 241 239
|
||||
3 240 242 235
|
||||
3 243 242 240
|
||||
3 227 244 241
|
||||
3 99 245 195
|
||||
3 246 242 243
|
||||
3 227 247 244
|
||||
3 248 242 246
|
||||
3 227 249 247
|
||||
3 250 242 248
|
||||
3 227 251 249
|
||||
3 252 242 250
|
||||
3 227 253 251
|
||||
3 252 254 242
|
||||
3 255 254 252
|
||||
3 227 256 253
|
||||
3 257 254 255
|
||||
3 227 258 256
|
||||
3 257 259 254
|
||||
3 260 259 257
|
||||
3 227 261 258
|
||||
3 262 259 260
|
||||
3 227 263 261
|
||||
3 262 264 259
|
||||
3 265 264 262
|
||||
3 227 266 263
|
||||
3 99 267 245
|
||||
3 265 268 264
|
||||
3 269 268 265
|
||||
3 227 270 266
|
||||
3 271 268 269
|
||||
3 227 272 270
|
||||
3 271 273 268
|
||||
3 274 273 271
|
||||
3 227 275 272
|
||||
3 274 276 273
|
||||
3 277 276 274
|
||||
3 227 278 275
|
||||
3 99 227 267
|
||||
3 279 276 277
|
||||
3 227 280 278
|
||||
3 279 281 276
|
||||
3 282 281 279
|
||||
3 227 283 280
|
||||
3 283 284 280
|
||||
3 284 285 280
|
||||
3 285 286 280
|
||||
3 286 287 280
|
||||
3 287 288 280
|
||||
3 288 289 280
|
||||
3 289 290 280
|
||||
3 290 291 280
|
||||
3 291 292 280
|
||||
3 292 293 280
|
||||
3 293 294 280
|
||||
3 294 295 280
|
||||
3 282 99 281
|
||||
3 282 227 99
|
||||
3 282 283 227
|
||||
3 296 283 282
|
||||
3 296 297 283
|
||||
3 298 297 296
|
||||
3 299 297 298
|
||||
3 111 300 108
|
||||
3 111 301 300
|
||||
3 111 302 301
|
||||
3 111 303 302
|
||||
3 111 304 303
|
||||
3 111 305 304
|
||||
3 111 306 305
|
||||
3 111 307 306
|
||||
3 111 308 307
|
||||
3 111 309 308
|
||||
3 111 310 309
|
||||
3 111 311 310
|
||||
3 111 312 311
|
||||
3 111 313 312
|
||||
3 111 314 313
|
||||
3 111 315 314
|
||||
3 111 316 315
|
||||
3 111 317 316
|
||||
3 111 318 317
|
||||
3 111 319 318
|
||||
3 111 320 319
|
||||
3 111 321 320
|
||||
3 111 322 321
|
||||
3 111 323 322
|
||||
3 111 324 323
|
||||
3 325 297 299
|
||||
3 111 326 324
|
||||
3 111 327 326
|
||||
3 111 328 327
|
||||
3 111 329 328
|
||||
3 111 330 329
|
||||
3 111 331 330
|
||||
3 111 332 331
|
||||
3 333 297 325
|
||||
3 111 334 332
|
||||
3 111 335 334
|
||||
3 111 336 335
|
||||
3 111 337 336
|
||||
3 111 338 337
|
||||
3 111 339 338
|
||||
3 340 297 333
|
||||
3 111 341 339
|
||||
3 111 342 341
|
||||
3 343 342 111
|
||||
3 343 344 342
|
||||
3 345 297 340
|
||||
3 346 344 343
|
||||
3 346 347 344
|
||||
3 348 347 346
|
||||
3 348 349 347
|
||||
3 350 349 348
|
||||
3 351 349 350
|
||||
3 351 352 349
|
||||
3 353 297 345
|
||||
3 354 352 351
|
||||
3 354 355 352
|
||||
3 356 355 354
|
||||
3 357 355 356
|
||||
3 357 358 355
|
||||
3 359 358 357
|
||||
3 359 360 358
|
||||
3 361 360 359
|
||||
3 362 360 361
|
||||
3 362 363 360
|
||||
3 364 363 362
|
||||
3 365 363 364
|
||||
3 366 363 365
|
||||
3 367 363 366
|
||||
3 368 363 367
|
||||
3 369 363 368
|
||||
3 370 363 369
|
||||
3 371 363 370
|
||||
3 372 363 371
|
||||
3 373 363 372
|
||||
3 374 363 373
|
||||
3 375 363 374
|
||||
3 376 297 353
|
||||
3 377 297 376
|
||||
3 378 297 377
|
||||
3 379 297 378
|
||||
3 380 297 379
|
||||
3 380 381 297
|
||||
3 382 381 380
|
||||
3 382 383 381
|
||||
3 384 383 382
|
||||
3 384 385 383
|
||||
3 386 385 384
|
||||
3 386 387 385
|
||||
3 388 387 386
|
||||
3 388 389 387
|
||||
3 390 389 388
|
||||
|
||||
@@ -95,6 +95,7 @@ set(VMIX_SRCS
|
||||
SrtReceiverSource.cpp
|
||||
MultiFileRecorder.cpp
|
||||
DisplaysView.cpp
|
||||
ScreenCaptureSource.cpp
|
||||
)
|
||||
|
||||
#####
|
||||
@@ -134,6 +135,8 @@ ELSE(APPLE)
|
||||
|
||||
set(PLATFORM_LIBS
|
||||
GTK::GTK
|
||||
X11::X11
|
||||
X11::xcb
|
||||
)
|
||||
|
||||
ENDIF(APPLE)
|
||||
|
||||
@@ -452,8 +452,8 @@ Symbol::Symbol(Type t, glm::vec3 pos) : Node(), type_(t)
|
||||
shadows[PATTERN]= shadow;
|
||||
icons[CAMERA] = new Mesh("mesh/icon_camera.ply");
|
||||
shadows[CAMERA] = shadow;
|
||||
icons[CUBE] = new Mesh("mesh/icon_cube.ply");
|
||||
shadows[CUBE] = shadow;
|
||||
icons[SCREEN] = new Mesh("mesh/icon_cube.ply");
|
||||
shadows[SCREEN] = shadow;
|
||||
icons[SHARE] = new Mesh("mesh/icon_share.ply");
|
||||
shadows[SHARE] = shadow;
|
||||
icons[RECEIVE] = new Mesh("mesh/icon_receive.ply");
|
||||
|
||||
@@ -60,7 +60,7 @@ protected:
|
||||
class Symbol : public Node
|
||||
{
|
||||
public:
|
||||
typedef enum { CIRCLE_POINT = 0, SQUARE_POINT, IMAGE, SEQUENCE, VIDEO, SESSION, CLONE, RENDER, GROUP, PATTERN, CAMERA, CUBE, SHARE, RECEIVE,
|
||||
typedef enum { CIRCLE_POINT = 0, SQUARE_POINT, IMAGE, SEQUENCE, VIDEO, SESSION, CLONE, RENDER, GROUP, PATTERN, CAMERA, SCREEN, SHARE, RECEIVE,
|
||||
DOTS, BUSY, LOCK, UNLOCK, EYE, EYESLASH, TELEVISION, ARROWS, ROTATION, CROP, CIRCLE, SQUARE, CLOCK, CLOCK_H, GRID, CROSS, EMPTY } Type;
|
||||
Symbol(Type t, glm::vec3 pos = glm::vec3(0.f));
|
||||
~Symbol();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* This file is part of vimix - video live mixer
|
||||
*
|
||||
* **Copyright** (C) 2019-2023 Bruno Herbelin <bruno.herbelin@gmail.com>
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
|
||||
#include "Log.h"
|
||||
#include "Decorations.h"
|
||||
#include "Stream.h"
|
||||
@@ -35,17 +34,14 @@
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define DEVICE_DEBUG
|
||||
#define GST_DEVICE_DEBUG
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if defined(APPLE)
|
||||
std::string gst_plugin_device = "avfvideosrc";
|
||||
std::string gst_plugin_vidcap = "avfvideosrc capture-screen=true";
|
||||
#else
|
||||
std::string gst_plugin_device = "v4l2src";
|
||||
std::string gst_plugin_vidcap = "ximagesrc";
|
||||
#endif
|
||||
|
||||
std::string pipelineForDevice(GstDevice *device, uint index)
|
||||
@@ -119,7 +115,6 @@ private:
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
|
||||
struct hasConnectedSource
|
||||
{
|
||||
inline bool operator()(const DeviceHandle &elem) const {
|
||||
@@ -131,6 +126,29 @@ private:
|
||||
Source *s_;
|
||||
};
|
||||
|
||||
std::string get_device_properties(GstDevice *device)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
GstStructure *stru = gst_device_get_properties(device);
|
||||
gint n = gst_structure_n_fields (stru);
|
||||
oss << "- " << gst_structure_get_name(stru) << " -" << std::endl;
|
||||
for (gint i = 0; i < n; ++i) {
|
||||
std::string name = gst_structure_nth_field_name (stru, i);
|
||||
if (name.find("device.path") !=std::string::npos || name.find("object.path") !=std::string::npos)
|
||||
oss << "Path : " << gst_structure_get_string(stru, name.c_str()) << std::endl;
|
||||
if (name.find("device.api") !=std::string::npos)
|
||||
oss << "Api : " << gst_structure_get_string(stru, name.c_str()) << std::endl;
|
||||
if (name.find("device.card") !=std::string::npos || name.find("cap.card") !=std::string::npos)
|
||||
oss << "Card : " << gst_structure_get_string(stru, name.c_str()) << std::endl;
|
||||
if (name.find("device.driver") !=std::string::npos || name.find("cap.driver") !=std::string::npos)
|
||||
oss << "Driver : " << gst_structure_get_string(stru, name.c_str()) << std::endl;
|
||||
}
|
||||
gst_structure_free (stru);
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void Device::add(GstDevice *device)
|
||||
{
|
||||
if (device==nullptr)
|
||||
@@ -146,7 +164,7 @@ void Device::add(GstDevice *device)
|
||||
if ( handle == handles_.cend() ) {
|
||||
|
||||
std::string p = pipelineForDevice(device, handles_.size());
|
||||
DeviceConfigSet confs = getDeviceConfigs(p);
|
||||
GstToolkit::PipelineConfigSet confs = GstToolkit::getPipelineConfigs(p);
|
||||
|
||||
// add if not in the list and valid
|
||||
if (!p.empty() && !confs.empty()) {
|
||||
@@ -154,12 +172,12 @@ void Device::add(GstDevice *device)
|
||||
dev.name = device_name;
|
||||
dev.pipeline = p;
|
||||
dev.configs = confs;
|
||||
handles_.push_back(dev);
|
||||
dev.properties = get_device_properties (device);
|
||||
#ifdef GST_DEVICE_DEBUG
|
||||
gchar *stru = gst_structure_to_string( gst_device_get_properties(device) );
|
||||
g_print("\nDevice %s plugged : %s\n", device_name, stru);
|
||||
g_free (stru);
|
||||
g_print("\n%s %s", device_name, gst_structure_to_string(stru) );
|
||||
#endif
|
||||
handles_.push_back(dev);
|
||||
Log::Info("Device %s plugged.", device_name);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -174,33 +192,46 @@ void Device::remove(GstDevice *device)
|
||||
if (device==nullptr)
|
||||
return;
|
||||
|
||||
gchar *device_name = gst_device_get_display_name (device);
|
||||
std::string devicename = gst_device_get_display_name (device);
|
||||
|
||||
// lock before change
|
||||
access_.lock();
|
||||
|
||||
// if a device with this name is listed
|
||||
auto handle = std::find_if(handles_.cbegin(), handles_.cend(), hasDeviceName(device_name) );
|
||||
auto handle = std::find_if(handles_.cbegin(), handles_.cend(), hasDeviceName(devicename) );
|
||||
if ( handle != handles_.cend() ) {
|
||||
|
||||
// remove the handle if there is no source connected
|
||||
if (handle->connected_sources.empty())
|
||||
handles_.erase(handle);
|
||||
else {
|
||||
// otherwise unplug all sources
|
||||
for (auto sit = handle->connected_sources.begin(); sit != handle->connected_sources.end(); ++sit)
|
||||
(*sit)->unplug();
|
||||
// and inform user
|
||||
Log::Warning("Device %s unplugged.", device_name);
|
||||
// NB; the handle will be removed at the destruction of the last DeviceSource
|
||||
// just inform the user if there is no source connected
|
||||
if (handle->connected_sources.empty()) {
|
||||
Log::Info("Device %s unplugged.", devicename.c_str());
|
||||
}
|
||||
else {
|
||||
// otherwise unplug all sources and close their streams
|
||||
bool first = true;
|
||||
for (auto sit = handle->connected_sources.begin(); sit != handle->connected_sources.end(); ++sit) {
|
||||
// for the first connected source in the list
|
||||
if (first) {
|
||||
// close the stream
|
||||
(*sit)->stream()->close();
|
||||
first = false;
|
||||
} else {
|
||||
// all others set the stream to null, so it is not deleted in destructor of StreamSource
|
||||
(*sit)->stream_ = nullptr;
|
||||
}
|
||||
// all connected sources are set to unplugged
|
||||
(*sit)->unplug();
|
||||
// and finally inform user
|
||||
Log::Warning("Device %s unplugged: sources %s disconnected.",
|
||||
devicename.c_str(), (*sit)->name().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// remove the handle
|
||||
handles_.erase(handle);
|
||||
}
|
||||
|
||||
// unlock access
|
||||
access_.unlock();
|
||||
|
||||
|
||||
g_free (device_name);
|
||||
}
|
||||
|
||||
Device::Device(): monitor_(nullptr), monitor_initialized_(false), monitor_unplug_event_(false)
|
||||
@@ -229,26 +260,6 @@ void Device::launchMonitoring(Device *d)
|
||||
}
|
||||
g_list_free(devices);
|
||||
|
||||
// Try to get captrure screen
|
||||
DeviceConfigSet confs = getDeviceConfigs(gst_plugin_vidcap);
|
||||
if (!confs.empty()) {
|
||||
DeviceConfig best = *confs.rbegin();
|
||||
DeviceConfigSet confscreen;
|
||||
// limit framerate to 30fps
|
||||
best.fps_numerator = MIN( best.fps_numerator, 30);
|
||||
best.fps_denominator = 1;
|
||||
confscreen.insert(best);
|
||||
// add to config list
|
||||
d->access_.lock();
|
||||
|
||||
DeviceHandle dev;
|
||||
dev.name = "Screen capture";
|
||||
dev.pipeline = gst_plugin_vidcap;
|
||||
dev.configs = confscreen;
|
||||
d->handles_.push_back(dev);
|
||||
d->access_.unlock();
|
||||
}
|
||||
|
||||
// monitor is initialized
|
||||
d->monitor_initialized_ = true;
|
||||
d->monitor_initialization_.notify_all();
|
||||
@@ -343,9 +354,21 @@ std::string Device::description(int index)
|
||||
return ret;
|
||||
}
|
||||
|
||||
DeviceConfigSet Device::config(int index)
|
||||
std::string Device::properties(int index)
|
||||
{
|
||||
DeviceConfigSet ret;
|
||||
std::string ret = "";
|
||||
|
||||
access_.lock();
|
||||
if (index > -1 && index < (int) handles_.size())
|
||||
ret = handles_[index].properties;
|
||||
access_.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
GstToolkit::PipelineConfigSet Device::config(int index)
|
||||
{
|
||||
GstToolkit::PipelineConfigSet ret;
|
||||
|
||||
access_.lock();
|
||||
if (index > -1 && index < (int) handles_.size())
|
||||
@@ -391,22 +414,27 @@ void DeviceSource::unsetDevice()
|
||||
// if this is the last source connected to the device handler
|
||||
// the stream will be removed by the ~StreamSource destructor
|
||||
// and the device handler should not keep reference to it
|
||||
if (h->connected_sources.empty()) {
|
||||
// if the cause of deletion is the unplugging of the device
|
||||
if (unplugged_)
|
||||
// remove the handle entirely
|
||||
Device::manager().handles_.erase(h);
|
||||
else
|
||||
// otherwise just cancel the reference to the stream
|
||||
h->stream = nullptr;
|
||||
}
|
||||
if (h->connected_sources.empty())
|
||||
// cancel the reference to the stream
|
||||
h->stream = nullptr;
|
||||
else
|
||||
// else this means another DeviceSource is using this stream
|
||||
// and we should avoid to delete the stream in the ~StreamSource destructor
|
||||
else
|
||||
stream_ = nullptr;
|
||||
}
|
||||
|
||||
device_ = "";
|
||||
}
|
||||
|
||||
void DeviceSource::reconnect()
|
||||
{
|
||||
// remember name
|
||||
std::string d = device_;
|
||||
// disconnect
|
||||
unsetDevice();
|
||||
// connect
|
||||
setDevice(d);
|
||||
}
|
||||
|
||||
void DeviceSource::setDevice(const std::string &devicename)
|
||||
{
|
||||
@@ -422,17 +450,27 @@ void DeviceSource::setDevice(const std::string &devicename)
|
||||
if (!device_.empty())
|
||||
unsetDevice();
|
||||
|
||||
// remember device name
|
||||
// if the stream referenced in this source remains after unsetDevice
|
||||
if (stream_) {
|
||||
delete stream_;
|
||||
stream_ = nullptr;
|
||||
}
|
||||
|
||||
// set new device name
|
||||
device_ = devicename;
|
||||
|
||||
// check existence of a device handle with that name
|
||||
auto h = std::find_if(Device::manager().handles_.begin(), Device::manager().handles_.end(), hasDeviceName(device_));
|
||||
|
||||
// found a device handle
|
||||
if ( h != Device::manager().handles_.end()) {
|
||||
|
||||
// find if a DeviceHandle with this device name already has a stream
|
||||
if ( h->stream != nullptr) {
|
||||
// find if a DeviceHandle with this device name already has a stream that is open
|
||||
if ( h->stream != nullptr ) {
|
||||
// just use it !
|
||||
stream_ = h->stream;
|
||||
// reinit to adapt to new stream
|
||||
init();
|
||||
}
|
||||
else {
|
||||
// start filling in the gstreamer pipeline
|
||||
@@ -440,16 +478,16 @@ void DeviceSource::setDevice(const std::string &devicename)
|
||||
pipeline << h->pipeline;
|
||||
|
||||
// test the device and get config
|
||||
DeviceConfigSet confs = h->configs;
|
||||
GstToolkit::PipelineConfigSet confs = h->configs;
|
||||
#ifdef DEVICE_DEBUG
|
||||
Log::Info("Device %s supported configs:", devicename.c_str());
|
||||
for( DeviceConfigSet::iterator it = confs.begin(); it != confs.end(); ++it ){
|
||||
for( GstToolkit::PipelineConfigSet::iterator it = confs.begin(); it != confs.end(); ++it ){
|
||||
float fps = static_cast<float>((*it).fps_numerator) / static_cast<float>((*it).fps_denominator);
|
||||
Log::Info(" - %s %s %d x %d %.1f fps", (*it).stream.c_str(), (*it).format.c_str(), (*it).width, (*it).height, fps);
|
||||
}
|
||||
#endif
|
||||
if (!confs.empty()) {
|
||||
DeviceConfig best = *confs.rbegin();
|
||||
GstToolkit::PipelineConfig best = *confs.rbegin();
|
||||
float fps = static_cast<float>(best.fps_numerator) / static_cast<float>(best.fps_denominator);
|
||||
Log::Info("Device %s selected its optimal config: %s %s %dx%d@%.1ffps", device_.c_str(), best.stream.c_str(), best.format.c_str(), best.width, best.height, fps);
|
||||
|
||||
@@ -460,13 +498,12 @@ void DeviceSource::setDevice(const std::string &devicename)
|
||||
pipeline << ",width=" << best.width;
|
||||
pipeline << ",height=" << best.height;
|
||||
|
||||
// decode jpeg if needed
|
||||
if ( best.stream.find("jpeg") != std::string::npos )
|
||||
pipeline << " ! jpegdec";
|
||||
|
||||
if ( device_.find("Screen") != std::string::npos )
|
||||
pipeline << " ! videoconvert ! video/x-raw,format=RGB ! queue max-size-buffers=3";
|
||||
|
||||
pipeline << " ! videoconvert";
|
||||
// always convert
|
||||
pipeline << " ! queue ! videoconvert";
|
||||
|
||||
// delete and reset render buffer to enforce re-init of StreamSource
|
||||
if (renderbuffer_)
|
||||
@@ -474,8 +511,6 @@ void DeviceSource::setDevice(const std::string &devicename)
|
||||
renderbuffer_ = nullptr;
|
||||
|
||||
// new stream
|
||||
if (stream_)
|
||||
delete stream_;
|
||||
stream_ = h->stream = new Stream;
|
||||
|
||||
// open gstreamer
|
||||
@@ -492,7 +527,7 @@ void DeviceSource::setDevice(const std::string &devicename)
|
||||
}
|
||||
else {
|
||||
unplugged_ = true;
|
||||
Log::Warning("No such device '%s'", device_.c_str());
|
||||
Log::Warning("No device named '%s'", device_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,9 +552,7 @@ void DeviceSource::setActive (bool on)
|
||||
}
|
||||
stream_->enable(streamactive);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -535,176 +568,14 @@ Source::Failure DeviceSource::failed() const
|
||||
return (unplugged_ || StreamSource::failed()) ? FAIL_CRITICAL : FAIL_NONE;
|
||||
}
|
||||
|
||||
DeviceConfigSet Device::getDeviceConfigs(const std::string &src_description)
|
||||
{
|
||||
DeviceConfigSet configs;
|
||||
|
||||
// create dummy pipeline to be tested
|
||||
std::string description = src_description;
|
||||
description += " name=devsrc ! fakesink name=sink";
|
||||
|
||||
// parse pipeline descriptor
|
||||
GError *error = NULL;
|
||||
GstElement *pipeline_ = gst_parse_launch (description.c_str(), &error);
|
||||
if (error != NULL) {
|
||||
Log::Warning("DeviceSource Could not construct test pipeline %s:\n%s", description.c_str(), error->message);
|
||||
g_clear_error (&error);
|
||||
return configs;
|
||||
}
|
||||
|
||||
// get the pipeline element named "devsrc" from the Device class
|
||||
GstElement *elem = gst_bin_get_by_name (GST_BIN (pipeline_), "devsrc");
|
||||
if (elem) {
|
||||
|
||||
// initialize the pipeline
|
||||
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_PAUSED);
|
||||
if (ret != GST_STATE_CHANGE_FAILURE) {
|
||||
|
||||
// get the first pad and its content
|
||||
GstIterator *iter = gst_element_iterate_src_pads(elem);
|
||||
GValue vPad = G_VALUE_INIT;
|
||||
GstPad* pad_ret = NULL;
|
||||
if (gst_iterator_next(iter, &vPad) == GST_ITERATOR_OK)
|
||||
{
|
||||
pad_ret = GST_PAD(g_value_get_object(&vPad));
|
||||
GstCaps *device_caps = gst_pad_query_caps (pad_ret, NULL);
|
||||
|
||||
// loop over all caps offered by the pad
|
||||
int C = gst_caps_get_size(device_caps);
|
||||
for (int c = 0; c < C; ++c) {
|
||||
// get GST cap
|
||||
GstStructure *decice_cap_struct = gst_caps_get_structure (device_caps, c);
|
||||
#ifdef GST_DEVICE_DEBUG
|
||||
gchar *capstext = gst_structure_to_string (decice_cap_struct);
|
||||
g_print("\nDevice caps: %s", capstext);
|
||||
g_free(capstext);
|
||||
#endif
|
||||
|
||||
// fill our config
|
||||
DeviceConfig config;
|
||||
|
||||
// not managing opengl texture-target types
|
||||
// TODO: support input devices texture-target video/x-raw(memory:GLMemory) for improved pipeline
|
||||
if ( gst_structure_has_field (decice_cap_struct, "texture-target"))
|
||||
continue;
|
||||
|
||||
// NAME : typically video/x-raw or image/jpeg
|
||||
config.stream = gst_structure_get_name (decice_cap_struct);
|
||||
|
||||
// FORMAT : typically BGRA or YUVY
|
||||
if ( gst_structure_has_field (decice_cap_struct, "format")) {
|
||||
// get generic value
|
||||
const GValue *val = gst_structure_get_value(decice_cap_struct, "format");
|
||||
|
||||
// if its a list of format string
|
||||
if ( GST_VALUE_HOLDS_LIST(val)) {
|
||||
int N = gst_value_list_get_size(val);
|
||||
for (int n = 0; n < N; n++ ){
|
||||
std::string f = gst_value_serialize( gst_value_list_get_value(val, n) );
|
||||
|
||||
// preference order : 1) RGBx, 2) JPEG, 3) ALL OTHER
|
||||
// select f if it contains R (e.g. for RGBx) and not already RGB in config
|
||||
if ( (f.find("R") != std::string::npos) && (config.format.find("R") == std::string::npos ) ) {
|
||||
config.format = f;
|
||||
break;
|
||||
}
|
||||
// default, take at least one if nothing yet in config
|
||||
else if ( config.format.empty() )
|
||||
config.format = f;
|
||||
}
|
||||
|
||||
}
|
||||
// single format
|
||||
else {
|
||||
config.format = gst_value_serialize(val);
|
||||
}
|
||||
}
|
||||
|
||||
// FRAMERATE : can be a fraction of a list of fractions
|
||||
if ( gst_structure_has_field (decice_cap_struct, "framerate")) {
|
||||
|
||||
// get generic value
|
||||
const GValue *val = gst_structure_get_value(decice_cap_struct, "framerate");
|
||||
// if its a single fraction
|
||||
if ( GST_VALUE_HOLDS_FRACTION(val)) {
|
||||
config.fps_numerator = gst_value_get_fraction_numerator(val);
|
||||
config.fps_denominator= gst_value_get_fraction_denominator(val);
|
||||
}
|
||||
// if its a range of fraction; take the max
|
||||
else if ( GST_VALUE_HOLDS_FRACTION_RANGE(val)) {
|
||||
config.fps_numerator = gst_value_get_fraction_numerator(gst_value_get_fraction_range_max(val));
|
||||
config.fps_denominator= gst_value_get_fraction_denominator(gst_value_get_fraction_range_max(val));
|
||||
}
|
||||
// deal otherwise with a list of fractions; find the max
|
||||
else if ( GST_VALUE_HOLDS_LIST(val)) {
|
||||
gdouble fps_max = 1.0;
|
||||
// loop over all fractions
|
||||
int N = gst_value_list_get_size(val);
|
||||
for (int i = 0; i < N; ++i ){
|
||||
const GValue *frac = gst_value_list_get_value(val, i);
|
||||
// read one fraction in the list
|
||||
if ( GST_VALUE_HOLDS_FRACTION(frac)) {
|
||||
int n = gst_value_get_fraction_numerator(frac);
|
||||
int d = gst_value_get_fraction_denominator(frac);
|
||||
// keep only the higher FPS
|
||||
gdouble f = 1.0;
|
||||
gst_util_fraction_to_double( n, d, &f );
|
||||
if ( f > fps_max ) {
|
||||
config.fps_numerator = n;
|
||||
config.fps_denominator = d;
|
||||
fps_max = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// default
|
||||
config.fps_numerator = 30;
|
||||
config.fps_denominator = 1;
|
||||
}
|
||||
|
||||
// WIDTH and HEIGHT
|
||||
if ( gst_structure_has_field (decice_cap_struct, "width"))
|
||||
gst_structure_get_int (decice_cap_struct, "width", &config.width);
|
||||
if ( gst_structure_has_field (decice_cap_struct, "height"))
|
||||
gst_structure_get_int (decice_cap_struct, "height", &config.height);
|
||||
|
||||
|
||||
// add this config
|
||||
configs.insert(config);
|
||||
}
|
||||
|
||||
}
|
||||
gst_iterator_free(iter);
|
||||
|
||||
// terminate pipeline
|
||||
gst_element_set_state (pipeline_, GST_STATE_NULL);
|
||||
}
|
||||
|
||||
g_object_unref (elem);
|
||||
}
|
||||
|
||||
gst_object_unref (pipeline_);
|
||||
|
||||
return configs;
|
||||
}
|
||||
|
||||
|
||||
glm::ivec2 DeviceSource::icon() const
|
||||
{
|
||||
if ( device_.find("Screen") != std::string::npos )
|
||||
return glm::ivec2(ICON_SOURCE_DEVICE_SCREEN);
|
||||
else
|
||||
return glm::ivec2(ICON_SOURCE_DEVICE);
|
||||
return glm::ivec2(ICON_SOURCE_DEVICE);
|
||||
}
|
||||
|
||||
std::string DeviceSource::info() const
|
||||
{
|
||||
if ( device_.find("Screen") != std::string::npos )
|
||||
return "Screen capture";
|
||||
else
|
||||
return "Device";
|
||||
return "Device";
|
||||
}
|
||||
|
||||
|
||||
@@ -714,5 +585,3 @@ std::string DeviceSource::info() const
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4,11 +4,15 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#include "GstToolkit.h"
|
||||
#include "StreamSource.h"
|
||||
|
||||
class DeviceSource : public StreamSource
|
||||
{
|
||||
friend class Device;
|
||||
|
||||
public:
|
||||
DeviceSource(uint64_t id = 0);
|
||||
~DeviceSource();
|
||||
@@ -24,10 +28,13 @@ public:
|
||||
// specific interface
|
||||
void setDevice(const std::string &devicename);
|
||||
inline std::string device() const { return device_; }
|
||||
void unplug() { unplugged_ = true; }
|
||||
void reconnect();
|
||||
|
||||
glm::ivec2 icon() const override;
|
||||
std::string info() const override;
|
||||
inline std::string info() const override;
|
||||
|
||||
protected:
|
||||
void unplug() { unplugged_ = true; }
|
||||
|
||||
private:
|
||||
std::string device_;
|
||||
@@ -35,48 +42,12 @@ private:
|
||||
void unsetDevice();
|
||||
};
|
||||
|
||||
struct DeviceConfig {
|
||||
gint width;
|
||||
gint height;
|
||||
gint fps_numerator;
|
||||
gint fps_denominator;
|
||||
std::string stream;
|
||||
std::string format;
|
||||
|
||||
DeviceConfig() {
|
||||
width = 0;
|
||||
height = 0;
|
||||
fps_numerator = 30;
|
||||
fps_denominator = 1;
|
||||
stream = "";
|
||||
format = "";
|
||||
}
|
||||
|
||||
inline bool operator < (const DeviceConfig b) const
|
||||
{
|
||||
int formatscore = this->format.find("R") != std::string::npos ? 2 : 1; // best score for RGBx
|
||||
int b_formatscore = b.format.find("R") != std::string::npos ? 2 : 1;
|
||||
float fps = static_cast<float>(this->fps_numerator) / static_cast<float>(this->fps_denominator);
|
||||
float b_fps = static_cast<float>(b.fps_numerator) / static_cast<float>(b.fps_denominator);
|
||||
return ( fps * static_cast<float>(this->height * formatscore) < b_fps * static_cast<float>(b.height * b_formatscore));
|
||||
}
|
||||
};
|
||||
|
||||
struct better_device_comparator
|
||||
{
|
||||
inline bool operator () (const DeviceConfig a, const DeviceConfig b) const
|
||||
{
|
||||
return (a < b);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::set<DeviceConfig, better_device_comparator> DeviceConfigSet;
|
||||
|
||||
struct DeviceHandle {
|
||||
|
||||
std::string name;
|
||||
std::string pipeline;
|
||||
DeviceConfigSet configs;
|
||||
std::string properties;
|
||||
GstToolkit::PipelineConfigSet configs;
|
||||
|
||||
Stream *stream;
|
||||
std::list<DeviceSource *> connected_sources;
|
||||
@@ -106,21 +77,21 @@ public:
|
||||
int numDevices () ;
|
||||
std::string name (int index) ;
|
||||
std::string description (int index) ;
|
||||
DeviceConfigSet config (int index) ;
|
||||
std::string properties (int index) ;
|
||||
GstToolkit::PipelineConfigSet config (int index) ;
|
||||
|
||||
int index (const std::string &device);
|
||||
bool exists (const std::string &device) ;
|
||||
bool exists (const std::string &device);
|
||||
void reload ();
|
||||
|
||||
static gboolean callback_device_monitor (GstBus *, GstMessage *, gpointer);
|
||||
static DeviceConfigSet getDeviceConfigs(const std::string &src_description);
|
||||
|
||||
private:
|
||||
|
||||
static void launchMonitoring(Device *d);
|
||||
static bool initialized();
|
||||
void remove(GstDevice *device);
|
||||
void add(GstDevice *device);
|
||||
void remove(GstDevice *device);
|
||||
|
||||
std::mutex access_;
|
||||
std::vector< DeviceHandle > handles_;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
**/
|
||||
//#define GST_DEVICE_DEBUG
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
@@ -306,3 +307,157 @@ std::string GstToolkit::used_decoding_plugins(GstElement *gstbin)
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
GstToolkit::PipelineConfigSet GstToolkit::getPipelineConfigs(const std::string &src_description)
|
||||
{
|
||||
PipelineConfigSet configs;
|
||||
|
||||
// create dummy pipeline to be tested
|
||||
std::string description = src_description;
|
||||
description += " name=devsrc ! fakesink name=sink";
|
||||
|
||||
// parse pipeline descriptor
|
||||
GError *error = NULL;
|
||||
GstElement *pipeline_ = gst_parse_launch (description.c_str(), &error);
|
||||
if (error != NULL) {
|
||||
g_printerr("DeviceSource Could not construct test pipeline %s:\n%s", description.c_str(), error->message);
|
||||
g_clear_error (&error);
|
||||
return configs;
|
||||
}
|
||||
|
||||
// get the pipeline element named "devsrc" from the Device class
|
||||
GstElement *elem = gst_bin_get_by_name (GST_BIN (pipeline_), "devsrc");
|
||||
if (elem) {
|
||||
|
||||
// initialize the pipeline
|
||||
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_PAUSED);
|
||||
if (ret != GST_STATE_CHANGE_FAILURE) {
|
||||
|
||||
// get the first pad and its content
|
||||
GstIterator *iter = gst_element_iterate_src_pads(elem);
|
||||
GValue vPad = G_VALUE_INIT;
|
||||
GstPad* pad_ret = NULL;
|
||||
if (gst_iterator_next(iter, &vPad) == GST_ITERATOR_OK)
|
||||
{
|
||||
pad_ret = GST_PAD(g_value_get_object(&vPad));
|
||||
GstCaps *device_caps = gst_pad_query_caps (pad_ret, NULL);
|
||||
|
||||
// loop over all caps offered by the pad
|
||||
int C = gst_caps_get_size(device_caps);
|
||||
for (int c = 0; c < C; ++c) {
|
||||
// get GST cap
|
||||
GstStructure *decice_cap_struct = gst_caps_get_structure (device_caps, c);
|
||||
#ifdef GST_DEVICE_DEBUG
|
||||
gchar *capstext = gst_structure_to_string (decice_cap_struct);
|
||||
g_print("\nPipeline caps: %s", capstext);
|
||||
g_free(capstext);
|
||||
#endif
|
||||
// fill our config
|
||||
PipelineConfig config;
|
||||
|
||||
// not managing opengl texture-target types
|
||||
// TODO: support input devices texture-target video/x-raw(memory:GLMemory) for improved pipeline
|
||||
if ( gst_structure_has_field (decice_cap_struct, "texture-target"))
|
||||
continue;
|
||||
|
||||
// NAME : typically video/x-raw or image/jpeg
|
||||
config.stream = gst_structure_get_name (decice_cap_struct);
|
||||
|
||||
// FORMAT : typically BGRA or YUVY
|
||||
if ( gst_structure_has_field (decice_cap_struct, "format")) {
|
||||
// get generic value
|
||||
const GValue *val = gst_structure_get_value(decice_cap_struct, "format");
|
||||
|
||||
// if its a list of format string
|
||||
if ( GST_VALUE_HOLDS_LIST(val)) {
|
||||
int N = gst_value_list_get_size(val);
|
||||
for (int n = 0; n < N; n++ ){
|
||||
std::string f = gst_value_serialize( gst_value_list_get_value(val, n) );
|
||||
|
||||
// preference order : 1) RGBx, 2) JPEG, 3) ALL OTHER
|
||||
// select f if it contains R (e.g. for RGBx) and not already RGB in config
|
||||
if ( (f.find("R") != std::string::npos) && (config.format.find("R") == std::string::npos ) ) {
|
||||
config.format = f;
|
||||
break;
|
||||
}
|
||||
// default, take at least one if nothing yet in config
|
||||
else if ( config.format.empty() )
|
||||
config.format = f;
|
||||
}
|
||||
|
||||
}
|
||||
// single format
|
||||
else {
|
||||
config.format = gst_value_serialize(val);
|
||||
}
|
||||
}
|
||||
|
||||
// FRAMERATE : can be a fraction of a list of fractions
|
||||
if ( gst_structure_has_field (decice_cap_struct, "framerate")) {
|
||||
|
||||
// get generic value
|
||||
const GValue *val = gst_structure_get_value(decice_cap_struct, "framerate");
|
||||
// if its a single fraction
|
||||
if ( GST_VALUE_HOLDS_FRACTION(val)) {
|
||||
config.fps_numerator = gst_value_get_fraction_numerator(val);
|
||||
config.fps_denominator= gst_value_get_fraction_denominator(val);
|
||||
}
|
||||
// if its a range of fraction; take the max
|
||||
else if ( GST_VALUE_HOLDS_FRACTION_RANGE(val)) {
|
||||
config.fps_numerator = gst_value_get_fraction_numerator(gst_value_get_fraction_range_max(val));
|
||||
config.fps_denominator= gst_value_get_fraction_denominator(gst_value_get_fraction_range_max(val));
|
||||
}
|
||||
// deal otherwise with a list of fractions; find the max
|
||||
else if ( GST_VALUE_HOLDS_LIST(val)) {
|
||||
gdouble fps_max = 1.0;
|
||||
// loop over all fractions
|
||||
int N = gst_value_list_get_size(val);
|
||||
for (int i = 0; i < N; ++i ){
|
||||
const GValue *frac = gst_value_list_get_value(val, i);
|
||||
// read one fraction in the list
|
||||
if ( GST_VALUE_HOLDS_FRACTION(frac)) {
|
||||
int n = gst_value_get_fraction_numerator(frac);
|
||||
int d = gst_value_get_fraction_denominator(frac);
|
||||
// keep only the higher FPS
|
||||
gdouble f = 1.0;
|
||||
gst_util_fraction_to_double( n, d, &f );
|
||||
if ( f > fps_max ) {
|
||||
config.fps_numerator = n;
|
||||
config.fps_denominator = d;
|
||||
fps_max = f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// default
|
||||
config.fps_numerator = 30;
|
||||
config.fps_denominator = 1;
|
||||
}
|
||||
|
||||
// WIDTH and HEIGHT
|
||||
if ( gst_structure_has_field (decice_cap_struct, "width"))
|
||||
gst_structure_get_int (decice_cap_struct, "width", &config.width);
|
||||
if ( gst_structure_has_field (decice_cap_struct, "height"))
|
||||
gst_structure_get_int (decice_cap_struct, "height", &config.height);
|
||||
|
||||
|
||||
// add this config
|
||||
configs.insert(config);
|
||||
}
|
||||
|
||||
}
|
||||
gst_iterator_free(iter);
|
||||
|
||||
// terminate pipeline
|
||||
gst_element_set_state (pipeline_, GST_STATE_NULL);
|
||||
}
|
||||
|
||||
g_object_unref (elem);
|
||||
}
|
||||
|
||||
gst_object_unref (pipeline_);
|
||||
|
||||
return configs;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
namespace GstToolkit
|
||||
{
|
||||
@@ -30,6 +31,46 @@ std::list<std::string> all_plugin_features(std::string pluginname);
|
||||
bool has_feature (std::string name);
|
||||
bool enable_feature (std::string name, bool enable);
|
||||
|
||||
|
||||
struct PipelineConfig {
|
||||
gint width;
|
||||
gint height;
|
||||
gint fps_numerator;
|
||||
gint fps_denominator;
|
||||
std::string stream;
|
||||
std::string format;
|
||||
|
||||
PipelineConfig() {
|
||||
width = 0;
|
||||
height = 0;
|
||||
fps_numerator = 30;
|
||||
fps_denominator = 1;
|
||||
stream = "";
|
||||
format = "";
|
||||
}
|
||||
|
||||
inline bool operator < (const PipelineConfig b) const
|
||||
{
|
||||
int formatscore = this->format.find("R") != std::string::npos ? 2 : 1; // best score for RGBx
|
||||
int b_formatscore = b.format.find("R") != std::string::npos ? 2 : 1;
|
||||
float fps = static_cast<float>(this->fps_numerator) / static_cast<float>(this->fps_denominator);
|
||||
float b_fps = static_cast<float>(b.fps_numerator) / static_cast<float>(b.fps_denominator);
|
||||
return ( fps * static_cast<float>(this->height * formatscore) < b_fps * static_cast<float>(b.height * b_formatscore));
|
||||
}
|
||||
};
|
||||
|
||||
struct better_config_comparator
|
||||
{
|
||||
inline bool operator () (const PipelineConfig a, const PipelineConfig b) const
|
||||
{
|
||||
return (a < b);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::set<PipelineConfig, better_config_comparator> PipelineConfigSet;
|
||||
|
||||
PipelineConfigSet getPipelineConfigs(const std::string &src_description);
|
||||
|
||||
}
|
||||
|
||||
#endif // __GSTGUI_TOOLKIT_H_
|
||||
|
||||
@@ -286,7 +286,7 @@ bool ImGuiToolkit::IconButton(int i, int j, const char *tooltip, const char* sho
|
||||
if (window->SkipItems)
|
||||
return false;
|
||||
|
||||
ImGui::PushID( i * 20 + j );
|
||||
ImGui::PushID( i * 20 + j + ( tooltip ? window->GetID(tooltip) : 0) );
|
||||
|
||||
// duplicate of ImGui::InvisibleButton to handle ImGuiButtonFlags_Repeat
|
||||
const ImGuiID id = window->GetID("##iconijbutton");
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "SessionSource.h"
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "ScreenCaptureSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "SrtReceiverSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
@@ -1369,7 +1370,7 @@ void ImGuiVisitor::visit (PatternSource& s)
|
||||
{
|
||||
for (uint p = 0; p < Pattern::count(); ++p){
|
||||
pattern_descriptor pattern = Pattern::get(p);
|
||||
std::string label = pattern.label + (pattern.animated ? " " ICON_FA_CARET_RIGHT : " ");
|
||||
std::string label = pattern.label + (pattern.animated ? " " ICON_FA_PLAY_CIRCLE : " ");
|
||||
if (pattern.available && ImGui::Selectable( label.c_str(), p == s.pattern()->type() )) {
|
||||
s.setPattern(p, s.pattern()->resolution());
|
||||
info.reset();
|
||||
@@ -1419,16 +1420,86 @@ void ImGuiVisitor::visit (DeviceSource& s)
|
||||
for (int d = 0; d < Device::manager().numDevices(); ++d){
|
||||
std::string namedev = Device::manager().name(d);
|
||||
if (ImGui::Selectable( namedev.c_str() )) {
|
||||
s.setDevice(namedev);
|
||||
info.reset();
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << " Device " << namedev;
|
||||
Action::manager().store(oss.str());
|
||||
if ( namedev.compare(s.device())==0 )
|
||||
s.reconnect();
|
||||
else {
|
||||
s.setDevice(namedev);
|
||||
info.reset();
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << " Device " << namedev;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImVec2 botom = ImGui::GetCursorPos();
|
||||
|
||||
// icon (>) to open player
|
||||
if ( s.playable() ) {
|
||||
ImGui::SetCursorPos(top);
|
||||
std::string msg = s.playing() ? "Open Player\n(source is playing)" : "Open Player\n(source is paused)";
|
||||
if (ImGuiToolkit::IconButton( s.playing() ? ICON_FA_PLAY_CIRCLE : ICON_FA_PAUSE_CIRCLE, msg.c_str()))
|
||||
UserInterface::manager().showSourceEditor(&s);
|
||||
top.x += ImGui::GetFrameHeight();
|
||||
}
|
||||
|
||||
// icon to show gstreamer properties
|
||||
ImGui::SetCursorPos(top);
|
||||
ImGuiToolkit::Icon(16, 16, false);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
int index = Device::manager().index( s.device() );
|
||||
std::string prop = Device::manager().properties( index );
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 14.0f);
|
||||
ImGui::TextUnformatted( prop.c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
ImGui::SetCursorPos(botom);
|
||||
}
|
||||
else {
|
||||
|
||||
info.reset();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ImGuiVisitor::visit (ScreenCaptureSource& s)
|
||||
{
|
||||
ImVec2 top = ImGui::GetCursorPos();
|
||||
top.x = 0.5f * ImGui::GetFrameHeight() + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN;
|
||||
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN);
|
||||
s.accept(info);
|
||||
ImGui::Text("%s", info.str().c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::Spacing();
|
||||
|
||||
if ( !s.failed() ) {
|
||||
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if (ImGui::BeginCombo("Window", s.window().c_str()))
|
||||
{
|
||||
for (int d = 0; d < ScreenCapture::manager().numWindow(); ++d){
|
||||
std::string namedev = ScreenCapture::manager().name(d);
|
||||
if (ImGui::Selectable( namedev.c_str() )) {
|
||||
if ( namedev.compare(s.window())==0 )
|
||||
s.reconnect();
|
||||
else {
|
||||
s.setWindow(namedev);
|
||||
info.reset();
|
||||
std::ostringstream oss;
|
||||
oss << s.name() << " Window " << namedev;
|
||||
Action::manager().store(oss.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImVec2 botom = ImGui::GetCursorPos();
|
||||
|
||||
// icon (>) to open player
|
||||
@@ -1441,11 +1512,15 @@ void ImGuiVisitor::visit (DeviceSource& s)
|
||||
|
||||
ImGui::SetCursorPos(botom);
|
||||
}
|
||||
else
|
||||
else {
|
||||
|
||||
info.reset();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ImGuiVisitor::visit (NetworkSource& s)
|
||||
{
|
||||
ImVec2 top = ImGui::GetCursorPos();
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
void visit (RenderSource& s) override;
|
||||
void visit (PatternSource& s) override;
|
||||
void visit (DeviceSource& s) override;
|
||||
void visit (ScreenCaptureSource& s) override;
|
||||
void visit (NetworkSource& s) override;
|
||||
void visit (MultiFileSource& s) override;
|
||||
void visit (GenericStreamSource& s) override;
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "SessionSource.h"
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "ScreenCaptureSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "SrtReceiverSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
@@ -296,9 +297,9 @@ void InfoVisitor::visit (DeviceSource& s)
|
||||
|
||||
std::ostringstream oss;
|
||||
|
||||
DeviceConfigSet confs = Device::manager().config( Device::manager().index(s.device()));
|
||||
GstToolkit::PipelineConfigSet confs = Device::manager().config( Device::manager().index(s.device()));
|
||||
if ( !confs.empty()) {
|
||||
DeviceConfig best = *confs.rbegin();
|
||||
GstToolkit::PipelineConfig best = *confs.rbegin();
|
||||
float fps = static_cast<float>(best.fps_numerator) / static_cast<float>(best.fps_denominator);
|
||||
|
||||
if (brief_) {
|
||||
@@ -322,6 +323,41 @@ void InfoVisitor::visit (DeviceSource& s)
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
|
||||
void InfoVisitor::visit (ScreenCaptureSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
return;
|
||||
|
||||
std::ostringstream oss;
|
||||
|
||||
GstToolkit::PipelineConfigSet confs = ScreenCapture::manager().config( ScreenCapture::manager().index(s.window()));
|
||||
if ( !confs.empty()) {
|
||||
GstToolkit::PipelineConfig best = *confs.rbegin();
|
||||
float fps = static_cast<float>(best.fps_numerator) / static_cast<float>(best.fps_denominator);
|
||||
|
||||
if (brief_) {
|
||||
oss << best.stream << " " << best.format << ", ";
|
||||
oss << best.width << " x " << best.height << ", ";
|
||||
oss << std::fixed << std::setprecision(0) << fps << "fps";
|
||||
}
|
||||
else {
|
||||
oss << s.window() << std::endl;
|
||||
oss << ScreenCapture::manager().description( ScreenCapture::manager().index(s.window()));
|
||||
oss << ", " << best.stream << " " << best.format << std::endl;
|
||||
oss << best.width << " x " << best.height << ", ";
|
||||
oss << std::fixed << std::setprecision(1) << fps << " fps";
|
||||
}
|
||||
}
|
||||
else {
|
||||
oss << s.window() << " not available.";
|
||||
}
|
||||
|
||||
information_ = oss.str();
|
||||
current_id_ = s.id();
|
||||
}
|
||||
|
||||
|
||||
void InfoVisitor::visit (NetworkSource& s)
|
||||
{
|
||||
if (current_id_ == s.id())
|
||||
|
||||
@@ -33,6 +33,7 @@ public:
|
||||
void visit (CloneSource& s) override;
|
||||
void visit (PatternSource& s) override;
|
||||
void visit (DeviceSource& s) override;
|
||||
void visit (ScreenCaptureSource& s) override;
|
||||
void visit (NetworkSource& s) override;
|
||||
void visit (MultiFileSource& s) override;
|
||||
void visit (GenericStreamSource& s) override;
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "MediaSource.h"
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "ScreenCaptureSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
#include "StreamSource.h"
|
||||
#include "NetworkSource.h"
|
||||
@@ -390,6 +391,18 @@ Source * Mixer::createSourceDevice(const std::string &namedevice)
|
||||
return s;
|
||||
}
|
||||
|
||||
Source * Mixer::createSourceScreen(const std::string &namewindow)
|
||||
{
|
||||
// ready to create a source
|
||||
ScreenCaptureSource *s = new ScreenCaptureSource;
|
||||
s->setWindow(namewindow);
|
||||
|
||||
// propose a new name based on pattern name
|
||||
s->setName( namewindow.substr(0, namewindow.find(" ")) );
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
Source * Mixer::createSourceNetwork(const std::string &nameconnection)
|
||||
{
|
||||
// ready to create a source
|
||||
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
Source * createSourceStream (const std::string &gstreamerpipeline);
|
||||
Source * createSourcePattern(uint pattern, glm::ivec2 res);
|
||||
Source * createSourceDevice (const std::string &namedevice);
|
||||
Source * createSourceScreen (const std::string &namedevice);
|
||||
Source * createSourceNetwork(const std::string &nameconnection);
|
||||
Source * createSourceSrt (const std::string &ip, const std::string &port);
|
||||
Source * createSourceGroup ();
|
||||
|
||||
620
src/ScreenCaptureSource.cpp
Normal file
620
src/ScreenCaptureSource.cpp
Normal file
@@ -0,0 +1,620 @@
|
||||
/*
|
||||
* This file is part of vimix - video live mixer
|
||||
*
|
||||
* **Copyright** (C) 2019-2023 Bruno Herbelin <bruno.herbelin@gmail.com>
|
||||
*
|
||||
* 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <gst/pbutils/gstdiscoverer.h>
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "Log.h"
|
||||
#include "Decorations.h"
|
||||
#include "Stream.h"
|
||||
#include "Visitor.h"
|
||||
|
||||
#include "ScreenCaptureSource.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define SCREENCAPTURE_DEBUG
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if defined(APPLE)
|
||||
std::string gst_plugin_vidcap = "avfvideosrc capture-screen=true";
|
||||
#else
|
||||
std::string gst_plugin_vidcap = "ximagesrc show-pointer=false";
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xproto.h>
|
||||
int X11_error_handler(Display *d, XErrorEvent *e);
|
||||
std::map<unsigned long, std::string> getListX11Windows();
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
struct hasScreenCaptureId
|
||||
{
|
||||
inline bool operator()(const ScreenCaptureHandle &elem) const {
|
||||
return (elem.id == _id);
|
||||
}
|
||||
explicit hasScreenCaptureId(unsigned long id) : _id(id) { }
|
||||
private:
|
||||
unsigned long _id;
|
||||
};
|
||||
|
||||
struct hasScreenCaptureName
|
||||
{
|
||||
inline bool operator()(const ScreenCaptureHandle &elem) const {
|
||||
return (elem.name.compare(_name) == 0);
|
||||
}
|
||||
explicit hasScreenCaptureName(const std::string &name) : _name(name) { }
|
||||
private:
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
struct hasAssociatedSource
|
||||
{
|
||||
inline bool operator()(const ScreenCaptureHandle &elem) const {
|
||||
auto sit = std::find(elem.associated_sources.begin(), elem.associated_sources.end(), s_);
|
||||
return sit != elem.associated_sources.end();
|
||||
}
|
||||
explicit hasAssociatedSource(Source *s) : s_(s) { }
|
||||
private:
|
||||
Source *s_;
|
||||
};
|
||||
|
||||
void ScreenCaptureHandle::update(const std::string &newname)
|
||||
{
|
||||
GstToolkit::PipelineConfigSet confs = GstToolkit::getPipelineConfigs(pipeline);
|
||||
|
||||
if (!confs.empty()) {
|
||||
GstToolkit::PipelineConfig best = *confs.rbegin();
|
||||
GstToolkit::PipelineConfigSet confscreen;
|
||||
// limit framerate to 30fps
|
||||
best.fps_numerator = MIN( best.fps_numerator, 30);
|
||||
best.fps_denominator = 1;
|
||||
confscreen.insert(best);
|
||||
|
||||
configs = confscreen;
|
||||
}
|
||||
name = newname;
|
||||
}
|
||||
|
||||
void ScreenCapture::add(const std::string &windowname, const std::string &pipeline, unsigned long id)
|
||||
{
|
||||
std::string pln = pipeline;
|
||||
|
||||
#if defined(LINUX)
|
||||
if (id > 0)
|
||||
pln += " xid=" + std::to_string(id);
|
||||
#endif
|
||||
|
||||
GstToolkit::PipelineConfigSet confs = GstToolkit::getPipelineConfigs(pln);
|
||||
|
||||
if (!confs.empty()) {
|
||||
GstToolkit::PipelineConfig best = *confs.rbegin();
|
||||
GstToolkit::PipelineConfigSet confscreen;
|
||||
// limit framerate to 30fps
|
||||
best.fps_numerator = MIN( best.fps_numerator, 30);
|
||||
best.fps_denominator = 1;
|
||||
confscreen.insert(best);
|
||||
|
||||
// remove previous occurence if already exists
|
||||
remove(windowname, id);
|
||||
|
||||
// add to config list
|
||||
access_.lock();
|
||||
ScreenCaptureHandle handle;
|
||||
handle.name = windowname;
|
||||
handle.pipeline = pln;
|
||||
handle.id = id;
|
||||
handle.configs = confscreen;
|
||||
|
||||
handles_.push_back(handle);
|
||||
access_.unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ScreenCapture::remove(const std::string &windowname, unsigned long id)
|
||||
{
|
||||
// lock before change
|
||||
access_.lock();
|
||||
|
||||
std::vector< ScreenCaptureHandle >::const_iterator handle = handles_.cend();
|
||||
|
||||
// if id is provided, find a device with the id
|
||||
if (id>0)
|
||||
handle = std::find_if(handles_.cbegin(), handles_.cend(), hasScreenCaptureId(id) );
|
||||
|
||||
// nofound : find device with the window name
|
||||
if ( handle == handles_.cend() )
|
||||
handle = std::find_if(handles_.cbegin(), handles_.cend(), hasScreenCaptureName(windowname) );
|
||||
|
||||
// found handle either by id or by name
|
||||
if ( handle != handles_.cend() ) {
|
||||
|
||||
// just inform if there is no source connected
|
||||
if (handle->associated_sources.empty()) {
|
||||
Log::Info("Window %s closed.", windowname.c_str());
|
||||
}
|
||||
else {
|
||||
// otherwise unplug all sources and close their streams
|
||||
for (auto sit = handle->associated_sources.begin(); sit != handle->associated_sources.end(); ++sit) {
|
||||
// inform user
|
||||
Log::Warning("Window %s closed: source %s deleted.", windowname.c_str(), (*sit)->name().c_str());
|
||||
(*sit)->trash();
|
||||
}
|
||||
}
|
||||
|
||||
// remove the handle
|
||||
handles_.erase(handle);
|
||||
}
|
||||
|
||||
// unlock access
|
||||
access_.unlock();
|
||||
}
|
||||
|
||||
|
||||
//#ifdef LINUX
|
||||
//Display *x11_display = NULL;
|
||||
//#endif
|
||||
|
||||
ScreenCapture::ScreenCapture(): monitor_initialized_(false)
|
||||
{
|
||||
//#ifdef LINUX
|
||||
// // Capture X11 errors to prevent crashing when capturing a window that is closed
|
||||
// x11_display = XOpenDisplay(NULL);
|
||||
// XSetErrorHandler(X11_error_handler);
|
||||
//#endif
|
||||
|
||||
// launch monitor
|
||||
std::thread(launchMonitoring, this).detach();
|
||||
}
|
||||
|
||||
|
||||
ScreenCapture::~ScreenCapture()
|
||||
{
|
||||
//#ifdef LINUX
|
||||
// XCloseDisplay(x11_display);
|
||||
//#endif
|
||||
}
|
||||
|
||||
void ScreenCapture::launchMonitoring(ScreenCapture *sc)
|
||||
{
|
||||
#if defined(LINUX)
|
||||
|
||||
// Get the list of windows in X11
|
||||
std::map<unsigned long,std::string> windowlist = getListX11Windows();
|
||||
// Add the window id=0 for screen capture entire screen
|
||||
windowlist[0] = SCREEN_CAPTURE_NAME;
|
||||
|
||||
// Go through the current list of window handles
|
||||
sc->access_.lock();
|
||||
for (auto hit = sc->handles_.begin(); hit != sc->handles_.end();) {
|
||||
// if this window handle is still in X11 list
|
||||
if (windowlist.count(hit->id) > 0) {
|
||||
// update name of window (could have changed)
|
||||
hit->update(windowlist.at(hit->id));
|
||||
// remove from list of X11 windows
|
||||
windowlist.erase(hit->id);
|
||||
// keep in the window handles
|
||||
++hit;
|
||||
}
|
||||
else {
|
||||
// remove from window handles
|
||||
hit = sc->handles_.erase(hit);
|
||||
}
|
||||
}
|
||||
sc->access_.unlock();
|
||||
|
||||
// Add in window handles list the remaining X11 windows
|
||||
for (auto wit = windowlist.cbegin(); wit != windowlist.cend(); ++wit) {
|
||||
// Add capture screen of this window
|
||||
sc->add( wit->second, gst_plugin_vidcap, wit->first);
|
||||
}
|
||||
|
||||
// monitor is initialized
|
||||
sc->monitor_initialized_ = true;
|
||||
sc->monitor_initialization_.notify_all();
|
||||
|
||||
// give time before continuing
|
||||
std::this_thread::sleep_for ( std::chrono::seconds(2) );
|
||||
|
||||
// de-initialize so next call will update
|
||||
sc->monitor_initialized_ = false;
|
||||
|
||||
#else
|
||||
// only one screen capture possibility on OSX
|
||||
sc->add(SCREEN_CAPTURE_NAME, gst_plugin_vidcap);
|
||||
|
||||
// monitor is initialized
|
||||
sc->monitor_initialized_ = true;
|
||||
sc->monitor_initialization_.notify_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ScreenCapture::initialized()
|
||||
{
|
||||
return ScreenCapture::manager().monitor_initialized_;
|
||||
}
|
||||
|
||||
void ScreenCapture::reload()
|
||||
{
|
||||
if ( !ScreenCapture::initialized() ) {
|
||||
// relaunch monitor
|
||||
std::thread(launchMonitoring, this).detach();
|
||||
|
||||
// wait for monitor initialization (if not already initialized)
|
||||
std::mutex mtx;
|
||||
std::unique_lock<std::mutex> lck(mtx);
|
||||
ScreenCapture::manager().monitor_initialization_.wait(lck, ScreenCapture::initialized);
|
||||
}
|
||||
}
|
||||
|
||||
int ScreenCapture::numWindow()
|
||||
{
|
||||
reload();
|
||||
|
||||
access_.lock();
|
||||
int ret = handles_.size();
|
||||
access_.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ScreenCapture::exists(const std::string &window)
|
||||
{
|
||||
reload();
|
||||
|
||||
access_.lock();
|
||||
auto h = std::find_if(handles_.cbegin(), handles_.cend(), hasScreenCaptureName(window));
|
||||
bool ret = (h != handles_.cend());
|
||||
access_.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string ScreenCapture::name(int index)
|
||||
{
|
||||
std::string ret = "";
|
||||
|
||||
access_.lock();
|
||||
if (index > -1 && index < (int) handles_.size())
|
||||
ret = handles_[index].name;
|
||||
access_.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string ScreenCapture::description(int index)
|
||||
{
|
||||
std::string ret = "";
|
||||
|
||||
access_.lock();
|
||||
if (index > -1 && index < (int) handles_.size())
|
||||
ret = handles_[index].pipeline;
|
||||
access_.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
GstToolkit::PipelineConfigSet ScreenCapture::config(int index)
|
||||
{
|
||||
GstToolkit::PipelineConfigSet ret;
|
||||
|
||||
access_.lock();
|
||||
if (index > -1 && index < (int) handles_.size())
|
||||
ret = handles_[index].configs;
|
||||
access_.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ScreenCapture::index(const std::string &window)
|
||||
{
|
||||
int i = -1;
|
||||
|
||||
access_.lock();
|
||||
auto h = std::find_if(handles_.cbegin(), handles_.cend(), hasScreenCaptureName(window));
|
||||
if (h != handles_.cend())
|
||||
i = std::distance(handles_.cbegin(), h);
|
||||
access_.unlock();
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
ScreenCaptureSource::ScreenCaptureSource(uint64_t id) : StreamSource(id), failure_(FAIL_NONE)
|
||||
{
|
||||
// set symbol
|
||||
symbol_ = new Symbol(Symbol::SCREEN, glm::vec3(0.75f, 0.75f, 0.01f));
|
||||
symbol_->scale_.y = 1.5f;
|
||||
}
|
||||
|
||||
ScreenCaptureSource::~ScreenCaptureSource()
|
||||
{
|
||||
unsetWindow();
|
||||
}
|
||||
|
||||
void ScreenCaptureSource::unsetWindow()
|
||||
{
|
||||
// unregister this device source from a Device handler
|
||||
auto h = std::find_if(ScreenCapture::manager().handles_.begin(), ScreenCapture::manager().handles_.end(), hasAssociatedSource(this));
|
||||
if (h != ScreenCapture::manager().handles_.end())
|
||||
{
|
||||
// remove this pointer to the list of connected sources
|
||||
h->associated_sources.remove(this);
|
||||
// if this is the last source connected to the device handler
|
||||
// the stream will be removed by the ~StreamSource destructor
|
||||
// and the device handler should not keep reference to it
|
||||
if (h->associated_sources.empty())
|
||||
// otherwise just cancel the reference to the stream
|
||||
h->stream = nullptr;
|
||||
// else this means another DeviceSource is using this stream
|
||||
// and we should avoid to delete the stream in the ~StreamSource destructor
|
||||
else
|
||||
stream_ = nullptr;
|
||||
}
|
||||
|
||||
window_ = "";
|
||||
}
|
||||
|
||||
|
||||
void ScreenCaptureSource::reconnect()
|
||||
{
|
||||
// remember name
|
||||
std::string d = window_;
|
||||
// disconnect
|
||||
unsetWindow();
|
||||
// connect
|
||||
setWindow(d);
|
||||
}
|
||||
|
||||
void ScreenCaptureSource::setWindow(const std::string &windowname)
|
||||
{
|
||||
if (window_.compare(windowname) == 0)
|
||||
return;
|
||||
|
||||
// instanciate and wait for monitor initialization if not already initialized
|
||||
ScreenCapture::manager().reload();
|
||||
|
||||
// if changing device
|
||||
if (!window_.empty())
|
||||
unsetWindow();
|
||||
|
||||
// if the stream referenced in this source remains after unsetDevice
|
||||
if (stream_) {
|
||||
delete stream_;
|
||||
stream_ = nullptr;
|
||||
}
|
||||
|
||||
// remember device name
|
||||
window_ = windowname;
|
||||
|
||||
// check existence of a window handle with that name
|
||||
auto h = std::find_if(ScreenCapture::manager().handles_.begin(), ScreenCapture::manager().handles_.end(), hasScreenCaptureName(window_));
|
||||
|
||||
// found a device handle
|
||||
if ( h != ScreenCapture::manager().handles_.end()) {
|
||||
|
||||
// find if a DeviceHandle with this device name already has a stream that is open
|
||||
if ( h->stream != nullptr ) {
|
||||
// just use it !
|
||||
stream_ = h->stream;
|
||||
// reinit to adapt to new stream
|
||||
init();
|
||||
}
|
||||
else {
|
||||
// start filling in the gstreamer pipeline
|
||||
std::ostringstream pipeline;
|
||||
pipeline << h->pipeline;
|
||||
|
||||
// test the device and get config
|
||||
GstToolkit::PipelineConfigSet confs = h->configs;
|
||||
#ifdef SCREENCAPTURE_DEBUG
|
||||
g_printerr("\nScreenCapture '%s' added with configs:\n", h->pipeline.c_str());
|
||||
for( GstToolkit::PipelineConfigSet::iterator it = confs.begin(); it != confs.end(); ++it ){
|
||||
float fps = static_cast<float>((*it).fps_numerator) / static_cast<float>((*it).fps_denominator);
|
||||
g_printerr(" - %s %s %d x %d %.1f fps\n", (*it).stream.c_str(), (*it).format.c_str(), (*it).width, (*it).height, fps);
|
||||
}
|
||||
#endif
|
||||
if (!confs.empty()) {
|
||||
GstToolkit::PipelineConfig best = *confs.rbegin();
|
||||
float fps = static_cast<float>(best.fps_numerator) / static_cast<float>(best.fps_denominator);
|
||||
Log::Info("ScreenCapture %s selected its optimal config: %s %s %dx%d@%.1ffps", window_.c_str(), best.stream.c_str(), best.format.c_str(), best.width, best.height, fps);
|
||||
|
||||
pipeline << " ! " << best.stream;
|
||||
if (!best.format.empty())
|
||||
pipeline << ",format=" << best.format;
|
||||
pipeline << ",framerate=" << best.fps_numerator << "/" << best.fps_denominator;
|
||||
|
||||
// convert (force alpha to 1)
|
||||
pipeline << " ! alpha alpha=1 ! queue ! videoconvertscale";
|
||||
|
||||
// delete and reset render buffer to enforce re-init of StreamSource
|
||||
if (renderbuffer_)
|
||||
delete renderbuffer_;
|
||||
renderbuffer_ = nullptr;
|
||||
|
||||
// new stream
|
||||
stream_ = h->stream = new Stream;
|
||||
|
||||
// open gstreamer
|
||||
h->stream->open( pipeline.str(), best.width, best.height);
|
||||
h->stream->play(true);
|
||||
}
|
||||
}
|
||||
|
||||
// reference this source in the handle
|
||||
h->associated_sources.push_back(this);
|
||||
|
||||
// will be ready after init and one frame rendered
|
||||
ready_ = false;
|
||||
}
|
||||
else {
|
||||
trash();
|
||||
Log::Warning("No window named '%s'", window_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenCaptureSource::setActive (bool on)
|
||||
{
|
||||
bool was_active = active_;
|
||||
|
||||
// try to activate (may fail if source is cloned)
|
||||
Source::setActive(on);
|
||||
|
||||
if (stream_) {
|
||||
// change status of stream (only if status changed)
|
||||
if (active_ != was_active) {
|
||||
|
||||
// activate a source if any of the handled device source is active
|
||||
auto h = std::find_if(ScreenCapture::manager().handles_.begin(), ScreenCapture::manager().handles_.end(), hasAssociatedSource(this));
|
||||
if (h != ScreenCapture::manager().handles_.end()) {
|
||||
bool streamactive = false;
|
||||
for (auto sit = h->associated_sources.begin(); sit != h->associated_sources.end(); ++sit) {
|
||||
if ( (*sit)->active_ )
|
||||
streamactive = true;
|
||||
}
|
||||
stream_->enable(streamactive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ScreenCaptureSource::accept(Visitor& v)
|
||||
{
|
||||
StreamSource::accept(v);
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
Source::Failure ScreenCaptureSource::failed() const
|
||||
{
|
||||
if ( StreamSource::failed() )
|
||||
return FAIL_CRITICAL;
|
||||
|
||||
return failure_;
|
||||
}
|
||||
|
||||
glm::ivec2 ScreenCaptureSource::icon() const
|
||||
{
|
||||
return glm::ivec2(ICON_SOURCE_DEVICE_SCREEN);
|
||||
}
|
||||
|
||||
std::string ScreenCaptureSource::info() const
|
||||
{
|
||||
return "Screen capture";
|
||||
}
|
||||
|
||||
|
||||
#if defined(LINUX)
|
||||
|
||||
//int X11_error_handler(Display *d, XErrorEvent *e)
|
||||
//{
|
||||
//#ifdef SCREENCAPTURE_DEBUG
|
||||
// char msg[80];
|
||||
// XGetErrorText(d, e->error_code, msg, sizeof(msg));
|
||||
|
||||
// g_printerr("\nX11 Error %d (%s): request %d.%d on xid %lu\n",
|
||||
// e->error_code, msg, e->request_code,
|
||||
// e->minor_code, e->resourceid);
|
||||
//#endif
|
||||
|
||||
// if (e->request_code == X_GetWindowAttributes || e->request_code == X_GetImage)
|
||||
// ScreenCapture::manager().remove(SCREEN_CAPTURE_NAME, e->resourceid);
|
||||
|
||||
// return 0;
|
||||
//}
|
||||
|
||||
xcb_atom_t getatom(xcb_connection_t* c, const char *atom_name)
|
||||
{
|
||||
xcb_intern_atom_cookie_t atom_cookie;
|
||||
xcb_atom_t atom = 0;
|
||||
xcb_intern_atom_reply_t *rep;
|
||||
|
||||
atom_cookie = xcb_intern_atom(c, 0, strlen(atom_name), atom_name);
|
||||
rep = xcb_intern_atom_reply(c, atom_cookie, NULL);
|
||||
if (NULL != rep) {
|
||||
atom = rep->atom;
|
||||
free(rep);
|
||||
}
|
||||
return atom;
|
||||
}
|
||||
|
||||
|
||||
std::map<unsigned long,std::string> getListX11Windows()
|
||||
{
|
||||
std::map<unsigned long,std::string> windowlist;
|
||||
|
||||
xcb_generic_error_t *e;
|
||||
xcb_connection_t* c = xcb_connect(NULL, NULL);
|
||||
static xcb_atom_t net_client_list = getatom(c, "_NET_CLIENT_LIST");
|
||||
static xcb_atom_t net_wm_name = getatom(c, "_NET_WM_NAME");
|
||||
xcb_screen_t* screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
|
||||
|
||||
xcb_get_property_cookie_t prop_cookie_list;
|
||||
xcb_get_property_reply_t *reply_prop_list;
|
||||
prop_cookie_list = xcb_get_property(c, 0, screen->root, net_client_list, XCB_GET_PROPERTY_TYPE_ANY, 0, 100);
|
||||
reply_prop_list = xcb_get_property_reply(c, prop_cookie_list, &e);
|
||||
if(e) {
|
||||
free(e);
|
||||
}
|
||||
if(reply_prop_list) {
|
||||
int value_len = xcb_get_property_value_length(reply_prop_list);
|
||||
if(value_len) {
|
||||
xcb_window_t* win = (xcb_window_t*) xcb_get_property_value(reply_prop_list);
|
||||
for(int i=0; i<value_len/4; i++) {
|
||||
xcb_get_property_cookie_t prop_cookie = xcb_get_property(c, 0, win[i], net_wm_name, XCB_GET_PROPERTY_TYPE_ANY, 0, 1000);
|
||||
xcb_get_property_reply_t *reply_prop = xcb_get_property_reply(c, prop_cookie, &e);
|
||||
if(e) {
|
||||
free(e);
|
||||
}
|
||||
if(reply_prop) {
|
||||
int reply_len = xcb_get_property_value_length(reply_prop);
|
||||
if( reply_len > 0) {
|
||||
char windowname[1000];
|
||||
snprintf(windowname, reply_len + 1, "%s", (char *) xcb_get_property_value(reply_prop) );
|
||||
if (isalpha(windowname[0]))
|
||||
windowlist[ win[i] ] = std::string ( windowname );
|
||||
}
|
||||
free(reply_prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(reply_prop_list);
|
||||
}
|
||||
|
||||
return windowlist;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
110
src/ScreenCaptureSource.h
Normal file
110
src/ScreenCaptureSource.h
Normal file
@@ -0,0 +1,110 @@
|
||||
#ifndef SCREENCAPTURESOURCE_H
|
||||
#define SCREENCAPTURESOURCE_H
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#include "GstToolkit.h"
|
||||
#include "StreamSource.h"
|
||||
|
||||
#define SCREEN_CAPTURE_NAME "Screen Capture"
|
||||
|
||||
class ScreenCaptureSource : public StreamSource
|
||||
{
|
||||
friend class ScreenCapture;
|
||||
|
||||
public:
|
||||
ScreenCaptureSource(uint64_t id = 0);
|
||||
~ScreenCaptureSource();
|
||||
|
||||
// Source interface
|
||||
Failure failed() const override;
|
||||
void accept (Visitor& v) override;
|
||||
void setActive (bool on) override;
|
||||
|
||||
// StreamSource interface
|
||||
Stream *stream() const override { return stream_; }
|
||||
|
||||
// specific interface
|
||||
void setWindow(const std::string &windowname);
|
||||
inline std::string window() const { return window_; }
|
||||
void reconnect();
|
||||
|
||||
glm::ivec2 icon() const override;
|
||||
inline std::string info() const override;
|
||||
|
||||
protected:
|
||||
|
||||
void unplug() { failure_ = FAIL_CRITICAL; }
|
||||
void trash() { failure_ = FAIL_FATAL; }
|
||||
|
||||
private:
|
||||
std::string window_;
|
||||
std::atomic<Source::Failure> failure_;
|
||||
void unsetWindow();
|
||||
};
|
||||
|
||||
struct ScreenCaptureHandle {
|
||||
|
||||
std::string name;
|
||||
std::string pipeline;
|
||||
unsigned long id;
|
||||
|
||||
GstToolkit::PipelineConfigSet configs;
|
||||
|
||||
Stream *stream;
|
||||
std::list<ScreenCaptureSource *> associated_sources;
|
||||
|
||||
ScreenCaptureHandle() : id(0), stream(nullptr) {}
|
||||
void update(const std::string &newname);
|
||||
};
|
||||
|
||||
class ScreenCapture
|
||||
{
|
||||
friend class ScreenCaptureSource;
|
||||
|
||||
ScreenCapture();
|
||||
~ScreenCapture();
|
||||
ScreenCapture(ScreenCapture const& copy) = delete;
|
||||
ScreenCapture& operator=(ScreenCapture const& copy) = delete;
|
||||
|
||||
public:
|
||||
|
||||
static ScreenCapture& manager()
|
||||
{
|
||||
// The only instance
|
||||
static ScreenCapture _instance;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
int numWindow () ;
|
||||
std::string name (int index) ;
|
||||
std::string description (int index) ;
|
||||
GstToolkit::PipelineConfigSet config (int index) ;
|
||||
|
||||
int index (const std::string &window);
|
||||
bool exists (const std::string &window);
|
||||
void reload ();
|
||||
|
||||
void add (const std::string &windowname, const std::string &pipeline, unsigned long id=0);
|
||||
void remove(const std::string &windowname, unsigned long id=0);
|
||||
|
||||
private:
|
||||
|
||||
static void launchMonitoring(ScreenCapture *d);
|
||||
static bool initialized();
|
||||
static bool need_refresh();
|
||||
|
||||
std::mutex access_;
|
||||
std::vector< ScreenCaptureHandle > handles_;
|
||||
|
||||
std::condition_variable monitor_initialization_;
|
||||
bool monitor_initialized_;
|
||||
std::mutex monitor_access_;
|
||||
|
||||
};
|
||||
|
||||
#endif // SCREENCAPTURESOURCE_H
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "StreamSource.h"
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "ScreenCaptureSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "SrtReceiverSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
@@ -432,6 +433,9 @@ void SessionLoader::load(XMLElement *sessionNode)
|
||||
else if ( std::string(pType) == "DeviceSource") {
|
||||
load_source = new DeviceSource(id_xml_);
|
||||
}
|
||||
else if ( std::string(pType) == "ScreenCaptureSource") {
|
||||
load_source = new ScreenCaptureSource(id_xml_);
|
||||
}
|
||||
else if ( std::string(pType) == "NetworkSource") {
|
||||
load_source = new NetworkSource(id_xml_);
|
||||
}
|
||||
@@ -615,6 +619,9 @@ Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, Mode mode)
|
||||
else if ( std::string(pType) == "DeviceSource") {
|
||||
load_source = new DeviceSource(id__);
|
||||
}
|
||||
else if ( std::string(pType) == "ScreenCaptureSource") {
|
||||
load_source = new ScreenCaptureSource(id__);
|
||||
}
|
||||
else if ( std::string(pType) == "NetworkSource") {
|
||||
load_source = new NetworkSource(id__);
|
||||
}
|
||||
@@ -1212,6 +1219,16 @@ void SessionLoader::visit (DeviceSource& s)
|
||||
}
|
||||
|
||||
|
||||
void SessionLoader::visit (ScreenCaptureSource& s)
|
||||
{
|
||||
std::string winname = std::string ( xmlCurrent_->Attribute("window") );
|
||||
|
||||
// change only if different window
|
||||
if ( winname != s.window() )
|
||||
s.setWindow(winname);
|
||||
}
|
||||
|
||||
|
||||
void SessionLoader::visit (NetworkSource& s)
|
||||
{
|
||||
std::string connect = std::string ( xmlCurrent_->Attribute("connection") );
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
void visit (RenderSource& s) override;
|
||||
void visit (PatternSource& s) override;
|
||||
void visit (DeviceSource& s) override;
|
||||
void visit (ScreenCaptureSource& s) override;
|
||||
void visit (NetworkSource& s) override;
|
||||
void visit (MultiFileSource& s) override;
|
||||
void visit (GenericStreamSource& s) override;
|
||||
|
||||
@@ -38,6 +38,7 @@ using namespace tinyxml2;
|
||||
#include "SessionSource.h"
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "ScreenCaptureSource.h"
|
||||
#include "NetworkSource.h"
|
||||
#include "SrtReceiverSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
@@ -845,6 +846,12 @@ void SessionVisitor::visit (DeviceSource& s)
|
||||
xmlCurrent_->SetAttribute("device", s.device().c_str() );
|
||||
}
|
||||
|
||||
void SessionVisitor::visit (ScreenCaptureSource& s)
|
||||
{
|
||||
xmlCurrent_->SetAttribute("type", "ScreenCaptureSource");
|
||||
xmlCurrent_->SetAttribute("window", s.window().c_str() );
|
||||
}
|
||||
|
||||
void SessionVisitor::visit (NetworkSource& s)
|
||||
{
|
||||
xmlCurrent_->SetAttribute("type", "NetworkSource");
|
||||
|
||||
@@ -65,6 +65,7 @@ public:
|
||||
void visit (RenderSource&) override;
|
||||
void visit (PatternSource& s) override;
|
||||
void visit (DeviceSource& s) override;
|
||||
void visit (ScreenCaptureSource& s) override;
|
||||
void visit (NetworkSource& s) override;
|
||||
void visit (MixingGroup& s) override;
|
||||
void visit (MultiFileSource& s) override;
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
|
||||
#define ICON_SOURCE_VIDEO 18, 13
|
||||
#define ICON_SOURCE_IMAGE 4, 9
|
||||
#define ICON_SOURCE_DEVICE_SCREEN 19, 1
|
||||
#define ICON_SOURCE_DEVICE_SCREEN 0, 2
|
||||
#define ICON_SOURCE_DEVICE 2, 14
|
||||
#define ICON_SOURCE_SEQUENCE 3, 9
|
||||
#define ICON_SOURCE_NETWORK 18, 11
|
||||
#define ICON_SOURCE_PATTERN 11, 5
|
||||
#define ICON_SOURCE_SESSION 19, 6
|
||||
#define ICON_SOURCE_GROUP 10, 6
|
||||
#define ICON_SOURCE_RENDER 0, 2
|
||||
#define ICON_SOURCE_RENDER 19, 1
|
||||
#define ICON_SOURCE_CLONE 9, 2
|
||||
#define ICON_SOURCE_GSTREAMER 16, 16
|
||||
#define ICON_SOURCE_SRT 14, 5
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
#include "MediaSource.h"
|
||||
#include "PatternSource.h"
|
||||
#include "DeviceSource.h"
|
||||
#include "ScreenCaptureSource.h"
|
||||
#include "MultiFileSource.h"
|
||||
#include "ShmdataBroadcast.h"
|
||||
#include "VideoBroadcast.h"
|
||||
@@ -2383,7 +2384,7 @@ void UserInterface::RenderHelp()
|
||||
ImGui::Text ("Connected webcam or frame grabber.");
|
||||
ImGui::NextColumn();
|
||||
ImGuiToolkit::Icon(ICON_SOURCE_DEVICE_SCREEN); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Screen"); ImGui::NextColumn();
|
||||
ImGui::Text ("Screen capture.");
|
||||
ImGui::Text ("Screen capture of the entire screen or a selected window.");
|
||||
ImGui::NextColumn();
|
||||
ImGuiToolkit::Icon(ICON_SOURCE_NETWORK); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Shared"); ImGui::NextColumn();
|
||||
ImGui::Text ("Connected stream from another vimix in the local network (shared output stream).");
|
||||
@@ -2405,8 +2406,8 @@ void UserInterface::RenderHelp()
|
||||
ImGui::Text(ICON_FA_SYNC); ImGui::NextColumn();
|
||||
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_BOLD); ImGui::Text("Internal");ImGui::PopFont();
|
||||
ImGui::NextColumn();
|
||||
ImGuiToolkit::Icon(ICON_SOURCE_RENDER); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Rendering"); ImGui::NextColumn();
|
||||
ImGui::Text ("Displays the rendering output as a source, with or without loopback.");
|
||||
ImGuiToolkit::Icon(ICON_SOURCE_RENDER); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Loopback"); ImGui::NextColumn();
|
||||
ImGui::Text ("Loopback the rendering output as a source, with or without recursion.");
|
||||
ImGui::NextColumn();
|
||||
ImGuiToolkit::Icon(ICON_SOURCE_CLONE); ImGui::SameLine(0, IMGUI_SAME_LINE);ImGui::Text("Clone"); ImGui::NextColumn();
|
||||
ImGui::Text ("Clones the frames of a source into another one, and applies a filter on the way.");
|
||||
@@ -2639,6 +2640,7 @@ void Navigator::clearNewPannel()
|
||||
pattern_type = -1;
|
||||
custom_pipeline = false;
|
||||
custom_connected = false;
|
||||
custom_screencapture = false;
|
||||
sourceSequenceFiles.clear();
|
||||
sourceMediaFileCurrent.clear();
|
||||
new_media_mode_changed = true;
|
||||
@@ -3661,32 +3663,59 @@ void Navigator::RenderNewPannel()
|
||||
ImGui::Text("Input device or stream:");
|
||||
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if (ImGui::BeginCombo("##External", "Select "))
|
||||
if (ImGui::BeginCombo("##ExternalConnected", "Select "))
|
||||
{
|
||||
// 1. Loopback source
|
||||
if ( ImGui::Selectable("Display Loopback") ) {
|
||||
custom_connected = false;
|
||||
custom_screencapture = false;
|
||||
new_source_preview_.setSource( Mixer::manager().createSourceRender(), "Display Loopback");
|
||||
}
|
||||
|
||||
// 2. Screen capture (open selector if more than one window)
|
||||
if (ScreenCapture::manager().numWindow() > 0) {
|
||||
std::string namewin = ScreenCapture::manager().name(0);
|
||||
if (ImGui::Selectable(namewin.c_str()) ) {
|
||||
custom_connected = false;
|
||||
if (ScreenCapture::manager().numWindow() > 1) {
|
||||
new_source_preview_.setSource();
|
||||
custom_screencapture = true;
|
||||
}
|
||||
else {
|
||||
new_source_preview_.setSource( Mixer::manager().createSourceScreen(namewin), namewin);
|
||||
custom_screencapture = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Network connected SRT
|
||||
if ( ImGui::Selectable("SRT Broadcast") ) {
|
||||
new_source_preview_.setSource();
|
||||
custom_connected = true;
|
||||
custom_screencapture = false;
|
||||
}
|
||||
|
||||
// 4. Devices
|
||||
ImGui::Separator();
|
||||
for (int d = 0; d < Device::manager().numDevices(); ++d){
|
||||
std::string namedev = Device::manager().name(d);
|
||||
if (ImGui::Selectable( namedev.c_str() )) {
|
||||
custom_connected = false;
|
||||
custom_screencapture = false;
|
||||
new_source_preview_.setSource( Mixer::manager().createSourceDevice(namedev), namedev);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Network connected vimix
|
||||
for (int d = 1; d < Connection::manager().numHosts(); ++d){
|
||||
std::string namehost = Connection::manager().info(d).name;
|
||||
if (ImGui::Selectable( namehost.c_str() )) {
|
||||
custom_connected = false;
|
||||
custom_screencapture = false;
|
||||
new_source_preview_.setSource( Mixer::manager().createSourceNetwork(namehost), namehost);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ImGui::Selectable("Display Loopback") ) {
|
||||
new_source_preview_.setSource( Mixer::manager().createSourceRender(), "Display Loopback");
|
||||
}
|
||||
|
||||
if ( ImGui::Selectable("SRT Broadcaster") ) {
|
||||
new_source_preview_.setSource();
|
||||
custom_connected = true;
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
@@ -3697,11 +3726,11 @@ void Navigator::RenderNewPannel()
|
||||
Device::manager().reload();
|
||||
ImGui::SameLine();
|
||||
ImGuiToolkit::HelpToolTip("Create a source capturing video streams from connected devices or machines;\n"
|
||||
ICON_FA_CARET_RIGHT " webcams or frame grabbers\n"
|
||||
ICON_FA_CARET_RIGHT " screen capture\n"
|
||||
ICON_FA_CARET_RIGHT " vimix display loopback\n"
|
||||
ICON_FA_CARET_RIGHT " vimix Peer-to-peer in local network\n"
|
||||
ICON_FA_CARET_RIGHT " broadcasted with SRT over network.");
|
||||
ICON_FA_CARET_RIGHT " vimix display loopback\n"
|
||||
ICON_FA_CARET_RIGHT " screen capture\n"
|
||||
ICON_FA_CARET_RIGHT " broadcasted with SRT over network.\n"
|
||||
ICON_FA_CARET_RIGHT " webcams or frame grabbers\n"
|
||||
ICON_FA_CARET_RIGHT " vimix Peer-to-peer in local network.");
|
||||
|
||||
if (custom_connected) {
|
||||
|
||||
@@ -3782,6 +3811,22 @@ void Navigator::RenderNewPannel()
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
if (custom_screencapture) {
|
||||
|
||||
ImGui::Text("\nWindow:");
|
||||
|
||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||
if (ImGui::BeginCombo("##ScreenCaptureSelect", "Select ", ImGuiComboFlags_HeightLarge))
|
||||
{
|
||||
for (int d = 0; d < ScreenCapture::manager().numWindow(); ++d){
|
||||
std::string namewin = ScreenCapture::manager().name(d);
|
||||
if (ImGui::Selectable( namewin.c_str() )) {
|
||||
new_source_preview_.setSource( Mixer::manager().createSourceScreen(namewin), namewin);
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
@@ -57,6 +57,7 @@ class Navigator
|
||||
int pattern_type;
|
||||
bool custom_pipeline;
|
||||
bool custom_connected;
|
||||
bool custom_screencapture;
|
||||
void clearButtonSelection();
|
||||
void clearNewPannel();
|
||||
void applyButtonSelection(int index);
|
||||
|
||||
@@ -33,6 +33,7 @@ class MediaSource;
|
||||
class StreamSource;
|
||||
class PatternSource;
|
||||
class DeviceSource;
|
||||
class ScreenCaptureSource;
|
||||
class GenericStreamSource;
|
||||
class SrtReceiverSource;
|
||||
class SessionFileSource;
|
||||
@@ -107,6 +108,7 @@ public:
|
||||
virtual void visit (SrtReceiverSource&) {}
|
||||
virtual void visit (GenericStreamSource&) {}
|
||||
virtual void visit (DeviceSource&) {}
|
||||
virtual void visit (ScreenCaptureSource&) {}
|
||||
virtual void visit (PatternSource&) {}
|
||||
virtual void visit (SessionFileSource&) {}
|
||||
virtual void visit (SessionGroupSource&) {}
|
||||
|
||||
Reference in New Issue
Block a user