Compare commits

...

1505 Commits

Author SHA1 Message Date
Bruno Herbelin
b6cedd0f8f Merge remote-tracking branch 'origin/beta' 2025-11-21 19:18:54 +01:00
brunoherbelin
f11e159c44 Add mouse over on sourece preview in side panel to show original source image 2025-11-19 23:47:40 +01:00
brunoherbelin
f9c14db284 typo in command flatpak 2025-11-19 21:29:48 +01:00
brunoherbelin
2a13cb9e68 BugFix Avoid reload media player on undo/redo 2025-11-19 21:28:11 +01:00
Bruno Herbelin
bfb66906b7 Merge remote-tracking branch 'origin/beta' 2025-11-18 20:56:41 +01:00
brunoherbelin
7872799eb8 no need for portal 2025-11-18 20:53:33 +01:00
Bruno Herbelin
4b8730e2a6 Merge remote-tracking branch 'origin/beta' 2025-11-18 19:41:45 +01:00
brunoherbelin
316e80d7e5 Fixed Snapcraft 2025-11-18 19:41:21 +01:00
Bruno Herbelin
29bd67638c Merge remote-tracking branch 'origin/beta' 2025-11-18 17:45:49 +01:00
brunoherbelin
03a3113d34 Ready for 0.8.5 2025-11-18 17:45:07 +01:00
brunoherbelin
9c8ccf7a9f Not ready for CANVAS : work in progress 2025-11-18 17:13:45 +01:00
brunoherbelin
cc6189e162 New MousePointer Brownian 2025-11-18 17:13:27 +01:00
brunoherbelin
97a58c2988 Use TabletInput with MousePointer to make Spring and Wiggly cursors pressure sentivite 2025-11-18 15:19:10 +01:00
brunoherbelin
3651ce2659 Update README to match change in runtime flatpak 2025-11-18 13:04:13 +01:00
brunoherbelin
cc1a6debb1 Change flatpak to freedesktop platform. In latest 25.08 there is already a recent gstreamer, no need to recompile it. Added --socket=session-bus to support prevention of screen suspend. Add socket x11 to be sure... 2025-11-18 12:40:18 +01:00
brunoherbelin
fcdfd34914 BugFix detection of GNOME desktop to force X11 2025-11-18 12:36:39 +01:00
brunoherbelin
9807da71dc x265 is not available most of time so use va h265 encoder instead (available in flatpak). May not work always, to be improved. 2025-11-18 12:03:04 +01:00
brunoherbelin
08d6d60f57 BugFix End property frame grabber on pipeline error 2025-11-18 12:01:40 +01:00
brunoherbelin
574058bec2 Re-implementation of TabletInput for Linux with X11 input for compatibility with Flatpak 2025-11-18 11:16:51 +01:00
brunoherbelin
b382b5a204 NEW support for pen tablet pressure in Texture Painting. Applies to brush size and/or pressure. TODO: check OSX 2025-11-16 23:19:26 +01:00
brunoherbelin
e51781ee1d BugFix Source Callback (delete first in source) and improved GUI seek callback. 2025-11-16 16:55:15 +01:00
brunoherbelin
84233e46cd Improved draw texture by drawing a line between mouse cursor coordinates (instead of discrete points). This allows to use smaller size of pencil. 2025-11-16 16:36:19 +01:00
brunoherbelin
8d26f5d78a Add Flag functionality to ControlManager and InputMappingWindow with a new Flag SourceCallback. Fixed media player window control. 2025-11-15 08:58:41 +01:00
brunoherbelin
137f110e1d BugFix Step to next frame in MediaPlayer image simulating timeline 2025-11-14 16:36:42 +01:00
Bruno Herbelin
25119a555c Merge remote-tracking branch 'origin/beta' 2025-11-14 09:01:16 +01:00
brunoherbelin
f569aa6e99 Update README.md for vimix flatpak: change version to 49 and improve build command 2025-11-14 09:00:36 +01:00
brunoherbelin
9823f3683c Update icons asset in rsc/images for improved visuals 2025-11-14 08:57:10 +01:00
brunoherbelin
abe2930647 BugFix Improved flag navigation and management in MediaPlayer and SourceControlWindow 2025-11-14 08:56:34 +01:00
brunoherbelin
0a9143f00a Improved Timeline Flags API 2025-11-12 19:59:34 +01:00
brunoherbelin
819b46cc01 BugFix Timeline time display, various minor fix 2025-11-12 19:58:13 +01:00
brunoherbelin
a3ce9e3b20 Update gstreamer module to version 1.26.8 2025-11-11 19:18:02 +01:00
brunoherbelin
7526a43cf0 Update MediaPlayer and SourceControlWindow to utilize paused time for flag operations and manage loop status 2025-11-10 23:09:15 +01:00
brunoherbelin
17699297f3 Add .cache to .gitignore to exclude cache files from version control 2025-11-10 23:08:54 +01:00
Bruno Herbelin
4460fc4308 Merge remote-tracking branch 'origin/beta' 2025-11-10 11:00:23 +01:00
brunoherbelin
8f6adbd77f Update README to fetch flatpak manifest from beta branch 2025-11-10 10:55:54 +01:00
brunoherbelin
a048b1aa21 Update runtime version and module commits in Vimix Flatpak configuration 2025-11-10 10:52:49 +01:00
brunoherbelin
4f84675024 Add broadcast option to command line arguments and implement network broadcasting functionality 2025-11-09 22:47:38 +01:00
brunoherbelin
6b7f4477b0 Refactor Broadcast manager methods to use FrameGrabber type instead of ID, forcing the use of singletons. Update OutputPreviewWindow to reflect these changes and remove unused variables. 2025-11-09 22:47:17 +01:00
brunoherbelin
ec9a4ef88a Add clipboard copy functionality for warning messages in Log::Render 2025-11-09 20:07:42 +01:00
brunoherbelin
2f7bd3a3d1 Enhance error handling in FrameGrabber destructor, improve connection logic in NetworkStream, and update stream protocols in NetworkToolkit. Refactor Stream and VideoStreamer for better failure logging and termination handling. Modify UserInterfaceManager to clear new panel on reload. 2025-11-09 19:55:28 +01:00
brunoherbelin
08114e2cbe Implement broadcast manager functionality and enhance FrameGrabber info and type methods 2025-11-09 11:38:01 +01:00
brunoherbelin
0ef06e400d Refactor ImageFilter to remove channel1_output_session and update blending logic in shaders 2025-11-08 14:11:37 +01:00
brunoherbelin
bdc313cd1f Add texture management as sampler2D uniform to ImageFilter and related components 2025-11-08 14:06:09 +01:00
brunoherbelin
9b432f3fc9 Refactor timeline flag handling and enhance media player functionality
- Removed the TimelineSlider function declaration from ImGuiToolkit.h.
- Updated InputTime function signature in ImGuiToolkit.h to include max_time and validity check.
- Modified MediaPlayer to manage flag statuses more effectively, avoiding repeated pauses during flagged sections.
- Added flag_status_ to MediaPlayer.h to track the current flag status.
- Enhanced SessionLoader to query and set flag types when loading media player sessions.
- Updated SessionVisitor to include flag type attributes in XML serialization.
- Extended Settings to save and load timeline flag configurations.
- Adjusted SourceControlWindow to incorporate flag type management and UI updates for flag interactions.
- Implemented new TimelineSlider function to handle timeline interactions with flags.
- Enhanced Timeline class to support flag types, including methods for adding, retrieving, and setting flag types.
- Updated TimeInterval structure to include a type field for flags.
2025-11-07 17:08:13 +01:00
brunoherbelin
7fd8bc5633 Fix GLFW version check for X11 on Gnome desktop initialization 2025-11-04 20:42:52 +01:00
brunoherbelin
cc3f0226ef Add timeline flag management functionality 2025-11-01 23:38:28 +01:00
brunoherbelin
1f10a359b5 Enhance UriDiscoverer by disabling GPU decoding state when discovery 2025-11-01 23:37:46 +01:00
brunoherbelin
16e6437dc4 Refactor _drawIcon function to make accessible outside 2025-11-01 23:36:22 +01:00
Bruno Herbelin
ed21ac0efb Merge remote-tracking branch 'origin/beta' 2025-10-30 23:36:46 +01:00
brunoherbelin
0e11072070 BugFix Set media player speed on session load
and Rewind (allows start with negative playspeed)
2025-10-30 23:28:00 +01:00
brunoherbelin
232dcf3a29 Reset media playback speed to normal before applying play speed callback 2025-10-29 08:00:27 +01:00
brunoherbelin
65329ccc49 Add screensaver inhibition support for X11 and Wayland 2025-10-28 11:13:05 +01:00
brunoherbelin
7d38ab6b0e Refactor window resize handling and enforce X11 on GNOME desktop 2025-10-27 23:15:58 +01:00
brunoherbelin
5b8a0f4ef2 bugfix; render with output window fullscreen 2025-10-27 18:46:32 +01:00
brunoherbelin
ba05aaa3ef ignore build dir and vsudio env 2025-10-27 12:38:02 +01:00
Bruno Herbelin
0317262f85 just trying to compile again 2025-10-19 16:20:41 +02:00
Bruno Herbelin
1f202e12fc Merge remote-tracking branch 'origin/beta' 2025-05-03 10:19:31 +02:00
Bruno Herbelin
c9ab557aaa BugFix snap X11?? 2025-05-03 10:19:13 +02:00
Bruno Herbelin
d1facbbcd4 Merge remote-tracking branch 'origin/beta' 2025-05-03 08:33:43 +02:00
Bruno Herbelin
a6840ade89 BugFix Counting ShaderSource for reacting in ShaderEditor 2025-04-23 20:51:40 +02:00
Bruno Herbelin
57b89bef0a BugFix Source panel
Disable bundle source for clones, select correct panel for replace + show shader editor with icon clic
2025-04-21 15:16:58 +02:00
Bruno Herbelin
28943adac8 Layers Selection Blending and Bundle
Avoid creation of bundle from selection that contains clones
2025-04-21 15:15:10 +02:00
Bruno Herbelin
337174617f BugFix Increase tolerance for seek failure detection 2025-04-21 15:13:01 +02:00
Bruno Herbelin
816f66a726 BugFix bounce play mediaplayer 2025-04-21 10:12:52 +02:00
Bruno Herbelin
38e80f2ef9 BugFix Show shader window only when active source 2025-04-20 15:28:42 +02:00
Bruno Herbelin
c1924c1f8b BugFix Loading failing after unloaded source 2025-04-20 10:22:48 +02:00
Bruno Herbelin
12b7590041 BugFix Loading failing for clones of unknown source 2025-04-20 10:06:56 +02:00
Bruno Herbelin
d084f13edf Update Help window and unified terminology 2025-04-05 11:04:54 +02:00
Bruno Herbelin
4a51e86fd5 BugFix Auto collapse Shader editor window 2025-04-05 11:03:21 +02:00
Bruno Herbelin
bbcc5ee5d1 Improve Input Mapping GUI 2025-03-30 13:12:24 +02:00
Bruno Herbelin
0af197fa98 BugFix MediaPlayer seek (improved) 2025-03-30 13:11:58 +02:00
Bruno Herbelin
9f208ee7d2 BugFix MediaPlayer seek, avoid blocking wait 2025-03-30 11:51:15 +02:00
Bruno Herbelin
15eb546c3d Auto collapse of Shader Editor window
Reduces height of shader editor when there is no shader to edit. Cleanup of Workspace window implementation.
2025-03-29 18:46:08 +01:00
Bruno Herbelin
1787739128 BugFix Sequence source and encoding 2025-03-09 19:09:37 +01:00
Bruno Herbelin
6d5c55dd2b BugFix UI 2025-03-08 21:00:36 +01:00
Bruno Herbelin
f9463d1149 Improve UI with CollapsingHeader
Session and Settings panels cleaner with collapsing headers that are saved in settings
2025-03-02 23:15:24 +01:00
Bruno Herbelin
d01366d069 New Shader Source
Implementation of Shader GLSL code source; basically a source with an ImageFilter that contains GLSL code. Connected so Shader Editor (like clone sources).
2025-02-23 22:58:26 +01:00
Bruno Herbelin
ea4615ea65 Draft CANVAS editing
Starting to add Canvas edit in Geometry view
2025-01-25 12:05:51 +01:00
Bruno Herbelin
928e798d86 OSC set current by source name 2025-01-18 09:49:52 +01:00
Bruno Herbelin
0dd5f73508 Merge remote-tracking branch 'origin/master' into beta 2025-01-10 23:55:36 +01:00
Bruno Herbelin
72f444ffc5 Fixed Manual review requested.
AnalyzeReviewTask.on_manual_review: Found files with executable stack. This adds PROT_EXEC to mmap(2) during mediation which may cause security denials. Either adjust your program to not require an executable stack, strip it with 'execstack --clear-execstack ...' or remove the affected file from your snap. Affected files: usr/lib/arm-linux-gnueabihf/libdirect-1.7.so.7.0.0
2025-01-10 23:47:44 +01:00
Bruno Herbelin
10fc130dbc Merge remote-tracking branch 'origin/master' into beta 2025-01-10 16:51:59 +01:00
Bruno Herbelin
65395f8af2 Fixed snapcraft 2025-01-10 15:57:53 +01:00
Bruno Herbelin
8048e91117 Merge branch 'beta' 2025-01-10 00:57:26 +01:00
Bruno Herbelin
19375e55b8 Package snap with launch under Wayland 2025-01-10 00:56:37 +01:00
Bruno Herbelin
4549665e08 oops 2025-01-09 19:51:35 +01:00
Bruno Herbelin
f8b42408e2 Ouups, glfw function not available... 2025-01-09 19:44:51 +01:00
Bruno Herbelin
077cd0b9fa Package Snap on Core24 with gpu and vimix launcher 2025-01-09 19:27:53 +01:00
Bruno Herbelin
8a336b3a6e Package launch_vimix script in flatpak and snap for wayland support
under wayland window manager, there are opengl driver issues (specifically for nvidia) so vimix shall be launched with specific settings using a launcher script
2025-01-09 18:58:46 +01:00
Bruno Herbelin
ab7a84c40f Packaging Flatpak for Wayland 2025-01-09 14:34:50 +01:00
Bruno Herbelin
d92e43d215 Merge remote-tracking branch 'origin/beta' 2025-01-06 12:15:52 +01:00
Bruno Herbelin
974618fc18 Cosmetic Icon menu patterns 2025-01-05 18:31:05 +01:00
Bruno Herbelin
88c2c3e12b BugFix Compile Color picker with TFD 2025-01-05 18:30:13 +01:00
Bruno Herbelin
26d35504c8 Centered zoom in Display view 2025-01-05 12:52:45 +01:00
Bruno Herbelin
24eec2687c Merge remote-tracking branch 'origin/beta' 2025-01-04 11:09:41 +01:00
Bruno Herbelin
e06bb0f9f3 Version 0.8.4 2025-01-04 11:09:11 +01:00
Bruno Herbelin
691cf421e3 Merge remote-tracking branch 'origin/beta' 2025-01-04 11:04:10 +01:00
Bruno Herbelin
0161126153 Update flatpak to Gnome 47 and latest dependencies 2025-01-04 11:02:21 +01:00
Bruno Herbelin
729cf52b68 BugFix Compilation without version 2025-01-04 09:40:49 +01:00
Bruno Herbelin
0bf89c18f9 Fix previous commit 2025-01-02 00:00:33 +01:00
Bruno Herbelin
40d3f83719 BugFix Important fix of Bundle Session source
premultiply alpha should NOT be applied to render session in framebuffer with alpha on surface : added a uniform to image shader to allow this. Fixed other problems related to creation and expand of bundle sources. Added a button to bundle a single source.
2025-01-01 23:55:39 +01:00
Bruno Herbelin
a36a7fe4b2 BugFix Render Shader Editor 2024-12-28 00:35:00 +01:00
Bruno Herbelin
717a8f2771 BugFix Trigger spring cursor while moving 2024-12-27 15:48:41 +01:00
Bruno Herbelin
ca0389a7b6 BugFix Icon menu for Metronome 2024-12-27 12:18:06 +01:00
Bruno Herbelin
25cad0bedf BugFix Shader Editor minor issues 2024-12-27 10:48:04 +01:00
Bruno Herbelin
76a96e39bd BugFix reverse Alpha source callback 2024-12-27 09:45:23 +01:00
Bruno Herbelin
29ef6c134e BugFix Set max beats per phase
Limit number of beats for input mapping to 32
2024-12-26 21:20:54 +01:00
Bruno Herbelin
fc1e011246 New Configuration of gamepad input
Settings now offer list of GLFW Joysticks to select as input mapping gamepad
2024-12-26 19:51:07 +01:00
Bruno Herbelin
dbe92512ae New Timer input mapping
First working implementation of metronome events mapping to source callbacks
2024-12-26 18:04:59 +01:00
Bruno Herbelin
d8a771e24f Merge changes for dev Input Mapping Timer 2024-12-23 19:16:01 +01:00
Bruno Herbelin
8a47e86685 BugFix main
return value on Test was wrong
2024-12-21 20:07:21 +01:00
Bruno Herbelin
fc91a74d05 BugFix Color dialog ends properly
Changed multithreading mechanism to use color value from color dialog, avoiding to rely on the testing of future value.
2024-12-21 19:55:00 +01:00
Bruno Herbelin
22d9c41357 BugFix Remove timeline single image
reset pbo size after close
2024-12-15 19:44:29 +01:00
Bruno Herbelin
0289f4c06e BugFix Shader Image filter set uniform value
Use uniform value from code on first compile, then keep value from active shader. Fix bug reading uniform value in code. Various code improvement.
2024-12-15 18:35:37 +01:00
Bruno Herbelin
21ff75b92e BugFix Shader editor and Image filter
Added 'build all' menu and fixed build of file shader
2024-12-13 23:38:01 +01:00
Bruno Herbelin
df0913727c Improve Shader Editor with shader files
Add a filename to FilteringProgram, and use the content of this file instead of code when file exists. Refer to this file in the menu of shader editor, when saving as or loading a file. Keep history of shader files in settings.
2024-12-10 00:14:35 +01:00
Bruno Herbelin
fb99136cc1 Improve Shader editor with selection of external files
IN PROGRESS - Allow user set a filename with the GLSL code for shaders, remember the list of shader files, control from dedicated menu in Shader editor window
2024-12-08 10:20:53 +01:00
Bruno Herbelin
b68a380e35 Adjust size of Italics font to match regular on screen 2024-12-08 10:17:10 +01:00
Bruno Herbelin
6027fc61dd UX tiny changes Media Player
Menu for selection and batch in Player add indication of selection.
2024-12-08 10:16:46 +01:00
Bruno Herbelin
edbc6190e4 Merge remote-tracking branch 'origin/beta' into beta 2024-11-28 19:47:01 +01:00
Bruno Herbelin
be55de91c1 Add SystemToolkit fn to load text file 2024-11-28 19:46:49 +01:00
Bruno Herbelin
7c55bab17b Change OSC Corner coordinate to be in Image reference frame 2024-11-27 23:17:26 +01:00
Bruno Herbelin
dec07ff3a5 BugFix Undo history
Force store of first status, and limit number of undo steps (even if huge number of 1000).
2024-11-27 19:43:41 +01:00
Bruno Herbelin
5742b20fa3 BugFix Shader Code refresh and convert to custom 2024-11-23 14:16:31 +01:00
Bruno Herbelin
ce4e964f09 New Window brightness and contrast corrections in Displays view 2024-11-23 10:10:07 +01:00
Bruno Herbelin
d28a82ff73 BugFix clamp OSC corner cordinates 2024-11-22 15:58:45 +01:00
Bruno Herbelin
ff39f8c458 Improve OSC with corner manipulation
Answering to issue #156
2024-11-22 15:08:28 +01:00
Bruno Herbelin
f24cb8dc96 BugFix OSC blending take float argument 2024-11-22 12:40:59 +01:00
Bruno Herbelin
d273a7fcb4 Bugfix return negative alpha values OSC sync session and batch 2024-11-20 23:35:28 +01:00
Bruno Herbelin
4e813c2540 Improve OSC commands for changing Blending modes
Response to issue #155. New OSC message to set blending mode of source, either by name (string) or integer (index)
2024-11-19 23:01:51 +01:00
Bruno Herbelin
2dd298f772 Merge remote-tracking branch 'origin/master' into beta 2024-11-17 09:57:05 +01:00
Bruno Herbelin
4d9063f16c BugFix return negative alpha values OSC sync command
Response to issue #157
2024-11-16 18:37:30 +01:00
Bruno Herbelin
136b1561c1 BugFix Custom Filter shader uniform variable
Response to issue #159. Allow uniform variable names with numbers and underscore, and values with float numbers (i.e. fixed the regular expression). Plus also fix a display bug in shader editor GUI.
2024-11-16 18:09:38 +01:00
Bruno Herbelin
5a933beb16 BugFix Transition Cross Fading temporarily disabled when faded
When session is already fade to black, the cross fading transition cannot be used. Added an icon in left panel to allow user to set the transition mode
2024-11-16 11:01:51 +01:00
Bruno Herbelin
7e2a34a825 BugFix Open side panel on clic on initials 2024-11-10 23:26:50 +01:00
Bruno Herbelin
b4600a289d Update doc and screenshots 2024-11-10 18:51:22 +01:00
Bruno Herbelin
e74ce1f4cc Merge remote-tracking branch 'origin/master' into beta 2024-11-10 16:50:39 +01:00
Bruno Herbelin
ba75777c0d Merge remote-tracking branch 'origin/beta' 2024-11-10 14:55:26 +01:00
Bruno Herbelin
92c0b4493a Release candidate 0.8.3 2024-11-10 14:55:15 +01:00
Bruno Herbelin
803231c558 BugFix Preview overlay on crop action in Geometry 2024-11-10 14:38:22 +01:00
Bruno Herbelin
120909f8d6 BugFix Sync Play to Session ready when loading
Add test of session state on Play callback; the play action is then called after all sources are ready, thus starting in sync.
2024-11-10 12:12:05 +01:00
Bruno Herbelin
f0f23cbd0b BugFix Media Source play and speed set on load
Fixed two problems; seek to key is causing jumps in timeline and undesirable behavior, set play speed on load was postponed (bad fix) and is now operational after normal load.
2024-11-10 11:37:35 +01:00
Bruno Herbelin
939a35ee97 BugFix Correct display of disabled media source 2024-11-09 20:17:32 +01:00
Bruno Herbelin
18e1785e57 BugFix Acceptable behavior for media Player timeline panel
First acceptable implementation of timeline editing panel, with cut and fading modes and UI.
2024-11-09 14:52:24 +01:00
Bruno Herbelin
bef2709834 Improve MediaPlayer with Stop and blackout at end
New media player loop mode to stop at end and set the frame to black or transparent (i.e. fading 0.f). Also fix bug to save status to 'playing' (i.e. user action) when mediaplayer is stopped automatically at end. Improve the way to test sources texture change for Source player window.
2024-11-09 10:23:51 +01:00
Bruno Herbelin
c063e2fc30 New icons 2024-11-09 10:10:05 +01:00
Bruno Herbelin
6e62ce15b5 Merge remote-tracking branch 'origin/beta' 2024-11-03 22:34:43 +01:00
Bruno Herbelin
19221e1fcc Fixed Icons 2024-11-03 22:20:40 +01:00
Bruno Herbelin
c25d6cb551 Improved icons for Action mapping color correction 2024-11-03 16:30:16 +01:00
Bruno Herbelin
86920a3083 BugFix Icon for Bundle source (GROUP) was taken 2024-11-03 14:01:49 +01:00
Bruno Herbelin
68c39290ec Improve UI in views
Eye candy and clarified tooltips for UI and menus in Geometry, layer and texture views
2024-11-03 10:38:48 +01:00
Bruno Herbelin
563f56d2a2 Improve Geometry view with mouse over handle 2024-11-02 23:35:55 +01:00
Bruno Herbelin
4174333b40 Improve busy animation Session Source in Transition view 2024-11-02 23:16:54 +01:00
Bruno Herbelin
a74445f9e4 Improve Layer view with Blending menu icons
New icons for blending modes declared in Shader class, used in combo box for blending mode selection in imguiVisitor, and added source context menu in Layer view.
2024-11-02 23:16:25 +01:00
Bruno Herbelin
859d8cc86c Improve Player show render after fading when needed
A source in Player shows the pre/post render split if there is a Mediaplayer that has a fading curve
2024-10-31 00:10:45 +01:00
Bruno Herbelin
d8d2f1c801 Improve Mixing and Layers view with clic on initials
Clic on initials of current source opens left panel for this source.
2024-10-28 22:14:17 +01:00
Bruno Herbelin
85308462eb Improve views
Just a bit more space and tooltips in the menus above Geometry, Texture and Display views
2024-10-28 20:52:39 +01:00
Bruno Herbelin
d84f8b14c4 Merge remote-tracking branch 'origin/master' into beta 2024-10-26 20:53:23 +02:00
A1219
0f4889923d Add files via upload (#150)
Added #include <algorithm> for compilation on Arch Linux
2024-10-17 11:38:18 +02:00
Bruno Herbelin
2093816f2d Merge remote-tracking branch 'origin/beta' 2024-10-13 23:58:30 +02:00
Bruno Herbelin
7de751f882 Improve error message of Mixer 2024-10-13 23:58:02 +02:00
Bruno Herbelin
6da476aebb bugFix Allow fail of Stream source without deleting FB
Reverting to previous code
2024-10-13 23:50:27 +02:00
Bruno Herbelin
7140d25a87 Improve GUI Player timeline panel
Adjust widgets to fit in panel
2024-10-13 23:29:28 +02:00
Bruno Herbelin
cb413a99f0 Merge remote-tracking branch 'origin/beta' 2024-10-13 20:32:02 +02:00
Bruno Herbelin
e6d21518d2 Bugfix Load mediaplayer with audio
Resolving issue #151
2024-10-13 20:30:58 +02:00
Bruno Herbelin
19e647ae84 BugFix Blur radius parameter
Scattered blur was wrongly adapting to resolution, and Gaussian blur was using half, not a quarter of the resolution
2024-10-13 15:04:44 +02:00
Bruno Herbelin
494f5c71bb Merge remote-tracking branch 'origin/beta' 2024-10-13 14:02:45 +02:00
Bruno Herbelin
c4ad80f3b9 BugFix Set Clone filter Uniform using callback for OSC
Async call to setProgramParameter is unsave (Issue  #149).
2024-10-13 14:02:08 +02:00
Bruno Herbelin
460fa6c8e6 Improve Player: show image after pipeline fading 2024-10-13 11:04:16 +02:00
Bruno Herbelin
5a62ffe178 BugFix Strong data testing before setting program parameters 2024-10-13 11:02:01 +02:00
Bruno Herbelin
c867d7bdf2 BugFix Prevent crash on calls to mixer before finished initializing 2024-10-13 11:01:14 +02:00
Bruno Herbelin
167cf7c659 Improve OSC Logs, Settings and testing
Stronger data testing on uniform message.
2024-10-13 11:00:29 +02:00
Bruno Herbelin
73a3ec3f63 Improve Undo History
Store a short label in XML for improved display of action history in list. Ensured all action labels include the source name (IMGUI visitor) and checked all action manager store action.
2024-10-09 16:15:01 +02:00
Bruno Herbelin
f8981248dc BugFix Safe access to xmldoc in snapshots and undo history
Added mutex to Action manager for access to undo history and added mutex to session's snapshots. Lock and unlock those mutex for all write access and long read access.
2024-10-08 19:05:52 +02:00
Bruno Herbelin
b90b2469b5 Merge remote-tracking branch 'origin/beta' 2024-10-05 14:01:42 +02:00
Bruno Herbelin
2de9ca144d Discard source callback (e.g. OSC) when user controls source
Cancel the source callback for alpha, grab, rotation etc. if the source is current and the related view has initialized an action (which would mean the user is controlling the current source).
2024-10-05 13:39:41 +02:00
Bruno Herbelin
f1a89a1c55 Improv Share pattern source among render window
As all RenderingWindow render the same frame buffer, a unique Stream pattern source can be rendered in all windows, thus saving resources.
2024-10-05 10:27:50 +02:00
Bruno Herbelin
3909aa4ab7 Minor improvements
Display hourglass in left panel to inform source is loading and prevent buttons when loading + Varia.
2024-10-05 01:07:05 +02:00
Bruno Herbelin
10e95f5388 BugFix Prevent warnings gstreamer 2024-10-04 19:23:16 +02:00
Bruno Herbelin
21bb2af7ea BugFix Stream discoverer don't need to unref caps 2024-10-04 17:41:37 +02:00
Bruno Herbelin
9b7b4071bf Merge remote-tracking branch 'origin/beta' 2024-10-04 17:12:33 +02:00
Bruno Herbelin
2ec267fc4f BugFix Buffer reset to be ready for reload 2024-10-04 17:10:07 +02:00
Bruno Herbelin
e6b954e9e8 BugFix prevent busy init of checkerboard texture 2024-10-04 16:29:51 +02:00
Bruno Herbelin
b2ce0f3934 Re-implementation of MediaPlayer and Stream update
Avoid using gst video frames, and simply copy buffer instead. Use gst memory map to access pointer to RGBA data. unreferencing the buffer frees the memory (apparently). Also free OpenGL objects on close to free memory.
Overall, memory consumption seems to be reduced.
2024-10-04 16:29:02 +02:00
Bruno Herbelin
01d3a91e40 BugFix MediaPlayer and Stream ending order fixed 2024-10-03 21:32:14 +02:00
Bruno Herbelin
cf0b87298d BugFix MediaPlayer adjustments 2024-10-03 20:34:24 +02:00
Bruno Herbelin
02c69c1686 BugFix Restore PlaySpeed on MediaPlayer load
Issue reported  #148
2024-10-03 19:16:08 +02:00
Bruno Herbelin
9a53ffa6d0 Cleanup code
Remove unused #include, add header when missing
2024-10-03 18:27:04 +02:00
Bruno Herbelin
d22e23937f Merge remote-tracking branch 'origin/beta' 2024-10-03 16:29:33 +02:00
Bruno Herbelin
7b820e10e0 BugFix Compilation _M_X64 OSC 2024-10-03 16:29:12 +02:00
Bruno Herbelin
d795fee579 Merge remote-tracking branch 'origin/beta' 2024-10-03 16:19:31 +02:00
Bruno Herbelin
80418162d4 Enabling GST_BUS_MESSAGE for mediaplayer and stream 2024-10-03 16:18:46 +02:00
Bruno Herbelin
32c83a6eee BugFix Gst Pipeline closing and unreferencing
Need to empty pipeline bus if IGNORE_GST_BUS_MESSAGE is not set. Ensuring all gst_objects are unreferenced properly to be erased.
2024-10-03 14:56:58 +02:00
Bruno Herbelin
d0b8cf0275 BugFix Ref pipeline for successful dereferencing and memory free 2024-10-01 14:26:10 +02:00
Bruno Herbelin
74fa6da8c2 Merge remote-tracking branch 'origin/master' into beta 2024-09-05 09:37:25 +02:00
Bruno Herbelin
f199f96f7b Minor OSX Bugfix 2024-09-02 13:19:36 +02:00
Bruno Herbelin
bfc13549e9 Merge remote-tracking branch 'origin/master' into beta 2024-09-01 23:50:23 +02:00
Bruno Herbelin
7660b07fa8 BugFix missing test of audio settings 2024-09-01 23:48:42 +02:00
Bruno Herbelin
b72054c2f7 BugFix missing window size for Shader Editor 2024-09-01 23:43:04 +02:00
Bruno Herbelin
9dcbc38231 DeviceSource pixel format not working on OSX
Discard the forced setting of pixel format for a device source as it causes problems and seems useless
2024-09-01 23:39:22 +02:00
Bruno Herbelin
dbb50cf580 Documenting how to perform post-install OSX 2024-09-01 23:37:31 +02:00
Bruno Herbelin
da0782d036 Apple MacOS Packaging and App signing
Successful cpack with fixup bundle on arm64 M2.
Signing of the .app and all binary dependencies within the cmake install process.
Fixed minor issues with OSX compilation.
2024-09-01 20:53:34 +02:00
Bruno Herbelin
778801992d Merge remote-tracking branch 'origin/beta' 2024-08-24 22:43:06 +02:00
Bruno Herbelin
f410ff0ed2 CMAKE generation warning fix
exec_program() command should not be called and has been superseded by execute_process()
2024-08-24 22:39:17 +02:00
Bruno Herbelin
23685253a8 Compilation fix GLAD with version Glad 2
Re-generated GLAD library with Version 2 from https://gen.glad.sh/
Integrated into CMake and RenderingManager loader.
2024-08-24 22:38:17 +02:00
Bruno Herbelin
21ad2cab07 Merge remote-tracking branch 'origin/master' into beta 2024-08-24 20:56:46 +02:00
Bruno Herbelin
c5a14422ee Left navigation panel priority for menu
Selecting a source does not exit Session or Playlist panel.
2024-08-24 20:29:30 +02:00
Bruno Herbelin
c12deb4b56 Compilation warning fix 2024-08-24 19:52:32 +02:00
Bruno Herbelin
792d98dfd5 Merge remote-tracking branch 'origin/master' into beta 2024-08-24 13:24:57 +02:00
Bruno Herbelin
12aa3b19d5 BUGFIX FOR APPLE OSXAUDIOSINK 2024-08-24 13:23:50 +02:00
Bruno Herbelin
4b3f782ece Merge remote-tracking branch 'origin/master' into beta 2024-08-24 08:55:01 +02:00
Bruno Herbelin
0016a41474 OSX Audio compatibility 2024-08-18 00:18:56 +02:00
Bruno Herbelin
e115981b9f OSX 14 M2 platform compilation 2024-08-17 23:31:03 +02:00
Bruno Herbelin
94fbe58fb7 New toolbox Timeline editor
DRAFT implementation of a new toolbox to apply fade-in and fade-out and to cut the timeline.
2024-07-13 12:32:05 +02:00
Bruno
90962d0391 Merge remote-tracking branch 'origin/beta' 2024-06-06 14:15:19 +02:00
Bruno Herbelin
7092de8809 Improv Shader Editor UI and clone create 2024-05-30 23:12:59 +02:00
Bruno Herbelin
81a0a724ab BugFix ImageFilters Repair filters
Use of secondary texture in image shader (renamed from mask texture) in image filters for ichannel_1 as texture of display loopback only if not needed otherwise (e.g. input image needed in Sharpen filter).
2024-05-26 23:56:23 +02:00
Bruno Herbelin
34297feed4 Improv GUI, begin menu icon, tool menu name 2024-05-26 10:55:46 +02:00
Bruno Herbelin
4407f8ac68 BugFix Allow Clone sources to retry after fail
By keeping the name of the origin source of a clone, the session can attempt at recreating a clone when the origin source changed or have been replaced, even after detachment of the clone.
2024-05-25 09:05:14 +02:00
Bruno Herbelin
3de691fa0d BugFix Rename source when replaced
Renaming of source when replacing a source should be done after deletion of the previous one for the name to be available
2024-05-25 08:58:06 +02:00
Bruno Herbelin
e7f388999d Improv appearance of tooltips
Ensure uniform border size around tooltips with image, lighter color background for better visibility.
2024-05-20 13:19:34 +02:00
Bruno Herbelin
8d66913a8e BugFix Restore CTRL+F for main window fullscreen
Bug introduced at commit e3b8ccff9e
2024-05-20 10:48:35 +02:00
Bruno Herbelin
b30b5b6474 Merge remote-tracking branch 'origin/master' into beta 2024-05-18 19:41:09 +02:00
Bruno Herbelin
19b8412d67 Merge remote-tracking branch 'origin/brunoherbelin-patch-1' 2024-05-18 19:27:06 +02:00
BHBN
24d2555c5e Fixed README.md 2024-05-18 19:23:07 +02:00
Bruno Herbelin
322564bc42 Merge remote-tracking branch 'origin/beta' 2024-05-18 19:21:11 +02:00
Bruno Herbelin
12bff7aa9e BugFix Shmdata sink broadcasting Shared Memory
Fix termination of shmdatasink, improved UI and doc.
2024-05-18 19:19:27 +02:00
Bruno Herbelin
d87a84db85 Merge remote-tracking branch 'origin/beta' 2024-05-18 13:26:11 +02:00
Bruno Herbelin
b38017eb1c Improv Left Panel Layout with buttons on top
Seemed better to have large title for panel (Session, Playlist and Settings) and the selection buttons aligned int the top row. Minor adjustments of layout.
2024-05-18 12:32:38 +02:00
Bruno Herbelin
2d99870ec2 BugFix Upgrade to gstreamer 1.24
Upgrade to Ubuntu 24.04 also lead to upgrade gst to 1.24, which introduces the propose_allocation field in GstAppSinkCallbacks: not initializing it causes a crash.
2024-05-18 11:03:52 +02:00
Bruno Herbelin
6f7a4aa234 BugFix. Synch for Recording
Allows synch or video and audio recording, even when pausing record and resuming at different times.
2024-05-17 21:39:10 +02:00
Bruno Herbelin
49add7e0f8 BugFix Typos Log 2024-05-17 19:12:34 +02:00
Bruno Herbelin
c2708078db BugFix Pause Recording: correct timing and sync audio 2024-05-04 00:45:49 +02:00
Bruno Herbelin
a46e68f145 BugFix Video recorder and image sequence encoder
Improved and simplified UI control of encoding of image sequences into video files. Bugs fixed to prevent problems with video recorder.
2024-05-01 23:29:08 +02:00
Bruno Herbelin
a4f8d46d69 BugFix H264 and H265 Recording
Use of zerolatency tuning is needed to ensure timestamps pushed by encoder are respected. Fixed encoding pipeline settings.
2024-04-30 19:18:33 +02:00
Bruno Herbelin
94d6bc4bca BugFix Close NewSource panel on exit
Ensures source preview is deleted, avoids crash.
2024-04-30 17:47:11 +02:00
Bruno Herbelin
ec78631691 Improv MultiFileRecorder to use gstreamer instead of STB
Allows adding images of different size and formats, faster decoding and encoding, add keyframes in recorded video.
2024-04-29 23:49:41 +02:00
Bruno Herbelin
cd8f9792ab BugFix Add keyframes every second of output recording
Also compilation warning fix for GValue init.
2024-04-29 23:45:48 +02:00
Bruno Herbelin
fd9c868c40 BugFix Duration of Timeline at zero to delete timeline
Also allows to Cancel on first creation of timeline
2024-04-25 18:51:48 +02:00
Bruno Herbelin
6282500305 Merge remote-tracking branch 'origin/master' into beta 2024-04-21 23:21:05 +02:00
Bruno Herbelin
7b5bc6d236 BugFix Implementation of Audio in Source
Generalized audio support into Source class, instead of MediaPlayer.
2024-04-21 23:20:44 +02:00
Bruno Herbelin
3e45ec7353 Fixup Snap package Unity
Removing plug to Unity seems to fix the problem with Unity7 (Unity8 is not available yet for production)
2024-04-17 12:04:02 +02:00
Bruno Herbelin
5051f4e38c Fixup Typo metainfo categories 2024-04-17 11:44:24 +02:00
Bruno Herbelin
5532e3349a Merge branch 'master' of https://github.com/brunoherbelin/vimix 2024-04-08 19:11:19 +02:00
Bruno Herbelin
4ea7a09583 Typo Fix metainfo.xml flatpak 2024-04-08 19:11:07 +02:00
Bruno Herbelin
affba5a29f Typo Fix metainfo.xml flatpak 2024-04-08 19:09:55 +02:00
Bruno Herbelin
d486055fc8 Fixup Flatpak
flathub uses 'rename desktop file' and the file vimix.desktop should not be already named io.github.brunoherbelin.Vimix.desktop.
2024-04-07 10:37:14 +02:00
Bruno Herbelin
52b7cd68c1 BugFix flatpak desktop file 2024-04-06 13:34:15 +02:00
Bruno Herbelin
afdf4bf108 BugFix Snap with Unity8
GLFW  was failing to create windows: using unity8 seems to fix this. Tested local snap
2024-04-06 11:17:27 +02:00
Bruno Herbelin
a33dbac984 Merge remote-tracking branch 'origin/beta' 2024-04-06 11:03:17 +02:00
Bruno Herbelin
ff48107b1a New Shader of Image Filter takes display loopback as channel 1
Allows blending with output in GLSL shader
2024-03-28 14:27:43 +01:00
Bruno Herbelin
3afaaed1fb BugFix Fail source on gstreamer error detection 2024-03-28 12:09:23 +01:00
Bruno Herbelin
7ca6299fba BugFix Exit all fullscreen on monitor disconnect 2024-03-28 09:54:52 +01:00
Bruno Herbelin
cba7f5b801 Merge remote-tracking branch 'origin/beta' 2024-03-25 13:25:55 +01:00
Bruno Herbelin
d75ed8aeee BugFix Include audio support to Snap package
Also disabling wayland : seems to be incompatible with GLFW window creation
2024-03-25 13:25:00 +01:00
Bruno Herbelin
b9b2984235 BugFix Include audio support to Flathub package 2024-03-24 22:06:24 +01:00
Bruno Herbelin
c2b7892e6c Merge branch 'beta' of https://github.com/brunoherbelin/vimix into beta 2024-03-23 22:07:33 +01:00
Bruno Herbelin
6ce07c0a4b Bugfix Correct coordinate projection on fullscreen GUI
When GUI window is fullscreen, computation of projection of UI elements should not correct for DPI
2024-03-23 22:07:09 +01:00
Bruno Herbelin
008e217a79 BugFix Update windows status on monitor disconnect
Un-fullscreen windows that were fullscreen in a disconnected monitor, update geometry for windows that were moved after monitor disconnect
2024-03-23 22:05:51 +01:00
Bruno Herbelin
5847e52fbc Improve Input Mapping GUI
Input Mode menu do not show tick mark, switch mode on input
2024-03-22 21:09:08 +01:00
Bruno Herbelin
e9baa25b46 Improved Transition view
Update Transition panel to natch new navigator panel style. Add curse selection in view.
2024-03-17 23:31:04 +01:00
Bruno Herbelin
b6213e1ed8 New Simplified Transition view with icons on source and target 2024-03-17 19:59:07 +01:00
Bruno Herbelin
59ac3a0bb8 Flatpak update 2024-03-16 10:22:26 +01:00
Bruno Herbelin
9b63972878 Merge remote-tracking branch 'origin/beta' 2024-03-15 23:28:32 +01:00
Bruno Herbelin
4955e24f12 BugFix Restart on Re-activation for Stream source 2024-03-15 23:19:51 +01:00
Bruno Herbelin
16fb654b98 BugFix Restart on Re-activation
Response to #133
2024-03-15 20:18:31 +01:00
Bruno Herbelin
eda88b3078 BugFix Create Favorite playlist even if not existing 2024-03-15 18:55:19 +01:00
Bruno Herbelin
2435277e49 Compilation Fix OSX 14 2024-03-15 18:50:53 +01:00
Bruno Herbelin
7caded7c95 Improved Transition view panel
Simplified and matching changes to transition view
2024-03-14 00:20:43 +01:00
Bruno Herbelin
df6cdf9a80 New Transition view improved actions
Add play icon on target to indicate action to do, replace 'open' button by fast forward icon. + Bugfix crash on exit in Transition view.
2024-03-14 00:03:19 +01:00
Bruno Herbelin
25be63ef2c BugFix Remove input callbacks when removing source
Response to issue #130
2024-03-11 23:09:35 +01:00
Bruno Herbelin
b0a042369f New Uniform variables in example GLSL shader 2024-03-11 00:21:38 +01:00
Bruno Herbelin
d8d68dcf71 BugFix Typo in media pattern for SVG 2024-03-10 15:42:23 +01:00
Bruno Herbelin
133fb661b3 BugFix Shader Editor window keeps text when disabled
Deselect clone source with custorm shader keeps shader code in editor, except when changing source. Support for CTRL+S for saving session after building shader.
2024-03-10 15:09:54 +01:00
Bruno Herbelin
d5f2b375a6 New User defined uniforms in Custom shaders 2024-03-10 11:51:13 +01:00
Bruno Herbelin
f280d3b64c New OSC Target for Alias and renaming source
Allow creating temporary OSC aliases to link to a target by ID or name. Allow renaming a source from OSC.
2024-03-09 00:05:56 +01:00
Bruno Herbelin
e20261fa66 BugFix OSX compile 2024-03-06 08:29:10 +01:00
Bruno Herbelin
b6a41d417d BugFix close stream 2024-03-03 23:26:16 +01:00
Bruno Herbelin
80f3052c2b Improv GUI selection list of Patterns for Source
Sub-menus for static and animated patterns (instead of ugly icons)
2024-03-03 23:18:57 +01:00
Bruno Herbelin
62060e0c04 BugFix Post-MemCheck fix memory leaks corrections 2024-03-03 20:02:29 +01:00
Bruno Herbelin
f3f7c57f10 BugFix Added Pipeline Bus sugnal handler
Captures error messages from the bus, and importantly removes all messages in the bus to free memory continuously instead of stacking messages indefinitely.
2024-03-03 09:39:10 +01:00
Bruno Herbelin
82d61909f6 BugFix ensure Mixer clear termination 2024-02-29 22:52:47 +01:00
Bruno Herbelin
03c68d1dc3 BugFix improved complete close of session
Wait for all registered pipelines to end when clearing the mixer manager (closing is asynchronous).
2024-02-28 18:47:54 +01:00
Bruno Herbelin
05f593e40c BugFix Ensure complete close of session upon termination
Async ending of gst pipeline in stream and media player sources caused a crash at termination as source was still not closed when process was ended. Fix is to ask for an immediate termination of gst pipeline upon delete of stream, and to wait for mixer manager to end current session when clearing.
2024-02-28 00:39:03 +01:00
Bruno Herbelin
5719c6cfc2 Merge remote-tracking branch 'origin/beta' 2024-02-25 22:18:59 +01:00
Bruno Herbelin
1ba8ff06e1 BugFix restore window decoration 2024-02-25 22:17:40 +01:00
Bruno Herbelin
fae5d09001 Improvement Performance optimization 2024-02-25 21:58:44 +01:00
Bruno Herbelin
3eecb412c6 Merge remote-tracking branch 'origin/beta' 2024-02-24 20:20:59 +01:00
Bruno Herbelin
b022be49a1 New Settings export and command-line load
Allows exporting settings in XML file and launch vimix from command line with given XML filename as argument to restore all settings and windows configuration.
2024-02-24 20:05:19 +01:00
Bruno Herbelin
7238eccfd2 BugFix CPU usage for nothing 2024-02-24 11:44:05 +01:00
Bruno Herbelin
e3b8ccff9e New Headless execution mode (DRAFT) 2024-02-23 23:29:20 +01:00
Bruno Herbelin
c1aa3c9d4d Add Action Fullscreen to View Menu 2024-02-22 22:40:32 +01:00
Bruno Herbelin
00345c94a3 New OSC Play fast forward 2024-02-18 19:26:01 +01:00
Bruno Herbelin
20001e6a26 New OSC grab and resize animate 2024-02-18 19:16:06 +01:00
Bruno Herbelin
c06fdc7760 New OSC animation of alpha, position, size, etc.
Added animation duration argument to OSC messages alpha, depth, position, size and angle.
2024-02-18 17:33:23 +01:00
Bruno Herbelin
8e28eba959 New OSC Filter attribute of source
Adding attribute for setting filter:  set filter by name, set method by name, set first argument value
2024-02-17 23:42:03 +01:00
Bruno Herbelin
47ff1a2dd8 BugFix Allow providing font size as runtime argument
Because with some monitors the resolution is not detected, font appearance can be unsatisfying (#121). User can now set it with --fontsize N argument when launching vimix in command line. Code for managing command line arguments is generaly improved. Man page updated.
2024-02-05 16:02:38 +01:00
Bruno Herbelin
00ff0f532f Work in progress OpenGL Decoding for gst appsink 2024-02-04 21:59:58 +01:00
Bruno Herbelin
2e0732c75b BugFix Improved Stream close (async)
Unified mechanism for async close of pipeline for stream and mediaplayer
2024-02-02 17:04:56 +01:00
Bruno Herbelin
842247de54 BugFix Seek OSC as percent of play duration
Response to issue #120
2024-02-02 14:53:23 +01:00
Bruno Herbelin
52840ce8ae Compilation fix 2024-01-30 20:32:39 +01:00
Bruno Herbelin
f640d2574b Merge remote-tracking branch 'origin/beta' 2024-01-29 00:26:06 +01:00
Bruno Herbelin
8598aad9e2 Eyecandy various GUI improvements 2024-01-28 20:40:12 +01:00
Bruno Herbelin
5dc82aadc7 New Cursor following overlay animation when busy 2024-01-28 19:05:15 +01:00
Bruno Herbelin
2f8411a658 Improved vimix first launch (or after upgrade)
Changed Mixer Load behavior at init, detect change of version and do not load settings if different, show About Vimix after change of version, fixed initial position of windows at first run.
2024-01-28 12:26:05 +01:00
Bruno Herbelin
fcce9b62d5 BugFix Bounce playback at first frame 2024-01-27 18:30:12 +01:00
Bruno Herbelin
5a077d2f52 BugFix Allow larger font size and UI scale 2024-01-27 14:45:36 +01:00
Bruno Herbelin
36f8ea8df0 BugFix Improve GPU Memory Framebuffer monitoring
Count allocation of framebuffers in Bytes and draw plot in ToolBox.
2024-01-27 13:00:38 +01:00
Bruno Herbelin
fe623d93a1 BugFix Clear state and unref playbin 2024-01-27 12:02:07 +01:00
Bruno Herbelin
d41b8a7c24 New import and export GLSL code in Shader editor 2024-01-21 14:49:23 +01:00
Bruno Herbelin
f016a82a32 Reimplementation of Dialogs for open and save files
Convert to generic file dialogs instead of specific implementation per type
2024-01-21 13:26:11 +01:00
Bruno Herbelin
d872aa4a6c Improved Tooltip of Source 2024-01-17 23:01:43 +01:00
Bruno Herbelin
2c7262ced4 New Tooltip on Source left panel with preview 2024-01-16 23:55:14 +01:00
Bruno Herbelin
01e21ea212 BugFix OSC set gamma 2024-01-16 19:07:39 +01:00
Bruno Herbelin
0eae04ab83 BugFix Accept empty string to create Text source 2024-01-16 18:47:54 +01:00
Bruno Herbelin
e42afcb434 BugFix OSC API extended 2024-01-14 18:59:42 +01:00
Bruno Herbelin
fad4be419a New Pause recording
Allows Pause/resume recording. Menu shows info when recording
2024-01-14 16:33:43 +01:00
Bruno Herbelin
9768d17b9b New behavior for Mixing View link
If selection for linking contains linked sources, offer to RE-link to change previous link (instead of only offer to remove the link)
2024-01-13 12:00:59 +01:00
Bruno Herbelin
e36bae2ab6 EasterEgg Insert vimix logo source 2024-01-13 00:15:59 +01:00
Bruno Herbelin
f8b5b1db9c BugFix MediaPlayer
Ensure change of direction operates inside timeline range, Ensures reload resets media player frames.
2024-01-12 20:25:38 +01:00
Bruno Herbelin
9ccf1a31bc BugFix restore view after source delete
Was causing crash in Texturing view
2024-01-10 20:10:59 +01:00
Bruno Herbelin
36d23b5dc2 Bugfix seek media player 2024-01-08 19:25:29 +01:00
Bruno Herbelin
9d27335d7d New Double clic action in Player window, Display window or background 2024-01-07 23:39:58 +01:00
Bruno Herbelin
a913cee7a4 New Preview of source for Player
F6 and F7 (new) keys trigger preview of output display and current source in player, respectively.
2024-01-06 15:31:32 +01:00
Bruno Herbelin
eeeba3d2b7 Compilation fix (snapcraft specific) 2024-01-05 20:23:51 +01:00
Bruno Herbelin
b8a56776da Merge remote-tracking branch 'origin/beta' 2024-01-03 23:51:59 +01:00
Bruno Herbelin
0e9fe58c06 Snapcraft packaging core22 fixed 2024-01-03 23:51:44 +01:00
Bruno Herbelin
6cf7635005 Merge remote-tracking branch 'origin/master' into beta 2024-01-03 16:30:51 +01:00
Bruno
6c7ea3a16d OSX Dirty Packaging 2024-01-03 15:40:08 +01:00
Bruno
4ded0d03d0 Updated flatpak release changelog 2024-01-03 10:52:17 +01:00
BHBN
ac45ad740d Another screenshot for online 2024-01-03 10:40:09 +01:00
Bruno Herbelin
f010c840e6 Merge remote-tracking branch 'origin/beta' 2024-01-02 15:36:44 +01:00
Bruno Herbelin
90b59908c6 Snapcraft core22 uses gnome extension 2024-01-02 15:36:25 +01:00
Bruno Herbelin
13c6693cdd Merge remote-tracking branch 'origin/beta' 2024-01-02 14:20:45 +01:00
Bruno Herbelin
cd56c960a6 Snap v0.8.2 2024-01-02 14:18:59 +01:00
Bruno Herbelin
a51423a4d6 Merge remote-tracking branch 'origin/beta' 2024-01-02 14:16:24 +01:00
Bruno Herbelin
80a25ec71c Updated flatpak to Gnome 45 2024-01-02 14:02:44 +01:00
Bruno Herbelin
51209179d1 BugFix Display view 2024-01-02 14:02:30 +01:00
Bruno Herbelin
b3937caa10 Auto stash before merge of "beta" and "origin/beta"
Updated submodule ext/imgui
2024-01-02 14:02:05 +01:00
Bruno Herbelin
8927ba7c73 Updated submodule ext/imgui 2024-01-02 13:49:40 +01:00
Bruno Herbelin
5f4c867618 Updated Beta flatpak 2024-01-02 13:17:29 +01:00
Bruno Herbelin
fced4178be Updated submodule ext/imgui 2024-01-02 13:12:30 +01:00
Bruno Herbelin
da0944c814 Updated submodule ext/tinyxml2 2024-01-02 13:09:42 +01:00
Bruno Herbelin
7aa14219b8 Updated submodule ext/stb 2024-01-02 13:09:36 +01:00
Bruno Herbelin
09a3494183 Updated submodule ext/glm 2024-01-02 13:05:41 +01:00
Bruno Herbelin
8924d81e0a BugFix Update linked sources and Texture view on Source change
When source change stream (e.g. change pattern), Texture view was not updated, and sources with mask texture were not adapted.
2024-01-02 10:38:37 +01:00
Bruno Herbelin
b599fbf88d BugFix Minor UI corrections and bugs fixed 2024-01-01 17:54:27 +01:00
Bruno Herbelin
578a72f560 New Distortion of output window in Displays view
Allows shape distortion of output rendering (e.g. for perspective correction of projection), added Grid snap cursor support in Displays view
2023-12-31 18:34:48 +01:00
Bruno Herbelin
6b1e298d43 BugFix diagonal distortion Geometry
And minor UI improvements
2023-12-31 18:32:03 +01:00
Bruno Herbelin
698665c4cc BugFix Capture menu in Display and Source windows
Support text scaling for small values (<0.7) that caused a UI display bug
2023-12-29 14:01:18 +01:00
Bruno Herbelin
caa3e4d07a New Geometry view option to show only visible sources
View setting to ignore mixing visibility applied to Geometry view, with a button to complement the workspace selection.
2023-12-29 11:25:29 +01:00
Bruno Herbelin
7606baa20b Bugfix display Capture menu
And addedactive label to enable restore default
2023-12-26 23:49:47 +01:00
Bruno Herbelin
fde6be3f97 BugFix and code cleanup
Fixed rendering of Mesh by using new TextureShader (instead of ImageShader which is dedicated to square Surfaces). Cleanup includes and code layout.
2023-12-26 23:08:09 +01:00
Bruno Herbelin
abdc70121d Merge remote-tracking branch 'origin/master' into beta 2023-12-26 15:15:13 +01:00
Bruno Herbelin
879a0524fc Merge remote-tracking branch 'origin/beta' 2023-12-26 15:14:15 +01:00
Bruno Herbelin
a529b34f99 BugFix play bounce and seek mode
Probably due to gstreamer change of implementation: to check if still backward compatible
2023-12-26 14:31:02 +01:00
Bruno Herbelin
e8daeb5f30 BugFix luma and chroma key
making alpha filters compatible with alpha fading of media
2023-12-26 12:04:16 +01:00
Bruno Herbelin
f610e8ba1e Add Alpha fading mode to media player timeline
Fading color (to black) is not useful for media with transparency; there fading of alpha is necessary; the fading mode allows to select fade color or fade alpha. Also the source control window had to be adapted, with a checkerboard to show the alpha effect. The cropping of the image in control window was also fixed.
2023-12-26 10:57:26 +01:00
Bruno Herbelin
b3245c967b Allow toggle snap cursor with pressing/release ALT key 2023-12-24 09:24:51 +01:00
Bruno Herbelin
0af9da2214 BugFix Mask Paint Cropped source 2023-12-23 22:37:59 +01:00
Bruno Herbelin
db68f80048 Finalizing implementation of Geometry crop and distortion
Texture view adapts to the cropped shape of source,
2023-12-23 22:10:23 +01:00
Bruno Herbelin
b0efd80e42 compilation fix for OSX 2023-12-23 14:24:32 +01:00
BHBN
3e12e0b84d Update README.md
copy-paste friendly command lines
2023-12-18 14:40:49 +01:00
Bruno Herbelin
5465a45dc6 First acceptable implementation of geometry distortion and crop
Fixed shape node and crop, added rounding corner.
2023-12-17 23:30:41 +01:00
Bruno Herbelin
53bd7d6ae2 DRAFT Source geometry 4 sides crop 2023-12-16 20:52:36 +01:00
Bruno Herbelin
d66751b6ac DRAFT Source geometry distortion shape nodes
MeshSurface allows distortion of surface of sources, image shader vertex change of mesh surface, new handles to distort mesh in geometry view
2023-12-10 22:12:28 +01:00
Bruno Herbelin
95de6d0afc Configure Snap Grid cursor in panel
Move setting proportional_grid out of application settings
2023-12-10 17:46:39 +01:00
Bruno Herbelin
147daa7681 Merge branch 'master' of https://github.com/brunoherbelin/vimix 2023-12-03 00:14:50 +01:00
Bruno Herbelin
ece925858a Improve snapcraft 2023-12-03 00:14:41 +01:00
Bruno Herbelin
657b05d077 Add Luminance parameter to Lumakey filter 2023-11-21 21:50:24 +01:00
Bruno Herbelin
cf3bceeb46 BugFix Seek callback
Fixed seek to accept different input (target time, target percent, or hh:mm:ss) and add OSC target for HH MM SS MS
2023-11-14 20:51:19 +01:00
Bruno Herbelin
6735e5eaaa New Reload source function
Generalize the reload of stream source to all types of sources. Enable OSC command to reload source.
2023-11-14 11:12:21 +01:00
Bruno Herbelin
7b9e71df40 BugFix RenderSource update after session ready 2023-11-13 23:50:03 +01:00
Bruno Herbelin
fae61f3d87 BugFix do not update render source with not active 2023-11-13 23:35:26 +01:00
Bruno Herbelin
a57419150e BugFix Stream Source reload
Change stream reopen() to StreamSource reload()
2023-11-13 23:03:31 +01:00
Bruno Herbelin
378257b7bf Minor improvement: mixing view shifted right 2023-11-13 18:30:42 +01:00
Bruno Herbelin
885b92a0a1 Bug Fix Text source 2023-11-13 18:30:20 +01:00
Bruno Herbelin
58371c36d3 Text source satisfying
Improved layout options, better UI, tested features.
2023-11-13 12:41:37 +01:00
Bruno Herbelin
d39064b209 New Text source
Initial implementation of Text Source, displaying free text or subtitle file. support for Pango font description and formatting tags via gstreamer textoverlay plugin. Saving and loading in XML, GUI for creation (in patterns) and for editing.
2023-11-12 01:15:54 +01:00
Bruno Herbelin
f497da7967 BugFix Correction of Seek source callback 2023-11-08 23:08:12 +01:00
Bruno Herbelin
03931cb232 New Audio recording 2023-11-05 17:02:10 +01:00
Bruno Herbelin
4eeb02d9d4 Merge remote-tracking branch 'origin/master' into beta 2023-11-01 18:13:37 +01:00
Bruno Herbelin
8ff5ae3555 BugFix type OSC 2023-11-01 15:59:20 +01:00
Bruno Herbelin
053a5e9dbe New Audio volume multipliers
Multiply the audio volume of Media Source by alpha and/or opacity (timeline).
2023-10-28 16:18:15 +02:00
Bruno Herbelin
5a1a88bf33 New support for AUDIO
Allows looking for audio streams in media files (settings) and enabling  / disabling audio for each MediaPlayer individually. Control of volume per media, saving in session file.
2023-10-28 12:58:17 +02:00
Bruno Herbelin
f19b18d806 BugFix UI 2023-10-27 21:13:46 +02:00
Bruno Herbelin
f6e4701d6b BugFix Storing Play status of source in XML
Undo and restore play status of Source. Fix reload / restore play speed.
2023-10-27 19:29:39 +02:00
Bruno Herbelin
c3d686e472 Bugfix multisampling support 2023-10-27 19:27:44 +02:00
Bruno Herbelin
6c7ff870e8 Merge remote-tracking branch 'origin/master' into beta 2023-09-24 23:15:21 +02:00
Bruno Herbelin
38bac83ddd BugFix Program not ending because of Ableton Link
Destructor of ableton::Link is blocked at deletion of static instance in program: using a pointer avoids the problem.
2023-09-24 16:00:45 +02:00
Bruno Herbelin
4d8c77cf3e OpenGL Fix for OSX
Apple OpenGL drivers do not support multisampling; testing for extension and disabling. Disabling other features not supported by Apple OpenGL (MIPMAP HINT, PERSPECTIVE_CORRECTION).
2023-09-24 15:58:49 +02:00
Bruno Herbelin
0a147697d2 videoconvertscale is not standard in all gstreamer version 2023-09-19 22:09:38 +02:00
Bruno Herbelin
21837e7464 Revert to OpenGL 4.1 loader
Seems to be the upper limit for Apple compatibility with Metal on Apple M2.
2023-09-17 23:45:19 +02:00
Bruno Herbelin
d747962e24 Compile OSCPACK for M2 APPLE 2023-09-17 23:24:25 +02:00
Bruno Herbelin
49f09d1b3a Auto stash before merge of "master" and "origin/master" 2023-09-17 18:28:32 +02:00
Bruno Herbelin
783d6e69b1 Merge remote-tracking branch 'origin/beta' 2023-09-17 18:22:51 +02:00
Bruno Herbelin
6452ff78c0 Minor UI improvements 2023-09-17 18:22:32 +02:00
Bruno Herbelin
a430d39849 BugFix Stream sources change input 2023-09-17 12:17:41 +02:00
Bruno Herbelin
33c222555f New Playlists and new main panel
Favorite and custom playlists of Sessions. Main panel separate control of current session (with preview) and selection of session in playlists. Bugfix in history of files.
2023-09-17 00:51:34 +02:00
Bruno Herbelin
00f7e0fe62 Improved OSX OpenGL compatibility
...but still crashes sometimes.
2023-09-06 19:05:58 +02:00
Bruno Herbelin
44a31ede74 BugFix and various improvements 2023-09-03 18:13:43 +02:00
Bruno Herbelin
fb3ee2aa8c New button to reorder the list of sessions and of media files
In navigator panel, the list of sessions can be reordered either alphabetically or by file modification date. Same for list of media files for creating a new media source.
2023-09-03 00:08:30 +02:00
Bruno Herbelin
58e5dd9186 BugFix Geometry view select source with CTRL+clic 2023-09-02 15:25:30 +02:00
Bruno Herbelin
27ec46c64e New Slider to control Mouse Pointer strength 2023-09-02 12:48:00 +02:00
Bruno Herbelin
12a5d777e5 Icons improved for Display, preview, etc
Also added a close icon to large preview. Fixed help with recent improvements.
2023-09-01 17:53:50 +02:00
Bruno Herbelin
276a94f9e8 New Keyboard selection of sources by Index keys [0 - 9] 2023-08-31 19:40:45 +02:00
Bruno Herbelin
ccc3c86900 Rename 'alternative' to 'snap cursor, with ALT or option key (OSX) 2023-08-31 15:11:35 +02:00
Bruno Herbelin
124415363f Improve Layers view icons and Workspace management
Added 'Workspace any' in list of workspaces to allow Geometry view to list sources from all workspaces. Updated icon for layers view, in left panel and in view.
2023-08-30 20:47:14 +02:00
Bruno Herbelin
de850b39f2 Update CMakeRC to version 2.0.1 2023-08-29 18:28:32 +02:00
Bruno Herbelin
379f73b6ca OSX opengl pedantic update
there was a strange warning " POSSIBLE ISSUE: unit 0 GLD_TEXTURE_INDEX_2D is unloadable and bound to sampler type (Float) - using zero texture because texture unloadable", related to the first use of the opengl texture. Initializing the white texture seems to fix the problem.
2023-08-29 14:40:09 +02:00
Bruno Herbelin
159b778fa9 Make README code easy to copy/paste
removed the "~$ " that simulated the Bash prompt
2023-08-29 00:11:36 +02:00
Bruno Herbelin
007f7a0ce1 New using arrow Keys to simulate source grabbing
Allows combining arrows with Mouse Pointer effects (e.g. grid). Also added progressive acceleration of movement during the first 1 second of key press (starting very slow movement for precise displacement). Bugs fixed in Mouse Pointer.
2023-08-29 00:10:32 +02:00
Bruno Herbelin
54fa642693 Fix MousePointer Draw
Apply io.DisplayFramebufferScale to draw coordinates (convert back mouse coordinates from GL to display)
2023-08-28 21:21:47 +02:00
Bruno Herbelin
2fbc6f9193 BugFix Mouse pointer
Avoid reading imgui io.MousePos
2023-08-28 13:06:49 +02:00
Bruno Herbelin
262c6fd8ab New ALT key selects alternative mouse Pointer
Maintain ALT to activate the selected mouse Pointer. Also possible to ALT LOCK for maintaining the cursor. Local popup window allows selecting. Changed the View options selection to match this popup approach.
2023-08-27 17:49:46 +02:00
Bruno Herbelin
7fcb53c7d0 New GRID in views to perrorm MousePointer snap to GRID
For this new MousePointer to snap to grid, a Grid specific for each view is necessary. Grid for moving is orthographic (with an aspect ratio), and grid for mixing or rotation is in polar coordinates. Rendering is done with new Primitives. The entire calculation of grab coordinates is changed to be able to snap to grid coordinates in all circumstances.
2023-08-27 12:15:31 +02:00
Bruno Herbelin
d0e1101bfb New Grid Primitive, new Coloring visitor and some Scene corrections 2023-08-21 23:02:34 +02:00
Bruno Herbelin
a8bb4ae6d1 New MousePointer to change behavior of mouse
Initial implementation, mostly replicating GLMixer features. 5 Modes; default, linear, spring, wiggly and metronome. Save in Settings. Selection in Navigation panel.
2023-08-19 23:39:41 +02:00
Bruno Herbelin
d743307e59 Mixer re-creates sources after CRITICAL failure that was fixed
Stream sources that fail CRITICAL and are later back to NONE failure can now be re-created and reintegrated in the Mixer
2023-08-18 11:58:47 +02:00
Bruno Herbelin
e92e9eb45c Various UI improvements 2023-08-17 17:51:29 +02:00
Bruno Herbelin
aaefa356ae Specific MediaPlayer error message for opening non-existing file 2023-08-17 17:50:59 +02:00
Bruno Herbelin
3a7da2bc98 Add possibility of Render Loopback source to replay 2023-08-17 17:49:40 +02:00
Bruno Herbelin
94b211e3b7 GST prefer horizontal device input 2023-08-16 23:13:01 +02:00
Bruno Herbelin
9ff5b90605 UI improvement: new icon for patten source
Minor UI improvements in help and menus
2023-08-16 14:06:16 +02:00
Bruno Herbelin
a033b74f7f 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.
2023-08-15 22:26:42 +02:00
Bruno Herbelin
bb4e81b00a BugFix Display of current source in workspace
Current source outline should not be shown in Geometry view if not in current workspace. Added possibility to switch workspace in source panel.
2023-08-13 10:17:01 +02:00
Bruno Herbelin
5f2e0b79cd BugFix Do not insert new source in foreground 2023-08-13 09:37:59 +02:00
Bruno Herbelin
64a7fef7c1 Improve Insert panel terminilogy 2023-08-13 09:33:18 +02:00
Bruno Herbelin
09dbc5c84e Improve UX List of New source type to insert
New icons for inserted source panel. Merged loopback into connected list. Removed 'internal' new source.
2023-08-12 20:47:26 +02:00
Bruno Herbelin
e71e0791bc Merge remote-tracking branch 'origin/beta' 2023-08-12 16:38:24 +02:00
Bruno Herbelin
2a4be39c9a Merge remote-tracking branch 'origin/beta' into beta 2023-08-12 16:01:36 +02:00
Bruno Herbelin
3cde191afb Compilation fix OSX
Pedantic warning CLang for 64bits compiler.
2023-08-12 16:01:12 +02:00
Bruno Herbelin
02a8d9a243 Merge remote-tracking branch 'origin/master' into beta 2023-08-12 15:19:39 +02:00
Bruno Herbelin
7f4e313a78 Merge remote-tracking branch 'origin/beta' 2023-08-12 15:18:45 +02:00
Bruno Herbelin
44e3d90dcb Merge remote-tracking branch 'origin/beta' 2023-08-12 15:17:12 +02:00
BHBN
945eb3ccc6 Merge pull request #102 from AdrianBunk/master
src/BaseToolkit.h: Add the missing #include <cstdint>
2023-08-12 15:13:12 +02:00
Bruno Herbelin
793008852a Updated lsit of Linux nvidia hardware accelerated plugins 2023-08-11 20:17:08 +02:00
Bruno Herbelin
7e791ee5e4 BugFix Correct detection of single frame media
Was causing a problem for play at loading (not paying before started)
2023-08-11 18:04:32 +02:00
Bruno Herbelin
1413490579 BugFix MediaPlayer first PTS used for rewind video
Also shows the gap in the video timeline
2023-08-11 17:16:07 +02:00
Bruno Herbelin
57154e5d0b BugFix OOps, crashed on delete timeline... 2023-08-11 13:49:30 +02:00
Bruno Herbelin
57a1556e23 Improve Pannel source with a bit of spacing 2023-08-11 13:07:08 +02:00
Bruno Herbelin
e248e92ca1 Fix Player menu for 'Timeline'
Replace 'Video' menu for MediaPlayer to 'Timeline'. Move Hardware decoding selection to MediaSource GUI Visitor.
2023-08-11 13:03:26 +02:00
Bruno Herbelin
fa5adcf08b BugFix Loading Timeline of MediaPlayer 2023-08-11 12:37:13 +02:00
Bruno Herbelin
87a51afd99 New Generalize option to restart on deactivation to any StreamSource
StreamSource now have the option 'restart on deactivation' like MediaPlayer. This option is saved in XML (added Visitors for Stream and StreamSource). The GUI is added as sub-menu in play bar (like for MediaPlayer). Some StreamSource subclasses needed to be fixed to allow this feature (e.g. MultiFileSource).
2023-08-10 18:47:44 +02:00
Bruno Herbelin
4efaa1f350 New MediaPlayer image with timeline
Enable playing and seeking into a timeline on a media player that loaded an image. Timeline sets a duration (end) and is saved/loaded. Add a gstreamer imagefreeze element in the pipeline to simulate a playable stream. Distinction must be made between 'isImage' (what was loaded) and 'singleFrame' (what is in the pipeline). GUI is added and customized with menu and dialog.
2023-08-10 00:46:48 +02:00
Bruno Herbelin
1d329600af BugFix MediaPlayer support for GIF animation
Playbin needs a videorate video filter, and thus user cannot change the video effect on a GIF
2023-08-09 12:11:15 +02:00
Bruno Herbelin
c2a0e51984 Code cleanup - eradicate sprintf 2023-08-08 23:31:11 +02:00
Bruno Herbelin
ebd59e38ce Improve UI Play Direction and speed
Play speed slider changes the speed without changing direction (not working well). Change play direction is now in a play sub-menu (mouse over) in play bar. Video menu changed, with Timeline section
2023-08-08 23:02:34 +02:00
Bruno Herbelin
f75b384c17 New support for gstreamer video effects in MediaPlayer
Implementation of the GUI allowing to set the pipeline element description
2023-08-08 17:43:46 +02:00
Bruno Herbelin
5419622c74 Upgrade MediaPlayer with gstreamer playbin (for GST > 18)
Change implementation of gst pipeline in MediaPlayer to use gstreamer playbin. This makes everything works more smoothly (including instant rate change). This also opens the possibility to allow audio mixing. Other bugfixes include set play speed rate at start, add video effects on reopen media player.
2023-08-07 19:40:08 +02:00
Adrian Bunk
ab40011954 src/BaseToolkit.h: Add the missing #include <cstdint>
This fixes #100
2023-08-05 10:50:35 +03:00
Bruno Herbelin
1b658e9b40 Code cleanup. Split code for each workspace windows
Renamed class and split code for compiling the multiple workspace windows as separate files (h and cpp).
2023-08-03 16:18:00 +02:00
Bruno Herbelin
49605f9c23 Fix help text 2023-07-23 11:27:26 +02:00
Bruno Herbelin
d9b6b808f7 BugFix Rendering Mask Ellipse 2023-07-06 22:20:48 +02:00
Bruno Herbelin
1607dd329d Merge remote-tracking branch 'origin/beta' 2023-07-05 23:49:22 +02:00
Bruno Herbelin
74337b2699 BugFix MediaPlayer speed change
Changed MediaPlayer::setEffect() mechanism to have a gst pipeline compatible with instant rate change.
2023-07-05 21:59:28 +02:00
Bruno Herbelin
d6a684bbe7 Improved behavior left panel
New behavior left panel depending on settings of always_visible panel. Remembers (access from UserInterface) the source that is currently selected in the left panel, and use this source as the one manipulated in TextureView. List possible source new source modes with large icons (instead of combo box).
2023-07-03 20:50:48 +02:00
Bruno Herbelin
feeb997f62 BugFix Keep source in selection when edited in Texture view 2023-06-25 18:24:31 +02:00
Bruno Herbelin
26da3bf9a8 Bugfix Set single source as current after area selection
Strict conditions to set a source as current after an area selection
2023-06-25 18:23:46 +02:00
Bruno Herbelin
82046afd9f BugFix Do not unset current source manipulated in a selection 2023-06-25 18:21:26 +02:00
Bruno Herbelin
97704deea0 Add intermediate display mode of source selected but not current
Frames of sources are now different between default, selected and current modes (thus emphasizing the outline for the unique current source).
2023-06-25 18:20:28 +02:00
Bruno Herbelin
64b2a18ff3 New feature: Texture view Mask Source
Enable use of a source as mask for another source. Improved Mask mechanism in Source class, with various flags for update of source (avoid repeated mask update (GPU costly). Using SourceLink to link source to mask (improved robustness of SourceLink).
2023-06-24 23:28:13 +02:00
Bruno Herbelin
cf16edceec Optim Enable generating FrameBufferImage of RGB and RGBA frame buffers 2023-06-24 23:22:34 +02:00
Bruno Herbelin
f6008737d1 Optim: request update on activation only if really needed
With previous order of operations, the need_update_ flag was set to true every frame when a clone was depending on the source.
2023-06-21 19:57:44 +02:00
Bruno Herbelin
61de8c4717 Bugfix: prevent infinite mask update in case of failure 2023-06-21 19:41:53 +02:00
Bruno Herbelin
83ad83e656 Optim: No need to force update of parent of clone source
The call to "origin_->touch()" was made useless when clone source changed of mechanism to use shading filters. It didn't change the rendering, but causes to force the (useless) update of the origin source.
2023-06-21 16:10:58 +02:00
Bruno Herbelin
2afb13c580 UX improvements (F6 large preview, panel menu) 2023-06-12 01:05:37 +02:00
Bruno Herbelin
e48a963503 Draft feature Live change of Media player pipeline
Intended for adding effects (e.g. Frei0r). Not active yet.
2023-06-12 01:04:38 +02:00
Bruno Herbelin
c846a0626f New left panel mode (auto hide or always visible) 2023-06-11 16:54:33 +02:00
Bruno Herbelin
a6b1d09ff1 BugFix saving and loading sources in order
Two problems resoved: saving in the opposite order (session visitor inserting opposite order), and loading of clones that needed a reordering.
2023-06-10 12:33:54 +02:00
Bruno Herbelin
3603e146cc BugFix Toggle windows 2023-06-09 21:14:18 +02:00
Bruno Herbelin
9ab597c0e9 Icons to show enabled and disabled Hardware decoding
Updated the left panel for media source to show indication of hardware decoding on/off
2023-06-08 00:19:21 +02:00
Bruno Herbelin
3251e9f845 Fix Show vimix main window on output close
Commit c0befa0f49 introduced a bug under OSX. Now fixed.
2023-06-07 22:57:40 +02:00
Bruno Herbelin
3e7b24a3de BugFix Prevents failure of hardware decoding of images. 2023-06-07 18:03:48 +02:00
Bruno Herbelin
6989cd4d40 Fix command line vimix
Commit 0f7d42ab83 broke the --clean option.
2023-06-07 17:50:01 +02:00
Bruno Herbelin
c7205a512e Compilation for OSX ARM M2 2023-06-07 16:40:02 +02:00
Bruno Herbelin
c0befa0f49 Show vimix main window on attempt to close an output window
Because only vimix Displays view is allowed to manage output windows, it is not possible to close the window from system window manager. Instead, we show vimix and the Displays view to help the user.
2023-06-04 18:21:39 +02:00
Bruno Herbelin
2223024383 Example script OSC peer2peer 2023-06-04 17:46:41 +02:00
Bruno Herbelin
216d9a1686 OSC Peer to peer request
Added OSC mechanism to request network stream. Improved stability of H264 streaming.
2023-06-04 16:14:45 +02:00
Bruno Herbelin
1f9bff6182 BugFix Detect and display hardware decoder name 2023-06-03 00:14:34 +02:00
Bruno Herbelin
0f7d42ab83 BugFix command line vimix
Open only the session file given in argument, even if auto load_at_start is given. Displays GIT version if possible.
2023-06-02 20:15:58 +02:00
Bruno Herbelin
60334f24f1 BugFix output view; prevent display when warning dialog is open 2023-06-02 00:26:23 +02:00
Bruno Herbelin
d7d099d2d7 Add icon to show hardware decoding info in source panel 2023-06-01 23:42:29 +02:00
Bruno Herbelin
514d4170be Smooth speed change (fix issue #96)
Use GST_SEEK_FLAG_INSTANT_RATE_CHANGE seeking event available since gst version 1.18 for a smooth (non flushing) change of play speed
2023-06-01 22:08:55 +02:00
Bruno Herbelin
6b1c1853b8 Add nvidia hardware accelerated decoding of av1 codec 2023-05-31 19:54:22 +02:00
Bruno Herbelin
ced318637f Source replacement and create source with software decoder 2023-05-31 19:27:08 +02:00
Bruno Herbelin
c2c7c37ef6 BugFix Copy blending mode when replacing source 2023-05-30 17:23:44 +02:00
Bruno Herbelin
7e5041eac5 BugFix loading of info of media source 2023-05-30 16:42:22 +02:00
Bruno Herbelin
4b7db87444 Code cleanup compilation 2023-05-30 15:07:43 +02:00
Bruno Herbelin
22aba74ed9 SRT client does not need to force long latency 2023-05-30 15:06:27 +02:00
Bruno Herbelin
5283dd2f9e Non fatal Warning on gstreamer discovery of unknown format
uridecodebin can fail for one of the media stream (e.g. audio) and still be able to load video.
2023-05-30 15:05:11 +02:00
Bruno Herbelin
8bc69ba0a4 Delay user notification for when source is ready 2023-05-30 15:03:32 +02:00
Bruno Herbelin
90207c6184 BugFix in User interface
Window sticked to view: restored to all views if user press display from another view.
Add press [Return] to validate Dialog.
2023-05-30 10:54:32 +02:00
Bruno Herbelin
678590fc4f Merge remote-tracking branch 'origin/master' into beta 2023-05-20 21:12:55 +02:00
Bruno Herbelin
9170aba906 Merge remote-tracking branch 'origin/beta' 2023-05-20 21:12:27 +02:00
BHBN
59a92bb172 Merge pull request #94 from RKelln/master
Add /speed OSC endpoint to control playspeed
2023-05-20 21:10:59 +02:00
Bruno Herbelin
f12cf8a6ce Merge remote-tracking branch 'RKelln/master' into beta 2023-05-20 21:09:20 +02:00
Ryan Kelln
26cb75f93f Improved handling of problematic video requiring software decode.
Always allow disabling hardware en/decode.
Warn and try to get info when gst discoverer errors.
2023-05-19 15:08:47 -04:00
Ryan Kelln
e8b05cd7c2 Add /speed OSC endpoint to control playspeed 2023-05-17 12:08:34 -04:00
Bruno Herbelin
4930c07e9c BugFix; disable mouse over and picking on source initials 2023-05-11 19:58:36 +02:00
Bruno Herbelin
39d4002491 Improved SRT Receiver source
Failure cause to RETRY to connect. Info visitor informs on status of connection. Icon associated to SRT Broadcast icon.
2023-04-30 13:30:24 +02:00
Bruno Herbelin
e095b139ae Merge remote-tracking branch 'origin/beta' 2023-04-29 14:50:42 +02:00
Bruno Herbelin
088cf97ebf SRT streaming improvement
Broadaster: muxing for network stream (alignment 7), ensured to be in listener mode. No forced demuxing of SRT input.
2023-04-29 14:19:27 +02:00
Bruno Herbelin
be90b30b8f BugFix Fullscreen main window 2023-04-29 12:48:28 +02:00
Bruno Herbelin
0ff3041e89 Updated screenshots for new packages 2023-04-28 22:16:04 +02:00
Bruno Herbelin
9d2c2c0e47 Merge remote-tracking branch 'origin/beta' 2023-04-28 00:51:22 +02:00
Bruno Herbelin
b35f2b7cb4 Show git version in cmake compile logs 2023-04-28 00:51:08 +02:00
Bruno Herbelin
1988c5fe9a Merge remote-tracking branch 'origin/beta' 2023-04-27 22:54:51 +02:00
Bruno Herbelin
aad99ce3a4 Fixup Snap missing dependencies
installing stage-package gstreamer1.0-libav has libblas and liblapack dependencies: stranlely snap does not install them automatically...
2023-04-27 22:54:32 +02:00
Bruno Herbelin
1d7209693a Merge remote-tracking branch 'origin/beta' 2023-04-25 22:48:40 +02:00
Bruno Herbelin
4017732d71 Snapcraft switch to Core22 and Gnome 42 2023-04-25 22:48:03 +02:00
Bruno Herbelin
c09f2a1121 BugFix Output View 2023-04-25 22:47:43 +02:00
Bruno Herbelin
9467a0700a BugFix Drop file on Mixing view 2023-04-23 23:15:31 +02:00
Bruno Herbelin
ef74faeef4 BugFix UI Source toolbox and Display view 2023-04-23 19:11:32 +02:00
Bruno Herbelin
2891ad7efe UI improved
Add Help and Log to About and to supplementary list of windows.
2023-04-22 22:09:46 +02:00
Bruno Herbelin
364ffc4659 BugFix crash selection area Displays view 2023-04-21 22:38:42 +02:00
Bruno Herbelin
8aa82274ff BugFix UI and completed missing help 2023-04-20 00:31:59 +02:00
Bruno Herbelin
71ca037ca8 BugFix Source toolbar 2023-04-19 23:21:36 +02:00
Bruno Herbelin
341aac8ff7 Added Mouse wheel input on all sliders.
mouse wheel performs minimal increment to adjust precisely values of sliders in source panels.
2023-04-19 22:56:43 +02:00
Bruno Herbelin
99ccfb8e94 Revert back: disable clic on source initials in mixing and layers view
There is a bug in testing of bounding box and the interaction is not clear.
2023-04-19 22:54:58 +02:00
Bruno Herbelin
4ed884de55 Mouse over icons on source (on Symbol and Characters)
Rename class Glyph in Decoration to Character ('Glyph' was used in system X typedef). Added MouseOver in Mixing and Layers view to show highlight color on icons of Symbol and Initials of source. Show left panel on clic on source Initials.
2023-04-16 23:21:08 +02:00
Bruno Herbelin
f9caa75aa7 New source toolbar, cleanup other toolbars and help window
Split Metrics and Source editor as two separate toolbars. Removed keyboard shortcut for metrics and sticky note. Ensure log notification are visible.
2023-04-16 14:51:58 +02:00
Bruno Herbelin
37a7bc43fd BugFix Imgui toolkit layout and colors 2023-04-16 14:50:12 +02:00
Bruno Herbelin
cdd6ac1d8b Added Counter of Turns in Timer Stopwatch 2023-04-14 00:23:57 +02:00
Bruno Herbelin
235e0efc09 Merge remote-tracking branch 'origin/master' into beta 2023-04-14 00:23:29 +02:00
BHBN
2f4f93d3ba Update README.md 2023-04-13 23:59:34 +02:00
Bruno Herbelin
f25a1b70f3 Adding Git tags info in About Vimix 2023-04-13 23:17:58 +02:00
Bruno Herbelin
6ec8edd5e2 Improved slider Threshold 2023-04-13 00:35:20 +02:00
Bruno Herbelin
8beeced7c2 Improved sliders Color correction Threshold and Posterize 2023-04-13 00:00:02 +02:00
Bruno Herbelin
f4add84a13 BugFix color picker dialog
Also blocks UI until color dialog is closed
2023-04-12 22:51:53 +02:00
Bruno Herbelin
c507d0ed7e Improved appearance Display view
Enhance difference between selected / unselected window
2023-04-12 20:51:28 +02:00
Bruno Herbelin
3ff193f42d Restore on start; open session and view settings, or start fresh 2023-04-12 20:00:19 +02:00
Bruno Herbelin
8104050658 Simplified Metrics info and memory usage 2023-04-12 19:26:48 +02:00
Bruno Herbelin
b53f0b4c1b BugFix - correcting previous change 2023-04-12 00:19:16 +02:00
Bruno Herbelin
7698c05dd5 Memory Leaks fix
Fixed tiny leaks in thumbnail saving to XML. Also cleaner ending freeing all allocated.
2023-04-10 12:46:57 +02:00
Bruno Herbelin
18734345a1 UI improvements, with context menu for additional windows 2023-04-09 16:11:16 +02:00
Bruno Herbelin
ad54ee4bda Improved stats on memory usage and Metrics display 2023-04-09 01:12:12 +02:00
Bruno Herbelin
f8a4b7b1db ALT+SHIFT for precise movement in Geometry and Texture views 2023-04-08 00:01:52 +02:00
Bruno Herbelin
e6966df9ac Added linked aspect ratio scaling in Metric 2023-04-07 23:00:18 +02:00
Bruno Herbelin
8e17fc0edb Metrics of Source; higher precision drag float 2023-04-07 15:23:04 +02:00
Bruno Herbelin
2cdb8c022d Show Output View in Main window using F6 2023-04-03 23:41:28 +02:00
Bruno Herbelin
04822346a6 Use CTRL+F to toggle Fullscreen for all window types
Main window and output windows use same keyboard shortcut for uniformity.
2023-04-03 23:38:59 +02:00
Bruno Herbelin
6fcfc2da34 Added Output support for default Gstreamer shared memory
With option to select and configure socket path
2023-04-03 19:20:57 +02:00
Bruno Herbelin
62c7b8cdc1 Merge remote-tracking branch 'origin/master' into beta 2023-04-02 00:20:37 +02:00
Bruno Herbelin
d2d899a8d0 Update desktop and manpage version 0.8.0 2023-04-02 00:17:30 +02:00
Bruno Herbelin
921e09227f typo in metainfo.xml 2023-04-01 22:35:43 +02:00
Bruno Herbelin
39c274a515 Merge remote-tracking branch 'origin/master' into beta 2023-04-01 18:12:53 +02:00
Bruno Herbelin
5db65b6e6e Update Flathub version 0.8.0 2023-04-01 18:05:19 +02:00
Bruno Herbelin
c71fc5575f Fixed typo filename 2023-04-01 18:01:33 +02:00
Bruno Herbelin
2ebf264feb Update screenshot webpage 2023-04-01 17:55:31 +02:00
Bruno Herbelin
aa20cf5138 Update webpage for 0.8.0 release 2023-04-01 17:41:09 +02:00
Bruno
5f96a8991a OSX info of Bundle integrating cmake version info
Attempt at linking vimix to MIME type .mix
2023-03-31 19:23:42 +02:00
Bruno
47e0d6ae19 BugFix UI Custom GST Source 2023-03-31 09:41:03 +02:00
Bruno Herbelin
e3e2d8d2f6 Fixe snap 0.8.0 2023-03-30 22:59:00 +02:00
Bruno Herbelin
f57e057f2a Update Snap Version 0.8.0
Using Core20 and Gnome 3-38 environment
2023-03-30 22:44:09 +02:00
Bruno Herbelin
dc0df8cc61 Merge remote-tracking branch 'origin/beta' 2023-03-29 23:27:01 +02:00
Bruno Herbelin
c30d7a2089 Add vaapi in Flatpak 2023-03-29 23:16:00 +02:00
Bruno
03bffb3319 OSX compilation 2023-03-28 22:29:56 +02:00
Bruno Herbelin
65dc93ed37 UI pedandic eye candies 2023-03-28 00:08:17 +02:00
Bruno Herbelin
8ed04b9c6e BugFix: capture frames
Correctly capture frames in RGB and RGBA from Player, and invert vertically only screenshots.
2023-03-27 23:37:08 +02:00
Bruno Herbelin
e6a9327bae Update vimix packages icons and Desktop entry 2023-03-27 17:08:10 +02:00
Bruno Herbelin
658c506653 BugFix: add failure management in SessionSource
As in update of Mixer manager, failed sources are managed in update of SessionSources. RenderSources that fail are re-created. Attach/Detach/Attached of sources functions is unified between Session and Mixer.
2023-03-27 15:59:59 +02:00
Bruno Herbelin
ad4a4281b4 BugFix: failed source detached from mixing group 2023-03-26 20:43:49 +02:00
Bruno Herbelin
6b0070ec56 Fixed Info panel Session File Source
and other UI minor details.
2023-03-26 20:05:35 +02:00
Bruno Herbelin
c9cf6baf4b Improved Device support for V4L2 streams
Allow to reload list of devices, avoid timestamp warning in Loopback, clear UI menu.
2023-03-26 17:36:18 +02:00
Bruno Herbelin
85a25a0a39 Improved report of Media Player error 2023-03-25 20:33:30 +01:00
Bruno Herbelin
e192d254f9 Improved package support of encoder selection (x265) 2023-03-25 00:05:16 +01:00
Bruno Herbelin
9e2b9d7cf4 fix missing include gl gst 2023-03-24 22:53:18 +01:00
Bruno Herbelin
cd545213e4 Extend support of gstreamer in Flatpak
Include plugins for shm, v4l2 and libav.
2023-03-24 16:19:57 +01:00
Bruno Herbelin
935d2ff02c Update Copyright date to 2023 2023-03-23 22:55:48 +01:00
Bruno Herbelin
b7d6218b89 New icon vimix 2023-03-23 22:50:43 +01:00
Bruno Herbelin
d86754b0e6 UI selection Workspace in Geometry View
Back to the combo box (smaller and unified with other views) but following the unified color accent.
2023-03-23 22:49:35 +01:00
Bruno Herbelin
b97674a404 Minor GUI fixes 2023-03-22 22:50:43 +01:00
Bruno Herbelin
f91522fc14 Introducing multiple levels of Source Failure
This allows Mixer manager to deal with failed sources with the appropriate action: try to repair, leave for user to recreate, or delete.
2023-03-22 22:50:08 +01:00
Bruno Herbelin
9b4ef00278 Copy&Paste source DUPLICATE content (not clone) 2023-03-19 06:19:52 +01:00
Bruno Herbelin
43270c7763 Improve MIXING view UX
Slight change of opacity of Mixing icon of source (stipple) when becoming non visible. Mouse over highlight on circular buttons in Mixing circle.
2023-03-18 11:18:56 +01:00
Bruno Herbelin
9a98fb399c OSC source target by ID with # prefix
Targetting source by id should be with '#' + the number (e.g. /vimix/#2/alpha). For backward compatibility the '#' is still optional.
2023-03-18 10:30:40 +01:00
Bruno Herbelin
c255b0249f Bugfix: support OSC source target by name with ID
fix a confusion between targetting source by ID (number) and targetting a source name starting with a number.
2023-03-17 11:58:28 +01:00
Bruno Herbelin
b0e71f6f18 Compilation fix - epx10 is not standard function 2023-03-14 20:50:19 +01:00
Bruno Herbelin
7e59377daf Unified Button and colors in Views, using accent color
Using standard UI colors and simplified buttons in all views.
2023-03-12 15:10:31 +01:00
Bruno Herbelin
73aa369e6d Slight visual effect on source entering mixing circle 2023-03-12 15:07:28 +01:00
Bruno Herbelin
731d7e5d6b Allow Aplpha channel for Bundle (Session Group Source) 2023-03-12 15:06:50 +01:00
Bruno Herbelin
bc5e1c7df9 Cleanup Displays view UI 2023-03-12 12:29:01 +01:00
Bruno Herbelin
1a15f9b581 Added timeout to show Session preview 2023-03-12 12:28:38 +01:00
Bruno Herbelin
136d6052d8 Slider Color correction with quadratic and logarithmic scale
Gamma (log10) and other color correction sliders (brightness, contrast, saturation) now range from [-1 to +1] with pow 2 scaling.
2023-03-12 11:19:43 +01:00
Bruno Herbelin
811b270bae Implementation of Custom Output area in Window Displays View
Changed the 'Scaled' mode of window draw to allow custom centering and scaling of the output framebuffer in the window. Use DisplaysView to grab handles of the output frame. Save all windows custom output scaling in Settings.
2023-03-12 00:31:41 +01:00
Bruno Herbelin
7870e3cfce Tooltips in Texture View
Unified UX for Views with menu; show tooltip in TextureView as it is in DisplaysView
2023-03-10 19:17:50 +01:00
Bruno Herbelin
4874af1d5a Fixed recentering of Displays View
Adjust to always ensure all monitors are shown in the view window, also for vertical alignments and window.
2023-03-10 17:37:39 +01:00
Bruno Herbelin
1b7ead0479 Full redraw on window resize
Necessary for OSX, better anyway
2023-03-10 17:03:42 +01:00
Bruno Herbelin
ac97984314 UX clarification: replace Button to open URL by icon 'Show in Finder' 2023-03-08 23:52:47 +01:00
Bruno Herbelin
8c778e8afb Larger IMGUI_RIGHT_ALIGN 2023-03-08 22:12:37 +01:00
Bruno Herbelin
2b6bbce1d9 Highly optimized pre-processed shader for white balance 2023-03-08 19:43:08 +01:00
Bruno Herbelin
b8e0a9c1dd Code compilation fix 2023-03-07 23:38:13 +01:00
Bruno Herbelin
4c3c3de065 Minor BugFix Transition View
Cancel transition if opening the session failed.
2023-03-07 23:03:15 +01:00
Bruno Herbelin
a74801a0af BugFix MediaPlayer terminate asynchronously to avoid hanging
Deleting a MediaPlayer requires stopping the pipeline and deleting it; the call to gst_element_set_state (pipeline_, GST_STATE_NULL); is however hanging. Running this in a separate thread seems to fix the problem. It is not 100% sure however if the gst_object_unref ( GST_OBJECT (pipeline_) ); will be thread safe and not crashing...
2023-03-07 22:35:22 +01:00
Bruno Herbelin
cbe8217790 UX improvements, highlight icon button, ComboIcon, reset value label
Major changes in ImGuiVisitor (all image filtering and ImageProcessingShader), new imGuiToolkit ComboIcon (replacing previous ComboIcon widget), new icons,
2023-03-05 23:35:06 +01:00
Bruno Herbelin
e1cdf34955 BugFix; replace or open after media file select 2023-03-05 00:33:17 +01:00
Bruno Herbelin
fefc20c52a Fix locked source selection and manipulation 2023-03-04 23:48:36 +01:00
Bruno Herbelin
ad1e574cfe Rendering Manager pattern improved
Use Phillips test pattern if available, and rescale to framebuffer resolution
2023-03-04 20:18:11 +01:00
Bruno Herbelin
c25d4b7dad BugFix Stream: ensure replacement of texture on re-open 2023-03-04 13:25:12 +01:00
Bruno Herbelin
207aa88daf UX improvement: do not ask user confirmation after file selection
Create a source from file; validate the file dialog is enough to create the source (without intermediate step of confirmation).
2023-03-03 20:46:34 +01:00
Bruno Herbelin
3be08a3e63 Prevent multiple color pickers and inform user 2023-03-03 20:23:05 +01:00
Bruno Herbelin
7d91ffbafa Minor UX improvement Display View 2023-03-03 19:52:12 +01:00
Bruno Herbelin
128ba084ad Change Settings of windows to 'OutputWindows' to avoid incompatibility 2023-03-03 19:51:29 +01:00
Bruno Herbelin
0defff8f7c BugFix WhiteBalance Display View and Rendering window
Use of Settings whitebalance parameters for rendering both window and display preview window. Not most optimal maybe, but efficient and clear.
2023-03-02 23:30:17 +01:00
Bruno Herbelin
6e3497e4c4 Store RenderingWindows whitebalance in Settings 2023-03-02 05:02:48 +01:00
Bruno Herbelin
1c309b2c89 Added WhiteBalance to RenderingWindow and Display View
Render output frame to output window using a Shader implementing white balance correction. Adjusting parameters of white balance from Display View with color picker (white) and slider (temperature). GLSL filter for white balance created from ShaderToy.
2023-03-01 23:24:26 +01:00
Bruno Herbelin
35507e7fbb Cleanup Rendering and Display view code to get texture of output 2023-02-28 19:12:51 +01:00
Bruno Herbelin
bc439829cf DRAFT feature for showing test pattern on output window 2023-02-28 00:24:19 +01:00
Bruno Herbelin
93f433f388 Disabling the framebuffer blit of output rendering
Blit of framebuffer is incompatible with the new features of Display View to adjust whitebalance and geometry of rendered frame on output windows.
2023-02-28 00:23:54 +01:00
Bruno Herbelin
f9e99e2a33 Minor corrections Displays view 2023-02-27 00:51:51 +01:00
Bruno Herbelin
94dcf7c3f3 Add TAB navigation to Displays View
And improve Metrics with GPU RAM information
2023-02-27 00:21:18 +01:00
Bruno Herbelin
c3bb29182e Multi Window support in Rendering Manager and Display View
Important reshape of Rendering Manager to support creation of multiple output windows. The Display View is now designed to allow creating and manipulating output windows. Settings are incompatible with previous version.
2023-02-26 23:04:38 +01:00
Bruno Herbelin
b11f6d5b3b BugFix Color selection Chromakey filter 2023-02-25 12:43:27 +01:00
Bruno Herbelin
d2b900f7c3 Compilation cleanup 2023-02-24 06:36:04 +01:00
Bruno Herbelin
bf2b5d8882 Improved UI and help 2023-02-22 22:22:12 +01:00
Bruno Herbelin
e7878bdb8f Changed grey accent color to green, make help icon more visible. 2023-02-22 12:47:13 +01:00
Bruno Herbelin
0670550521 Improved UI for Color Picker 2023-02-21 11:46:56 +01:00
Bruno
f5df923c51 BugFix: non-ImGui calls to accent color cause crash 2023-02-21 10:10:22 +01:00
Bruno
f1f62816b5 OSX implementation of color dialog (with tinyfiledialog) 2023-02-21 10:05:18 +01:00
Bruno Herbelin
db462690b3 New color dialog to enable system color picking (GTK only)
Use GTK ColorChooserDialog for color selection of chromakey under linux
2023-02-21 00:09:24 +01:00
Bruno Herbelin
c28685c700 Improved OSC control for Batch, with sync status 2023-02-19 22:06:58 +01:00
Bruno Herbelin
1f1780597c Finalized Source Callbacks for color correction
Action Input mapping for gamma and invert color corrections
2023-02-19 12:40:23 +01:00
Bruno Herbelin
1590251dad New Source Callbacks for Play control )fast forward, seek, etc.)
2 new callbacks (PlayFastForward, PlaySpeed), modified Seek callback to take target time in seconds (instead of ratio of duration). Integrating this in Input Mapping GUI and Session saving.
2023-02-19 01:05:52 +01:00
Bruno Herbelin
d25c17342b Improved logs and Settings for hardware gstreamer plugins 2023-02-17 18:53:54 +01:00
Bruno Herbelin
e105022185 BugFix: correctly approximate rendering output aspect ratio 2023-02-16 19:28:21 +01:00
Bruno Herbelin
16931917b7 BugFix: Clone source failed do not crash
Clone source that lost its origin can be replaced.
2023-02-16 19:15:25 +01:00
Bruno Herbelin
e2e316a079 Renaming Session Group to Session Bundles and Session Child
For the user interface, use the term 'Bundle' and 'Child' session instead of Group.
2023-02-15 22:27:14 +01:00
Bruno Herbelin
1dd2151a20 BugFix: OSC target Batch testing was preventing other targets
Restore normal use of target /current for OSC
2023-02-14 00:34:21 +01:00
Bruno Herbelin
c7367ad46a Enable negative Alpha in Source Callback for inactive source
setAlpha() to negative value allows to make the source inactive (outside mixing circle)
2023-02-13 23:51:16 +01:00
Bruno Herbelin
21045411e7 BugFix UI Input Mapping 2023-02-09 23:18:43 +01:00
Bruno Herbelin
128e8834e8 Changed mechanism of Source Callback and Input Mapping
Session stores list of all callback instances and reacts on input release by calling the reverse callback if it exists, or by finishing the ongoing callback. This means the behavior of Callbacks is different for those who are reversible (i.e. returns a non-null reverse) from those which do not have reverse. The reversible callbacks enforce to be exclusive while active (key pressed), others can be repeated and complementary (run in parallel).
2023-02-09 23:18:24 +01:00
Bruno Herbelin
7433772606 Improves readability Player and Output image
True color without window transparency, info icons with shadow for readability
2023-02-06 23:30:40 +01:00
Bruno
8967f5f090 Forced int64 type for Session batch argument 2023-02-06 15:25:10 +01:00
Bruno Herbelin
3b51a6e2a9 Added OSC interface for batch# 2023-02-06 08:21:37 +01:00
Bruno Herbelin
c5cb635b4e Input Mapping for Batch of Sources
Session contains a set of 'Batch' that are created in the Player (renamed from PlayGroups). Session InputCallback can now target either a Source or a Batch, using std::variant (new type Target). Input Mapping reacts to input to create callbacks to a target, either a single source (as before) or to a Batch (multiple sources).
2023-02-05 17:05:47 +01:00
Bruno Herbelin
1e9f8d707e Unified Menu for capture actions in Player and Output 2023-02-04 13:54:17 +01:00
Bruno Herbelin
69a0aa4bd8 Accept all types of sources in Player
The concept of 'Selection' evolves to accept sources of any type, not only sources that are 'playable'. This way user can create pools to reference in OSC and in Input Mapping.
2023-02-03 19:43:02 +01:00
Bruno Herbelin
581fa88055 More informative error message on missing pattern 2023-02-03 19:30:45 +01:00
Bruno Herbelin
f991ae5aed Pedantic imgui coding 2023-02-03 19:30:17 +01:00
Bruno Herbelin
44825ece04 BugFix: Update after Session Group creation 2023-02-03 19:28:33 +01:00
Bruno Herbelin
eac2c5c020 BugFix: prevent crash on embedded session update 2023-02-03 19:27:31 +01:00
Bruno Herbelin
a593e97227 BugFix: Show Shader editor as WorkspaceWindow 2023-02-03 19:26:41 +01:00
BHBN
ecad786f50 User-built flatpack compile the Beta branch
When building the flatpak package using the instructions at https://github.com/brunoherbelin/vimix/tree/master/flatpak, users have the preview of the Beta version of vimix.
2023-02-02 22:55:28 +01:00
Bruno Herbelin
9012d33c05 Logging improvement on delete / create source 2023-01-31 21:23:26 +01:00
Bruno Herbelin
268751815f BugFix New Source File Doubleclic
WTF did I think it would be a good idea to delete a source in a separate thread? This obviously causes a crash. To be investigated when a source tailes to delete... but should not happen...
2023-01-31 21:22:53 +01:00
Bruno Herbelin
6529b170e6 Cleanup Source Fail reporting
All Stream report failure with logs, read in InfoVisitor for Sources. ImGuiVisitor for Sources also detect failure of source and its stream.
Cleanup of unused includes and functions.
2023-01-30 00:07:52 +01:00
Bruno Herbelin
5ce465cb30 New Session and Mixer mechanism for Failed sources
When a source in a session fails, it is not anymore deleted after update; the Mixer keeps it in the session but detaches it from the scene. This way the user can access the failed source in the navigator (listed in RED), and Replace the source. The Replacement of source is available for any source. The source visitor does not skip a visit if the source failed.
2023-01-29 14:33:35 +01:00
Bruno Herbelin
48f1df2fd6 ImGuiToolkit to render a Disabled Button 2023-01-29 10:54:57 +01:00
Bruno Herbelin
3e6ddf560a Player UI improvement
Changing icons of Player selection to 'CIRCLE' icons because the icon of Player is the CIRCLE with triangle. Also allows to have an icon for User Selection.
2023-01-29 10:54:28 +01:00
Bruno Herbelin
0051533ac8 Improved management of failed sources
Clone is failed if its origin is failed, handle MediaPlayer visitor and error message when fail, get SourceList of non-failed sources of a list.
2023-01-29 10:50:15 +01:00
Bruno Herbelin
e69ac7ca28 Correction of invalid keyboard shortcut in documentation 2023-01-27 22:23:28 +01:00
Bruno Herbelin
9c8abb8edf Updated selection target for OSC
Adds ability to target a selection of sources stored in the Player
2023-01-27 20:43:29 +01:00
Bruno Herbelin
9ee434f275 Cleanup Player UI
Add play/pause button on source icon in selection (dynamic or stored selection). Display source icon in lower left corner, instead of play status. Fix alignment disabled timeline. Minor bugfix.
2023-01-27 19:25:05 +01:00
Bruno Herbelin
4826d9fbf0 Logging unknown OSC attribute
Users otherwise don't know what is wrong when sending incorrect OSC attribute
2023-01-27 17:33:44 +01:00
Bruno Herbelin
3fd7b8ed3c Magnifying glass for Player and Output windows
Replace the 'inspector' menu in favor of a magnifying glass button at top right corner of imgui window for Player and Output preview. Disable the magnifying glass upon window unfocus.
2023-01-16 00:18:17 +01:00
Bruno Herbelin
ebc8d483d9 BugFix display source button UV in muti-source Player 2023-01-14 23:42:59 +01:00
Bruno Herbelin
9821d3595a BugFix display inpector UV in Player of cropped sources 2023-01-14 23:06:34 +01:00
Bruno Herbelin
f21be9d10c Put beta in home-made flatpak 2023-01-01 22:25:10 +01:00
Bruno Herbelin
d221036cde Remove Window Refresh callback
Rendering draw should NOT be called twice
2023-01-01 22:15:38 +01:00
Bruno Herbelin
1dbff48ebb Cleanup views and bugfixes
Remove dependency to Imgui in View class. Cosmetic improvement UI in views.
2023-01-01 16:34:47 +01:00
Bruno Herbelin
43e56fc433 Polishing up DisplaysView
Options to fit output window on all screens. Added doubleclic function to View class (Transition view and Displays view have specific reaction to double clic.
2022-12-30 21:46:08 +01:00
Bruno Herbelin
b3b562f4bb Stabilized Displays View
Manipulation of output window from Displays View, fullscreen and window modes. Adapted preview window of display.
2022-12-29 20:50:40 +01:00
Bruno Herbelin
784ac996d3 First operational implementation of Displays View 2022-12-29 00:39:51 +01:00
Bruno Herbelin
fb6a95078d Creation and minimal integration of Displays View 2022-12-26 15:46:37 +01:00
Bruno Herbelin
189e7b8bc9 Cleanup monitor management in Rendering Manager 2022-12-26 15:45:42 +01:00
Bruno Herbelin
55967ad27c Changed icon of output window 2022-12-24 00:51:20 +01:00
Bruno Herbelin
e2c82af4d6 Implementation of custom session resolution
Moved presets of resolution to RenderView (framebuffer class is lower level). Changed logic of UI selection of session resolution change.
2022-12-23 20:23:39 +01:00
Bruno Herbelin
8712923bec Detecting monitors in Rendering Manager 2022-12-18 12:11:42 +01:00
Bruno Herbelin
416635179b Fix warning runtime invalid scancode 2022-12-18 12:03:29 +01:00
BHBN
c1b635e036 Create jekyll-gh-pages.yml 2022-12-13 23:38:05 +01:00
Bruno Herbelin
2860d8f1de Update doc and README to mention flatpak 2022-12-13 18:32:32 +01:00
Bruno Herbelin
c1fb07b4c7 Fixed flatpak according to flathub recommendations 2022-12-12 23:19:37 +01:00
BHBN
5036c2231c Update build instructions to reference flatpak 2022-12-11 20:51:06 +01:00
BHBN
c848666e17 Update instruction flatpak markdown layout 2022-12-11 20:40:19 +01:00
Bruno Herbelin
55aef98a30 markdown readme 2022-12-11 16:09:38 +01:00
Bruno Herbelin
7cbbf799dc Documenting how to make a flatpak of vimix 2022-12-11 16:06:51 +01:00
Bruno Herbelin
30a4e0297c Making flatpak usable by command line 2022-12-11 15:19:10 +01:00
Bruno Herbelin
5f68f51693 Support for non-US keyboard layout
Hack to translate key press index to matched letter as key. Should work on most Latin keyboard layout, but not tested otherwise...
2022-12-11 14:10:19 +01:00
Bruno Herbelin
8e6aaf29e0 BugFix WorkspaceWindow toggle 2022-12-11 13:26:28 +01:00
Bruno Herbelin
cde0e74a2e Fixup Flatpak for Flathub install
NB: the Vimix.json flatpak for Flathub is in the dedicated Flathub branch for submission to repo. The local flatpak/.Vimix.json is for testing locally building flatpak with latest code.
2022-12-10 14:38:30 +01:00
Bruno Herbelin
2a573cbab3 Prepare for version 0.7.3 2022-12-10 11:18:15 +01:00
Bruno Herbelin
941275a1b9 UI Integration of output to SRT, Shmdata and V4L2
Improved user interface and messages for the activation of Output streaming with SRT, shared memory, or loopback camera with V4L2 under linux.
2022-12-09 20:10:37 +01:00
Bruno Herbelin
c5884ec498 Fixed and unified implementation shmdata and video broadcast 2022-12-07 09:32:08 +01:00
Bruno Herbelin
da06cf52ec Integration of Shmdata in vimix
Unified menu in output window for streaming (for SRT, Shmdata and peer to peer). Cleanup SRT broadcaster and bugfix on FrameGrabber default frame timing.
2022-12-06 23:21:17 +01:00
Bruno Herbelin
07e8f489c1 Initial implementation of Shmdata broadcast
If gstshmdatasink is available (from shmdata https://gitlab.com/sat-mtl/tools/shmdata/), the shmdata broadcaster can capture output and share it to memory
2022-12-06 23:19:00 +01:00
Bruno Herbelin
baed2ac031 Tolerate unknown audio codec to play video media
The case of  MISSING_PLUGINS for audio in gst discoverer should not prevent from decoding video stream in media. The failure of discoverer should only be in absence of video stream.
2022-12-04 19:11:44 +01:00
Bruno Herbelin
cdab138b2f GUI Renaming Network sharing to Peer-to-Peer sharing 2022-12-04 18:29:07 +01:00
Bruno Herbelin
06524edfb3 Bugfix - repair problem caused by previous change 2022-12-04 18:22:47 +01:00
Bruno Herbelin
0e40550427 Bugfix Monitor detect devices even if monitor crashes
As gst_device_monitor_start can crash, the Device::manager should still fill in the list of devices at first run (fix problem on Flatpak).
2022-12-04 14:16:37 +01:00
Bruno Herbelin
e08b6ade9e Fix C++17 compilation warning 2022-12-04 13:17:51 +01:00
Bruno Herbelin
36bc4944f9 Exploring options for RIST protocol stream broadcasting 2022-12-04 12:14:32 +01:00
Bruno Herbelin
a0be95d634 Enabling SHM streaming in localhost
Adding a mechanism to revert to UDP when SHM fails; we can thus re-enable the SHP streaming for programs in localhost
2022-12-04 00:29:09 +01:00
Bruno Herbelin
faf8d4c4ad Add shmdata lib to flatpak, detect gstshmdata plugin at runtime 2022-12-03 18:29:56 +01:00
Bruno Herbelin
8af740caa8 Detecting shmdata library and gst plugin
Find shmdata library and add it to gstreamer plugin path. User is informed on how to build shmdata.
2022-12-03 18:00:58 +01:00
Bruno Herbelin
69fa3521f9 Removed submodule ext/shmdata 2022-12-03 17:45:07 +01:00
Bruno Herbelin
991a96d3dc Add shmdata submodule 2022-12-02 18:53:00 +01:00
Bruno Herbelin
b10bf06305 Minimize compilation gstreamer in flatpak 2022-11-25 22:39:10 +01:00
Bruno Herbelin
7f54b30fbe packaging x264 encoder in flatpak 2022-11-25 21:22:46 +01:00
Bruno Herbelin
ee79043536 Packaging frei0r plugins in flatpak 2022-11-22 00:15:58 +01:00
Bruno Herbelin
c9e6611b92 Packaging gstreamer and SRT in flatpak 2022-11-21 00:50:47 +01:00
Bruno Herbelin
73d128d89a Merge remote-tracking branch 'origin/master' 2022-11-20 00:06:39 +01:00
Bruno Herbelin
5a240acd86 Fix cmake vimix version 2022-11-20 00:06:30 +01:00
Bruno Herbelin
7dc4a5cf87 Fix cmake vimix version 2022-11-20 00:01:53 +01:00
Bruno Herbelin
6d835297b2 tag 0.7.2 2022-11-19 23:44:08 +01:00
Bruno Herbelin
b44c29e235 No build in flatpak 2022-11-19 22:25:36 +01:00
Bruno Herbelin
8da9a9cf27 flatpak repo preparation 2022-11-19 22:22:37 +01:00
Bruno Herbelin
a3617626f7 Merge remote-tracking branch 'origin/master' 2022-11-19 20:20:27 +01:00
Bruno Herbelin
e44832ea9e Packaging with flatpak
Successful flatpak-builder process. Not tested further.
2022-11-19 20:20:12 +01:00
Bruno
f841e78dcf Bundle fix (OSX) 2022-10-26 10:01:28 +02:00
Bruno
e7a8d48cca Packaging fix (Cpack OSX) 2022-10-26 09:44:10 +02:00
Bruno
69e8d0e32f Compilation fix (OSX) 2022-10-25 21:38:21 +02:00
Bruno
830d1a6bf9 OSX entitlements to include audio input
Used for gstreamer sources that generates visuals from audio
2022-10-25 00:32:18 +02:00
Bruno Herbelin
e9b72b442a Cleanup source tree
Move all C++ source files in the src subfolder. Adapted the cmake process accordingly and cleanup.
2022-10-25 00:29:22 +02:00
Bruno Herbelin
77ac7eca18 OSC message fror session open, close and save 2022-10-22 10:45:56 +02:00
BHBN
86d4198ffd Merge pull request #54 from felixgonsug/flatpak-build
adding a startpoint to flatpak building
2022-10-20 21:32:26 +02:00
felix
37dfe31ac2 adding a startpoint to flatpak building 2022-10-18 13:34:27 -03:00
Bruno Herbelin
584e1c48e6 oops 2022-10-15 21:40:13 +02:00
Bruno Herbelin
15766ceb97 Prevent bad window manipulation 2022-10-15 19:12:09 +02:00
Bruno Herbelin
6e79f28b69 Prevent Player Inspector conflict with info overlay 2022-10-15 19:11:53 +02:00
Bruno Herbelin
e9632d206b OOps, fixed debug testing 2022-10-15 19:10:55 +02:00
Bruno Herbelin
a0f55bfcb5 Added Fading target for OSC session 2022-10-15 19:06:52 +02:00
Bruno Herbelin
3c32f1da6e Bugfix generation image sequence
Fixed pb with non-power of two height of video, added more informative error messages, fixed UI issue.
2022-10-15 15:19:17 +02:00
Bruno Herbelin
7e13c1b22a Move Group/Ungroup actions to Edit menu 2022-10-15 11:51:16 +02:00
Bruno Herbelin
2fc52e673f Added Color Correction mapping input
Map image processing source callbacks to key inputs.
2022-10-15 00:26:16 +02:00
Bruno Herbelin
48001a660b Source callbacks for Image Processing color correction
Added SourceCallback classes for brightness, contrast, saturation, etc. Added OSC interface to modify color corrections
2022-10-14 19:05:14 +02:00
Bruno Herbelin
5a6daf79b6 Allow Nil Values in OSC messages
Allows providing only one argument value when two (e.g. x and y) are required by specifying the NIL type ('N') in the OSC message. E.g. /vimix/current/position Nf 0.5 sets the Y position.
2022-10-13 17:34:17 +02:00
Bruno Herbelin
ae4fd9f7df Disable Broardast and inform user if SRT not available 2022-10-13 16:24:01 +02:00
Bruno Herbelin
7dfa8776fd Minor improvement Tooltips Settings 2022-09-10 11:24:02 +02:00
Bruno Herbelin
a836796fcc Fix previous 2022-09-08 23:36:13 +02:00
Bruno Herbelin
fb131972d4 Non-blocking deletion of source in SourcePreview
Detach a thread to delete the source currently in SourcePreview in Source new panel; avoids freezing display.
2022-09-08 23:31:27 +02:00
Bruno Herbelin
140ce358fa Added history of recent SRT hosts
Saving known hosts in settings and validating ip and port in SRT connector for source
2022-09-08 20:36:58 +02:00
Bruno Herbelin
dd92f2dccb Improved OSC sync
Accept OSC request to sync source by name or id. Changed OSC seek request to be by percent target
2022-08-17 19:11:21 +02:00
Bruno Herbelin
d62004eadf Update screenshots documentation recording 2022-08-17 18:49:25 +02:00
Bruno Herbelin
abc21e9692 Send source name in Status bundle of all sources 2022-08-09 23:44:22 +02:00
Bruno Herbelin
a13b0d5d91 BugFix Shadertoy ImageFilter 2022-08-08 21:17:16 +02:00
Bruno Herbelin
12f8c75c2d Update screenshots documentation 2022-08-07 11:17:28 +02:00
Bruno Herbelin
bdc1920166 Clone with copy attributes
Two modes of cloning: from the source panel with 'Clone & filter' clones with copy of attributes (geometry, alpha, etc.), from the Insert source panel with 'Internal' source creates a fresh new copy.
2022-08-07 11:13:45 +02:00
Bruno Herbelin
8cb0d57ffe Documenting advances features 2022-08-05 23:22:34 +02:00
Bruno Herbelin
7344689263 Update screenshot Ubuntu snap 2022-08-04 00:23:06 +02:00
Bruno Herbelin
f521ca1118 Fix UI and doc of Share local network 2022-08-04 00:22:38 +02:00
Bruno Herbelin
6712f1383e Fun vimix crow in About dialog 2022-08-02 23:36:09 +02:00
Bruno Herbelin
d756fd4a29 New communication image
Crow drawing done by DALL-E (no human copyright)
2022-08-01 22:22:36 +02:00
Bruno Herbelin
e070ef1b7f adding screenshots documentation SRT 2022-08-01 13:02:05 +02:00
Bruno Herbelin
ea7786a002 Set SRT Latency to 200ms
TODO: make it configurable
2022-08-01 12:39:55 +02:00
Bruno Herbelin
3eec07fac1 BugFix Discoverer for Stream 2022-08-01 11:22:50 +02:00
Bruno Herbelin
46afa76af8 update SRT documentation 2022-07-30 22:09:09 +02:00
Bruno Herbelin
6cc1ba64d8 adding screenshots documentation SRT 2022-07-29 23:26:20 +02:00
Bruno Herbelin
225596481f no architecture snap 2022-07-29 14:44:28 +02:00
Bruno Herbelin
4ef3b3b332 snapping fix 2022-07-29 14:33:52 +02:00
Bruno Herbelin
e7ac768b5b Merge remote-tracking branch 'origin/master' 2022-07-29 00:45:13 +02:00
Bruno Herbelin
b1e8833daa Complement snapcraft info and archittectures 2022-07-29 00:45:07 +02:00
Bruno Herbelin
155d71dc80 Complement snapcraft info and archittectures 2022-07-28 23:27:41 +02:00
Bruno Herbelin
94ebf17134 Add link to Wiki Filters and ShaderToy in Help 2022-07-28 23:11:12 +02:00
Bruno Herbelin
d6fe2edf0d Update and new images for wiki
Preparing documentation for filters a,d group features
2022-07-28 00:00:59 +02:00
Bruno Herbelin
867cd7e583 BugFix test number string 2022-07-27 23:33:36 +02:00
Bruno Herbelin
1a2471a32d Implementation of OSC targets Position, Size, Angle and Seek
Creation of SourceCallback to seek in MediaSource
2022-07-27 17:55:51 +02:00
Bruno Herbelin
057dd9c01d New images for wiki on filters 2022-07-23 23:18:11 +02:00
Bruno Herbelin
5a2c0e15e9 Change Player menu and new Frame inspector
Frame menu is active when a single source is selected. The Frame menu include actions to capture frame and to enable Frame Inspector. Frame inspector zooms on the image at cursor coordinate. Previous Control menu actions are back to main menu.
2022-07-23 22:45:14 +02:00
Bruno Herbelin
ae5ae24f6f BugFix source editor if playable 2022-07-23 12:07:44 +02:00
Bruno Herbelin
7a2f3fe840 BugFix Quit action in menu 2022-07-23 12:07:07 +02:00
Bruno Herbelin
b46788c81a snap 0.7.1 2022-07-21 23:05:20 +02:00
Bruno Herbelin
3c1f37e5f9 BugFix GLSL shader array init 2022-07-21 22:42:32 +02:00
Bruno Herbelin
d8d4322b2e Added option Recorder file naming style
VideoRecorder and PNGRecorder now have setting to decide how to name the files, with date prefix or sequentially numbered.  A base name is given with session filename.
2022-07-20 23:47:22 +02:00
Bruno Herbelin
1613e9ce46 BugFix: session creator restore play status of all types of sources
moved 'play' attribute to source instead of mediaplayer and use source callback to set play state after initialization.
2022-07-19 23:52:51 +02:00
Bruno Herbelin
aee3c8db1b Added ShaderToy support for iMouse to ImageFilter 2022-07-19 22:43:49 +02:00
Bruno Herbelin
becaeedff1 New fun shader vimix logo 2022-07-19 00:09:17 +02:00
Bruno Herbelin
c9c6651368 BugFix load display faster 2022-07-08 22:14:39 +02:00
Bruno Herbelin
d77371912b Changed Group ALL sources action to Group ACTIVE sources
Manage mixing groups and clones on the way. This makes the action more flexible for the user, allowing to group only a selection.
2022-07-06 23:34:36 +02:00
Bruno Herbelin
93cb12be89 BugFix UI Sequence recorder 2022-07-05 23:03:45 +02:00
Bruno Herbelin
4ac2bd8e92 BugFix fisplay Shader Code 2022-07-05 22:18:12 +02:00
Bruno Herbelin
34d52c975e Compilation fix
not needed call of non standard gst call
2022-07-05 22:17:59 +02:00
Bruno Herbelin
85194c7f4f Integration of MultiFileRecorder in UI for sequence creation 2022-07-04 00:07:23 +02:00
Bruno Herbelin
af009e03a0 BugFix New source pannel
Clear status of new source pannel when changing type of input.
2022-07-02 11:34:43 +02:00
Bruno Herbelin
cef7379c07 Initial implementation of MultiFileRecorder
Generate a video from a sequence of images.
2022-07-02 11:34:04 +02:00
Bruno Herbelin
f1e75d8593 BugFix change texture input Stream Sources 2022-06-22 23:48:21 +02:00
Bruno Herbelin
c5a194e98c Frame capture settings and options 2022-06-22 23:01:01 +02:00
Bruno Herbelin
7858033628 Player Frame capture F10
New feature of Player: capture frame (F10 shortcut). Extending the Screenshot class for reading pixels and saving to PNG. Cleaup of screenshot (now associated to F9).
2022-06-22 01:40:47 +02:00
Bruno Herbelin
f2405e02f6 Player display of non-playable source
Show also non-playable sources to allow testing pre- post-display. Show post-processed image only if source made changes.
2022-06-21 01:37:30 +02:00
Bruno Herbelin
452221daa5 User input unified and fixed for clone source
Fixed slider in player, show filtered image when disabled (outside mixing circle), correct timing for clone source (different for filters).
2022-06-20 17:29:12 +02:00
Bruno Herbelin
91f551c2d8 BugFix ShaderEditor 2022-06-19 02:00:32 +02:00
Bruno Herbelin
2ca1763280 Cleanup headers and licenses shaders code 2022-06-18 19:35:52 +02:00
Bruno Herbelin
da4a8333f7 Bugfix and clean code image filters ui, save and load 2022-06-18 18:08:16 +02:00
Bruno Herbelin
c0b08f3219 F11 for screen capture 2022-06-18 17:26:05 +02:00
Bruno Herbelin
c273e9125c F12 for enable/disable output
replacing the 'END' key that is not available on many keyboards.
2022-06-18 11:30:40 +02:00
Bruno Herbelin
7244b95844 Documentation and icons for FrameBuffer filters
unified icons, new entry in help window.
2022-06-18 11:30:11 +02:00
Bruno Herbelin
a298b6587d Added header for GLSL code, GLP3+
Ref to original ShaderToy authors.
2022-06-11 23:35:01 +02:00
Bruno Herbelin
d87f6b74f3 Fixed Shader Editor menu and behavior
Only Clones with ImageFilter of custom type are linked to UI for ShaderEditor. New menu to try presets of shader code. Link to ShaderToy website.
2022-06-10 00:04:56 +02:00
Bruno Herbelin
1f0b145740 Original implementation of Smooth Image filters
Smoothing and noise reduction filters + noise generators.
2022-06-08 23:44:19 +02:00
Bruno Herbelin
f6d528d36d Finalizing implementation of chroma and luma key Transparency filters 2022-06-07 23:49:21 +02:00
Bruno Herbelin
ea6502a282 Removing chromakey and lumakey from standard color correction shader
These effects now should be performed with effect on clone (alpha image processing shaders).
2022-06-07 19:04:52 +02:00
Bruno
8a36a94e73 not all GL Shading compilers accept ## for comments... 2022-06-07 10:45:23 +02:00
Bruno Herbelin
1604eaa239 Original implementation of Alpha Image filters
Chromakey (to finish), lumakey and alpha fill.
2022-06-06 23:33:36 +02:00
Bruno Herbelin
fec2fb7ce6 Original implementation of Resampling Image filters
This involves also resizing the renderbuffer of the clone source. Upsampling is cubic (faster approximation) and Downsampling is bilinear.
2022-06-05 23:43:23 +02:00
Bruno Herbelin
d2e3b854aa Put sobel as default edge filter
Default should be archetypal and efficient filter
2022-06-05 10:19:56 +02:00
Bruno Herbelin
f8e8d33c61 Bugfix show editor of clone source 2022-06-05 10:18:36 +02:00
Bruno Herbelin
dd76135efd Polishing sharpen and edge Image filters 2022-06-02 23:57:43 +02:00
Bruno Herbelin
d7be7a69ab Original implementation of Edge Image filters 2022-06-01 23:49:12 +02:00
Bruno Herbelin
fd942b28c6 Finishing Sharpen Image filters 2022-05-31 23:14:53 +02:00
Bruno Herbelin
7c850b0405 Original implementation of Sharpen Image filters 2022-05-31 22:53:28 +02:00
Bruno Herbelin
e3bb95b3dd Original implementation of Blur Image Filters
With Gaussian, fast Gaussian, Hashed and morphological (opening and closing) methods. Remembering shader code for other fast methods.
2022-05-31 00:34:37 +02:00
Bruno Herbelin
662d8bcfda Minor UI and wording changes 2022-05-24 23:28:36 +02:00
Bruno Herbelin
3c0b2c64e1 Added opposite action of 'Group all sources'
Mixer action ungroupAll expands all SessionGroupSources.
2022-05-24 21:30:54 +02:00
Bruno Herbelin
ed7d42cf6d Improved Player view of pre- and post-filtered images 2022-05-23 19:31:36 +02:00
Bruno
8852914ceb BugFix FrameBuffer Filter init 2022-05-23 08:55:16 +02:00
Bruno Herbelin
944778175a Improved computation of framebuffer memory usage 2022-05-23 00:45:54 +02:00
Bruno Herbelin
81704c08c9 Show post-processed image in Player by default 2022-05-22 22:19:07 +02:00
Bruno Herbelin
810059e6da Bugfix attach source 2022-05-22 22:18:43 +02:00
Bruno Herbelin
8d95bd16fd BugFix: change Device of DeviceSource with different resolution 2022-05-22 22:04:45 +02:00
Bruno Herbelin
4600253d1e Cleanup alignment Source pannel 2022-05-22 18:02:59 +02:00
Bruno Herbelin
d695aa9f57 FrameBuffer creation flags replace booleans
Instead of many creation options (with alpha, with multisampling, etc) use a single flag with boolean operators. Creation of the new mipmap flag for FrameBuffer, rendering the current FBO into multiple sub-resolutions.
2022-05-22 15:14:10 +02:00
Bruno Herbelin
7867aac55f Cleanup use of new icons 2022-05-22 11:18:57 +02:00
Bruno Herbelin
e26563c3d6 Remove all reference and icons from iconmonstr
Icons were unused and license was incompatible with GPL
2022-05-21 22:39:30 +02:00
Bruno
07ad262857 Sofware limiter for OSX seems to need more margin 2022-05-19 09:08:53 +02:00
Bruno Herbelin
cb0abd51db RenderingManager: FPS software limiter even with VSYNC
V-sync on multiple windows is not always performing well. So limiting to 61 FPS works with both VSYNC at 60FPS or without VSYNC. This means the settings for VSYNC is useless (removed from Settings panel).
2022-05-18 23:46:27 +02:00
Bruno
cc69baf0dd Compilation fix 2022-05-18 12:50:07 +02:00
Bruno Herbelin
852a8d04c9 Fixup UI ImageFilter 2022-05-18 00:19:48 +02:00
Bruno Herbelin
ffdacb3850 Unified implementation of filters for CloneSources
All filters now derive from FrameBufferFilter, which is always used in a CloneSource. Default FrameBufferFilter is Passthrough filter. Others are Delay and Image filters. Implemented UI selection of filter type, XML session save and load. Linked ImageFilter to Code editor.
2022-05-18 00:10:14 +02:00
Bruno Herbelin
062e8357fa Fixing morphological shaders code 2022-05-15 23:34:09 +02:00
Bruno Herbelin
07dece9cd7 Adding Filters 2022-05-15 23:33:18 +02:00
Bruno Herbelin
d628a513d9 Place clone at same depth of origin
TODO: shift other sources to make room?
2022-05-11 21:27:10 +02:00
Bruno Herbelin
6012ad9b1e Unified edge ImageFilters to invert 2022-05-07 23:31:21 +02:00
Bruno Herbelin
c3e618de36 BugFix Show Player 2022-05-07 23:30:52 +02:00
Bruno Herbelin
6b9795fe96 Remove (obsolete) filters from ImageProcessingShader
This shader is now only for Color correction
2022-05-07 23:15:35 +02:00
Bruno Herbelin
f7da3a347d New morphological operators 2022-05-07 23:01:17 +02:00
Bruno Herbelin
cf020d06c6 Place clone next to origin upon cloning 2022-05-07 23:01:02 +02:00
Bruno Herbelin
137b5ca4f9 BugFix ImageFilter timing
new debuging shader
2022-05-07 18:24:38 +02:00
Bruno Herbelin
82be9326a8 Fixed ImageFilter timing 2022-05-07 15:39:06 +02:00
Bruno Herbelin
222282dced Populating CloneSource with many preset ImageFilters 2022-05-07 13:39:08 +02:00
Bruno Herbelin
c7a2086850 Change Source filter to Color Correction
Removed filter selection from UI
2022-05-03 23:31:08 +02:00
Bruno Herbelin
168ac5065d Performance improvement: disable render when No ImageFilter selected 2022-05-03 23:30:21 +02:00
Bruno Herbelin
158ea1984f BugFix: more stable use of FBOs for delay 2022-05-03 23:29:18 +02:00
Bruno Herbelin
f66d73e385 Improved and added settings for MediaPlayer split view slider 2022-05-03 20:04:02 +02:00
Bruno Herbelin
252ed1c6f2 Added slider to show source pre-post processed in Player 2022-05-03 00:39:10 +02:00
Bruno Herbelin
69e35167bc BugFix Support for GStreamer upgrade to 1.20 2022-05-02 19:50:30 +02:00
Bruno Herbelin
2140075133 bugfix equal operator 2022-05-01 22:25:20 +02:00
Bruno Herbelin
80469ead18 Initial commit of ImageFilter shader presets
Clone source can choose a filter
2022-05-01 22:24:59 +02:00
Bruno Herbelin
77dc563219 Minimally operational Shader editor
Can edit code in GLSL, syntax highlighted, and compile shader. Compatible with ShaderToy code.
2022-04-23 01:02:31 +02:00
Bruno Herbelin
9d7f0b22f7 BugFix ATI get memory GL_TEXTURE_FREE_MEMORY_ATI 2022-04-21 18:38:34 +02:00
Bruno Herbelin
56b17116e3 Preliminary implementation of Shader editor
Connect TextEditor with ImageFilter from current Clone Source.  GLSL Compilation seems to work....
2022-04-21 00:18:37 +02:00
Bruno Herbelin
c71791b649 Bugfix handle keyboard for TextEditor. Starting to create Shader Editor
New WorkspaceWindow for the shader editor.
2022-04-20 18:26:31 +02:00
Bruno Herbelin
c8f8fcf9d3 BugFix: prevent View Terminate if not previously Initiated
Ensures Action::Manager stores terminated actions only if previously initiated (caused iterative action storing on keyboard repeat).
2022-04-20 16:43:42 +02:00
Bruno Herbelin
d41a85f4a1 New ImageFilter applied to Clone Sources
Preliminary implementation, effective but without consequence on the rendering.
2022-04-19 01:23:50 +02:00
Bruno Herbelin
3c465f9a7a Improved Shader compilation 2022-04-19 01:23:04 +02:00
Bruno Herbelin
c25427cf4a Add possibility to define ShadingProgram with GLSL code 2022-04-18 14:02:51 +02:00
Bruno Herbelin
1c8575e40c Add test of resource availability 2022-04-18 14:01:50 +02:00
Bruno Herbelin
e512eab1e8 Move code of getGPUMemoryInformation to Rendering manager 2022-04-18 14:01:21 +02:00
Bruno Herbelin
4dd8ceb245 Clone Source connection with directional dot line
use arrows to draw dot line between clone and its origin
2022-04-17 21:46:48 +02:00
Bruno Herbelin
07e2bd4bcf Improvement: reuse FBO for Thumbnailing
Avoid create and delete a new FBO for each thumbnail, as they are often the same size.
2022-04-17 12:51:55 +02:00
Bruno Herbelin
05eb62bb35 Allow CTRL+move of Locked sources 2022-04-17 12:35:34 +02:00
Bruno Herbelin
0df3342757 Improve Source naming increment 2022-04-17 11:22:32 +02:00
Bruno Herbelin
0615f38a26 BugFix Thumbnail in 21:9 aspect ratio 2022-04-17 00:19:34 +02:00
Bruno Herbelin
1fe63b68ee Message improvement Warning Change XML version 2022-04-16 23:52:13 +02:00
Bruno Herbelin
1e7dbb5331 BugFix Source init with clones when inactive at creation 2022-04-16 23:25:20 +02:00
Bruno Herbelin
0ddc03b7c0 Fixed Clone Source Activation 2022-04-16 21:18:45 +02:00
Bruno Herbelin
becc3d0953 Clone Source loading fixed and simplified
Fixed loading order. Removed the option of origin selection for Clone: not meaningful anymore with chain of clones.
2022-04-16 19:52:52 +02:00
Bruno Herbelin
48b1bfaebd Graphics Card Memory check before allocating FBO
Improved warning when allocating FrameBuffer. Avoid allocating FrameBuffer when buffering delay in Clone Source if we risk to consume all RAM in graphics card.
2022-04-16 12:57:31 +02:00
Bruno Herbelin
c043026764 Clone Source; dynamic memory for delay, connection line to origin 2022-04-16 01:33:41 +02:00
Bruno Herbelin
0aed9fc306 Added dotted line rendering 2022-04-16 01:31:55 +02:00
Bruno Herbelin
739559783b GPU monitoring of RAM available when allocating FBO
Warning when allocation of FBO is critical.
2022-04-16 01:31:09 +02:00
Bruno Herbelin
0f4076acab Documenting SRT Broadcast 2022-04-11 22:41:22 +02:00
BHBN
9692ac3f4d Merge pull request #43 from prez/musl
Fix compilation on musl libc
2022-04-11 22:24:01 +02:00
prez
365a333b1d Fix compilation on musl libc 2022-04-11 17:09:36 +02:00
Bruno Herbelin
f039755bde Set Max Clone delay to 2.0s 2022-04-10 23:32:44 +02:00
Bruno Herbelin
d314f1bae2 Player empty message more meaningful 2022-04-10 00:50:56 +02:00
Bruno Herbelin
aae1915519 SessionFile source restore version snapshot from UI 2022-04-10 00:50:31 +02:00
Bruno Herbelin
220df8918c Lock behavior change: do not show lock icon on unlocked inactive sources
Show unlocked icon only when active, show locked icon always
2022-04-09 19:29:37 +02:00
Bruno Herbelin
3e41655902 Unified Logs for sources initialization 2022-04-09 15:20:48 +02:00
Bruno Herbelin
54a23f5ae7 BugFix CountVisitor 2022-04-09 15:07:55 +02:00
Bruno Herbelin
edffcf8902 Added Total count of source in Session mix info 2022-04-09 14:46:09 +02:00
Bruno Herbelin
dd55f41264 BugFix Session Group init and playable status.
Improved logs
2022-04-09 14:45:12 +02:00
Bruno Herbelin
8fef0052a3 New CountVisitor to count the number of sources in session
Session size is the number of elements, use CountVisitor to count the total number of sources inside (recursively through SessionSources).
2022-04-09 00:35:20 +02:00
Bruno Herbelin
f2db10d29a Added Double Frame on Session Source 2022-04-09 00:33:43 +02:00
Bruno Herbelin
33756c775c Fixed Session load recursion, allow higher level of imbrication
Recursive load of SessionFile in a Session is now detected by filename and prevented. Deeper level of integration of sessionfile inside session is thus possible (set to 3). Sessions now have an id, allowing to reference them by id in the logs. Terminology is clarified between level and recursion.
2022-04-08 17:44:39 +02:00
Bruno Herbelin
ef65dd8cc6 Source list UI with Initials of source and name 2022-04-03 23:29:12 +02:00
Bruno Herbelin
74d0d851ca Display list of sources in Session Group UI panel 2022-04-03 23:18:14 +02:00
Bruno Herbelin
49ebc17334 Source info() gives type, InfoVisitor gives instance info
Changed (back) to clean use of source->info() to return type dependent info string. The InfoVisitor gives unified detailed information about instance.
2022-04-03 00:02:20 +02:00
Bruno Herbelin
548aba5b7c BugFix wrong initialization time counter 2022-04-02 23:59:00 +02:00
Bruno Herbelin
dc1f1e02a1 Unifying icons for session and group sources UI 2022-04-02 17:45:57 +02:00
Bruno
d10b809687 Unified Information string on Group Source 2022-03-28 10:57:32 +02:00
Bruno
ad438ef339 BugFix Session Group creation
Duplicate Action history store can cause crash
2022-03-28 10:57:09 +02:00
Bruno Herbelin
8a7a6ed4f5 Improve group session (play, info) 2022-03-27 23:38:35 +02:00
Bruno Herbelin
c6097e0397 Bugfix and cleanup Group source insert
NB: default setAlpha now re-uses the source previous mixing coordinates (does not force default location)
2022-03-27 15:30:56 +02:00
Bruno Herbelin
0f3e856438 BugFix WorkspaceWindow clear on ESC and Transition view 2022-03-27 00:09:18 +01:00
Bruno Herbelin
5b6ec81cee Depth sort View all scene (background, workspace and foreground) 2022-03-26 15:08:47 +01:00
Bruno Herbelin
e888bfbc8d BugFix SessionFile source import 2022-03-26 15:02:28 +01:00
Bruno Herbelin
b04ab65258 Merge remote-tracking branch 'origin/master' 2022-03-24 23:55:04 +01:00
Bruno Herbelin
46b707f246 Unified & fixed implementation of Group of sources (formerly flatten)
Fixed MixingGroup keep&restore when making Session Group Sources. New global feature to Group all sources into one session source. Unused but potentially useful implementation of flatten of mixer session into one new session source.
2022-03-24 23:52:00 +01:00
Bruno Herbelin
f2a6073829 Unified & fixed implementation of Group of sources (formerly flatten)
Fixed MixingGroup keep&restore when making Session Group Sources. New global feature to Group all sources into one session source. Unused but potentially useful implementation of flatten of mixer session into one new session source.
2022-03-24 00:23:27 +01:00
Bruno Herbelin
e5926a5371 BugFix Load mixing groups for Session source and groups 2022-03-23 22:03:02 +01:00
Bruno Herbelin
86c4581a50 BugFix gstreamer HW decoding
Enforce low priority when disabling on launch.
2022-03-23 22:01:07 +01:00
Bruno Herbelin
a80074dc21 Mixer functions cleanup, new flatten 'Embed in new' session 2022-03-22 00:20:41 +01:00
Bruno Herbelin
3effdd1408 Spelling and fix README and doc 2022-03-22 00:19:53 +01:00
Bruno
b927c55216 Temporary fix for preventing horizontal scrolling in main pannel 2022-03-21 16:24:40 +01:00
Bruno
49c590a9b5 OSX compilation update 0.7.0 2022-03-21 16:19:52 +01:00
Bruno Herbelin
8df6a2dd83 Display more informative error message on XML load fail. 2022-03-20 12:06:32 +01:00
Bruno Herbelin
fe66c95a29 Load Settings even for different version.
Cleanup Settings implementation.
2022-03-20 12:06:09 +01:00
Bruno Herbelin
c080959f64 Avoid very-long-line-length-in-source-file warning for glad generated source code 2022-03-20 00:30:56 +01:00
Bruno Herbelin
f27a88787d Add linux manpage and update main accordingly 2022-03-19 21:24:42 +01:00
Bruno Herbelin
5853495125 IMGUI API change for Text display 2022-03-19 16:46:59 +01:00
Bruno Herbelin
f49d94948d Bugfix Duplicate input mapping 2022-03-19 14:58:27 +01:00
Bruno Herbelin
82dad0fad3 UI improvement, rescaling windows content with user font scale 2022-03-19 14:58:12 +01:00
Bruno Herbelin
fea99498af Show initials of source in combo selectors. 2022-03-19 01:27:14 +01:00
Bruno Herbelin
5582ee8ed8 Compilation warning fix 2022-03-18 21:46:22 +01:00
Bruno Herbelin
f4b6db9404 Remove uncessary font, find fonts in system if not available. 2022-03-18 21:46:08 +01:00
Bruno Herbelin
5c92362aae Fixing typos and spelling mistakes 2022-03-16 23:28:39 +01:00
Bruno Herbelin
667ea9fa64 Fix glfw dependency different version 2022-03-15 22:15:18 +01:00
Bruno Herbelin
b710750035 Merge remote-tracking branch 'origin/master' 2022-03-15 18:49:12 +01:00
Bruno Herbelin
491ff01c79 Optional library dependency CMake 2022-03-14 23:34:02 +01:00
Bruno Herbelin
4981145dd8 Fix version from git 2022-03-14 23:34:02 +01:00
Bruno Herbelin
e9497b03f4 Fix version from git 2022-03-14 23:31:50 +01:00
Bruno Herbelin
d76e518db1 Optional library dependency CMake 2022-03-14 22:52:50 +01:00
Bruno Herbelin
a2906c6aa5 Preparing 0.7 2022-03-14 01:10:45 +01:00
Bruno Herbelin
e0676c66a0 Metronome fix
do not call now() multiple times to avoid time difference between calls.
2022-03-13 23:13:32 +01:00
Bruno Herbelin
0b12c5a169 Bugfix and Help Input mapping.
Duplicate input mapping, drag'n drop, and various UI improvements.
2022-03-12 18:05:53 +01:00
Bruno Herbelin
39b61fe331 Reimplementation of SourceInputCallbacks into Session
Session should be the object holding the list of inputs parameters (e.g. synchrony) and the list of source callbacks. This also avoids mixing input when copying sources.  Code could be improved but is operational.
2022-03-07 00:23:24 +01:00
Bruno Herbelin
83e77681d9 Various BugFix and UI improvements
Prevent key repeat for commands, allow maintain ESC key, replaced timeline BPM display (innacurate and confusing) with a simplified icon indicator for Metronome synchronization of Media player, improved Media Player loop mode tooltip, improved Pattern source selector.
2022-02-26 22:35:27 +01:00
Bruno Herbelin
95a69937bd BugFix Mediaplayer loop and info mediaplayer 2022-02-26 22:32:37 +01:00
Bruno Herbelin
c355bd7569 Bugfix: update depth of sources from dropped media file
To be investigated further if problem persists: patch seems to work (increment twice View::need_deep_update_) but not fully sure why two frames are necessary...
2022-02-22 23:32:43 +01:00
Bruno Herbelin
559a186cd9 BugFix repeated toggle clear workspace 2022-02-22 22:43:58 +01:00
Bruno Herbelin
7e81ef37d7 Upper-case keyboard key info 2022-02-21 12:33:22 +01:00
Bruno Herbelin
2e167d260d New Geometry source callback
Set Geometry callback applies and interpolates position, scale and rotation of a source. Implemented UI and XML.
2022-02-21 12:32:48 +01:00
Bruno Herbelin
aa50d818ec Added directionality and speed selection in Input Callback
Unified implementation of constructor for SourceCallbacks. New icons and IconMultitouch for configuration GUI of input callback.
2022-02-20 21:03:39 +01:00
Bruno Herbelin
b9dd0a3877 Added icons in Output window menu bar
Unified appearance with other windows
2022-02-20 21:01:28 +01:00
Bruno Herbelin
e03ef7e214 BugFix Clone is playable if its origin is playable too 2022-02-19 12:42:42 +01:00
Bruno Herbelin
caa05d739f BugFix double clic output window 2022-02-19 03:24:25 +01:00
Bruno Herbelin
f580673dea Minor changes in SRT and generator sources to help user
Clearly state 'listener' and 'call' roles for SRT. Add a '>' indicator to indicate if a generator is playable.
2022-02-18 19:09:37 +01:00
Bruno Herbelin
297d271e63 Change direction Loom
more logical to set positive delta to increase visibility
2022-02-15 23:44:54 +01:00
Bruno Herbelin
a28e4be5a3 Bugfix play selection 2022-02-15 23:44:14 +01:00
Bruno Herbelin
6b672acdc7 Improved UI for Input Mapping 2022-02-14 23:40:26 +01:00
Bruno Herbelin
26f5368264 Implementation Control manager with singleton mechanism (not static) 2022-02-13 22:29:55 +01:00
Bruno Herbelin
fc5b967973 Imput Mapping support for Multitouch with TouchOSC
16 touch buttons in Multitouch tab of TouchOSC companion app for user customized callbacks.
2022-02-13 12:49:43 +01:00
Bruno
3cf497fa91 Change Multitouch TouchOSC
Akai MPC inspiration for 16 buttons with touch variability
2022-02-11 12:48:43 +01:00
Bruno Herbelin
f50411e9db Bugfix Callbacks
Added duration to SetAlpha callback. Saving and loading Play callback.
2022-02-11 00:28:25 +01:00
Bruno Herbelin
74eca2e527 Added UI actions to change key of source callbacks and copy-paste
Drag&drop input button keys to change key associated to a list of source callbacks (i.e. move). Copy Paste in context popup menu to remember the input model to copy at another input.
2022-02-10 00:24:38 +01:00
Bruno Herbelin
6200e78e93 Bugfix Guru plot 2022-02-07 23:51:43 +01:00
Bruno Herbelin
904c122ee0 Minor UI improvement Input Mapping 2022-02-07 23:34:45 +01:00
Bruno Herbelin
741afaea18 Improved Source Callback for multi-callbacks compatibility 2022-02-07 17:45:34 +01:00
Bruno Herbelin
6cf86d80e2 Input Mapping suport for Gamepad Axis (multiply SourceCallback)
Apply the ControlValue as a multiplyer to the Callback. UI with indication bars for gamepad axis.
2022-02-07 13:27:05 +01:00
Bruno Herbelin
886305ec13 Input Mapping support for Gamepad buttons
Cleanup UI defines.
2022-02-06 23:37:11 +01:00
Bruno Herbelin
ab040f5268 First working implementation of Inputs Mapping
Management of inputs in Control, Management of callbacks creator per input in Source, Saving and Loading in Session, Unified renaming of SourceCallbacks, User interface window for creating and editing input mapping from Keyboard and Numerical keypad, with appropriate Settings.
2022-02-06 00:36:05 +01:00
Bruno Herbelin
8404e0f670 Milestone in SourceCallbacks and Keyboard callback trigger
Updated SourceCallback class to accept cloning, reversing, and visitors for saving./loading. New mechanism in Source to listen to key triggers for launching SourceCallbacks. Saving and loading in SessionVisitor and SessionCreator.
2022-01-30 00:25:08 +01:00
Bruno Herbelin
3605ae14b5 Validate Port value in UI before changing 2022-01-28 18:44:32 +01:00
Bruno Herbelin
bce372bd79 Display Info of created source in Preview 2022-01-26 18:58:43 +01:00
Bruno
715c48b7eb Slight Font offset ajustment for FontAwesome glyphs 2022-01-25 13:11:23 +01:00
Bruno Herbelin
eb9a3c2ad1 Make Metrics semi-transparent when Workspace cleared 2022-01-24 23:53:53 +01:00
Bruno Herbelin
b261829aea BugFix interference output window rescale on Workspace UI windows 2022-01-24 23:53:27 +01:00
Bruno Herbelin
595be6b7b8 Change Shortcut for sources Restart to CTRL_SPACE
Updated Help UI accordingly, plus including SrtReceiverSource
2022-01-24 23:34:23 +01:00
Bruno Herbelin
9ed76ae4da Finalizing implementation of SrtReceiverSource
Added icons, unified UI, loading and saving in XML.
2022-01-24 23:06:47 +01:00
Bruno Herbelin
2ae0ef40d4 SrtReceiverSource for broadcasted stream
Implemented dedicated source, with UI for creation and saving appropriate settings.
Also updated info and imgui visitors accordingly
2022-01-24 20:18:33 +01:00
Bruno Herbelin
f5f7d3c154 Fixed nvidia hw encoding pipelines 2022-01-23 19:43:07 +01:00
Bruno Herbelin
a9ab4dbe38 More robust implementation of Video Broadcast
Testing GST features and using HW accelerated  encoding if available
2022-01-23 12:17:08 +01:00
Bruno Herbelin
5c3c26851c Implemented Broadcast
Initial implementation of SRT streaming as listener. Changed stream terminology to distinguish network broadcasting and network sharing in local network. Updated user settings accordingly.
2022-01-23 01:10:10 +01:00
Bruno Herbelin
2b3696aab1 Slight change in terminology to distinguish streaming from broadcasting
VideoStream is reserved for point-to-point video streaming (between vimix), while VideoBroadcast is for sending out to many potential clients.
2022-01-22 00:23:59 +01:00
Bruno Herbelin
35ec0c9bcf BugFix: prevent repeated initialization 2022-01-22 00:20:40 +01:00
Bruno Herbelin
4f915d6708 UI info procrastination 2022-01-20 23:27:43 +01:00
Bruno Herbelin
f4eb8b246b Minor improvements in InfoVisitor for source info 2022-01-20 22:46:47 +01:00
Bruno Herbelin
1a80e52241 Initiating implementation of VideoBroadcast 2022-01-20 22:46:24 +01:00
Bruno Herbelin
afa27a04fe BugFix: fail DeviceSource if not plugged 2022-01-20 18:01:07 +01:00
Bruno Herbelin
b82c83de5e BugFix DeviceSource: shared access to stream vis Device::manager
Creation of multiple DeviceSources is possible (also for multiple sessions and transitions) through centralized management of gst streams. Creation and deletion of a shared stream accross DeviceSources is handled.
2022-01-20 01:28:30 +01:00
Bruno Herbelin
625e2305ba Fix Quit call for Linux 2022-01-17 23:55:44 +01:00
Bruno
6b4781b7d5 BugFix Device manager initialization
Ensure initialization is complete before other calls to Device::manager can operate (e.g. setDevice in session Creator)
2022-01-17 23:47:07 +01:00
Bruno
7acffabdd8 restore commented line 2022-01-17 20:23:50 +01:00
Bruno
e6f2aa2399 Merge remote-tracking branch 'origin/master' 2022-01-17 20:21:04 +01:00
Bruno
ece7e04c7c BugFix: correctly wait for Mixer to save file on exit
On the way, also improved Connection Manager ending properly.
2022-01-17 20:20:44 +01:00
Bruno
0b4d273e08 BugFix: correctly wait for Mixer to save file on exit
On the way, also improved Connection Manager ending properly.
2022-01-17 19:45:58 +01:00
Bruno Herbelin
81e8d6d99c Refactoring Session saving
use Session::save static method to save a session from a thread (same mechanism as Session::load). It calls Action::takeSnapshot if saving version is required. Mixer is busy during saving, pops up info when done.
2022-01-17 00:38:48 +01:00
Bruno Herbelin
e96444671e BugFix: give time to Save on exit and stop recordings on Quit 2022-01-16 19:16:51 +01:00
Bruno Herbelin
e52785a8b4 Cleanup UI for source info, cleanup code 2022-01-16 17:47:43 +01:00
Bruno Herbelin
ddccc5ff6b Strengthen implementation of Clone and Render sources
Change of vocabulary, UI display of playtime
2022-01-16 15:27:22 +01:00
Bruno Herbelin
8bbcef585f Isolate implementation of RenderSource and new Non-Recursive mode
Separate files for RenderSource, make RenderSource playable, and implement two rendering mechanisms
2022-01-16 12:20:28 +01:00
Bruno Herbelin
e58041227b Added delay and image selection to CloneSource 2022-01-15 00:15:52 +01:00
Bruno Herbelin
3678e8fb27 Isolate implementation of CloneSource 2022-01-13 22:00:25 +01:00
Bruno Herbelin
1146a9125b Cleanup extension filename and bugfix session preview 2022-01-13 21:41:50 +01:00
Bruno Herbelin
aab0c055ed UI improvements
Make room for more icons in left bar if necessary. Also react to resizing of workspace for windows not visible.
2022-01-09 13:18:13 +01:00
Bruno Herbelin
685082e212 Improved UI main panel & control
bugfix; scrolling about and settings icons, clic outside buttonswitch. Updated default windows position.
2022-01-09 00:01:06 +01:00
Bruno Herbelin
1bb8b636b9 Bugfix pop font 2022-01-08 15:04:31 +01:00
Bruno Herbelin
43c51c3b82 Navigator lower-left corner icons for Fullscreen and Workspace mode
Added icons to toggle fullscreen & show/hide windows. Using View names instead of hardcoded string. Cleanup some UI variables.
2022-01-08 15:04:21 +01:00
Bruno Herbelin
b9ed64fba2 Make sure the name of the view is set correclty
even if there was a setting found
2022-01-08 15:01:12 +01:00
Bruno Herbelin
ceaac03adf Log Window - Display last log line in title when collapsed 2022-01-08 10:57:12 +01:00
Bruno Herbelin
3085a837c8 Nicer rendering of workspace windows with transparency 2022-01-08 01:05:10 +01:00
Bruno Herbelin
2e5e2c8430 On Save-as, open FileDialog in same directory of previous filename 2022-01-08 00:49:20 +01:00
Bruno Herbelin
48df5c0eb1 Cleanup UI - Filters menu, Save-as property
Cleanup code in ImGuiToolkit::IconButton
2022-01-08 00:35:02 +01:00
Bruno Herbelin
fcad6766c3 Update UI update and TransitionView to match WorkspaceWindows clear
Enter Workspace Clear mode when switching to Transition view (instead of specific hack). Do not render all windows when in Clear workspace mode.
2022-01-07 20:08:32 +01:00
Bruno Herbelin
c91a4670de Added Link status next to source preview in left pannel
Give quick access to select the linked group.
2022-01-07 18:31:25 +01:00
Bruno Herbelin
5e7c325874 BugFix: double-clic selection of mixing group crash
Improved CTRL+Clic for mixing group selection instead.
2022-01-07 17:57:16 +01:00
Bruno Herbelin
6b8a1428d7 Disabled Debug log Framebuffer 2022-01-07 17:56:26 +01:00
Bruno Herbelin
5bd81db37e Bugfix DeviceSource
API changed v4l2.
2022-01-07 16:10:58 +01:00
Bruno Herbelin
9834baedfa Passing main argument to open session 2022-01-07 11:10:28 +01:00
Bruno Herbelin
0b4c42859d Last minute entries into Help toolbox 2022-01-07 01:56:35 +01:00
Bruno Herbelin
a92b45ae26 Select Linked MixingGroup on double clic & a bug fix 2022-01-06 22:04:05 +01:00
Bruno Herbelin
1e2096e691 Bugfix restore Session Recent combo on start 2022-01-06 21:46:41 +01:00
Bruno Herbelin
3df6ffe280 Major BugFix: avoid default gst g_main_context
Do NOT update g_main_context at RenderingManager update. Do not use g_main_context for Dialogs. Do not rely on default g_main_context for Device discoverer (implemented a thread save g_main_loop dedicated).
2022-01-06 20:20:30 +01:00
Bruno Herbelin
d4f370c071 UI WorkspaceWindows follow window resize
Fixed bug in restore window position.
2022-01-06 14:58:35 +01:00
Bruno Herbelin
b894ea866a Make Timer a workspace window 2022-01-06 09:13:50 +01:00
Bruno Herbelin
3a300a4ca3 New UI windows behavior to clear workspace on ESC
Press ESC to toggle a 'clear workspace' mode. 
Cleanup of UserInterface rendering of output preview to enable this.
Move include to internal_imgui.h outside of ImGuiToolkit.h.
2022-01-06 01:21:18 +01:00
Bruno Herbelin
0ad844d10e Do not show source panel after source creation 2022-01-05 15:48:36 +01:00
Bruno
d310a45f72 Updated CMake comment to new tinyfiledialog git 2022-01-05 15:38:24 +01:00
Bruno
57c31804b4 Cleanup UI dialog save on exit
Also set save-on-exit by default
2022-01-05 15:37:47 +01:00
Bruno Herbelin
5fb70a9b9a Save on exit; test before closing
Detect window close or quit events to make sure a filename is given if save-on-exit is active.
2022-01-05 15:08:52 +01:00
Bruno Herbelin
d402143989 Cleanup icons and minor bugfix UI 2022-01-05 01:02:44 +01:00
Bruno Herbelin
ad8d799cf6 BugFix Session group 2022-01-04 22:13:41 +01:00
Bruno
cec705264d Replaced Tinyfiledialog repo
Updated code is at https://git.code.sf.net/p/tinyfiledialogs/code
2022-01-04 18:16:12 +01:00
Bruno
28a2e61361 Removed submodule ext/tfd 2022-01-04 18:04:32 +01:00
Bruno
6017215ada Get Tinyfiledialog from its original author 2022-01-04 17:48:14 +01:00
Bruno
8f3128e4b3 Merge remote-tracking branch 'origin/master' 2022-01-04 14:38:04 +01:00
Bruno Herbelin
109e6f590a Disabling vtenc streaming for OSX (could not make it work) 2022-01-04 14:37:56 +01:00
Bruno
ac86d75e10 Merge remote-tracking branch 'origin/master' 2022-01-04 12:33:59 +01:00
Bruno Herbelin
8f0491ea57 Video Streamer with H264 hardware acceleration
Simplified option in user menu; lower bandwidth with H264, otherwise use JPEG. Always use RAW for localhost.
2022-01-04 12:33:46 +01:00
Bruno
6403e51ba7 Merge remote-tracking branch 'origin/master' 2022-01-04 10:02:05 +01:00
Bruno Herbelin
e0522608a4 UI improvement; hovering icons indicate possible action
Source filters icons without button. Unified lock icon with view. Updated help.
2022-01-04 00:54:12 +01:00
Bruno Herbelin
4b9a230803 oops 2022-01-03 22:16:55 +01:00
Bruno Herbelin
fc5246efaa New & improved align and distribute in MixingView 2022-01-03 18:20:02 +01:00
Bruno Herbelin
4eebfbb89f Improved Glyph layout
Support for shape and placement of glyph
2022-01-03 17:16:55 +01:00
Bruno Herbelin
353d2c4744 New Glyph decoration to show initials of source 2022-01-03 15:41:08 +01:00
Bruno Herbelin
2718e83132 Happy new year 2022 2022-01-02 23:17:22 +01:00
Bruno Herbelin
7547d1179d Cleanup UI
Ensure icons are dedicated to one single thing.
2022-01-02 19:54:48 +01:00
Bruno Herbelin
61e89286bc Fixed Device manager: restore gmainloop
The gmainloop is used by gst to detect devices. Fixed bugs on detection of invalid devices.
2022-01-02 14:17:10 +01:00
Bruno Herbelin
53ae715816 Restart the source after insertion from New Source panel 2022-01-02 11:49:02 +01:00
Bruno Herbelin
8cb37dba36 ORDER change: add sources at the end when create
Inserting sources at front was changing their index every time, which broke OSC addressing
2022-01-02 11:41:47 +01:00
Bruno Herbelin
4426f70de7 Code editor for Custom pattern gstreamer
Bugfix in Streamsource and UI
2022-01-01 23:59:30 +01:00
Bruno Herbelin
f0ca13150f New Custom pattern entry for New Source 2022-01-01 10:14:51 +01:00
Bruno Herbelin
780a20689c Improved user message for stream discovery failure 2022-01-01 10:13:46 +01:00
Bruno Herbelin
28f9ed1d8d Cleanup & new unwrapped function 2022-01-01 10:12:50 +01:00
Bruno Herbelin
2b5b8ad02c Bugfix Stream timeout initialization test 2021-12-31 14:24:51 +01:00
Bruno Herbelin
d5092b1765 Save & load GenericStrreamSource 2021-12-31 13:30:51 +01:00
Bruno Herbelin
fda62314f9 UI integration of GenericStreamSource 2021-12-31 13:16:39 +01:00
Bruno Herbelin
17018c137f MultiLine text display 2021-12-31 13:16:16 +01:00
Bruno Herbelin
8838c19c39 String functions to wrap test or join lists 2021-12-31 13:16:00 +01:00
Bruno Herbelin
f02a99a4e2 Improved GenericStreamSource, with stream discoverer
Also timeout to fail if open does not works + new GST icon.
2021-12-31 13:15:23 +01:00
Bruno Herbelin
7b26b0f23e Unified IMGUI_SAME_LINE width 2021-12-31 13:13:29 +01:00
Bruno Herbelin
0e9984827a Cleanup gst_element_get_state 2021-12-31 13:12:12 +01:00
Bruno Herbelin
033d41863a Added Stream Discoverer to detect frame size from a gstreamer pipeline
Previous use of Stream are not affected (the discoverer is passively returning the given width and height). But if the Stream is created without dimensions, it will run a discoverer to try to get preroll frames and detect width and height from there.
2021-12-30 00:15:43 +01:00
Bruno Herbelin
bc540044ac Accept launch of vimix if OSC connection failed 2021-12-29 14:41:27 +01:00
Bruno Herbelin
76a2535da3 Fixed issue of low quality stream in JPEG: new default to RGB RAW RTP stream
Backward compatibility through menu un stream output view (allow selecting JPEG)
2021-12-29 14:37:56 +01:00
Bruno Herbelin
ff48877d16 Fixed OSC feedback after source change. Added OSC command to lock source. 2021-12-27 23:36:28 +01:00
Bruno Herbelin
4b8efabc5f Improve and cleanup OSC control and translation
Changed default send Port to 7001. Updated documentation.
2021-12-27 17:28:11 +01:00
Bruno Herbelin
c79be090df Implementation of OSC settings and translator
Translations are in a config xml file in settings directory, and can be directly edited in text by the user. Settings UI allows changing Ports for incoming and outgoing UDP.
2021-12-27 01:04:49 +01:00
Bruno Herbelin
626eab7e8f Update and advertising of TouchOSC layout 2021-12-27 01:02:43 +01:00
Bruno Herbelin
c103b7d883 Finalization of OSC API, Tutorial for TouchOSC 2021-12-26 14:44:07 +01:00
Bruno Herbelin
cde055e29b Implementation of Session control
With Session recall from OSC
2021-12-26 01:20:44 +01:00
Bruno Herbelin
1cb448c42e Output session fading fixed for OSC and animation.
Linear interpolation (instead of dichotomy converge) for fading at Session update. Mixing View update reads value of session fading to animate the cursor (which was preventing other manipulation of fading). Cleanup fading in OSC controller, with animation options and fade-in and fade-out controls.
2021-12-26 00:41:02 +01:00
Bruno Herbelin
3d05444f30 Improved OSC control with TouchOSC
Added Looming source callback, and cleanup sync of sources. New horizontal version of OSCTouch UI.
2021-12-25 16:05:43 +01:00
Bruno Herbelin
7a551189d9 Improved log of OSC message. 2021-12-25 00:41:51 +01:00
Bruno Herbelin
b885e70fed Remove spaces from Source name
Replace space by underscore
2021-12-25 00:41:24 +01:00
Bruno Herbelin
0a27c14041 Control manager and TouchOSC sync 2021-12-23 22:17:05 +01:00
Bruno Herbelin
eb8e33e311 Correct call to Source Activation (inheritance) 2021-12-23 22:16:16 +01:00
Bruno Herbelin
2d44a60b90 Bi-directional OSC communication for Control manager
Unified OSC message declaration with Communicator
2021-12-21 23:48:20 +01:00
Bruno Herbelin
135b6a5702 cleanup SourceCallbacks on source destructor 2021-12-21 00:19:55 +01:00
Bruno Herbelin
706c72fda8 More OSC control
Grab and resize dynamically, select source by index, etc.
2021-12-21 00:19:39 +01:00
Bruno Herbelin
fb7bdba388 Code cleanup 2021-12-20 00:30:59 +01:00
Bruno Herbelin
733d08638d Control manager thread save with SourceCallbacks 2021-12-20 00:30:50 +01:00
Bruno Herbelin
cb3cca8a64 catchup previous commits 2021-12-20 00:29:57 +01:00
Bruno Herbelin
a3a581794e Node update callbacks do not need to be disabled 2021-12-20 00:26:08 +01:00
Bruno Herbelin
f921e7610c New mechanism for source update with callbacks
Similarly to Node update callbacks, sources now have SourceCallbacks called at the start of each update. Several SourceCallback are implemented to ensure thread safe update of more complex properties (mixing alpha, depth, etc.).
2021-12-20 00:25:42 +01:00
Bruno Herbelin
8deb364025 Cleanup of main update calbacks
Clarify update and draw of rendering manager by using callbacks (instead of hidden calls in draw method).
2021-12-19 01:12:25 +01:00
Bruno Herbelin
3a9c6f56bf Work in progress OSC Control manager
Support for log, output and source targets. Now needs to be developed for all attributes.
2021-12-19 01:11:29 +01:00
Bruno Herbelin
a612154123 Initial implementation of Control manager
Control manager will handle control actions, recorded or from OSC. Here skeleton for receiving OSC messages is in place. Cleanup of includes for NetworkToolkit. Touched a bit the BaseToolkit.
2021-12-18 16:02:37 +01:00
Bruno Herbelin
bbc5e50491 bugfix show Player when clic source 2021-12-18 10:18:36 +01:00
Bruno
6e3dd8165e Merge remote-tracking branch 'origin/master' 2021-12-13 09:37:13 +01:00
Bruno Herbelin
a7689a8f54 Help window, setting to show/hide Tooltips
Menu and keyboard shortcut declaration centralized. List of all keyboard shortcuts. ImGuiToolkit unified tooltips.
2021-12-12 23:12:56 +01:00
Bruno Herbelin
731a1af1a6 Defines for ImGuiToolkit icons for source. 2021-12-12 23:10:07 +01:00
Bruno Herbelin
f53ebd4389 BugFix crash on close Player window 2021-12-11 21:35:23 +01:00
Bruno
a2d61cc30a Merge remote-tracking branch 'origin/master' 2021-12-09 12:36:21 +01:00
Bruno Herbelin
baa6ddb401 Implementation of user defined mixing deactivation limit
Mixing view handles to grab and scale limbo area. Saving of user defined limit in Session (and snapshot). Testing for source activation outside of update during session update loop.
2021-12-08 23:55:27 +01:00
Bruno
4455aa6709 Merge remote-tracking branch 'origin/master' 2021-12-06 14:04:06 +01:00
Bruno Herbelin
315a8534d5 Store output PNG capture in list of recent recordings 2021-12-06 12:35:09 +01:00
Bruno Herbelin
d77bd4034d Improved UI tooltips 2021-12-06 12:29:22 +01:00
Bruno Herbelin
fa71797ed2 Unified implementation saving and loading settings history files 2021-12-06 11:41:03 +01:00
Bruno Herbelin
8c63552573 Global settings for Save and continue auto-preload
Added configuration for recent recording list. Added tooltip for filename in list.
2021-12-06 11:16:47 +01:00
Bruno Herbelin
a18d53c637 Improved Save and continue recording
When triggered from menu, prepare the UI for next openning of the new source pannel
2021-12-06 00:16:53 +01:00
Bruno Herbelin
ffe05368e8 Update New Source panel for Media
Added list of recent files, recent recordings, and folders list of media files. All saved in settings. Connect list of recent recordings with recorder.
2021-12-05 18:41:58 +01:00
Bruno Herbelin
923d84f378 Unified SystemToolkit list directory with dialog file patterns 2021-12-05 18:39:58 +01:00
Bruno Herbelin
e5334aae0a Cleaner file patterns for dialogs 2021-12-05 18:33:53 +01:00
Bruno Herbelin
4675be7e2a Unified notification of source creation with Source info
New virtual function source::info used for notification after adding source
2021-12-05 18:32:23 +01:00
Bruno Herbelin
bf3fc61ef7 Bad idea: do not use nvidia hardware decoder for jpeg
just crashes without reason and not very useful
2021-12-04 23:05:12 +01:00
Bruno Herbelin
ebd9fab312 Improved ordering of hardware decoding and log info 2021-12-04 00:23:27 +01:00
Bruno Herbelin
d359cf33d1 Improved runtime check of hardware encoder
gstreamer toolkit has_feature now tests possible instanciation
2021-12-03 23:36:07 +01:00
Bruno Herbelin
14bab1e299 Runtime selection of hardware GPU encoder
temporary implementation
2021-12-03 01:05:32 +01:00
Bruno Herbelin
4c4ad144b9 Finish Frame grabber after return on failed initialization 2021-12-03 01:05:04 +01:00
Bruno Herbelin
1157c0b1c5 cleanup frame grabber gst timer 2021-12-03 01:03:21 +01:00
Bruno Herbelin
68b2c5e0c1 Frame grabber threaded initialization
Start gstreamer init of frame grabber in a thread and wait future return from initializer before switching to active recording mode.
2021-12-02 11:45:22 +01:00
Bruno Herbelin
b97fd06f2a Bugfix Resolution frame buffer
Set width to power of 2, needed for encoding (even if OpenGL accepts to display it)
2021-12-02 11:40:31 +01:00
Bruno Herbelin
51f0f5bd66 Fixed ending of recording on exit 2021-12-02 09:35:02 +01:00
Bruno Herbelin
66f445997d Preliminary implementation of recording 'save & continue' 2021-12-01 23:05:41 +01:00
Bruno
addd199407 Merge remote-tracking branch 'origin/master' 2021-11-29 11:16:22 +01:00
Bruno Herbelin
73d4f7c1ea Ensure swap interval 2021-11-28 23:58:01 +01:00
Bruno Herbelin
25fc5562db Unified layout of HelpMarkers 2021-11-28 23:57:33 +01:00
Bruno Herbelin
4d52bcb5b3 Fix glfw set window pos 2021-11-28 20:50:56 +01:00
Bruno Herbelin
3d2de560b0 Timelines of metro-synched media player 2021-11-28 11:36:56 +01:00
Bruno Herbelin
809e30d906 Timeline display in beat unit for synched to metronome 2021-11-27 19:26:33 +01:00
Bruno Herbelin
ef9e41f20d Fixed display of time in minimal string mode 2021-11-27 19:23:51 +01:00
Bruno Herbelin
1b4849f214 Media player synchronicity to beat or phase
Metronome synched play, rewind and step. saving in xml.
2021-11-26 12:22:39 +01:00
Bruno
26cc67cd41 Merge remote-tracking branch 'origin/master' 2021-11-25 09:56:24 +01:00
Bruno Herbelin
e123d139e4 Introducing modes of Metronome synchronicity 2021-11-24 21:48:51 +01:00
Bruno Herbelin
a8abd52afb Merge remote-tracking branch 'origin/add-code-of-conduct-1' 2021-11-24 20:32:49 +01:00
Bruno Herbelin
091e99f21b New export function for Version of a session 2021-11-24 20:32:29 +01:00
Bruno
7b7875e23f Merge remote-tracking branch 'origin/add-code-of-conduct-1' 2021-11-24 10:41:55 +01:00
Bruno Herbelin
b6593c2a83 Added date to snapshot
Allows showing date of version
2021-11-24 00:15:40 +01:00
Bruno Herbelin
5ac7887360 Convert Snapshots into Versions of session
Added auto-snapshot on save to have an Iterative Saving mode, and change terminology of 'snapshots' to 'versions' management.
2021-11-23 22:47:44 +01:00
Bruno Herbelin
ed7627af6f Fixed UI spacing proportional to screen DPI 2021-11-23 22:46:22 +01:00
BHBN
1ea9fc54b2 Create CODE_OF_CONDUCT.md 2021-11-22 00:22:18 +01:00
Bruno Herbelin
3819571ec0 Fixed UI display time in readable form 2021-11-21 22:39:11 +01:00
Bruno Herbelin
94f131fc57 Fixed panel window show/hide 2021-11-21 22:10:17 +01:00
Bruno Herbelin
3c20314aab Metronome and Stopwatch User Interface
New Timer window in UI for Metronome (Ableton Link management) and replaces Timers. Former Timers in Metrics are replaced with Runtime (of session, of program and of total vimix runtime in settings). Temporarily disconnected Metronome from MediaPlayer actions.
2021-11-21 16:54:56 +01:00
Bruno Herbelin
1506d36407 Readable GST time is at 1/10th second precision 2021-11-21 16:52:09 +01:00
Bruno Herbelin
aa4b2967c7 Adding a timestamp to Session instanciation
Used to compute runtime of a session
2021-11-21 16:51:19 +01:00
Bruno Herbelin
a42881d31f Texture view slider UI fix 2021-11-21 16:46:18 +01:00
Bruno Herbelin
6a3ff2f235 More freedom of grab translation for all views 2021-11-17 23:11:15 +01:00
Bruno Herbelin
fc4e3dc362 Metronome settings and UI improvements 2021-11-14 00:18:32 +01:00
Bruno Herbelin
d6c689c5bb Cleanup include ImGuiToolkit 2021-11-14 00:18:10 +01:00
Bruno Herbelin
8676e9b900 Integration of Ableton link in vimix application
No useful functionality yet. Only connecting, set parameters, show metrics and save settings.
2021-11-13 15:01:02 +01:00
Bruno Herbelin
c271cad9aa Cleanup headers and dependencies 2021-11-13 00:14:05 +01:00
Bruno Herbelin
8e3bf786c0 Initial implementation of Metronome from Ableton LINK
Added submodule for github ableton link, and compiled draft of Metronome class.
2021-11-13 00:13:50 +01:00
Bruno Herbelin
a6ba694fbd Code warning cleanup and add GPL license header to all CPP files 2021-11-10 23:19:38 +01:00
Bruno
790ccc320e OSX bundle install of Frei0r plugins 2021-11-10 21:16:16 +01:00
Bruno Herbelin
26e951e59b Update website screenshot 2021-11-10 11:02:05 +01:00
Bruno Herbelin
a97581f5d7 semi-transparent icon of lock to inform its not interactive
need to CTRL+clic to unlock, as opposed to other handles: this is not perfect but shows the difference
2021-11-10 00:13:57 +01:00
Bruno Herbelin
5bf280ca4d warning on not found pattern 2021-11-10 00:13:08 +01:00
Bruno Herbelin
fe00baa701 Fixed snap to find frei0r plugins
added the package to install and the PATH to find in $SNAP
2021-11-10 00:12:55 +01:00
Bruno Herbelin
d3cb1d7f42 oops 2021-11-08 00:08:19 +01:00
Bruno Herbelin
593363732a Pattern generator improvement
Testing gstreamer feature to provide only available patterns, and added many more patterns to choose from.
2021-11-08 00:05:16 +01:00
Bruno Herbelin
d00f4cf715 Cosmetics
More adapted icons and link to user manual
2021-11-07 12:25:51 +01:00
Bruno Herbelin
cac31dbb21 Help info for mask file open 2021-11-07 12:25:06 +01:00
Bruno Herbelin
eb1fa6ca04 clean file dialog after cancel 2021-11-07 12:24:46 +01:00
Bruno Herbelin
0ac515ea5a Yet more update graphical manual 2021-11-07 01:42:39 +01:00
Bruno Herbelin
190e2a4952 Continue update graphical manual 2021-11-06 22:50:59 +01:00
Bruno Herbelin
0857b1bab6 Update graphical user manual
New screenshots from vimix 6.1
2021-11-06 18:08:52 +01:00
Bruno Herbelin
f3e42fdc95 Bugfix window resize log window 2021-10-31 00:06:08 +02:00
Bruno Herbelin
d617f3308a Added Cancel button to Transition view
And minor code improvements.
2021-10-30 19:57:43 +02:00
Bruno Herbelin
27cec85443 Do not re-open previous session if new session last created 2021-10-26 23:50:26 +02:00
Bruno Herbelin
63f7cab508 Improved gstreamer support for GPU decoding in Linux 2021-10-26 23:38:41 +02:00
Bruno Herbelin
ce0ac1bee1 Minor improvement precision media player gap timing 2021-10-12 23:20:16 +02:00
Bruno Herbelin
2c2584c8df Keyboard shortcut END for Disable output 2021-10-11 23:17:29 +02:00
Bruno Herbelin
14fd4d96c3 Shortcut for output window fullscreen and raise 2021-10-11 22:46:38 +02:00
Bruno Herbelin
dd7a63413c Fixed keyboard arrows control 2021-10-09 23:40:18 +02:00
Bruno Herbelin
6d0c2301c1 Keyboard input for Source Controller
SPACE toggle play/pause, B for begin restart
2021-10-09 00:16:55 +02:00
Bruno Herbelin
8bf8f05add compilation warnings 2021-09-24 00:46:20 +02:00
Bruno Herbelin
bd773d54c6 Improve stability FPS measure in timecounter 2021-09-24 00:45:47 +02:00
Bruno Herbelin
f4c52b7ed3 Fixed output monitor disablling 2021-09-19 11:07:13 +02:00
Bruno Herbelin
5b1504c8f6 Added general DISABLE output action menu
Makes sure the output is black, unrelated to session openning or opacity
2021-09-17 11:31:52 +02:00
Bruno
06187b9a1a work-in progress Helper and keyboard shortcuts 2021-08-26 15:51:07 +02:00
Bruno
7fb6e57829 Added 1200px height selection (e.g. for WUXGA displays) 2021-08-20 21:32:12 +02:00
Bruno
5ec954dbb5 UI fix 2021-08-16 22:55:21 +02:00
Bruno
a6bc30cf62 Fixed Frame grabber 2021-08-16 22:26:59 +02:00
Bruno
df165252fa Fixed OSX vtenc_h264_hw support for Recording 2021-08-16 15:52:58 +02:00
Bruno
da9c94f675 Temporarily disable v4l loopback: not working anymore 2021-08-15 00:32:20 +02:00
Bruno
031cef6357 optimize jpegenc 2021-08-15 00:30:28 +02:00
Bruno
ef5f3efd2e BugFix changing resolution of session 2021-08-14 23:15:18 +02:00
Bruno
bc8c4e3c7b Cleanup UI for centralized Recording settings 2021-08-14 21:57:59 +02:00
Bruno
f5da4c8bc2 Recording: support for NVIDIA nvenc and improved stability
Let gstreamer appsrc generate PTS automatically (seems to fix crash of encoding after long duration). Added test for GPU encoders and switch if enabled and available.
2021-08-14 13:41:53 +02:00
Bruno
644741a1ab Attempt at improving recoding when buffer if full 2021-08-12 00:05:41 +02:00
Bruno
09f46e7a27 Minor GUI layout improvement 2021-08-12 00:05:22 +02:00
Bruno
db4e1d214f BugFix drop Session file 2021-08-11 22:18:08 +02:00
Bruno
79433dd45c Improved Log message Video recording 2021-08-11 22:17:43 +02:00
Bruno
fe72c9b829 Cleanup and improve stability of FrameGrabber 2021-08-11 20:48:18 +02:00
Bruno
b37d22ba47 Improved FrameGrabber with clock duration and priority strategies
Keep track of actual FrameGrabber duration (different from timestamp). Two strategies for frame PTS: clock and framerate priorities. Implemented variable Framerate selection for VideoRecorder.  Integration of all this in UserInterface and Settings.
2021-08-11 00:20:28 +02:00
Bruno
7fb08e618f Added a READABLE time string conversion 2021-08-11 00:17:07 +02:00
Bruno
63c6f1169b Add icons on info and warning Logs 2021-08-11 00:16:19 +02:00
Bruno
0eff8fd24d Minor compilation warning fixed 2021-08-09 10:08:32 +02:00
Bruno
818e554d35 removing debuging info 2021-08-08 23:58:54 +02:00
Bruno
5a18dbaf37 Video Recoding Buffer management
Implemented methods to supervise encoding in FrameGrabber, avoid running out of buffer, and give user a selection of buffer sizes for recording.
2021-08-08 23:58:35 +02:00
Bruno
ddd9bb4e99 minor compilation fix 2021-08-07 20:26:59 +02:00
Bruno
38a2aa90e0 Improved FrameBufferSuface API 2021-08-07 20:26:30 +02:00
Bruno
139137770c BugFix: prevent missing symbol when attach 2021-08-07 20:25:52 +02:00
Bruno
789bf1bd00 BugFix prevent failed FrameBuffer fill 2021-08-07 20:23:20 +02:00
Bruno
563e762dde Store mask after fill 2021-08-07 20:22:07 +02:00
Bruno
28da5f8f39 BugFix Restore fading when cross fading 2021-08-07 20:21:37 +02:00
Bruno
5c42061fd9 Work in progress - towards display initials on sources 2021-08-07 20:21:37 +02:00
Bruno
843224ca35 oops.. correct Mesh file parse 2021-08-07 14:14:16 +02:00
Bruno
e47e76962b Fixed Recording (timing and UI)
Improved frame grabber timing and fixed UserInterface to show the "saving file" info.
2021-08-07 12:34:05 +02:00
Bruno
2f0e4e3212 Improved recording time acuracy 2021-08-07 01:02:39 +02:00
Bruno
fb3e1d0d25 Detecting EOF recording and unexpected termination 2021-08-06 21:23:01 +02:00
Bruno
e9b7e55570 work in progress recording probe 2021-08-06 17:56:48 +02:00
Bruno
8c206898f0 Dialog media include more formats
Integrate exotic file extensions and uppercase equivalent of all possible files to select with dialogs. 
Code cleanup
2021-08-06 16:43:25 +02:00
Bruno
a9c9683b8b longer recording timeout 2021-08-06 13:26:19 +02:00
Bruno
4f43ddf088 Draw a glyph in IMGUI
Proof of concept to show how to access Font texture in opengl to draw one glygh
2021-08-06 13:25:19 +02:00
Bruno
a9c8b67975 Implementation of custom Masks
FrameBuffer accepts to fill any size of FrameBufferImage as input, and a Dialog in TextureView allows to select a JPG or PNG.
2021-08-06 13:23:59 +02:00
Bruno
d1b7073ff9 Reimplementation of Dialogs
Cleanup code to integrate multithreading process for dialogs into the DialogToolkit (avoid poluting UserInterfaceManager and improves reliability)
2021-08-06 13:21:16 +02:00
Bruno
58afcacab9 BugFix thumbnailing
1. avoid crash by cathing the correct exception and 2. ensure we capture a frame by waiting a little
2021-08-04 12:55:51 +02:00
Bruno
5eddfcf196 Add tag icon in thumbnail to identify as user defined 2021-08-04 00:29:31 +02:00
Bruno
9a87764949 Improved UI for inactive videos
Display Player for videos even in disabled state, but with disabled controls
2021-08-03 20:02:04 +02:00
Bruno
fc4e40fba3 Display mixing source original texture when inactive
Re-using activesurface_ for manipulation and display of the source's input texture in the Mixer icon when inactive.
2021-08-02 22:27:06 +02:00
Bruno
e8acfc1c26 New Media Player option to Rewind on Disabled 2021-08-01 19:10:46 +02:00
Bruno
eaadc210ae Performance improvement for transliteration
Tracing CPU usage identified the cost of ICU transliteration: using a static dictionnary to improve performance
2021-08-01 16:44:46 +02:00
Bruno
8002f3164c Confirmed performance improvement without glBufferSubData 2021-08-01 16:43:25 +02:00
Bruno
48f92bc52b Cleanup session properties panel 2021-08-01 12:13:38 +02:00
Bruno
d1e833e0a1 Properties pannel of Session
Also added custom thumbnail of session.
2021-08-01 00:29:44 +02:00
Bruno
c5f0be2b32 Compilation Linux 2021-07-30 21:36:47 +02:00
Bruno
dbcf3bb0ea backward compatibility title window 2021-07-30 16:48:26 +02:00
Bruno
e7a79f6cdc Cleanup path_relative_to_path and path_absolute_from_path 2021-07-30 16:08:24 +02:00
Bruno
63b043dc4b Improved windows titles management
Display filename (no path) before APP_NAME, clean APP_TITLE when no file, bugs fixed.
2021-07-30 16:08:00 +02:00
Bruno
d2a576c99c Support for relative path for files in mix
File path in mix session file add a relative reference to the location of the session mix file. If SessionCreator cannot find the absolute path, it tries to load the file at the relative location. Done for MediaSource, SessionFileSource and SequenceSource.
2021-07-30 00:22:44 +02:00
Bruno
fc91e7cbdd Draft Function path_relative_to_path 2021-07-28 19:03:38 +02:00
Bruno
0555361a57 BugFix glfw set Window Title
Function is not thread safe, causing crash when saving.
2021-07-27 20:05:39 +02:00
Bruno
c923815a01 Added Apple Code signing script in cmake 2021-07-27 20:05:01 +02:00
Bruno
7a4d2ac027 Merge remote-tracking branch 'origin/master' 2021-07-27 09:15:47 +02:00
Bruno
442e1096be Compilation and packaging OSX 10.14 2021-07-27 09:12:53 +02:00
Bruno
6eaf8852ae OSX compilation 2021-07-27 09:06:53 +02:00
Bruno
3612fca707 Add keyboard shortcut play/pause
Space bar to toggle play/pause current source, B  for 'beginning' and N for 'next frame'
2021-07-26 12:51:07 +02:00
Bruno
4736d403a1 bugfix save as 2021-07-26 12:21:06 +02:00
Bruno
a18fd3177c Follow clang-tidy and clazy suggestions
variables non-POD should not be 'static' outside a class. Always use and init variables. Delete useless classes.
2021-07-17 16:45:01 +02:00
Bruno
5930b0f8fe UI bugfix locked source in texture view 2021-07-17 11:55:23 +02:00
Bruno
1c7c64db59 Merge remote-tracking branch 'origin/master' 2021-07-17 10:33:53 +02:00
Bruno
9bc780bcda Update CMAKE for OSX
minor text message changes
2021-07-17 10:33:43 +02:00
Bruno
b3f89e0464 for OSX 2021-07-17 10:32:13 +02:00
Bruno
1de4822c67 OSX compile 2021-07-03 11:27:08 +02:00
Bruno
c846e4072a postponing the dev of snapshot interpolation 2021-07-03 10:19:00 +02:00
Bruno
c4f26bd500 added -v and -t command line options 2021-07-02 22:16:55 +02:00
Bruno
041c01135a Small improvement timing fade in and out
Adding a buffer of 0 opacity before or after fading to avoid jumps to previous or next frame of a segment
2021-06-28 23:43:44 +02:00
Bruno
aa904f26ad Recording timeout with timing slider
Changed timout recording in uint milisecond.
2021-06-28 21:33:17 +02:00
Bruno
ff99d37eb6 Player Video Fading dialog
New dialog to apply fade in & out with parameters. Fixed Timeline fading functions. New ImGuiToolkit items to draw icons in Combo boxes.
2021-06-28 00:21:29 +02:00
Bruno
e8a500dc99 BugFix negative play speed Selection 2021-06-24 23:15:50 +02:00
Bruno
9f4f247cd2 Bugfix jump gaps MediaPlayer 2
Timeline copy should not overwrite pts of first frame.
2021-06-23 20:34:00 +02:00
Bruno
e1ac930dd6 Pedantic tooltip 2021-06-21 23:15:17 +02:00
Bruno
c20ed94f46 Bugfix jump gaps MediaPlayer
now that we use first frame time, testing jumps should be done with beginning time
2021-06-21 23:14:43 +02:00
Bruno
4efe754a8d MediaPlayer decoder information improved 2021-06-20 23:54:19 +02:00
Bruno
bb83f7fcb7 Timeline fade in and fade out 2021-06-20 23:53:52 +02:00
Bruno
79fa6082b0 Player: shoft slider on first frame of MediaPlayer 2021-06-20 18:50:12 +02:00
Bruno
f2ecc88955 Clean string from infor visitor on media player 2021-06-20 18:49:39 +02:00
Bruno
7253c1ec1a Merge remote-tracking branch 'origin/master' 2021-06-19 09:49:53 +02:00
BHBN
cf32c9fc12 Merge pull request #39 from brunoherbelin/dev
Accept new Player (dev)
2021-06-19 09:49:11 +02:00
Bruno
3086735be1 Merge branch 'HEAD' 2021-06-19 01:04:11 +02:00
Bruno
5a54e84dd8 Player slight improvements
tick marks count adapted to fps, clamped refresh frequency computation, listing of all sources playable from menu
2021-06-19 01:03:21 +02:00
Bruno
1ef26c0c95 Warning on failed discovery of MediaPlayer framerate
Default to 30fps
2021-06-19 01:03:21 +02:00
Bruno
887142079b Fixed Timeline display 2021-06-19 01:03:21 +02:00
Bruno
b75ea00c0d Unique play/pause button for multiple sources 2021-06-19 01:03:21 +02:00
Bruno
319fbfa84d Bugfix display STRING time 2021-06-19 01:03:21 +02:00
Bruno
3874252797 Bugfix computation time with gaps 2021-06-19 01:03:21 +02:00
Bruno
5dfc45af5f Fixed Timeline ticks display 2021-06-19 01:03:21 +02:00
Bruno
1f203801db Player: reset selection on session change 2021-06-19 01:03:21 +02:00
Bruno
291410a2b3 Player UI: video menu and speed reset icon
+ rename private variables to follow usual style
2021-06-19 01:03:21 +02:00
Bruno
dfc4937688 Player: move up timeline and adjust size
keep play button bar at the bottom for all configurations, avoid text and buttons overlay when Player is small. Fix cut timing in selection
2021-06-19 01:03:21 +02:00
Bruno
a0b763ab71 Timeline management in Player
Actions at key times (durations of all videos) to allow to adjust other videos duration (change speed of cut)
2021-06-19 01:03:21 +02:00
Bruno
ad36ac5cd9 Player timeline for selection
Selection of media sources now displays in a list with proportional timelines, showing actual play time and cursor on effective timeline with opacity curve.
2021-06-19 01:03:21 +02:00
Bruno
cd40d6d7e8 Improved management of selection in Player 2021-06-19 01:03:21 +02:00
Bruno
ec4214ebf8 improved quality realtime recorder h264 2021-06-19 01:03:21 +02:00
Bruno
a403d40b6c Stick window on current view
menu item for media player and output preview to pin the window in current view
2021-06-19 01:03:21 +02:00
Bruno
7dcfc97f33 UI details 2021-06-19 01:03:21 +02:00
Bruno
5ea056a483 Bugfix timeline display array 2021-06-19 01:03:21 +02:00
Bruno
cd1702bb53 Define UNICODE symbols 2021-06-19 01:03:21 +02:00
Bruno
6ff266581a work in progress selection timeline 2021-06-19 01:03:21 +02:00
Bruno
6b7d108407 Minor improvements timeline display 2021-06-19 01:03:21 +02:00
Bruno
473e24bcd7 Fixed and improved TimeCounter 2021-06-19 01:03:21 +02:00
Bruno
1f5056bf15 BugFix IconButton (pop id) 2021-06-19 01:03:21 +02:00
Bruno
61fa062794 Correct implementation of DeviceSource resize
previous commit was bad...
2021-06-19 01:03:21 +02:00
Bruno
0e48cf4505 Bugfix and cleanup Info on source in UI 2021-06-19 01:03:21 +02:00
Bruno
b606f479e9 Bugfix change device source resolution 2021-06-19 01:03:21 +02:00
Bruno
2ccbf1ec12 Bugfix closing single frame stream 2021-06-19 01:03:21 +02:00
Bruno
ac6e84bb1c New InfoVisitor: get string to describe sources
Unified code in ImGui visitor and Player
2021-06-19 01:03:21 +02:00
Bruno
11d12c1f29 New Timeline actions
Smooth and auto cut actions added on the side of the timeline UI.
2021-06-19 01:03:21 +02:00
Bruno
c9707e7335 Improved link between ImGuiVisitor and SourcePlayer
Source panel shows description and icon to open player UI. Changed icon player, and fixed source selection.
2021-06-19 01:03:21 +02:00
Bruno
2c0be68a3c Cleaup UI Selection source Player 2021-06-19 01:03:21 +02:00
Bruno
2add317106 Improved UI media player (info media) 2021-06-19 01:03:21 +02:00
Bruno
60ec11982a OSX compilation 2021-06-19 01:03:21 +02:00
Bruno
b2284cf1b4 Improved cursor EditPlotHistoLines 2021-06-19 01:02:12 +02:00
Bruno
e87ef2774b New SourcePlayer
Work in progress; Sources now have play/pause and associated play functions. Media player can play all playable sources, and adapts to control a media player when possible. Selection of play groups (to finalize)
2021-06-19 01:02:12 +02:00
Bruno
7a9fcaefd6 Minor code improvements 2021-06-19 01:02:12 +02:00
Bruno
2333a7a11a Bugfix Do not lock session in action manager
This was causing mutex deadlock
2021-06-19 01:02:12 +02:00
Bruno
2a7857c499 Bugfix; verify frame grabbers before use 2021-06-19 01:02:12 +02:00
Bruno
e892dc1eb5 Implemented delayed start of recording 2021-06-19 01:02:12 +02:00
Bruno
9c8d1f31f6 bugfix linux shared webcam ui 2021-06-19 01:02:12 +02:00
Bruno
048db7a44b Merge remote-tracking branch 'origin/master' 2021-06-19 01:01:59 +02:00
Bruno
1d94d494b6 Merge remote-tracking branch 'origin/dev' into dev 2021-06-19 00:50:31 +02:00
Bruno
c6ac35addb Player slight improvements
tick marks count adapted to fps, clamped refresh frequency computation, listing of all sources playable from menu
2021-06-19 00:49:20 +02:00
Bruno
9ec279754b Warning on failed discovery of MediaPlayer framerate
Default to 30fps
2021-06-19 00:49:20 +02:00
Bruno
bdcf28c5da Fixed Timeline display 2021-06-19 00:49:20 +02:00
Bruno
edf0f8074a Unique play/pause button for multiple sources 2021-06-19 00:49:20 +02:00
Bruno
c83a946cbd Bugfix display STRING time 2021-06-19 00:49:20 +02:00
Bruno
8604babeb6 Bugfix computation time with gaps 2021-06-19 00:49:20 +02:00
Bruno
7252b74539 Fixed Timeline ticks display 2021-06-19 00:49:20 +02:00
Bruno
08fbaa039f Player: reset selection on session change 2021-06-19 00:49:20 +02:00
Bruno
30f9fb50eb Player UI: video menu and speed reset icon
+ rename private variables to follow usual style
2021-06-19 00:49:20 +02:00
Bruno
e3578df8a0 Player: move up timeline and adjust size
keep play button bar at the bottom for all configurations, avoid text and buttons overlay when Player is small. Fix cut timing in selection
2021-06-19 00:49:20 +02:00
Bruno
37445b8857 Timeline management in Player
Actions at key times (durations of all videos) to allow to adjust other videos duration (change speed of cut)
2021-06-19 00:49:20 +02:00
Bruno
99ea14fab0 Player timeline for selection
Selection of media sources now displays in a list with proportional timelines, showing actual play time and cursor on effective timeline with opacity curve.
2021-06-19 00:49:20 +02:00
Bruno
1717c143b2 Improved management of selection in Player 2021-06-19 00:49:20 +02:00
Bruno
53223d0876 improved quality realtime recorder h264 2021-06-19 00:49:20 +02:00
Bruno
096bcb4132 Stick window on current view
menu item for media player and output preview to pin the window in current view
2021-06-19 00:49:20 +02:00
Bruno
05cc70bdbd UI details 2021-06-19 00:49:20 +02:00
Bruno
45653b52b5 Bugfix timeline display array 2021-06-19 00:49:20 +02:00
Bruno
d1841f2863 Define UNICODE symbols 2021-06-19 00:49:20 +02:00
Bruno
f85de11711 work in progress selection timeline 2021-06-19 00:49:20 +02:00
Bruno
9d81a105ee Minor improvements timeline display 2021-06-19 00:49:20 +02:00
Bruno
deb6af9dea Fixed and improved TimeCounter 2021-06-19 00:49:20 +02:00
Bruno
ab512b76aa BugFix IconButton (pop id) 2021-06-19 00:49:20 +02:00
Bruno
bcbeee7247 Correct implementation of DeviceSource resize
previous commit was bad...
2021-06-19 00:49:20 +02:00
Bruno
bcdc94c3b9 Bugfix and cleanup Info on source in UI 2021-06-19 00:49:20 +02:00
Bruno
6ebcf49758 Bugfix change device source resolution 2021-06-19 00:49:20 +02:00
Bruno
a936ab6851 Bugfix closing single frame stream 2021-06-19 00:49:20 +02:00
Bruno
95378660dd New InfoVisitor: get string to describe sources
Unified code in ImGui visitor and Player
2021-06-19 00:49:20 +02:00
Bruno
e49bdac3e8 New Timeline actions
Smooth and auto cut actions added on the side of the timeline UI.
2021-06-19 00:49:20 +02:00
Bruno
48380fab7e Improved link between ImGuiVisitor and SourcePlayer
Source panel shows description and icon to open player UI. Changed icon player, and fixed source selection.
2021-06-19 00:49:20 +02:00
Bruno
ce92529a84 Cleaup UI Selection source Player 2021-06-19 00:49:20 +02:00
Bruno
8e29a555c8 Improved UI media player (info media) 2021-06-19 00:49:20 +02:00
Bruno
a1b6ec066b OSX compilation 2021-06-19 00:49:20 +02:00
Bruno
fb59bf491f Improved cursor EditPlotHistoLines 2021-06-19 00:48:11 +02:00
Bruno
86aec7d2ba New SourcePlayer
Work in progress; Sources now have play/pause and associated play functions. Media player can play all playable sources, and adapts to control a media player when possible. Selection of play groups (to finalize)
2021-06-19 00:48:11 +02:00
Bruno
579f7d5609 Minor code improvements 2021-06-19 00:48:11 +02:00
Bruno
c87b1ac363 Bugfix Do not lock session in action manager
This was causing mutex deadlock
2021-06-19 00:48:11 +02:00
Bruno
543648112b Bugfix; verify frame grabbers before use 2021-06-19 00:48:11 +02:00
Bruno
daa3b9e978 Implemented delayed start of recording 2021-06-19 00:48:11 +02:00
Bruno
fb8da181da bugfix linux shared webcam ui 2021-06-19 00:48:11 +02:00
Bruno
6cc5a8af9e Player slight improvements
tick marks count adapted to fps, clamped refresh frequency computation, listing of all sources playable from menu
2021-06-19 00:47:47 +02:00
Bruno
1a1956962a Warning on failed discovery of MediaPlayer framerate
Default to 30fps
2021-06-18 23:58:23 +02:00
Bruno
e422a1b403 Fixed Timeline display 2021-06-18 23:57:42 +02:00
Bruno
295ece79ae Unique play/pause button for multiple sources 2021-06-18 00:22:07 +02:00
Bruno
08f8ee159a Bugfix display STRING time 2021-06-18 00:21:36 +02:00
Bruno
e2d416b3fb Bugfix computation time with gaps 2021-06-18 00:21:09 +02:00
Bruno
16ed97b4cb Fixed Timeline ticks display 2021-06-17 21:42:15 +02:00
Bruno
82739702bd Player: reset selection on session change 2021-06-16 23:47:42 +02:00
Bruno
0010c9e3d5 Player UI: video menu and speed reset icon
+ rename private variables to follow usual style
2021-06-16 23:12:31 +02:00
Bruno
f59d4af92b Player: move up timeline and adjust size
keep play button bar at the bottom for all configurations, avoid text and buttons overlay when Player is small. Fix cut timing in selection
2021-06-15 23:51:59 +02:00
Bruno
a7df619a05 Timeline management in Player
Actions at key times (durations of all videos) to allow to adjust other videos duration (change speed of cut)
2021-06-14 23:42:20 +02:00
Bruno
f3759c2ef5 Player timeline for selection
Selection of media sources now displays in a list with proportional timelines, showing actual play time and cursor on effective timeline with opacity curve.
2021-06-13 00:24:45 +02:00
Bruno
d547f3a8a8 Improved management of selection in Player 2021-06-07 22:53:29 +02:00
Bruno
583e53d8a8 improved quality realtime recorder h264 2021-06-07 00:04:23 +02:00
Bruno
a27bf08ce0 Stick window on current view
menu item for media player and output preview to pin the window in current view
2021-06-07 00:04:06 +02:00
Bruno
060fb5ad2d UI details 2021-06-07 00:03:06 +02:00
Bruno
ffc00d9035 Bugfix timeline display array 2021-06-06 14:54:55 +02:00
Bruno
83a9d281c2 Define UNICODE symbols 2021-06-06 14:54:21 +02:00
Bruno
b6c853c308 work in progress selection timeline 2021-06-06 00:17:19 +02:00
Bruno
552c09d377 Minor improvements timeline display 2021-05-30 22:56:13 +02:00
Bruno
61164e627b Fixed and improved TimeCounter 2021-05-26 23:31:34 +02:00
Bruno
3c71ee1ff2 BugFix IconButton (pop id) 2021-05-25 22:17:57 +02:00
Bruno
07f610e84a Correct implementation of DeviceSource resize
previous commit was bad...
2021-05-25 20:20:43 +02:00
Bruno
faf344bc03 Bugfix and cleanup Info on source in UI 2021-05-25 09:09:23 +02:00
Bruno
e9482a3dfc Bugfix change device source resolution 2021-05-25 09:08:51 +02:00
Bruno
d4a7ce3487 Bugfix closing single frame stream 2021-05-25 09:08:24 +02:00
Bruno
3ef2737d82 New InfoVisitor: get string to describe sources
Unified code in ImGui visitor and Player
2021-05-24 20:39:56 +02:00
BHBN
582b67f4e1 Add ref to GLP 3 or later 2021-05-24 12:10:51 +02:00
Bruno
5dd6c0af78 New Timeline actions
Smooth and auto cut actions added on the side of the timeline UI.
2021-05-24 00:58:21 +02:00
Bruno
893e4f4723 Improved link between ImGuiVisitor and SourcePlayer
Source panel shows description and icon to open player UI. Changed icon player, and fixed source selection.
2021-05-23 12:32:32 +02:00
Bruno
d137e87f0e Cleaup UI Selection source Player 2021-05-23 00:55:24 +02:00
Bruno
7f152077e5 Improved UI media player (info media) 2021-05-22 01:34:19 +02:00
Bruno
74a9b229d0 OSX compilation 2021-05-19 23:30:07 +02:00
Bruno
80cf57a979 Improved cursor EditPlotHistoLines 2021-05-19 23:29:57 +02:00
Bruno
19ba943075 New SourcePlayer
Work in progress; Sources now have play/pause and associated play functions. Media player can play all playable sources, and adapts to control a media player when possible. Selection of play groups (to finalize)
2021-05-19 00:31:37 +02:00
Bruno
ba0e25a272 Minor code improvements 2021-05-17 16:13:21 +02:00
Bruno
575c487fa6 Bugfix Do not lock session in action manager
This was causing mutex deadlock
2021-05-17 16:10:46 +02:00
Bruno
1e91b2aa29 Bugfix; verify frame grabbers before use 2021-05-13 23:15:47 +02:00
Bruno
e10cf40f38 Implemented delayed start of recording 2021-05-13 22:15:50 +02:00
Bruno
23defd2117 bugfix linux shared webcam ui 2021-05-12 21:01:19 +02:00
Bruno
1227b87565 Merge remote-tracking branch 'origin/master' 2021-05-08 23:19:23 +02:00
Bruno
424ef16831 ignore local OSX files 2021-05-08 12:34:47 +02:00
Bruno
87059df28a Merge remote-tracking branch 'origin/master' 2021-05-08 12:33:41 +02:00
BHBN
520ff24ac2 Merge pull request #33 from brunoherbelin/dev
Dev
2021-05-08 12:32:48 +02:00
Bruno
920304773b Merge remote-tracking branch 'origin/dev' 2021-05-08 12:27:07 +02:00
Bruno
e55059c82c OSX update 2021-05-08 12:24:37 +02:00
Bruno
6a96c91fe1 Merge branch 'dev' 2021-05-08 12:19:13 +02:00
Bruno
f995e1cf72 Reordering source type in UI 2021-05-08 12:17:35 +02:00
Bruno
3be09553cf Multiple file selection for OSX
and finetuning creation UI for MultiFile source
2021-05-07 18:56:54 +02:00
Bruno
ee28a03a6c clean MultiFileSource 2021-05-06 23:56:31 +02:00
Bruno
d56700a9d5 Saving, undo and dynamic change of MultiFileSource 2021-05-06 23:44:15 +02:00
Bruno
250df8b651 Bugfix: allow changing resolution of stream 2021-05-06 22:18:46 +02:00
Bruno
e071ffe590 Create new Source type MultiFile
MultiFileSource plays a sequence of numbered images.
2021-05-06 00:24:01 +02:00
brunoherbelin
737b45a18c bugfix test initialization before render source 2021-05-04 21:40:24 +02:00
Bruno
1d2b7b17e8 CTRL + clic to lock/unlock 2021-05-01 20:03:42 +02:00
Bruno
bd71a4b581 restoring copy of transform_ matrix
would be possible to optimize and avoid this copy, but should be verified everywhere that this change of paradigm is taken into account (i.e. run update after copy transform if needed)
2021-05-01 19:12:09 +02:00
Bruno
451ff64b6f Cosmetics
Moved string truncate to BaseToolkit, fixed SystemToolkit max memory, clean left panel UI
2021-05-01 16:39:01 +02:00
Bruno
5e0dd60adb re-open should not change gl texture
rename 'ready' to 'opened' to avoid confusion
2021-05-01 15:35:36 +02:00
Bruno
a05cd380fb Using source mode UNINITIALIZED to replace initialized_ bool
info about source initialization was duplicated; using the mode state machine only avoids to keep a flag
2021-05-01 10:29:34 +02:00
Bruno
fd3102c0d3 memory monitor on latest value (not average) 2021-05-01 09:38:21 +02:00
Bruno
1bdd52232c compile fix 2021-05-01 00:36:02 +02:00
Bruno
381f68aaae Change READY state of source
a source is ready after rendering one frame (which is after being initialized)
2021-05-01 00:34:58 +02:00
Bruno
058fde19ce Fixed merging and action manager bug 2021-04-30 12:33:23 +02:00
Bruno
63544c603d Fixed Session thumbnail
wait for all sources to be ready before creating the thumbnail.
2021-04-30 10:17:10 +02:00
Bruno
92cd8f945e align behavior with stream 2021-04-29 23:13:30 +02:00
Bruno
5244941d9b remove commented code 2021-04-29 23:13:03 +02:00
Bruno
877fb09fa3 more elegant session source init 2021-04-29 23:12:46 +02:00
Bruno
34827ab068 ensure execute open immediately
and various code cleanup
2021-04-29 23:12:05 +02:00
Bruno
9bbe875690 Cleanup and bigsfix UI for thumbnail and preview source 2021-04-29 23:11:04 +02:00
Bruno
c7f09fb12d Prevent crash on exit
Global objects are deleted before  glfw terminates, thus crashing opengl access. Omitting glfw terminate on program exit is harmless, so...
2021-04-29 23:08:23 +02:00
Bruno
3f6d2bb6e3 Unifying list selection UI in session panel 2021-04-28 13:52:38 +02:00
Bruno
bd2500314f decide for byte aligned opengl textures all over 2021-04-28 13:18:01 +02:00
Bruno
a28f46a9eb perform render thumbnail only if framebuffer available 2021-04-28 11:23:11 +02:00
Bruno
7b7bd763c9 clear code copy vec4 2021-04-28 11:22:27 +02:00
Bruno
1118e4bfe0 add source propose a name, does not change it yet
and use filename only if provided
2021-04-28 11:21:47 +02:00
Bruno
b0bc88e9c2 unified behavior stream and media player 2021-04-28 11:19:59 +02:00
Bruno
038fea3539 opengl good practice: unbind texture after set 2021-04-28 11:18:45 +02:00
Bruno
ee20718c51 remove debug code 2021-04-27 23:21:33 +02:00
Bruno
f46ffc004a Validate list of filenames 2021-04-27 23:20:18 +02:00
Bruno
d2f0f42c2d imgui code cleanup 2021-04-27 23:19:58 +02:00
Bruno
be36662efc Fixed unique source name algorithm 2021-04-26 23:51:47 +02:00
Bruno
c7d6d37a8e oops 2021-04-26 22:06:06 +02:00
Bruno
7ba87fcee8 Fixed ButtonOpenUrl 2021-04-26 18:49:01 +02:00
Bruno
c3713c9ce7 work in progress tooltips with thumbnails of sessions 2021-04-26 13:48:20 +02:00
Bruno
98861cea6c Fixed Mixer delete and rename 2021-04-26 13:47:48 +02:00
brunoherbelin
d89290f9b2 alphabetically sort list of files in a dir 2021-04-26 09:22:22 +02:00
Bruno
1b6b2ecd4d Bugfix undo of multiple sources delete 2021-04-26 00:29:26 +02:00
Bruno
0e3575c1ca Using new basetoolkit function for unique naming
applied to source and snapshot names
2021-04-25 23:59:18 +02:00
Bruno
055f5c4c4e Creating a base toolkit for functions independent from other toolkits 2021-04-25 20:09:22 +02:00
Bruno
129d5445c3 Implementation of UI for snapshot manipulation
with thumbnails appearing on mouse over and in edit context menu
2021-04-25 14:02:06 +02:00
Bruno
6e202def1e Bugfix Action Snapshot replace 2021-04-25 11:39:45 +02:00
Bruno
0c51b16353 use framebuffer blit for thumbnail of RenderView 2021-04-25 01:27:51 +02:00
Bruno
c5ee77e969 Fixed blit of framebuffer 2021-04-25 01:27:32 +02:00
Bruno
c7962abfbb Save thumbnail of session when storing history or snapshot 2021-04-25 00:27:59 +02:00
Bruno
d371f6ae8e Cleanup FrameBufferImage API 2021-04-25 00:27:32 +02:00
Bruno
8336f6a595 Fixed RenderView thumbnailer 2021-04-25 00:26:38 +02:00
Bruno
c3a24a6d7f Cleanup XML read & write utility functions 2021-04-25 00:26:05 +02:00
Bruno
f643e80de7 Using std future promises to get thumbnail of render view 2021-04-24 13:14:57 +02:00
Bruno
5473c0b38b Store image size in XML
and discard loaded image if size does not match
2021-04-24 13:14:03 +02:00
Bruno
eb54b67caa New function to get thumbnail of render view 2021-04-22 23:38:00 +02:00
Bruno
97e7e5f4a1 Cleanup image saving and loading in xml session 2021-04-22 23:37:39 +02:00
brunoherbelin
da64172848 Work-in-progress: Interpolation of snapshot in Action manager 2021-04-21 23:35:34 +02:00
Bruno
e2d2e6ddd8 prefix ++View::need_deep_update_ 2021-04-19 19:24:50 +02:00
Bruno
896cae2d07 Fixed window resize 2021-04-19 18:29:10 +02:00
Bruno
409870dddb OSX cmake fix 2021-04-19 18:28:17 +02:00
Bruno
c19acda62d Added Snapshot Replace 2021-04-18 21:02:23 +02:00
Bruno
a50a6e129c Better implementation of SessionSnapshots saving 2021-04-18 18:22:21 +02:00
Bruno
d68987be0f C++ improved declaration of singleton managers 2021-04-18 13:27:19 +02:00
Bruno
bb64579fa5 Cleanup FileDialog (unused) 2021-04-18 13:15:18 +02:00
Bruno
2392d844d9 Making classes non-assignable
Following CppCheck recomendation, all classes that should not be manipulated by value are made non-assignable to ensure no mistake is made.
2021-04-18 13:04:16 +02:00
Bruno
c6d01c1420 Optimizing iteration
prefix ++i is faster than post i++
2021-04-18 11:38:03 +02:00
Bruno
8389010002 CoreSource complement
The CORE of a source is all what can be interpolated
2021-04-18 10:59:39 +02:00
Bruno
31eb13e16d Unused =operator: copy function instead 2021-04-18 10:58:03 +02:00
Bruno
7cafbc032b Working on source interpolation 2021-04-18 10:56:37 +02:00
brunoherbelin
cc752050f8 SourceCore to isolate core properties of a source 2021-04-17 14:40:00 +02:00
Bruno
c90bec36c5 clean useless ptr 2021-04-17 14:10:42 +02:00
Bruno
c1415b021a convert linklist to list of sources 2021-04-17 14:10:14 +02:00
brunoherbelin
1081b4a54d allow undo of trigger snapshot 2021-04-17 14:09:09 +02:00
brunoherbelin
ebb5fd16bb Draft implementation of Snapshots, with saving and UI 2021-04-17 10:28:12 +02:00
brunoherbelin
2d4a6d1fe6 Snapshots in Action manager 2021-04-15 22:50:28 +02:00
brunoherbelin
11df7c28b4 More robust clone XML mechanism: use ID 2021-04-13 22:41:57 +02:00
brunoherbelin
17d2a63132 Important change: sources keep their id all lifelong.
This simplifies a lot history and testing in session.
2021-04-13 22:26:26 +02:00
brunoherbelin
268486b652 std::string compatible imgui text input 2021-04-13 21:28:36 +02:00
brunoherbelin
f5af24b384 Update settings 2021-04-13 21:27:51 +02:00
brunoherbelin
6d522876ad Loading session with menu do not use smooth transition 2021-04-13 21:24:56 +02:00
brunoherbelin
2814763b97 std::string compatible imgui text input 2021-04-13 21:23:42 +02:00
brunoherbelin
765133a3bd BugFix 2021-04-11 15:24:24 +02:00
brunoherbelin
ab41a0c5d8 First implementation of Sticky Notes 2021-04-11 15:13:46 +02:00
brunoherbelin
eee9f49c05 Refurbishing the left panel
Toggle settings to show whole panel of settings
More space for main session panel (added notes)
2021-04-11 01:27:21 +02:00
Bruno
d7102809fc Auto stash before checking out "origin/dev" 2021-04-10 00:47:05 +02:00
Bruno
e792c119ea Merge branch 'master' into origin/dev 2021-04-10 00:45:00 +02:00
brunoherbelin
6e4ced8dcb Unified undo history messages 2021-04-10 00:22:16 +02:00
brunoherbelin
e69be79aed Compilation defines to cleanup old code 2021-04-09 22:50:16 +02:00
brunoherbelin
87dc282fb7 Improved MediaPlayer memory consumption:
Avoid duplicating Timeline object and limit number of URI discoverers to
two parallel threads.
2021-04-09 11:23:05 +02:00
brunoherbelin
38f7fb7c16 Bugfix timeline (prevent zero div) 2021-04-09 11:22:06 +02:00
brunoherbelin
6173e8279f Improved dev toolbox (saving statistics of memory) 2021-04-09 11:21:43 +02:00
brunoherbelin
4f661c5c05 Merge branch 'master' of github.com:brunoherbelin/vimix 2021-04-08 00:27:20 +02:00
brunoherbelin
28172430dc Added 'Source' to metrics (moved to UserInterfaceManager) 2021-04-08 00:26:52 +02:00
brunoherbelin
5a9d4dd55e Temporary disabling feature 'follow image processing' 2021-04-07 23:00:09 +02:00
brunoherbelin
b3880ad380 Limiting memory for media player 2021-04-07 22:57:29 +02:00
BHBN
b45c846a85 Update README.md 2021-04-07 13:23:22 +02:00
brunoherbelin
788fa693fd Draft implementation of Following mechanism for Image processing 2021-04-06 23:20:13 +02:00
brunoherbelin
1d45ab1d20 Cleanup DummySource (bad bad bad) 2021-04-05 14:55:21 +02:00
brunoherbelin
ae1c3d85ab bugfix: store mask after applying effect 2021-04-05 13:31:31 +02:00
brunoherbelin
c22df2eb2a (continue) Migrating clipboard manipulation to Session XML management 2021-04-05 13:06:31 +02:00
brunoherbelin
d3a130d9ba (continue) Migrating clipboard manipulation to Session XML management 2021-04-05 13:05:38 +02:00
brunoherbelin
8a57b52fcc Migrating clipboard manipulation to Session XML management 2021-04-05 13:04:44 +02:00
brunoherbelin
dbc9803f9e center on source only if source is not visible 2021-04-04 22:21:42 +02:00
brunoherbelin
3e376eb166 ensure output and media player window are visible 2021-04-04 20:55:55 +02:00
brunoherbelin
12aca05aef prevent potential memoryleak 2021-04-04 14:38:04 +02:00
brunoherbelin
ce1de27618 Improved README 2021-04-04 14:19:36 +02:00
brunoherbelin
66d5148e3a Prevent potential memory leak 2021-04-04 13:40:17 +02:00
brunoherbelin
d2b4a825eb avoid pedantic compilation warning 2021-04-04 13:27:56 +02:00
brunoherbelin
f443720319 Programming style improvement: following Cppcheck suggestions. 2021-04-04 13:13:06 +02:00
brunoherbelin
b4627a1613 Various potential memory leak fixed 2021-04-04 01:25:35 +02:00
brunoherbelin
ceea9c10d5 gstreamer memory cleanup in mediaplayer and stream 2021-04-04 01:24:13 +02:00
brunoherbelin
a143360497 Memory leak fix 2021-04-02 22:38:34 +02:00
brunoherbelin
163757cb69 Improved layout and menu Media Player UI 2021-04-02 13:57:11 +02:00
brunoherbelin
4537b986ca Support to forced software decoding option
With reload of media player when option is changed
2021-04-02 12:14:20 +02:00
Bruno
aafac2a7a8 merge 2021-04-01 20:46:46 +02:00
brunoherbelin
7344258b2f fixed hardware decoding detection OSX 2021-04-01 09:12:41 +02:00
brunoherbelin
c59994b7e5 Implemented a detection of hardware decoding used in pipeline
Simple check for names of decoder inside uridecodebin and cross check
with the list of known hardware Decoder Names
2021-04-01 00:14:02 +02:00
brunoherbelin
b089f59e2a Implemented a detection of hardware decoding used in pipeline
Simple check for names of decoder inside uridecodebin and cross check
with the list of known hardware Decoder Names
2021-04-01 00:11:05 +02:00
Bruno
649d2b7ef7 Merge remote-tracking branch 'origin/master' 2021-03-31 19:24:48 +02:00
brunoherbelin
8bef575e8b minor changes to media player pipeline (improved performance?) 2021-03-31 15:40:02 +02:00
brunoherbelin
559a036e6d BugFix: reload list of recent sessions after any change to history 2021-03-30 23:51:06 +02:00
brunoherbelin
0b845591f9 Improved transition view
Responsive buttons placement and clarified actions.
2021-03-30 23:02:24 +02:00
brunoherbelin
a8ef68ed59 Thought of the day. 2021-03-30 19:14:34 +02:00
brunoherbelin
7293b8b9dd BugFix: clean interrupt stream when ending abruptly 2021-03-30 19:03:33 +02:00
brunoherbelin
8eef5465c9 eyecandy: better icons for file menu 2021-03-29 23:18:14 +02:00
brunoherbelin
c08cb95dc1 Bugfix: restating correct order session loading properties 2021-03-29 23:17:54 +02:00
brunoherbelin
010166e7b0 renaming mediaplayer attribute force software decoding 2021-03-29 23:17:16 +02:00
brunoherbelin
4c7fb94616 Small update of webpage (link to installation guide) 2021-03-29 22:23:00 +02:00
Bruno
46f486a367 Documenting installation for wiki 2021-03-29 22:05:41 +02:00
brunoherbelin
ea195dcf11 add link to vimeo 2021-03-28 21:03:43 +02:00
brunoherbelin
e6979acded Revert "Try to integrate embedded video in Jekyll webpage"
This reverts commit 43b4fc81b9.
2021-03-28 20:59:46 +02:00
brunoherbelin
43b4fc81b9 Try to integrate embedded video in Jekyll webpage 2021-03-28 20:56:30 +02:00
brunoherbelin
d4ce6ebee6 trying snap eextensions: [gnome-3-28] 2021-03-28 18:37:29 +02:00
brunoherbelin
1c9a5ece83 setLocale in C (not std C++) 2021-03-27 23:36:51 +01:00
brunoherbelin
8a75664264 Preventing display glitch from invalid scaling of view 2021-03-27 23:31:18 +01:00
brunoherbelin
6687bdd258 BugFix: mixed-up Locale for XML I/O caused by GTK Dialogs 2021-03-27 23:17:19 +01:00
brunoherbelin
e525ecad36 Cleanup main 2021-03-27 23:15:49 +01:00
brunoherbelin
e8b5dc0649 BugFix: not using GST g_main_context to avoid GTK conflict 2021-03-27 19:21:18 +01:00
brunoherbelin
3a0f96c1ec fixed add extension on saved filename 2021-03-27 18:23:54 +01:00
brunoherbelin
e4c06ba1bb Fixed #ifdef compilation 2021-03-27 18:21:13 +01:00
brunoherbelin
bc4eadfd08 Bugfix view config loading 2021-03-27 18:13:09 +01:00
Bruno
ee2ce3802f Linux Dialogs in GTK for SNAP compatibility
Discarding use of ZENITY under linux (previously used with the tinyfiledialog) because snapcraft makes  it impossible to use :(. Reimplementation of GTK+ dialogs directly inside vimix code. Note: no changes for OSX. Complete cleanup of cmake file.
2021-03-27 13:03:22 +01:00
brunoherbelin
43d44634f7 Trying to fix the tinyfiledialog zenity integration 2021-03-22 16:25:51 +01:00
brunoherbelin
41bd7fc958 problem snap and zenity 2021-03-22 14:34:47 +01:00
brunoherbelin
1e458a642c Merge branch 'master' of github.com:brunoherbelin/vimix 2021-03-22 13:59:05 +01:00
brunoherbelin
b255e41091 Work in progress: force sofware decoder for a media player 2021-03-22 13:58:55 +01:00
brunoherbelin
bc22832ad6 information on tinyfiledialog in about. 2021-03-22 13:35:17 +01:00
brunoherbelin
f59ac505b7 shift grab source (even on rotation) 2021-03-21 14:23:30 +01:00
brunoherbelin
e15b962221 Cosmetics: improve reordering source in left panel 2021-03-21 14:09:11 +01:00
brunoherbelin
28d4d4acc4 Bugfix: prevent crash with current source when reordering 2021-03-21 13:24:54 +01:00
brunoherbelin
5f800f3723 Creating texture only on draw 2021-03-20 23:29:30 +01:00
brunoherbelin
2537ca03c8 fix 2021-03-20 22:05:30 +01:00
brunoherbelin
1860402452 Bugfix un-understandable crash on texture mixing quadratic. 2021-03-20 22:03:57 +01:00
brunoherbelin
bec9385c68 BugFix (apparently problematic memmove under OSX) 2021-03-20 18:38:46 +01:00
brunoherbelin
798139e097 Cosmetic: add label to button in source imgui visitor 2021-03-20 14:57:13 +01:00
brunoherbelin
6683d76222 OSX package name with patch version 2021-03-20 13:49:56 +01:00
brunoherbelin
6d2d16b644 updated OSX icon 2021-03-20 13:31:09 +01:00
547 changed files with 105621 additions and 58934 deletions

50
.github/workflows/jekyll-gh-pages.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
name: Deploy Jekyll with GitHub Pages dependencies preinstalled
on:
# Runs on pushes targeting the default branch
push:
branches: ["master"]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow one concurrent deployment
concurrency:
group: "pages"
cancel-in-progress: true
jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Pages
uses: actions/configure-pages@v2
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
source: ./docs
destination: ./_site
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1

18
.gitignore vendored
View File

@@ -18,3 +18,21 @@ rules.ninja
/CMakeLists.txt.user.*
rsc/shaders/paint.fs
osx/.DS_Store
.DS_Store
osx/runvimix
*.autosave
flatpak/.flatpak-builder
flatpak/repo/
flatpak/build/
build/
.vscode/
.cache/

9
.gitmodules vendored
View File

@@ -13,9 +13,12 @@
[submodule "ext/Dirent"]
path = ext/Dirent
url = https://github.com/tronkko/dirent.git
[submodule "ext/tfd"]
path = ext/tfd
url = https://github.com/native-toolkit/tinyfiledialogs.git
[submodule "ext/glm"]
path = ext/glm
url = https://github.com/g-truc/glm.git
[submodule "ext/link"]
path = ext/link
url = https://github.com/Ableton/link.git
[submodule "ext/tfd"]
path = ext/tfd
url = https://git.code.sf.net/p/tinyfiledialogs/code

0
1 Normal file
View File

View File

@@ -1,247 +0,0 @@
#include <string>
#include <algorithm>
#include "Log.h"
#include "View.h"
#include "Mixer.h"
#include "MixingGroup.h"
#include "tinyxml2Toolkit.h"
#include "SessionSource.h"
#include "SessionVisitor.h"
#include "SessionCreator.h"
#include "Settings.h"
#include "ActionManager.h"
#ifndef NDEBUG
#define ACTION_DEBUG
#endif
using namespace tinyxml2;
Action::Action(): step_(0), max_step_(0)
{
}
void Action::clear()
{
// clean the history
xmlDoc_.Clear();
step_ = 0;
max_step_ = 0;
// start fresh
store("Session start");
}
void Action::store(const std::string &label)
{
// ignore if locked or if no label is given
if (locked_ || label.empty())
return;
// incremental naming of history nodes
step_++;
std::string nodename = "H" + std::to_string(step_);
// erase future
for (uint e = step_; e <= max_step_; e++) {
std::string name = "H" + std::to_string(e);
XMLElement *node = xmlDoc_.FirstChildElement( name.c_str() );
if ( node )
xmlDoc_.DeleteChild(node);
}
max_step_ = step_;
// create history node
XMLElement *sessionNode = xmlDoc_.NewElement( nodename.c_str() );
xmlDoc_.InsertEndChild(sessionNode);
// label describes the action
sessionNode->SetAttribute("label", label.c_str());
// view indicates the view when this action occured
sessionNode->SetAttribute("view", (int) Mixer::manager().view()->mode());
// get session to operate on
Session *se = Mixer::manager().session();
// save all sources using source visitor
SessionVisitor sv(&xmlDoc_, sessionNode);
for (auto iter = se->begin(); iter != se->end(); iter++, sv.setRoot(sessionNode) )
(*iter)->accept(sv);
// debug
#ifdef ACTION_DEBUG
Log::Info("Action stored %s '%s'", nodename.c_str(), label.c_str());
// XMLSaveDoc(&xmlDoc_, "/home/bhbn/history.xml");
#endif
}
void Action::undo()
{
// not possible to go to 1 -1 = 0
if (step_ <= 1)
return;
// restore always changes step_ to step_ - 1
restore( step_ - 1);
}
void Action::redo()
{
// not possible to go to max_step_ + 1
if (step_ >= max_step_)
return;
// restore always changes step_ to step_ + 1
restore( step_ + 1);
}
void Action::stepTo(uint target)
{
// get reasonable target
uint t = CLAMP(target, 1, max_step_);
// ignore t == step_
if (t != step_)
restore(t);
}
std::string Action::label(uint s) const
{
std::string l = "";
if (s > 0 && s <= max_step_) {
std::string nodename = "H" + std::to_string(s);
const XMLElement *sessionNode = xmlDoc_.FirstChildElement( nodename.c_str() );
l = sessionNode->Attribute("label");
}
return l;
}
void Action::restore(uint target)
{
// lock
locked_ = true;
// get history node of target step
step_ = CLAMP(target, 1, max_step_);
std::string nodename = "H" + std::to_string(step_);
XMLElement *sessionNode = xmlDoc_.FirstChildElement( nodename.c_str() );
// ask view to refresh, and switch to action view if user prefers
int view = Settings::application.current_view ;
if (Settings::application.action_history_follow_view)
sessionNode->QueryIntAttribute("view", &view);
Mixer::manager().setView( (View::Mode) view);
#ifdef ACTION_DEBUG
Log::Info("Restore %s '%s' ", nodename.c_str(), sessionNode->Attribute("label"));
#endif
//
// compare source lists
//
// we operate on the current session
Session *se = Mixer::manager().session();
if (se == nullptr)
return;
// sessionsources contains list of ids of all sources currently in the session (before loading)
SourceIdList session_sources = se->getIdList();
// for( auto it = sessionsources.begin(); it != sessionsources.end(); it++)
// Log::Info("sessionsources id %s", std::to_string(*it).c_str());
// load history status:
// - if a source exists, its attributes are updated, and that's all
// - if a source does not exists (in current session), it is created inside the session
SessionLoader loader( se );
loader.load( sessionNode );
// loaded_sources contains map of xml ids of all sources treated by loader
std::map< uint64_t, Source* > loaded_sources = loader.getSources();
// remove intersect of both lists (sources were updated by SessionLoader)
for( auto lsit = loaded_sources.begin(); lsit != loaded_sources.end(); ){
auto ssit = std::find(session_sources.begin(), session_sources.end(), (*lsit).first);
if ( ssit != session_sources.end() ) {
lsit = loaded_sources.erase(lsit);
session_sources.erase(ssit);
}
else
lsit++;
}
// remaining ids in list sessionsources : to remove
while ( !session_sources.empty() ){
Source *s = Mixer::manager().findSource( session_sources.front() );
if (s!=nullptr) {
#ifdef ACTION_DEBUG
Log::Info("Delete id %s\n", std::to_string(session_sources.front() ).c_str());
#endif
// remove the source from the mixer
Mixer::manager().detach( s );
// delete source from session
se->deleteSource( s );
}
session_sources.pop_front();
}
// remaining sources in list loaded_sources : to add
for ( auto lsit = loaded_sources.begin(); lsit != loaded_sources.end(); lsit++)
{
#ifdef ACTION_DEBUG
Log::Info("Recreate id %s to %s\n", std::to_string((*lsit).first).c_str(), std::to_string((*lsit).second->id()).c_str());
#endif
// attach created source
Mixer::manager().attach( (*lsit).second );
// change the history to match the new id
replaceSourceId( (*lsit).first, (*lsit).second->id());
}
//
// compare mixing groups
//
// Get the list of mixing groups in the xml loader
std::list< SourceList > loadergroups = loader.getMixingGroups();
// clear all session groups
auto group_iter = se->beginMixingGroup();
while ( group_iter != se->endMixingGroup() )
group_iter = se->deleteMixingGroup(group_iter);
// apply all changes creating or modifying groups in the session
// (after this, new groups are created and existing groups are adjusted)
for (auto group_loader_it = loadergroups.begin(); group_loader_it != loadergroups.end(); group_loader_it++) {
se->link( *group_loader_it, Mixer::manager().view(View::MIXING)->scene.fg() );
}
// free
locked_ = false;
}
void Action::replaceSourceId(uint64_t previousid, uint64_t newid)
{
// loop over every session history step
XMLElement* historyNode = xmlDoc_.FirstChildElement("H1");
for( ; historyNode ; historyNode = historyNode->NextSiblingElement())
{
// loop over every source in session history
XMLElement* sourceNode = historyNode->FirstChildElement("Source");
for( ; sourceNode ; sourceNode = sourceNode->NextSiblingElement())
{
// check if this source node has this id
uint64_t id_source_ = 0;
sourceNode->QueryUnsigned64Attribute("id", &id_source_);
if ( id_source_ == previousid )
// change to new id
sourceNode->SetAttribute("id", newid);
}
}
}

View File

@@ -1,49 +0,0 @@
#ifndef ACTIONMANAGER_H
#define ACTIONMANAGER_H
#include <atomic>
#include <tinyxml2.h>
class Action
{
// Private Constructor
Action();
Action(Action const& copy); // Not Implemented
Action& operator=(Action const& copy); // Not Implemented
public:
static Action& manager()
{
// The only instance
static Action _instance;
return _instance;
}
void store(const std::string &label);
void clear();
void undo();
void redo();
void stepTo(uint target);
inline uint current() const { return step_; }
inline uint max() const { return max_step_; }
std::string label(uint s) const;
private:
void restore(uint target);
void replaceSourceId(uint64_t previousid, uint64_t newid);
// void replaceSourceId(tinyxml2::XMLElement* parentnode, uint64_t previousid, uint64_t newid);
tinyxml2::XMLDocument xmlDoc_;
uint step_;
uint max_step_;
std::atomic<bool> locked_;
};
#endif // ACTIONMANAGER_H

File diff suppressed because it is too large Load Diff

128
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

View File

@@ -1,113 +0,0 @@
#ifndef DECORATIONS_H
#define DECORATIONS_H
#include <string>
#include <glm/glm.hpp>
#include "Primitives.h"
#include "Mesh.h"
class Frame : public Node
{
public:
typedef enum { ROUND = 0, SHARP, GROUP } CornerType;
typedef enum { THIN = 0, LARGE } BorderType;
typedef enum { NONE = 0, GLOW, DROP, PERSPECTIVE } ShadowType;
Frame(CornerType corner, BorderType border, ShadowType shadow);
~Frame();
void update (float dt) override;
void draw (glm::mat4 modelview, glm::mat4 projection) override;
void accept (Visitor& v) override;
glm::vec4 color;
protected:
Mesh *right_;
Mesh *left_;
Mesh *top_;
Mesh *shadow_;
LineSquare *square_;
};
class Handles : public Node
{
public:
typedef enum { RESIZE = 0, RESIZE_H, RESIZE_V, ROTATE, SCALE, CROP, MENU, LOCKED, UNLOCKED } Type;
Handles(Type type);
~Handles();
void update (float dt) override;
void draw (glm::mat4 modelview, glm::mat4 projection) override;
void accept (Visitor& v) override;
Type type() const { return type_; }
Primitive *handle() const { return handle_; }
void overlayActiveCorner(glm::vec2 v) {corner_ = v;}
glm::vec4 color;
protected:
Mesh *handle_;
Mesh *shadow_;
glm::vec2 corner_;
Type type_;
};
class Symbol : public Node
{
public:
typedef enum { CIRCLE_POINT = 0, SQUARE_POINT, IMAGE, VIDEO, SESSION, CLONE, RENDER, GROUP, PATTERN, CAMERA, CUBE, SHARE,
DOTS, BUSY, LOCK, UNLOCK, EYE, EYESLASH, VECTORSLASH, ARROWS, ROTATION, CROP, CIRCLE, SQUARE, CLOCK, CLOCK_H, GRID, CROSS, EMPTY } Type;
Symbol(Type t = CIRCLE_POINT, glm::vec3 pos = glm::vec3(0.f));
~Symbol();
void draw (glm::mat4 modelview, glm::mat4 projection) override;
void accept (Visitor& v) override;
GlmToolkit::AxisAlignedBoundingBox bbox() const { return symbol_->bbox(); }
Type type() const { return type_; }
glm::vec4 color;
protected:
Mesh *symbol_;
Mesh *shadow_;
Type type_;
};
class Disk : public Node
{
public:
Disk();
~Disk();
void draw (glm::mat4 modelview, glm::mat4 projection) override;
void accept (Visitor& v) override;
glm::vec4 color;
protected:
static Mesh *disk_;
};
//class SelectionBox : public Group
//{
//public:
// SelectionBox();
// void draw (glm::mat4 modelview, glm::mat4 projection) override;
// glm::vec4 color;
//protected:
// LineSquare *square_;
// GlmToolkit::AxisAlignedBoundingBox bbox_;
//};
#endif // DECORATIONS_H

View File

@@ -1,573 +0,0 @@
#include <algorithm>
#include <sstream>
#include <glm/gtc/matrix_transform.hpp>
#include <gst/pbutils/gstdiscoverer.h>
#include <gst/pbutils/pbutils.h>
#include <gst/gst.h>
#include "DeviceSource.h"
#include "defines.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "Resource.h"
#include "Decorations.h"
#include "Stream.h"
#include "Visitor.h"
#include "Log.h"
#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
////EXAMPLE :
///
//v4l2deviceprovider, udev-probed=(boolean)true,
//device.bus_path=(string)pci-0000:00:14.0-usb-0:2:1.0,
//sysfs.path=(string)/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/video4linux/video0,
//device.bus=(string)usb,
//device.subsystem=(string)video4linux,
//device.vendor.id=(string)1bcf,
//device.vendor.name=(string)"Sunplus\\x20IT\\x20Co\\x20",
//device.product.id=(string)2286,
//device.product.name=(string)"AUSDOM\ FHD\ Camera:\ AUSDOM\ FHD\ C",
//device.serial=(string)Sunplus_IT_Co_AUSDOM_FHD_Camera,
//device.capabilities=(string):capture:,
//device.api=(string)v4l2,
//device.path=(string)/dev/video0,
//v4l2.device.driver=(string)uvcvideo,
//v4l2.device.card=(string)"AUSDOM\ FHD\ Camera:\ AUSDOM\ FHD\ C",
//v4l2.device.bus_info=(string)usb-0000:00:14.0-2,
//v4l2.device.version=(uint)328748,
//v4l2.device.capabilities=(uint)2225078273,
//v4l2.device.device_caps=(uint)69206017;
//Device added: AUSDOM FHD Camera: AUSDOM FHD C - v4l2src device=/dev/video0
//v4l2deviceprovider, udev-probed=(boolean)true,
//device.bus_path=(string)pci-0000:00:14.0-usb-0:4:1.0,
//sysfs.path=(string)/sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4:1.0/video4linux/video2,
//device.bus=(string)usb,
//device.subsystem=(string)video4linux,
//device.vendor.id=(string)046d,
//device.vendor.name=(string)046d,
//device.product.id=(string)080f,
//device.product.name=(string)"UVC\ Camera\ \(046d:080f\)",
//device.serial=(string)046d_080f_3EA77580,
//device.capabilities=(string):capture:,
//device.api=(string)v4l2,
//device.path=(string)/dev/video2,
//v4l2.device.driver=(string)uvcvideo,
//v4l2.device.card=(string)"UVC\ Camera\ \(046d:080f\)",
//v4l2.device.bus_info=(string)usb-0000:00:14.0-4,
//v4l2.device.version=(uint)328748,
//v4l2.device.capabilities=(uint)2225078273,
//v4l2.device.device_caps=(uint)69206017; // decimal of hexadecimal v4l code Device Caps : 0x04200001
//Device added: UVC Camera (046d:080f) - v4l2src device=/dev/video2
std::string pipelineForDevice(GstDevice *device, uint index)
{
std::ostringstream pipe;
const gchar *str = gst_structure_get_string(gst_device_get_properties(device), "device.api");
if (str && gst_plugin_device.find(str) != std::string::npos)
{
pipe << gst_plugin_device;
#if defined(APPLE)
pipe << " device-index=" << index;
#else
str = gst_structure_get_string(gst_device_get_properties(device), "device.path");
if (str)
pipe << " device=" << str;
#endif
}
return pipe.str();
}
gboolean
Device::callback_device_monitor (GstBus *, GstMessage * message, gpointer )
{
GstDevice *device;
gchar *name;
switch (GST_MESSAGE_TYPE (message)) {
case GST_MESSAGE_DEVICE_ADDED: {
gst_message_parse_device_added (message, &device);
name = gst_device_get_display_name (device);
// ignore if already in the list
if ( std::find(manager().src_name_.begin(), manager().src_name_.end(), name) != manager().src_name_.end())
break;
manager().src_name_.push_back(name);
#ifdef GST_DEVICE_DEBUG
gchar *stru = gst_structure_to_string( gst_device_get_properties(device) );
g_print("\nDevice %s plugged : %s\n", name, stru);
g_free (stru);
#endif
g_free (name);
std::string p = pipelineForDevice(device, manager().src_description_.size());
manager().src_description_.push_back(p);
DeviceConfigSet confs = getDeviceConfigs(p);
manager().src_config_.push_back(confs);
manager().list_uptodate_ = false;
gst_object_unref (device);
}
break;
case GST_MESSAGE_DEVICE_REMOVED: {
gst_message_parse_device_removed (message, &device);
name = gst_device_get_display_name (device);
manager().remove(name);
#ifdef GST_DEVICE_DEBUG
g_print("\nDevice %s unplugged\n", name);
#endif
g_free (name);
manager().list_uptodate_ = false;
gst_object_unref (device);
}
break;
default:
break;
}
return G_SOURCE_CONTINUE;
}
void Device::remove(const char *device)
{
std::vector< std::string >::iterator nameit = src_name_.begin();
std::vector< std::string >::iterator descit = src_description_.begin();
std::vector< DeviceConfigSet >::iterator coit = src_config_.begin();
while (nameit != src_name_.end()){
if ( (*nameit).compare(device) == 0 )
{
src_name_.erase(nameit);
src_description_.erase(descit);
src_config_.erase(coit);
break;
}
nameit++;
descit++;
coit++;
}
}
Device::Device()
{
GstBus *bus;
GstCaps *caps;
// create GStreamer device monitor to capture
// when a device is plugged in or out
monitor_ = gst_device_monitor_new ();
bus = gst_device_monitor_get_bus (monitor_);
gst_bus_add_watch (bus, callback_device_monitor, NULL);
gst_object_unref (bus);
caps = gst_caps_new_empty_simple ("video/x-raw");
gst_device_monitor_add_filter (monitor_, "Video/Source", caps);
gst_caps_unref (caps);
gst_device_monitor_set_show_all_devices(monitor_, true);
gst_device_monitor_start (monitor_);
// initial fill of the list
GList *devices = gst_device_monitor_get_devices(monitor_);
GList *tmp;
for (tmp = devices; tmp ; tmp = tmp->next ) {
GstDevice *device = (GstDevice *) tmp->data;
gchar *name = gst_device_get_display_name (device);
src_name_.push_back(name);
g_free (name);
#ifdef GST_DEVICE_DEBUG
gchar *stru = gst_structure_to_string( gst_device_get_properties(device) );
g_print("\nDevice %s already plugged : %s", name, stru);
g_free (stru);
#endif
std::string p = pipelineForDevice(device, src_description_.size());
src_description_.push_back(p);
DeviceConfigSet confs = getDeviceConfigs(p);
src_config_.push_back(confs);
}
g_list_free(devices);
// Add config for plugged screen
src_name_.push_back("Screen capture");
src_description_.push_back(gst_plugin_vidcap);
// Try to auto find resolution
DeviceConfigSet confs = getDeviceConfigs(gst_plugin_vidcap);
if (!confs.empty()) {
// fix the framerate (otherwise at 1 FPS
DeviceConfig best = *confs.rbegin();
DeviceConfigSet confscreen;
best.fps_numerator = 15;
confscreen.insert(best);
src_config_.push_back(confscreen);
}
// TODO Use lib glfw to get monitors
// TODO Detect auto removal of monitors
list_uptodate_ = true;
}
int Device::numDevices() const
{
return src_name_.size();
}
bool Device::exists(const std::string &device) const
{
std::vector< std::string >::const_iterator d = std::find(src_name_.begin(), src_name_.end(), device);
return d != src_name_.end();
}
struct hasDevice: public std::unary_function<DeviceSource*, bool>
{
inline bool operator()(const DeviceSource* elem) const {
return (elem && elem->device() == _d);
}
hasDevice(std::string d) : _d(d) { }
private:
std::string _d;
};
Source *Device::createSource(const std::string &device) const
{
Source *s = nullptr;
// find if a DeviceSource with this device is already registered
std::list< DeviceSource *>::const_iterator d = std::find_if(device_sources_.begin(), device_sources_.end(), hasDevice(device));
// if already registered, clone the device source
if ( d != device_sources_.end()) {
CloneSource *cs = (*d)->clone();
s = cs;
}
// otherwise, we are free to create a new device source
else {
DeviceSource *ds = new DeviceSource();
ds->setDevice(device);
s = ds;
}
return s;
}
bool Device::unplugged(const std::string &device) const
{
if (list_uptodate_)
return false;
return !exists(device);
}
std::string Device::name(int index) const
{
if (index > -1 && index < (int) src_name_.size())
return src_name_[index];
else
return "";
}
std::string Device::description(int index) const
{
if (index > -1 && index < (int) src_description_.size())
return src_description_[index];
else
return "";
}
DeviceConfigSet Device::config(int index) const
{
if (index > -1 && index < (int) src_config_.size())
return src_config_[index];
else
return DeviceConfigSet();
}
int Device::index(const std::string &device) const
{
int i = -1;
std::vector< std::string >::const_iterator p = std::find(src_name_.begin(), src_name_.end(), device);
if (p != src_name_.end())
i = std::distance(src_name_.begin(), p);
return i;
}
DeviceSource::DeviceSource() : StreamSource()
{
// create stream
stream_ = new Stream;
// set symbol
symbol_ = new Symbol(Symbol::CAMERA, glm::vec3(0.75f, 0.75f, 0.01f));
symbol_->scale_.y = 1.5f;
}
DeviceSource::~DeviceSource()
{
// unregister this device source
Device::manager().device_sources_.remove(this);
}
void DeviceSource::setDevice(const std::string &devicename)
{
device_ = devicename;
Log::Notify("Creating Source with device '%s'", device_.c_str());
int index = Device::manager().index(device_);
if (index > -1) {
// register this device source
Device::manager().device_sources_.push_back(this);
// start filling in the gstreamer pipeline
std::ostringstream pipeline;
pipeline << Device::manager().description(index);
// test the device and get config
DeviceConfigSet confs = Device::manager().config(index);
#ifdef DEVICE_DEBUG
Log::Info("Device %s supported configs:", devicename.c_str());
for( DeviceConfigSet::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
DeviceConfig 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);
pipeline << " ! " << best.stream;
if (!best.format.empty())
pipeline << ",format=" << best.format;
pipeline << ",framerate=" << best.fps_numerator << "/" << best.fps_denominator;
pipeline << ",width=" << best.width;
pipeline << ",height=" << best.height;
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";
stream_->open( pipeline.str(), best.width, best.height);
stream_->play(true);
}
else
Log::Warning("No such device '%s'", device_.c_str());
}
void DeviceSource::accept(Visitor& v)
{
Source::accept(v);
if (!failed())
v.visit(*this);
}
bool DeviceSource::failed() const
{
return stream_->failed() || Device::manager().unplugged(device_);
}
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* ret = NULL;
if (gst_iterator_next(iter, &vPad) == GST_ITERATOR_OK)
{
ret = GST_PAD(g_value_get_object(&vPad));
GstCaps *device_caps = gst_pad_query_caps (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 n = 0; n < N; n++ ){
const GValue *frac = gst_value_list_get_value(val, n);
// 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;
}
}
}
}
}
// 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(19, 1);
else
return glm::ivec2(2, 14);
}

View File

@@ -1,130 +0,0 @@
#ifndef DEVICESOURCE_H
#define DEVICESOURCE_H
#include <vector>
#include <set>
#include "GstToolkit.h"
#include "StreamSource.h"
class DeviceSource : public StreamSource
{
public:
DeviceSource();
~DeviceSource();
// Source interface
bool failed() const override;
void accept (Visitor& v) override;
// StreamSource interface
Stream *stream() const override { return stream_; }
// specific interface
void setDevice(const std::string &devicename);
inline std::string device() const { return device_; }
glm::ivec2 icon() const override;
private:
std::string device_;
};
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 = 1;
fps_denominator = 1;
stream = "";
format = "";
}
inline DeviceConfig& operator = (const DeviceConfig& b)
{
if (this != &b) {
this->width = b.width;
this->height = b.height;
this->fps_numerator = b.fps_numerator;
this->fps_denominator = b.fps_denominator;
this->stream = b.stream;
this->format = b.format;
}
return *this;
}
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;
class Device
{
friend class DeviceSource;
Device();
Device(Device const& copy); // Not Implemented
Device& operator=(Device const& copy); // Not Implemented
public:
static Device& manager()
{
// The only instance
static Device _instance;
return _instance;
}
int numDevices () const;
std::string name (int index) const;
std::string description (int index) const;
DeviceConfigSet config (int index) const;
int index (const std::string &device) const;
bool exists (const std::string &device) const;
bool unplugged (const std::string &device) const;
Source *createSource(const std::string &device) const;
static gboolean callback_device_monitor (GstBus *, GstMessage *, gpointer);
static DeviceConfigSet getDeviceConfigs(const std::string &src_description);
private:
void remove(const char *device);
std::vector< std::string > src_name_;
std::vector< std::string > src_description_;
std::vector< DeviceConfigSet > src_config_;
bool list_uptodate_;
GstDeviceMonitor *monitor_;
std::list< DeviceSource * > device_sources_;
};
#endif // DEVICESOURCE_H

View File

@@ -1,100 +0,0 @@
#include <algorithm>
#include <glm/gtc/matrix_transform.hpp>
#include "defines.h"
#include "DrawVisitor.h"
#include "Scene.h"
DrawVisitor::DrawVisitor(Node *nodetodraw, glm::mat4 projection, bool force): force_(force)
{
targets_.push_back(nodetodraw);
modelview_ = glm::identity<glm::mat4>();
projection_ = projection;
num_duplicat_ = 1;
transform_duplicat_ = glm::identity<glm::mat4>();
}
DrawVisitor::DrawVisitor(std::vector<Node *> nodestodraw, glm::mat4 projection, bool force): force_(force)
{
targets_ = nodestodraw;
modelview_ = glm::identity<glm::mat4>();
projection_ = projection;
num_duplicat_ = 1;
transform_duplicat_ = glm::identity<glm::mat4>();
}
void DrawVisitor::loop(int num, glm::mat4 transform)
{
num_duplicat_ = CLAMP(num, 1, 10000);
transform_duplicat_ = transform;
}
void DrawVisitor::visit(Node &n)
{
// force visible status if required
bool v = n.visible_;
if (force_)
n.visible_ = true;
// find the node with this id
std::vector<Node *>::iterator it = std::find_if(targets_.begin(), targets_.end(), hasId(n.id()));
// found this node in the list of targets: draw it
if (it != targets_.end()) {
targets_.erase(it);
for (int i = 0; i < num_duplicat_; ++i) {
// draw multiple copies if requested
n.draw(modelview_, projection_);
modelview_ *= transform_duplicat_;
}
}
// restore visibility
n.visible_ = v;
if (targets_.empty()) return;
// update transform
modelview_ *= n.transform_;
}
void DrawVisitor::visit(Group &n)
{
// no need to traverse deeper if this node was drawn already
if (targets_.empty()) return;
// traverse children
glm::mat4 mv = modelview_;
for (NodeSet::iterator node = n.begin(); !targets_.empty() && node != n.end(); node++) {
if ( (*node)->visible_ || force_)
(*node)->accept(*this);
modelview_ = mv;
}
}
void DrawVisitor::visit(Scene &n)
{
modelview_ = glm::identity<glm::mat4>();
n.root()->accept(*this);
}
void DrawVisitor::visit(Switch &n)
{
// no need to traverse deeper if this node was drawn already
if (targets_.empty()) return;
// traverse acive child
glm::mat4 mv = modelview_;
if ( n.activeChild()->visible_ || force_)
n.activeChild()->accept(*this);
modelview_ = mv;
}
void DrawVisitor::visit(Primitive &n)
{
}

View File

@@ -1,29 +0,0 @@
#ifndef DRAWVISITOR_H
#define DRAWVISITOR_H
#include "GlmToolkit.h"
#include "Visitor.h"
class DrawVisitor : public Visitor
{
glm::mat4 modelview_;
glm::mat4 projection_;
std::vector<Node *> targets_;
bool force_;
int num_duplicat_;
glm::mat4 transform_duplicat_;
public:
DrawVisitor(Node *nodetodraw, glm::mat4 projection, bool force = false);
DrawVisitor(std::vector<Node *> nodestodraw, glm::mat4 projection, bool force = false);
void loop(int num, glm::mat4 transform);
void visit(Scene& n) override;
void visit(Node& n) override;
void visit(Primitive& n) override;
void visit(Group& n) override;
void visit(Switch& n) override;
};
#endif // DRAWVISITOR_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,112 +0,0 @@
#ifndef __IMGUI_FILE_DIALOG_H_
#define __IMGUI_FILE_DIALOG_H_
#include "imgui.h"
#include <vector>
#include <string>
#include <map>
#include <future>
#include <functional>
#define MAX_FILE_DIALOG_NAME_BUFFER 1024
struct FileInfoStruct
{
char type = ' ';
std::string filePath;
std::string fileName;
std::string ext;
};
class FileDialog
{
private:
std::vector<FileInfoStruct> m_FileList;
std::map<std::string, ImVec4> m_FilterColor;
std::string m_SelectedFileName;
std::string m_SelectedExt;
std::string m_CurrentPath;
std::vector<std::string> m_CurrentPath_Decomposition;
std::string m_Name;
bool m_ShowDialog;
bool m_ShowDrives;
public:
static char FileNameBuffer[MAX_FILE_DIALOG_NAME_BUFFER];
static char DirectoryNameBuffer[MAX_FILE_DIALOG_NAME_BUFFER];
static char SearchBuffer[MAX_FILE_DIALOG_NAME_BUFFER];
static int FilterIndex;
bool IsOk;
bool m_AnyWindowsHovered;
bool m_CreateDirectoryMode;
private:
std::string dlg_key;
std::string dlg_name;
const char *dlg_filters;
std::string dlg_path;
std::string dlg_defaultFileName;
std::string dlg_defaultExt;
std::function<void(std::string, bool*)> dlg_optionsPane;
size_t dlg_optionsPaneWidth;
std::string searchTag;
std::string dlg_userString;
public:
static FileDialog* Instance()
{
static FileDialog *_instance = new FileDialog();
return _instance;
}
static void RenderCurrent();
// Open an Open File dialog for TEXT file
static void SetCurrentOpenText();
// Open an Open File dialog for IMAGE file
static void SetCurrentOpenImage();
// Open an Open File dialog for MEDIA file
static void SetCurrentOpenMedia();
protected:
FileDialog(); // Prevent construction
FileDialog(const FileDialog&) {} // Prevent construction by copying
FileDialog& operator =(const FileDialog&) { return *this; } // Prevent assignment
~FileDialog(); // Prevent unwanted destruction
public:
void OpenDialog(const std::string& vKey, const char* vName, const char* vFilters,
const std::string& vPath, const std::string& vDefaultFileName,
std::function<void(std::string, bool*)> vOptionsPane, size_t vOptionsPaneWidth = 250, const std::string& vUserString = "");
void OpenDialog(const std::string& vKey, const char* vName, const char* vFilters,
const std::string& vDefaultFileName,
std::function<void(std::string, bool*)> vOptionsPane, size_t vOptionsPaneWidth = 250, const std::string& vUserString = "");
void OpenDialog(const std::string& vKey, const char* vName, const char* vFilters,
const std::string& vPath, const std::string& vDefaultFileName, const std::string& vUserString = "");
void OpenDialog(const std::string& vKey, const char* vName, const char* vFilters,
const std::string& vFilePathName, const std::string& vUserString = "");
void CloseDialog(const std::string& vKey);
bool Render(const std::string& vKey, ImVec2 geometry);
std::string GetFilepathName();
std::string GetCurrentPath();
std::string GetCurrentFileName();
std::string GetCurrentFilter();
std::string GetUserString();
void SetFilterColor(std::string vFilter, ImVec4 vColor);
bool GetFilterColor(std::string vFilter, ImVec4 *vColor);
void ClearFilterColor();
private:
void SetPath(const std::string& vPath);
void ScanDir(const std::string& vPath);
void SetCurrentDir(const std::string& vPath);
bool CreateDir(const std::string& vPath);
void ComposeNewPath(std::vector<std::string>::iterator vIter);
void GetDrives();
};
#endif // __IMGUI_FILE_DIALOG_H_

View File

@@ -1,384 +0,0 @@
#include <sstream>
#include "FrameBuffer.h"
#include "ImageShader.h"
#include "Resource.h"
#include "Settings.h"
#include "Log.h"
#include <glm/gtc/matrix_transform.hpp>
#include <glad/glad.h>
#include <stb_image.h>
#include <stb_image_write.h>
const char* FrameBuffer::aspect_ratio_name[5] = { "4:3", "3:2", "16:10", "16:9", "21:9" };
glm::vec2 FrameBuffer::aspect_ratio_size[5] = { glm::vec2(4.f,3.f), glm::vec2(3.f,2.f), glm::vec2(16.f,10.f), glm::vec2(16.f,9.f) , glm::vec2(21.f,9.f) };
const char* FrameBuffer::resolution_name[4] = { "720", "1080", "1440", "2160" };
float FrameBuffer::resolution_height[4] = { 720.f, 1080.f, 1440.f, 2160.f };
glm::vec3 FrameBuffer::getResolutionFromParameters(int ar, int h)
{
float width = aspect_ratio_size[ar].x * resolution_height[h] / aspect_ratio_size[ar].y;
glm::vec3 res = glm::vec3( width, resolution_height[h] , 0.f);
return res;
}
glm::ivec2 FrameBuffer::getParametersFromResolution(glm::vec3 res)
{
glm::ivec2 p = glm::ivec2(-1);
// get aspect ratio parameter
static int num_ar = ((int)(sizeof(FrameBuffer::aspect_ratio_size) / sizeof(*FrameBuffer::aspect_ratio_size)));
float myratio = res.x / res.y;
for(int ar = 0; ar < num_ar; ar++) {
if ( myratio - (FrameBuffer::aspect_ratio_size[ar].x / FrameBuffer::aspect_ratio_size[ar].y ) < EPSILON){
p.x = ar;
break;
}
}
// get height parameter
static int num_height = ((int)(sizeof(FrameBuffer::resolution_height) / sizeof(*FrameBuffer::resolution_height)));
for(int h = 0; h < num_height; h++) {
if ( res.y - FrameBuffer::resolution_height[h] < 1){
p.y = h;
break;
}
}
return p;
}
FrameBuffer::FrameBuffer(glm::vec3 resolution, bool useAlpha, bool multiSampling):
textureid_(0), intermediate_textureid_(0), framebufferid_(0), intermediate_framebufferid_(0),
use_alpha_(useAlpha), use_multi_sampling_(multiSampling)
{
attrib_.viewport = glm::ivec2(resolution);
setProjectionArea(glm::vec2(1.f, 1.f));
attrib_.clear_color = glm::vec4(0.f, 0.f, 0.f, 0.f);
}
FrameBuffer::FrameBuffer(uint width, uint height, bool useAlpha, bool multiSampling):
textureid_(0), intermediate_textureid_(0), framebufferid_(0), intermediate_framebufferid_(0),
use_alpha_(useAlpha), use_multi_sampling_(multiSampling)
{
attrib_.viewport = glm::ivec2(width, height);
setProjectionArea(glm::vec2(1.f, 1.f));
attrib_.clear_color = glm::vec4(0.f, 0.f, 0.f, 0.f);
}
void FrameBuffer::init()
{
// generate texture
glGenTextures(1, &textureid_);
glBindTexture(GL_TEXTURE_2D, textureid_);
glTexStorage2D(GL_TEXTURE_2D, 1, use_alpha_ ? GL_RGBA8 : GL_RGB8, attrib_.viewport.x, attrib_.viewport.y);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
// create a framebuffer object
glGenFramebuffers(1, &framebufferid_);
glBindFramebuffer(GL_FRAMEBUFFER, framebufferid_);
// take settings into account: no multisampling for level 0
use_multi_sampling_ &= Settings::application.render.multisampling > 0;
if (use_multi_sampling_){
// create a multisample texture
glGenTextures(1, &intermediate_textureid_);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, intermediate_textureid_);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, Settings::application.render.multisampling,
use_alpha_ ? GL_RGBA8 : GL_RGB8, attrib_.viewport.x, attrib_.viewport.y, GL_TRUE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
// attach the multisampled texture to FBO (framebufferid_ currently binded)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, intermediate_textureid_, 0);
// create an intermediate FBO : this is the FBO to use for reading
glGenFramebuffers(1, &intermediate_framebufferid_);
glBindFramebuffer(GL_FRAMEBUFFER, intermediate_framebufferid_);
// attach the 2D texture to intermediate FBO (intermediate_framebufferid_)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid_, 0);
// Log::Info("New FBO %d Multi Sampling ", framebufferid_);
}
else {
// direct attach the 2D texture to FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid_, 0);
// Log::Info("New FBO %d Single Sampling ", framebufferid_);
}
checkFramebufferStatus();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
FrameBuffer::~FrameBuffer()
{
if (framebufferid_)
glDeleteFramebuffers(1, &framebufferid_);
if (intermediate_framebufferid_)
glDeleteFramebuffers(1, &intermediate_framebufferid_);
if (textureid_)
glDeleteTextures(1, &textureid_);
if (intermediate_textureid_)
glDeleteTextures(1, &intermediate_textureid_);
}
uint FrameBuffer::texture() const
{
if (framebufferid_ == 0)
return Resource::getTextureBlack();
return textureid_;
}
float FrameBuffer::aspectRatio() const
{
return static_cast<float>(attrib_.viewport.x) / static_cast<float>(attrib_.viewport.y);
}
std::string FrameBuffer::info() const
{
glm::ivec2 p = FrameBuffer::getParametersFromResolution(resolution());
std::ostringstream info;
info << attrib_.viewport.x << "x" << attrib_.viewport.y;
if (p.x > -1)
info << "px, " << FrameBuffer::aspect_ratio_name[p.x];
return info.str();
}
glm::vec3 FrameBuffer::resolution() const
{
return glm::vec3(attrib_.viewport.x, attrib_.viewport.y, 0.f);
}
void FrameBuffer::begin(bool clear)
{
if (!framebufferid_)
init();
glBindFramebuffer(GL_FRAMEBUFFER, framebufferid_);
Rendering::manager().pushAttrib(attrib_);
if (clear)
glClear(GL_COLOR_BUFFER_BIT);
}
void FrameBuffer::end()
{
// if multisampling frame buffer
if (use_multi_sampling_) {
// blit the multisample FBO into unisample FBO to generate 2D texture
// Doing this blit will automatically resolve the multisampled FBO.
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediate_framebufferid_);
glBlitFramebuffer(0, 0, attrib_.viewport.x, attrib_.viewport.y,
0, 0, attrib_.viewport.x, attrib_.viewport.y, GL_COLOR_BUFFER_BIT, GL_NEAREST);
}
FrameBuffer::release();
Rendering::manager().popAttrib();
}
void FrameBuffer::release()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void FrameBuffer::readPixels(uint8_t *target_data)
{
if (!framebufferid_)
return;
if (use_multi_sampling_)
glBindFramebuffer(GL_READ_FRAMEBUFFER, intermediate_framebufferid_);
else
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
if (use_alpha())
glPixelStorei(GL_PACK_ALIGNMENT, 4);
else
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, attrib_.viewport.x, attrib_.viewport.y, (use_alpha_? GL_RGBA : GL_RGB), GL_UNSIGNED_BYTE, target_data);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
bool FrameBuffer::blit(FrameBuffer *destination)
{
if (!framebufferid_ || !destination)
return false;
if (!destination->framebufferid_)
destination->init();
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destination->framebufferid_);
// blit to the frame buffer object
glBlitFramebuffer(0, 0, attrib_.viewport.x, attrib_.viewport.y,
0, 0, destination->width(), destination->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return true;
}
void FrameBuffer::checkFramebufferStatus()
{
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
switch (status){
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
Log::Warning("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT is returned if any of the framebuffer attachment points are framebuffer incomplete.");
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
Log::Warning("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT is returned if the framebuffer does not have at least one image attached to it.");
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
Log::Warning("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER is returned if the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for any color attachment point(s) named by GL_DRAWBUFFERi.");
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
Log::Warning("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER is returned if GL_READ_BUFFER is not GL_NONE and the value of GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is GL_NONE for the color attachment point named by GL_READ_BUFFER.");
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
Log::Warning("GL_FRAMEBUFFER_UNSUPPORTED is returned if the combination of internal formats of the attached images violates an implementation-dependent set of restrictions.");
break;
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
Log::Warning("GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE is returned if the value of GL_RENDERBUFFER_SAMPLES is not the same for all attached renderbuffers; if the value of GL_TEXTURE_SAMPLES is the not same for all attached textures; or, if the attached images are a mix of renderbuffers and textures, the value of GL_RENDERBUFFER_SAMPLES does not match the value of GL_TEXTURE_SAMPLES.\nGL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE is also returned if the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not the same for all attached textures; or, if the attached images are a mix of renderbuffers and textures, the value of GL_TEXTURE_FIXED_SAMPLE_LOCATIONS is not GL_TRUE for all attached textures.");
break;
case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS:
Log::Warning("GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS is returned if any framebuffer attachment is layered, and any populated attachment is not layered, or if all populated color attachments are not from textures of the same target.");
break;
case GL_FRAMEBUFFER_UNDEFINED:
Log::Warning(" GL_FRAMEBUFFER_UNDEFINED is returned if target is the default framebuffer, but the default framebuffer does not exist.");
break;
case GL_FRAMEBUFFER_COMPLETE:
#ifndef NDEBUG
Log::Info("Framebuffer created %d x %d.", width(), height());
#endif
break;
}
}
glm::mat4 FrameBuffer::projection() const
{
return projection_;
}
glm::vec2 FrameBuffer::projectionArea() const
{
return projection_area_;
}
void FrameBuffer::setProjectionArea(glm::vec2 c)
{
projection_area_.x = CLAMP(c.x, 0.1f, 1.f);
projection_area_.y = CLAMP(c.y, 0.1f, 1.f);
projection_ = glm::ortho(-projection_area_.x, projection_area_.x, projection_area_.y, -projection_area_.y, -1.f, 1.f);
}
FrameBufferImage::FrameBufferImage(int w, int h) :
rgb(nullptr), width(w), height(h)
{
if (width>0 && height>0)
rgb = new uint8_t[width*height*3];
}
FrameBufferImage::FrameBufferImage(jpegBuffer jpgimg) :
rgb(nullptr), width(0), height(0)
{
int c = 0;
if (jpgimg.buffer != nullptr && jpgimg.len >0)
rgb = stbi_load_from_memory(jpgimg.buffer, jpgimg.len, &width, &height, &c, 3);
}
FrameBufferImage::~FrameBufferImage() {
if (rgb!=nullptr)
delete rgb;
}
FrameBufferImage::jpegBuffer FrameBufferImage::getJpeg()
{
jpegBuffer jpgimg;
// if we hold a valid image
if (rgb!=nullptr && width>0 && height>0) {
// allocate JPEG buffer
// (NB: JPEG will need less than this but we can't know before...)
jpgimg.buffer = (unsigned char *) malloc( width * height * 3 * sizeof(unsigned char));
stbi_write_jpg_to_func( [](void *context, void *data, int size)
{
memcpy(((FrameBufferImage::jpegBuffer*)context)->buffer + ((FrameBufferImage::jpegBuffer*)context)->len, data, size);
((FrameBufferImage::jpegBuffer*)context)->len += size;
}
,&jpgimg, width, height, 3, rgb, FBI_JPEG_QUALITY);
}
return jpgimg;
}
FrameBufferImage *FrameBuffer::image(){
FrameBufferImage *img = nullptr;
// not ready
if (!framebufferid_)
return img;
// allocate image
img = new FrameBufferImage(attrib_.viewport.x, attrib_.viewport.y);
// get pixels into image
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); // set buffer target readpixel
readPixels(img->rgb);
return img;
}
bool FrameBuffer::fill(FrameBufferImage *image)
{
if (!framebufferid_)
init();
// not compatible for RGB
if (use_alpha_ || use_multi_sampling_)
return false;
// invalid image
if ( image == nullptr ||
image->rgb==nullptr ||
image->width !=attrib_.viewport.x ||
image->height!=attrib_.viewport.y )
return false;
// fill texture with image
glBindTexture(GL_TEXTURE_2D, textureid_);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image->width, image->height,
GL_RGB, GL_UNSIGNED_BYTE, image->rgb);
return true;
}

View File

@@ -1,357 +0,0 @@
#include <algorithm>
// Desktop OpenGL function loader
#include <glad/glad.h>
// gstreamer
#include <gst/gstformat.h>
#include <gst/video/video.h>
#include "defines.h"
#include "Log.h"
#include "GstToolkit.h"
#include "FrameBuffer.h"
#include "FrameGrabber.h"
FrameGrabbing::FrameGrabbing(): pbo_index_(0), pbo_next_index_(0), size_(0), width_(0), height_(0), use_alpha_(0), caps_(nullptr)
{
pbo_[0] = 0;
pbo_[1] = 0;
}
FrameGrabbing::~FrameGrabbing()
{
// stop and delete all frame grabbers
clearAll();
// cleanup
if (caps_!=nullptr)
gst_caps_unref (caps_);
if (pbo_[0])
glDeleteBuffers(2, pbo_);
}
void FrameGrabbing::add(FrameGrabber *rec)
{
if (rec != nullptr)
grabbers_.push_back(rec);
}
FrameGrabber *FrameGrabbing::front()
{
if (grabbers_.empty())
return nullptr;
else
return grabbers_.front();
}
struct fgId: public std::unary_function<FrameGrabber*, bool>
{
inline bool operator()(const FrameGrabber* elem) const {
return (elem && elem->id() == _id);
}
fgId(uint64_t id) : _id(id) { }
private:
uint64_t _id;
};
FrameGrabber *FrameGrabbing::get(uint64_t id)
{
if (id > 0 && grabbers_.size() > 0 )
{
std::list<FrameGrabber *>::iterator iter = std::find_if(grabbers_.begin(), grabbers_.end(), fgId(id));
if (iter != grabbers_.end())
return (*iter);
}
return nullptr;
}
void FrameGrabbing::stopAll()
{
std::list<FrameGrabber *>::iterator iter;
for (iter=grabbers_.begin(); iter != grabbers_.end(); iter++ )
(*iter)->stop();
}
void FrameGrabbing::clearAll()
{
std::list<FrameGrabber *>::iterator iter;
for (iter=grabbers_.begin(); iter != grabbers_.end(); )
{
FrameGrabber *rec = *iter;
rec->stop();
iter = grabbers_.erase(iter);
delete rec;
}
}
void FrameGrabbing::grabFrame(FrameBuffer *frame_buffer, float dt)
{
if (frame_buffer == nullptr)
return;
// if different frame buffer from previous frame
if ( frame_buffer->width() != width_ ||
frame_buffer->height() != height_ ||
frame_buffer->use_alpha() != use_alpha_) {
// define stream properties
width_ = frame_buffer->width();
height_ = frame_buffer->height();
use_alpha_ = frame_buffer->use_alpha();
size_ = width_ * height_ * (use_alpha_ ? 4 : 3);
// first time initialization
if ( pbo_[0] == 0 )
glGenBuffers(2, pbo_);
// re-affect pixel buffer object
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo_[1]);
glBufferData(GL_PIXEL_PACK_BUFFER, size_, NULL, GL_STREAM_READ);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo_[0]);
glBufferData(GL_PIXEL_PACK_BUFFER, size_, NULL, GL_STREAM_READ);
// reset indices
pbo_index_ = 0;
pbo_next_index_ = 0;
// new caps
if (caps_!=nullptr)
gst_caps_unref (caps_);
caps_ = gst_caps_new_simple ("video/x-raw",
"format", G_TYPE_STRING, use_alpha_ ? "RGBA" : "RGB",
"width", G_TYPE_INT, width_,
"height", G_TYPE_INT, height_,
"framerate", GST_TYPE_FRACTION, 30, 1,
NULL);
}
// fill a frame in buffer
if (!grabbers_.empty() && size_ > 0) {
GstBuffer *buffer = nullptr;
// set buffer target for writing in a new frame
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo_[pbo_index_]);
#ifdef USE_GLREADPIXEL
// get frame
frame_buffer->readPixels();
#else
glBindTexture(GL_TEXTURE_2D, frame_buffer->texture());
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
#endif
// update case ; alternating indices
if ( pbo_next_index_ != pbo_index_ ) {
// set buffer target for saving the frame
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo_[pbo_next_index_]);
// new buffer
buffer = gst_buffer_new_and_alloc (size_);
// map gst buffer into a memory WRITE target
GstMapInfo map;
gst_buffer_map (buffer, &map, GST_MAP_WRITE);
// map PBO pixels into a memory READ pointer
unsigned char* ptr = (unsigned char*) glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
// transfer pixels from PBO memory to buffer memory
if (NULL != ptr)
memmove(map.data, ptr, size_);
// un-map
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
gst_buffer_unmap (buffer, &map);
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
// alternate indices
pbo_next_index_ = pbo_index_;
pbo_index_ = (pbo_index_ + 1) % 2;
// a frame was successfully grabbed
if (buffer != nullptr) {
// give the frame to all recorders
std::list<FrameGrabber *>::iterator iter = grabbers_.begin();
while (iter != grabbers_.end())
{
FrameGrabber *rec = *iter;
rec->addFrame(buffer, caps_, dt);
if (rec->finished()) {
iter = grabbers_.erase(iter);
delete rec;
}
else
iter++;
}
// unref / free the frame
gst_buffer_unref(buffer);
}
}
}
FrameGrabber::FrameGrabber(): finished_(false), active_(false), accept_buffer_(false),
pipeline_(nullptr), src_(nullptr), caps_(nullptr), timestamp_(0)
{
// unique id
id_ = GlmToolkit::uniqueId();
// configure fix parameter
frame_duration_ = gst_util_uint64_scale_int (1, GST_SECOND, 30); // 30 FPS
timeframe_ = 2 * frame_duration_;
}
FrameGrabber::~FrameGrabber()
{
if (src_ != nullptr)
gst_object_unref (src_);
if (caps_ != nullptr)
gst_caps_unref (caps_);
if (pipeline_ != nullptr) {
gst_element_set_state (pipeline_, GST_STATE_NULL);
gst_object_unref (pipeline_);
}
}
bool FrameGrabber::finished() const
{
return finished_;
}
bool FrameGrabber::busy() const
{
if (active_)
return accept_buffer_ ? true : false;
else
return false;
}
double FrameGrabber::duration() const
{
return gst_guint64_to_gdouble( GST_TIME_AS_MSECONDS(timestamp_) ) / 1000.0;
}
void FrameGrabber::stop ()
{
// send end of stream
gst_app_src_end_of_stream (src_);
// stop recording
active_ = false;
}
std::string FrameGrabber::info() const
{
if (active_)
return GstToolkit::time_to_string(timestamp_);
else
return "Inactive";
}
// appsrc needs data and we should start sending
void FrameGrabber::callback_need_data (GstAppSrc *, guint , gpointer p)
{
FrameGrabber *grabber = static_cast<FrameGrabber *>(p);
if (grabber)
grabber->accept_buffer_ = true;
}
// appsrc has enough data and we can stop sending
void FrameGrabber::callback_enough_data (GstAppSrc *, gpointer p)
{
FrameGrabber *grabber = static_cast<FrameGrabber *>(p);
if (grabber)
grabber->accept_buffer_ = false;
}
void FrameGrabber::addFrame (GstBuffer *buffer, GstCaps *caps, float dt)
{
// ignore
if (buffer == nullptr)
return;
// first time initialization
if (pipeline_ == nullptr)
init(caps);
// cancel if finished
if (finished_)
return;
// stop if an incompatilble frame buffer given
if ( !gst_caps_is_equal( caps_, caps ))
{
stop();
// Log::Warning("FrameGrabber interrupted: new session (%s)\nincompatible with recording (%s)", gst_caps_to_string(frame.caps), gst_caps_to_string(caps_));
Log::Warning("FrameGrabber interrupted because the resolution changed.");
}
// store a frame if recording is active
if (active_)
{
// calculate dt in ns
timeframe_ += gst_gdouble_to_guint64( dt * 1000000.f );
// if time is passed one frame duration (with 10% margin)
// and if the encoder accepts data
if ( timeframe_ > frame_duration_ - 3000000 && accept_buffer_) {
// set timing of buffer
buffer->pts = timestamp_;
buffer->duration = frame_duration_;
// increment ref counter to make sure the frame remains available
gst_buffer_ref(buffer);
// push
gst_app_src_push_buffer (src_, buffer);
// NB: buffer will be unrefed by the appsrc
accept_buffer_ = false;
// next timestamp
timestamp_ += frame_duration_;
// restart frame counter
timeframe_ = 0;
}
}
// did the recording terminate with sink receiving end-of-stream ?
else {
if (!finished_)
{
// Wait for EOS message
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline_));
GstMessage *msg = gst_bus_poll(bus, GST_MESSAGE_EOS, GST_TIME_AS_USECONDS(1));
// received EOS
if (msg) {
// stop the pipeline
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_NULL);
if (ret == GST_STATE_CHANGE_FAILURE)
Log::Warning("FrameGrabber Could not stop.");
finished_ = true;
}
}
}
if (finished_)
terminate();
}

View File

@@ -1,128 +0,0 @@
#ifndef FRAMEGRABBER_H
#define FRAMEGRABBER_H
#include <atomic>
#include <list>
#include <string>
#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
#include "GlmToolkit.h"
// use glReadPixel or glGetTextImage
// read pixels & pbo should be the fastest
// https://stackoverflow.com/questions/38140527/glreadpixels-vs-glgetteximage
#define USE_GLREADPIXEL
class FrameBuffer;
/**
* @brief The FrameGrabber class defines the base class for all recorders
* used to save images or videos from a frame buffer.
*
* Every subclass shall at least implement init() and terminate()
*
* The FrameGrabbing manager calls addFrame() for all its grabbers.
*/
class FrameGrabber
{
friend class FrameGrabbing;
uint64_t id_;
public:
FrameGrabber();
virtual ~FrameGrabber();
inline uint64_t id() const { return id_; }
virtual void stop();
virtual std::string info() const;
virtual double duration() const;
virtual bool finished() const;
virtual bool busy() const;
protected:
// only FrameGrabbing manager can add frame
virtual void addFrame(GstBuffer *buffer, GstCaps *caps, float dt);
// only addFrame method shall call those
virtual void init(GstCaps *caps) = 0;
virtual void terminate() = 0;
// thread-safe testing termination
std::atomic<bool> finished_;
std::atomic<bool> active_;
std::atomic<bool> accept_buffer_;
// gstreamer pipeline
GstElement *pipeline_;
GstAppSrc *src_;
GstCaps *caps_;
GstClockTime timeframe_;
GstClockTime timestamp_;
GstClockTime frame_duration_;
// gstreamer callbacks
static void callback_need_data (GstAppSrc *, guint, gpointer user_data);
static void callback_enough_data (GstAppSrc *, gpointer user_data);
};
/**
* @brief The FrameGrabbing class manages all frame grabbers
*
* Session calls grabFrame after each render
*
*/
class FrameGrabbing
{
friend class Mixer;
// Private Constructor
FrameGrabbing();
FrameGrabbing(FrameGrabbing const& copy); // Not Implemented
FrameGrabbing& operator=(FrameGrabbing const& copy); // Not Implemented
public:
static FrameGrabbing& manager()
{
// The only instance
static FrameGrabbing _instance;
return _instance;
}
~FrameGrabbing();
inline uint width() const { return width_; }
inline uint height() const { return height_; }
void add(FrameGrabber *rec);
FrameGrabber *front();
FrameGrabber *get(uint64_t id);
void stopAll();
void clearAll();
protected:
// only for friend Session
void grabFrame(FrameBuffer *frame_buffer, float dt);
private:
std::list<FrameGrabber *> grabbers_;
guint pbo_[2];
guint pbo_index_;
guint pbo_next_index_;
guint size_;
guint width_;
guint height_;
bool use_alpha_;
GstCaps *caps_;
};
#endif // FRAMEGRABBER_H

View File

@@ -1,154 +0,0 @@
#include <algorithm>
#include "Log.h"
#include "Scene.h"
#include "GarbageVisitor.h"
GarbageVisitor::GarbageVisitor(Node *nodetocollect) : Visitor()
{
targets_.push_front(nodetocollect);
current_ = nullptr;
found_ = false;
}
GarbageVisitor::GarbageVisitor(Source *sourcetocollect) : Visitor()
{
targets_.push_front(sourcetocollect->group(View::MIXING));
targets_.push_front(sourcetocollect->group(View::GEOMETRY));
targets_.push_front(sourcetocollect->group(View::RENDERING));
current_ = nullptr;
found_ = false;
}
void GarbageVisitor::visit(Node &n)
{
// found the target
// if (n.id() == target_->id())
// if ( &n == target_ )
if ( std::count(targets_.begin(), targets_.end(), &n) )
{
// end recursive
found_ = true;
// take the node out of the Tree
if (current_)
current_->detach(&n);
}
}
GarbageVisitor::~GarbageVisitor()
{
// actually delete the Node
}
void GarbageVisitor::visit(Group &n)
{
// no need to go further if the node was found (or is this group)
if (found_)
return;
// current group
current_ = &n;
// loop over members of a group
// and stop when found
for (NodeSet::iterator node = n.begin(); !found_ && node != n.end(); node++) {
// visit the child node
(*node)->accept(*this);
// un-stack recursive browsing
current_ = &n;
}
}
void GarbageVisitor::visit(Scene &n)
{
n.root()->accept(*this);
}
void GarbageVisitor::visit(Switch &n)
{
}
void GarbageVisitor::visit(Primitive &n)
{
}
void GarbageVisitor::visit(Surface &n)
{
}
void GarbageVisitor::visit(ImageSurface &n)
{
}
void GarbageVisitor::visit(FrameBufferSurface &n)
{
}
void GarbageVisitor::visit(MediaSurface &n)
{
}
void GarbageVisitor::visit(MediaPlayer &n)
{
}
void GarbageVisitor::visit(Shader &n)
{
}
void GarbageVisitor::visit(ImageShader &n)
{
}
void GarbageVisitor::visit(ImageProcessingShader &n)
{
}
void GarbageVisitor::visit(LineStrip &n)
{
}
void GarbageVisitor::visit(LineSquare &)
{
}
void GarbageVisitor::visit(Mesh &n)
{
}
void GarbageVisitor::visit(Frame &n)
{
}
void GarbageVisitor::visit (Source& s)
{
}
void GarbageVisitor::visit (MediaSource& s)
{
}

View File

@@ -1,46 +0,0 @@
#ifndef GARBAGEVISITOR_H
#define GARBAGEVISITOR_H
#include <list>
#include "Source.h"
#include "Visitor.h"
class GarbageVisitor : public Visitor
{
Group *current_;
std::list<Node *> targets_;
bool found_;
public:
GarbageVisitor(Source *sourcetocollect);
GarbageVisitor(Node *nodetocollect);
~GarbageVisitor();
void visit(Scene& n) override;
void visit(Node& n) override;
void visit(Group& n) override;
void visit(Switch& n) override;
void visit(Primitive& n) override;
void visit(Surface& n) override;
void visit(ImageSurface& n) override;
void visit(MediaSurface& n) override;
void visit(FrameBufferSurface& n) override;
void visit(LineStrip& n) override;
void visit(LineSquare&) override;
void visit(Mesh& n) override;
void visit(Frame& n) override;
void visit(MediaPlayer& n) override;
void visit(Shader& n) override;
void visit(ImageShader& n) override;
void visit(ImageProcessingShader& n) override;
void visit (Source& s) override;
void visit (MediaSource& s) override;
};
#endif // GARBAGEVISITOR_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,46 +0,0 @@
#ifndef GEOMETRYVIEW_H
#define GEOMETRYVIEW_H
#include "View.h"
class GeometryView : public View
{
public:
GeometryView();
void draw () override;
void update (float dt) override;
void resize (int) override;
int size () override;
bool canSelect(Source *) override;
std::pair<Node *, glm::vec2> pick(glm::vec2 P) override;
Cursor grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2> pick) override;
void terminate() override;
void arrow (glm::vec2) override;
private:
Surface *output_surface_;
Node *overlay_position_;
Node *overlay_position_cross_;
Symbol *overlay_rotation_;
Symbol *overlay_rotation_fix_;
Group *overlay_rotation_clock_;
Symbol *overlay_rotation_clock_tic_;
Node *overlay_rotation_clock_hand_;
Symbol *overlay_scaling_;
Symbol *overlay_scaling_cross_;
Node *overlay_scaling_grid_;
Node *overlay_crop_;
void updateSelectionOverlay() override;
bool overlay_selection_active_;
Group *overlay_selection_stored_status_;
Handles *overlay_selection_scale_;
Handles *overlay_selection_rotate_;
void applySelectionTransform(glm::mat4 M);
};
#endif // GEOMETRYVIEW_H

View File

@@ -1,172 +0,0 @@
#include <sstream>
#include <iomanip>
using namespace std;
#include <gst/gl/gl.h>
#include "GstToolkit.h"
string GstToolkit::time_to_string(guint64 t, time_string_mode m)
{
if (t == GST_CLOCK_TIME_NONE) {
switch (m) {
case TIME_STRING_FIXED:
return "00:00:00.00";
case TIME_STRING_MINIMAL:
return "0.0";
default:
return "00.00";
}
}
guint ms = GST_TIME_AS_MSECONDS(t);
guint s = ms / 1000;
ostringstream oss;
// MINIMAL: keep only the 2 higher values (most significant)
if (m == TIME_STRING_MINIMAL) {
int count = 0;
if (s / 3600) {
oss << s / 3600 << ':';
count++;
}
if ((s % 3600) / 60) {
oss << (s % 3600) / 60 << ':';
count++;
}
if (count < 2) {
oss << setw(count > 0 ? 2 : 1) << setfill('0') << (s % 3600) % 60;
count++;
}
if (count < 2 )
oss << '.'<< setw(1) << setfill('0') << (ms % 1000) / 10;
}
else {
// TIME_STRING_FIXED : fixed length string (11 chars) HH:mm:ss.ii"
// TIME_STRING_RIGHT : always show the right part (seconds), not the min or hours if none
if (m == TIME_STRING_FIXED || (s / 3600) )
oss << setw(2) << setfill('0') << s / 3600 << ':';
if (m == TIME_STRING_FIXED || ((s % 3600) / 60) )
oss << setw(2) << setfill('0') << (s % 3600) / 60 << ':';
oss << setw(2) << setfill('0') << (s % 3600) % 60 << '.';
oss << setw(2) << setfill('0') << (ms % 1000) / 10;
}
return oss.str();
}
std::string GstToolkit::filename_to_uri(std::string path)
{
// set uri to open
gchar *uritmp = gst_filename_to_uri(path.c_str(), NULL);
std::string uri( uritmp );
g_free(uritmp);
return uri;
}
list<string> GstToolkit::all_plugins()
{
list<string> pluginlist;
GList *l, *g;
l = gst_registry_get_plugin_list (gst_registry_get ());
for (g = l; g; g = g->next) {
GstPlugin *plugin = GST_PLUGIN (g->data);
pluginlist.push_front( string( gst_plugin_get_name (plugin) ) );
}
gst_plugin_list_free (l);
return pluginlist;
}
list<string> GstToolkit::all_plugin_features(string pluginname)
{
list<string> featurelist;
GList *l, *g;
l = gst_registry_get_feature_list_by_plugin (gst_registry_get (), pluginname.c_str());
for (g = l; g; g = g->next) {
GstPluginFeature *feature = GST_PLUGIN_FEATURE (g->data);
featurelist.push_front( string( gst_plugin_feature_get_name (feature) ) );
}
gst_plugin_feature_list_free (l);
return featurelist;
}
bool GstToolkit::enable_feature (string name, bool enable) {
GstRegistry *registry = NULL;
GstElementFactory *factory = NULL;
registry = gst_registry_get();
if (!registry) return false;
factory = gst_element_factory_find (name.c_str());
if (!factory) return false;
if (enable) {
gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), GST_RANK_PRIMARY + 1);
}
else {
gst_plugin_feature_set_rank (GST_PLUGIN_FEATURE (factory), GST_RANK_NONE);
}
gst_registry_add_feature (registry, GST_PLUGIN_FEATURE (factory));
return true;
}
string GstToolkit::gst_version()
{
std::ostringstream oss;
oss << GST_VERSION_MAJOR << '.' << GST_VERSION_MINOR << '.';
oss << std::setw(2) << setfill('0') << GST_VERSION_MICRO ;
if (GST_VERSION_NANO > 0)
oss << ( (GST_VERSION_NANO < 2 ) ? " - (CVS)" : " - (Prerelease)");
return oss.str();
}
// see https://developer.ridgerun.com/wiki/index.php?title=GStreamer_modify_the_elements_rank
std::list<std::string> GstToolkit::enable_gpu_decoding_plugins(bool enable)
{
static list<string> pluginslist;
static GstRegistry* plugins_register = nullptr;
if ( plugins_register == nullptr ) {
plugins_register = gst_registry_get();
#if GST_GL_HAVE_PLATFORM_GLX
// https://gstreamer.freedesktop.org/documentation/nvcodec/index.html?gi-language=c#plugin-nvcodec
const char *plugins[6] = { "nvh264dec", "nvh265dec", "nvmpeg2videodec",
"nvmpeg4videodec", "nvvp8dec", "nvvp9dec" };
const int N = 6;
#elif GST_GL_HAVE_PLATFORM_CGL
const char *plugins[1] = { "vtdec_hw" };
const int N = 1;
#else
const char *plugins[0] = { };
const int N = 0;
#endif
for (int i = 0; i < N; i++) {
GstPluginFeature* feature = gst_registry_lookup_feature(plugins_register, plugins[i]);
if(feature != NULL) {
pluginslist.push_front( string( plugins[i] ) );
gst_plugin_feature_set_rank(feature, enable ? GST_RANK_PRIMARY + 1 : GST_RANK_MARGINAL);
gst_object_unref(feature);
}
}
}
return pluginslist;
}

View File

@@ -1,31 +0,0 @@
#ifndef __GSTGUI_TOOLKIT_H_
#define __GSTGUI_TOOLKIT_H_
#include <gst/gst.h>
#include <string>
#include <list>
namespace GstToolkit
{
typedef enum {
TIME_STRING_FIXED = 0,
TIME_STRING_ADJUSTED,
TIME_STRING_MINIMAL
} time_string_mode;
std::string time_to_string(guint64 t, time_string_mode m = TIME_STRING_ADJUSTED);
std::string filename_to_uri(std::string filename);
std::string gst_version();
std::list<std::string> all_plugins();
std::list<std::string> enable_gpu_decoding_plugins(bool enable = true);
std::list<std::string> all_plugin_features(std::string pluginname);
bool enable_feature (std::string name, bool enable);
}
#endif // __GSTGUI_TOOLKIT_H_

File diff suppressed because it is too large Load Diff

View File

@@ -1,69 +0,0 @@
#ifndef __IMGUI_TOOLKIT_H_
#define __IMGUI_TOOLKIT_H_
#include <glib.h>
#include <string>
#include <list>
#include <vector>
#include <utility>
#include "rsc/fonts/IconsFontAwesome5.h"
namespace ImGuiToolkit
{
// Icons from resource icon.dds
void Icon (int i, int j, bool enabled = true);
bool IconButton (int i, int j, const char *tooltips = nullptr);
bool IconToggle (int i, int j, int i_toggle, int j_toggle, bool* toggle, const char *tooltips[] = nullptr);
void ShowIconsWindow(bool* p_open);
// icon buttons
bool ButtonIcon (int i, int j, const char* tooltip = nullptr);
bool ButtonIconToggle (int i, int j, int i_toggle, int j_toggle, bool* toggle);
bool ButtonIconMultistate (std::vector<std::pair<int, int> > icons, int* state);
bool ComboIcon (std::vector<std::pair<int, int> > icons, std::vector<std::string> labels, int* state);
// utility buttons
bool ButtonToggle (const char* label, bool* toggle);
void ButtonSwitch (const char* label, bool* toggle , const char *help = nullptr);
void ButtonOpenUrl (const char* url, const ImVec2& size_arg = ImVec2(0,0));
void ToolTip (const char* desc, const char* shortcut = nullptr);
void HelpMarker (const char* desc, const char* icon = ICON_FA_QUESTION_CIRCLE, const char* shortcut = nullptr);
void HelpIcon (const char* desc, int i = 19, int j = 5, const char* shortcut = nullptr);
// utility sliders
bool TimelineSlider (const char* label, guint64 *time, guint64 start, guint64 end, guint64 step, const float width);
bool InvisibleSliderInt(const char* label, uint *index, uint min, uint max, const ImVec2 size);
bool EditPlotLines(const char* label, float *array, int values_count, float values_min, float values_max, const ImVec2 size);
bool EditPlotHistoLines(const char* label, float *histogram_array, float *lines_array, int values_count, float values_min, float values_max, bool *released, const ImVec2 size);
// fonts from ressources 'fonts/'
typedef enum {
FONT_DEFAULT =0,
FONT_BOLD,
FONT_ITALIC,
FONT_MONO,
FONT_LARGE
} font_style;
void SetFont (font_style type, const std::string &ttf_font_name, int pointsize, int oversample = 2);
void PushFont (font_style type);
void WindowText(const char* window_name, ImVec2 window_pos, const char* text);
bool WindowButton(const char* window_name, ImVec2 window_pos, const char* text);
void WindowDragFloat(const char* window_name, ImVec2 window_pos, float* v, float v_speed, float v_min, float v_max, const char* format);
// color of gui items
typedef enum {
ACCENT_BLUE =0,
ACCENT_ORANGE,
ACCENT_GREY
} accent_color;
void SetAccentColor (accent_color color);
struct ImVec4 HighlightColor (bool active = true);
void ShowStats (bool* p_open, int* p_corner, bool* p_timer);
}
#endif // __IMGUI_TOOLKIT_H_

View File

@@ -1,628 +0,0 @@
#include "ImGuiVisitor.h"
#include <vector>
#include <algorithm>
#include <sstream>
#include <iomanip>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/constants.hpp>
#include "defines.h"
#include "Log.h"
#include "Scene.h"
#include "Primitives.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "MediaPlayer.h"
#include "MediaSource.h"
#include "SessionSource.h"
#include "PatternSource.h"
#include "DeviceSource.h"
#include "NetworkSource.h"
#include "Settings.h"
#include "Mixer.h"
#include "ActionManager.h"
#include "imgui.h"
#include "ImGuiToolkit.h"
#include "UserInterfaceManager.h"
#include "SystemToolkit.h"
ImGuiVisitor::ImGuiVisitor()
{
}
void ImGuiVisitor::visit(Node &n)
{
}
void ImGuiVisitor::visit(Group &n)
{
// MODEL VIEW
ImGui::PushID(std::to_string(n.id()).c_str());
if (ImGuiToolkit::ButtonIcon(1, 16)) {
n.translation_.x = 0.f;
n.translation_.y = 0.f;
n.rotation_.z = 0.f;
n.scale_.x = 1.f;
n.scale_.y = 1.f;
Action::manager().store("Geometry Reset");
}
ImGui::SameLine(0, 10);
ImGui::Text("Geometry");
if (ImGuiToolkit::ButtonIcon(6, 15)) {
n.translation_.x = 0.f;
n.translation_.y = 0.f;
Action::manager().store("Position 0.0, 0.0");
}
ImGui::SameLine(0, 10);
float translation[2] = { n.translation_.x, n.translation_.y};
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if ( ImGui::SliderFloat2("Position", translation, -5.0, 5.0) )
{
n.translation_.x = translation[0];
n.translation_.y = translation[1];
}
if (ImGui::IsItemDeactivatedAfterEdit()){
std::ostringstream oss;
oss << "Position " << std::setprecision(3) << n.translation_.x << ", " << n.translation_.y;
Action::manager().store(oss.str());
}
if (ImGuiToolkit::ButtonIcon(3, 15)) {
n.scale_.x = 1.f;
n.scale_.y = 1.f;
Action::manager().store("Scale 1.0 x 1.0");
}
ImGui::SameLine(0, 10);
float scale[2] = { n.scale_.x, n.scale_.y} ;
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if ( ImGui::SliderFloat2("Scale", scale, -MAX_SCALE, MAX_SCALE, "%.2f") )
{
n.scale_.x = CLAMP_SCALE(scale[0]);
n.scale_.y = CLAMP_SCALE(scale[1]);
}
if (ImGui::IsItemDeactivatedAfterEdit()){
std::ostringstream oss;
oss << "Scale " << std::setprecision(3) << n.scale_.x << " x " << n.scale_.y;
Action::manager().store(oss.str());
}
if (ImGuiToolkit::ButtonIcon(18, 9)){
n.rotation_.z = 0.f;
Action::manager().store("Angle 0.0");
}
ImGui::SameLine(0, 10);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::SliderAngle("Angle", &(n.rotation_.z), -180.f, 180.f) ;
if (ImGui::IsItemDeactivatedAfterEdit()) {
std::ostringstream oss;
oss << "Angle " << std::setprecision(3) << n.rotation_.z * 180.f / M_PI;
Action::manager().store(oss.str());
}
ImGui::PopID();
// spacing
ImGui::Spacing();
}
void ImGuiVisitor::visit(Switch &n)
{
if (n.numChildren()>0)
n.activeChild()->accept(*this);
}
void ImGuiVisitor::visit(Scene &n)
{
ImGui::SetNextItemOpen(true, ImGuiCond_Once);
if (ImGui::CollapsingHeader("Scene Property Tree"))
{
n.root()->accept(*this);
}
}
void ImGuiVisitor::visit(Primitive &n)
{
ImGui::PushID(std::to_string(n.id()).c_str());
ImGui::Text("Primitive %d");
n.shader()->accept(*this);
ImGui::PopID();
}
void ImGuiVisitor::visit(FrameBufferSurface &n)
{
ImGui::Text("Framebuffer");
}
void ImGuiVisitor::visit(MediaSurface &n)
{
ImGui::Text("%s", n.path().c_str());
if (n.mediaPlayer())
n.mediaPlayer()->accept(*this);
}
void ImGuiVisitor::visit(MediaPlayer &n)
{
ImGui::Text("Media Player");
}
void ImGuiVisitor::visit(Shader &n)
{
ImGui::PushID(std::to_string(n.id()).c_str());
// Base color
// if (ImGuiToolkit::ButtonIcon(10, 2)) {
// n.blending = Shader::BLEND_OPACITY;
// n.color = glm::vec4(1.f, 1.f, 1.f, 1.f);
// }
// ImGui::SameLine(0, 10);
// ImGui::ColorEdit3("Color", glm::value_ptr(n.color), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel ) ;
// ImGui::SameLine(0, 5);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
int mode = n.blending;
if (ImGui::Combo("Blending", &mode, "Normal\0Screen\0Subtract\0Multiply\0Soft light"
"\0Hard light\0Soft subtract\0Lighten only\0") ) {
n.blending = Shader::BlendMode(mode);
std::ostringstream oss;
oss << "Blending ";
switch(n.blending) {
case Shader::BLEND_OPACITY:
oss<<"Normal";
break;
case Shader::BLEND_SCREEN:
oss<<"Screen";
break;
case Shader::BLEND_SUBTRACT:
oss<<"Subtract";
break;
case Shader::BLEND_MULTIPLY:
oss<<"Multiply";
break;
case Shader::BLEND_HARD_LIGHT:
oss<<"Hard light";
break;
case Shader::BLEND_SOFT_LIGHT:
oss<<"Soft light";
break;
case Shader::BLEND_SOFT_SUBTRACT:
oss<<"Soft subtract";
break;
case Shader::BLEND_LIGHTEN_ONLY:
oss<<"Lighten only";
break;
case Shader::BLEND_NONE:
oss<<"None";
break;
}
Action::manager().store(oss.str());
}
ImGui::PopID();
}
//void ImGuiVisitor::visit(ImageShader &n)
//{
// ImGui::PushID(std::to_string(n.id()).c_str());
// // get index of the mask used in this ImageShader
// int item_current = n.mask;
//// if (ImGuiToolkit::ButtonIcon(10, 3)) n.mask = 0;
//// ImGui::SameLine(0, 10);
// ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
// // combo list of masks
// if ( ImGui::Combo("Mask", &item_current, ImageShader::mask_names, IM_ARRAYSIZE(ImageShader::mask_names) ) )
// {
// if (item_current < (int) ImageShader::mask_presets.size())
// n.mask = item_current;
// else {
// // TODO ask for custom mask
// }
// Action::manager().store("Mask "+ std::string(ImageShader::mask_names[n.mask]));
// }
// ImGui::PopID();
//}
void ImGuiVisitor::visit(ImageProcessingShader &n)
{
ImGui::PushID(std::to_string(n.id()).c_str());
if (ImGuiToolkit::ButtonIcon(6, 2)) {
ImageProcessingShader defaultvalues;
n = defaultvalues;
Action::manager().store("Reset Filters");
}
ImGui::SameLine(0, 10);
ImGui::Text("Filters");
if (ImGuiToolkit::ButtonIcon(6, 4)) {
n.gamma = glm::vec4(1.f, 1.f, 1.f, 1.f);
Action::manager().store("Gamma & Color");
}
ImGui::SameLine(0, 10);
ImGui::ColorEdit3("Gamma Color", glm::value_ptr(n.gamma), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel) ;
if (ImGui::IsItemDeactivatedAfterEdit())
Action::manager().store("Gamma Color changed");
ImGui::SameLine(0, 5);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::SliderFloat("Gamma", &n.gamma.w, 0.5f, 10.f, "%.2f", 2.f);
if (ImGui::IsItemDeactivatedAfterEdit()){
std::ostringstream oss;
oss << "Gamma " << std::setprecision(2) << n.gamma.w;
Action::manager().store(oss.str());
}
// ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
// ImGui::SliderFloat4("Levels", glm::value_ptr(n.levels), 0.0, 1.0);
if (ImGuiToolkit::ButtonIcon(5, 16)) {
n.brightness = 0.f;
n.contrast = 0.f;
Action::manager().store("B & C 0.0 0.0");
}
ImGui::SameLine(0, 10);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
float bc[2] = { n.brightness, n.contrast};
if ( ImGui::SliderFloat2("B & C", bc, -1.0, 1.0) )
{
n.brightness = bc[0];
n.contrast = bc[1];
}
if (ImGui::IsItemDeactivatedAfterEdit()){
std::ostringstream oss;
oss << "B & C " << std::setprecision(2) << n.brightness << " " << n.contrast;
Action::manager().store(oss.str());
}
if (ImGuiToolkit::ButtonIcon(9, 16)) {
n.saturation = 0.f;
Action::manager().store("Saturation 0.0");
}
ImGui::SameLine(0, 10);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::SliderFloat("Saturation", &n.saturation, -1.0, 1.0);
if (ImGui::IsItemDeactivatedAfterEdit()){
std::ostringstream oss;
oss << "Saturation " << std::setprecision(2) << n.saturation;
Action::manager().store(oss.str());
}
if (ImGuiToolkit::ButtonIcon(12, 4)) {
n.hueshift = 0.f;
Action::manager().store("Hue shift 0.0");
}
ImGui::SameLine(0, 10);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::SliderFloat("Hue shift", &n.hueshift, 0.0, 1.0);
if (ImGui::IsItemDeactivatedAfterEdit()){
std::ostringstream oss;
oss << "Hue shift " << std::setprecision(2) << n.hueshift;
Action::manager().store(oss.str());
}
if (ImGuiToolkit::ButtonIcon(18, 1)) {
n.nbColors = 0;
Action::manager().store("Posterize None");
}
ImGui::SameLine(0, 10);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::SliderInt("Posterize", &n.nbColors, 0, 16, n.nbColors == 0 ? "None" : "%d colors");
if (ImGui::IsItemDeactivatedAfterEdit()){
std::ostringstream oss;
oss << "Posterize ";
if (n.nbColors == 0) oss << "None"; else oss << n.nbColors;
Action::manager().store(oss.str());
}
if (ImGuiToolkit::ButtonIcon(8, 1)) {
n.threshold = 0.f;
Action::manager().store("Threshold None");
}
ImGui::SameLine(0, 10);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::SliderFloat("Threshold", &n.threshold, 0.0, 1.0, n.threshold < 0.001 ? "None" : "%.2f");
if (ImGui::IsItemDeactivatedAfterEdit()){
std::ostringstream oss;
oss << "Threshold ";
if (n.threshold < 0.001) oss << "None"; else oss << std::setprecision(2) << n.threshold;
Action::manager().store(oss.str());
}
if (ImGuiToolkit::ButtonIcon(3, 1)) {
n.lumakey = 0.f;
Action::manager().store("Lumakey 0.0");
}
ImGui::SameLine(0, 10);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::SliderFloat("Lumakey", &n.lumakey, 0.0, 1.0);
if (ImGui::IsItemDeactivatedAfterEdit()){
std::ostringstream oss;
oss << "Lumakey " << std::setprecision(2) << n.lumakey;
Action::manager().store(oss.str());
}
if (ImGuiToolkit::ButtonIcon(13, 4)) {
n.chromakey = glm::vec4(0.f, 0.8f, 0.f, 1.f);
n.chromadelta = 0.f;
Action::manager().store("Chromakey & Color Reset");
}
ImGui::SameLine(0, 10);
ImGui::ColorEdit3("Chroma color", glm::value_ptr(n.chromakey), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel ) ;
if (ImGui::IsItemDeactivatedAfterEdit())
Action::manager().store("Chroma color changed");
ImGui::SameLine(0, 5);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::SliderFloat("Chromakey", &n.chromadelta, 0.0, 1.0, n.chromadelta < 0.001 ? "None" : "Tolerance %.2f");
if (ImGui::IsItemDeactivatedAfterEdit()){
std::ostringstream oss;
oss << "Chromakey ";
if (n.chromadelta < 0.001) oss << "None"; else oss << std::setprecision(2) << n.chromadelta;
Action::manager().store(oss.str());
}
if (ImGuiToolkit::ButtonIcon(6, 16)) {
n.invert = 0;
Action::manager().store("Invert None");
}
ImGui::SameLine(0, 10);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::Combo("Invert", &n.invert, "None\0Invert Color\0Invert Luminance\0"))
Action::manager().store("Invert " + std::string(n.invert<1 ? "None": (n.invert>1 ? "Luminance" : "Color")));
if (ImGuiToolkit::ButtonIcon(1, 7)) {
n.filterid = 0;
Action::manager().store("Filter None");
}
ImGui::SameLine(0, 10);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::Combo("Filter", &n.filterid, ImageProcessingShader::filter_names, IM_ARRAYSIZE(ImageProcessingShader::filter_names) ) )
Action::manager().store("Filter " + std::string(ImageProcessingShader::filter_names[n.filterid]));
ImGui::PopID();
ImGui::Spacing();
}
void ImGuiVisitor::visit (Source& s)
{
ImGui::PushID(std::to_string(s.id()).c_str());
// blending
s.blendingShader()->accept(*this);
// preview
float preview_width = ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN;
float preview_height = 4.5f * ImGui::GetFrameHeightWithSpacing();
ImVec2 pos = ImGui::GetCursorPos(); // remember where we were...
float space = ImGui::GetStyle().ItemSpacing.y;
float width = preview_width;
float height = s.frame()->projectionArea().y * width / ( s.frame()->projectionArea().x * s.frame()->aspectRatio());
if (height > preview_height - space) {
height = preview_height - space;
width = height * s.frame()->aspectRatio() * ( s.frame()->projectionArea().x / s.frame()->projectionArea().y);
}
// centered image
ImGui::SetCursorPos( ImVec2(pos.x + 0.5f * (preview_width-width), pos.y + 0.5f * (preview_height-height-space)) );
ImGui::Image((void*)(uintptr_t) s.frame()->texture(), ImVec2(width, height));
// inform on visibility status
ImGui::SetCursorPos( ImVec2(preview_width + 20, pos.y ) );
if (s.active()) {
if (s.blendingShader()->color.a > 0.f)
ImGuiToolkit::HelpMarker("Visible", ICON_FA_EYE);
else
ImGuiToolkit::HelpMarker("Not visible", ICON_FA_EYE_SLASH);
}
else
ImGuiToolkit::HelpMarker("Inactive", ICON_FA_SNOWFLAKE);
// Inform on workspace
ImGui::SetCursorPos( ImVec2(preview_width + 20, pos.y + ImGui::GetFrameHeightWithSpacing()) );
if (s.workspace() == Source::BACKGROUND)
ImGuiToolkit::HelpIcon("in Background",10, 16);
else if (s.workspace() == Source::FOREGROUND)
ImGuiToolkit::HelpIcon("in Foreground",12, 16);
else
ImGuiToolkit::HelpIcon("in Workspace",11, 16);
// locking
ImGui::SetCursorPos( ImVec2(preview_width + 20, pos.y + 2.f * ImGui::GetFrameHeightWithSpacing()) );
const char *tooltip[2] = {"Unlocked", "Locked"};
bool l = s.locked();
if (ImGuiToolkit::IconToggle(15,6,17,6, &l, tooltip ) ) {
s.setLocked(l);
if (l) {
Mixer::selection().clear();
Action::manager().store(s.name() + std::string(": lock."));
}
else {
Mixer::selection().set(&s);
Action::manager().store(s.name() + std::string(": unlock."));
}
}
// toggle enable/disable image processing
bool on = s.imageProcessingEnabled();
ImGui::SetCursorPos( ImVec2(preview_width + 15, pos.y + 3.5f * ImGui::GetFrameHeightWithSpacing()) );
if ( ImGuiToolkit::ButtonToggle(ICON_FA_MAGIC, &on) ){
std::ostringstream oss;
oss << s.name() << ": " << ( on ? "Enable Filter" : "Disable Filter");
Action::manager().store(oss.str());
}
s.setImageProcessingEnabled(on);
ImGui::SetCursorPosY(pos.y + preview_height); // ...come back
// image processing pannel
if (s.imageProcessingEnabled())
s.processingShader()->accept(*this);
// geometry direct control
// s.groupNode(View::GEOMETRY)->accept(*this);
// s.groupNode((View::Mode) Settings::application.current_view)->accept(*this);
ImGui::PopID();
}
void ImGuiVisitor::visit (MediaSource& s)
{
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
ImGui::SameLine(0, 10);
if ( s.mediaplayer()->isImage() )
ImGui::Text("Image File");
else
ImGui::Text("Video File");
if ( ImGui::Button(IMGUI_TITLE_MEDIAPLAYER, ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
UserInterface::manager().showMediaPlayer( s.mediaplayer());
ImGuiToolkit::ButtonOpenUrl( SystemToolkit::path_filename(s.path()).c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
}
void ImGuiVisitor::visit (SessionFileSource& s)
{
if (s.session() == nullptr)
return;
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
ImGui::SameLine(0, 10);
ImGui::Text("Session File");
// ImGui::Text("%s", SystemToolkit::base_filename(s.path()).c_str());
if (ImGuiToolkit::ButtonIcon(3, 2)) s.session()->setFading(0.f);
float f = s.session()->fading();
ImGui::SameLine(0, 10);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::SliderFloat("Fading", &f, 0.0, 1.0, f < 0.001 ? "None" : "%.2f") )
s.session()->setFading(f);
if (ImGui::IsItemDeactivatedAfterEdit()){
std::ostringstream oss;
oss << s.name() << ": Fading " << std::setprecision(2) << f;
Action::manager().store(oss.str());
}
if ( ImGui::Button( ICON_FA_FILE_UPLOAD " Open Session", ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
Mixer::manager().set( s.detach() );
ImGuiToolkit::ButtonOpenUrl( SystemToolkit::path_filename(s.path()).c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
ImGui::Text("Contains %d sources.", s.session()->numSource());
if ( ImGui::Button( ICON_FA_FILE_EXPORT " Import", ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
Mixer::manager().import( &s );
}
void ImGuiVisitor::visit (SessionGroupSource& s)
{
if (s.session() == nullptr)
return;
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
ImGui::SameLine(0, 10);
ImGui::Text("Flat Sesion group");
ImGui::Text("Contains %d sources.", s.session()->numSource());
if ( ImGui::Button( ICON_FA_UPLOAD " Expand", ImVec2(IMGUI_RIGHT_ALIGN, 0)) ){
Mixer::manager().import( &s );
}
}
void ImGuiVisitor::visit (RenderSource& s)
{
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
ImGui::SameLine(0, 10);
ImGui::Text("Rendering Output");
if ( ImGui::Button(IMGUI_TITLE_PREVIEW, ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
Settings::application.widget.preview = true;
}
void ImGuiVisitor::visit (CloneSource& s)
{
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
ImGui::SameLine(0, 10);
ImGui::Text("Clone");
if ( ImGui::Button(s.origin()->name().c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
Mixer::manager().setCurrentSource(s.origin());
}
void ImGuiVisitor::visit (PatternSource& s)
{
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
ImGui::SameLine(0, 10);
ImGui::Text("Pattern");
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("##Patterns", Pattern::pattern_types[s.pattern()->type()].c_str()) )
{
for (uint p = 0; p < Pattern::pattern_types.size(); ++p){
if (ImGui::Selectable( Pattern::pattern_types[p].c_str() )) {
s.setPattern(p, s.pattern()->resolution());
std::ostringstream oss;
oss << s.name() << ": Pattern " << Pattern::pattern_types[p];
Action::manager().store(oss.str());
}
}
ImGui::EndCombo();
}
}
void ImGuiVisitor::visit (DeviceSource& s)
{
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
ImGui::SameLine(0, 10);
ImGui::Text("Device");
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("##Hardware", s.device().c_str()))
{
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);
std::ostringstream oss;
oss << s.name() << " Device " << namedev;
Action::manager().store(oss.str());
}
}
ImGui::EndCombo();
}
DeviceConfigSet confs = Device::manager().config( Device::manager().index(s.device().c_str()));
if ( !confs.empty()) {
DeviceConfig best = *confs.rbegin();
float fps = static_cast<float>(best.fps_numerator) / static_cast<float>(best.fps_denominator);
ImGui::Text("%s %s %dx%d@%.1ffps", best.stream.c_str(), best.format.c_str(), best.width, best.height, fps);
}
}
void ImGuiVisitor::visit (NetworkSource& s)
{
ImGuiToolkit::Icon(s.icon().x, s.icon().y);
ImGui::SameLine(0, 10);
ImGui::Text("Network stream");
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_STREAM, 0.9f));
ImGui::Text("%s", s.connection().c_str());
ImGui::PopStyleColor(1);
NetworkStream *ns = s.networkStream();
ImGui::Text(" - %s (%dx%d)\n - Server address %s", NetworkToolkit::protocol_name[ns->protocol()],
ns->resolution().x, ns->resolution().y, ns->serverAddress().c_str());
if ( ImGui::Button( ICON_FA_REPLY " Reconnect", ImVec2(IMGUI_RIGHT_ALIGN, 0)) )
{
// TODO : reload ?
s.setConnection(s.connection());
}
}

View File

@@ -1,151 +0,0 @@
#include <glad/glad.h>
#include "defines.h"
#include "Visitor.h"
#include "Resource.h"
#include "rsc/fonts/IconsFontAwesome5.h"
#include "ImageShader.h"
ShadingProgram imageShadingProgram("shaders/image.vs", "shaders/image.fs");
ShadingProgram imageAlphaProgram ("shaders/image.vs", "shaders/imageblending.fs");
const char* MaskShader::mask_names[3] = { ICON_FA_EXPAND, ICON_FA_EDIT, ICON_FA_SHAPES };
const char* MaskShader::mask_shapes[5] = { "Elipse", "Oblong", "Rectangle", "Horizontal", "Vertical" };
std::vector< ShadingProgram* > MaskShader::mask_programs;
ImageShader::ImageShader(): Shader(), stipple(0.0)
{
// static program shader
program_ = &imageShadingProgram;
// reset instance
reset();
}
void ImageShader::use()
{
Shader::use();
// set stippling
program_->setUniform("stipple", stipple);
// setup mask texture
glActiveTexture(GL_TEXTURE1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture (GL_TEXTURE_2D, mask_texture);
glActiveTexture(GL_TEXTURE0);
}
void ImageShader::reset()
{
Shader::reset();
// default mask
mask_texture = Resource::getTextureWhite();
// no stippling
stipple = 0.f;
}
void ImageShader::operator = (const ImageShader &S)
{
Shader::operator =(S);
mask_texture = S.mask_texture;
stipple = S.stipple;
}
void ImageShader::accept(Visitor& v) {
Shader::accept(v);
v.visit(*this);
}
AlphaShader::AlphaShader(): ImageShader()
{
// to inverse alpha mode, use dedicated shading program
program_ = &imageAlphaProgram;
// reset instance
reset();
blending = Shader::BLEND_NONE;
}
MaskShader::MaskShader(): Shader(), mode(0)
{
// first initialization
if ( mask_programs.empty() ) {
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/simple.fs"));
mask_programs.push_back(new ShadingProgram("shaders/image.vs", "shaders/mask_draw.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_elipse.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_round.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_box.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_horizontal.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_vertical.fs"));
}
// reset instance
reset();
// static program shader
program_ = mask_programs[0];
}
void MaskShader::use()
{
// select program to use
mode = CLAMP(mode, 0, 2);
shape = CLAMP(shape, 0, 4);
program_ = mode < 2 ? mask_programs[mode] : mask_programs[shape+2] ;
// actual use of shader program
Shader::use();
// shape parameters
size = shape < HORIZONTAL ? glm::max(glm::abs(size), glm::vec2(0.2)) : size;
program_->setUniform("size", size);
program_->setUniform("blur", blur);
// brush parameters
program_->setUniform("cursor", cursor);
program_->setUniform("brush", brush);
program_->setUniform("option", option);
program_->setUniform("effect", effect);
}
void MaskShader::reset()
{
Shader::reset();
// default mask
mode = 0;
// default shape
shape = 0;
blur = 0.5f;
size = glm::vec2(1.f, 1.f);
// default brush
cursor = glm::vec4(-10.f, -10.f, 1.f, 1.f);
brush = glm::vec3(0.5f, 0.1f, 0.f);
option = 0;
effect = 0;
}
void MaskShader::operator = (const MaskShader &S)
{
Shader::operator =(S);
mode = S.mode;
shape = S.shape;
blur = S.blur;
size = S.size;
}
void MaskShader::accept(Visitor& v) {
Shader::accept(v);
v.visit(*this);
}

View File

@@ -1,390 +0,0 @@
// Opengl
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/matrix_access.hpp>
#include <glm/gtx/vector_angle.hpp>
#include "imgui.h"
#include "ImGuiToolkit.h"
#include <string>
#include <sstream>
#include <iomanip>
#include "Mixer.h"
#include "defines.h"
#include "Settings.h"
#include "Decorations.h"
#include "UserInterfaceManager.h"
#include "BoundingBoxVisitor.h"
#include "ActionManager.h"
#include "Log.h"
#include "LayerView.h"
LayerView::LayerView() : View(LAYER), aspect_ratio(1.f)
{
// read default settings
if ( Settings::application.views[mode_].name.empty() ) {
// no settings found: store application default
Settings::application.views[mode_].name = "Layer";
scene.root()->scale_ = glm::vec3(LAYER_DEFAULT_SCALE, LAYER_DEFAULT_SCALE, 1.0f);
scene.root()->translation_ = glm::vec3(2.2f, 1.2f, 0.0f);
saveSettings();
}
else
restoreSettings();
// Geometry Scene background
frame_ = new Group;
Surface *rect = new Surface;
rect->shader()->color.a = 0.3f;
frame_->attach(rect);
Frame *border = new Frame(Frame::ROUND, Frame::THIN, Frame::PERSPECTIVE);
border->color = glm::vec4( COLOR_FRAME, 0.95f );
frame_->attach(border);
scene.bg()->attach(frame_);
persp_left_ = new Mesh("mesh/perspective_axis_left.ply");
persp_left_->shader()->color = glm::vec4( COLOR_FRAME_LIGHT, 1.f );
persp_left_->scale_.x = LAYER_PERSPECTIVE;
persp_left_->translation_.z = -0.1f;
persp_left_->translation_.x = -1.f;
scene.bg()->attach(persp_left_);
persp_right_ = new Mesh("mesh/perspective_axis_right.ply");
persp_right_->shader()->color = glm::vec4( COLOR_FRAME_LIGHT, 1.f );
persp_right_->scale_.x = LAYER_PERSPECTIVE;
persp_right_->translation_.z = -0.1f;
persp_right_->translation_.x = 1.f;
scene.bg()->attach(persp_right_);
}
void LayerView::draw()
{
View::draw();
// initialize the verification of the selection
static bool candidate_flatten_group = false;
// display popup menu
if (show_context_menu_ == MENU_SELECTION) {
// initialize the verification of the selection
candidate_flatten_group = true;
// start loop on selection
SourceList::iterator it = Mixer::selection().begin();
float depth_first = (*it)->depth();
for (; it != Mixer::selection().end(); it++) {
// test if selection is contiguous in layer (i.e. not interrupted)
SourceList::iterator inter = Mixer::manager().session()->find(depth_first, (*it)->depth());
if ( inter != Mixer::manager().session()->end() && !Mixer::selection().contains(*inter)){
// NOT a group: there is a source in the session that
// - is between two selected sources (in depth)
// - is not part of the selection
candidate_flatten_group = false;
break;
}
}
ImGui::OpenPopup( "LayerSelectionContextMenu" );
show_context_menu_ = MENU_NONE;
}
if (ImGui::BeginPopup("LayerSelectionContextMenu")) {
// colored context menu
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiToolkit::HighlightColor());
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.36f, 0.36f, 0.36f, 0.44f));
// special action of Mixing view
if (candidate_flatten_group){
if (ImGui::Selectable( ICON_FA_DOWNLOAD " Flatten" )) {
Mixer::manager().groupSelection();
}
}
else {
ImGui::TextDisabled( ICON_FA_DOWNLOAD " Flatten" );
}
ImGui::Separator();
// manipulation of sources in Mixing view
if (ImGui::Selectable( ICON_FA_ALIGN_CENTER " Distribute" )){
SourceList dsl = depth_sorted(Mixer::selection().getCopy());
SourceList::iterator it = dsl.begin();
float depth = (*it)->depth();
float depth_inc = (dsl.back()->depth() - depth) / static_cast<float>(Mixer::selection().size()-1);
for (it++; it != dsl.end(); it++) {
depth += depth_inc;
(*it)->setDepth(depth);
}
Action::manager().store(std::string("Selection Layer Distribute."));
View::need_deep_update_++;
}
if (ImGui::Selectable( ICON_FA_RULER_HORIZONTAL " Compress" )){
SourceList dsl = depth_sorted(Mixer::selection().getCopy());
SourceList::iterator it = dsl.begin();
float depth = (*it)->depth();
for (it++; it != dsl.end(); it++) {
depth += LAYER_STEP;
(*it)->setDepth(depth);
}
Action::manager().store(std::string("Selection Layer Compress."));
View::need_deep_update_++;
}
if (ImGui::Selectable( ICON_FA_EXCHANGE_ALT " Reverse order" )){
SourceList dsl = depth_sorted(Mixer::selection().getCopy());
SourceList::iterator it = dsl.begin();
SourceList::reverse_iterator rit = dsl.rbegin();
for (; it != dsl.end(); it++, rit++) {
(*it)->setDepth((*rit)->depth());
}
Action::manager().store(std::string("Selection Layer Reverse order."));
View::need_deep_update_++;
}
ImGui::PopStyleColor(2);
ImGui::EndPopup();
}
}
void LayerView::update(float dt)
{
View::update(dt);
// a more complete update is requested
if (View::need_deep_update_ > 0) {
// update rendering of render frame
FrameBuffer *output = Mixer::manager().session()->frame();
if (output){
// correct with aspect ratio
aspect_ratio = output->aspectRatio();
frame_->scale_.x = aspect_ratio;
persp_left_->translation_.x = -aspect_ratio;
persp_right_->translation_.x = aspect_ratio + 0.06;
}
}
if (Mixer::manager().view() == this )
{
// update the selection overlay
updateSelectionOverlay();
}
}
bool LayerView::canSelect(Source *s) {
return ( View::canSelect(s) && s->active() );
}
void LayerView::resize ( int scale )
{
float z = CLAMP(0.01f * (float) scale, 0.f, 1.f);
z *= z;
z *= LAYER_MAX_SCALE - LAYER_MIN_SCALE;
z += LAYER_MIN_SCALE;
scene.root()->scale_.x = z;
scene.root()->scale_.y = z;
// Clamp translation to acceptable area
glm::vec3 border_left(scene.root()->scale_.x * -2.f, scene.root()->scale_.y * -1.f, 0.f);
glm::vec3 border_right(scene.root()->scale_.x * 8.f, scene.root()->scale_.y * 8.f, 0.f);
scene.root()->translation_ = glm::clamp(scene.root()->translation_, border_left, border_right);
}
int LayerView::size ()
{
float z = (scene.root()->scale_.x - LAYER_MIN_SCALE) / (LAYER_MAX_SCALE - LAYER_MIN_SCALE);
return (int) ( sqrt(z) * 100.f);
}
std::pair<Node *, glm::vec2> LayerView::pick(glm::vec2 P)
{
// get picking from generic View
std::pair<Node *, glm::vec2> pick = View::pick(P);
// deal with internal interactive objects
if ( overlay_selection_icon_ != nullptr && pick.first == overlay_selection_icon_ ) {
openContextMenu(MENU_SELECTION);
}
else {
// get if a source was picked
Source *s = Mixer::manager().findSource(pick.first);
if (s != nullptr) {
// pick on the lock icon; unlock source
if ( pick.first == s->lock_) {
lock(s, false);
pick = { s->locker_, pick.second };
}
// pick on the open lock icon; lock source and cancel pick
else if ( pick.first == s->unlock_ ) {
lock(s, true);
pick = { nullptr, glm::vec2(0.f) };
}
// pick a locked source; cancel pick
else if ( s->locked() ) {
pick = { nullptr, glm::vec2(0.f) };
}
// pick the symbol: ask to show editor
else if ( pick.first == s->symbol_ ) {
UserInterface::manager().showSourceEditor(s);
}
}
else
pick = { nullptr, glm::vec2(0.f) };
}
return pick;
}
float LayerView::setDepth(Source *s, float d)
{
if (!s)
return -1.f;
// move the layer node of the source
Group *sourceNode = s->group(mode_);
float depth = d < 0.f ? sourceNode->translation_.z : d;
// negative or no depth given; find the front most depth
if ( depth < 0.f ) {
// default to place visible in front of background
depth = LAYER_BACKGROUND + LAYER_STEP;
// find the front-most souce in the workspace (behind FOREGROUND)
for (NodeSet::iterator node = scene.ws()->begin(); node != scene.ws()->end(); node++) {
// place in front of previous sources
depth = MAX(depth, (*node)->translation_.z + LAYER_STEP);
// in case node is already at max depth
if ((*node)->translation_.z + DELTA_DEPTH > MAX_DEPTH )
(*node)->translation_.z -= DELTA_DEPTH;
}
}
// change depth
sourceNode->translation_.z = CLAMP( depth, MIN_DEPTH, MAX_DEPTH);
// request reordering of scene at next update
View::need_deep_update_++;
// request update of source
s->touch();
return sourceNode->translation_.z;
}
View::Cursor LayerView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2> pick)
{
if (!s)
return Cursor();
// unproject
glm::vec3 gl_Position_from = Rendering::manager().unProject(from, scene.root()->transform_);
glm::vec3 gl_Position_to = Rendering::manager().unProject(to, scene.root()->transform_);
// compute delta translation
glm::vec3 dest_translation = s->stored_status_->translation_ + gl_Position_to - gl_Position_from;
// discretized translation with ALT
if (UserInterface::manager().altModifier())
dest_translation.x = ROUND(dest_translation.x, 5.f);
// apply change
float d = setDepth( s, MAX( -dest_translation.x, 0.f) );
std::ostringstream info;
info << "Depth " << std::fixed << std::setprecision(2) << d << " ";
// info << (s->locked() ? ICON_FA_LOCK : ICON_FA_LOCK_OPEN); // TODO static not locked
// store action in history
current_action_ = s->name() + ": " + info.str();
return Cursor(Cursor_ResizeNESW, info.str() );
}
void LayerView::arrow (glm::vec2 movement)
{
static int accumulator = 0;
accumulator++;
glm::vec3 gl_Position_from = Rendering::manager().unProject(glm::vec2(0.f), scene.root()->transform_);
glm::vec3 gl_Position_to = Rendering::manager().unProject(glm::vec2(movement.x-movement.y, 0.f), scene.root()->transform_);
glm::vec3 gl_delta = gl_Position_to - gl_Position_from;
bool first = true;
glm::vec3 delta_translation(0.f);
for (auto it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) {
// individual move with SHIFT
if ( !Source::isCurrent(*it) && UserInterface::manager().shiftModifier() )
continue;
Group *sourceNode = (*it)->group(mode_);
glm::vec3 dest_translation(0.f);
if (first) {
// dest starts at current
dest_translation = sourceNode->translation_;
// + ALT : discrete displacement
if (UserInterface::manager().altModifier()) {
if (accumulator > 10) {
dest_translation += glm::sign(gl_delta) * 0.21f;
dest_translation.x = ROUND(dest_translation.x, 10.f);
accumulator = 0;
}
else
break;
}
else {
// normal case: dest += delta
dest_translation += gl_delta * ARROWS_MOVEMENT_FACTOR;
}
// store action in history
std::ostringstream info;
info << "Depth " << std::fixed << std::setprecision(2) << (*it)->depth() << " ";
current_action_ = (*it)->name() + ": " + info.str();
// delta for others to follow
delta_translation = dest_translation - sourceNode->translation_;
}
else {
// dest = current + delta from first
dest_translation = sourceNode->translation_ + delta_translation;
}
// apply & request update
setDepth( *it, MAX( -dest_translation.x, 0.f) );
first = false;
}
}
void LayerView::updateSelectionOverlay()
{
View::updateSelectionOverlay();
if (overlay_selection_->visible_) {
// calculate bbox on selection
GlmToolkit::AxisAlignedBoundingBox selection_box = BoundingBoxVisitor::AABB(Mixer::selection().getCopy(), this);
overlay_selection_->scale_ = selection_box.scale();
overlay_selection_->translation_ = selection_box.center();
// slightly extend the boundary of the selection
overlay_selection_frame_->scale_ = glm::vec3(1.f) + glm::vec3(0.07f, 0.07f, 1.f) / overlay_selection_->scale_;
}
}

View File

@@ -1,241 +0,0 @@
#include <thread>
// Desktop OpenGL function loader
#include <glad/glad.h>
// gstreamer
#include <gst/gstformat.h>
#include <gst/video/video.h>
#include "defines.h"
#include "Settings.h"
#include "GstToolkit.h"
#include "SystemToolkit.h"
#include "FrameBuffer.h"
#include "Log.h"
#include "Loopback.h"
bool Loopback::system_loopback_initialized = false;
#if defined(LINUX)
/**
*
* Linux video 4 linux loopback device
*
* 1) Linux system has to have the v4l2loopback package
* See documentation at https://github.com/umlaeute/v4l2loopback
*
* $ sudo -A apt install v4l2loopback-dkms
*
* 2) User (sudo) has to install a v4l2loopback
*
* $ sudo -A modprobe v4l2loopback exclusive_caps=1 video_nr=10
*
* 3) But to do that, the user has to enter sudo passwd
*
* The command line above should be preceeded by
* export SUDO_ASKPASS="/tmp/mysudo.sh"
*
* where mysudo.sh contains the following:
* #!/bin/bash
* zenity --password --title=Authentication
*
* 4) Optionaly, we can set the dynamic properties of the stream
*
* $ sudo v4l2loopback-ctl set-caps "RGBA:640x480" /dev/video10
* $ sudo v4l2loopback-ctl set-fps 30 /dev/video10
*
* 5) Finally, the gstreamer pipeline can write into v4l2sink
*
* gst-launch-1.0 videotestsrc ! v4l2sink device=/dev/video10
*
*
* Useful command lines for debug
* $ v4l2-ctl --all -d 10
* $ gst-launch-1.0 v4l2src device=/dev/video10 ! videoconvert ! autovideosink
* $ gst-launch-1.0 videotestsrc ! v4l2sink device=/dev/video10
*/
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
std::string Loopback::system_loopback_name = "/dev/video10";
std::string Loopback::system_loopback_pipeline = "appsrc name=src ! videoconvert ! videorate ! video/x-raw,framerate=30/1 ! v4l2sink sync=false name=sink";
bool Loopback::initializeSystemLoopback()
{
if (!Loopback::systemLoopbackInitialized()) {
// create script for asking sudo password
std::string sudoscript = SystemToolkit::full_filename(SystemToolkit::settings_path(), "sudo.sh");
FILE *file = fopen(sudoscript.c_str(), "w");
if (file) {
fprintf(file, "#!/bin/bash\n");
fprintf(file, "zenity --password --title=Authentication\n");
fclose(file);
// make script executable
int fildes = 0;
fildes = open(sudoscript.c_str(), O_RDWR);
fchmod(fildes, S_IRWXU | S_IRWXG | S_IROTH | S_IWOTH);
close(fildes);
// create command line for installing v4l2loopback
std::string cmdline = "export SUDO_ASKPASS=\"" + sudoscript + "\"\n";
cmdline += "sudo -A apt install v4l2loopback-dkms 2>&1\n";
cmdline += "sudo -A modprobe -r v4l2loopback 2>&1\n";
cmdline += "sudo -A modprobe v4l2loopback exclusive_caps=1 video_nr=10 card_label=\"vimix loopback\" 2>&1\n";
// execute v4l2 command line
std::string report;
FILE *fp = popen(cmdline.c_str(), "r");
if (fp != NULL) {
// get stdout content from command line
char linestdout[PATH_MAX];
while (fgets(linestdout, PATH_MAX, fp) != NULL)
report += linestdout;
// error reported by pclose?
if (pclose(fp) != 0 )
Log::Warning("Failed to initialize system v4l2loopback\n%s", report.c_str());
// okay, probaly all good...
else
system_loopback_initialized = true;
}
else
Log::Warning("Failed to initialize system v4l2loopback\nCannot execute command line");
}
else
Log::Warning("Failed to initialize system v4l2loopback\nCannot create script", sudoscript.c_str());
}
return system_loopback_initialized;
}
bool Loopback::systemLoopbackInitialized()
{
// test if already initialized
if (!system_loopback_initialized) {
// check the existence of loopback device
if ( SystemToolkit::file_exists(system_loopback_name) )
system_loopback_initialized = true;
}
return system_loopback_initialized;
}
#else
std::string Loopback::system_loopback_name = "undefined";
std::string Loopback::system_loopback_pipeline = "";
bool Loopback::initializeSystemLoopback()
{
system_loopback_initialized = false;
return false;
}
bool Loopback::systemLoopbackInitialized()
{
return false;
}
#endif
Loopback::Loopback() : FrameGrabber()
{
frame_duration_ = gst_util_uint64_scale_int (1, GST_SECOND, 60);
}
void Loopback::init(GstCaps *caps)
{
// ignore
if (caps == nullptr)
return;
if (!Loopback::systemLoopbackInitialized()){
Log::Warning("Loopback system shall be initialized first.");
finished_ = true;
return;
}
// create a gstreamer pipeline
std::string description = Loopback::system_loopback_pipeline;
// parse pipeline descriptor
GError *error = NULL;
pipeline_ = gst_parse_launch (description.c_str(), &error);
if (error != NULL) {
Log::Warning("Loopback Could not construct pipeline %s:\n%s", description.c_str(), error->message);
g_clear_error (&error);
finished_ = true;
return;
}
// setup device sink
g_object_set (G_OBJECT (gst_bin_get_by_name (GST_BIN (pipeline_), "sink")),
"device", Loopback::system_loopback_name.c_str(),
NULL);
// setup custom app source
src_ = GST_APP_SRC( gst_bin_get_by_name (GST_BIN (pipeline_), "src") );
if (src_) {
g_object_set (G_OBJECT (src_),
"stream-type", GST_APP_STREAM_TYPE_STREAM,
"is-live", TRUE,
"format", GST_FORMAT_TIME,
// "do-timestamp", TRUE,
NULL);
// Direct encoding (no buffering)
gst_app_src_set_max_bytes( src_, 0 );
// instruct src to use the required caps
caps_ = gst_caps_copy( caps );
gst_app_src_set_caps (src_, caps_);
// setup callbacks
GstAppSrcCallbacks callbacks;
callbacks.need_data = FrameGrabber::callback_need_data;
callbacks.enough_data = FrameGrabber::callback_enough_data;
callbacks.seek_data = NULL; // stream type is not seekable
gst_app_src_set_callbacks (src_, &callbacks, this, NULL);
}
else {
Log::Warning("Loopback Could not configure source");
finished_ = true;
return;
}
// start recording
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
Log::Warning("Loopback Could not open %s", Loopback::system_loopback_name.c_str());
finished_ = true;
return;
}
// all good
#if defined(LINUX)
Log::Notify("Loopback started (v4l2loopback on %s)", Loopback::system_loopback_name.c_str());
#else
Log::Notify("Loopback started (%s)", Loopback::system_loopback_name.c_str());
#endif
// start
active_ = true;
}
void Loopback::terminate()
{
Log::Notify("Loopback to %s terminated.", Loopback::system_loopback_name.c_str());
}

View File

@@ -1,31 +0,0 @@
#ifndef LOOPBACK_H
#define LOOPBACK_H
#include <vector>
#include <gst/pbutils/pbutils.h>
#include <gst/app/gstappsrc.h>
#include "FrameGrabber.h"
class Loopback : public FrameGrabber
{
static std::string system_loopback_pipeline;
static std::string system_loopback_name;
static bool system_loopback_initialized;
void init(GstCaps *caps) override;
void terminate() override;
public:
Loopback();
static bool systemLoopbackInitialized();
static bool initializeSystemLoopback();
};
#endif // LOOPBACK_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,148 +0,0 @@
#include <glm/gtc/matrix_transform.hpp>
#include "MediaSource.h"
#include "defines.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "Resource.h"
#include "Decorations.h"
#include "MediaPlayer.h"
#include "Visitor.h"
#include "Log.h"
MediaSource::MediaSource() : Source(), path_("")
{
// create media player
mediaplayer_ = new MediaPlayer;
}
MediaSource::~MediaSource()
{
// delete media player
delete mediaplayer_;
}
void MediaSource::setPath(const std::string &p)
{
Log::Notify("Creating Source with media '%s'", p.c_str());
path_ = p;
mediaplayer_->open(path_);
mediaplayer_->play(true);
}
std::string MediaSource::path() const
{
return path_;
}
MediaPlayer *MediaSource::mediaplayer() const
{
return mediaplayer_;
}
glm::ivec2 MediaSource::icon() const
{
if (mediaplayer_->isImage())
return glm::ivec2(4, 9);
else
return glm::ivec2(18, 13);
}
bool MediaSource::failed() const
{
return mediaplayer_->failed();
}
uint MediaSource::texture() const
{
return mediaplayer_->texture();
}
void MediaSource::init()
{
if ( mediaplayer_->isOpen() ) {
// update video
mediaplayer_->update();
// once the texture of media player is created
if (mediaplayer_->texture() != Resource::getTextureBlack()) {
// get the texture index from media player, apply it to the media surface
texturesurface_->setTextureIndex( mediaplayer_->texture() );
// create Frame buffer matching size of media player
float height = float(mediaplayer_->width()) / mediaplayer_->aspectRatio();
FrameBuffer *renderbuffer = new FrameBuffer(mediaplayer_->width(), (uint)height, true);
// icon in mixing view
if (mediaplayer_->isImage())
symbol_ = new Symbol(Symbol::IMAGE, glm::vec3(0.75f, 0.75f, 0.01f));
else
symbol_ = new Symbol(Symbol::VIDEO, glm::vec3(0.75f, 0.75f, 0.01f));
symbol_->scale_.y = 1.5f;
// set the renderbuffer of the source and attach rendering nodes
attach(renderbuffer);
// force update of activation mode
active_ = true;
// deep update to reorder
View::need_deep_update_++;
// done init
initialized_ = true;
Log::Info("Source '%s' linked to Media %s.", name().c_str(), std::to_string(mediaplayer_->id()).c_str());
}
}
}
void MediaSource::setActive (bool on)
{
bool was_active = active_;
Source::setActive(on);
// change status of media player (only if status changed)
if ( active_ != was_active ) {
mediaplayer_->enable(active_);
}
}
void MediaSource::update(float dt)
{
Source::update(dt);
// update video
mediaplayer_->update();
}
void MediaSource::render()
{
if (!initialized_)
init();
else {
// blendingshader_->color.r = mediaplayer_->currentTimelineFading();
// blendingshader_->color.g = mediaplayer_->currentTimelineFading();
// blendingshader_->color.b = mediaplayer_->currentTimelineFading();
// render the media player into frame buffer
renderbuffer_->begin();
// texturesurface_->shader()->color.a = mediaplayer_->currentTimelineFading();
texturesurface_->shader()->color.r = mediaplayer_->currentTimelineFading();
texturesurface_->shader()->color.g = mediaplayer_->currentTimelineFading();
texturesurface_->shader()->color.b = mediaplayer_->currentTimelineFading();
texturesurface_->draw(glm::identity<glm::mat4>(), renderbuffer_->projection());
renderbuffer_->end();
}
}
void MediaSource::accept(Visitor& v)
{
Source::accept(v);
v.visit(*this);
}

1255
Mixer.cpp

File diff suppressed because it is too large Load Diff

View File

@@ -1,675 +0,0 @@
// Opengl
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/matrix_access.hpp>
#include <glm/gtx/vector_angle.hpp>
#include "imgui.h"
#include "ImGuiToolkit.h"
#include <string>
#include <sstream>
#include <iomanip>
#include "Mixer.h"
#include "defines.h"
#include "Settings.h"
#include "Decorations.h"
#include "UserInterfaceManager.h"
#include "BoundingBoxVisitor.h"
#include "ActionManager.h"
#include "MixingGroup.h"
#include "Log.h"
#include "MixingView.h"
// internal utility
float sin_quad_texture(float x, float y);
uint textureMixingQuadratic();
MixingView::MixingView() : View(MIXING), limbo_scale_(MIXING_LIMBO_SCALE)
{
// read default settings
if ( Settings::application.views[mode_].name.empty() ) {
// no settings found: store application default
Settings::application.views[mode_].name = "Mixing";
scene.root()->scale_ = glm::vec3(MIXING_DEFAULT_SCALE, MIXING_DEFAULT_SCALE, 1.0f);
scene.root()->translation_ = glm::vec3(0.0f, 0.0f, 0.0f);
saveSettings();
}
else
restoreSettings();
// Mixing scene background
Mesh *tmp = new Mesh("mesh/disk.ply");
tmp->scale_ = glm::vec3(limbo_scale_, limbo_scale_, 1.f);
tmp->shader()->color = glm::vec4( COLOR_LIMBO_CIRCLE, 0.6f );
scene.bg()->attach(tmp);
mixingCircle_ = new Mesh("mesh/disk.ply");
mixingCircle_->setTexture(textureMixingQuadratic());
mixingCircle_->shader()->color = glm::vec4( 1.f, 1.f, 1.f, 1.f );
scene.bg()->attach(mixingCircle_);
circle_ = new Mesh("mesh/circle.ply");
circle_->shader()->color = glm::vec4( COLOR_CIRCLE, 1.0f );
scene.bg()->attach(circle_);
// Mixing scene foreground
// button frame
tmp = new Mesh("mesh/disk.ply");
tmp->scale_ = glm::vec3(0.033f, 0.033f, 1.f);
tmp->translation_ = glm::vec3(0.f, 1.f, 0.f);
tmp->shader()->color = glm::vec4( COLOR_CIRCLE, 0.9f );
scene.fg()->attach(tmp);
// interactive button
button_white_ = new Disk();
button_white_->scale_ = glm::vec3(0.026f, 0.026f, 1.f);
button_white_->translation_ = glm::vec3(0.f, 1.f, 0.f);
button_white_->color = glm::vec4( 0.85f, 0.85f, 0.85f, 1.0f );
scene.fg()->attach(button_white_);
// button frame
tmp = new Mesh("mesh/disk.ply");
tmp->scale_ = glm::vec3(0.033f, 0.033f, 1.f);
tmp->translation_ = glm::vec3(0.f, -1.f, 0.f);
tmp->shader()->color = glm::vec4( COLOR_CIRCLE, 0.9f );
scene.fg()->attach(tmp);
// interactive button
button_black_ = new Disk();
button_black_->scale_ = glm::vec3(0.026f, 0.026f, 1.f);
button_black_->translation_ = glm::vec3(0.f, -1.f, 0.f);
button_black_->color = glm::vec4( 0.1f, 0.1f, 0.1f, 1.0f );
scene.fg()->attach(button_black_);
// moving slider
slider_root_ = new Group;
scene.fg()->attach(slider_root_);
// interactive slider
slider_ = new Disk();
slider_->scale_ = glm::vec3(0.08f, 0.08f, 1.f);
slider_->translation_ = glm::vec3(0.0f, 1.0f, 0.f);
slider_->color = glm::vec4( COLOR_CIRCLE, 0.9f );
slider_root_->attach(slider_);
// dark mask in front
tmp = new Mesh("mesh/disk.ply");
tmp->scale_ = glm::vec3(0.075f, 0.075f, 1.f);
tmp->translation_ = glm::vec3(0.0f, 1.0f, 0.f);
tmp->shader()->color = glm::vec4( COLOR_SLIDER_CIRCLE, 1.0f );
slider_root_->attach(tmp);
stashCircle_ = new Disk();
stashCircle_->scale_ = glm::vec3(0.5f, 0.5f, 1.f);
stashCircle_->translation_ = glm::vec3(2.f, -1.0f, 0.f);
stashCircle_->color = glm::vec4( COLOR_STASH_CIRCLE, 0.6f );
// scene.bg()->attach(stashCircle_);
}
void MixingView::draw()
{
// temporarily force shaders to use opacity blending for rendering icons
Shader::force_blending_opacity = true;
// draw scene of this view
View::draw();
// restore state
Shader::force_blending_opacity = false;
// display popup menu
if (show_context_menu_ == MENU_SELECTION) {
ImGui::OpenPopup( "MixingSelectionContextMenu" );
show_context_menu_ = MENU_NONE;
}
if (ImGui::BeginPopup("MixingSelectionContextMenu")) {
// colored context menu
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiToolkit::HighlightColor());
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.36f, 0.36f, 0.36f, 0.44f));
// special action of Mixing view: link or unlink
SourceList selected = Mixer::selection().getCopy();
if ( Mixer::manager().session()->canlink( selected )) {
// the selected sources can be linked
if (ImGui::Selectable( ICON_FA_LINK " Link" )){
// assemble a MixingGroup
Mixer::manager().session()->link(selected, scene.fg() );
Action::manager().store(std::string("Sources linked."));
// clear selection and select one of the sources of the group
Source *cur = Mixer::selection().front();
Mixer::manager().unsetCurrentSource();
Mixer::selection().clear();
Mixer::manager().setCurrentSource( cur );
}
}
else {
// the selected sources cannot be linked: offer to unlink!
if (ImGui::Selectable( ICON_FA_UNLINK " Unlink" )){
// dismantle MixingGroup(s)
Mixer::manager().session()->unlink(selected);
Action::manager().store(std::string("Sources unlinked."));
}
}
ImGui::Separator();
// manipulation of sources in Mixing view
if (ImGui::Selectable( ICON_FA_CROSSHAIRS " Center" )){
glm::vec2 center = glm::vec2(0.f, 0.f);
for (SourceList::iterator it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) {
// compute barycenter (1)
center += glm::vec2((*it)->group(View::MIXING)->translation_);
}
// compute barycenter (2)
center /= Mixer::selection().size();
for (SourceList::iterator it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) {
(*it)->group(View::MIXING)->translation_ -= glm::vec3(center, 0.f);
(*it)->touch();
}
Action::manager().store(std::string("Selection Mixing Center."));
}
if (ImGui::Selectable( ICON_FA_HAYKAL " Distribute" )){
SourceList list;
glm::vec2 center = glm::vec2(0.f, 0.f);
for (SourceList::iterator it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) {
list.push_back(*it);
// compute barycenter (1)
center += glm::vec2((*it)->group(View::MIXING)->translation_);
}
// compute barycenter (2)
center /= list.size();
// sort the vector of sources in clockwise order around the center pos_
list = mixing_sorted( list, center);
// average distance
float d = 0.f;
for (SourceList::iterator it = list.begin(); it != list.end(); it++) {
d += glm::distance(glm::vec2((*it)->group(View::MIXING)->translation_), center);
}
d /= list.size();
// distribute with equal angle
float angle = 0.f;
for (SourceList::iterator it = list.begin(); it != list.end(); it++) {
glm::vec2 P = center + glm::rotate(glm::vec2(0.f, d), angle);
(*it)->group(View::MIXING)->translation_.x = P.x;
(*it)->group(View::MIXING)->translation_.y = P.y;
(*it)->touch();
angle -= glm::two_pi<float>() / float(list.size());
}
Action::manager().store(std::string("Selection Mixing Distribute."));
}
if (ImGui::Selectable( ICON_FA_CLOUD_SUN " Expand & hide" )){
SourceList::iterator it = Mixer::selection().begin();
for (; it != Mixer::selection().end(); it++) {
(*it)->setAlpha(0.f);
}
Action::manager().store(std::string("Selection Mixing Expand & hide."));
}
if (ImGui::Selectable( ICON_FA_SUN " Compress & show" )){
SourceList::iterator it = Mixer::selection().begin();
for (; it != Mixer::selection().end(); it++) {
(*it)->setAlpha(0.99f);
}
Action::manager().store(std::string("Selection Mixing Compress & show."));
}
ImGui::PopStyleColor(2);
ImGui::EndPopup();
}
}
void MixingView::resize ( int scale )
{
float z = CLAMP(0.01f * (float) scale, 0.f, 1.f);
z *= z;
z *= MIXING_MAX_SCALE - MIXING_MIN_SCALE;
z += MIXING_MIN_SCALE;
scene.root()->scale_.x = z;
scene.root()->scale_.y = z;
// Clamp translation to acceptable area
glm::vec3 border(scene.root()->scale_.x * 1.f, scene.root()->scale_.y * 1.f, 0.f);
scene.root()->translation_ = glm::clamp(scene.root()->translation_, -border, border);
}
int MixingView::size ()
{
float z = (scene.root()->scale_.x - MIXING_MIN_SCALE) / (MIXING_MAX_SCALE - MIXING_MIN_SCALE);
return (int) ( sqrt(z) * 100.f);
}
void MixingView::centerSource(Source *s)
{
// setup view so that the center of the source ends at screen coordinates (650, 150)
// -> this is just next to the navigation pannel
glm::vec2 screenpoint = glm::vec2(500.f, 20.f) * Rendering::manager().mainWindow().dpiScale();
glm::vec3 pos_to = Rendering::manager().unProject(screenpoint, scene.root()->transform_);
glm::vec3 pos_from( - s->group(View::MIXING)->scale_.x, s->group(View::MIXING)->scale_.y, 0.f);
pos_from += s->group(View::MIXING)->translation_;
glm::vec4 pos_delta = glm::vec4(pos_to.x, pos_to.y, 0.f, 0.f) - glm::vec4(pos_from.x, pos_from.y, 0.f, 0.f);
pos_delta = scene.root()->transform_ * pos_delta;
scene.root()->translation_ += glm::vec3(pos_delta);
}
void MixingView::update(float dt)
{
View::update(dt);
// // always update the mixinggroups
// for (auto g = groups_.begin(); g != groups_.end(); g++)
// (*g)->update(dt);
// a more complete update is requested
// for mixing, this means restore position of the fading slider
if (View::need_deep_update_ > 0) {
//
// Set slider to match the actual fading of the session
//
float f = Mixer::manager().session()->empty() ? 0.f : Mixer::manager().session()->fading();
// reverse calculate angle from fading & move slider
slider_root_->rotation_.z = SIGN(slider_root_->rotation_.z) * asin(f) * 2.f;
// visual feedback on mixing circle
f = 1.f - f;
mixingCircle_->shader()->color = glm::vec4(f, f, f, 1.f);
}
// the current view is the mixing view
if (Mixer::manager().view() == this )
{
//
// Set session fading to match the slider angle (during animation)
//
// calculate fading from angle
float f = sin( ABS(slider_root_->rotation_.z) * 0.5f);
// apply fading
if ( ABS_DIFF( f, Mixer::manager().session()->fading()) > EPSILON )
{
// apply fading to session
Mixer::manager().session()->setFading(f);
// visual feedback on mixing circle
f = 1.f - f;
mixingCircle_->shader()->color = glm::vec4(f, f, f, 1.f);
}
// update the selection overlay
updateSelectionOverlay();
}
}
std::pair<Node *, glm::vec2> MixingView::pick(glm::vec2 P)
{
// get picking from generic View
std::pair<Node *, glm::vec2> pick = View::pick(P);
// deal with internal interactive objects
if ( pick.first == button_white_ || pick.first == button_black_ ) {
RotateToCallback *anim = nullptr;
if (pick.first == button_white_)
anim = new RotateToCallback(0.f, 500.f);
else
anim = new RotateToCallback(SIGN(slider_root_->rotation_.z) * M_PI, 500.f);
// animate clic
pick.first->update_callbacks_.push_back(new BounceScaleCallback(0.3f));
// reset & start animation
slider_root_->update_callbacks_.clear();
slider_root_->update_callbacks_.push_back(anim);
}
else if ( overlay_selection_icon_ != nullptr && pick.first == overlay_selection_icon_ ) {
openContextMenu(MENU_SELECTION);
}
else {
// get if a source was picked
Source *s = Mixer::manager().findSource(pick.first);
if (s != nullptr) {
// pick on the lock icon; unlock source
if ( pick.first == s->lock_) {
lock(s, false);
pick = { s->locker_, pick.second };
}
// pick on the open lock icon; lock source and cancel pick
else if ( pick.first == s->unlock_ ) {
lock(s, true);
pick = { nullptr, glm::vec2(0.f) };
}
// pick a locked source ; cancel pick
else if ( s->locked() ) {
pick = { nullptr, glm::vec2(0.f) };
}
// pick the symbol: ask to show editor
else if ( pick.first == s->symbol_ ) {
UserInterface::manager().showSourceEditor(s);
}
// pick on the mixing group rotation icon
else if ( pick.first == s->rotation_mixingroup_ ) {
s->mixinggroup_->setAction( MixingGroup::ACTION_ROTATE_ALL );
}
// pick source of a mixing group
else if ( s->mixinggroup_ != nullptr ) {
if (UserInterface::manager().ctrlModifier()) {
SourceList linked = s->mixinggroup_->getCopy();
linked.remove(s);
if (Mixer::selection().empty())
Mixer::selection().add(linked);
}
else if (UserInterface::manager().shiftModifier())
s->mixinggroup_->setAction( MixingGroup::ACTION_GRAB_ONE );
else
s->mixinggroup_->setAction( MixingGroup::ACTION_GRAB_ALL );
}
}
}
return pick;
}
View::Cursor MixingView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2> pick)
{
View::Cursor ret = Cursor();
ret.type = Cursor_ResizeAll;
// unproject
glm::vec3 gl_Position_from = Rendering::manager().unProject(from, scene.root()->transform_);
glm::vec3 gl_Position_to = Rendering::manager().unProject(to, scene.root()->transform_);
// No source is given
if (!s) {
// if interaction with slider
if (pick.first == slider_) {
// apply rotation to match angle with mouse cursor
float angle = glm::orientedAngle( glm::normalize(glm::vec2(0.f, 1.0)), glm::normalize(glm::vec2(gl_Position_to)));
// snap on 0 and PI angles
if ( ABS_DIFF(angle, 0.f) < 0.05)
angle = 0.f;
else if ( ABS_DIFF(angle, M_PI) < 0.05)
angle = M_PI;
// animate slider (rotation angle on its parent)
slider_root_->rotation_.z = angle;
// cursor feedback
slider_->color = glm::vec4( COLOR_CIRCLE_OVER, 0.9f );
std::ostringstream info;
info << "Global opacity " << 100 - int(Mixer::manager().session()->fading() * 100.0) << " %";
return Cursor(Cursor_Hand, info.str() );
}
// nothing to do
return Cursor();
}
//
// Interaction with source
//
// compute delta translation
s->group(mode_)->translation_ = s->stored_status_->translation_ + gl_Position_to - gl_Position_from;
// manage mixing group
if (s->mixinggroup_ != nullptr ) {
// inform mixing groups to follow the current source
if (Source::isCurrent(s) && s->mixinggroup_->action() > MixingGroup::ACTION_UPDATE) {
s->mixinggroup_->follow(s);
if (s->mixinggroup_->action() == MixingGroup::ACTION_ROTATE_ALL)
ret.type = Cursor_Hand;
}
else
s->mixingGroup()->setAction(MixingGroup::ACTION_NONE);
}
// request update
s->touch();
// // trying to enter stash
// if ( glm::distance( glm::vec2(s->group(mode_)->translation_), glm::vec2(stashCircle_->translation_)) < stashCircle_->scale_.x) {
// // refuse to put an active source in stash
// if (s->active())
// s->group(mode_)->translation_ = s->stored_status_->translation_;
// else {
// Mixer::manager().conceal(s);
// s->group(mode_)->scale_ = glm::vec3(MIXING_ICON_SCALE) - glm::vec3(0.1f, 0.1f, 0.f);
// }
// }
// else if ( Mixer::manager().concealed(s) ) {
// Mixer::manager().uncover(s);
// s->group(mode_)->scale_ = glm::vec3(MIXING_ICON_SCALE);
// }
std::ostringstream info;
if (s->active()) {
info << "Alpha " << std::fixed << std::setprecision(3) << s->blendingShader()->color.a << " ";
info << ( (s->blendingShader()->color.a > 0.f) ? ICON_FA_EYE : ICON_FA_EYE_SLASH);
}
else
info << "Inactive " << ICON_FA_SNOWFLAKE;
// store action in history
current_action_ = s->name() + ": " + info.str();
// update cursor
ret.info = info.str();
return ret;
}
void MixingView::terminate()
{
View::terminate();
// terminate all mixing group actions
for (auto g = Mixer::manager().session()->beginMixingGroup();
g != Mixer::manager().session()->endMixingGroup(); g++)
(*g)->setAction( MixingGroup::ACTION_FINISH );
}
View::Cursor MixingView::over (glm::vec2 pos)
{
View::Cursor ret = Cursor();
std::pair<Node *, glm::vec2> pick = View::pick(pos);
// deal with internal interactive objects
if ( pick.first == slider_ ) {
slider_->color = glm::vec4( COLOR_CIRCLE_OVER, 0.9f );
ret.type = Cursor_Hand;
}
else
slider_->color = glm::vec4( COLOR_CIRCLE, 0.9f );
return ret;
}
void MixingView::arrow (glm::vec2 movement)
{
static int accumulator = 0;
accumulator++;
glm::vec3 gl_Position_from = Rendering::manager().unProject(glm::vec2(0.f), scene.root()->transform_);
glm::vec3 gl_Position_to = Rendering::manager().unProject(movement, scene.root()->transform_);
glm::vec3 gl_delta = gl_Position_to - gl_Position_from;
bool first = true;
glm::vec3 delta_translation(0.f);
for (auto it = Mixer::selection().begin(); it != Mixer::selection().end(); it++) {
// individual move with SHIFT
if ( !Source::isCurrent(*it) && UserInterface::manager().shiftModifier() )
continue;
Group *sourceNode = (*it)->group(mode_);
glm::vec3 dest_translation(0.f);
if (first) {
// dest starts at current
dest_translation = sourceNode->translation_;
// + ALT : discrete displacement
if (UserInterface::manager().altModifier()) {
if (accumulator > 10) {
dest_translation += glm::sign(gl_delta) * 0.11f;
dest_translation.x = ROUND(dest_translation.x, 10.f);
dest_translation.y = ROUND(dest_translation.y, 10.f);
accumulator = 0;
}
else
break;
}
else {
// normal case: dest += delta
dest_translation += gl_delta * ARROWS_MOVEMENT_FACTOR;
}
// store action in history
std::ostringstream info;
if ((*it)->active()) {
info << "Alpha " << std::fixed << std::setprecision(3) << (*it)->blendingShader()->color.a << " ";
info << ( ((*it)->blendingShader()->color.a > 0.f) ? ICON_FA_EYE : ICON_FA_EYE_SLASH);
}
else
info << "Inactive " << ICON_FA_SNOWFLAKE;
current_action_ = (*it)->name() + ": " + info.str();
// delta for others to follow
delta_translation = dest_translation - sourceNode->translation_;
}
else {
// dest = current + delta from first
dest_translation = sourceNode->translation_ + delta_translation;
}
// apply & request update
sourceNode->translation_ = dest_translation;
(*it)->touch();
first = false;
}
}
void MixingView::setAlpha(Source *s)
{
if (!s)
return;
// move the layer node of the source
Group *sourceNode = s->group(mode_);
glm::vec2 mix_pos = glm::vec2(DEFAULT_MIXING_TRANSLATION);
for(NodeSet::iterator it = scene.ws()->begin(); it != scene.ws()->end(); it++) {
// avoid superposing icons: distribute equally
if ( glm::distance(glm::vec2((*it)->translation_), mix_pos) < DELTA_ALPHA) {
mix_pos += glm::vec2(-0.03f, 0.03f);
}
}
sourceNode->translation_.x = mix_pos.x;
sourceNode->translation_.y = mix_pos.y;
// request update
s->touch();
}
void MixingView::updateSelectionOverlay()
{
View::updateSelectionOverlay();
if (overlay_selection_->visible_) {
// calculate bbox on selection
GlmToolkit::AxisAlignedBoundingBox selection_box = BoundingBoxVisitor::AABB(Mixer::selection().getCopy(), this);
overlay_selection_->scale_ = selection_box.scale();
overlay_selection_->translation_ = selection_box.center();
// slightly extend the boundary of the selection
overlay_selection_frame_->scale_ = glm::vec3(1.f) + glm::vec3(0.01f, 0.01f, 1.f) / overlay_selection_->scale_;
}
}
#define CIRCLE_PIXELS 64
#define CIRCLE_PIXEL_RADIUS 1024.0
//#define CIRCLE_PIXELS 256
//#define CIRCLE_PIXEL_RADIUS 16384.0
//#define CIRCLE_PIXELS 1024
//#define CIRCLE_PIXEL_RADIUS 262144.0
float sin_quad_texture(float x, float y) {
// return 0.5f + 0.5f * cos( M_PI * CLAMP( ( ( x * x ) + ( y * y ) ) / CIRCLE_PIXEL_RADIUS, 0.f, 1.f ) );
float D = sqrt( ( x * x )/ CIRCLE_PIXEL_RADIUS + ( y * y )/ CIRCLE_PIXEL_RADIUS );
return 0.5f + 0.5f * cos( M_PI * CLAMP( D * sqrt(D), 0.f, 1.f ) );
}
uint textureMixingQuadratic()
{
static GLuint texid = 0;
if (texid == 0) {
// generate the texture with alpha exactly as computed for sources
GLubyte matrix[CIRCLE_PIXELS*CIRCLE_PIXELS * 4];
GLubyte color[4] = {0,0,0,0};
GLfloat luminance = 1.f;
GLfloat alpha = 0.f;
GLfloat distance = 0.f;
int l = -CIRCLE_PIXELS / 2 + 1, c = 0;
for (int i = 0; i < CIRCLE_PIXELS / 2; ++i) {
c = -CIRCLE_PIXELS / 2 + 1;
for (int j=0; j < CIRCLE_PIXELS / 2; ++j) {
// distance to the center
distance = sin_quad_texture( (float) c , (float) l );
// distance = 1.f - (GLfloat) ((c * c) + (l * l)) / CIRCLE_PIXEL_RADIUS; // quadratic
// distance = 1.f - (GLfloat) sqrt( (GLfloat) ((c * c) + (l * l))) / (GLfloat) sqrt(CIRCLE_PIXEL_RADIUS); // linear
// transparency
alpha = 255.f * CLAMP( distance , 0.f, 1.f);
color[3] = static_cast<GLubyte>(alpha);
// luminance adjustment
luminance = 255.f * CLAMP( 0.2f + 0.75f * distance, 0.f, 1.f);
color[0] = color[1] = color[2] = static_cast<GLubyte>(luminance);
// 1st quadrant
memmove(&matrix[ j * 4 + i * CIRCLE_PIXELS * 4 ], color, 4 * sizeof(GLubyte));
// 4nd quadrant
memmove(&matrix[ (CIRCLE_PIXELS -j -1)* 4 + i * CIRCLE_PIXELS * 4 ], color, 4 * sizeof(GLubyte));
// 3rd quadrant
memmove(&matrix[ j * 4 + (CIRCLE_PIXELS -i -1) * CIRCLE_PIXELS * 4 ], color, 4 * sizeof(GLubyte));
// 4th quadrant
memmove(&matrix[ (CIRCLE_PIXELS -j -1) * 4 + (CIRCLE_PIXELS -i -1) * CIRCLE_PIXELS * 4 ], color, 4 * sizeof(GLubyte));
++c;
}
++l;
}
// setup texture
glGenTextures(1, &texid);
glBindTexture(GL_TEXTURE_2D, texid);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, CIRCLE_PIXELS, CIRCLE_PIXELS);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, CIRCLE_PIXELS, CIRCLE_PIXELS, GL_BGRA, GL_UNSIGNED_BYTE, matrix);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
return texid;
}

View File

@@ -1,164 +0,0 @@
#include <sstream>
#include <glm/gtc/matrix_transform.hpp>
#include "PatternSource.h"
#include "defines.h"
#include "ImageShader.h"
#include "Resource.h"
#include "Decorations.h"
#include "Stream.h"
#include "Visitor.h"
#include "Log.h"
#define MAX_PATTERN 24
// smpte (0) SMPTE 100%% color bars
// snow (1) Random (television snow)
// black (2) 100%% Black
// white (3) 100%% White
// red (4) Red
// green (5) Green
// blue (6) Blue
// checkers-1 (7) Checkers 1px
// checkers-2 (8) Checkers 2px
// checkers-4 (9) Checkers 4px
// checkers-8 (10) Checkers 8px
// circular (11) Circular
// blink (12) Blink
// smpte75 (13) SMPTE 75%% color bars
// zone-plate (14) Zone plate
// gamut (15) Gamut checkers
// chroma-zone-plate (16) Chroma zone plate
// solid-color (17) Solid color
// ball (18) Moving ball
// smpte100 (19) SMPTE 100%% color bars
// bar (20) Bar
// pinwheel (21) Pinwheel
// spokes (22) Spokes
// gradient (23) Gradient
// colors (24) Colors
const char* pattern_internal_[MAX_PATTERN] = { "videotestsrc pattern=black",
"videotestsrc pattern=white",
"videotestsrc pattern=gradient",
"videotestsrc pattern=checkers-1 ! video/x-raw,format=GRAY8 ! videoconvert",
"videotestsrc pattern=checkers-8 ! video/x-raw,format=GRAY8 ! videoconvert",
"videotestsrc pattern=circular",
"frei0r-src-lissajous0r ratiox=0.001 ratioy=0.999 ! videoconvert",
"videotestsrc pattern=pinwheel",
"videotestsrc pattern=spokes",
"videotestsrc pattern=red",
"videotestsrc pattern=green",
"videotestsrc pattern=blue",
"videotestsrc pattern=smpte100",
"videotestsrc pattern=colors",
"videotestsrc pattern=smpte",
"videotestsrc pattern=snow",
"videotestsrc pattern=blink",
"videotestsrc pattern=zone-plate",
"videotestsrc pattern=chroma-zone-plate",
"videotestsrc pattern=bar horizontal-speed=5",
"videotestsrc pattern=ball",
"frei0r-src-ising0r",
"videotestsrc pattern=black ! timeoverlay halignment=center valignment=center font-desc=\"Sans, 72\" ",
"videotestsrc pattern=black ! clockoverlay halignment=center valignment=center font-desc=\"Sans, 72\" "
};
std::vector<std::string> Pattern::pattern_types = { "Black",
"White",
"Gradient",
"Checkers 1x1 px",
"Checkers 8x8 px",
"Circles",
"Lissajous",
"Pinwheel",
"Spokes",
"Red",
"Green",
"Blue",
"Color bars",
"RGB grid",
"SMPTE test pattern",
"Television snow",
"Blink",
"Fresnel zone plate",
"Chroma zone plate",
"Bar moving",
"Ball bouncing"
#if GST_VERSION_MINOR > 17
,
"Blob",
"Timer",
"Clock"
#endif
};
Pattern::Pattern() : Stream(), type_(MAX_PATTERN) // invalid pattern
{
}
glm::ivec2 Pattern::resolution()
{
return glm::ivec2( width_, height_);
}
void Pattern::open( uint pattern, glm::ivec2 res )
{
type_ = MIN(pattern, MAX_PATTERN-1);
std::string gstreamer_pattern = pattern_internal_[type_];
// there is always a special case...
switch(type_)
{
case 18: // zone plates
case 17:
{
std::ostringstream oss;
oss << " kx2=" << (int)(res.x * 10.f / res.y) << " ky2=10 kt=4";
gstreamer_pattern += oss.str(); // Zone plate
}
break;
default:
break;
}
// all patterns before 'SMPTE test pattern' are single frames (not animated)
single_frame_ = type_ < 14;
// (private) open stream
Stream::open(gstreamer_pattern, res.x, res.y);
}
PatternSource::PatternSource() : StreamSource()
{
// create stream
stream_ = (Stream *) new Pattern;
// set symbol
symbol_ = new Symbol(Symbol::PATTERN, glm::vec3(0.75f, 0.75f, 0.01f));
symbol_->scale_.y = 1.5f;
}
void PatternSource::setPattern(uint type, glm::ivec2 resolution)
{
Log::Notify("Creating Source with pattern '%s'", Pattern::pattern_types[type].c_str());
pattern()->open( (uint) type, resolution );
stream_->play(true);
}
void PatternSource::accept(Visitor& v)
{
Source::accept(v);
if (!failed())
v.visit(*this);
}
Pattern *PatternSource::pattern() const
{
return dynamic_cast<Pattern *>(stream_);
}

107
README.md
View File

@@ -1,5 +1,8 @@
# vimix
Live Video Mixing
__Live Video Mixing__
<img src=docs/vimix_screenshot.png width="800">
vimix performs graphical mixing and blending of several movie clips and
computer generated graphics, with image processing effects in real-time.
@@ -7,59 +10,135 @@ computer generated graphics, with image processing effects in real-time.
Its intuitive and hands-on user interface gives direct control on image opacity and
shape for producing live graphics during concerts and VJ-ing sessions.
The ouput image is typically projected full-screen on an external
monitor or a projector, but can be recorded live (no audio).
The output image is typically projected full-screen on an external
monitor or a projector, and can be streamed live (SRT, Shmdata) or recorded (without audio).
vimix is the successor for GLMixer - https://sourceforge.net/projects/glmixer/
# Install
# License
GPL-3.0-or-later
See [LICENSE](https://github.com/brunoherbelin/vimix/blob/master/LICENSE)
# Install vimix
Check the [Quick Installation Guide](https://github.com/brunoherbelin/vimix/wiki/Quick-Installation-Guide)
### Linux
Download and install a release package from https://snapcraft.io/vimix
Download and install a released [flatpak package](https://flathub.org/apps/details/io.github.brunoherbelin.Vimix)
flatpak install --user vimix
NB: Build your flatpak package to get the latest beta version; instructions are [here](https://github.com/brunoherbelin/vimix/tree/master/flatpak).
[![vimix](https://snapcraft.io/vimix/badge.svg)](https://snapcraft.io/vimix)
Download and install a released [snap package](https://snapcraft.io/vimix)
snap install vimix
Install the stable debian package (slower release frequency)
sudo apt install vimix
### Mac OSX
Download and open a release package from https://github.com/brunoherbelin/vimix/releases
NB: You'll need to accept the exception in OSX security preference.
# Build vimix
## Clone
git clone --recursive https://github.com/brunoherbelin/vimix.git
This will create the directory 'vimix', download the latest version of vimix code,
and (recursively) clone all the internal git Dependencies.
and (recursively) clone all the internal git dependencies.
## Compile
First time after git clone:
mkdir vimix-build
cd vimix-build
cmake -DCMAKE_BUILD_TYPE=Release ../vimix
cmake --build .
This will create the directory 'vimix-build', configure the program for build, and compile vimix.
If successful, the compilation will have produced the executable `vimix` in the `src` directory.
You can run vimix with `./src/vimix` :
...
[100%] Built target vimix
./src/vimix
## Update clone and re-compile
### Dependencies
Run these commands from the `vimix-build` directory if you did 'Clone' and 'Compile' previously and only want to get the latest update and rebuild.
git -C ../vimix/ pull
cmake --build .
This will pull the latest commit from git and recompile.
## Try the Beta branch
Run this commands from the `vimix-build` directory before runing 'Update clone and re-compile above'
git -C ../vimix/ checkout beta
It should say;
branch 'beta' set up to track 'origin/beta'.
Switched to a new branch 'beta'
## Dependencies
**Compiling tools:**
- gcc
- make
- cmake
- git
**Libraries:**
- gstreamer
- gst-plugins : base, good, bad & ugly
- libpng
- gst-plugins (libav, base, good, bad & ugly)
- libglfw3
- libicu (icu-i18n icu-uc icu-io)
#### Install Dependencies
Optionnal:
**Ubuntu**
- glm
- stb
- TinyXML2
- AbletonLink
- Shmdata
apt-get install build-essential cmake libpng-dev libglfw3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libicu-dev
### Install Dependencies
**OSX with Brew**
#### Ubuntu
apt-get install build-essential cmake libpng-dev libglfw3-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-libav libicu-dev libgtk-3-dev
Optionnal:
apt-get install libglm-dev libstb-dev libtinyxml2-dev ableton-link-dev
> Follow these instructions to [install Shmdata](https://github.com/nicobou/shmdata/blob/develop/doc/install-from-sources.md).
git clone https://gitlab.com/sat-metalab/shmdata.git
mkdir shmdata-build
cd shmdata-build
cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=Release -DWITH_PYTHON=0 -DWITH_SDCRASH=0 -DWITH_SDFLOW=0 ../shmdata-build
cmake --build . --target package
sudo dpkg -i ./libshmdata_1.3*_amd64.deb
#### OSX with Brew
brew install cmake libpng glfw gstreamer icu4c
brew install cmake libpng glfw gstreamer gst-libav gst-plugins-bad gst-plugins-base gst-plugins-good gst-plugins-ugly icu4c

View File

@@ -1,311 +0,0 @@
#include <thread>
// Desktop OpenGL function loader
#include <glad/glad.h>
// standalone image loader
#include <stb_image.h>
#include <stb_image_write.h>
// gstreamer
#include <gst/gstformat.h>
#include <gst/video/video.h>
#include "Settings.h"
#include "GstToolkit.h"
#include "defines.h"
#include "SystemToolkit.h"
#include "FrameBuffer.h"
#include "Log.h"
#include "Recorder.h"
PNGRecorder::PNGRecorder() : FrameGrabber()
{
}
void PNGRecorder::init(GstCaps *caps)
{
// ignore
if (caps == nullptr)
return;
// create a gstreamer pipeline
std::string description = "appsrc name=src ! videoconvert ! pngenc ! filesink name=sink";
// parse pipeline descriptor
GError *error = NULL;
pipeline_ = gst_parse_launch (description.c_str(), &error);
if (error != NULL) {
Log::Warning("PNG Capture Could not construct pipeline %s:\n%s", description.c_str(), error->message);
g_clear_error (&error);
finished_ = true;
return;
}
// verify location path (path is always terminated by the OS dependent separator)
std::string path = SystemToolkit::path_directory(Settings::application.record.path);
if (path.empty())
path = SystemToolkit::home_path();
filename_ = path + "vimix_" + SystemToolkit::date_time_string() + ".png";
// setup file sink
g_object_set (G_OBJECT (gst_bin_get_by_name (GST_BIN (pipeline_), "sink")),
"location", filename_.c_str(),
"sync", FALSE,
NULL);
// setup custom app source
src_ = GST_APP_SRC( gst_bin_get_by_name (GST_BIN (pipeline_), "src") );
if (src_) {
g_object_set (G_OBJECT (src_),
"stream-type", GST_APP_STREAM_TYPE_STREAM,
"is-live", TRUE,
"format", GST_FORMAT_TIME,
// "do-timestamp", TRUE,
NULL);
// Direct encoding (no buffering)
gst_app_src_set_max_bytes( src_, 0 );
// instruct src to use the required caps
caps_ = gst_caps_copy( caps );
gst_app_src_set_caps (src_, caps_);
// setup callbacks
GstAppSrcCallbacks callbacks;
callbacks.need_data = FrameGrabber::callback_need_data;
callbacks.enough_data = FrameGrabber::callback_enough_data;
callbacks.seek_data = NULL; // stream type is not seekable
gst_app_src_set_callbacks (src_, &callbacks, this, NULL);
}
else {
Log::Warning("PNG Capture Could not configure source");
finished_ = true;
return;
}
// start pipeline
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
Log::Warning("PNG Capture Could not record %s", filename_.c_str());
finished_ = true;
return;
}
// all good
Log::Info("PNG Capture started.");
// start recording !!
active_ = true;
}
void PNGRecorder::terminate()
{
Log::Notify("PNG Capture %s is ready.", filename_.c_str());
}
void PNGRecorder::addFrame(GstBuffer *buffer, GstCaps *caps, float dt)
{
FrameGrabber::addFrame(buffer, caps, dt);
// PNG Recorder specific :
// stop after one frame
if (timestamp_ > 0) {
stop();
}
}
const char* VideoRecorder::profile_name[VideoRecorder::DEFAULT] = {
"H264 (Realtime)",
"H264 (High 4:4:4)",
"H265 (Realtime)",
"H265 (HQ Animation)",
"ProRes (Standard)",
"ProRes (HQ 4444)",
"WebM VP8 (2MB/s)",
"Multiple JPEG"
};
const std::vector<std::string> VideoRecorder::profile_description {
// Control x264 encoder quality :
// pass
// quant (4) Constant Quantizer
// qual (5) Constant Quality
// quantizer
// The total range is from 0 to 51, where 0 is lossless, 18 can be considered visually lossless,
// and 51 is terrible quality. A sane range is 18-26, and the default is 23.
// speed-preset
// veryfast (3)
// faster (4)
// fast (5)
#ifndef APPLE
// "video/x-raw, format=I420 ! x264enc pass=4 quantizer=26 speed-preset=3 threads=4 ! video/x-h264, profile=baseline ! h264parse ! ",
"video/x-raw, format=I420 ! x264enc tune=\"zerolatency\" threads=4 ! video/x-h264, profile=baseline ! h264parse ! ",
#else
"video/x-raw, format=I420 ! vtenc_h264_hw realtime=1 allow-frame-reordering=0 ! h264parse ! ",
#endif
"video/x-raw, format=Y444_10LE ! x264enc pass=4 quantizer=16 speed-preset=4 threads=4 ! video/x-h264, profile=(string)high-4:4:4 ! h264parse ! ",
// Control x265 encoder quality :
// NB: apparently x265 only accepts I420 format :(
// speed-preset
// veryfast (3)
// faster (4)
// fast (5)
// Tune
// psnr (1)
// ssim (2) DEFAULT
// grain (3)
// zerolatency (4) Encoder latency is removed
// fastdecode (5)
// animation (6) optimize the encode quality for animation content without impacting the encode speed
// crf Quality-controlled variable bitrate [0 51]
// default 28
// 24 for x265 should be visually transparent; anything lower will probably just waste file size
"video/x-raw, format=I420 ! x265enc tune=4 speed-preset=3 ! video/x-h265, profile=(string)main ! h265parse ! ",
"video/x-raw, format=I420 ! x265enc tune=6 speed-preset=4 option-string=\"crf=24\" ! video/x-h265, profile=(string)main ! h265parse ! ",
// Apple ProRes encoding parameters
// pass
// cbr (0) Constant Bitrate Encoding
// quant (2) Constant Quantizer
// pass1 (512) VBR Encoding - Pass 1
// profile
// 0 proxy
// 1 lt
// 2 standard
// 3 hq
// 4 4444
"avenc_prores_ks pass=2 profile=2 quantizer=26 ! ",
"video/x-raw, format=Y444_10LE ! avenc_prores_ks pass=2 profile=4 quantizer=12 ! ",
// VP8 WebM encoding
"vp8enc end-usage=vbr cpu-used=8 max-quantizer=35 deadline=100000 target-bitrate=200000 keyframe-max-dist=360 token-partitions=2 static-threshold=100 ! ",
"jpegenc ! "
};
// Too slow
//// WebM VP9 encoding parameters
//// https://www.webmproject.org/docs/encoder-parameters/
//// https://developers.google.com/media/vp9/settings/vod/
//"vp9enc end-usage=vbr end-usage=vbr cpu-used=3 max-quantizer=35 target-bitrate=200000 keyframe-max-dist=360 token-partitions=2 static-threshold=1000 ! "
// FAILED
// x265 encoder quality
// string description = "appsrc name=src ! videoconvert ! "
// "x265enc tune=4 speed-preset=2 option-string='crf=28' ! h265parse ! "
// "qtmux ! filesink name=sink";
VideoRecorder::VideoRecorder() : FrameGrabber()
{
}
void VideoRecorder::init(GstCaps *caps)
{
// ignore
if (caps == nullptr)
return;
// create a gstreamer pipeline
std::string description = "appsrc name=src ! videoconvert ! ";
if (Settings::application.record.profile < 0 || Settings::application.record.profile >= DEFAULT)
Settings::application.record.profile = H264_STANDARD;
description += profile_description[Settings::application.record.profile];
// verify location path (path is always terminated by the OS dependent separator)
std::string path = SystemToolkit::path_directory(Settings::application.record.path);
if (path.empty())
path = SystemToolkit::home_path();
// setup filename & muxer
if( Settings::application.record.profile == JPEG_MULTI) {
std::string folder = path + "vimix_" + SystemToolkit::date_time_string();
filename_ = SystemToolkit::full_filename(folder, "%05d.jpg");
if (SystemToolkit::create_directory(folder))
description += "multifilesink name=sink";
}
else if( Settings::application.record.profile == VP8) {
filename_ = path + "vimix_" + SystemToolkit::date_time_string() + ".webm";
description += "webmmux ! filesink name=sink";
}
else {
filename_ = path + "vimix_" + SystemToolkit::date_time_string() + ".mov";
description += "qtmux ! filesink name=sink";
}
// parse pipeline descriptor
GError *error = NULL;
pipeline_ = gst_parse_launch (description.c_str(), &error);
if (error != NULL) {
Log::Warning("VideoRecorder Could not construct pipeline %s:\n%s", description.c_str(), error->message);
g_clear_error (&error);
finished_ = true;
return;
}
// setup file sink
g_object_set (G_OBJECT (gst_bin_get_by_name (GST_BIN (pipeline_), "sink")),
"location", filename_.c_str(),
"sync", FALSE,
NULL);
// setup custom app source
src_ = GST_APP_SRC( gst_bin_get_by_name (GST_BIN (pipeline_), "src") );
if (src_) {
g_object_set (G_OBJECT (src_),
"stream-type", GST_APP_STREAM_TYPE_STREAM,
"is-live", TRUE,
"format", GST_FORMAT_TIME,
// "do-timestamp", TRUE,
NULL);
// Direct encoding (no buffering)
gst_app_src_set_max_bytes( src_, 0 );
// instruct src to use the required caps
caps_ = gst_caps_copy( caps );
gst_app_src_set_caps (src_, caps_);
// setup callbacks
GstAppSrcCallbacks callbacks;
callbacks.need_data = FrameGrabber::callback_need_data;
callbacks.enough_data = FrameGrabber::callback_enough_data;
callbacks.seek_data = NULL; // stream type is not seekable
gst_app_src_set_callbacks (src_, &callbacks, this, NULL);
}
else {
Log::Warning("VideoRecorder Could not configure source");
finished_ = true;
return;
}
// start recording
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
Log::Warning("VideoRecorder Could not record %s", filename_.c_str());
finished_ = true;
return;
}
// all good
Log::Info("Video Recording started (%s)", profile_name[Settings::application.record.profile]);
// start recording !!
active_ = true;
}
void VideoRecorder::terminate()
{
Log::Notify("Video Recording %s is ready.", filename_.c_str());
}
std::string VideoRecorder::info() const
{
if (active_)
return GstToolkit::time_to_string(timestamp_);
else
return "Saving file...";
}

View File

@@ -1,57 +0,0 @@
#ifndef RECORDER_H
#define RECORDER_H
#include <vector>
#include <gst/pbutils/pbutils.h>
#include <gst/app/gstappsrc.h>
#include "FrameGrabber.h"
class PNGRecorder : public FrameGrabber
{
std::string filename_;
public:
PNGRecorder();
protected:
void init(GstCaps *caps) override;
void terminate() override;
void addFrame(GstBuffer *buffer, GstCaps *caps, float dt) override;
};
class VideoRecorder : public FrameGrabber
{
std::string filename_;
void init(GstCaps *caps) override;
void terminate() override;
public:
typedef enum {
H264_STANDARD = 0,
H264_HQ,
H265_REALTIME,
H265_ANIMATION,
PRORES_STANDARD,
PRORES_HQ,
VP8,
JPEG_MULTI,
DEFAULT
} Profile;
static const char* profile_name[DEFAULT];
static const std::vector<std::string> profile_description;
VideoRecorder();
std::string info() const override;
};
#endif // RECORDER_H

View File

@@ -1,84 +0,0 @@
// Opengl
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/matrix_access.hpp>
#include <glm/gtx/vector_angle.hpp>
#include "defines.h"
#include "Settings.h"
#include "Decorations.h"
#include "RenderView.h"
RenderView::RenderView() : View(RENDERING), frame_buffer_(nullptr), fading_overlay_(nullptr)
{
}
RenderView::~RenderView()
{
if (frame_buffer_)
delete frame_buffer_;
if (fading_overlay_)
delete fading_overlay_;
}
bool RenderView::canSelect(Source *s) {
return false;
}
void RenderView::setFading(float f)
{
if (fading_overlay_ == nullptr)
fading_overlay_ = new Surface;
fading_overlay_->shader()->color.a = CLAMP( f < EPSILON ? 0.f : f, 0.f, 1.f);
}
float RenderView::fading() const
{
if (fading_overlay_)
return fading_overlay_->shader()->color.a;
else
return 0.f;
}
void RenderView::setResolution(glm::vec3 resolution, bool useAlpha)
{
// use default resolution if invalid resolution is given (default behavior)
if (resolution.x < 2.f || resolution.y < 2.f)
resolution = FrameBuffer::getResolutionFromParameters(Settings::application.render.ratio, Settings::application.render.res);
// do we need to change resolution ?
if (frame_buffer_ && frame_buffer_->resolution() != resolution) {
// new frame buffer
delete frame_buffer_;
frame_buffer_ = nullptr;
}
if (!frame_buffer_)
// output frame is an RBG Multisamples FrameBuffer
frame_buffer_ = new FrameBuffer(resolution, useAlpha, true);
// reset fading
setFading();
}
void RenderView::draw()
{
static glm::mat4 projection = glm::ortho(-1.f, 1.f, 1.f, -1.f, -SCENE_DEPTH, 1.f);
if (frame_buffer_) {
// draw in frame buffer
glm::mat4 P = glm::scale( projection, glm::vec3(1.f / frame_buffer_->aspectRatio(), 1.f, 1.f));
// render the scene normally (pre-multiplied alpha in RGB)
frame_buffer_->begin();
scene.root()->draw(glm::identity<glm::mat4>(), P);
fading_overlay_->draw(glm::identity<glm::mat4>(), projection);
frame_buffer_->end();
}
}

View File

@@ -1,27 +0,0 @@
#ifndef RENDERVIEW_H
#define RENDERVIEW_H
#include "View.h"
class RenderView : public View
{
FrameBuffer *frame_buffer_;
Surface *fading_overlay_;
public:
RenderView ();
~RenderView ();
void draw () override;
bool canSelect(Source *) override;
void setResolution (glm::vec3 resolution = glm::vec3(0.f), bool useAlpha = false);
glm::vec3 resolution() const { return frame_buffer_->resolution(); }
void setFading(float f = 0.f);
float fading() const;
inline FrameBuffer *frame () const { return frame_buffer_; }
};
#endif // RENDERVIEW_H

View File

@@ -1,897 +0,0 @@
#include <cstring>
#include <thread>
#include <mutex>
#include <chrono>
#include <stdlib.h>
// Desktop OpenGL function loader
#include <glad/glad.h> // Initialized with gladLoadGLLoader()
// Include glfw3.h after our OpenGL definitions
#define GLFW_INCLUDE_GLEXT
#include <GLFW/glfw3.h>
#ifdef APPLE
#include "osx/CocoaToolkit.h"
#define GLFW_EXPOSE_NATIVE_COCOA
#define GLFW_EXPOSE_NATIVE_NSGL
#else
#define GLFW_EXPOSE_NATIVE_X11
#define GLFW_EXPOSE_NATIVE_GLX
#endif
#include <GLFW/glfw3native.h>
#include <glm/gtc/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale
#include <glm/ext/matrix_clip_space.hpp> // glm::perspective
// Include GStreamer
#include <gst/gl/gl.h>
#include <gst/gl/gstglcontext.h>
#ifdef GLFW_EXPOSE_NATIVE_COCOA
//#include <gst/gl/cocoa/gstgldisplay_cocoa.h>
#endif
#ifdef GLFW_EXPOSE_NATIVE_GLX
#include <gst/gl/x11/gstgldisplay_x11.h>
#endif
// standalone image loader
#include <stb_image.h>
// vmix
#include "defines.h"
#include "Log.h"
#include "Resource.h"
#include "Settings.h"
#include "Primitives.h"
#include "Mixer.h"
#include "SystemToolkit.h"
#include "GstToolkit.h"
#include "UserInterfaceManager.h"
#include "RenderingManager.h"
// local statics
static GstGLContext *global_gl_context = NULL;
static GstGLDisplay *global_display = NULL;
static std::map<GLFWwindow *, RenderingWindow*> GLFW_window_;
static void glfw_error_callback(int error, const char* description)
{
Log::Error("Glfw Error %d: %s", error, description);
}
static void WindowRefreshCallback( GLFWwindow * )
{
Rendering::manager().draw();
}
static void WindowResizeCallback( GLFWwindow *w, int width, int height)
{
int id = GLFW_window_[w]->index();
if (!Settings::application.windows[id].fullscreen) {
Settings::application.windows[id].w = width;
Settings::application.windows[id].h = height;
}
}
static void WindowMoveCallback( GLFWwindow *w, int x, int y)
{
int id = GLFW_window_[w]->index();
if (!Settings::application.windows[id].fullscreen) {
Settings::application.windows[id].x = x;
Settings::application.windows[id].y = y;
}
}
static void WindowEscapeFullscreen( GLFWwindow *w, int key, int, int action, int)
{
if (action == GLFW_PRESS && key == GLFW_KEY_ESCAPE)
{
// escape fullscreen
GLFW_window_[w]->exitFullscreen();
}
}
static void WindowToggleFullscreen( GLFWwindow *w, int button, int action, int)
{
static double seconds = 0.f;
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
{
// detect double clic
if ( glfwGetTime() - seconds < 0.2f ) {
// toggle fullscreen
GLFW_window_[w]->toggleFullscreen();
}
// for next clic detection
seconds = glfwGetTime();
}
}
Rendering::Rendering()
{
// main_window_ = nullptr;
request_screenshot_ = false;
}
bool Rendering::init()
{
// Setup window
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit()){
Log::Error("Failed to Initialize GLFW.");
return false;
}
// Decide GL+GLSL versions GL 3.3 + GLSL 150
glsl_version = "#version 150";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
#if __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
#endif
//
// OpenGL Multisampling main window
//
glfwWindowHint(GLFW_SAMPLES, Settings::application.render.multisampling);
main_.init(0);
// set application icon
main_.setIcon("images/vimix_256x256.png");
// additional window callbacks for main window
glfwSetWindowRefreshCallback( main_.window(), WindowRefreshCallback );
glfwSetDropCallback( main_.window(), Rendering::FileDropped);
//
// Gstreamer setup
//
std::string plugins_path = SystemToolkit::cwd_path() + "gstreamer-1.0";
std::string plugins_scanner = SystemToolkit::cwd_path() + "gst-plugin-scanner" ;
if ( SystemToolkit::file_exists(plugins_path)) {
Log::Info("Found Gstreamer plugins in %s", plugins_path.c_str());
g_setenv ("GST_PLUGIN_SYSTEM_PATH", plugins_path.c_str(), TRUE);
g_setenv ("GST_PLUGIN_SCANNER", plugins_scanner.c_str(), TRUE);
}
g_setenv ("GST_GL_API", "opengl3", TRUE);
gst_init (NULL, NULL);
// increase selection rank for GPU decoding plugins
std::list<std::string> gpuplugins = GstToolkit::enable_gpu_decoding_plugins(Settings::application.render.gpu_decoding);
if (Settings::application.render.gpu_decoding) {
if (gpuplugins.size() > 0) {
Log::Info("Video decoding favoring the following GPU decoding plugin(s):");
for(auto it = gpuplugins.begin(); it != gpuplugins.end(); it++)
Log::Info(" - %s", (*it).c_str());
}
}
//#if GST_GL_HAVE_PLATFORM_WGL
// global_gl_context = gst_gl_context_new_wrapped (display, (guintptr) wglGetCurrentContext (),
// GST_GL_PLATFORM_WGL, GST_GL_API_OPENGL);
//#elif GST_GL_HAVE_PLATFORM_CGL
//// global_display = GST_GL_DISPLAY ( glfwGetCocoaMonitor(main_.window()) );
// global_display = GST_GL_DISPLAY (gst_gl_display_cocoa_new ());
// global_gl_context = gst_gl_context_new_wrapped (global_display,
// (guintptr) 0,
// GST_GL_PLATFORM_CGL, GST_GL_API_OPENGL);
//#elif GST_GL_HAVE_PLATFORM_GLX
// global_display = (GstGLDisplay*) gst_gl_display_x11_new_with_display( glfwGetX11Display() );
// global_gl_context = gst_gl_context_new_wrapped (global_display,
// (guintptr) glfwGetGLXContext(main_.window()),
// GST_GL_PLATFORM_GLX, GST_GL_API_OPENGL);
//#endif
//
// output window
//
glfwWindowHint(GLFW_SAMPLES, 0); // no need for multisampling in displaying output
output_.init(1, main_.window());
output_.setIcon("images/vimix_256x256.png");
// special callbacks for user input in output window
glfwSetKeyCallback( output_.window(), WindowEscapeFullscreen);
glfwSetMouseButtonCallback( output_.window(), WindowToggleFullscreen);
return true;
}
void Rendering::show()
{
// show output window
output_.show();
// show main window
main_.show();
// show menu on first show
UserInterface::manager().showPannel(NAV_MENU);
}
bool Rendering::isActive()
{
return !glfwWindowShouldClose(main_.window());
}
void Rendering::pushFrontDrawCallback(RenderingCallback function)
{
draw_callbacks_.push_front(function);
}
void Rendering::pushBackDrawCallback(RenderingCallback function)
{
draw_callbacks_.push_back(function);
}
void Rendering::draw()
{
// guint64 _time = gst_util_get_timestamp ();
// operate on main window context
main_.makeCurrent();
// User Interface step 1
UserInterface::manager().NewFrame();
// Custom draw
std::list<Rendering::RenderingCallback>::iterator iter;
for (iter=draw_callbacks_.begin(); iter != draw_callbacks_.end(); iter++)
{
(*iter)();
}
// User Interface step 2
UserInterface::manager().Render();
// perform screenshot if requested
if (request_screenshot_) {
// glfwMakeContextCurrent(main_window_);
screenshot_.captureGL(0, 0, main_.width(), main_.height());
request_screenshot_ = false;
}
// draw output window (and swap buffer output)
output_.draw( Mixer::manager().session()->frame() );
// swap GL buffers
glfwSwapBuffers(main_.window());
glfwSwapBuffers(output_.window());
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
glfwPollEvents();
// change windows
main_.toggleFullscreen_();
output_.toggleFullscreen_();
// no g_main_loop_run(loop) : update global GMainContext
g_main_context_iteration(NULL, FALSE);
// software framerate limiter 60FPS if not v-sync
if ( Settings::application.render.vsync < 1 ) {
static GTimer *timer = g_timer_new ();
double elapsed = g_timer_elapsed (timer, NULL) * 1000000.0;
if (elapsed < 16000)
g_usleep( 16000 - (gulong)elapsed );
g_timer_start(timer);
}
}
void Rendering::terminate()
{
// close window
glfwDestroyWindow(output_.window());
glfwDestroyWindow(main_.window());
glfwTerminate();
}
void Rendering::close()
{
glfwSetWindowShouldClose(main_.window(), true);
}
void Rendering::pushAttrib(RenderingAttrib ra)
{
// push it to top of pile
draw_attributes_.push_front(ra);
// apply Changes to OpenGL
glViewport(0, 0, ra.viewport.x, ra.viewport.y);
glClearColor(ra.clear_color.r, ra.clear_color.g, ra.clear_color.b, ra.clear_color.a);
}
void Rendering::popAttrib()
{
// pops the top of the pile
if (draw_attributes_.size() > 0)
draw_attributes_.pop_front();
// set attribute element to default
RenderingAttrib ra = currentAttrib();
// apply Changes to OpenGL
glViewport(0, 0, ra.viewport.x, ra.viewport.y);
glClearColor(ra.clear_color.r, ra.clear_color.g, ra.clear_color.b, ra.clear_color.a);
}
RenderingAttrib Rendering::currentAttrib()
{
// default rendering attrib is the main window's
RenderingAttrib ra = main_.attribs();
// but if there is an element at top, return it
if (draw_attributes_.size() > 0)
ra = draw_attributes_.front();
return ra;
}
glm::mat4 Rendering::Projection()
{
static glm::mat4 projection = glm::ortho(-SCENE_UNIT, SCENE_UNIT, -SCENE_UNIT, SCENE_UNIT, -SCENE_DEPTH, 1.f);
glm::mat4 scale = glm::scale(glm::identity<glm::mat4>(), glm::vec3(1.f, main_.aspectRatio(), 1.f));
return projection * scale;
}
glm::vec3 Rendering::unProject(glm::vec2 screen_coordinate, glm::mat4 modelview)
{
glm::vec3 coordinates = glm::vec3( screen_coordinate.x, main_.height() - screen_coordinate.y, 0.f);
glm::vec4 viewport = glm::vec4( 0.f, 0.f, main_.width(), main_.height());
// Log::Info("unproject %d x %d", main_window_attributes_.viewport.x, main_window_attributes_.viewport.y);
glm::vec3 point = glm::unProject(coordinates, modelview, Projection(), viewport);
return point;
}
glm::vec2 Rendering::project(glm::vec3 scene_coordinate, glm::mat4 modelview, bool to_framebuffer)
{
glm::vec4 viewport;
if (to_framebuffer)
viewport= glm::vec4( 0.f, 0.f, main_.width(), main_.height());
else
viewport= glm::vec4( 0.f, 0.f, main_.width() / main_.dpiScale(), main_.height() / main_.dpiScale());
glm::vec3 P = glm::project( scene_coordinate, modelview, Projection(), viewport );
return glm::vec2(P.x, viewport.w - P.y);
}
void Rendering::FileDropped(GLFWwindow *, int path_count, const char* paths[])
{
for (int i = 0; i < path_count; ++i) {
std::string filename(paths[i]);
if (filename.empty())
break;
// try to create a source
Mixer::manager().addSource ( Mixer::manager().createSourceFile( filename ) );
}
if (path_count>0) {
UserInterface::manager().showPannel();
Rendering::manager().mainWindow().show();
}
}
Screenshot *Rendering::currentScreenshot()
{
return &screenshot_;
}
void Rendering::requestScreenshot()
{
request_screenshot_ = true;
}
// custom surface with a new VAO
class WindowSurface : public Primitive {
public:
WindowSurface(Shader *s = new ImageShader);
};
WindowSurface::WindowSurface(Shader *s) : Primitive(s)
{
points_ = std::vector<glm::vec3> { glm::vec3( -1.f, -1.f, 0.f ), glm::vec3( -1.f, 1.f, 0.f ),
glm::vec3( 1.f, -1.f, 0.f ), glm::vec3( 1.f, 1.f, 0.f ) };
colors_ = std::vector<glm::vec4> { glm::vec4( 1.f, 1.f, 1.f , 1.f ), glm::vec4( 1.f, 1.f, 1.f, 1.f ),
glm::vec4( 1.f, 1.f, 1.f, 1.f ), glm::vec4( 1.f, 1.f, 1.f, 1.f ) };
texCoords_ = std::vector<glm::vec2> { glm::vec2( 0.f, 1.f ), glm::vec2( 0.f, 0.f ),
glm::vec2( 1.f, 1.f ), glm::vec2( 1.f, 0.f ) };
indices_ = std::vector<uint> { 0, 1, 2, 3 };
drawMode_ = GL_TRIANGLE_STRIP;
}
RenderingWindow::RenderingWindow() : window_(nullptr), master_(nullptr),
index_(-1), dpi_scale_(1.f), textureid_(0), fbo_(0), surface_(nullptr), request_toggle_fullscreen_(false)
{
}
RenderingWindow::~RenderingWindow()
{
if (surface_ != nullptr)
delete surface_;
if (fbo_ != 0)
glDeleteFramebuffers(1, &fbo_);
}
void RenderingWindow::setTitle(const std::string &title)
{
std::string fulltitle = Settings::application.windows[index_].name;
if ( !title.empty() )
fulltitle += " -- " + title;
glfwSetWindowTitle(window_, fulltitle.c_str());
}
void RenderingWindow::setIcon(const std::string &resource)
{
size_t fpsize = 0;
const char *fp = Resource::getData(resource, &fpsize);
if (fp != nullptr) {
GLFWimage icon;
icon.pixels = stbi_load_from_memory( (const stbi_uc*)fp, fpsize, &icon.width, &icon.height, nullptr, 4 );
glfwSetWindowIcon( window_, 1, &icon );
free( icon.pixels );
}
}
bool RenderingWindow::isFullscreen ()
{
// return (glfwGetWindowMonitor(window_) != nullptr);
return Settings::application.windows[index_].fullscreen;
}
GLFWmonitor *RenderingWindow::monitorAt(int x, int y)
{
// default to primary monitor
GLFWmonitor *mo = glfwGetPrimaryMonitor();
// list all monitors
int count_monitors = 0;
GLFWmonitor** monitors = glfwGetMonitors(&count_monitors);
// if there is more than one monitor
if (count_monitors > 1) {
// pick at the coordinates given or at pos of window
// try every monitor
int i = 0;
for (; i < count_monitors; i++) {
int workarea_x, workarea_y, workarea_width, workarea_height;
#if GLFW_VERSION_MINOR > 2
glfwGetMonitorWorkarea(monitors[i], &workarea_x, &workarea_y, &workarea_width, &workarea_height);
#else
glfwGetMonitorPos(monitors[i], &workarea_x, &workarea_y);
const GLFWvidmode *vm = glfwGetVideoMode(monitors[i]);
workarea_width = vm->width;
workarea_height = vm->height;
#endif
if ( x >= workarea_x && x <= workarea_x + workarea_width &&
y >= workarea_y && y <= workarea_y + workarea_height)
break;
}
// found the monitor containing this point !
if ( i < count_monitors)
mo = monitors[i];
}
return mo;
}
GLFWmonitor *RenderingWindow::monitorNamed(const std::string &name)
{
// default to primary monitor
GLFWmonitor *mo = glfwGetPrimaryMonitor();
// list all monitors
int count_monitors = 0;
GLFWmonitor** monitors = glfwGetMonitors(&count_monitors);
// if there is more than one monitor
if (count_monitors > 1) {
// pick at the coordinates given or at pos of window
// try every monitor
int i = 0;
for (; i < count_monitors; i++) {
if ( std::string( glfwGetMonitorName(monitors[i])) == name )
break;
}
// found the monitor with this name
if ( i < count_monitors)
mo = monitors[i];
}
return mo;
}
GLFWmonitor *RenderingWindow::monitor()
{
// pick at the coordinates given or at pos of window
int x, y;
glfwGetWindowPos(window_, &x, &y);
return monitorAt(x, y);
}
void RenderingWindow::setFullscreen_(GLFWmonitor *mo)
{
// done request
request_toggle_fullscreen_ = false;
// if in fullscreen mode
if (mo == nullptr) {
// store fullscreen mode
Settings::application.windows[index_].fullscreen = false;
// set to window mode
glfwSetInputMode( window_, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
glfwSetWindowMonitor( window_, nullptr, Settings::application.windows[index_].x,
Settings::application.windows[index_].y,
Settings::application.windows[index_].w,
Settings::application.windows[index_].h, 0 );
}
// not in fullscreen mode
else {
// store fullscreen mode
Settings::application.windows[index_].fullscreen = true;
Settings::application.windows[index_].monitor = glfwGetMonitorName(mo);
// set to fullscreen mode
const GLFWvidmode * mode = glfwGetVideoMode(mo);
glfwSetInputMode( window_, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
glfwSetWindowMonitor( window_, mo, 0, 0, mode->width, mode->height, mode->refreshRate);
// Enable vsync on output window only (i.e. not 0 if has a master)
// Workaround for disabled vsync in fullscreen (https://github.com/glfw/glfw/issues/1072)
glfwSwapInterval( nullptr == master_ ? 0 : Settings::application.render.vsync);
}
}
void RenderingWindow::exitFullscreen()
{
if (isFullscreen()) {
// exit fullscreen
request_toggle_fullscreen_ = true;
}
}
void RenderingWindow::toggleFullscreen()
{
request_toggle_fullscreen_ = true;
}
void RenderingWindow::toggleFullscreen_()
{
if (request_toggle_fullscreen_) {
// if in fullscreen mode
if (glfwGetWindowMonitor(window_) != nullptr) {
// exit fullscreen
setFullscreen_(nullptr);
}
// not in fullscreen mode
else {
// enter fullscreen in monitor where the window is
setFullscreen_(monitor());
}
}
}
int RenderingWindow::width()
{
return window_attributes_.viewport.x;
}
int RenderingWindow::height()
{
return window_attributes_.viewport.y;
}
int RenderingWindow::pixelsforRealHeight(float milimeters)
{
GLFWmonitor *mo = monitor();
int mm_w = 0;
int mm_h = 0;
glfwGetMonitorPhysicalSize(mo, &mm_w, &mm_h);
float pixels = milimeters;
if (mm_h > 0)
pixels *= static_cast<float>(glfwGetVideoMode(mo)->height) / static_cast<float>(mm_h);
else
pixels *= 5; // something reasonnable if monitor's physical size is unknown
return static_cast<int>( round(pixels) );
}
float RenderingWindow::aspectRatio()
{
return static_cast<float>(window_attributes_.viewport.x) / static_cast<float>(window_attributes_.viewport.y);
}
bool RenderingWindow::init(int index, GLFWwindow *share)
{
index_ = index;
master_ = share;
// access Settings
Settings::WindowConfig winset = Settings::application.windows[index_];
// do not show at creation
glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE);
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
glfwWindowHint(GLFW_AUTO_ICONIFY, GLFW_FALSE);
// create the window normal
window_ = glfwCreateWindow(winset.w, winset.h, winset.name.c_str(), NULL, master_);
if (window_ == NULL){
Log::Error("Failed to create GLFW Window %d", index_);
return false;
}
// set position
glfwSetWindowPos(window_, winset.x, winset.y);
/// CALLBACKS
// store global ref to pointers (used by callbacks)
GLFW_window_[window_] = this;
// window position and resize callbacks
glfwSetWindowSizeCallback( window_, WindowResizeCallback );
// glfwSetFramebufferSizeCallback( window_, WindowResizeCallback );
glfwSetWindowPosCallback( window_, WindowMoveCallback );
// take opengl context ownership
glfwMakeContextCurrent(window_);
//
// Initialize OpenGL loader on first call
//
static bool glad_initialized = false;
if ( !glad_initialized ) {
bool err = gladLoadGLLoader((GLADloadproc) glfwGetProcAddress) == 0;
if (err) {
Log::Error("Failed to initialize GLAD OpenGL loader.");
return false;
}
glad_initialized = true;
}
// get rendering area
glfwGetFramebufferSize(window_, &(window_attributes_.viewport.x), &(window_attributes_.viewport.y));
// DPI scaling (retina)
dpi_scale_ = float(window_attributes_.viewport.y) / float(winset.h);
// This hint can improve the speed of texturing when perspective-correct texture coordinate interpolation isn't needed
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
// fast mipmaps (we are not really using mipmaps anyway)
glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST);
// acurate derivative for shader
glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
// if not main window
if ( master_ != NULL ) {
// Enable vsync on output window
glfwSwapInterval(Settings::application.render.vsync);
// no need for multisampling
glDisable(GL_MULTISAMPLE);
// clear to black
window_attributes_.clear_color = glm::vec4(0.f, 0.f, 0.f, 1.f);
// give back context ownership
glfwMakeContextCurrent(master_);
}
else {
// Disable vsync on main window
glfwSwapInterval(0);
// Enable Antialiasing multisampling
if (Settings::application.render.multisampling > 0) {
glEnable(GL_MULTISAMPLE);
glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
}
// clear to grey
window_attributes_.clear_color = glm::vec4(COLOR_BGROUND, 1.f);
}
return true;
}
void RenderingWindow::show()
{
glfwShowWindow(window_);
if ( Settings::application.windows[index_].fullscreen ) {
GLFWmonitor *mo = monitorNamed(Settings::application.windows[index_].monitor);
setFullscreen_(mo);
}
}
void RenderingWindow::makeCurrent()
{
// handle window resize
glfwGetFramebufferSize(window_, &(window_attributes_.viewport.x), &(window_attributes_.viewport.y));
// ensure main context is current
glfwMakeContextCurrent(window_);
// set and clear
glViewport(0, 0, window_attributes_.viewport.x, window_attributes_.viewport.y);
glClearColor(window_attributes_.clear_color.r, window_attributes_.clear_color.g,
window_attributes_.clear_color.b, window_attributes_.clear_color.a);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
// TODO update parameters for draw on resize event (not every frame)
void RenderingWindow::draw(FrameBuffer *fb)
{
if (!window_ || !fb)
return;
// only draw if window is not iconified
if( !glfwGetWindowAttrib(window_, GLFW_ICONIFIED ) ) {
// update viewport (could be done with callback)
glfwGetFramebufferSize(window_, &(window_attributes_.viewport.x), &(window_attributes_.viewport.y));
// take context ownership
glfwMakeContextCurrent(window_);
// setup attribs
Rendering::manager().pushAttrib(window_attributes_);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// blit framebuffer
if (Settings::application.render.blit) {
if ( textureid_ != fb->texture()) {
textureid_ = fb->texture();
// create a new fbo in this opengl context
if (fbo_ != 0)
glDeleteFramebuffers(1, &fbo_);
glGenFramebuffers(1, &fbo_);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
// attach the 2D texture to local FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid_, 0);
Log::Info("Blit to output window enabled.");
}
// calculate scaling factor of frame buffer inside window
int rx, ry, rw, rh;
float renderingAspectRatio = fb->aspectRatio();
if (aspectRatio() < renderingAspectRatio) {
int nh = (int)( float(window_attributes_.viewport.x) / renderingAspectRatio);
rx = 0;
ry = (window_attributes_.viewport.y - nh) / 2;
rw = window_attributes_.viewport.x;
rh = (window_attributes_.viewport.y + nh) / 2;
} else {
int nw = (int)( float(window_attributes_.viewport.y) * renderingAspectRatio );
rx = (window_attributes_.viewport.x - nw) / 2;
ry = 0;
rw = (window_attributes_.viewport.x + nw) / 2;
rh = window_attributes_.viewport.y;
}
// select fbo texture read target
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_);
// select screen target
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
// blit operation from fbo (containing texture) to screen
glBlitFramebuffer(0, fb->height(), fb->width(), 0, rx, ry, rw, rh, GL_COLOR_BUFFER_BIT, GL_LINEAR);
}
// draw geometry
else
{
// VAO is not shared between multiple contexts of different windows
// so we have to create a new VAO for rendering the surface in this window
if (surface_ == 0)
surface_ = new WindowSurface;
// calculate scaling factor of frame buffer inside window
float windowAspectRatio = aspectRatio();
float renderingAspectRatio = fb->aspectRatio();
glm::vec3 scale;
if (windowAspectRatio < renderingAspectRatio)
scale = glm::vec3(1.f, windowAspectRatio / renderingAspectRatio, 1.f);
else
scale = glm::vec3(renderingAspectRatio / windowAspectRatio, 1.f, 1.f);
// make sure previous shader in another glcontext is disabled
ShadingProgram::enduse();
// draw
glBindTexture(GL_TEXTURE_2D, fb->texture());
// surface->shader()->color.a = 0.4f; // TODO alpha blending ?
static glm::mat4 projection = glm::ortho(-1.f, 1.f, -1.f, 1.f, -1.f, 1.f);
surface_->draw(glm::scale(glm::identity<glm::mat4>(), scale), projection);
// done drawing (unload shader from this glcontext)
ShadingProgram::enduse();
glBindTexture(GL_TEXTURE_2D, 0);
}
// restore attribs
Rendering::manager().popAttrib();
}
// give back context ownership
glfwMakeContextCurrent(master_);
}
//
// Discarded because not working under OSX - kept in case it would become useful
//
// Linking pipeline to the rendering instance ensures the opengl contexts
// created by gstreamer inside plugins (e.g. glsinkbin) is the same
//
static GstBusSyncReply
bus_sync_handler (GstBus *, GstMessage * msg, gpointer )
{
if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_NEED_CONTEXT) {
const gchar* contextType;
gst_message_parse_context_type(msg, &contextType);
if (!g_strcmp0(contextType, GST_GL_DISPLAY_CONTEXT_TYPE)) {
GstContext *displayContext = gst_context_new(GST_GL_DISPLAY_CONTEXT_TYPE, TRUE);
gst_context_set_gl_display(displayContext, global_display);
gst_element_set_context(GST_ELEMENT(msg->src), displayContext);
gst_context_unref (displayContext);
g_info ("Managed %s\n", contextType);
}
if (!g_strcmp0(contextType, "gst.gl.app_context")) {
GstContext *appContext = gst_context_new("gst.gl.app_context", TRUE);
GstStructure* structure = gst_context_writable_structure(appContext);
gst_structure_set(structure, "context", GST_TYPE_GL_CONTEXT, global_gl_context, nullptr);
gst_element_set_context(GST_ELEMENT(msg->src), appContext);
gst_context_unref (appContext);
g_info ("Managed %s\n", contextType);
}
}
gst_message_unref (msg);
return GST_BUS_DROP;
}
void Rendering::LinkPipeline( GstPipeline *pipeline )
{
// capture bus signals to force a unique opengl context for all GST elements
GstBus* m_bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
gst_bus_set_sync_handler (m_bus, (GstBusSyncHandler) bus_sync_handler, pipeline, NULL);
gst_object_unref (m_bus);
// GstBus* m_bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
// gst_bus_enable_sync_message_emission (m_bus);
// g_signal_connect (m_bus, "sync-message", G_CALLBACK (bus_sync_handler), pipeline);
// gst_object_unref (m_bus);
}

View File

@@ -1,137 +0,0 @@
#include "Screenshot.h"
#include <memory.h>
#include <assert.h>
#include <thread>
#include <atomic>
#include <glad/glad.h>
// standalone image loader
#include <stb_image.h>
#include <stb_image_write.h>
Screenshot::Screenshot()
{
Width = Height = 0;
Data = nullptr;
Pbo = 0;
Pbo_size = 0;
Pbo_full = false;
}
Screenshot::~Screenshot()
{
if (Pbo > 0)
glDeleteBuffers(1, &Pbo);
if (Data)
free(Data);
}
bool Screenshot::isFull()
{
return Pbo_full;
}
void Screenshot::captureGL(int x, int y, int w, int h)
{
Width = w - x;
Height = h - y;
unsigned int size = Width * Height * 3;
// create BPO
if (Pbo == 0)
glGenBuffers(1, &Pbo);
// bind
glBindBuffer(GL_PIXEL_PACK_BUFFER, Pbo);
// init
if (Pbo_size != size) {
Pbo_size = size;
if (Data) free(Data);
Data = (unsigned char*) malloc(Pbo_size);
glBufferData(GL_PIXEL_PACK_BUFFER, Pbo_size, NULL, GL_STREAM_READ);
}
// screenshot to PBO (fast)
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(x, y, w, h, GL_RGB, GL_UNSIGNED_BYTE, 0);
Pbo_full = true;
// done
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
void Screenshot::save(std::string filename)
{
// is there something to save?
if (Pbo && Pbo_size > 0 && Pbo_full) {
// bind buffer
glBindBuffer(GL_PIXEL_PACK_BUFFER, Pbo);
// get pixels (quite fast)
unsigned char* ptr = (unsigned char*) glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
if (NULL != ptr) {
memmove(Data, ptr, Pbo_size);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
}
// initiate saving in thread (slow)
std::thread(storeToFile, this, filename).detach();
// ready for next
Pbo_full = false;
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
}
void Screenshot::RemoveAlpha()
{
unsigned int* p = (unsigned int*)Data;
int n = Width * Height;
while (n-- > 0)
{
*p |= 0xFF000000;
p++;
}
}
void Screenshot::FlipVertical()
{
int comp = 4;
int stride = Width * comp;
unsigned char* line_tmp = new unsigned char[stride];
unsigned char* line_a = (unsigned char*)Data;
unsigned char* line_b = (unsigned char*)Data + (stride * (Height - 1));
while (line_a < line_b)
{
memcpy(line_tmp, line_a, stride);
memcpy(line_a, line_b, stride);
memcpy(line_b, line_tmp, stride);
line_a += stride;
line_b -= stride;
}
delete[] line_tmp;
}
// Thread to perform slow operation of saving to file
void Screenshot::storeToFile(Screenshot *s, std::string filename)
{
static std::atomic<bool> ScreenshotSavePending_ = false;
// only one save at a time
if (ScreenshotSavePending_)
return;
ScreenshotSavePending_ = true;
// got data to save ?
if (s && s->Data) {
// save file
stbi_flip_vertically_on_write(true);
stbi_write_png(filename.c_str(), s->Width, s->Height, 3, s->Data, s->Width * 3);
}
ScreenshotSavePending_ = false;
}

View File

@@ -1,449 +0,0 @@
#include <algorithm>
#include "defines.h"
#include "Settings.h"
#include "FrameBuffer.h"
#include "Session.h"
#include "FrameGrabber.h"
#include "SessionCreator.h"
#include "SessionSource.h"
#include "MixingGroup.h"
#include "Log.h"
Session::Session() : failedSource_(nullptr), active_(true), fading_target_(0.f)
{
filename_ = "";
config_[View::RENDERING] = new Group;
config_[View::RENDERING]->scale_ = glm::vec3(0.f);
config_[View::GEOMETRY] = new Group;
config_[View::GEOMETRY]->scale_ = Settings::application.views[View::GEOMETRY].default_scale;
config_[View::GEOMETRY]->translation_ = Settings::application.views[View::GEOMETRY].default_translation;
config_[View::LAYER] = new Group;
config_[View::LAYER]->scale_ = Settings::application.views[View::LAYER].default_scale;
config_[View::LAYER]->translation_ = Settings::application.views[View::LAYER].default_translation;
config_[View::MIXING] = new Group;
config_[View::MIXING]->scale_ = Settings::application.views[View::MIXING].default_scale;
config_[View::MIXING]->translation_ = Settings::application.views[View::MIXING].default_translation;
config_[View::TEXTURE] = new Group;
config_[View::TEXTURE]->scale_ = Settings::application.views[View::TEXTURE].default_scale;
config_[View::TEXTURE]->translation_ = Settings::application.views[View::TEXTURE].default_translation;
}
Session::~Session()
{
// TODO delete all mixing groups?
auto group_iter = mixing_groups_.begin();
while ( group_iter != mixing_groups_.end() ){
delete (*group_iter);
group_iter = mixing_groups_.erase(group_iter);
}
// delete all sources
for(auto it = sources_.begin(); it != sources_.end(); ) {
// erase this source from the list
it = deleteSource(*it);
}
delete config_[View::RENDERING];
delete config_[View::GEOMETRY];
delete config_[View::LAYER];
delete config_[View::MIXING];
delete config_[View::TEXTURE];
}
void Session::setActive (bool on)
{
if (active_ != on) {
active_ = on;
for(auto it = sources_.begin(); it != sources_.end(); it++) {
(*it)->setActive(active_);
}
}
}
// update all sources
void Session::update(float dt)
{
// no update until render view is initialized
if ( render_.frame() == nullptr )
return;
// pre-render of all sources
failedSource_ = nullptr;
for( SourceList::iterator it = sources_.begin(); it != sources_.end(); it++){
// ensure the RenderSource is rendering this session
RenderSource *s = dynamic_cast<RenderSource *>( *it );
if ( s!= nullptr && s->session() != this )
s->setSession(this);
if ( (*it)->failed() ) {
failedSource_ = (*it);
}
else {
// render the source
(*it)->render();
// update the source
(*it)->update(dt);
}
}
// update session's mixing groups
auto group_iter = mixing_groups_.begin();
while ( group_iter != mixing_groups_.end() ){
// update all valid groups
if ((*group_iter)->size() > 1) {
(*group_iter)->update(dt);
group_iter++;
}
else
// delete invalid groups (singletons)
group_iter = deleteMixingGroup(group_iter);
}
// apply fading (smooth dicotomic reaching)
float f = render_.fading();
if ( ABS_DIFF(f, fading_target_) > EPSILON) {
render_.setFading( f + ( fading_target_ - f ) / 2.f);
}
// update the scene tree
render_.update(dt);
// draw render view in Frame Buffer
render_.draw();
}
SourceList::iterator Session::addSource(Source *s)
{
SourceList::iterator its = sources_.end();
// lock before change
access_.lock();
// find the source
its = find(s);
// ok, its NOT in the list !
if (its == sources_.end()) {
// insert the source in the rendering
render_.scene.ws()->attach(s->group(View::RENDERING));
// insert the source to the beginning of the list
sources_.push_front(s);
// return the iterator to the source created at the beginning
its = sources_.begin();
}
// unlock access
access_.unlock();
return its;
}
SourceList::iterator Session::deleteSource(Source *s)
{
// lock before change
access_.lock();
// find the source
SourceList::iterator its = find(s);
// ok, its in the list !
if (its != sources_.end()) {
// remove Node from the rendering scene
render_.scene.ws()->detach( s->group(View::RENDERING) );
// inform group
if (s->mixingGroup() != nullptr)
s->mixingGroup()->detach(s);
// erase the source from the update list & get next element
its = sources_.erase(its);
// delete the source : safe now
delete s;
}
// unlock access
access_.unlock();
// return end of next element
return its;
}
void Session::removeSource(Source *s)
{
// lock before change
access_.lock();
// find the source
SourceList::iterator its = find(s);
// ok, its in the list !
if (its != sources_.end()) {
// remove Node from the rendering scene
render_.scene.ws()->detach( s->group(View::RENDERING) );
// inform group
if (s->mixingGroup() != nullptr)
s->mixingGroup()->detach(s);
// erase the source from the update list & get next element
sources_.erase(its);
}
// unlock access
access_.unlock();
}
Source *Session::popSource()
{
Source *s = nullptr;
SourceList::iterator its = sources_.begin();
if (its != sources_.end())
{
s = *its;
// remove Node from the rendering scene
render_.scene.ws()->detach( s->group(View::RENDERING) );
// erase the source from the update list & get next element
sources_.erase(its);
}
return s;
}
void Session::setResolution(glm::vec3 resolution, bool useAlpha)
{
// setup the render view: if not specified the default config resulution will be used
render_.setResolution( resolution, useAlpha );
// store the actual resolution set in the render view
config_[View::RENDERING]->scale_ = render_.resolution();
}
void Session::setFading(float f, bool forcenow)
{
if (forcenow)
render_.setFading( f );
fading_target_ = CLAMP(f, 0.f, 1.f);
}
SourceList::iterator Session::begin()
{
return sources_.begin();
}
SourceList::iterator Session::end()
{
return sources_.end();
}
SourceList::iterator Session::find(Source *s)
{
return std::find(sources_.begin(), sources_.end(), s);
}
SourceList::iterator Session::find(uint64_t id)
{
return std::find_if(sources_.begin(), sources_.end(), Source::hasId(id));
}
SourceList::iterator Session::find(std::string namesource)
{
return std::find_if(sources_.begin(), sources_.end(), Source::hasName(namesource));
}
SourceList::iterator Session::find(Node *node)
{
return std::find_if(sources_.begin(), sources_.end(), Source::hasNode(node));
}
SourceList::iterator Session::find(float depth_from, float depth_to)
{
return std::find_if(sources_.begin(), sources_.end(), Source::hasDepth(depth_from, depth_to));
}
SourceList Session::getDepthSortedList() const
{
return depth_sorted(sources_);
}
uint Session::numSource() const
{
return sources_.size();
}
SourceIdList Session::getIdList() const
{
return ids(sources_);
}
bool Session::empty() const
{
return sources_.empty();
}
SourceList::iterator Session::at(int index)
{
if (index<0)
return sources_.end();
int i = 0;
SourceList::iterator it = sources_.begin();
while ( i < index && it != sources_.end() ){
i++;
it++;
}
return it;
}
int Session::index(SourceList::iterator it) const
{
int index = -1;
int count = 0;
for(auto i = sources_.begin(); i != sources_.end(); i++, count++) {
if ( i == it ) {
index = count;
break;
}
}
return index;
}
void Session::move(int current_index, int target_index)
{
if ( current_index < 0 || current_index > (int) sources_.size()
|| target_index < 0 || target_index > (int) sources_.size()
|| target_index == current_index )
return;
SourceList::iterator from = at(current_index);
SourceList::iterator to = at(target_index);
if ( target_index > current_index )
to++;
Source *s = (*from);
sources_.erase(from);
sources_.insert(to, s);
}
bool Session::canlink (SourceList sources)
{
bool canlink = true;
// verify that all sources given are valid in the sesion
validate(sources);
for (auto it = sources.begin(); it != sources.end(); it++) {
// this source is linked
if ( (*it)->mixingGroup() != nullptr ) {
// askt its group to detach it
canlink = false;
}
}
return canlink;
}
void Session::link(SourceList sources, Group *parent)
{
// we need at least 2 sources to make a group
if (sources.size() > 1) {
unlink(sources);
// create and add a new mixing group
MixingGroup *g = new MixingGroup(sources);
mixing_groups_.push_back(g);
// if provided, attach the group to the parent
if (g && parent != nullptr)
g->attachTo( parent );
}
}
void Session::unlink (SourceList sources)
{
// verify that all sources given are valid in the sesion
validate(sources);
// brute force : detach all given sources
for (auto it = sources.begin(); it != sources.end(); it++) {
// this source is linked
if ( (*it)->mixingGroup() != nullptr ) {
// askt its group to detach it
(*it)->mixingGroup()->detach(*it);
}
}
}
std::list<SourceList> Session::getMixingGroups () const
{
std::list<SourceList> lmg;
for (auto group_it = mixing_groups_.begin(); group_it!= mixing_groups_.end(); group_it++)
lmg.push_back( (*group_it)->getCopy() );
return lmg;
}
std::list<MixingGroup *>::iterator Session::deleteMixingGroup (std::list<MixingGroup *>::iterator g)
{
if (g != mixing_groups_.end()) {
delete (*g);
return mixing_groups_.erase(g);
}
return mixing_groups_.end();
}
std::list<MixingGroup *>::iterator Session::beginMixingGroup()
{
return mixing_groups_.begin();
}
std::list<MixingGroup *>::iterator Session::endMixingGroup()
{
return mixing_groups_.end();
}
void Session::lock()
{
access_.lock();
}
void Session::unlock()
{
access_.unlock();
}
void Session::validate (SourceList &sources)
{
// verify that all sources given are valid in the sesion
// and remove the invalid sources
for (auto _it = sources.begin(); _it != sources.end(); ) {
SourceList::iterator found = std::find(sources_.begin(), sources_.end(), *_it);
if ( found == sources_.end() )
_it = sources.erase(_it);
else
_it++;
}
}
Session *Session::load(const std::string& filename, uint recursion)
{
// create session
SessionCreator creator(recursion);
creator.load(filename);
// return created session
return creator.session();
}

114
Session.h
View File

@@ -1,114 +0,0 @@
#ifndef SESSION_H
#define SESSION_H
#include <mutex>
#include "RenderView.h"
#include "Source.h"
class FrameGrabber;
class MixingGroup;
class Session
{
public:
Session();
~Session();
static Session *load(const std::string& filename, uint recursion = 0);
// add given source into the session
SourceList::iterator addSource (Source *s);
// delete the source s from the session
SourceList::iterator deleteSource (Source *s);
// remove this source from the session
// Does not delete the source
void removeSource(Source *s);
// get ptr to front most source and remove it from the session
// Does not delete the source
Source *popSource();
// management of list of sources
bool empty() const;
uint numSource() const;
SourceList::iterator begin ();
SourceList::iterator end ();
SourceList::iterator find (Source *s);
SourceList::iterator find (std::string name);
SourceList::iterator find (Node *node);
SourceList::iterator find (float depth_from, float depth_to);
SourceList getDepthSortedList () const;
SourceList::iterator find (uint64_t id);
SourceIdList getIdList() const;
SourceList::iterator at (int index);
int index (SourceList::iterator it) const;
void move (int current_index, int target_index);
// update all sources and mark sources which failed
void update (float dt);
// update mode (active or not)
void setActive (bool on);
inline bool active () { return active_; }
// return the last source which failed
Source *failedSource () { return failedSource_; }
// get frame result of render
inline FrameBuffer *frame () const { return render_.frame(); }
// configure rendering resolution
void setResolution (glm::vec3 resolution, bool useAlpha = false);
// manipulate fading of output
void setFading (float f, bool forcenow = false);
inline float fading () const { return fading_target_; }
// configuration for group nodes of views
inline Group *config (View::Mode m) const { return config_.at(m); }
// name of file containing this session (for transfer)
void setFilename (const std::string &filename) { filename_ = filename; }
std::string filename () const { return filename_; }
// get the list of sources in mixing groups
std::list<SourceList> getMixingGroups () const;
// returns true if something can be done to create a mixing group
bool canlink (SourceList sources);
// try to link sources of the given list:
// can either create a new mixing group or extend an existing group
void link (SourceList sources, Group *parent = nullptr);
// try to unlink sources of the given list:
// can either delete an entire mixing group, or remove the given sources from existing groups
void unlink (SourceList sources);
// iterators for looping over mixing groups
std::list<MixingGroup *>::iterator beginMixingGroup ();
std::list<MixingGroup *>::iterator endMixingGroup ();
std::list<MixingGroup *>::iterator deleteMixingGroup (std::list<MixingGroup *>::iterator g);
// lock and unlock access (e.g. while saving)
void lock ();
void unlock ();
protected:
RenderView render_;
std::string filename_;
Source *failedSource_;
SourceList sources_;
void validate(SourceList &sources);
std::list<MixingGroup *> mixing_groups_;
std::map<View::Mode, Group*> config_;
bool active_;
std::list<FrameGrabber *> grabbers_;
float fading_target_;
std::mutex access_;
};
#endif // SESSION_H

View File

@@ -1,668 +0,0 @@
#include "SessionCreator.h"
#include "Log.h"
#include "defines.h"
#include "Scene.h"
#include "Primitives.h"
#include "Mesh.h"
#include "Source.h"
#include "MediaSource.h"
#include "SessionSource.h"
#include "StreamSource.h"
#include "PatternSource.h"
#include "DeviceSource.h"
#include "NetworkSource.h"
#include "Session.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "MediaPlayer.h"
#include <tinyxml2.h>
#include "tinyxml2Toolkit.h"
using namespace tinyxml2;
std::string SessionCreator::info(const std::string& filename)
{
std::string ret = "";
XMLDocument doc;
XMLError eResult = doc.LoadFile(filename.c_str());
if ( XMLResultError(eResult)) {
Log::Warning("%s could not be openned.", filename.c_str());
return ret;
}
XMLElement *header = doc.FirstChildElement(APP_NAME);
if (header != nullptr && header->Attribute("date") != 0) {
int s = header->IntAttribute("size");
ret = std::to_string( s ) + " source" + ( s > 1 ? "s\n" : "\n");
const char *att_string = header->Attribute("resolution");
if (att_string)
ret += std::string( att_string ) + "\n";
att_string = header->Attribute("date");
if (att_string) {
std::string date( att_string );
ret += date.substr(6,2) + "/" + date.substr(4,2) + "/" + date.substr(0,4) + " @ ";
ret += date.substr(8,2) + ":" + date.substr(10,2);
}
}
return ret;
}
SessionCreator::SessionCreator(int recursion): SessionLoader(nullptr, recursion)
{
}
void SessionCreator::load(const std::string& filename)
{
XMLError eResult = xmlDoc_.LoadFile(filename.c_str());
if ( XMLResultError(eResult)){
Log::Warning("%s could not be openned.", filename.c_str());
return;
}
XMLElement *header = xmlDoc_.FirstChildElement(APP_NAME);
if (header == nullptr) {
Log::Warning("%s is not a %s session file.", filename.c_str(), APP_NAME);
return;
}
int version_major = -1, version_minor = -1;
header->QueryIntAttribute("major", &version_major);
header->QueryIntAttribute("minor", &version_minor);
if (version_major != XML_VERSION_MAJOR || version_minor != XML_VERSION_MINOR){
Log::Warning("%s session file is in version v%d.%d. but this vimix program expects v%d.%d.\n"
"Loading might fail or lead to different or incomplete configuration.\n"
"You can save this session again to avoid this warning.",
filename.c_str(), version_major, version_minor, XML_VERSION_MAJOR, XML_VERSION_MINOR);
// return;
}
// session file seems legit, create a session
session_ = new Session;
// load views config (includes resolution of session rendering)
loadConfig( xmlDoc_.FirstChildElement("Views") );
// ready to read sources
SessionLoader::load( xmlDoc_.FirstChildElement("Session") );
// create groups
std::list< SourceList > groups = getMixingGroups();
for (auto group_it = groups.begin(); group_it != groups.end(); group_it++)
session_->link( *group_it );
// all good
session_->setFilename(filename);
}
void SessionCreator::loadConfig(XMLElement *viewsNode)
{
if (viewsNode != nullptr) {
// ok, ready to read views
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Mixing"), *session_->config(View::MIXING));
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Geometry"), *session_->config(View::GEOMETRY));
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Layer"), *session_->config(View::LAYER));
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Texture"), *session_->config(View::TEXTURE));
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Rendering"), *session_->config(View::RENDERING));
}
}
SessionLoader::SessionLoader(Session *session, int recursion): Visitor(),
session_(session), xmlCurrent_(nullptr), recursion_(recursion)
{
}
std::map< uint64_t, Source* > SessionLoader::getSources() const
{
return sources_id_;
}
// groups_sources_id_ is parsed in XML and contains list of groups of ids
// Here we return the list of groups of newly created sources
// based on correspondance map sources_id_
// NB: importantly the list is cleared from duplicates
std::list< SourceList > SessionLoader::getMixingGroups() const
{
std::list< SourceList > groups_new_sources_id;
// perform conversion from xml id to new id
for (auto git = groups_sources_id_.begin(); git != groups_sources_id_.end(); git++)
{
SourceList new_sources;
for (auto sit = (*git).begin(); sit != (*git).end(); sit++ ) {
if (sources_id_.count(*sit) > 0)
new_sources.push_back( sources_id_.at(*sit) );
}
new_sources.sort();
groups_new_sources_id.push_back( new_sources );
}
// remove duplicates
groups_new_sources_id.unique();
return groups_new_sources_id;
}
void SessionLoader::load(XMLElement *sessionNode)
{
sources_id_.clear();
if (recursion_ > MAX_SESSION_LEVEL) {
Log::Warning("Recursive or imbricated sessions detected! Interrupting loading after %d iterations.\n", MAX_SESSION_LEVEL);
return;
}
if (sessionNode != nullptr && session_ != nullptr) {
XMLElement* sourceNode = sessionNode->FirstChildElement("Source");
for( ; sourceNode ; sourceNode = sourceNode->NextSiblingElement())
{
xmlCurrent_ = sourceNode;
// source to load
Source *load_source = nullptr;
// check if a source with the given id exists in the session
uint64_t id_xml_ = 0;
xmlCurrent_->QueryUnsigned64Attribute("id", &id_xml_);
SourceList::iterator sit = session_->find(id_xml_);
// no source with this id exists
if ( sit == session_->end() ) {
// create a new source depending on type
const char *pType = xmlCurrent_->Attribute("type");
if (!pType)
continue;
if ( std::string(pType) == "MediaSource") {
load_source = new MediaSource;
}
else if ( std::string(pType) == "SessionSource") {
load_source = new SessionFileSource;
}
else if ( std::string(pType) == "GroupSource") {
load_source = new SessionGroupSource;
}
else if ( std::string(pType) == "RenderSource") {
load_source = new RenderSource;
}
else if ( std::string(pType) == "PatternSource") {
load_source = new PatternSource;
}
else if ( std::string(pType) == "DeviceSource") {
load_source = new DeviceSource;
}
else if ( std::string(pType) == "NetworkSource") {
load_source = new NetworkSource;
}
// skip failed (including clones)
if (!load_source)
continue;
// add source to session
session_->addSource(load_source);
}
// get reference to the existing source
else
load_source = *sit;
// apply config to source
load_source->accept(*this);
load_source->touch();
// remember
sources_id_[id_xml_] = load_source;
}
// create clones after all sources, to be able to clone a source created above
sourceNode = sessionNode->FirstChildElement("Source");
for( ; sourceNode ; sourceNode = sourceNode->NextSiblingElement())
{
xmlCurrent_ = sourceNode;
// verify type of node
const char *pType = xmlCurrent_->Attribute("type");
if ( pType && std::string(pType) == "CloneSource") {
// check if a source with same id exists
uint64_t id_xml_ = 0;
xmlCurrent_->QueryUnsigned64Attribute("id", &id_xml_);
SourceList::iterator sit = session_->find(id_xml_);
// no source clone with this id exists
if ( sit == session_->end() ) {
// clone from given origin
XMLElement* originNode = xmlCurrent_->FirstChildElement("origin");
if (originNode) {
std::string sourcename = std::string ( originNode->GetText() );
SourceList::iterator origin = session_->find(sourcename);
// found the orign source
if (origin != session_->end()) {
// create a new source of type Clone
Source *clone_source = (*origin)->clone();
// add source to session
session_->addSource(clone_source);
// apply config to source
clone_source->accept(*this);
clone_source->touch();
// remember
sources_id_[id_xml_] = clone_source;
}
}
}
}
}
}
}
Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, bool clone_duplicates)
{
xmlCurrent_ = sourceNode;
// source to load
Source *load_source = nullptr;
bool is_clone = false;
SourceList::iterator sit = session_->end();
// check if a source with the given id exists in the session
if (clone_duplicates) {
uint64_t id__ = 0;
xmlCurrent_->QueryUnsigned64Attribute("id", &id__);
sit = session_->find(id__);
}
// no source with this id exists
if ( sit == session_->end() ) {
// create a new source depending on type
const char *pType = xmlCurrent_->Attribute("type");
if (pType) {
if ( std::string(pType) == "MediaSource") {
load_source = new MediaSource;
}
else if ( std::string(pType) == "SessionSource") {
load_source = new SessionFileSource;
}
else if ( std::string(pType) == "GroupSource") {
load_source = new SessionGroupSource;
}
else if ( std::string(pType) == "RenderSource") {
load_source = new RenderSource;
}
else if ( std::string(pType) == "PatternSource") {
load_source = new PatternSource;
}
else if ( std::string(pType) == "DeviceSource") {
load_source = new DeviceSource;
}
else if ( std::string(pType) == "NetworkSource") {
load_source = new NetworkSource;
}
else if ( std::string(pType) == "CloneSource") {
// clone from given origin
XMLElement* originNode = xmlCurrent_->FirstChildElement("origin");
if (originNode) {
std::string sourcename = std::string ( originNode->GetText() );
SourceList::iterator origin = session_->find(sourcename);
// found the orign source
if (origin != session_->end())
load_source = (*origin)->clone();
}
}
}
}
// clone existing source
else {
load_source = (*sit)->clone();
is_clone = true;
}
// apply config to source
if (load_source) {
load_source->accept(*this);
// increment depth for clones (avoid supperposition)
if (is_clone)
load_source->group(View::LAYER)->translation_.z += 0.2f;
}
return load_source;
}
void SessionLoader::XMLToNode(tinyxml2::XMLElement *xml, Node &n)
{
if (xml != nullptr){
XMLElement *node = xml->FirstChildElement("Node");
if ( !node || std::string(node->Name()).find("Node") == std::string::npos )
return;
XMLElement *scaleNode = node->FirstChildElement("scale");
if (scaleNode)
tinyxml2::XMLElementToGLM( scaleNode->FirstChildElement("vec3"), n.scale_);
XMLElement *translationNode = node->FirstChildElement("translation");
if (translationNode)
tinyxml2::XMLElementToGLM( translationNode->FirstChildElement("vec3"), n.translation_);
XMLElement *rotationNode = node->FirstChildElement("rotation");
if (rotationNode)
tinyxml2::XMLElementToGLM( rotationNode->FirstChildElement("vec3"), n.rotation_);
XMLElement *cropNode = node->FirstChildElement("crop");
if (cropNode)
tinyxml2::XMLElementToGLM( cropNode->FirstChildElement("vec3"), n.crop_);
}
}
void SessionLoader::visit(Node &n)
{
XMLToNode(xmlCurrent_, n);
}
void SessionLoader::visit(MediaPlayer &n)
{
XMLElement* mediaplayerNode = xmlCurrent_->FirstChildElement("MediaPlayer");
if (mediaplayerNode) {
uint64_t id__ = -1;
mediaplayerNode->QueryUnsigned64Attribute("id", &id__);
// timeline
XMLElement *timelineelement = mediaplayerNode->FirstChildElement("Timeline");
if (timelineelement) {
Timeline tl;
tl.setTiming( n.timeline()->interval(), n.timeline()->step());
XMLElement *gapselement = timelineelement->FirstChildElement("Gaps");
if (gapselement) {
XMLElement* gap = gapselement->FirstChildElement("Interval");
for( ; gap ; gap = gap->NextSiblingElement())
{
GstClockTime a = GST_CLOCK_TIME_NONE;
GstClockTime b = GST_CLOCK_TIME_NONE;
gap->QueryUnsigned64Attribute("begin", &a);
gap->QueryUnsigned64Attribute("end", &b);
tl.addGap( a, b );
}
}
XMLElement *fadingselement = timelineelement->FirstChildElement("Fading");
if (fadingselement) {
XMLElement* array = fadingselement->FirstChildElement("array");
XMLElementDecodeArray(array, tl.fadingArray(), MAX_TIMELINE_ARRAY * sizeof(float));
}
n.setTimeline(tl);
}
// change play status only if different id (e.g. new media player)
if ( n.id() != id__ ) {
double speed = 1.0;
mediaplayerNode->QueryDoubleAttribute("speed", &speed);
n.setPlaySpeed(speed);
int loop = 1;
mediaplayerNode->QueryIntAttribute("loop", &loop);
n.setLoop( (MediaPlayer::LoopMode) loop);
bool play = true;
mediaplayerNode->QueryBoolAttribute("play", &play);
n.play(play);
}
}
}
void SessionLoader::visit(Shader &n)
{
XMLElement* color = xmlCurrent_->FirstChildElement("color");
if ( color ) {
tinyxml2::XMLElementToGLM( color->FirstChildElement("vec4"), n.color);
XMLElement* blending = xmlCurrent_->FirstChildElement("blending");
if (blending) {
int blend = 0;
blending->QueryIntAttribute("mode", &blend);
n.blending = (Shader::BlendMode) blend;
}
}
}
void SessionLoader::visit(ImageShader &n)
{
const char *pType = xmlCurrent_->Attribute("type");
if ( std::string(pType) != "ImageShader" )
return;
XMLElement* uniforms = xmlCurrent_->FirstChildElement("uniforms");
if (uniforms) {
uniforms->QueryFloatAttribute("stipple", &n.stipple);
}
}
void SessionLoader::visit(MaskShader &n)
{
const char *pType = xmlCurrent_->Attribute("type");
if ( std::string(pType) != "MaskShader" )
return;
xmlCurrent_->QueryUnsignedAttribute("mode", &n.mode);
xmlCurrent_->QueryUnsignedAttribute("shape", &n.shape);
XMLElement* uniforms = xmlCurrent_->FirstChildElement("uniforms");
if (uniforms) {
uniforms->QueryFloatAttribute("blur", &n.blur);
uniforms->QueryIntAttribute("option", &n.option);
XMLElement* size = uniforms->FirstChildElement("size");
if (size)
tinyxml2::XMLElementToGLM( size->FirstChildElement("vec2"), n.size);
}
}
void SessionLoader::visit(ImageProcessingShader &n)
{
const char *pType = xmlCurrent_->Attribute("type");
if ( std::string(pType) != "ImageProcessingShader" )
return;
XMLElement* uniforms = xmlCurrent_->FirstChildElement("uniforms");
if (uniforms) {
uniforms->QueryFloatAttribute("brightness", &n.brightness);
uniforms->QueryFloatAttribute("contrast", &n.contrast);
uniforms->QueryFloatAttribute("saturation", &n.saturation);
uniforms->QueryFloatAttribute("hueshift", &n.hueshift);
uniforms->QueryFloatAttribute("threshold", &n.threshold);
uniforms->QueryFloatAttribute("lumakey", &n.lumakey);
uniforms->QueryIntAttribute("nbColors", &n.nbColors);
uniforms->QueryIntAttribute("invert", &n.invert);
uniforms->QueryFloatAttribute("chromadelta", &n.chromadelta);
uniforms->QueryIntAttribute("filter", &n.filterid);
}
XMLElement* gamma = xmlCurrent_->FirstChildElement("gamma");
if (gamma)
tinyxml2::XMLElementToGLM( gamma->FirstChildElement("vec4"), n.gamma);
XMLElement* levels = xmlCurrent_->FirstChildElement("levels");
if (levels)
tinyxml2::XMLElementToGLM( levels->FirstChildElement("vec4"), n.levels);
XMLElement* chromakey = xmlCurrent_->FirstChildElement("chromakey");
if (chromakey)
tinyxml2::XMLElementToGLM( chromakey->FirstChildElement("vec4"), n.chromakey);
}
void SessionLoader::visit (Source& s)
{
XMLElement* sourceNode = xmlCurrent_;
const char *pName = sourceNode->Attribute("name");
s.setName(pName);
bool l = false;
sourceNode->QueryBoolAttribute("locked", &l);
s.setLocked(l);
xmlCurrent_ = sourceNode->FirstChildElement("Mixing");
if (xmlCurrent_) s.groupNode(View::MIXING)->accept(*this);
xmlCurrent_ = sourceNode->FirstChildElement("Geometry");
if (xmlCurrent_) s.groupNode(View::GEOMETRY)->accept(*this);
xmlCurrent_ = sourceNode->FirstChildElement("Layer");
if (xmlCurrent_) s.groupNode(View::LAYER)->accept(*this);
xmlCurrent_ = sourceNode->FirstChildElement("Texture");
if (xmlCurrent_) {
s.groupNode(View::TEXTURE)->accept(*this);
bool m = true;
xmlCurrent_->QueryBoolAttribute("mirrored", &m);
s.setTextureMirrored(m);
}
xmlCurrent_ = sourceNode->FirstChildElement("Blending");
if (xmlCurrent_) s.blendingShader()->accept(*this);
xmlCurrent_ = sourceNode->FirstChildElement("Mask");
if (xmlCurrent_) {
// read the mask shader attributes
s.maskShader()->accept(*this);
// if there is an Image mask stored
XMLElement* imageNode = xmlCurrent_->FirstChildElement("Image");
if (imageNode) {
// if there is an internal array of data
XMLElement* array = imageNode->FirstChildElement("array");
if (array) {
// create a temporary jpeg with size of the array
FrameBufferImage::jpegBuffer jpgimg;
array->QueryUnsignedAttribute("len", &jpgimg.len);
// ok, we got a size of data to load
if (jpgimg.len>0) {
// allocate jpeg buffer
jpgimg.buffer = (unsigned char*) malloc(jpgimg.len);
// actual decoding of array
if (XMLElementDecodeArray(array, jpgimg.buffer, jpgimg.len) )
// create and set the image from jpeg
s.setMask(new FrameBufferImage(jpgimg));
// free temporary buffer
if (jpgimg.buffer)
free(jpgimg.buffer);
}
}
}
}
xmlCurrent_ = sourceNode->FirstChildElement("ImageProcessing");
if (xmlCurrent_) {
bool on = xmlCurrent_->BoolAttribute("enabled", true);
s.processingShader()->accept(*this);
s.setImageProcessingEnabled(on);
}
xmlCurrent_ = sourceNode->FirstChildElement("MixingGroup");
if (xmlCurrent_) {
SourceIdList idlist;
XMLElement* sourceNode = xmlCurrent_->FirstChildElement("source");
for ( ; sourceNode ; sourceNode = sourceNode->NextSiblingElement()) {
uint64_t id__ = 0;
sourceNode->QueryUnsigned64Attribute("id", &id__);
idlist.push_back(id__);
}
groups_sources_id_.push_back(idlist);
}
// restore current
xmlCurrent_ = sourceNode;
}
void SessionLoader::visit (MediaSource& s)
{
// set uri
XMLElement* uriNode = xmlCurrent_->FirstChildElement("uri");
if (uriNode) {
std::string uri = std::string ( uriNode->GetText() );
// load only new files
if ( uri != s.path() )
s.setPath(uri);
}
// set config media player
s.mediaplayer()->accept(*this);
}
void SessionLoader::visit (SessionFileSource& s)
{
// set fading
float f = 0.f;
xmlCurrent_->QueryFloatAttribute("fading", &f);
s.session()->setFading(f);
// set uri
XMLElement* pathNode = xmlCurrent_->FirstChildElement("path");
if (pathNode) {
std::string path = std::string ( pathNode->GetText() );
// load only new files
if ( path != s.path() )
s.load(path, recursion_ + 1);
}
}
void SessionLoader::visit (SessionGroupSource& s)
{
// set resolution from host session
s.setResolution( session_->config(View::RENDERING)->scale_ );
// get the inside session
XMLElement* sessionGroupNode = xmlCurrent_->FirstChildElement("Session");
if (sessionGroupNode) {
// only parse if newly created
if (s.session()->empty()) {
// load session inside group
SessionLoader grouploader( s.session(), recursion_ + 1 );
grouploader.load( sessionGroupNode );
}
}
}
void SessionLoader::visit (RenderSource& s)
{
s.setSession( session_ );
}
void SessionLoader::visit (PatternSource& s)
{
uint t = xmlCurrent_->UnsignedAttribute("pattern");
glm::ivec2 resolution(800, 600);
XMLElement* res = xmlCurrent_->FirstChildElement("resolution");
if (res)
tinyxml2::XMLElementToGLM( res->FirstChildElement("ivec2"), resolution);
// change only if different pattern
if ( t != s.pattern()->type() )
s.setPattern(t, resolution);
}
void SessionLoader::visit (DeviceSource& s)
{
std::string devname = std::string ( xmlCurrent_->Attribute("device") );
// change only if different device
if ( devname != s.device() )
s.setDevice(devname);
}
void SessionLoader::visit (NetworkSource& s)
{
std::string connect = std::string ( xmlCurrent_->Attribute("connection") );
// change only if different device
if ( connect != s.connection() )
s.setConnection(connect);
}

View File

@@ -1,79 +0,0 @@
#ifndef SESSIONCREATOR_H
#define SESSIONCREATOR_H
#include <map>
#include <tinyxml2.h>
#include "Visitor.h"
#include "SourceList.h"
class Session;
class SessionLoader : public Visitor {
public:
SessionLoader(Session *session, int recursion = 0);
inline Session *session() const { return session_; }
void load(tinyxml2::XMLElement *sessionNode);
std::map< uint64_t, Source* > getSources() const;
std::list< SourceList > getMixingGroups() const;
Source *createSource(tinyxml2::XMLElement *sourceNode, bool clone_duplicates = true);
// Elements of Scene
void visit (Node& n) override;
void visit (Scene&) override {}
void visit (Group&) override {}
void visit (Switch&) override {}
void visit (Primitive&) override {}
// Elements with attributes
void visit (MediaPlayer& n) override;
void visit (Shader& n) override;
void visit (ImageShader& n) override;
void visit (MaskShader& n) override;
void visit (ImageProcessingShader& n) override;
// Sources
void visit (Source& s) override;
void visit (MediaSource& s) override;
void visit (SessionFileSource& s) override;
void visit (SessionGroupSource& s) override;
void visit (RenderSource& s) override;
void visit (PatternSource& s) override;
void visit (DeviceSource& s) override;
void visit (NetworkSource& s) override;
protected:
// result created session
Session *session_;
// parsing current xml
tinyxml2::XMLElement *xmlCurrent_;
// level of loading recursion
int recursion_;
// map of correspondance from xml source id (key) to new source pointer (value)
std::map< uint64_t, Source* > sources_id_;
// list of groups (lists of xml source id)
std::list< SourceIdList > groups_sources_id_;
static void XMLToNode(tinyxml2::XMLElement *xml, Node &n);
};
class SessionCreator : public SessionLoader {
tinyxml2::XMLDocument xmlDoc_;
void loadConfig(tinyxml2::XMLElement *viewsNode);
public:
SessionCreator(int recursion = 0);
void load(const std::string& filename);
static std::string info(const std::string& filename);
};
#endif // SESSIONCREATOR_H

View File

@@ -1,386 +0,0 @@
#include <glm/gtc/matrix_transform.hpp>
#include <thread>
#include <chrono>
#include "SessionSource.h"
#include "defines.h"
#include "Log.h"
#include "FrameBuffer.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "Resource.h"
#include "Decorations.h"
#include "SearchVisitor.h"
#include "Session.h"
#include "SessionCreator.h"
#include "Mixer.h"
SessionSource::SessionSource() : Source()
{
failed_ = false;
session_ = new Session;
}
SessionSource::~SessionSource()
{
// delete session
if (session_)
delete session_;
}
Session *SessionSource::detach()
{
// remember pointer to give away
Session *giveaway = session_;
// work on a new session
session_ = new Session;
// make disabled
initialized_ = false;
// ask to delete me
failed_ = true;
// lost ref to previous session: to be deleted elsewhere...
return giveaway;
}
bool SessionSource::failed() const
{
return failed_;
}
uint SessionSource::texture() const
{
if (session_ && session_->frame())
return session_->frame()->texture();
else
return Resource::getTextureBlack();
}
void SessionSource::setActive (bool on)
{
Source::setActive(on);
// change status of session (recursive change of internal sources)
if (session_ != nullptr)
session_->setActive(active_);
}
void SessionSource::update(float dt)
{
if (session_ == nullptr)
return;
// update content
if (active_)
session_->update(dt);
// delete a source which failed
if (session_->failedSource() != nullptr) {
session_->deleteSource(session_->failedSource());
// fail session if all sources failed
if ( session_->numSource() < 1)
failed_ = true;
}
Source::update(dt);
}
SessionFileSource::SessionFileSource() : SessionSource(), path_("")
{
// specific node for transition view
groups_[View::TRANSITION]->visible_ = false;
groups_[View::TRANSITION]->scale_ = glm::vec3(0.1f, 0.1f, 1.f);
groups_[View::TRANSITION]->translation_ = glm::vec3(-1.f, 0.f, 0.f);
frames_[View::TRANSITION] = new Switch;
Frame *frame = new Frame(Frame::ROUND, Frame::THIN, Frame::DROP);
frame->translation_.z = 0.1;
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.95f);
frames_[View::TRANSITION]->attach(frame);
frame = new Frame(Frame::ROUND, Frame::LARGE, Frame::DROP);
frame->translation_.z = 0.01;
frame->color = glm::vec4( COLOR_TRANSITION_SOURCE, 1.f);
frames_[View::TRANSITION]->attach(frame);
groups_[View::TRANSITION]->attach(frames_[View::TRANSITION]);
overlays_[View::TRANSITION] = new Group;
overlays_[View::TRANSITION]->translation_.z = 0.1;
overlays_[View::TRANSITION]->visible_ = false;
Symbol *loader = new Symbol(Symbol::DOTS);
loader->scale_ = glm::vec3(2.f, 2.f, 1.f);
loader->update_callbacks_.push_back(new InfiniteGlowCallback);
overlays_[View::TRANSITION]->attach(loader);
Symbol *center = new Symbol(Symbol::CIRCLE_POINT, glm::vec3(0.f, -1.05f, 0.1f));
overlays_[View::TRANSITION]->attach(center);
groups_[View::TRANSITION]->attach(overlays_[View::TRANSITION]);
// set symbol
symbol_ = new Symbol(Symbol::SESSION, glm::vec3(0.75f, 0.75f, 0.01f));
symbol_->scale_.y = 1.5f;
wait_for_sources_ = false;
}
void SessionFileSource::load(const std::string &p, uint recursion)
{
path_ = p;
// delete session
if (session_) {
delete session_;
session_ = nullptr;
}
// init session
if ( path_.empty() ) {
// empty session
session_ = new Session;
Log::Warning("Empty Session filename provided.");
}
else {
// launch a thread to load the session file
sessionLoader_ = std::async(std::launch::async, Session::load, path_, recursion);
Log::Notify("Opening %s", p.c_str());
}
}
void SessionFileSource::init()
{
// init is first about getting the loaded session
if (session_ == nullptr) {
// did the loader finish ?
if (sessionLoader_.wait_for(std::chrono::milliseconds(4)) == std::future_status::ready) {
session_ = sessionLoader_.get();
if (session_ == nullptr)
failed_ = true;
}
}
else {
session_->update(dt_);
if (wait_for_sources_) {
// force update of of all sources
active_ = true;
touch();
// check that every source is ready..
bool ready = true;
for (SourceList::iterator iter = session_->begin(); iter != session_->end(); iter++)
{
// interrupt if any source is NOT ready
if ( !(*iter)->ready() ){
ready = false;
break;
}
}
// if all sources are ready, done with initialization!
if (ready) {
// done init
wait_for_sources_ = false;
initialized_ = true;
Log::Info("Source Session %s loaded %d sources.", path_.c_str(), session_->numSource());
}
}
else if ( !failed_ ) {
// set resolution
session_->setResolution( session_->config(View::RENDERING)->scale_ );
// update to draw framebuffer
session_->update(dt_);
// get the texture index from framebuffer of session, apply it to the surface
texturesurface_->setTextureIndex( session_->frame()->texture() );
// create Frame buffer matching size of session
FrameBuffer *renderbuffer = new FrameBuffer( session_->frame()->resolution() );
// set the renderbuffer of the source and attach rendering nodes
attach(renderbuffer);
// wait for all sources to init
if (session_->numSource() > 0)
wait_for_sources_ = true;
else {
initialized_ = true;
Log::Info("New Session created (%d x %d).", renderbuffer->width(), renderbuffer->height());
}
}
}
if (initialized_)
{
// remove the loading icon
Node *loader = overlays_[View::TRANSITION]->back();
overlays_[View::TRANSITION]->detach(loader);
delete loader;
// deep update to reorder
View::need_deep_update_++;
}
}
void SessionFileSource::accept(Visitor& v)
{
Source::accept(v);
if (!failed())
v.visit(*this);
}
SessionGroupSource::SessionGroupSource() : SessionSource(), resolution_(glm::vec3(0.f))
{
// // redo frame for layers view
// frames_[View::LAYER]->clear();
// // Groups in LAYER have an additional border
// Group *group = new Group;
// Frame *frame = new Frame(Frame::ROUND, Frame::THIN, Frame::PERSPECTIVE);
// frame->translation_.z = 0.1;
// frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.95f);
// group->attach(frame);
// Frame *persp = new Frame(Frame::GROUP, Frame::THIN, Frame::NONE);
// persp->translation_.z = 0.1;
// persp->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.95f);
// group->attach(persp);
// frames_[View::LAYER]->attach(group);
// group = new Group;
// frame = new Frame(Frame::ROUND, Frame::LARGE, Frame::PERSPECTIVE);
// frame->translation_.z = 0.1;
// frame->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
// group->attach(frame);
// persp = new Frame(Frame::GROUP, Frame::LARGE, Frame::NONE);
// persp->translation_.z = 0.1;
// persp->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
// group->attach(persp);
// frames_[View::LAYER]->attach(group);
// set symbol
symbol_ = new Symbol(Symbol::GROUP, glm::vec3(0.75f, 0.75f, 0.01f));
symbol_->scale_.y = 1.5f;
}
void SessionGroupSource::init()
{
if ( resolution_.x > 0.f && resolution_.y > 0.f ) {
session_->setResolution( resolution_ );
// update to draw framebuffer
session_->update( dt_ );
// get the texture index from framebuffer of session, apply it to the surface
texturesurface_->setTextureIndex( session_->frame()->texture() );
// create Frame buffer matching size of session
FrameBuffer *renderbuffer = new FrameBuffer( session_->frame()->resolution() );
// set the renderbuffer of the source and attach rendering nodes
attach(renderbuffer);
// deep update to reorder
View::need_deep_update_++;
// done init
initialized_ = true;
Log::Info("Source Group (%d x %d).", int(renderbuffer->resolution().x), int(renderbuffer->resolution().y) );
}
}
bool SessionGroupSource::import(Source *source)
{
bool ret = false;
if ( session_ )
{
SourceList::iterator its = session_->addSource(source);
if (its != session_->end())
ret = true;
}
return ret;
}
void SessionGroupSource::accept(Visitor& v)
{
Source::accept(v);
if (!failed())
v.visit(*this);
}
RenderSource::RenderSource() : Source(), session_(nullptr)
{
// set symbol
symbol_ = new Symbol(Symbol::RENDER, glm::vec3(0.75f, 0.75f, 0.01f));
symbol_->scale_.y = 1.5f;
}
bool RenderSource::failed() const
{
if (initialized_ && session_!=nullptr)
return renderbuffer_->resolution() != session_->frame()->resolution();
return false;
}
uint RenderSource::texture() const
{
if (session_ && session_->frame())
return session_->frame()->texture();
else
return Resource::getTextureBlack(); // getTextureTransparent ?
}
void RenderSource::init()
{
if (session_ && session_->frame() && session_->frame()->texture() != Resource::getTextureBlack()) {
FrameBuffer *fb = session_->frame();
// get the texture index from framebuffer of view, apply it to the surface
texturesurface_->setTextureIndex( fb->texture() );
// create Frame buffer matching size of output session
FrameBuffer *renderbuffer = new FrameBuffer( fb->resolution() );
// set the renderbuffer of the source and attach rendering nodes
attach(renderbuffer);
// deep update to reorder
View::need_deep_update_++;
// done init
initialized_ = true;
Log::Info("Source Render linked to session (%d x %d).", int(fb->resolution().x), int(fb->resolution().y) );
}
}
glm::vec3 RenderSource::resolution() const
{
if (initialized_)
return renderbuffer_->resolution();
else if (session_ && session_->frame())
return session_->frame()->resolution();
else
return glm::vec3(0.f);
}
void RenderSource::accept(Visitor& v)
{
Source::accept(v);
// if (!failed())
v.visit(*this);
}

View File

@@ -1,534 +0,0 @@
#include "SessionVisitor.h"
#include "Log.h"
#include "defines.h"
#include "Scene.h"
#include "Decorations.h"
#include "Source.h"
#include "MediaSource.h"
#include "Session.h"
#include "SessionSource.h"
#include "PatternSource.h"
#include "DeviceSource.h"
#include "NetworkSource.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "MediaPlayer.h"
#include "MixingGroup.h"
#include "SystemToolkit.h"
#include <iostream>
#include <tinyxml2.h>
using namespace tinyxml2;
bool SessionVisitor::saveSession(const std::string& filename, Session *session)
{
// creation of XML doc
XMLDocument xmlDoc;
XMLElement *rootnode = xmlDoc.NewElement(APP_NAME);
rootnode->SetAttribute("major", XML_VERSION_MAJOR);
rootnode->SetAttribute("minor", XML_VERSION_MINOR);
rootnode->SetAttribute("size", session->numSource());
rootnode->SetAttribute("date", SystemToolkit::date_time_string().c_str());
rootnode->SetAttribute("resolution", session->frame()->info().c_str());
xmlDoc.InsertEndChild(rootnode);
// 1. list of sources
XMLElement *sessionNode = xmlDoc.NewElement("Session");
xmlDoc.InsertEndChild(sessionNode);
SessionVisitor sv(&xmlDoc, sessionNode);
for (auto iter = session->begin(); iter != session->end(); iter++, sv.setRoot(sessionNode) )
// source visitor
(*iter)->accept(sv);
// 2. config of views
XMLElement *views = xmlDoc.NewElement("Views");
xmlDoc.InsertEndChild(views);
{
XMLElement *mixing = xmlDoc.NewElement( "Mixing" );
mixing->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::MIXING), &xmlDoc));
views->InsertEndChild(mixing);
XMLElement *geometry = xmlDoc.NewElement( "Geometry" );
geometry->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::GEOMETRY), &xmlDoc));
views->InsertEndChild(geometry);
XMLElement *layer = xmlDoc.NewElement( "Layer" );
layer->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::LAYER), &xmlDoc));
views->InsertEndChild(layer);
XMLElement *appearance = xmlDoc.NewElement( "Texture" );
appearance->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::TEXTURE), &xmlDoc));
views->InsertEndChild(appearance);
XMLElement *render = xmlDoc.NewElement( "Rendering" );
render->InsertEndChild( SessionVisitor::NodeToXML(*session->config(View::RENDERING), &xmlDoc));
views->InsertEndChild(render);
}
// save file to disk
return ( XMLSaveDoc(&xmlDoc, filename) );
}
SessionVisitor::SessionVisitor(tinyxml2::XMLDocument *doc,
tinyxml2::XMLElement *root,
bool recursive) : Visitor(), recursive_(recursive), xmlCurrent_(root)
{
if (doc == nullptr)
xmlDoc_ = new XMLDocument;
else
xmlDoc_ = doc;
}
tinyxml2::XMLElement *SessionVisitor::NodeToXML(Node &n, tinyxml2::XMLDocument *doc)
{
XMLElement *newelement = doc->NewElement("Node");
newelement->SetAttribute("visible", n.visible_);
newelement->SetAttribute("id", n.id());
XMLElement *scale = doc->NewElement("scale");
scale->InsertEndChild( XMLElementFromGLM(doc, n.scale_) );
newelement->InsertEndChild(scale);
XMLElement *translation = doc->NewElement("translation");
translation->InsertEndChild( XMLElementFromGLM(doc, n.translation_) );
newelement->InsertEndChild(translation);
XMLElement *rotation = doc->NewElement("rotation");
rotation->InsertEndChild( XMLElementFromGLM(doc, n.rotation_) );
newelement->InsertEndChild(rotation);
XMLElement *crop = doc->NewElement("crop");
crop->InsertEndChild( XMLElementFromGLM(doc, n.crop_) );
newelement->InsertEndChild(crop);
return newelement;
}
void SessionVisitor::visit(Node &n)
{
XMLElement *newelement = NodeToXML(n, xmlDoc_);
// insert into hierarchy
xmlCurrent_->InsertEndChild(newelement);
// parent for next visits
xmlCurrent_ = newelement;
}
void SessionVisitor::visit(Group &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "Group");
if (recursive_) {
// loop over members of a group
XMLElement *group = xmlCurrent_;
for (NodeSet::iterator node = n.begin(); node != n.end(); node++) {
(*node)->accept(*this);
// revert to group as current
xmlCurrent_ = group;
}
}
}
void SessionVisitor::visit(Switch &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "Switch");
xmlCurrent_->SetAttribute("active", n.active());
if (recursive_) {
// loop over members of the group
XMLElement *group = xmlCurrent_;
for(uint i = 0; i < n.numChildren(); i++) {
n.child(i)->accept(*this);
// revert to group as current
xmlCurrent_ = group;
}
}
}
void SessionVisitor::visit(Primitive &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "Primitive");
if (recursive_) {
// go over members of a primitive
XMLElement *Primitive = xmlCurrent_;
xmlCurrent_ = xmlDoc_->NewElement("Shader");
n.shader()->accept(*this);
Primitive->InsertEndChild(xmlCurrent_);
// revert to primitive as current
xmlCurrent_ = Primitive;
}
}
void SessionVisitor::visit(Surface &)
{
}
void SessionVisitor::visit(ImageSurface &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "ImageSurface");
XMLText *filename = xmlDoc_->NewText( n.resource().c_str() );
XMLElement *image = xmlDoc_->NewElement("resource");
image->InsertEndChild(filename);
xmlCurrent_->InsertEndChild(image);
}
void SessionVisitor::visit(FrameBufferSurface &)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "FrameBufferSurface");
}
void SessionVisitor::visit(MediaSurface &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "MediaSurface");
n.mediaPlayer()->accept(*this);
}
void SessionVisitor::visit(MediaPlayer &n)
{
XMLElement *newelement = xmlDoc_->NewElement("MediaPlayer");
newelement->SetAttribute("id", n.id());
if (!n.isImage()) {
newelement->SetAttribute("play", n.isPlaying());
newelement->SetAttribute("loop", (int) n.loop());
newelement->SetAttribute("speed", n.playSpeed());
// timeline
XMLElement *timelineelement = xmlDoc_->NewElement("Timeline");
// gaps in timeline
XMLElement *gapselement = xmlDoc_->NewElement("Gaps");
TimeIntervalSet gaps = n.timeline()->gaps();
for( auto it = gaps.begin(); it!= gaps.end(); it++) {
XMLElement *g = xmlDoc_->NewElement("Interval");
g->SetAttribute("begin", (*it).begin);
g->SetAttribute("end", (*it).end);
gapselement->InsertEndChild(g);
}
timelineelement->InsertEndChild(gapselement);
// fading in timeline
XMLElement *fadingelement = xmlDoc_->NewElement("Fading");
XMLElement *array = XMLElementEncodeArray(xmlDoc_, n.timeline()->fadingArray(), MAX_TIMELINE_ARRAY * sizeof(float));
fadingelement->InsertEndChild(array);
timelineelement->InsertEndChild(fadingelement);
newelement->InsertEndChild(timelineelement);
}
xmlCurrent_->InsertEndChild(newelement);
}
void SessionVisitor::visit(Shader &n)
{
// Shader of a simple type
xmlCurrent_->SetAttribute("type", "Shader");
xmlCurrent_->SetAttribute("id", n.id());
XMLElement *color = xmlDoc_->NewElement("color");
color->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.color) );
xmlCurrent_->InsertEndChild(color);
XMLElement *blend = xmlDoc_->NewElement("blending");
blend->SetAttribute("mode", int(n.blending) );
xmlCurrent_->InsertEndChild(blend);
}
void SessionVisitor::visit(ImageShader &n)
{
// Shader of a textured type
xmlCurrent_->SetAttribute("type", "ImageShader");
xmlCurrent_->SetAttribute("id", n.id());
XMLElement *uniforms = xmlDoc_->NewElement("uniforms");
uniforms->SetAttribute("stipple", n.stipple);
xmlCurrent_->InsertEndChild(uniforms);
}
void SessionVisitor::visit(MaskShader &n)
{
// Shader of a mask type
xmlCurrent_->SetAttribute("type", "MaskShader");
xmlCurrent_->SetAttribute("id", n.id());
xmlCurrent_->SetAttribute("mode", n.mode);
xmlCurrent_->SetAttribute("shape", n.shape);
XMLElement *uniforms = xmlDoc_->NewElement("uniforms");
uniforms->SetAttribute("blur", n.blur);
uniforms->SetAttribute("option", n.option);
XMLElement *size = xmlDoc_->NewElement("size");
size->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.size) );
uniforms->InsertEndChild(size);
xmlCurrent_->InsertEndChild(uniforms);
}
void SessionVisitor::visit(ImageProcessingShader &n)
{
// Shader of a textured type
xmlCurrent_->SetAttribute("type", "ImageProcessingShader");
xmlCurrent_->SetAttribute("id", n.id());
XMLElement *filter = xmlDoc_->NewElement("uniforms");
filter->SetAttribute("brightness", n.brightness);
filter->SetAttribute("contrast", n.contrast);
filter->SetAttribute("saturation", n.saturation);
filter->SetAttribute("hueshift", n.hueshift);
filter->SetAttribute("threshold", n.threshold);
filter->SetAttribute("lumakey", n.lumakey);
filter->SetAttribute("nbColors", n.nbColors);
filter->SetAttribute("invert", n.invert);
filter->SetAttribute("chromadelta", n.chromadelta);
filter->SetAttribute("filter", n.filterid);
xmlCurrent_->InsertEndChild(filter);
XMLElement *gamma = xmlDoc_->NewElement("gamma");
gamma->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.gamma) );
xmlCurrent_->InsertEndChild(gamma);
XMLElement *levels = xmlDoc_->NewElement("levels");
levels->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.levels) );
xmlCurrent_->InsertEndChild(levels);
XMLElement *chromakey = xmlDoc_->NewElement("chromakey");
chromakey->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.chromakey) );
xmlCurrent_->InsertEndChild(chromakey);
}
void SessionVisitor::visit(LineStrip &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "LineStrip");
XMLElement *points_node = xmlDoc_->NewElement("points");
std::vector<glm::vec2> path = n.path();
for(size_t i = 0; i < path.size(); ++i)
{
XMLElement *p = XMLElementFromGLM(xmlDoc_, path[i]);
p->SetAttribute("index", (int) i);
points_node->InsertEndChild(p);
}
xmlCurrent_->InsertEndChild(points_node);
}
void SessionVisitor::visit(LineSquare &)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "LineSquare");
}
void SessionVisitor::visit(Mesh &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "Mesh");
XMLText *filename = xmlDoc_->NewText( n.meshPath().c_str() );
XMLElement *obj = xmlDoc_->NewElement("resource");
obj->InsertEndChild(filename);
xmlCurrent_->InsertEndChild(obj);
filename = xmlDoc_->NewText( n.texturePath().c_str() );
XMLElement *tex = xmlDoc_->NewElement("texture");
tex->InsertEndChild(filename);
xmlCurrent_->InsertEndChild(tex);
}
void SessionVisitor::visit(Frame &n)
{
// Node of a different type
xmlCurrent_->SetAttribute("type", "Frame");
XMLElement *color = xmlDoc_->NewElement("color");
color->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.color) );
xmlCurrent_->InsertEndChild(color);
}
void SessionVisitor::visit(Scene &n)
{
XMLElement *xmlRoot = xmlDoc_->NewElement("Scene");
xmlDoc_->InsertEndChild(xmlRoot);
// start recursive traverse from root node
recursive_ = true;
xmlCurrent_ = xmlRoot;
n.root()->accept(*this);
}
void SessionVisitor::visit (Source& s)
{
XMLElement *sourceNode = xmlDoc_->NewElement( "Source" );
sourceNode->SetAttribute("id", s.id());
sourceNode->SetAttribute("name", s.name().c_str() );
sourceNode->SetAttribute("locked", s.locked() );
// insert into hierarchy
xmlCurrent_->InsertFirstChild(sourceNode);
xmlCurrent_ = xmlDoc_->NewElement( "Mixing" );
sourceNode->InsertEndChild(xmlCurrent_);
s.groupNode(View::MIXING)->accept(*this);
xmlCurrent_ = xmlDoc_->NewElement( "Geometry" );
sourceNode->InsertEndChild(xmlCurrent_);
s.groupNode(View::GEOMETRY)->accept(*this);
xmlCurrent_ = xmlDoc_->NewElement( "Layer" );
sourceNode->InsertEndChild(xmlCurrent_);
s.groupNode(View::LAYER)->accept(*this);
xmlCurrent_ = xmlDoc_->NewElement( "Texture" );
xmlCurrent_->SetAttribute("mirrored", s.textureMirrored() );
sourceNode->InsertEndChild(xmlCurrent_);
s.groupNode(View::TEXTURE)->accept(*this);
xmlCurrent_ = xmlDoc_->NewElement( "Blending" );
sourceNode->InsertEndChild(xmlCurrent_);
s.blendingShader()->accept(*this);
xmlCurrent_ = xmlDoc_->NewElement( "Mask" );
sourceNode->InsertEndChild(xmlCurrent_);
s.maskShader()->accept(*this);
// if we are saving a pain mask
if (s.maskShader()->mode == MaskShader::PAINT) {
// get the mask previously stored
FrameBufferImage *img = s.getMask();
if (img != nullptr) {
// get the jpeg encoded buffer
FrameBufferImage::jpegBuffer jpgimg = img->getJpeg();
if (jpgimg.buffer != nullptr) {
// fill the xml array with jpeg buffer
XMLElement *array = XMLElementEncodeArray(xmlDoc_, jpgimg.buffer, jpgimg.len);
// free the buffer
free(jpgimg.buffer);
// if we could create the array
if (array) {
// create an Image node to store the mask image
XMLElement *imageelement = xmlDoc_->NewElement("Image");
imageelement->InsertEndChild(array);
xmlCurrent_->InsertEndChild(imageelement);
}
}
}
}
xmlCurrent_ = xmlDoc_->NewElement( "ImageProcessing" );
xmlCurrent_->SetAttribute("enabled", s.imageProcessingEnabled());
sourceNode->InsertEndChild(xmlCurrent_);
s.processingShader()->accept(*this);
if (s.mixingGroup()) {
xmlCurrent_ = xmlDoc_->NewElement( "MixingGroup" );
sourceNode->InsertEndChild(xmlCurrent_);
s.mixingGroup()->accept(*this);
}
xmlCurrent_ = sourceNode; // parent for next visits (other subtypes of Source)
}
void SessionVisitor::visit (MediaSource& s)
{
xmlCurrent_->SetAttribute("type", "MediaSource");
XMLElement *uri = xmlDoc_->NewElement("uri");
xmlCurrent_->InsertEndChild(uri);
XMLText *text = xmlDoc_->NewText( s.path().c_str() );
uri->InsertEndChild( text );
s.mediaplayer()->accept(*this);
}
void SessionVisitor::visit (SessionFileSource& s)
{
xmlCurrent_->SetAttribute("type", "SessionSource");
if (s.session() != nullptr)
xmlCurrent_->SetAttribute("fading", s.session()->fading());
XMLElement *path = xmlDoc_->NewElement("path");
xmlCurrent_->InsertEndChild(path);
XMLText *text = xmlDoc_->NewText( s.path().c_str() );
path->InsertEndChild( text );
}
void SessionVisitor::visit (SessionGroupSource& s)
{
xmlCurrent_->SetAttribute("type", "GroupSource");
Session *se = s.session();
XMLElement *sessionNode = xmlDoc_->NewElement("Session");
xmlCurrent_->InsertEndChild(sessionNode);
for (auto iter = se->begin(); iter != se->end(); iter++){
setRoot(sessionNode);
(*iter)->accept(*this);
}
}
void SessionVisitor::visit (RenderSource&)
{
xmlCurrent_->SetAttribute("type", "RenderSource");
}
void SessionVisitor::visit (CloneSource& s)
{
xmlCurrent_->SetAttribute("type", "CloneSource");
XMLElement *origin = xmlDoc_->NewElement("origin");
xmlCurrent_->InsertEndChild(origin);
XMLText *text = xmlDoc_->NewText( s.origin()->name().c_str() );
origin->InsertEndChild( text );
}
void SessionVisitor::visit (PatternSource& s)
{
xmlCurrent_->SetAttribute("type", "PatternSource");
xmlCurrent_->SetAttribute("pattern", s.pattern()->type() );
XMLElement *resolution = xmlDoc_->NewElement("resolution");
resolution->InsertEndChild( XMLElementFromGLM(xmlDoc_, s.pattern()->resolution() ) );
xmlCurrent_->InsertEndChild(resolution);
}
void SessionVisitor::visit (DeviceSource& s)
{
xmlCurrent_->SetAttribute("type", "DeviceSource");
xmlCurrent_->SetAttribute("device", s.device().c_str() );
}
void SessionVisitor::visit (NetworkSource& s)
{
xmlCurrent_->SetAttribute("type", "NetworkSource");
xmlCurrent_->SetAttribute("connection", s.connection().c_str() );
}
void SessionVisitor::visit (MixingGroup& g)
{
xmlCurrent_->SetAttribute("size", g.size());
for (auto it = g.begin(); it != g.end(); it++) {
XMLElement *sour = xmlDoc_->NewElement("source");
sour->SetAttribute("id", (*it)->id());
xmlCurrent_->InsertEndChild(sour);
}
}

View File

@@ -1,62 +0,0 @@
#ifndef XMLVISITOR_H
#define XMLVISITOR_H
#include "Visitor.h"
#include "tinyxml2Toolkit.h"
class Session;
class SessionVisitor : public Visitor {
bool recursive_;
tinyxml2::XMLDocument *xmlDoc_;
tinyxml2::XMLElement *xmlCurrent_;
public:
SessionVisitor(tinyxml2::XMLDocument *doc = nullptr,
tinyxml2::XMLElement *root = nullptr,
bool recursive = false);
inline void setRoot(tinyxml2::XMLElement *root) { xmlCurrent_ = root; }
static bool saveSession(const std::string& filename, Session *session);
// Elements of Scene
void visit(Scene& n) override;
void visit(Node& n) override;
void visit(Group& n) override;
void visit(Switch& n) override;
void visit(Primitive& n) override;
void visit(Surface&) override;
void visit(ImageSurface& n) override;
void visit(MediaSurface& n) override;
void visit(FrameBufferSurface&) override;
void visit(LineStrip& n) override;
void visit(LineSquare&) override;
void visit(Mesh& n) override;
void visit(Frame& n) override;
// Elements with attributes
void visit(MediaPlayer& n) override;
void visit(Shader& n) override;
void visit(ImageShader& n) override;
void visit(MaskShader& n) override;
void visit(ImageProcessingShader& n) override;
// Sources
void visit (Source& s) override;
void visit (MediaSource& s) override;
void visit (SessionFileSource& s) override;
void visit (SessionGroupSource& s) override;
void visit (RenderSource&) override;
void visit (CloneSource& s) override;
void visit (PatternSource& s) override;
void visit (DeviceSource& s) override;
void visit (NetworkSource& s) override;
void visit (MixingGroup& s) override;
protected:
static tinyxml2::XMLElement *NodeToXML(Node &n, tinyxml2::XMLDocument *doc);
};
#endif // XMLVISITOR_H

View File

@@ -1,500 +0,0 @@
#include <algorithm>
#include <iostream>
using namespace std;
#include <tinyxml2.h>
#include "tinyxml2Toolkit.h"
using namespace tinyxml2;
#include "defines.h"
#include "Settings.h"
#include "SystemToolkit.h"
Settings::Application Settings::application;
static string settingsFilename = "";
void Settings::Save()
{
XMLDocument xmlDoc;
XMLDeclaration *pDec = xmlDoc.NewDeclaration();
xmlDoc.InsertFirstChild(pDec);
XMLElement *pRoot = xmlDoc.NewElement(application.name.c_str());
#ifdef VIMIX_VERSION_MAJOR
pRoot->SetAttribute("major", VIMIX_VERSION_MAJOR);
pRoot->SetAttribute("minor", VIMIX_VERSION_MINOR);
xmlDoc.InsertEndChild(pRoot);
#endif
string comment = "Settings for " + application.name;
XMLComment *pComment = xmlDoc.NewComment(comment.c_str());
pRoot->InsertEndChild(pComment);
// block: windows
{
XMLElement *windowsNode = xmlDoc.NewElement( "Windows" );
for (int i = 0; i < application.windows.size(); i++)
{
const Settings::WindowConfig& w = application.windows[i];
XMLElement *window = xmlDoc.NewElement( "Window" );
window->SetAttribute("id", i);
window->SetAttribute("name", w.name.c_str());
window->SetAttribute("x", w.x);
window->SetAttribute("y", w.y);
window->SetAttribute("w", w.w);
window->SetAttribute("h", w.h);
window->SetAttribute("f", w.fullscreen);
window->SetAttribute("m", w.monitor.c_str());
windowsNode->InsertEndChild(window);
}
pRoot->InsertEndChild(windowsNode);
}
// General application preferences
XMLElement *applicationNode = xmlDoc.NewElement( "Application" );
applicationNode->SetAttribute("scale", application.scale);
applicationNode->SetAttribute("accent_color", application.accent_color);
applicationNode->SetAttribute("pannel_stick", application.pannel_stick);
applicationNode->SetAttribute("smooth_transition", application.smooth_transition);
applicationNode->SetAttribute("smooth_cursor", application.smooth_cursor);
applicationNode->SetAttribute("action_history_follow_view", application.action_history_follow_view);
applicationNode->SetAttribute("accept_connections", application.accept_connections);
pRoot->InsertEndChild(applicationNode);
// Widgets
XMLElement *widgetsNode = xmlDoc.NewElement( "Widgets" );
widgetsNode->SetAttribute("preview", application.widget.preview);
widgetsNode->SetAttribute("history", application.widget.history);
widgetsNode->SetAttribute("media_player", application.widget.media_player);
widgetsNode->SetAttribute("shader_editor", application.widget.shader_editor);
widgetsNode->SetAttribute("stats", application.widget.stats);
widgetsNode->SetAttribute("stats_timer", application.widget.stats_timer);
widgetsNode->SetAttribute("stats_corner", application.widget.stats_corner);
widgetsNode->SetAttribute("logs", application.widget.logs);
widgetsNode->SetAttribute("toolbox", application.widget.toolbox);
pRoot->InsertEndChild(widgetsNode);
// Render
XMLElement *RenderNode = xmlDoc.NewElement( "Render" );
RenderNode->SetAttribute("vsync", application.render.vsync);
RenderNode->SetAttribute("multisampling", application.render.multisampling);
RenderNode->SetAttribute("blit", application.render.blit);
RenderNode->SetAttribute("gpu_decoding", application.render.gpu_decoding);
RenderNode->SetAttribute("ratio", application.render.ratio);
RenderNode->SetAttribute("res", application.render.res);
pRoot->InsertEndChild(RenderNode);
// Record
XMLElement *RecordNode = xmlDoc.NewElement( "Record" );
RecordNode->SetAttribute("path", application.record.path.c_str());
RecordNode->SetAttribute("profile", application.record.profile);
RecordNode->SetAttribute("timeout", application.record.timeout);
pRoot->InsertEndChild(RecordNode);
// Transition
XMLElement *TransitionNode = xmlDoc.NewElement( "Transition" );
TransitionNode->SetAttribute("auto_open", application.transition.auto_open);
TransitionNode->SetAttribute("hide_windows", application.transition.hide_windows);
TransitionNode->SetAttribute("cross_fade", application.transition.cross_fade);
TransitionNode->SetAttribute("duration", application.transition.duration);
TransitionNode->SetAttribute("profile", application.transition.profile);
pRoot->InsertEndChild(TransitionNode);
// Source
XMLElement *SourceConfNode = xmlDoc.NewElement( "Source" );
SourceConfNode->SetAttribute("new_type", application.source.new_type);
SourceConfNode->SetAttribute("ratio", application.source.ratio);
SourceConfNode->SetAttribute("res", application.source.res);
pRoot->InsertEndChild(SourceConfNode);
// Brush
XMLElement *BrushNode = xmlDoc.NewElement( "Brush" );
BrushNode->InsertEndChild( XMLElementFromGLM(&xmlDoc, application.brush) );
pRoot->InsertEndChild(BrushNode);
// bloc connections
{
XMLElement *connectionsNode = xmlDoc.NewElement( "Connections" );
// map<int, std::string>::iterator iter;
// for (iter=application.instance_names.begin(); iter != application.instance_names.end(); iter++)
// {
// XMLElement *connection = xmlDoc.NewElement( "Instance" );
// connection->SetAttribute("name", iter->second.c_str());
// connection->SetAttribute("id", iter->first);
// connectionsNode->InsertEndChild(connection);
// }
pRoot->InsertEndChild(connectionsNode);
}
// bloc views
{
XMLElement *viewsNode = xmlDoc.NewElement( "Views" );
// save current view only if [mixing, geometry, layers, appearance]
int v = application.current_view > 4 ? 1 : application.current_view;
viewsNode->SetAttribute("current", v);
viewsNode->SetAttribute("workspace", application.current_workspace);
map<int, Settings::ViewConfig>::iterator iter;
for (iter=application.views.begin(); iter != application.views.end(); iter++)
{
const Settings::ViewConfig& v = iter->second;
XMLElement *view = xmlDoc.NewElement( "View" );
view->SetAttribute("name", v.name.c_str());
view->SetAttribute("id", iter->first);
XMLElement *scale = xmlDoc.NewElement("default_scale");
scale->InsertEndChild( XMLElementFromGLM(&xmlDoc, v.default_scale) );
view->InsertEndChild(scale);
XMLElement *translation = xmlDoc.NewElement("default_translation");
translation->InsertEndChild( XMLElementFromGLM(&xmlDoc, v.default_translation) );
view->InsertEndChild(translation);
viewsNode->InsertEndChild(view);
}
pRoot->InsertEndChild(viewsNode);
}
// bloc history
{
XMLElement *recent = xmlDoc.NewElement( "Recent" );
XMLElement *recentsession = xmlDoc.NewElement( "Session" );
recentsession->SetAttribute("path", application.recentSessions.path.c_str());
recentsession->SetAttribute("autoload", application.recentSessions.load_at_start);
recentsession->SetAttribute("autosave", application.recentSessions.save_on_exit);
recentsession->SetAttribute("valid", application.recentSessions.front_is_valid);
for(auto it = application.recentSessions.filenames.begin();
it != application.recentSessions.filenames.end(); it++) {
XMLElement *fileNode = xmlDoc.NewElement("path");
XMLText *text = xmlDoc.NewText( (*it).c_str() );
fileNode->InsertEndChild( text );
recentsession->InsertFirstChild(fileNode);
};
recent->InsertEndChild(recentsession);
XMLElement *recentfolder = xmlDoc.NewElement( "Folder" );
for(auto it = application.recentFolders.filenames.begin();
it != application.recentFolders.filenames.end(); it++) {
XMLElement *fileNode = xmlDoc.NewElement("path");
XMLText *text = xmlDoc.NewText( (*it).c_str() );
fileNode->InsertEndChild( text );
recentfolder->InsertFirstChild(fileNode);
};
recent->InsertEndChild(recentfolder);
XMLElement *recentmedia = xmlDoc.NewElement( "Import" );
recentmedia->SetAttribute("path", application.recentImport.path.c_str());
for(auto it = application.recentImport.filenames.begin();
it != application.recentImport.filenames.end(); it++) {
XMLElement *fileNode = xmlDoc.NewElement("path");
XMLText *text = xmlDoc.NewText( (*it).c_str() );
fileNode->InsertEndChild( text );
recentmedia->InsertFirstChild(fileNode);
}
recent->InsertEndChild(recentmedia);
pRoot->InsertEndChild(recent);
}
// First save : create filename
if (settingsFilename.empty())
settingsFilename = SystemToolkit::full_filename(SystemToolkit::settings_path(), APP_SETTINGS);
XMLError eResult = xmlDoc.SaveFile(settingsFilename.c_str());
XMLResultError(eResult);
}
void Settings::Load()
{
XMLDocument xmlDoc;
if (settingsFilename.empty())
settingsFilename = SystemToolkit::full_filename(SystemToolkit::settings_path(), APP_SETTINGS);
XMLError eResult = xmlDoc.LoadFile(settingsFilename.c_str());
// do not warn if non existing file
if (eResult == XML_ERROR_FILE_NOT_FOUND)
return;
// warn and return on other error
else if (XMLResultError(eResult))
return;
XMLElement *pRoot = xmlDoc.FirstChildElement(application.name.c_str());
if (pRoot == nullptr) return;
// cancel on different root name
if (application.name.compare( string( pRoot->Value() ) ) != 0 )
return;
#ifdef VIMIX_VERSION_MAJOR
// cancel on different version
int version_major = -1, version_minor = -1;
pRoot->QueryIntAttribute("major", &version_major);
pRoot->QueryIntAttribute("minor", &version_minor);
if (version_major != VIMIX_VERSION_MAJOR || version_minor != VIMIX_VERSION_MINOR)
return;
#endif
XMLElement * applicationNode = pRoot->FirstChildElement("Application");
if (applicationNode != nullptr) {
applicationNode->QueryFloatAttribute("scale", &application.scale);
applicationNode->QueryIntAttribute("accent_color", &application.accent_color);
applicationNode->QueryBoolAttribute("pannel_stick", &application.pannel_stick);
applicationNode->QueryBoolAttribute("smooth_transition", &application.smooth_transition);
applicationNode->QueryBoolAttribute("smooth_cursor", &application.smooth_cursor);
applicationNode->QueryBoolAttribute("action_history_follow_view", &application.action_history_follow_view);
applicationNode->QueryBoolAttribute("accept_connections", &application.accept_connections);
}
// Widgets
XMLElement * widgetsNode = pRoot->FirstChildElement("Widgets");
if (widgetsNode != nullptr) {
widgetsNode->QueryBoolAttribute("preview", &application.widget.preview);
widgetsNode->QueryBoolAttribute("history", &application.widget.history);
widgetsNode->QueryBoolAttribute("media_player", &application.widget.media_player);
widgetsNode->QueryBoolAttribute("shader_editor", &application.widget.shader_editor);
widgetsNode->QueryBoolAttribute("stats", &application.widget.stats);
widgetsNode->QueryBoolAttribute("stats_timer", &application.widget.stats_timer);
widgetsNode->QueryIntAttribute("stats_corner", &application.widget.stats_corner);
widgetsNode->QueryBoolAttribute("logs", &application.widget.logs);
widgetsNode->QueryBoolAttribute("toolbox", &application.widget.toolbox);
}
// Render
XMLElement * rendernode = pRoot->FirstChildElement("Render");
if (rendernode != nullptr) {
rendernode->QueryIntAttribute("vsync", &application.render.vsync);
rendernode->QueryIntAttribute("multisampling", &application.render.multisampling);
rendernode->QueryBoolAttribute("blit", &application.render.blit);
rendernode->QueryBoolAttribute("gpu_decoding", &application.render.gpu_decoding);
rendernode->QueryIntAttribute("ratio", &application.render.ratio);
rendernode->QueryIntAttribute("res", &application.render.res);
}
// Record
XMLElement * recordnode = pRoot->FirstChildElement("Record");
if (recordnode != nullptr) {
recordnode->QueryIntAttribute("profile", &application.record.profile);
recordnode->QueryFloatAttribute("timeout", &application.record.timeout);
const char *path_ = recordnode->Attribute("path");
if (path_)
application.record.path = std::string(path_);
else
application.record.path = SystemToolkit::home_path();
}
// Source
XMLElement * sourceconfnode = pRoot->FirstChildElement("Source");
if (sourceconfnode != nullptr) {
sourceconfnode->QueryIntAttribute("new_type", &application.source.new_type);
sourceconfnode->QueryIntAttribute("ratio", &application.source.ratio);
sourceconfnode->QueryIntAttribute("res", &application.source.res);
}
// Transition
XMLElement * transitionnode = pRoot->FirstChildElement("Transition");
if (transitionnode != nullptr) {
transitionnode->QueryBoolAttribute("hide_windows", &application.transition.hide_windows);
transitionnode->QueryBoolAttribute("auto_open", &application.transition.auto_open);
transitionnode->QueryBoolAttribute("cross_fade", &application.transition.cross_fade);
transitionnode->QueryFloatAttribute("duration", &application.transition.duration);
transitionnode->QueryIntAttribute("profile", &application.transition.profile);
}
// bloc windows
{
XMLElement * pElement = pRoot->FirstChildElement("Windows");
if (pElement)
{
XMLElement* windowNode = pElement->FirstChildElement("Window");
for( ; windowNode ; windowNode=windowNode->NextSiblingElement())
{
Settings::WindowConfig w;
w.name = std::string(windowNode->Attribute("name"));
windowNode->QueryIntAttribute("x", &w.x); // If this fails, original value is left as-is
windowNode->QueryIntAttribute("y", &w.y);
windowNode->QueryIntAttribute("w", &w.w);
windowNode->QueryIntAttribute("h", &w.h);
windowNode->QueryBoolAttribute("f", &w.fullscreen);
w.monitor = std::string(windowNode->Attribute("m"));
int i = 0;
windowNode->QueryIntAttribute("id", &i);
application.windows[i] = w;
}
}
}
// Brush
XMLElement * brushnode = pRoot->FirstChildElement("Brush");
if (brushnode != nullptr) {
tinyxml2::XMLElementToGLM( brushnode->FirstChildElement("vec3"), application.brush);
}
// bloc views
{
application.views.clear(); // trash existing list
XMLElement * pElement = pRoot->FirstChildElement("Views");
if (pElement)
{
pElement->QueryIntAttribute("current", &application.current_view);
pElement->QueryIntAttribute("workspace", &application.current_workspace);
XMLElement* viewNode = pElement->FirstChildElement("View");
for( ; viewNode ; viewNode=viewNode->NextSiblingElement())
{
int id = 0;
viewNode->QueryIntAttribute("id", &id);
application.views[id].name = viewNode->Attribute("name");
XMLElement* scaleNode = viewNode->FirstChildElement("default_scale");
tinyxml2::XMLElementToGLM( scaleNode->FirstChildElement("vec3"),
application.views[id].default_scale);
XMLElement* translationNode = viewNode->FirstChildElement("default_translation");
tinyxml2::XMLElementToGLM( translationNode->FirstChildElement("vec3"),
application.views[id].default_translation);
}
}
}
// bloc Connections
{
XMLElement * pElement = pRoot->FirstChildElement("Connections");
if (pElement)
{
// XMLElement* connectionNode = pElement->FirstChildElement("Instance");
// for( ; connectionNode ; connectionNode=connectionNode->NextSiblingElement())
// {
// int id = 0;
// connectionNode->QueryIntAttribute("id", &id);
// application.instance_names[id] = connectionNode->Attribute("name");
// }
}
}
// bloc history of recent
{
XMLElement * pElement = pRoot->FirstChildElement("Recent");
if (pElement)
{
// recent session filenames
XMLElement * pSession = pElement->FirstChildElement("Session");
if (pSession)
{
const char *path_ = pSession->Attribute("path");
if (path_)
application.recentSessions.path = std::string(path_);
else
application.recentSessions.path = SystemToolkit::home_path();
application.recentSessions.filenames.clear();
XMLElement* path = pSession->FirstChildElement("path");
for( ; path ; path = path->NextSiblingElement())
{
const char *p = path->GetText();
if (p)
application.recentSessions.push( std::string (p) );
}
pSession->QueryBoolAttribute("autoload", &application.recentSessions.load_at_start);
pSession->QueryBoolAttribute("autosave", &application.recentSessions.save_on_exit);
pSession->QueryBoolAttribute("valid", &application.recentSessions.front_is_valid);
}
// recent session folders
XMLElement * pFolder = pElement->FirstChildElement("Folder");
if (pFolder)
{
application.recentFolders.filenames.clear();
XMLElement* path = pFolder->FirstChildElement("path");
for( ; path ; path = path->NextSiblingElement())
{
const char *p = path->GetText();
if (p)
application.recentFolders.push( std::string (p) );
}
}
// recent media uri
XMLElement * pImport = pElement->FirstChildElement("Import");
if (pImport)
{
const char *path_ = pImport->Attribute("path");
if (path_)
application.recentImport.path = std::string(path_);
else
application.recentImport.path = SystemToolkit::home_path();
application.recentImport.filenames.clear();
XMLElement* path = pImport->FirstChildElement("path");
for( ; path ; path = path->NextSiblingElement())
{
const char *p = path->GetText();
if (p)
application.recentImport.push( std::string (p) );
}
}
}
}
}
void Settings::Lock()
{
std::string lockfile = SystemToolkit::full_filename(SystemToolkit::settings_path(), "lock");
application.fresh_start = false;
FILE *file = fopen(lockfile.c_str(), "r");
int l = 0;
if (file) {
if ( fscanf(file, "%d", &l) < 1)
l = 0;
fclose(file);
}
// not locked or file not existing
if ( l < 1 ) {
file = fopen(lockfile.c_str(), "w");
if (file) {
fprintf(file, "1");
fclose(file);
}
application.fresh_start = true;
}
}
void Settings::Unlock()
{
std::string lockfile = SystemToolkit::full_filename(SystemToolkit::settings_path(), "lock");
FILE *file = fopen(lockfile.c_str(), "w");
if (file) {
fprintf(file, "0");
fclose(file);
}
}
void Settings::Check()
{
Settings::Save();
XMLDocument xmlDoc;
XMLError eResult = xmlDoc.LoadFile(settingsFilename.c_str());
if (XMLResultError(eResult))
return;
xmlDoc.Print();
}

View File

@@ -1,253 +0,0 @@
#ifndef __SETTINGS_H_
#define __SETTINGS_H_
#include "defines.h"
#include <string>
#include <map>
#include <vector>
#include <list>
#include <glm/glm.hpp>
namespace Settings {
struct WidgetsConfig
{
bool stats;
int stats_corner;
bool stats_timer;
bool logs;
bool preview;
bool history;
bool media_player;
bool media_player_view;
bool shader_editor;
bool toolbox;
WidgetsConfig() {
stats = false;
stats_timer = false;
stats_corner = 1;
logs = false;
preview = false;
history = false;
media_player = false;
media_player_view = true;
shader_editor = false;
toolbox = false;
}
};
struct WindowConfig
{
std::string name;
int x,y,w,h;
bool fullscreen;
std::string monitor;
WindowConfig() : name(""), x(15), y(15), w(1280), h(720), fullscreen(false), monitor("") { }
};
struct ViewConfig
{
std::string name;
glm::vec3 default_scale;
glm::vec3 default_translation;
ViewConfig() : name("") {
default_scale = glm::vec3(1.f);
default_translation = glm::vec3(0.f);
}
};
#define RECORD_MAX_TIMEOUT 1800.f
struct RecordConfig
{
std::string path;
int profile;
float timeout;
RecordConfig() : path("") {
profile = 0;
timeout = RECORD_MAX_TIMEOUT;
}
};
struct History
{
std::string path;
std::list<std::string> filenames;
bool front_is_valid;
bool load_at_start;
bool save_on_exit;
History() {
path = IMGUI_LABEL_RECENT_FILES;
front_is_valid = false;
load_at_start = false;
save_on_exit = false;
}
void push(const std::string &filename) {
if (filename.empty()) {
front_is_valid = false;
return;
}
filenames.remove(filename);
filenames.push_front(filename);
if (filenames.size() > MAX_RECENT_HISTORY)
filenames.pop_back();
front_is_valid = true;
}
void remove(const std::string &filename) {
if (filename.empty())
return;
if (filenames.front() == filename)
front_is_valid = false;
filenames.remove(filename);
}
};
struct TransitionConfig
{
bool cross_fade;
bool auto_open;
bool hide_windows;
float duration;
int profile;
TransitionConfig() {
cross_fade = true;
auto_open = true;
hide_windows = true;
duration = 1.f;
profile = 0;
}
};
struct RenderConfig
{
bool blit;
int vsync;
int multisampling;
int ratio;
int res;
float fading;
bool gpu_decoding;
RenderConfig() {
blit = false;
vsync = 1;
multisampling = 2;
ratio = 3;
res = 1;
fading = 0.0;
gpu_decoding = true;
}
};
struct SourceConfig
{
int new_type;
int ratio;
int res;
SourceConfig() {
new_type = 0;
ratio = 3;
res = 1;
}
};
struct Application
{
// instance check
bool fresh_start;
int instance_id;
// Verification
std::string name;
std::string executable;
// Global settings Application interface
float scale;
int accent_color;
bool pannel_stick;
bool smooth_transition;
bool smooth_cursor;
bool action_history_follow_view;
// connection settings
bool accept_connections;
// std::map<int, std::string> instance_names;
// Settings of widgets
WidgetsConfig widget;
// Settings of Views
int current_view;
int current_workspace;
std::map<int, ViewConfig> views;
// settings brush texture paint
glm::vec3 brush;
// settings render
RenderConfig render;
// settings exporters
RecordConfig record;
// settings new source
SourceConfig source;
// settings transition
TransitionConfig transition;
// multiple windows handling
std::vector<WindowConfig> windows;
// recent files histories
History recentSessions;
History recentFolders;
History recentImport;
Application() : fresh_start(false), instance_id(0), name(APP_NAME), executable(APP_NAME) {
scale = 1.f;
accent_color = 0;
pannel_stick = false;
smooth_transition = true;
smooth_cursor = false;
action_history_follow_view = false;
accept_connections = false;
current_view = 1;
current_workspace= 1;
brush = glm::vec3(0.5f, 0.1f, 0.f);
windows = std::vector<WindowConfig>(3);
windows[0].name = APP_NAME APP_TITLE;
windows[0].w = 1600;
windows[0].h = 900;
windows[1].name = APP_NAME " -- Output";
}
};
// minimal implementation of settings
// Can be accessed r&w anywhere
extern Application application;
// Save and Load store settings in XML file
void Save();
void Load();
void Lock();
void Unlock();
void Check();
}
#endif /* __SETTINGS_H_ */

View File

@@ -1,267 +0,0 @@
#include "Shader.h"
#include "Resource.h"
#include "FrameBuffer.h"
#include "Log.h"
#include "Visitor.h"
#include "RenderingManager.h"
#include <fstream>
#include <sstream>
#include <iostream>
#include <chrono>
#include <ctime>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "GlmToolkit.h"
#include <glm/gtc/type_ptr.hpp>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/string_cast.hpp>
// Globals
ShadingProgram *ShadingProgram::currentProgram_ = nullptr;
ShadingProgram simpleShadingProgram("shaders/simple.vs", "shaders/simple.fs");
// Blending presets for matching with Shader::BlendModes:
GLenum blending_equation[9] = { GL_FUNC_ADD, // normal
GL_FUNC_ADD, // screen
GL_FUNC_REVERSE_SUBTRACT, // subtract
GL_FUNC_ADD, // multiply
GL_FUNC_ADD, // soft light
GL_FUNC_ADD, // hard light
GL_FUNC_REVERSE_SUBTRACT, // soft subtract
GL_MAX, // lighten only
GL_FUNC_ADD};
GLenum blending_source_function[9] = { GL_ONE, // normal
GL_ONE, // screen
GL_SRC_COLOR, // subtract (can be GL_ONE)
GL_DST_COLOR, // multiply : src x dst color
GL_DST_COLOR, // soft light : src x dst color
GL_SRC_COLOR, // hard light : src x src color
GL_DST_COLOR, // soft subtract
GL_ONE, // lighten only
GL_ONE};
GLenum blending_destination_function[9] = {GL_ONE_MINUS_SRC_ALPHA,// normal
GL_ONE, // screen
GL_ONE, // subtract
GL_ONE_MINUS_SRC_ALPHA, // multiply
GL_ONE, // soft light
GL_ONE, // hard light
GL_ONE, // soft subtract
GL_ONE, // lighten only
GL_ZERO};
ShadingProgram::ShadingProgram(const std::string& vertex_file, const std::string& fragment_file) : vertex_id_(0), fragment_id_(0), id_(0)
{
vertex_file_ = vertex_file;
fragment_file_ = fragment_file;
}
void ShadingProgram::init()
{
vertex_code_ = Resource::getText(vertex_file_);
fragment_code_ = Resource::getText(fragment_file_);
compile();
link();
}
bool ShadingProgram::initialized()
{
return (id_ != 0);
}
void ShadingProgram::compile()
{
const char* vcode = vertex_code_.c_str();
vertex_id_ = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_id_, 1, &vcode, NULL);
glCompileShader(vertex_id_);
const char* fcode = fragment_code_.c_str();
fragment_id_ = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_id_, 1, &fcode, NULL);
glCompileShader(fragment_id_);
checkCompileErr();
}
void ShadingProgram::link()
{
id_ = glCreateProgram();
glAttachShader(id_, vertex_id_);
glAttachShader(id_, fragment_id_);
glLinkProgram(id_);
checkLinkingErr();
glUseProgram(id_);
glUniform1i(glGetUniformLocation(id_, "iChannel0"), 0);
glUniform1i(glGetUniformLocation(id_, "iChannel1"), 1);
glUseProgram(0);
glDeleteShader(vertex_id_);
glDeleteShader(fragment_id_);
}
void ShadingProgram::use()
{
if (currentProgram_ == nullptr || currentProgram_ != this)
{
currentProgram_ = this;
glUseProgram(id_);
}
}
void ShadingProgram::enduse()
{
glUseProgram(0);
currentProgram_ = nullptr ;
}
template<>
void ShadingProgram::setUniform<int>(const std::string& name, int val) {
glUniform1i(glGetUniformLocation(id_, name.c_str()), val);
}
template<>
void ShadingProgram::setUniform<bool>(const std::string& name, bool val) {
glUniform1i(glGetUniformLocation(id_, name.c_str()), val);
}
template<>
void ShadingProgram::setUniform<float>(const std::string& name, float val) {
glUniform1f(glGetUniformLocation(id_, name.c_str()), val);
}
template<>
void ShadingProgram::setUniform<float>(const std::string& name, float val1, float val2) {
glUniform2f(glGetUniformLocation(id_, name.c_str()), val1, val2);
}
template<>
void ShadingProgram::setUniform<float>(const std::string& name, float val1, float val2, float val3) {
glUniform3f(glGetUniformLocation(id_, name.c_str()), val1, val2, val3);
}
template<>
void ShadingProgram::setUniform<glm::vec2>(const std::string& name, glm::vec2 val) {
glm::vec2 v(val);
glUniform2fv(glGetUniformLocation(id_, name.c_str()), 1, glm::value_ptr(v));
}
template<>
void ShadingProgram::setUniform<glm::vec3>(const std::string& name, glm::vec3 val) {
glm::vec3 v(val);
glUniform3fv(glGetUniformLocation(id_, name.c_str()), 1, glm::value_ptr(v));
}
template<>
void ShadingProgram::setUniform<glm::vec4>(const std::string& name, glm::vec4 val) {
glm::vec4 v(val);
glUniform4fv(glGetUniformLocation(id_, name.c_str()), 1, glm::value_ptr(v));
}
template<>
void ShadingProgram::setUniform<glm::mat4>(const std::string& name, glm::mat4 val) {
glm::mat4 m(val);
glUniformMatrix4fv(glGetUniformLocation(id_, name.c_str()), 1, GL_FALSE, glm::value_ptr(m));
}
// template<>
// void ShadingProgram::setUniform<float*>(const std::string& name, float* val) {
// glUniformMatrix4fv(glGetUniformLocation(id_, name.c_str()), 1, GL_FALSE, val);
// }
void ShadingProgram::checkCompileErr()
{
int success;
char infoLog[1024];
glGetShaderiv(vertex_id_, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertex_id_, 1024, NULL, infoLog);
Log::Warning("Error compiling Vertex ShadingProgram:\n%s", infoLog);
}
glGetShaderiv(fragment_id_, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragment_id_, 1024, NULL, infoLog);
Log::Warning("Error compiling Fragment ShadingProgram:\n%s", infoLog);
}
}
void ShadingProgram::checkLinkingErr()
{
int success;
char infoLog[1024];
glGetProgramiv(id_, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(id_, 1024, NULL, infoLog);
Log::Warning("Error linking ShadingProgram:\n%s", infoLog);
}
}
bool Shader::force_blending_opacity = false;
Shader::Shader() : blending(BLEND_OPACITY)
{
// create unique id
id_ = GlmToolkit::uniqueId();
program_ = &simpleShadingProgram;
reset();
}
void Shader::operator = (const Shader &S )
{
color = S.color;
blending = S.blending;
iTransform = S.iTransform;
}
void Shader::accept(Visitor& v) {
v.visit(*this);
}
void Shader::use()
{
// initialization on first use
if (!program_->initialized())
program_->init();
// Use program
program_->use();
// set uniforms
program_->setUniform("projection", projection);
program_->setUniform("modelview", modelview);
program_->setUniform("iTransform", iTransform);
program_->setUniform("color", color);
glm::vec3 iResolution = glm::vec3( Rendering::manager().currentAttrib().viewport, 0.f);
program_->setUniform("iResolution", iResolution);
// Blending Function
if (force_blending_opacity) {
glEnable(GL_BLEND);
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
else if ( blending < BLEND_NONE ) {
glEnable(GL_BLEND);
glBlendEquationSeparate(blending_equation[blending], GL_FUNC_ADD);
glBlendFuncSeparate(blending_source_function[blending], blending_destination_function[blending], GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
else
glDisable(GL_BLEND);
}
void Shader::reset()
{
projection = glm::identity<glm::mat4>();
modelview = glm::identity<glm::mat4>();
iTransform = glm::identity<glm::mat4>();
color = glm::vec4(1.f, 1.f, 1.f, 1.f);
}

View File

@@ -1,81 +0,0 @@
#ifndef __SHADER_H_
#define __SHADER_H_
#include <string>
#include <vector>
#include <glm/glm.hpp>
// Forward declare classes referenced
class Visitor;
class FrameBuffer;
class ShadingProgram
{
public:
ShadingProgram(const std::string& vertex_file, const std::string& fragment_file);
void init();
bool initialized();
void use();
template<typename T> void setUniform(const std::string& name, T val);
template<typename T> void setUniform(const std::string& name, T val1, T val2);
template<typename T> void setUniform(const std::string& name, T val1, T val2, T val3);
static void enduse();
private:
void checkCompileErr();
void checkLinkingErr();
void compile();
void link();
unsigned int vertex_id_, fragment_id_, id_;
std::string vertex_code_;
std::string fragment_code_;
std::string vertex_file_;
std::string fragment_file_;
static ShadingProgram *currentProgram_;
};
class Shader
{
uint64_t id_;
public:
Shader();
virtual ~Shader() {}
// unique identifyer generated at instanciation
inline uint64_t id () const { return id_; }
virtual void use();
virtual void reset();
virtual void accept(Visitor& v);
void operator = (const Shader &D );
glm::mat4 projection;
glm::mat4 modelview;
glm::mat4 iTransform;
glm::vec4 color;
typedef enum {
BLEND_OPACITY = 0,
BLEND_SCREEN,
BLEND_SUBTRACT,
BLEND_MULTIPLY,
BLEND_SOFT_LIGHT,
BLEND_HARD_LIGHT,
BLEND_SOFT_SUBTRACT,
BLEND_LIGHTEN_ONLY,
BLEND_NONE
} BlendMode;
BlendMode blending;
static bool force_blending_opacity;
protected:
ShadingProgram *program_;
};
#endif /* __SHADER_H_ */

View File

@@ -1,853 +0,0 @@
#include <algorithm>
#include <locale>
#include <tinyxml2.h>
#include <glm/gtc/matrix_access.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "defines.h"
#include "FrameBuffer.h"
#include "Decorations.h"
#include "Resource.h"
#include "SearchVisitor.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "SystemToolkit.h"
#include "SessionVisitor.h"
#include "Log.h"
#include "MixingGroup.h"
#include "Source.h"
Source::Source() : initialized_(false), symbol_(nullptr), active_(true), locked_(false), need_update_(true), workspace_(STAGE)
{
// create unique id
id_ = GlmToolkit::uniqueId();
sprintf(initials_, "__");
name_ = "Source";
mode_ = Source::UNINITIALIZED;
// create groups and overlays for each view
// default rendering node
groups_[View::RENDERING] = new Group;
groups_[View::RENDERING]->visible_ = false;
// default mixing nodes
groups_[View::MIXING] = new Group;
groups_[View::MIXING]->visible_ = false;
groups_[View::MIXING]->scale_ = glm::vec3(0.15f, 0.15f, 1.f);
groups_[View::MIXING]->translation_ = glm::vec3(DEFAULT_MIXING_TRANSLATION, 0.f);
frames_[View::MIXING] = new Switch;
Frame *frame = new Frame(Frame::ROUND, Frame::THIN, Frame::DROP);
frame->translation_.z = 0.1;
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.95f);
frames_[View::MIXING]->attach(frame);
frame = new Frame(Frame::ROUND, Frame::LARGE, Frame::DROP);
frame->translation_.z = 0.01;
frame->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
frames_[View::MIXING]->attach(frame);
groups_[View::MIXING]->attach(frames_[View::MIXING]);
overlays_[View::MIXING] = new Group;
overlays_[View::MIXING]->translation_.z = 0.1;
overlays_[View::MIXING]->visible_ = false;
Symbol *center = new Symbol(Symbol::CIRCLE_POINT, glm::vec3(0.f, 0.f, 0.1f));
overlays_[View::MIXING]->attach(center);
groups_[View::MIXING]->attach(overlays_[View::MIXING]);
overlay_mixinggroup_ = new Switch;
overlay_mixinggroup_->translation_.z = 0.1;
center = new Symbol(Symbol::CIRCLE_POINT, glm::vec3(0.f, 0.f, 0.1f));
center->scale_= glm::vec3(1.6f, 1.6f, 1.f);
center->color = glm::vec4( COLOR_MIXING_GROUP, 0.96f);
overlay_mixinggroup_->attach(center);
rotation_mixingroup_ = new Symbol(Symbol::ROTATION, glm::vec3(0.f, 0.f, 0.1f));
rotation_mixingroup_->color = glm::vec4( COLOR_MIXING_GROUP, 0.94f);
rotation_mixingroup_->scale_ = glm::vec3(3.f, 3.f, 1.f);
overlay_mixinggroup_->attach(rotation_mixingroup_);
groups_[View::MIXING]->attach(overlay_mixinggroup_);
// default geometry nodes
groups_[View::GEOMETRY] = new Group;
groups_[View::GEOMETRY]->visible_ = false;
frames_[View::GEOMETRY] = new Switch;
frame = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE);
frame->translation_.z = 0.1;
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.8f);
frames_[View::GEOMETRY]->attach(frame);
frame = new Frame(Frame::SHARP, Frame::LARGE, Frame::GLOW);
frame->translation_.z = 0.1;
frame->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
frames_[View::GEOMETRY]->attach(frame);
groups_[View::GEOMETRY]->attach(frames_[View::GEOMETRY]);
overlays_[View::GEOMETRY] = new Group;
overlays_[View::GEOMETRY]->translation_.z = 0.15;
overlays_[View::GEOMETRY]->visible_ = false;
handles_[View::GEOMETRY][Handles::RESIZE] = new Handles(Handles::RESIZE);
handles_[View::GEOMETRY][Handles::RESIZE]->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
handles_[View::GEOMETRY][Handles::RESIZE]->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(handles_[View::GEOMETRY][Handles::RESIZE]);
handles_[View::GEOMETRY][Handles::RESIZE_H] = new Handles(Handles::RESIZE_H);
handles_[View::GEOMETRY][Handles::RESIZE_H]->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
handles_[View::GEOMETRY][Handles::RESIZE_H]->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(handles_[View::GEOMETRY][Handles::RESIZE_H]);
handles_[View::GEOMETRY][Handles::RESIZE_V] = new Handles(Handles::RESIZE_V);
handles_[View::GEOMETRY][Handles::RESIZE_V]->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
handles_[View::GEOMETRY][Handles::RESIZE_V]->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(handles_[View::GEOMETRY][Handles::RESIZE_V]);
handles_[View::GEOMETRY][Handles::ROTATE] = new Handles(Handles::ROTATE);
handles_[View::GEOMETRY][Handles::ROTATE]->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
handles_[View::GEOMETRY][Handles::ROTATE]->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(handles_[View::GEOMETRY][Handles::ROTATE]);
handles_[View::GEOMETRY][Handles::SCALE] = new Handles(Handles::SCALE);
handles_[View::GEOMETRY][Handles::SCALE]->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
handles_[View::GEOMETRY][Handles::SCALE]->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(handles_[View::GEOMETRY][Handles::SCALE]);
handles_[View::GEOMETRY][Handles::MENU] = new Handles(Handles::MENU);
handles_[View::GEOMETRY][Handles::MENU]->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
handles_[View::GEOMETRY][Handles::MENU]->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(handles_[View::GEOMETRY][Handles::MENU]);
handles_[View::GEOMETRY][Handles::CROP] = new Handles(Handles::CROP);
handles_[View::GEOMETRY][Handles::CROP]->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
handles_[View::GEOMETRY][Handles::CROP]->translation_.z = 0.1;
overlays_[View::GEOMETRY]->attach(handles_[View::GEOMETRY][Handles::CROP]);
frame = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE);
frame->translation_.z = 0.1;
frame->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 0.7f);
overlays_[View::GEOMETRY]->attach(frame);
groups_[View::GEOMETRY]->attach(overlays_[View::GEOMETRY]);
// default layer nodes
groups_[View::LAYER] = new Group;
groups_[View::LAYER]->visible_ = false;
groups_[View::LAYER]->translation_.z = -1.f;
frames_[View::LAYER] = new Switch;
frame = new Frame(Frame::ROUND, Frame::THIN, Frame::PERSPECTIVE);
frame->translation_.z = 0.1;
frame->color = glm::vec4( COLOR_DEFAULT_SOURCE, 0.95f);
frames_[View::LAYER]->attach(frame);
frame = new Frame(Frame::ROUND, Frame::LARGE, Frame::PERSPECTIVE);
frame->translation_.z = 0.1;
frame->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f);
frames_[View::LAYER]->attach(frame);
groups_[View::LAYER]->attach(frames_[View::LAYER]);
overlays_[View::LAYER] = new Group;
overlays_[View::LAYER]->translation_.z = 0.15;
overlays_[View::LAYER]->visible_ = false;
groups_[View::LAYER]->attach(overlays_[View::LAYER]);
// default appearance node
groups_[View::TEXTURE] = new Group;
groups_[View::TEXTURE]->visible_ = false;
frames_[View::TEXTURE] = new Switch;
frame = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE);
frame->translation_.z = 0.1;
frame->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 0.7f);
frames_[View::TEXTURE]->attach(frame);
frame = new Frame(Frame::SHARP, Frame::LARGE, Frame::NONE);
frame->translation_.z = 0.1;
frame->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f);
frames_[View::TEXTURE]->attach(frame);
groups_[View::TEXTURE]->attach(frames_[View::TEXTURE]);
overlays_[View::TEXTURE] = new Group;
overlays_[View::TEXTURE]->translation_.z = 0.1;
overlays_[View::TEXTURE]->visible_ = false;
handles_[View::TEXTURE][Handles::RESIZE] = new Handles(Handles::RESIZE);
handles_[View::TEXTURE][Handles::RESIZE]->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f);
handles_[View::TEXTURE][Handles::RESIZE]->translation_.z = 0.1;
overlays_[View::TEXTURE]->attach(handles_[View::TEXTURE][Handles::RESIZE]);
handles_[View::TEXTURE][Handles::RESIZE_H] = new Handles(Handles::RESIZE_H);
handles_[View::TEXTURE][Handles::RESIZE_H]->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f);
handles_[View::TEXTURE][Handles::RESIZE_H]->translation_.z = 0.1;
overlays_[View::TEXTURE]->attach(handles_[View::TEXTURE][Handles::RESIZE_H]);
handles_[View::TEXTURE][Handles::RESIZE_V] = new Handles(Handles::RESIZE_V);
handles_[View::TEXTURE][Handles::RESIZE_V]->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f);
handles_[View::TEXTURE][Handles::RESIZE_V]->translation_.z = 0.1;
overlays_[View::TEXTURE]->attach(handles_[View::TEXTURE][Handles::RESIZE_V]);
handles_[View::TEXTURE][Handles::ROTATE] = new Handles(Handles::ROTATE);
handles_[View::TEXTURE][Handles::ROTATE]->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f);
handles_[View::TEXTURE][Handles::ROTATE]->translation_.z = 0.1;
overlays_[View::TEXTURE]->attach(handles_[View::TEXTURE][Handles::ROTATE]);
handles_[View::TEXTURE][Handles::SCALE] = new Handles(Handles::SCALE);
handles_[View::TEXTURE][Handles::SCALE]->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f);
handles_[View::TEXTURE][Handles::SCALE]->translation_.z = 0.1;
overlays_[View::TEXTURE]->attach(handles_[View::TEXTURE][Handles::SCALE]);
handles_[View::TEXTURE][Handles::MENU] = new Handles(Handles::MENU);
handles_[View::TEXTURE][Handles::MENU]->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f);
handles_[View::TEXTURE][Handles::MENU]->translation_.z = 0.1;
overlays_[View::TEXTURE]->attach(handles_[View::TEXTURE][Handles::MENU]);
groups_[View::TEXTURE]->attach(overlays_[View::TEXTURE]);
// empty transition node
groups_[View::TRANSITION] = new Group;
// locker switch button : locked / unlocked icons
locker_ = new Switch;
lock_ = new Handles(Handles::LOCKED);
locker_->attach(lock_);
unlock_ = new Handles(Handles::UNLOCKED);
locker_->attach(unlock_);
// create objects
stored_status_ = new Group;
// simple image shader (with texturing) for blending
blendingshader_ = new ImageShader;
// mask produced by dedicated shader
maskshader_ = new MaskShader;
masksurface_ = new Surface(maskshader_);
// filtered image shader (with texturing and processing) for rendering
processingshader_ = new ImageProcessingShader;
// default rendering with image processing enabled
renderingshader_ = (Shader *) processingshader_;
// for drawing in mixing view
mixingshader_ = new ImageShader;
mixingshader_->stipple = 1.0;
mixinggroup_ = nullptr;
// create media surface:
// - textured with original texture from media player
// - crop & repeat UV can be managed here
// - additional custom shader can be associated
texturesurface_ = new Surface(renderingshader_);
// will be created at init
renderbuffer_ = nullptr;
rendersurface_ = nullptr;
mixingsurface_ = nullptr;
maskbuffer_ = nullptr;
maskimage_ = nullptr;
mask_need_update_ = false;
}
Source::~Source()
{
// inform clones that they lost their origin
for (auto it = clones_.begin(); it != clones_.end(); it++)
(*it)->detach();
clones_.clear();
// delete objects
delete stored_status_;
if (renderbuffer_)
delete renderbuffer_;
if (maskbuffer_)
delete maskbuffer_;
if (maskimage_)
delete maskimage_;
// all groups and their children are deleted in the scene
// this includes rendersurface_, overlays, blendingshader_ and rendershader_
delete groups_[View::RENDERING];
delete groups_[View::MIXING];
delete groups_[View::GEOMETRY];
delete groups_[View::LAYER];
delete groups_[View::TEXTURE];
delete groups_[View::TRANSITION];
groups_.clear();
frames_.clear();
overlays_.clear();
// don't forget that the processing shader
// could be created but not used
if ( renderingshader_ != processingshader_ )
delete processingshader_;
delete texturesurface_;
}
void Source::setName (const std::string &name)
{
name_ = SystemToolkit::transliterate(name);
initials_[0] = std::toupper( name_.front(), std::locale("C") );
initials_[1] = std::toupper( name_.back(), std::locale("C") );
}
void Source::accept(Visitor& v)
{
v.visit(*this);
}
Source::Mode Source::mode() const
{
return mode_;
}
void Source::setMode(Source::Mode m)
{
// make visible on first time
if ( mode_ == Source::UNINITIALIZED ) {
for (auto g = groups_.begin(); g != groups_.end(); g++)
(*g).second->visible_ = true;
}
// choose frame 0 if visible, 1 if selected
uint index_frame = m == Source::VISIBLE ? 0 : 1;
for (auto f = frames_.begin(); f != frames_.end(); f++)
(*f).second->setActive(index_frame);
// show overlay if current
bool current = m >= Source::CURRENT;
for (auto o = overlays_.begin(); o != overlays_.end(); o++)
(*o).second->visible_ = current & !locked_;
// the lock icon
locker_->setActive( locked_ ? 0 : 1);
// the mixing group overlay
overlay_mixinggroup_->visible_ = mixinggroup_!= nullptr && !locked_;
overlay_mixinggroup_->setActive(current);
// show in appearance view if current
groups_[View::TEXTURE]->visible_ = m > Source::VISIBLE;
mode_ = m;
}
void Source::setImageProcessingEnabled (bool on)
{
// avoid repeating
if ( on == imageProcessingEnabled() )
return;
// set pointer
if (on) {
// set the current rendering shader to be the
// (previously prepared) processing shader
renderingshader_ = (Shader *) processingshader_;
}
else {
// clone the current Image processing shader
// (because the one currently attached to the source
// will be deleted in replaceRenderngShader().)
ImageProcessingShader *tmp = new ImageProcessingShader(*processingshader_);
// loose reference to current processing shader (to delete)
// and keep reference to the newly created one
// and keep it for later
processingshader_ = tmp;
// set the current rendering shader to a simple one
renderingshader_ = (Shader *) new ImageShader;
}
// apply to nodes in subclasses
// this calls replaceShader() on the Primitive and
// will delete the previously attached shader
texturesurface_->replaceShader(renderingshader_);
}
void Source::setTextureMirrored (bool on)
{
texturesurface_->setMirrorTexture(on);
}
bool Source::textureMirrored ()
{
return texturesurface_->mirrorTexture();
}
bool Source::imageProcessingEnabled()
{
return ( renderingshader_ == processingshader_ );
}
void Source::render()
{
if (!initialized_)
init();
else {
// render the view into frame buffer
renderbuffer_->begin();
texturesurface_->draw(glm::identity<glm::mat4>(), renderbuffer_->projection());
renderbuffer_->end();
}
}
void Source::attach(FrameBuffer *renderbuffer)
{
// invalid argument
if (renderbuffer == nullptr)
return;
// replace renderbuffer_
if (renderbuffer_)
delete renderbuffer_;
renderbuffer_ = renderbuffer;
// if a symbol is available, add it to overlay
if (symbol_) {
overlays_[View::MIXING]->attach( symbol_ );
overlays_[View::LAYER]->attach( symbol_ );
}
// create the surfaces to draw the frame buffer in the views
rendersurface_ = new FrameBufferSurface(renderbuffer_, blendingshader_);
groups_[View::RENDERING]->attach(rendersurface_);
groups_[View::GEOMETRY]->attach(rendersurface_);
// for mixing and layer views, add another surface to overlay
// (stippled view on top with transparency)
mixingsurface_ = new FrameBufferSurface(renderbuffer_, mixingshader_);
groups_[View::MIXING]->attach(mixingsurface_);
groups_[View::LAYER]->attach(mixingsurface_);
// for views showing a scaled mixing surface, a dedicated transparent surface allows grabbing
Surface *surfacetmp = new Surface();
surfacetmp->setTextureIndex(Resource::getTextureTransparent());
groups_[View::TEXTURE]->attach(surfacetmp);
groups_[View::MIXING]->attach(surfacetmp);
groups_[View::LAYER]->attach(surfacetmp);
// Transition group node is optionnal
if (groups_[View::TRANSITION]->numChildren() > 0)
groups_[View::TRANSITION]->attach(mixingsurface_);
// hack to place the symbols in the corner independently of aspect ratio
symbol_->translation_.x += 0.1f * (renderbuffer_->aspectRatio()-1.f);
// add lock icon to views (displayed on front)
groups_[View::LAYER]->attach( locker_ );
groups_[View::MIXING]->attach( locker_ );
groups_[View::GEOMETRY]->attach( locker_ );
groups_[View::TEXTURE]->attach( locker_ );
// scale all icon nodes to match aspect ratio
for (int v = View::MIXING; v < View::INVALID; v++) {
NodeSet::iterator node;
for (node = groups_[(View::Mode) v]->begin();
node != groups_[(View::Mode) v]->end(); node++) {
(*node)->scale_.x = renderbuffer_->aspectRatio();
}
}
// (re) create the masking buffer
if (maskbuffer_)
delete maskbuffer_;
maskbuffer_ = new FrameBuffer( glm::vec3(0.5) * renderbuffer->resolution() );
// make the source visible
if ( mode_ == UNINITIALIZED )
setMode(VISIBLE);
// request update
need_update_ = true;
}
void Source::setActive (bool on)
{
active_ = on;
// do not disactivate if a clone depends on it
for(auto clone = clones_.begin(); clone != clones_.end(); clone++) {
if ( (*clone)->active() )
active_ = true;
}
// an inactive source is visible only in the MIXING view
groups_[View::RENDERING]->visible_ = active_;
groups_[View::GEOMETRY]->visible_ = active_;
groups_[View::LAYER]->visible_ = active_;
}
void Source::setLocked (bool on)
{
locked_ = on;
setMode(mode_);
}
// Transfer functions from coordinates to alpha (1 - transparency)
// linear distance
float linear_(float x, float y) {
return 1.f - CLAMP( sqrt( ( x * x ) + ( y * y ) ), 0.f, 1.f );
}
// quadratic distance
float quad_(float x, float y) {
return 1.f - CLAMP( ( x * x ) + ( y * y ), 0.f, 1.f );
}
// best alpha transfer function: quadratic sinusoidal shape
float sin_quad_(float x, float y) {
float D = sqrt( ( x * x ) + ( y * y ) );
return 0.5f + 0.5f * cos( M_PI * CLAMP( D * sqrt(D), 0.f, 1.f ) );
// return 0.5f + 0.5f * cos( M_PI * CLAMP( ( ( x * x ) + ( y * y ) ), 0.f, 1.f ) );
}
float Source::depth() const
{
return group(View::RENDERING)->translation_.z;
}
void Source::setDepth(float d)
{
groups_[View::LAYER]->translation_.z = CLAMP(d, MIN_DEPTH, MAX_DEPTH);
touch();
}
float Source::alpha() const
{
return blendingShader()->color.a;
}
void Source::setAlpha(float a)
{
glm::vec2 dist = glm::vec2(groups_[View::MIXING]->translation_);
glm::vec2 step = glm::normalize(glm::vec2(1.f, 1.f));// step in diagonal by default
// step in direction of source translation if possible
if ( glm::length(dist) > DELTA_ALPHA)
step = glm::normalize(dist);
// converge to reduce the difference of alpha
// using dichotomic algorithm
float delta = sin_quad_(dist.x, dist.y) - CLAMP(a, 0.f, 1.f);
while ( glm::abs(delta) > DELTA_ALPHA ){
dist += step * (delta / 2.f);
delta = sin_quad_(dist.x, dist.y) - CLAMP(a, 0.f, 1.f);
}
// apply new mixing coordinates
groups_[View::MIXING]->translation_.x = dist.x;
groups_[View::MIXING]->translation_.y = dist.y;
touch();
}
void Source::update(float dt)
{
// keep delta-t
dt_ = dt;
// update nodes if needed
if (renderbuffer_ && mixingsurface_ && maskbuffer_ && need_update_)
{
// ADJUST alpha based on MIXING node
// read position of the mixing node and interpret this as transparency of render output
glm::vec2 dist = glm::vec2(groups_[View::MIXING]->translation_);
// use the sinusoidal transfer function
blendingshader_->color = glm::vec4(1.f, 1.f, 1.f, sin_quad_( dist.x, dist.y ));
mixingshader_->color = blendingshader_->color;
// CHANGE update status based on limbo
bool a = glm::length(dist) < MIXING_LIMBO_SCALE;
setActive( a );
// adjust scale of mixing icon : smaller if not active
groups_[View::MIXING]->scale_ = glm::vec3(MIXING_ICON_SCALE) - ( a ? glm::vec3(0.f, 0.f, 0.f) : glm::vec3(0.03f, 0.03f, 0.f) );
// MODIFY geometry based on GEOMETRY node
groups_[View::RENDERING]->translation_ = groups_[View::GEOMETRY]->translation_;
groups_[View::RENDERING]->rotation_ = groups_[View::GEOMETRY]->rotation_;
glm::vec3 s = groups_[View::GEOMETRY]->scale_;
// avoid any null scale
s.x = CLAMP_SCALE(s.x);
s.y = CLAMP_SCALE(s.y);
s.z = 1.f;
groups_[View::GEOMETRY]->scale_ = s;
groups_[View::RENDERING]->scale_ = s;
// MODIFY CROP projection based on GEOMETRY crop
renderbuffer_->setProjectionArea( glm::vec2(groups_[View::GEOMETRY]->crop_) );
// Mixing and layer icons scaled based on GEOMETRY crop
mixingsurface_->scale_ = groups_[View::GEOMETRY]->crop_;
mixingsurface_->scale_.x *= renderbuffer_->aspectRatio();
mixingsurface_->update(dt_);
// Layers icons are displayed in Perspective (diagonal)
groups_[View::LAYER]->translation_.x = -groups_[View::LAYER]->translation_.z;
groups_[View::LAYER]->translation_.y = groups_[View::LAYER]->translation_.x / LAYER_PERSPECTIVE;
// Update workspace based on depth, and
// adjust vertical position of icon depending on workspace
if (groups_[View::LAYER]->translation_.x < -LAYER_FOREGROUND) {
groups_[View::LAYER]->translation_.y -= 0.3f;
workspace_ = Source::FOREGROUND;
}
else if (groups_[View::LAYER]->translation_.x < -LAYER_BACKGROUND) {
groups_[View::LAYER]->translation_.y -= 0.15f;
workspace_ = Source::STAGE;
}
else
workspace_ = Source::BACKGROUND;
// MODIFY depth based on LAYER node
groups_[View::MIXING]->translation_.z = groups_[View::LAYER]->translation_.z;
groups_[View::GEOMETRY]->translation_.z = groups_[View::LAYER]->translation_.z;
groups_[View::RENDERING]->translation_.z = groups_[View::LAYER]->translation_.z;
// MODIFY texture projection based on APPEARANCE node
// UV to node coordinates
static glm::mat4 UVtoScene = GlmToolkit::transform(glm::vec3(1.f, -1.f, 0.f),
glm::vec3(0.f, 0.f, 0.f),
glm::vec3(-2.f, 2.f, 1.f));
// Aspect Ratio correction transform : coordinates of Appearance Frame are scaled by render buffer width
glm::mat4 Ar = glm::scale(glm::identity<glm::mat4>(), glm::vec3(renderbuffer_->aspectRatio(), 1.f, 1.f) );
// Translation : same as Appearance Frame (modified by Ar)
glm::mat4 Tra = glm::translate(glm::identity<glm::mat4>(), groups_[View::TEXTURE]->translation_);
// Scaling : inverse scaling (larger UV when smaller Appearance Frame)
glm::vec2 scale = glm::vec2(groups_[View::TEXTURE]->scale_.x,groups_[View::TEXTURE]->scale_.y);
scale = glm::sign(scale) * glm::max( glm::vec2(glm::epsilon<float>()), glm::abs(scale));
glm::mat4 Sca = glm::scale(glm::identity<glm::mat4>(), glm::vec3(scale, 1.f));
// Rotation : same angle than Appearance Frame, inverted axis
glm::mat4 Rot = glm::rotate(glm::identity<glm::mat4>(), groups_[View::TEXTURE]->rotation_.z, glm::vec3(0.f, 0.f, -1.f) );
// Combine transformations (non transitive) in this order:
// 1. switch to Scene coordinate system
// 2. Apply the aspect ratio correction
// 3. Apply the translation
// 4. Apply the rotation (centered after translation)
// 5. Revert aspect ration correction
// 6. Apply the Scaling (independent of aspect ratio)
// 7. switch back to UV coordinate system
texturesurface_->shader()->iTransform = glm::inverse(UVtoScene) * glm::inverse(Sca) * glm::inverse(Ar) * Rot * Tra * Ar * UVtoScene;
// if a mask image was given to be updated
if (mask_need_update_) {
// fill the mask buffer (once)
if (maskbuffer_->fill(maskimage_) )
mask_need_update_ = false;
}
// otherwise, render the mask buffer
else
{
// draw mask in mask frame buffer
maskbuffer_->begin(false);
// loopback maskbuffer texture for painting
masksurface_->setTextureIndex(maskbuffer_->texture());
// fill surface with mask texture
masksurface_->draw(glm::identity<glm::mat4>(), maskbuffer_->projection());
maskbuffer_->end();
}
// set the rendered mask as mask for blending
blendingshader_->mask_texture = maskbuffer_->texture();
// inform mixing group
if (mixinggroup_)
mixinggroup_->setAction(MixingGroup::ACTION_UPDATE);
// do not update next frame
need_update_ = false;
}
}
FrameBuffer *Source::frame() const
{
if (initialized_ && renderbuffer_)
{
return renderbuffer_;
}
else {
static FrameBuffer *black = new FrameBuffer(640,480);
return black;
}
}
bool Source::contains(Node *node) const
{
if ( node == nullptr )
return false;
hasNode tester(node);
return tester(this);
}
void Source::storeMask(FrameBufferImage *img)
{
// free the output mask storage
if (maskimage_ != nullptr) {
delete maskimage_;
maskimage_ = nullptr;
}
// if no image is provided
if (img == nullptr) {
// if ready
if (maskbuffer_!=nullptr) {
// get & store image from mask buffer
maskimage_ = maskbuffer_->image();
}
}
else
// store the given image
maskimage_ = img;
// maskimage_ can now be accessed with Source::getStoredMask
}
void Source::setMask(FrameBufferImage *img)
{
// if a valid image is given
if (img != nullptr && img->width>0 && img->height>0) {
// remember this new image as the current mask
// NB: will be freed when replaced
storeMask(img);
// ask Source::update to use it at next update for filling mask buffer
mask_need_update_ = true;
// ask to update the source
touch();
}
else
mask_need_update_ = false;
}
std::string Source::xml(Source *s)
{
std::string x = "";
tinyxml2::XMLDocument xmlDoc;
tinyxml2::XMLElement *selectionNode = xmlDoc.NewElement(APP_NAME);
selectionNode->SetAttribute("size", 1);
xmlDoc.InsertEndChild(selectionNode);
SessionVisitor sv(&xmlDoc, selectionNode);
s->accept(sv);
// get compact string
tinyxml2::XMLPrinter xmlPrint(0, true);
xmlDoc.Print( &xmlPrint );
x = xmlPrint.CStr();
return x;
}
bool Source::hasNode::operator()(const Source* elem) const
{
if (_n && elem)
{
// quick case (most frequent and easy to answer)
if (elem->rendersurface_ == _n)
return true;
// general case: traverse tree of all Groups recursively using a SearchVisitor
SearchVisitor sv(_n);
// search in groups for all views
for (auto g = elem->groups_.begin(); g != elem->groups_.end(); g++) {
(*g).second->accept(sv);
if (sv.found())
return true;
}
// search in overlays for all views
for (auto g = elem->overlays_.begin(); g != elem->overlays_.end(); g++) {
(*g).second->accept(sv);
if (sv.found())
return true;
}
}
return false;
}
void Source::clearMixingGroup()
{
mixinggroup_ = nullptr;
overlay_mixinggroup_->visible_ = false;
}
CloneSource *Source::clone()
{
CloneSource *s = new CloneSource(this);
clones_.push_back(s);
return s;
}
CloneSource::CloneSource(Source *origin) : Source(), origin_(origin)
{
// set symbol
symbol_ = new Symbol(Symbol::CLONE, glm::vec3(0.75f, 0.75f, 0.01f));
symbol_->scale_.y = 1.5f;
}
CloneSource::~CloneSource()
{
if (origin_)
origin_->clones_.remove(this);
}
CloneSource *CloneSource::clone()
{
// do not clone a clone : clone the original instead
if (origin_)
return origin_->clone();
else
return nullptr;
}
void CloneSource::init()
{
if (origin_ && origin_->ready()) {
// get the texture index from framebuffer of view, apply it to the surface
texturesurface_->setTextureIndex( origin_->texture() );
// create Frame buffer matching size of session
FrameBuffer *renderbuffer = new FrameBuffer( origin_->frame()->resolution(), true);
// set the renderbuffer of the source and attach rendering nodes
attach(renderbuffer);
// deep update to reorder
View::need_deep_update_++;
// done init
initialized_ = true;
Log::Info("Source %s cloning source %s.", name().c_str(), origin_->name().c_str() );
}
}
void CloneSource::setActive (bool on)
{
active_ = on;
groups_[View::RENDERING]->visible_ = active_;
groups_[View::GEOMETRY]->visible_ = active_;
groups_[View::LAYER]->visible_ = active_;
if (initialized_ && origin_ != nullptr)
origin_->touch();
}
uint CloneSource::texture() const
{
if (initialized_ && origin_ != nullptr)
return origin_->texture();
else
return Resource::getTextureBlack();
}
void CloneSource::accept(Visitor& v)
{
Source::accept(v);
if (!failed())
v.visit(*this);
}

View File

@@ -1,120 +0,0 @@
#include <algorithm>
#include <glm/gtx/vector_angle.hpp>
#include <glm/gtx/rotate_vector.hpp>
#include "Source.h"
#include "SourceList.h"
// utility to sort Sources by depth
bool compare_depth (Source * first, Source * second)
{
return ( first->depth() < second->depth() );
}
SourceList depth_sorted(SourceList list)
{
SourceList sl = list;
sl.sort(compare_depth);
return sl;
}
// utility to sort Sources in MixingView in a clockwise order
// in reference to a center point
struct clockwise_centered {
clockwise_centered(glm::vec2 c) : center(c) { }
bool operator() (Source * first, Source * second) {
glm::vec2 pos_first = glm::vec2(first->group(View::MIXING)->translation_)-center;
float angle_first = glm::orientedAngle( glm::normalize(pos_first), glm::vec2(1.f, 0.f) );
glm::vec2 pos_second = glm::vec2(second->group(View::MIXING)->translation_)-center;
float angle_second = glm::orientedAngle( glm::normalize(pos_second), glm::vec2(1.f, 0.f) );
return (angle_first < angle_second);
}
glm::vec2 center;
};
SourceList mixing_sorted(SourceList list, glm::vec2 center)
{
SourceList sl = list;
sl.sort(clockwise_centered(center));
return sl;
}
SourceIdList ids (SourceList list)
{
SourceIdList idlist;
for( auto sit = list.begin(); sit != list.end(); sit++)
idlist.push_back( (*sit)->id() );
// make sure no duplicate
idlist.unique();
return idlist;
}
SourceListCompare compare (SourceList first, SourceList second)
{
SourceListCompare ret = SOURCELIST_DISTINCT;
if (first.empty() || first.empty())
return ret;
// a new test list: start with the second list and remove all commons with first list
SourceList test = second;
for (auto it = first.begin(); it != first.end(); it++){
test.remove(*it);
}
// all sources of the second list were in the first list
if (test.empty()) {
// same size, therefore they are the same!
if (first.size() == second.size())
ret = SOURCELIST_EQUAL;
// otherwise, first list contains all sources of the second list.
else
ret = SOURCELIST_SECOND_IN_FIRST;
}
// some sources of the second list were in the first
else if ( second.size() != test.size() ){
// if the number of sources removed from second is the number of sources in the first
if (second.size() - test.size() == first.size())
ret = SOURCELIST_FIRST_IN_SECOND;
// else, there is a patrial intersection
else
ret = SOURCELIST_INTERSECT;
}
// else no intersection, lists are distinct (return detault)
return ret;
}
SourceList intersect (SourceList first, SourceList second)
{
// take second list and remove all elements also in first list
// -> builds the list of what remains in second list
SourceList l1 = second;
for (auto it = first.begin(); it != first.end(); it++)
l1.remove(*it);
// take second list and remove all elements in the remainer list
// -> builds the list of what is in second list and was part of the first list
SourceList l2 = second;
for (auto it = l1.begin(); it != l1.end(); it++)
l2.remove(*it);
return l2;
}
SourceList join (SourceList first, SourceList second)
{
SourceList l = second;
for (auto it = first.begin(); it != first.end(); it++)
l.push_back(*it);
l.unique();
return l;
}

View File

@@ -1,28 +0,0 @@
#ifndef SOURCELIST_H
#define SOURCELIST_H
#include <list>
#include <glm/glm.hpp>
class Source;
typedef std::list<Source *> SourceList;
SourceList depth_sorted (SourceList list);
SourceList mixing_sorted (SourceList list, glm::vec2 center = glm::vec2(0.f, 0.f));
SourceList intersect (SourceList first, SourceList second);
SourceList join (SourceList first, SourceList second);
typedef enum {
SOURCELIST_DISTINCT = 0,
SOURCELIST_INTERSECT = 1,
SOURCELIST_EQUAL = 2,
SOURCELIST_FIRST_IN_SECOND = 3,
SOURCELIST_SECOND_IN_FIRST = 4
} SourceListCompare;
SourceListCompare compare (SourceList first, SourceList second);
typedef std::list<uint64_t> SourceIdList;
SourceIdList ids (SourceList list);
#endif // SOURCELIST_H

View File

@@ -1,740 +0,0 @@
#include <thread>
using namespace std;
// Desktop OpenGL function loader
#include <glad/glad.h>
// vmix
#include "defines.h"
#include "Log.h"
#include "Resource.h"
#include "Visitor.h"
#include "SystemToolkit.h"
#include "GlmToolkit.h"
#include "Stream.h"
#ifndef NDEBUG
#define STREAM_DEBUG
#endif
#define USE_GST_APPSINK_CALLBACKS_
Stream::Stream()
{
// create unique id
id_ = GlmToolkit::uniqueId();
description_ = "undefined";
pipeline_ = nullptr;
width_ = -1;
height_ = -1;
single_frame_ = false;
live_ = false;
ready_ = false;
failed_ = false;
enabled_ = true;
desired_state_ = GST_STATE_PAUSED;
// start index in frame_ stack
write_index_ = 0;
last_index_ = 0;
// no PBO by default
pbo_[0] = pbo_[1] = 0;
pbo_size_ = 0;
pbo_index_ = 0;
pbo_next_index_ = 0;
// OpenGL texture
textureindex_ = 0;
}
Stream::~Stream()
{
close();
}
void Stream::accept(Visitor& v) {
v.visit(*this);
}
guint Stream::texture() const
{
if (textureindex_ == 0)
return Resource::getTextureBlack();
return textureindex_;
}
void Stream::open(const std::string &gstreamer_description, int w, int h)
{
// set gstreamer pipeline source
description_ = gstreamer_description;
width_ = w;
height_ = h;
// close before re-openning
if (isOpen())
close();
execute_open();
}
std::string Stream::description() const
{
return description_;
}
void Stream::execute_open()
{
// reset
ready_ = false;
// Add custom app sink to the gstreamer pipeline
string description = description_;
description += " ! appsink name=sink";
// parse pipeline descriptor
GError *error = NULL;
pipeline_ = gst_parse_launch (description.c_str(), &error);
if (error != NULL) {
Log::Warning("Stream %s Could not construct pipeline %s:\n%s", std::to_string(id_).c_str(), description.c_str(), error->message);
g_clear_error (&error);
failed_ = true;
return;
}
g_object_set(G_OBJECT(pipeline_), "name", std::to_string(id_).c_str(), NULL);
// GstCaps *caps = gst_static_caps_get (&frame_render_caps);
string capstring = "video/x-raw,format=RGBA,width="+ std::to_string(width_) +
",height=" + std::to_string(height_);
GstCaps *caps = gst_caps_from_string(capstring.c_str());
if (!caps || !gst_video_info_from_caps (&v_frame_video_info_, caps)) {
Log::Warning("Stream %d Could not configure video frame info", id_);
failed_ = true;
return;
}
// setup appsink
GstElement *sink = gst_bin_get_by_name (GST_BIN (pipeline_), "sink");
if (!sink) {
Log::Warning("Stream %s Could not configure sink", std::to_string(id_).c_str());
failed_ = true;
return;
}
// instruct sink to use the required caps
gst_app_sink_set_caps (GST_APP_SINK(sink), caps);
// Instruct appsink to drop old buffers when the maximum amount of queued buffers is reached.
gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 30);
gst_app_sink_set_drop (GST_APP_SINK(sink), true);
#ifdef USE_GST_APPSINK_CALLBACKS_
// set the callbacks
GstAppSinkCallbacks callbacks;
if (single_frame_) {
callbacks.new_preroll = callback_new_preroll;
callbacks.eos = NULL;
callbacks.new_sample = NULL;
Log::Info("Stream %s contains a single frame", std::to_string(id_).c_str());
}
else {
callbacks.new_preroll = callback_new_preroll;
callbacks.eos = callback_end_of_stream;
callbacks.new_sample = callback_new_sample;
}
gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL);
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false);
#else
// connect signals callbacks
g_signal_connect(G_OBJECT(sink), "new-preroll", G_CALLBACK (callback_new_preroll), this);
if (!single_frame_) {
g_signal_connect(G_OBJECT(sink), "new-sample", G_CALLBACK (callback_new_sample), this);
g_signal_connect(G_OBJECT(sink), "eos", G_CALLBACK (callback_end_of_stream), this);
}
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), true);
#endif
// set to desired state (PLAY or PAUSE)
GstStateChangeReturn ret = gst_element_set_state (pipeline_, desired_state_);
if (ret == GST_STATE_CHANGE_FAILURE) {
Log::Warning("Stream %s Could not open '%s'", std::to_string(id_).c_str(), description_.c_str());
failed_ = true;
return;
}
else if (ret == GST_STATE_CHANGE_NO_PREROLL) {
Log::Info("Stream %s is a live stream", std::to_string(id_).c_str());
live_ = true;
}
// instruct the sink to send samples synched in time if not live source
gst_base_sink_set_sync (GST_BASE_SINK(sink), !live_);
// all good
Log::Info("Stream %s Opened '%s' (%d x %d)", std::to_string(id_).c_str(), description.c_str(), width_, height_);
ready_ = true;
// done with refs
gst_object_unref (sink);
gst_caps_unref (caps);
}
bool Stream::isOpen() const
{
return ready_;
}
bool Stream::failed() const
{
return failed_;
}
void Stream::close()
{
// not openned?
if (!ready_) {
// nothing else to change
return;
}
// un-ready
ready_ = false;
// clean up GST
if (pipeline_ != nullptr) {
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_NULL);
if (ret == GST_STATE_CHANGE_ASYNC) {
GstState state;
gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE);
}
gst_object_unref (pipeline_);
pipeline_ = nullptr;
}
desired_state_ = GST_STATE_PAUSED;
// cleanup eventual remaining frame memory
for(guint i = 0; i < N_FRAME; i++){
if ( frame_[i].full ) {
gst_video_frame_unmap(&frame_[i].vframe);
frame_[i].status = INVALID;
}
}
write_index_ = 0;
last_index_ = 0;
// cleanup opengl texture
if (textureindex_)
glDeleteTextures(1, &textureindex_);
textureindex_ = 0;
// cleanup picture buffer
if (pbo_[0])
glDeleteBuffers(2, pbo_);
pbo_size_ = 0;
}
guint Stream::width() const
{
return width_;
}
guint Stream::height() const
{
return height_;
}
float Stream::aspectRatio() const
{
return static_cast<float>(width_) / static_cast<float>(height_);
}
void Stream::enable(bool on)
{
if ( !ready_ || pipeline_ == nullptr)
return;
if ( enabled_ != on ) {
enabled_ = on;
// default to pause
GstState requested_state = GST_STATE_PAUSED;
// unpause only if enabled
if (enabled_) {
requested_state = desired_state_;
}
// apply state change
GstStateChangeReturn ret = gst_element_set_state (pipeline_, requested_state);
if (ret == GST_STATE_CHANGE_FAILURE) {
Log::Warning("Stream %s Failed to enable", std::to_string(id_).c_str());
failed_ = true;
}
}
}
bool Stream::enabled() const
{
return enabled_;
}
bool Stream::singleFrame() const
{
return single_frame_;
}
bool Stream::live() const
{
return live_;
}
void Stream::play(bool on)
{
// ignore if disabled, and cannot play an image
if (!enabled_)
return;
// request state
GstState requested_state = on ? GST_STATE_PLAYING : GST_STATE_PAUSED;
// ignore if requesting twice same state
if (desired_state_ == requested_state)
return;
// accept request to the desired state
desired_state_ = requested_state;
// if not ready yet, the requested state will be handled later
if ( pipeline_ == nullptr )
return;
// all ready, apply state change immediately
GstStateChangeReturn ret = gst_element_set_state (pipeline_, desired_state_);
if (ret == GST_STATE_CHANGE_FAILURE) {
Log::Warning("Stream %s Failed to play", std::to_string(id_).c_str());
failed_ = true;
}
#ifdef STREAM_DEBUG
else if (on)
Log::Info("Stream %s Start", std::to_string(id_).c_str());
else
Log::Info("Stream %s Stop", std::to_string(id_).c_str());
#endif
// activate live-source
if (live_)
gst_element_get_state (pipeline_, NULL, NULL, GST_CLOCK_TIME_NONE);
// reset time counter
timecount_.reset();
}
bool Stream::isPlaying(bool testpipeline) const
{
// if not ready yet, answer with requested state
if ( !testpipeline || pipeline_ == nullptr || !enabled_)
return desired_state_ == GST_STATE_PLAYING;
// if ready, answer with actual state
GstState state;
gst_element_get_state (pipeline_, &state, NULL, GST_CLOCK_TIME_NONE);
return state == GST_STATE_PLAYING;
}
void Stream::init_texture(guint index)
{
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &textureindex_);
glBindTexture(GL_TEXTURE_2D, textureindex_);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width_, height_);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_,
GL_RGBA, GL_UNSIGNED_BYTE, frame_[index].vframe.data[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// set pbo image size
pbo_size_ = height_ * width_ * 4;
// create pixel buffer objects,
if (pbo_[0])
glDeleteBuffers(2, pbo_);
glGenBuffers(2, pbo_);
for(int i = 0; i < 2; i++ ) {
// create 2 PBOs
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[i]);
// glBufferDataARB with NULL pointer reserves only memory space.
glBufferData(GL_PIXEL_UNPACK_BUFFER, pbo_size_, 0, GL_STREAM_DRAW);
// fill in with reset picture
GLubyte* ptr = (GLubyte*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
if (ptr) {
// update data directly on the mapped buffer
memmove(ptr, frame_[index].vframe.data[0], pbo_size_);
// release pointer to mapping buffer
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
}
else {
// did not work, disable PBO
glDeleteBuffers(2, pbo_);
pbo_[0] = pbo_[1] = 0;
pbo_size_ = 0;
break;
}
}
// should be good to go, wrap it up
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
pbo_index_ = 0;
pbo_next_index_ = 1;
#ifdef STREAM_DEBUG
Log::Info("Stream %s Use Pixel Buffer Object texturing.", std::to_string(id_).c_str());
#endif
}
void Stream::fill_texture(guint index)
{
// is this the first frame ?
if (textureindex_ < 1)
{
// initialize texture
init_texture(index);
}
else {
glBindTexture(GL_TEXTURE_2D, textureindex_);
// use dual Pixel Buffer Object
if (pbo_size_ > 0) {
// In dual PBO mode, increment current index first then get the next index
pbo_index_ = (pbo_index_ + 1) % 2;
pbo_next_index_ = (pbo_index_ + 1) % 2;
// bind PBO to read pixels
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[pbo_index_]);
// copy pixels from PBO to texture object
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, 0);
// bind the next PBO to write pixels
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo_[pbo_next_index_]);
// See http://www.songho.ca/opengl/gl_pbo.html#map for more details
glBufferData(GL_PIXEL_UNPACK_BUFFER, pbo_size_, 0, GL_STREAM_DRAW);
// map the buffer object into client's memory
GLubyte* ptr = (GLubyte*) glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
if (ptr) {
// update data directly on the mapped buffer
// NB : equivalent but faster (memmove instead of memcpy ?) than
// glNamedBufferSubData(pboIds[nextIndex], 0, imgsize, vp->getBuffer())
memmove(ptr, frame_[index].vframe.data[0], pbo_size_);
// release pointer to mapping buffer
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
}
// done with PBO
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
else {
// without PBO, use standard opengl (slower)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width_, height_,
GL_RGBA, GL_UNSIGNED_BYTE, frame_[index].vframe.data[0]);
}
}
}
void Stream::update()
{
// discard
if (failed_)
return;
// not ready yet
if (!ready_)
return;
// // prevent unnecessary updates: disabled or already filled image
// if (!enabled_)
// return;
// local variables before trying to update
guint read_index = 0;
bool need_loop = false;
// locked access to current index
index_lock_.lock();
// get the last frame filled from fill_frame()
read_index = last_index_;
// Do NOT miss and jump directly to a pre-roll
for (guint i = 0; i < N_FRAME; ++i) {
if (frame_[i].status == PREROLL) {
read_index = i;
break;
}
}
// unlock access to index change
index_lock_.unlock();
// lock frame while reading it
frame_[read_index].access.lock();
// do not fill a frame twice
if (frame_[read_index].status != INVALID ) {
// is this an End-of-Stream frame ?
if (frame_[read_index].status == EOS )
{
// will execute seek command below (after unlock)
need_loop = true;
}
// otherwise just fill non-empty SAMPLE or PREROLL
else if (frame_[read_index].full)
{
// fill the texture with the frame at reading index
fill_texture(read_index);
// double update for pre-roll frame and dual PBO (ensure frame is displayed now)
if (frame_[read_index].status == PREROLL && pbo_size_ > 0)
fill_texture(read_index);
}
// avoid reading it again
frame_[read_index].status = INVALID;
}
// unkock frame after reading it
frame_[read_index].access.unlock();
if (need_loop) {
// stop on end of stream
play(false);
}
}
double Stream::updateFrameRate() const
{
return timecount_.frameRate();
}
// CALLBACKS
bool Stream::fill_frame(GstBuffer *buf, FrameStatus status)
{
// Log::Info("Stream fill frame");
// Do NOT overwrite an unread EOS
if ( frame_[write_index_].status == EOS )
write_index_ = (write_index_ + 1) % N_FRAME;
// lock access to frame
frame_[write_index_].access.lock();
// always empty frame before filling it again
if ( frame_[write_index_].full ) {
if ( GST_MINI_OBJECT_REFCOUNT_VALUE( &frame_[write_index_].vframe.buffer->mini_object ) > 0)
gst_video_frame_unmap(&frame_[write_index_].vframe);
frame_[write_index_].full = false;
}
// accept status of frame received
frame_[write_index_].status = status;
// a buffer is given (not EOS)
if (buf != NULL) {
// get the frame from buffer
if ( !gst_video_frame_map (&frame_[write_index_].vframe, &v_frame_video_info_, buf, GST_MAP_READ ) )
{
Log::Info("Stream %s Failed to map the video buffer", std::to_string(id_).c_str());
// free access to frame & exit
frame_[write_index_].status = INVALID;
frame_[write_index_].access.unlock();
return false;
}
// successfully filled the frame
frame_[write_index_].full = true;
// validate frame format
if( GST_VIDEO_INFO_IS_RGB(&(frame_[write_index_].vframe).info) && GST_VIDEO_INFO_N_PLANES(&(frame_[write_index_].vframe).info) == 1)
{
// set presentation time stamp
frame_[write_index_].position = buf->pts;
}
// full but invalid frame : will be deleted next iteration
// (should never happen)
else {
Log::Info("Stream %s Received an Invalid frame", std::to_string(id_).c_str());
frame_[write_index_].status = INVALID;
frame_[write_index_].access.unlock();
return false;
}
}
// else; null buffer for EOS: give a position
else {
frame_[write_index_].status = EOS;
#ifdef STREAM_DEBUG
Log::Info("Stream %s Reached End Of Stream", std::to_string(id_).c_str());
#endif
}
// unlock access to frame
frame_[write_index_].access.unlock();
// lock access to change current index (very quick)
index_lock_.lock();
// indicate update() that this is the last frame filled (and unlocked)
last_index_ = write_index_;
// unlock access to index change
index_lock_.unlock();
// for writing, we will access the next in stack
write_index_ = (write_index_ + 1) % N_FRAME;
// calculate actual FPS of update
timecount_.tic();
return true;
}
void Stream::callback_end_of_stream (GstAppSink *, gpointer p)
{
Stream *m = (Stream *)p;
if (m && m->ready_) {
m->fill_frame(NULL, Stream::EOS);
}
}
GstFlowReturn Stream::callback_new_preroll (GstAppSink *sink, gpointer p)
{
GstFlowReturn ret = GST_FLOW_OK;
// blocking read pre-roll samples
GstSample *sample = gst_app_sink_pull_preroll(sink);
// if got a valid sample
if (sample != NULL) {
// send frames to media player only if ready
Stream *m = (Stream *)p;
if (m && m->ready_) {
// get buffer from sample
GstBuffer *buf = gst_sample_get_buffer (sample);
// fill frame from buffer
if ( !m->fill_frame(buf, Stream::PREROLL) )
ret = GST_FLOW_ERROR;
}
}
else
ret = GST_FLOW_FLUSHING;
// release sample
gst_sample_unref (sample);
return ret;
}
GstFlowReturn Stream::callback_new_sample (GstAppSink *sink, gpointer p)
{
GstFlowReturn ret = GST_FLOW_OK;
// if (gst_app_sink_is_eos (sink))
// Log::Info("callback_new_sample got EOS");
// non-blocking read new sample
GstSample *sample = gst_app_sink_pull_sample(sink);
// if got a valid sample
if (sample != NULL && !gst_app_sink_is_eos (sink)) {
// send frames to media player only if ready
Stream *m = (Stream *)p;
if (m && m->ready_) {
// get buffer from sample (valid until sample is released)
GstBuffer *buf = gst_sample_get_buffer (sample) ;
// fill frame with buffer
if ( !m->fill_frame(buf, Stream::SAMPLE) )
ret = GST_FLOW_ERROR;
}
}
else
ret = GST_FLOW_FLUSHING;
// release sample
gst_sample_unref (sample);
return ret;
}
Stream::TimeCounter::TimeCounter() {
reset();
}
void Stream::TimeCounter::tic ()
{
// how long since last time
GstClockTime t = gst_util_get_timestamp ();
GstClockTime dt = t - last_time;
// one more frame since last time
nbFrames++;
// calculate instantaneous framerate
// Exponential moving averate with previous framerate to filter jitter (50/50)
// The divition of frame/time is done on long integer GstClockTime, counting in microsecond
// NB: factor 100 to get 0.01 precision
fps = 0.5 * fps + 0.005 * static_cast<double>( ( 100 * GST_SECOND * nbFrames ) / dt );
// reset counter every second
if ( dt >= GST_SECOND)
{
last_time = t;
nbFrames = 0;
}
}
GstClockTime Stream::TimeCounter::dt ()
{
GstClockTime t = gst_util_get_timestamp ();
GstClockTime dt = t - tic_time;
tic_time = t;
// return the instantaneous delta t
return dt;
}
void Stream::TimeCounter::reset ()
{
last_time = gst_util_get_timestamp ();;
tic_time = last_time;
nbFrames = 0;
fps = 0.0;
}
double Stream::TimeCounter::frameRate() const
{
return fps;
}

View File

@@ -1,118 +0,0 @@
#include <sstream>
#include <glm/gtc/matrix_transform.hpp>
#include "StreamSource.h"
#include "defines.h"
#include "ImageShader.h"
#include "Resource.h"
#include "Decorations.h"
#include "Stream.h"
#include "Visitor.h"
#include "Log.h"
GenericStreamSource::GenericStreamSource() : StreamSource()
{
// create stream
stream_ = new Stream;
// set symbol
symbol_ = new Symbol(Symbol::EMPTY, glm::vec3(0.75f, 0.75f, 0.01f));
symbol_->scale_.y = 1.5f;
}
void GenericStreamSource::setDescription(const std::string &desc)
{
Log::Notify("Creating Source with Stream description '%s'", desc.c_str());
stream_->open(desc);
stream_->play(true);
}
void GenericStreamSource::accept(Visitor& v)
{
Source::accept(v);
if (!failed())
v.visit(*this);
}
StreamSource::StreamSource() : Source(), stream_(nullptr)
{
}
StreamSource::~StreamSource()
{
// delete stream
if (stream_)
delete stream_;
}
bool StreamSource::failed() const
{
return (stream_ != nullptr && stream_->failed() );
}
uint StreamSource::texture() const
{
if (stream_ == nullptr)
return Resource::getTextureBlack();
else
return stream_->texture();
}
void StreamSource::init()
{
if ( stream_ && stream_->isOpen() ) {
// update video
stream_->update();
// once the texture of media player is created
if (stream_->texture() != Resource::getTextureBlack()) {
// get the texture index from media player, apply it to the media surface
texturesurface_->setTextureIndex( stream_->texture() );
// create Frame buffer matching size of media player
float height = float(stream_->width()) / stream_->aspectRatio();
FrameBuffer *renderbuffer = new FrameBuffer(stream_->width(), (uint)height, true);
// set the renderbuffer of the source and attach rendering nodes
attach(renderbuffer);
// deep update to reorder
View::need_deep_update_++;
// force update of activation mode
active_ = true;
// done init
initialized_ = true;
Log::Info("Source '%s' linked to Stream %s", name().c_str(), std::to_string(stream_->id()).c_str());
}
}
}
void StreamSource::setActive (bool on)
{
bool was_active = active_;
Source::setActive(on);
// change status of media player (only if status changed)
if ( active_ != was_active ) {
if (stream_)
stream_->enable(active_);
}
}
void StreamSource::update(float dt)
{
Source::update(dt);
// update stream
if (stream_)
stream_->update();
}

View File

@@ -1,404 +0,0 @@
#include <thread>
#include <sstream>
// Desktop OpenGL function loader
#include <glad/glad.h>
// standalone image loader
#include <stb_image.h>
#include <stb_image_write.h>
// gstreamer
#include <gst/gstformat.h>
#include <gst/video/video.h>
//osc
#include "osc/OscOutboundPacketStream.h"
#include "Settings.h"
#include "GstToolkit.h"
#include "defines.h"
#include "SystemToolkit.h"
#include "Session.h"
#include "FrameBuffer.h"
#include "Log.h"
#include "Connection.h"
#include "NetworkToolkit.h"
#include "Streamer.h"
#include <iostream>
#include <cstring>
#ifndef NDEBUG
#define STREAMER_DEBUG
#endif
void StreamingRequestListener::ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint )
{
char sender[IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH];
remoteEndpoint.AddressAndPortAsString(sender);
try{
if( std::strcmp( m.AddressPattern(), OSC_PREFIX OSC_STREAM_REQUEST) == 0 ){
#ifdef STREAMER_DEBUG
Log::Info("%s wants a stream.", sender);
#endif
osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
int reply_to_port = (arg++)->AsInt32();
const char *client_name = (arg++)->AsString();
if (Streaming::manager().enabled())
Streaming::manager().addStream(sender, reply_to_port, client_name);
else
Streaming::manager().refuseStream(sender, reply_to_port);
}
else if( std::strcmp( m.AddressPattern(), OSC_PREFIX OSC_STREAM_DISCONNECT) == 0 ){
#ifdef STREAMER_DEBUG
Log::Info("%s does not need streaming anymore.", sender);
#endif
osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
int port = (arg++)->AsInt32();
Streaming::manager().removeStream(sender, port);
}
}
catch( osc::Exception& e ){
// any parsing errors such as unexpected argument types, or
// missing arguments get thrown as exceptions.
Log::Info("error while parsing message '%s' from %s : %s", m.AddressPattern(), sender, e.what());
}
}
void wait_for_request_(UdpListeningReceiveSocket *receiver)
{
receiver->Run();
}
Streaming::Streaming() : enabled_(false)
{
int port = Connection::manager().info().port_stream_request;
receiver_ = new UdpListeningReceiveSocket(IpEndpointName( IpEndpointName::ANY_ADDRESS, port ), &listener_ );
std::thread(wait_for_request_, receiver_).detach();
}
Streaming::~Streaming()
{
if (receiver_!=nullptr) {
receiver_->Break();
delete receiver_;
}
}
bool Streaming::busy()
{
bool b = false;
streamers_lock_.lock();
std::vector<VideoStreamer *>::const_iterator sit = streamers_.begin();
for (; sit != streamers_.end() && !b; sit++)
b = (*sit)->busy() ;
streamers_lock_.unlock();
return b;
}
std::vector<std::string> Streaming::listStreams()
{
std::vector<std::string> ls;
streamers_lock_.lock();
std::vector<VideoStreamer *>::const_iterator sit = streamers_.begin();
for (; sit != streamers_.end(); sit++)
ls.push_back( (*sit)->info() );
streamers_lock_.unlock();
return ls;
}
void Streaming::enable(bool on)
{
if (on) {
// accept streaming requests
enabled_ = true;
Log::Info("Accepting stream requests to %s.", Connection::manager().info().name.c_str());
}
else {
// refuse streaming requests
enabled_ = false;
// ending and removing all streaming
streamers_lock_.lock();
for (auto sit = streamers_.begin(); sit != streamers_.end(); sit=streamers_.erase(sit))
(*sit)->stop();
streamers_lock_.unlock();
Log::Info("Refusing stream requests to %s. No streaming ongoing.", Connection::manager().info().name.c_str());
}
}
void Streaming::removeStream(const std::string &sender, int port)
{
// get ip of sender
std::string sender_ip = sender.substr(0, sender.find_last_of(":"));
// parse the list for a streamers matching IP and port
streamers_lock_.lock();
std::vector<VideoStreamer *>::const_iterator sit = streamers_.begin();
for (; sit != streamers_.end(); sit++){
NetworkToolkit::StreamConfig config = (*sit)->config_;
if (config.client_address.compare(sender_ip) == 0 && config.port == port ) {
#ifdef STREAMER_DEBUG
Log::Info("Ending streaming to %s:%d", config.client_address.c_str(), config.port);
#endif
// match: stop this streamer
(*sit)->stop();
// remove from list
streamers_.erase(sit);
break;
}
}
streamers_lock_.unlock();
}
void Streaming::removeStreams(const std::string &clientname)
{
// remove all streamers matching given IP
streamers_lock_.lock();
std::vector<VideoStreamer *>::const_iterator sit = streamers_.begin();
while ( sit != streamers_.end() ){
NetworkToolkit::StreamConfig config = (*sit)->config_;
if (config.client_name.compare(clientname) == 0) {
#ifdef STREAMER_DEBUG
Log::Info("Ending streaming to %s:%d", config.client_address.c_str(), config.port);
#endif
// match: stop this streamer
(*sit)->stop();
// remove from list
sit = streamers_.erase(sit);
}
else
sit++;
}
streamers_lock_.unlock();
}
void Streaming::refuseStream(const std::string &sender, int reply_to)
{
// get ip of client
std::string sender_ip = sender.substr(0, sender.find_last_of(":"));
// prepare to reply to client
IpEndpointName host( sender_ip.c_str(), reply_to );
UdpTransmitSocket socket( host );
// build OSC message
char buffer[IP_MTU_SIZE];
osc::OutboundPacketStream p( buffer, IP_MTU_SIZE );
p.Clear();
p << osc::BeginMessage( OSC_PREFIX OSC_STREAM_REJECT );
p << osc::EndMessage;
// send OSC message to client
socket.Send( p.Data(), p.Size() );
// inform user
Log::Warning("A connection request for streaming came and was rejected.\nYou can Accept connections from the Output window.");
}
void Streaming::addStream(const std::string &sender, int reply_to, const std::string &clientname)
{
// get ip of client
std::string sender_ip = sender.substr(0, sender.find_last_of(":"));
// get port used to send the request
std::string sender_port = sender.substr(sender.find_last_of(":") + 1);
// prepare to reply to client
IpEndpointName host( sender_ip.c_str(), reply_to );
UdpTransmitSocket socket( host );
// prepare an offer
NetworkToolkit::StreamConfig conf;
conf.client_address = sender_ip;
conf.client_name = clientname;
conf.port = std::stoi(sender_port); // this port seems free, so re-use it!
conf.width = FrameGrabbing::manager().width();
conf.height = FrameGrabbing::manager().height();
// TEMP DISABLED : TODO Fix snap to allow system wide shared access
// offer SHM if same IP that our host IP (i.e. on the same machine)
// if( NetworkToolkit::is_host_ip(conf.client_address) )
// conf.protocol = NetworkToolkit::SHM_RAW;
// // any other IP : offer network streaming
// else
conf.protocol = NetworkToolkit::UDP_JPEG;
// build OSC message
char buffer[IP_MTU_SIZE];
osc::OutboundPacketStream p( buffer, IP_MTU_SIZE );
p.Clear();
p << osc::BeginMessage( OSC_PREFIX OSC_STREAM_OFFER );
p << conf.port;
p << (int) conf.protocol;
p << conf.width << conf.height;
p << osc::EndMessage;
// send OSC message to client
socket.Send( p.Data(), p.Size() );
#ifdef STREAMER_DEBUG
Log::Info("Replying to %s:%d", sender_ip.c_str(), reply_to);
Log::Info("Starting streaming to %s:%d", sender_ip.c_str(), conf.port);
#endif
// create streamer & remember it
VideoStreamer *streamer = new VideoStreamer(conf);
streamers_lock_.lock();
streamers_.push_back(streamer);
streamers_lock_.unlock();
// start streamer
FrameGrabbing::manager().add(streamer);
}
VideoStreamer::VideoStreamer(NetworkToolkit::StreamConfig conf): FrameGrabber(), config_(conf)
{
}
void VideoStreamer::init(GstCaps *caps)
{
// ignore
if (caps == nullptr)
return;
// check that config matches the given buffer properties
gint w = 0, h = 0;
GstStructure *capstruct = gst_caps_get_structure (caps, 0);
if ( gst_structure_has_field (capstruct, "width"))
gst_structure_get_int (capstruct, "width", &w);
if ( gst_structure_has_field (capstruct, "height"))
gst_structure_get_int (capstruct, "height", &h);
if ( config_.width != w || config_.height != h) {
Log::Warning("Streaming cannot start: given frames (%d x %d) incompatible with stream (%d x %d)",
w, w, config_.width, config_.height);
finished_ = true;
return;
}
// prevent eroneous protocol values
if (config_.protocol < 0 || config_.protocol >= NetworkToolkit::DEFAULT)
config_.protocol = NetworkToolkit::UDP_JPEG;
// create a gstreamer pipeline
std::string description = "appsrc name=src ! videoconvert ! ";
description += NetworkToolkit::protocol_send_pipeline[config_.protocol];
// parse pipeline descriptor
GError *error = NULL;
pipeline_ = gst_parse_launch (description.c_str(), &error);
if (error != NULL) {
Log::Warning("VideoStreamer Could not construct pipeline %s:\n%s", description.c_str(), error->message);
g_clear_error (&error);
finished_ = true;
return;
}
// setup streaming sink
if (config_.protocol == NetworkToolkit::UDP_JPEG || config_.protocol == NetworkToolkit::UDP_H264) {
g_object_set (G_OBJECT (gst_bin_get_by_name (GST_BIN (pipeline_), "sink")),
"host", config_.client_address.c_str(),
"port", config_.port, NULL);
}
else if (config_.protocol == NetworkToolkit::SHM_RAW) {
std::string path = SystemToolkit::full_filename(SystemToolkit::temp_path(), "shm");
path += std::to_string(config_.port);
g_object_set (G_OBJECT (gst_bin_get_by_name (GST_BIN (pipeline_), "sink")),
"socket-path", path.c_str(), NULL);
}
// setup custom app source
src_ = GST_APP_SRC( gst_bin_get_by_name (GST_BIN (pipeline_), "src") );
if (src_) {
g_object_set (G_OBJECT (src_),
"stream-type", GST_APP_STREAM_TYPE_STREAM,
"is-live", TRUE,
"format", GST_FORMAT_TIME,
// "do-timestamp", TRUE,
NULL);
// Direct encoding (no buffering)
gst_app_src_set_max_bytes( src_, 0 );
// instruct src to use the required caps
caps_ = gst_caps_copy( caps );
gst_app_src_set_caps (src_, caps_);
// setup callbacks
GstAppSrcCallbacks callbacks;
callbacks.need_data = FrameGrabber::callback_need_data;
callbacks.enough_data = FrameGrabber::callback_enough_data;
callbacks.seek_data = NULL; // stream type is not seekable
gst_app_src_set_callbacks (src_, &callbacks, this, NULL);
}
else {
Log::Warning("VideoStreamer Could not configure capture source");
finished_ = true;
return;
}
// start recording
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
Log::Warning("VideoStreamer failed");
finished_ = true;
return;
}
// all good
Log::Notify("Streaming to %s.", config_.client_name.c_str());
// start streaming !!
active_ = true;
}
void VideoStreamer::terminate()
{
// send EOS
gst_app_src_end_of_stream (src_);
// make sure the shared memory socket is deleted
if (config_.protocol == NetworkToolkit::SHM_RAW) {
std::string path = SystemToolkit::full_filename(SystemToolkit::temp_path(), "shm");
path += std::to_string(config_.port);
SystemToolkit::remove_file(path);
}
Log::Notify("Streaming to %s finished after %s s.", config_.client_name.c_str(),
GstToolkit::time_to_string(timestamp_).c_str());
}
void VideoStreamer::stop ()
{
// stop recording
FrameGrabber::stop ();
// force finished
finished_ = true;
}
std::string VideoStreamer::info() const
{
std::ostringstream ret;
if (active_) {
ret << NetworkToolkit::protocol_name[config_.protocol];
ret << " to ";
ret << config_.client_name;
}
else
ret << "Streaming terminated.";
return ret.str();
}

View File

@@ -1,85 +0,0 @@
#ifndef STREAMER_H
#define STREAMER_H
#include <mutex>
#include <gst/pbutils/pbutils.h>
#include <gst/app/gstappsrc.h>
#include "osc/OscReceivedElements.h"
#include "osc/OscPacketListener.h"
#include "ip/UdpSocket.h"
#include "NetworkToolkit.h"
#include "FrameGrabber.h"
class Session;
class VideoStreamer;
class StreamingRequestListener : public osc::OscPacketListener {
protected:
virtual void ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint );
};
class Streaming
{
friend class StreamingRequestListener;
// Private Constructor
Streaming();
Streaming(Streaming const& copy); // Not Implemented
Streaming& operator=(Streaming const& copy); // Not Implemented
public:
static Streaming& manager()
{
// The only instance
static Streaming _instance;
return _instance;
}
~Streaming();
void enable(bool on);
inline bool enabled() const { return enabled_; }
void removeStreams(const std::string &clientname);
void removeStream(const std::string &sender, int port);
bool busy();
std::vector<std::string> listStreams();
protected:
void addStream(const std::string &sender, int reply_to, const std::string &clientname);
void refuseStream(const std::string &sender, int reply_to);
private:
bool enabled_;
StreamingRequestListener listener_;
UdpListeningReceiveSocket *receiver_;
std::vector<VideoStreamer *> streamers_;
std::mutex streamers_lock_;
};
class VideoStreamer : public FrameGrabber
{
friend class Streaming;
void init(GstCaps *caps) override;
void terminate() override;
void stop() override;
// connection information
NetworkToolkit::StreamConfig config_;
public:
VideoStreamer(NetworkToolkit::StreamConfig conf);
std::string info() const override;
};
#endif // STREAMER_H

View File

@@ -1,399 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sstream>
#include <fstream>
#include <iomanip>
#include <ctime>
#include <chrono>
#include <locale>
#include <unicode/ustream.h>
#include <unicode/translit.h>
using namespace std;
#ifdef WIN32
#include <windows.h>
#define mkdir(dir, mode) _mkdir(dir)
#include <include/dirent.h>
#include <sys/resource.h>
#define PATH_SEP '\\'
#define PATH_SETTINGS "\\\AppData\\Roaming\\"
#elif defined(LINUX) or defined(APPLE)
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <dirent.h>
#define PATH_SEP '/'
#endif
#if defined(APPLE)
#define PATH_SETTINGS "/Library/Application Support/"
#include <mach/task.h>
#include <mach/mach_init.h>
#elif defined(LINUX)
#include <sys/sysinfo.h>
#define PATH_SETTINGS "/.config/"
#endif
#include "defines.h"
#include "SystemToolkit.h"
/// The amount of memory currently being used by this process, in bytes.
/// it will try to report the resident set in RAM
long SystemToolkit::memory_usage()
{
#if defined(LINUX)
// Grabbing info directly from the /proc pseudo-filesystem. Reading from
// /proc/self/statm gives info on your own process, as one line of
// numbers that are: virtual mem program size, resident set size,
// shared pages, text/code, data/stack, library, dirty pages. The
// mem sizes should all be multiplied by the page size.
size_t size = 0;
FILE *file = fopen("/proc/self/statm", "r");
if (file) {
unsigned long m = 0;
int ret = 0;
ret = fscanf (file, "%lu", &m); // virtual mem program size,
ret = fscanf (file, "%lu", &m); // resident set size,
fclose (file);
if (ret>0)
size = (size_t)m * getpagesize();
}
return (long)size;
#elif defined(APPLE)
// Inspired from
// http://miknight.blogspot.com/2005/11/resident-set-size-in-mac-os-x.html
struct task_basic_info t_info;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
task_info(current_task(), TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
return t_info.resident_size;
#elif defined(WIN32)
// According to MSDN...
PROCESS_MEMORY_COUNTERS counters;
if (GetProcessMemoryInfo (GetCurrentProcess(), &counters, sizeof (counters)))
return counters.PagefileUsage;
else return 0;
#else
return 0;
#endif
}
long SystemToolkit::memory_max_usage() {
struct rusage r_usage;
getrusage(RUSAGE_SELF,&r_usage);
return r_usage.ru_maxrss;
// return r_usage.ru_isrss;
}
string SystemToolkit::byte_to_string(long b)
{
double numbytes = static_cast<double>(b);
ostringstream oss;
std::list<std::string> list = {" Bytes", " KB", " MB", " GB", " TB"};
std::list<std::string>::iterator i = list.begin();
while(numbytes >= 1024.0 && i != list.end())
{
i++;
numbytes /= 1024.0;
}
oss << std::fixed << std::setprecision(2) << numbytes << *i;
return oss.str();
}
string SystemToolkit::bits_to_string(long b)
{
double numbytes = static_cast<double>(b);
ostringstream oss;
std::list<std::string> list = {" bit", " Kbit", " Mbit", " Gbit", " Tbit"};
std::list<std::string>::iterator i = list.begin();
while(numbytes >= 1000.0 && i != list.end())
{
i++;
numbytes /= 1000.0;
}
oss << std::fixed << std::setprecision(2) << numbytes << *i;
return oss.str();
}
string SystemToolkit::date_time_string()
{
chrono::system_clock::time_point now = chrono::system_clock::now();
time_t t = chrono::system_clock::to_time_t(now);
tm* datetime = localtime(&t);
auto duration = now.time_since_epoch();
auto millis = chrono::duration_cast<chrono::milliseconds>(duration).count() % 1000;
ostringstream oss;
oss << setw(4) << setfill('0') << to_string(datetime->tm_year + 1900);
oss << setw(2) << setfill('0') << to_string(datetime->tm_mon + 1);
oss << setw(2) << setfill('0') << to_string(datetime->tm_mday );
oss << setw(2) << setfill('0') << to_string(datetime->tm_hour );
oss << setw(2) << setfill('0') << to_string(datetime->tm_min );
oss << setw(2) << setfill('0') << to_string(datetime->tm_sec );
oss << setw(3) << setfill('0') << to_string(millis);
// fixed length string (17 chars) YYYYMMDDHHmmssiii
return oss.str();
}
string SystemToolkit::filename(const string& path)
{
return path.substr(path.find_last_of(PATH_SEP) + 1);
}
string SystemToolkit::base_filename(const string& path)
{
string basefilename = SystemToolkit::filename(path);
const size_t period_idx = basefilename.rfind('.');
if (string::npos != period_idx)
{
basefilename.erase(period_idx);
}
return basefilename;
}
string SystemToolkit::path_filename(const string& path)
{
return path.substr(0, path.find_last_of(PATH_SEP) + 1);
}
string SystemToolkit::trunc_filename(const string& path, int lenght)
{
string trunc = path;
int l = path.size();
if ( l > lenght ) {
trunc = string("...") + path.substr( l - lenght + 3 );
}
return trunc;
}
string SystemToolkit::extension_filename(const string& filename)
{
string ext = filename.substr(filename.find_last_of(".") + 1);
return ext;
}
std::string SystemToolkit::home_path()
{
// 1. find home
char *mHomePath;
// try the system user info
// NB: avoids depending on changes of the $HOME env. variable
struct passwd* pwd = getpwuid(getuid());
if (pwd)
mHomePath = pwd->pw_dir;
else
// try the $HOME environment variable
mHomePath = getenv("HOME");
return string(mHomePath) + PATH_SEP;
}
std::string SystemToolkit::cwd_path()
{
char mCwdPath[PATH_MAX];
if (getcwd(mCwdPath, sizeof(mCwdPath)) != NULL)
return string(mCwdPath) + PATH_SEP;
else
return string();
}
std::string SystemToolkit::username()
{
// 1. find home
char *user;
// try the system user info
struct passwd* pwd = getpwuid(getuid());
if (pwd)
user = pwd->pw_name;
else
// try the $USER environment variable
user = getenv("USER");
return string(user);
}
bool SystemToolkit::create_directory(const string& path)
{
return !mkdir(path.c_str(), 0755) || errno == EEXIST;
// TODO : verify WIN32 implementation
}
bool SystemToolkit::remove_file(const string& path)
{
bool ret = true;
if (file_exists(path)) {
ret = (remove(path.c_str()) == 0);
}
return ret;
// TODO : verify WIN32 implementation
}
string SystemToolkit::settings_path()
{
// start from home folder
// NB: use the env.variable $HOME to allow system to specify
// another directory (e.g. inside a snap)
string home(getenv("HOME"));
// 2. try to access user settings folder
string settingspath = home + PATH_SETTINGS;
if (SystemToolkit::file_exists(settingspath)) {
// good, we have a place to put the settings file
// settings should be in 'vimix' subfolder
settingspath += APP_NAME;
// 3. create the vmix subfolder in settings folder if not existing already
if ( !SystemToolkit::file_exists(settingspath)) {
if ( !create_directory(settingspath) )
// fallback to home if settings path cannot be created
settingspath = home;
}
return settingspath;
}
else {
// fallback to home if settings path does not exists
return home;
}
}
string SystemToolkit::temp_path()
{
string temp;
const char *tmpdir = getenv("TMPDIR");
if (tmpdir)
temp = std::string(tmpdir);
else
temp = std::string( P_tmpdir );
temp += PATH_SEP;
return temp;
// TODO : verify WIN32 implementation
}
string SystemToolkit::full_filename(const std::string& path, const string &filename)
{
string fullfilename = path;
fullfilename += PATH_SEP;
fullfilename += filename;
return fullfilename;
}
bool SystemToolkit::file_exists(const string& path)
{
if (path.empty())
return false;
return access(path.c_str(), R_OK) == 0;
// TODO : WIN32 implementation
}
// tests if dir is a directory and return its path, empty string otherwise
std::string SystemToolkit::path_directory(const std::string& path)
{
string directorypath = "";
DIR *dir;
if ((dir = opendir (path.c_str())) != NULL) {
directorypath = path + PATH_SEP;
closedir (dir);
}
return directorypath;
}
list<string> SystemToolkit::list_directory(const string& path, const string& filter)
{
list<string> ls;
DIR *dir;
struct dirent *ent;
if ((dir = opendir (path.c_str())) != NULL) {
// list all the files and directories within directory
while ((ent = readdir (dir)) != NULL) {
if ( ent->d_type == DT_REG)
{
string filename = string(ent->d_name);
if ( extension_filename(filename) == filter)
ls.push_back( full_filename(path, filename) );
}
}
closedir (dir);
}
return ls;
}
void SystemToolkit::open(const string& url)
{
#ifdef WIN32
ShellExecuteA( nullptr, nullptr, url.c_str(), nullptr, nullptr, 0 );
#elif defined APPLE
char buf[2048];
sprintf( buf, "open '%s'", url.c_str() );
system( buf );
#else
char buf[2048];
sprintf( buf, "xdg-open '%s'", url.c_str() );
int r = system( buf );
#endif
}
void SystemToolkit::execute(const string& command)
{
#ifdef WIN32
ShellExecuteA( nullptr, nullptr, url.c_str(), nullptr, nullptr, 0 );
#elif defined APPLE
int r = system( command.c_str() );
#else
int r = system( command.c_str() );
#endif
}
// example :
// std::thread (SystemToolkit::execute,
// "gst-launch-1.0 udpsrc port=5000 ! application/x-rtp,encoding-name=JPEG,payload=26 ! rtpjpegdepay ! jpegdec ! autovideosink").detach();;
// Using ICU transliteration :
// https://unicode-org.github.io/icu/userguide/transforms/general/#icu-transliterators
std::string SystemToolkit::transliterate(std::string input)
{
auto ucs = icu::UnicodeString::fromUTF8(input);
UErrorCode status = U_ZERO_ERROR;
icu::Transliterator *firstTrans = icu::Transliterator::createInstance(
"any-NFKD ; [:Nonspacing Mark:] Remove; NFKC; Latin", UTRANS_FORWARD, status);
firstTrans->transliterate(ucs);
icu::Transliterator *secondTrans = icu::Transliterator::createInstance(
"any-NFKD ; [:Nonspacing Mark:] Remove; [@!#$*%~] Remove; NFKC", UTRANS_FORWARD, status);
secondTrans->transliterate(ucs);
std::ostringstream output;
output << ucs;
return output.str();
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,352 +0,0 @@
#include <algorithm>
#include <cmath>
#include "defines.h"
#include "Log.h"
#include "Timeline.h"
struct includesTime: public std::unary_function<TimeInterval, bool>
{
inline bool operator()(const TimeInterval s) const
{
return s.includes(_t);
}
includesTime(GstClockTime t) : _t(t) { }
private:
GstClockTime _t;
};
Timeline::Timeline()
{
reset();
}
Timeline::~Timeline()
{
}
Timeline& Timeline::operator = (const Timeline& b)
{
if (this != &b) {
this->timing_ = b.timing_;
this->step_ = b.step_;
this->gaps_ = b.gaps_;
this->gaps_array_need_update_ = b.gaps_array_need_update_;
memcpy( this->gapsArray_, b.gapsArray_, MAX_TIMELINE_ARRAY * sizeof(float));
memcpy( this->fadingArray_, b.fadingArray_, MAX_TIMELINE_ARRAY * sizeof(float));
}
return *this;
}
void Timeline::reset()
{
// reset timing
timing_.reset();
timing_.begin = 0;
first_ = 0;
step_ = GST_CLOCK_TIME_NONE;
clearGaps();
clearFading();
}
bool Timeline::is_valid()
{
return timing_.is_valid() && step_ != GST_CLOCK_TIME_NONE;
}
void Timeline::setFirst(GstClockTime first)
{
first_ = first;
}
void Timeline::setEnd(GstClockTime end)
{
timing_.end = end;
}
void Timeline::setStep(GstClockTime dt)
{
step_ = dt;
}
void Timeline::setTiming(TimeInterval interval, GstClockTime step)
{
timing_ = interval;
if (step != GST_CLOCK_TIME_NONE)
step_ = step;
}
GstClockTime Timeline::next(GstClockTime time) const
{
GstClockTime next_time = time;
TimeInterval gap;
if (gapAt(time, gap) && gap.is_valid())
next_time = gap.end;
return next_time;
}
GstClockTime Timeline::previous(GstClockTime time) const
{
GstClockTime prev_time = time;
TimeInterval gap;
if (gapAt(time, gap) && gap.is_valid())
prev_time = gap.begin;
return prev_time;
}
float *Timeline::gapsArray()
{
if (gaps_array_need_update_) {
fillArrayFromGaps(gapsArray_, MAX_TIMELINE_ARRAY);
}
return gapsArray_;
}
void Timeline::update()
{
updateGapsFromArray(gapsArray_, MAX_TIMELINE_ARRAY);
gaps_array_need_update_ = false;
}
bool Timeline::gapAt(const GstClockTime t, TimeInterval &gap) const
{
TimeIntervalSet::const_iterator g = std::find_if(gaps_.begin(), gaps_.end(), includesTime(t));
if ( g != gaps_.end() ) {
gap = (*g);
return true;
}
return false;
}
bool Timeline::addGap(GstClockTime begin, GstClockTime end)
{
return addGap( TimeInterval(begin, end) );
}
bool Timeline::addGap(TimeInterval s)
{
if ( s.is_valid() ) {
gaps_array_need_update_ = true;
return gaps_.insert(s).second;
}
return false;
}
void Timeline::setGaps(TimeIntervalSet g)
{
gaps_array_need_update_ = true;
gaps_ = g;
}
bool Timeline::removeGaptAt(GstClockTime t)
{
TimeIntervalSet::const_iterator s = std::find_if(gaps_.begin(), gaps_.end(), includesTime(t));
if ( s != gaps_.end() ) {
gaps_.erase(s);
gaps_array_need_update_ = true;
return true;
}
return false;
}
TimeIntervalSet Timeline::sections() const
{
TimeIntervalSet sec;
GstClockTime begin_sec = timing_.begin;
if (gaps_.size() > 0) {
auto it = gaps_.begin();
if ((*it).begin == begin_sec) {
begin_sec = (*it).end;
++it;
}
for (; it != gaps_.end(); ++it)
{
sec.insert( TimeInterval(begin_sec, (*it).begin) );
begin_sec = (*it).end;
}
}
if (begin_sec != timing_.end)
sec.insert( TimeInterval(begin_sec, timing_.end) );
return sec;
}
void Timeline::clearGaps()
{
gaps_.clear();
for(int i=0;i<MAX_TIMELINE_ARRAY;++i)
gapsArray_[i] = 0.f;
gaps_array_need_update_ = true;
}
float Timeline::fadingAt(const GstClockTime t)
{
double true_index = (static_cast<double>(MAX_TIMELINE_ARRAY) * static_cast<double>(t)) / static_cast<double>(timing_.end);
double previous_index = floor(true_index);
float percent = static_cast<float>(true_index - previous_index);
size_t keyframe_index = CLAMP( static_cast<size_t>(previous_index), 0, MAX_TIMELINE_ARRAY-1);
size_t keyframe_next_index = CLAMP( keyframe_index+1, 0, MAX_TIMELINE_ARRAY-1);
float v = fadingArray_[keyframe_index];
v += percent * (fadingArray_[keyframe_next_index] - fadingArray_[keyframe_index]);
return v;
}
void Timeline::clearFading()
{
for(int i=0;i<MAX_TIMELINE_ARRAY;++i)
fadingArray_[i] = 1.f;
}
void Timeline::smoothFading(uint N)
{
const float kernel[7] = { 2.f, 22.f, 97.f, 159.f, 97.f, 22.f, 2.f};
float tmparray[MAX_TIMELINE_ARRAY];
for (uint n = 0; n < N; ++n) {
for (long i = 0; i < MAX_TIMELINE_ARRAY; ++i) {
tmparray[i] = 0.f;
float divider = 0.f;
for( long j = 0; j < 7; ++j) {
long k = i - 3 + j;
if (k > -1 && k < MAX_TIMELINE_ARRAY-1) {
tmparray[i] += fadingArray_[k] * kernel[j];
divider += kernel[j];
}
}
tmparray[i] *= 1.f / divider;
}
memcpy( fadingArray_, tmparray, MAX_TIMELINE_ARRAY * sizeof(float));
}
}
void Timeline::autoFading(uint milisecond)
{
GstClockTime stepduration = timing_.end / MAX_TIMELINE_ARRAY;
stepduration = GST_TIME_AS_MSECONDS(stepduration);
uint N = milisecond / stepduration;
// reset all to zero
for(int i=0;i<MAX_TIMELINE_ARRAY;++i)
fadingArray_[i] = 0.f;
// get sections (inverse of gaps)
TimeIntervalSet sec = sections();
// fading for each section
// NB : there is at least one
for (auto it = sec.begin(); it != sec.end(); ++it)
{
// get index of begining of section
size_t s = ( (*it).begin * MAX_TIMELINE_ARRAY ) / timing_.end;
// get index of ending of section
size_t e = ( (*it).end * MAX_TIMELINE_ARRAY ) / timing_.end;
// calculate size of the smooth transition in [s e] interval
uint n = MIN( (e-s)/3, N );
// linear fade in starting at s
size_t i = s;
for (; i < s+n; ++i)
fadingArray_[i] = static_cast<float>(i-s) / static_cast<float>(n);
// plateau
for (; i < e-n; ++i)
fadingArray_[i] = 1.f;
// linear fade out ending at e
for (; i < e; ++i)
fadingArray_[i] = static_cast<float>(e-i) / static_cast<float>(n);
}
}
void Timeline::updateGapsFromArray(float *array, size_t array_size)
{
// reset gaps
gaps_.clear();
// fill the gaps from array
if (array != nullptr && array_size > 0 && timing_.is_valid()) {
// loop over the array to detect gaps
float status = 0.f;
GstClockTime begin_gap = GST_CLOCK_TIME_NONE;
for (size_t i = 0; i < array_size; ++i) {
// detect a change of value between two slots
if ( array[i] != status) {
// compute time of the event in array
GstClockTime t = (timing_.duration() * i) / array_size;
// change from 0.f to 1.f : begin of a gap
if (array[i] > 0.f) {
begin_gap = t;
}
// change from 1.f to 0.f : end of a gap
else {
addGap( begin_gap, t );
begin_gap = GST_CLOCK_TIME_NONE;
}
// swap
status = array[i];
}
}
// end a potentially pending gap if reached end of array with no end of gap
if (begin_gap != GST_CLOCK_TIME_NONE)
addGap( begin_gap, timing_.end );
}
}
void Timeline::fillArrayFromGaps(float *array, size_t array_size)
{
// fill the array from gaps
if (array != nullptr && array_size > 0 && timing_.is_valid()) {
for(int i=0;i<array_size;++i)
gapsArray_[i] = 0.f;
// for each gap
for (auto it = gaps_.begin(); it != gaps_.end(); ++it)
{
size_t s = ( (*it).begin * array_size ) / timing_.end;
size_t e = ( (*it).end * array_size ) / timing_.end;
// fill with 1 where there is a gap
for (size_t i = s; i < e; ++i) {
gapsArray_[i] = 1.f;
}
}
gaps_array_need_update_ = false;
}
// // NB: less efficient algorithm :
// TimeInterval gap;
// for (size_t i = 0; i < array_size; ++i) {
// GstClockTime t = (timing_.duration() * i) / array_size;
// array[i] = gapAt(t, gap) ? 1.f : 0.f;
// }
}

View File

@@ -1,397 +0,0 @@
// Opengl
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/matrix_access.hpp>
#include <glm/gtx/vector_angle.hpp>
#include "imgui.h"
#include "ImGuiToolkit.h"
#include <string>
#include <sstream>
#include "Mixer.h"
#include "defines.h"
#include "Settings.h"
#include "SessionSource.h"
#include "DrawVisitor.h"
#include "Decorations.h"
#include "UserInterfaceManager.h"
#include "Log.h"
#include "TransitionView.h"
TransitionView::TransitionView() : View(TRANSITION), transition_source_(nullptr)
{
// read default settings
if ( Settings::application.views[mode_].name.empty() )
{
// no settings found: store application default
Settings::application.views[mode_].name = "Transition";
scene.root()->scale_ = glm::vec3(TRANSITION_DEFAULT_SCALE, TRANSITION_DEFAULT_SCALE, 1.0f);
scene.root()->translation_ = glm::vec3(1.5f, 0.f, 0.0f);
saveSettings();
}
else
restoreSettings();
// Geometry Scene background
gradient_ = new Switch;
gradient_->attach(new ImageSurface("images/gradient_0_cross_linear.png"));
gradient_->attach(new ImageSurface("images/gradient_1_black_linear.png"));
gradient_->attach(new ImageSurface("images/gradient_2_cross_quad.png"));
gradient_->attach(new ImageSurface("images/gradient_3_black_quad.png"));
gradient_->scale_ = glm::vec3(0.501f, 0.006f, 1.f);
gradient_->translation_ = glm::vec3(-0.5f, -0.005f, -0.01f);
scene.fg()->attach(gradient_);
mark_1s_ = new Mesh("mesh/h_mark.ply");
mark_1s_->translation_ = glm::vec3(-1.f, -0.01f, 0.0f);
mark_1s_->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f );
scene.fg()->attach(mark_1s_);
mark_100ms_ = new Mesh("mesh/h_mark.ply");
mark_100ms_->translation_ = glm::vec3(-1.f, -0.01f, 0.0f);
mark_100ms_->scale_ = glm::vec3(0.5f, 0.5f, 0.0f);
mark_100ms_->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f );
scene.fg()->attach(mark_100ms_);
// move the whole forground below the icons
scene.fg()->translation_ = glm::vec3(0.f, -0.11f, 0.0f);
output_surface_ = new Surface;
scene.bg()->attach(output_surface_);
Frame *border = new Frame(Frame::ROUND, Frame::THIN, Frame::GLOW);
border->color = glm::vec4( COLOR_FRAME, 1.0f );
scene.bg()->attach(border);
scene.bg()->scale_ = glm::vec3(0.1f, 0.1f, 1.f);
scene.bg()->translation_ = glm::vec3(0.4f, 0.f, 0.0f);
}
void TransitionView::update(float dt)
{
// update scene
View::update(dt);
// a more complete update is requested
if (View::need_deep_update_ > 0) {
// update rendering of render frame
FrameBuffer *output = Mixer::manager().session()->frame();
if (output){
float aspect_ratio = output->aspectRatio();
for (NodeSet::iterator node = scene.bg()->begin(); node != scene.bg()->end(); node++) {
(*node)->scale_.x = aspect_ratio;
}
output_surface_->setTextureIndex( output->texture() );
}
}
// Update transition source
if ( transition_source_ != nullptr) {
float d = transition_source_->group(View::TRANSITION)->translation_.x;
// Transfer this movement to changes in mixing
// cross fading
if ( Settings::application.transition.cross_fade )
{
float f = 0.f;
// change alpha of session:
if (Settings::application.transition.profile == 0)
// linear => identical coordinates in Mixing View
f = d;
else {
// quadratic => square coordinates in Mixing View
f = (d+1.f)*(d+1.f) -1.f;
}
transition_source_->group(View::MIXING)->translation_.x = CLAMP(f, -1.f, 0.f);
transition_source_->group(View::MIXING)->translation_.y = 0.f;
}
// fade to black
else
{
// change alpha of session ; hidden before -0.5, visible after
transition_source_->group(View::MIXING)->translation_.x = d < -0.5f ? -1.f : 0.f;
transition_source_->group(View::MIXING)->translation_.y = 0.f;
// fade to black at 50% : fade-out [-1.0 -0.5], fade-in [-0.5 0.0]
float f = 0.f;
if (Settings::application.transition.profile == 0)
f = ABS(2.f * d + 1.f); // linear
else {
f = ( 2.f * d + 1.f); // quadratic
f *= f;
}
Mixer::manager().session()->setFading( 1.f - f );
}
// request update
transition_source_->touch();
if (d > 0.2f && Settings::application.transition.auto_open)
Mixer::manager().setView(View::MIXING);
}
}
void TransitionView::draw()
{
// update the GUI depending on changes in settings
gradient_->setActive( 2*Settings::application.transition.profile + (Settings::application.transition.cross_fade ? 0 : 1) );
// draw scene of this view
View::draw();
// 100ms tic marks
int n = static_cast<int>( Settings::application.transition.duration / 0.1f );
glm::mat4 T = glm::translate(glm::identity<glm::mat4>(), glm::vec3( 1.f / n, 0.f, 0.f));
DrawVisitor dv(mark_100ms_, Rendering::manager().Projection());
dv.loop(n+1, T);
scene.accept(dv);
// 1s tic marks
int N = static_cast<int>( Settings::application.transition.duration );
T = glm::translate(glm::identity<glm::mat4>(), glm::vec3( 10.f / n, 0.f, 0.f));
DrawVisitor dv2(mark_1s_, Rendering::manager().Projection());
dv2.loop(N+1, T);
scene.accept(dv2);
// display interface duration
glm::vec2 P = Rendering::manager().project(glm::vec3(-0.17f, -0.14f, 0.f), scene.root()->transform_, false);
ImGui::SetNextWindowPos(ImVec2(P.x, P.y), ImGuiCond_Always);
if (ImGui::Begin("##Transition", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground
| ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings
| ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus))
{
// style grey
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.27f, 0.27f, 0.27f, 0.55f));
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.27f, 0.27f, 0.27f, 0.79f));
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.27f, 0.27f, 0.27f, 0.7f));
ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.15f, 0.15f, 0.15f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, ImVec4(0.10f, 0.10f, 0.10f, 1.00f));
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.00f, 0.00f, 0.00f, 0.00f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.27f, 0.27f, 0.27f, 0.55f)); // 7 colors
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
ImGui::SetNextItemWidth(180.f);
ImGui::SliderFloat("##transitionduration", &Settings::application.transition.duration,
TRANSITION_MIN_DURATION, TRANSITION_MAX_DURATION, "%.1f s");
ImGui::SameLine();
if ( ImGui::Button(ICON_FA_STEP_FORWARD) )
play(false);
ImGui::PopFont();
ImGui::PopStyleColor(7); // 7 colors
ImGui::End();
}
P = Rendering::manager().project(glm::vec3(-0.535f, -0.14f, 0.f), scene.root()->transform_, false);
ImGui::SetNextWindowPos(ImVec2(P.x, P.y), ImGuiCond_Always);
if (ImGui::Begin("##TransitionType", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground
| ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings
| ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus))
{
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
// black background in icon 'transition to black'
if (!Settings::application.transition.cross_fade) {
ImVec2 draw_pos = ImGui::GetCursorScreenPos();
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 0.0f, 0.0f, 1.f));
ImGuiToolkit::Icon(19,1);
ImGui::PopStyleColor();
ImGui::SetCursorScreenPos(draw_pos);
}
// toggle transition mode
const char *tooltip[2] = {"Transition to black", "Cross fading"};
ImGuiToolkit::IconToggle(0,2,0,8, &Settings::application.transition.cross_fade, tooltip );
ImGui::PopFont();
ImGui::End();
}
}
bool TransitionView::canSelect(Source *s) {
return ( s!=nullptr && s == transition_source_);
}
void TransitionView::attach(SessionFileSource *ts)
{
// store source for later (detatch & interaction)
transition_source_ = ts;
if ( transition_source_ != nullptr) {
// insert in scene
Group *tg = transition_source_->group(View::TRANSITION);
tg->visible_ = true;
scene.ws()->attach(tg);
// in fade to black transition, start transition from current fading value
if ( !Settings::application.transition.cross_fade) {
// reverse calculate x position to match actual fading of session
float d = 0.f;
if (Settings::application.transition.profile == 0)
d = -1.f + 0.5f * Mixer::manager().session()->fading(); // linear
else {
d = -1.f - 0.5f * ( sqrt(1.f - Mixer::manager().session()->fading()) - 1.f); // quadratic
}
transition_source_->group(View::TRANSITION)->translation_.x = d;
}
}
}
Session *TransitionView::detach()
{
// by default, nothing to return
Session *ret = nullptr;
if ( transition_source_ != nullptr) {
// get and detatch the group node from the view workspace
Group *tg = transition_source_->group(View::TRANSITION);
scene.ws()->detach( tg );
// test if the icon of the transition source is "Ready"
if ( tg->translation_.x > 0.f )
// detatch the session and return it
ret = transition_source_->detach();
else
// not detached: make current
Mixer::manager().setCurrentSource(transition_source_);
// done with transition
transition_source_ = nullptr;
}
return ret;
}
void TransitionView::zoom (float factor)
{
if (transition_source_ != nullptr) {
float d = transition_source_->group(View::TRANSITION)->translation_.x;
d += 0.1f * factor;
transition_source_->group(View::TRANSITION)->translation_.x = CLAMP(d, -1.f, 0.f);
}
}
std::pair<Node *, glm::vec2> TransitionView::pick(glm::vec2 P)
{
std::pair<Node *, glm::vec2> pick = View::pick(P);
if (transition_source_ != nullptr) {
// start animation when clic on target
if (pick.first == output_surface_)
play(true);
// otherwise cancel animation
else
transition_source_->group(View::TRANSITION)->clearCallbacks();
}
return pick;
}
void TransitionView::play(bool open)
{
if (transition_source_ != nullptr) {
// toggle play/stop with button
if (!transition_source_->group(View::TRANSITION)->update_callbacks_.empty() && !open) {
// just cancel previous animation
transition_source_->group(View::TRANSITION)->clearCallbacks();
return;
}
// else cancel previous animation and start new one
transition_source_->group(View::TRANSITION)->clearCallbacks();
// if want to open session after play, target movement till end position, otherwise stop at 0
float target_x = open ? 0.4f : 0.f;
// calculate how far to reach target
float time = CLAMP(- transition_source_->group(View::TRANSITION)->translation_.x, 0.f, 1.f);
// extra distance to reach transition if want to open
time += open ? 0.2f : 0.f;
// calculate remaining time on the total duration, in ms
time *= Settings::application.transition.duration * 1000.f;
// if remaining time is more than 50ms
if (time > 50.f) {
// start animation
MoveToCallback *anim = new MoveToCallback(glm::vec3(target_x, 0.0, 0.0), time);
transition_source_->group(View::TRANSITION)->update_callbacks_.push_back(anim);
}
// otherwise finish animation
else
transition_source_->group(View::TRANSITION)->translation_.x = target_x;
}
}
View::Cursor TransitionView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2>)
{
if (!s)
return Cursor();
// unproject
glm::vec3 gl_Position_from = Rendering::manager().unProject(from, scene.root()->transform_);
glm::vec3 gl_Position_to = Rendering::manager().unProject(to, scene.root()->transform_);
// compute delta translation
float d = s->stored_status_->translation_.x + gl_Position_to.x - gl_Position_from.x;
std::ostringstream info;
if (d > 0.2) {
s->group(mode_)->translation_.x = 0.4;
info << "Open session";
}
else {
s->group(mode_)->translation_.x = CLAMP(d, -1.f, 0.f);
info << "Transition " << int( 100.f * (1.f + s->group(View::TRANSITION)->translation_.x)) << "%";
}
return Cursor(Cursor_ResizeEW, info.str() );
}
void TransitionView::arrow (glm::vec2 movement)
{
Source *s = Mixer::manager().currentSource();
if (s) {
glm::vec3 gl_Position_from = Rendering::manager().unProject(glm::vec2(0.f), scene.root()->transform_);
glm::vec3 gl_Position_to = Rendering::manager().unProject(movement, scene.root()->transform_);
glm::vec3 gl_delta = gl_Position_to - gl_Position_from;
float d = s->group(mode_)->translation_.x + gl_delta.x * ARROWS_MOVEMENT_FACTOR;
s->group(mode_)->translation_.x = CLAMP(d, -1.f, 0.f);
// request update
s->touch();
}
}
View::Cursor TransitionView::drag (glm::vec2 from, glm::vec2 to)
{
Cursor ret = View::drag(from, to);
// Clamp translation to acceptable area
scene.root()->translation_ = glm::clamp(scene.root()->translation_, glm::vec3(1.f, -1.7f, 0.f), glm::vec3(2.f, 1.7f, 0.f));
return ret;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,180 +0,0 @@
#ifndef __UI_MANAGER_H_
#define __UI_MANAGER_H_
#include <string>
#include <list>
#define NAV_COUNT 68
#define NAV_MAX 64
#define NAV_NEW 65
#define NAV_MENU 66
#define NAV_TRANS 67
struct ImVec2;
class Source;
class MediaPlayer;
class SourcePreview {
Source *source_;
std::string label_;
public:
SourcePreview();
void setSource(Source *s = nullptr, std::string label = "");
Source *getSource();
void Render(float width, bool controlbutton = false);
bool ready() const;
inline bool filled() const { return source_ != nullptr; }
};
class Navigator
{
// geometry left bar & pannel
float width_;
float height_;
float pannel_width_;
float sourcelist_height_;
float padding_width_;
// behavior pannel
bool pannel_visible_;
bool view_pannel_visible;
bool selected_button[NAV_COUNT];
int pattern_type;
void clearButtonSelection();
void applyButtonSelection(int index);
// side pannels
void RenderSourcePannel(Source *s);
void RenderMainPannel();
void RenderTransitionPannel();
void RenderNewPannel();
void RenderViewPannel(ImVec2 draw_pos, ImVec2 draw_size);
SourcePreview new_source_preview_;
public:
Navigator();
bool pannelVisible() { return pannel_visible_; }
void hidePannel();
void showPannelSource(int index);
void togglePannelMenu();
void togglePannelNew();
void Render();
};
class ToolBox
{
bool show_demo_window;
bool show_icons_window;
bool show_sandbox;
public:
ToolBox();
void Render();
};
class MediaController
{
MediaPlayer *mp_;
std::string current_;
bool follow_active_source_;
bool media_playing_mode_;
bool slider_pressed_;
public:
MediaController();
void setMediaPlayer(MediaPlayer *mp = nullptr);
void followCurrentSource();
void Render();
};
class UserInterface
{
friend class Navigator;
Navigator navigator;
ToolBox toolbox;
MediaController mediacontrol;
bool ctrl_modifier_active;
bool alt_modifier_active;
bool shift_modifier_active;
bool show_vimix_about;
bool show_imgui_about;
bool show_gst_about;
bool show_opengl_about;
int show_view_navigator;
int target_view_navigator;
unsigned int screenshot_step;
// frame grabbers
uint64_t video_recorder_;
uint64_t webcam_emulator_;
// Private Constructor
UserInterface();
UserInterface(UserInterface const& copy); // Not Implemented
UserInterface& operator=(UserInterface const& copy); // Not Implemented
public:
static UserInterface& manager()
{
// The only instance
static UserInterface _instance;
return _instance;
}
// pre-loop initialization
bool Init();
// loop update start new frame
void NewFrame();
// loop update rendering
void Render();
// Post-loop termination
void Terminate();
// status querries
inline bool ctrlModifier() const { return ctrl_modifier_active; }
inline bool altModifier() const { return alt_modifier_active; }
inline bool shiftModifier() const { return shift_modifier_active; }
void StartScreenshot();
void showPannel(int id = 0);
void showSourceEditor(Source *s);
void showMediaPlayer(MediaPlayer *mp);
// TODO implement the shader editor
std::string currentTextEdit;
void fillShaderEditor(std::string text);
protected:
void showMenuFile();
void showMenuEdit();
void selectSaveFilename();
void selectOpenFilename();
void RenderPreview();
void RenderHistory();
void RenderShaderEditor();
int RenderViewNavigator(int* shift);
void handleKeyboard();
void handleMouse();
void handleScreenshot();
void RenderAbout(bool* p_open);
};
#endif /* #define __UI_MANAGER_H_ */

View File

@@ -1,88 +0,0 @@
#ifndef VISITOR_H
#define VISITOR_H
#include <string>
// Forward declare different kind of Node
class Node;
class Group;
class Switch;
class Primitive;
class Scene;
class Surface;
class ImageSurface;
class MediaSurface;
class FrameBufferSurface;
class LineStrip;
class LineSquare;
class LineCircle;
class Mesh;
class Frame;
class Handles;
class Symbol;
class Disk;
class Stream;
class MediaPlayer;
class Shader;
class ImageShader;
class MaskShader;
class ImageProcessingShader;
class Source;
class MediaSource;
class PatternSource;
class DeviceSource;
class GenericStreamSource;
class SessionFileSource;
class SessionGroupSource;
class RenderSource;
class CloneSource;
class NetworkSource;
class MixingGroup;
// Declares the interface for the visitors
class Visitor {
public:
// Need to declare overloads for basic kind of Nodes to visit
virtual void visit (Scene&) = 0;
virtual void visit (Node&) = 0;
virtual void visit (Primitive&) = 0;
virtual void visit (Group&) = 0;
virtual void visit (Switch&) = 0;
// not mandatory for all others
virtual void visit (Surface&) {}
virtual void visit (ImageSurface&) {}
virtual void visit (MediaSurface&) {}
virtual void visit (FrameBufferSurface&) {}
virtual void visit (LineStrip&) {}
virtual void visit (LineSquare&) {}
virtual void visit (Mesh&) {}
virtual void visit (Frame&) {}
virtual void visit (Handles&) {}
virtual void visit (Symbol&) {}
virtual void visit (Disk&) {}
virtual void visit (Stream&) {}
virtual void visit (MediaPlayer&) {}
virtual void visit (Shader&) {}
virtual void visit (ImageShader&) {}
virtual void visit (MaskShader&) {}
virtual void visit (ImageProcessingShader&) {}
// utility
virtual void visit (MixingGroup&) {}
virtual void visit (Source&) {}
virtual void visit (MediaSource&) {}
virtual void visit (NetworkSource&) {}
virtual void visit (GenericStreamSource&) {}
virtual void visit (DeviceSource&) {}
virtual void visit (PatternSource&) {}
virtual void visit (SessionFileSource&) {}
virtual void visit (SessionGroupSource&) {}
virtual void visit (RenderSource&) {}
virtual void visit (CloneSource&) {}
};
#endif // VISITOR_H

View File

@@ -0,0 +1,41 @@
set(BUNDLE_NAME @BUNDLE_NAME@)
set(BUNDLE_LIBS_DIR @BUNDLE_LIBS_DIR@)
set(BUNDLE_DIRS @BUNDLE_DIRS@)
set(APPLE_CODESIGN_IDENTITY @APPLE_CODESIGN_IDENTITY@)
set(APPLE_CODESIGN_ENTITLEMENTS @APPLE_CODESIGN_ENTITLEMENTS@)
set(BUNDLE_PATH "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}")
include(BundleUtilities)
#fixup_bundle tries to copy system libraries without this. Wtf?
function(gp_resolved_file_type_override file type)
if(file MATCHES "^(/usr/lib)")
set(type "system" PARENT_SCOPE)
endif()
endfunction()
file(GLOB_RECURSE BUNDLE_LIBS "${CMAKE_INSTALL_PREFIX}/${BUNDLE_LIBS_DIR}/*.dylib")
set(BU_CHMOD_BUNDLE_ITEMS ON)
fixup_bundle("${BUNDLE_PATH}" "${BUNDLE_LIBS}" "${BUNDLE_DIRS}")
if(DEFINED APPLE_CODESIGN_IDENTITY AND DEFINED APPLE_CODESIGN_ENTITLEMENTS)
# execute_process(COMMAND
# codesign --verbose=4 --deep --force --options runtime
# --entitlements "${APPLE_CODESIGN_ENTITLEMENTS}"
# --sign "${APPLE_CODESIGN_IDENTITY}"
# "${BUNDLE_NAME}"
# )
foreach(PATH_TO_SIGN IN LISTS BUNDLE_LIBS BUNDLE_PATH)
execute_process(COMMAND
codesign --verbose=4 --deep --force
--entitlements "${APPLE_CODESIGN_ENTITLEMENTS}"
--sign "${APPLE_CODESIGN_IDENTITY}"
"${PATH_TO_SIGN}"
)
endforeach()
else()
message(STATUS "Not signing bundle. Specify -DAPPLE_CODESIGN_IDENTITY and -DAPPLE_CODESIGN_ENTITLEMENTS to cmake before running cpack to sign")
endif()

View File

@@ -34,7 +34,7 @@ endif()
set(_version 2.0.0)
cmake_minimum_required(VERSION 3.3)
cmake_minimum_required(VERSION 3.12)
include(CMakeParseArguments)
if(COMMAND cmrc_add_resource_library)
@@ -77,6 +77,10 @@ set(hpp_content [==[
#include <system_error>
#include <type_traits>
#if !(defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) || defined(CMRC_NO_EXCEPTIONS))
#define CMRC_NO_EXCEPTIONS 1
#endif
namespace cmrc { namespace detail { struct dummy; } }
#define CMRC_DECLARE(libid) \
@@ -101,7 +105,7 @@ public:
iterator cbegin() const noexcept { return _begin; }
iterator end() const noexcept { return _end; }
iterator cend() const noexcept { return _end; }
std::size_t size() const { return std::distance(begin(), end()); }
std::size_t size() const { return static_cast<std::size_t>(std::distance(begin(), end())); }
file() = default;
file(iterator beg, iterator end) noexcept : _begin(beg), _end(end) {}
@@ -243,16 +247,16 @@ public:
return !(*this == rhs);
}
iterator operator++() noexcept {
iterator& operator++() noexcept {
++_base_iter;
return *this;
}
iterator operator++(int) noexcept {
auto cp = *this;
++_base_iter;
return cp;
}
iterator& operator++(int) noexcept {
++_base_iter;
return *this;
}
};
using const_iterator = iterator;
@@ -275,7 +279,7 @@ inline std::string normalize_path(std::string path) {
}
auto off = path.npos;
while ((off = path.find("//")) != path.npos) {
path.erase(path.begin() + off);
path.erase(path.begin() + static_cast<std::string::difference_type>(off));
}
return path;
}
@@ -339,7 +343,12 @@ public:
file open(const std::string& path) const {
auto entry_ptr = _get(path);
if (!entry_ptr || !entry_ptr->is_file()) {
#ifdef CMRC_NO_EXCEPTIONS
fprintf(stderr, "Error no such file or directory: %s\n", path.c_str());
abort();
#else
throw std::system_error(make_error_code(std::errc::no_such_file_or_directory), path);
#endif
}
auto& dat = entry_ptr->as_file();
return file{dat.begin_ptr, dat.end_ptr};
@@ -362,10 +371,20 @@ public:
directory_iterator iterate_directory(const std::string& path) const {
auto entry_ptr = _get(path);
if (!entry_ptr) {
#ifdef CMRC_NO_EXCEPTIONS
fprintf(stderr, "Error no such file or directory: %s\n", path.c_str());
abort();
#else
throw std::system_error(make_error_code(std::errc::no_such_file_or_directory), path);
#endif
}
if (!entry_ptr->is_directory()) {
#ifdef CMRC_NO_EXCEPTIONS
fprintf(stderr, "Error not a directory: %s\n", path.c_str());
abort();
#else
throw std::system_error(make_error_code(std::errc::not_a_directory), path);
#endif
}
return entry_ptr->as_directory().begin();
}
@@ -387,14 +406,14 @@ endif()
file(GENERATE OUTPUT "${cmrc_hpp}" CONTENT "${hpp_content}" CONDITION ${_generate})
add_library(cmrc-base INTERFACE)
target_include_directories(cmrc-base INTERFACE "${CMRC_INCLUDE_DIR}")
target_include_directories(cmrc-base INTERFACE $<BUILD_INTERFACE:${CMRC_INCLUDE_DIR}>)
# Signal a basic C++11 feature to require C++11.
target_compile_features(cmrc-base INTERFACE cxx_nullptr)
set_property(TARGET cmrc-base PROPERTY INTERFACE_CXX_EXTENSIONS OFF)
add_library(cmrc::base ALIAS cmrc-base)
function(cmrc_add_resource_library name)
set(args ALIAS NAMESPACE)
set(args ALIAS NAMESPACE TYPE)
cmake_parse_arguments(ARG "" "${args}" "" "${ARGN}")
# Generate the identifier for the resource library's namespace
set(ns_re "[a-zA-Z_][a-zA-Z0-9_]*")
@@ -410,6 +429,14 @@ function(cmrc_add_resource_library name)
endif()
endif()
set(libname "${name}")
# Check that type is either "STATIC" or "OBJECT", or default to "STATIC" if
# not set
if(NOT DEFINED ARG_TYPE)
set(ARG_TYPE STATIC)
elseif(NOT "${ARG_TYPE}" MATCHES "^(STATIC|OBJECT)$")
message(SEND_ERROR "${ARG_TYPE} is not a valid TYPE (STATIC and OBJECT are acceptable)")
set(ARG_TYPE STATIC)
endif()
# Generate a library with the compiled in character arrays.
string(CONFIGURE [=[
#include <cmrc/cmrc.hpp>
@@ -468,7 +495,7 @@ function(cmrc_add_resource_library name)
# Generate the actual static library. Each source file is just a single file
# with a character array compiled in containing the contents of the
# corresponding resource file.
add_library(${name} STATIC ${libcpp})
add_library(${name} ${ARG_TYPE} ${libcpp})
set_property(TARGET ${name} PROPERTY CMRC_LIBDIR "${libdir}")
set_property(TARGET ${name} PROPERTY CMRC_NAMESPACE "${ARG_NAMESPACE}")
target_link_libraries(${name} PUBLIC cmrc::base)
@@ -558,7 +585,7 @@ function(cmrc_add_resources name)
endif()
get_filename_component(dirpath "${ARG_PREFIX}${relpath}" DIRECTORY)
_cmrc_register_dirs("${name}" "${dirpath}")
get_filename_component(abs_out "${libdir}/intermediate/${relpath}.cpp" ABSOLUTE)
get_filename_component(abs_out "${libdir}/intermediate/${ARG_PREFIX}${relpath}.cpp" ABSOLUTE)
# Generate a symbol name relpath the file's character array
_cm_encode_fpath(sym "${relpath}")
# Get the symbol name for the parent directory

View File

@@ -33,9 +33,10 @@ find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_check_modules(PKG_GSTREAMER gstreamer-${GSTREAMER_ABI_VERSION})
exec_program(${PKG_CONFIG_EXECUTABLE}
ARGS --variable pluginsdir gstreamer-${GSTREAMER_ABI_VERSION}
OUTPUT_VARIABLE PKG_GSTREAMER_PLUGIN_DIR)
execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE}
--variable pluginsdir gstreamer-${GSTREAMER_ABI_VERSION}
OUTPUT_VARIABLE PKG_GSTREAMER_PLUGIN_DIR_TMP)
string(STRIP ${PKG_GSTREAMER_PLUGIN_DIR_TMP} PKG_GSTREAMER_PLUGIN_DIR)
endif()
find_library(GSTREAMER_LIBRARY

View File

@@ -0,0 +1,76 @@
# - Try to find gst-plugins-base
# Once done this will define
#
# GSTREAMER_PLUGINS_BASE_FOUND - system has gst-plugins-base
#
# And for all the plugin libraries specified in the COMPONENTS
# of find_package, this module will define:
#
# GSTREAMER_<plugin_lib>_LIBRARY_FOUND - system has <plugin_lib>
# GSTREAMER_<plugin_lib>_LIBRARY - the <plugin_lib> library
# GSTREAMER_<plugin_lib>_INCLUDE_DIR - the <plugin_lib> include directory
#
# Copyright (c) 2010, Collabora Ltd.
# @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
set(GSTREAMER_ABI_VERSION "1.0")
# Find the pkg-config file for doing the version check
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_check_modules(PKG_GSTREAMER_PLUGINS_BAD gstreamer-plugins-bad-${GSTREAMER_ABI_VERSION})
endif()
# Find the plugin libraries
include(MacroFindGStreamerLibrary)
macro(_find_gst_plugins_bad_component _name _header)
find_gstreamer_library(${_name} ${_header} ${GSTREAMER_ABI_VERSION})
set(_GSTREAMER_PLUGINS_BAD_EXTRA_VARIABLES ${_GSTREAMER_PLUGINS_BAD_EXTRA_VARIABLES}
GSTREAMER_${_name}_LIBRARY GSTREAMER_${_name}_INCLUDE_DIR)
endmacro()
foreach(_component ${GStreamerPluginsBad_FIND_COMPONENTS})
if (${_component} STREQUAL "player")
_find_gst_plugins_bad_component(PLAYER gstplayer.h)
elseif (${_component} STREQUAL "webrtc")
_find_gst_plugins_bad_component(WEBRTC webrtc.h)
elseif (${_component} STREQUAL "srtsrc")
_find_gst_plugins_bad_component(SRTSRC mpegts.h)
else()
message (AUTHOR_WARNING "FindGStreamerPluginBad.cmake: Invalid component \"${_component}\" was specified")
endif()
endforeach()
get_filename_component(_GSTREAMER_BAD_LIB_DIR ${GSTREAMER_PLAYER_LIBRARY} PATH)
set(PKG_GSTREAMER_BAD_PLUGIN_DIR ${_GSTREAMER_BAD_LIB_DIR}/gstreamer-${GSTREAMER_ABI_VERSION})
# Version check
if (GStreamerPluginsBad_FIND_VERSION)
if (PKG_GSTREAMER_PLUGINS_BAD_FOUND)
if("${PKG_GSTREAMER_PLUGINS_BAD_VERSION}" VERSION_LESS "${GStreamerPluginsBad_FIND_VERSION}")
message(STATUS "Found gst-plugins-base version ${PKG_GSTREAMER_PLUGINS_BAD_VERSION}, but at least version ${GStreamerPluginsBad_FIND_VERSION} is required")
set(GSTREAMER_PLUGINS_BAD_VERSION_COMPATIBLE FALSE)
else()
set(GSTREAMER_PLUGINS_BAD_VERSION_COMPATIBLE TRUE)
endif()
else()
# We can't make any version checks without pkg-config, just assume version is compatible and hope...
set(GSTREAMER_PLUGINS_BAD_VERSION_COMPATIBLE TRUE)
endif()
else()
# No version constrain was specified, thus we consider the version compatible
set(GSTREAMER_PLUGINS_BAD_VERSION_COMPATIBLE TRUE)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GStreamerPluginsBad DEFAULT_MSG
GSTREAMER_PLUGINS_BAD_VERSION_COMPATIBLE
${_GSTREAMER_PLUGINS_BAD_EXTRA_VARIABLES})

View File

@@ -64,6 +64,8 @@ foreach(_component ${GStreamerPluginsBase_FIND_COMPONENTS})
endif()
endforeach()
get_filename_component(_GSTREAMER_BASE_LIB_DIR ${GSTREAMER_APP_LIBRARY} PATH)
set(PKG_GSTREAMER_BASE_PLUGIN_DIR ${_GSTREAMER_BASE_LIB_DIR}/gstreamer-${GSTREAMER_ABI_VERSION})
# Version check
if (GStreamerPluginsBase_FIND_VERSION)

147
cmake/modules/FindGTK.cmake Normal file
View File

@@ -0,0 +1,147 @@
# - Try to find GTK+ 3.x or 4.x
#
# Copyright (C) 2012 Raphael Kubo da Costa <rakuco@webkit.org>
# Copyright (C) 2013, 2015, 2020 Igalia S.L.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS
# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#[=======================================================================[.rst:
FindGTK
-------
Find GTK headers and libraries.
Optional Components
^^^^^^^^^^^^^^^^^^^
The ``COMPONENTS`` (or ``OPTIONAL_COMPONENTS``) keyword can be passed to
``find_package()``, the following GTK components can be searched for:
- ``unix-print``
Imported Targets
^^^^^^^^^^^^^^^^
``GTK::GTK``
The GTK library, if found.
``GTK::UnixPrint``
The GTK unix-print library, if found.
Result Variables
^^^^^^^^^^^^^^^^
This will define the following variables in your project:
``GTK_FOUND``
true if (the requested version of) GTK is available.
``GTK_UNIX_PRINT_FOUND``
true if the ``unix-print`` component is available.
``GTK_4``
whether GTK 4 was detected
``GTK_3``
whether GTK 3 was detected
``GTK_VERSION``
the version of GTK.
``GTK_SUPPORTS_BROADWAY``
true if the Broadway target is built into GTK.
``GTK_SUPPORTS_QUARTZ``
true if the Quartz target is built into GTK.
``GTK_SUPPORTS_WAYLAND``
true if the Wayland target is built into GTK.
``GTK_SUPPORTS_WIN32``
true if the Windows target is built into GTK.
``GTK_SUPPORTS_X11``
true if the X11 target is built into GTK.
#]=======================================================================]
if (GTK_FIND_VERSION VERSION_LESS 3.90)
set(GTK_PC_MODULE "gtk+-3.0")
set(GTK_PC_UNIX_PRINT_MODULE "gtk+-unix-print-3.0")
set(GTK_4 FALSE)
set(GTK_3 TRUE)
else ()
set(GTK_PC_MODULE "gtk4")
set(GTK_PC_UNIX_PRINT_MODULE "gtk4-unix-print")
set(GTK_4 TRUE)
set(GTK_3 FALSE)
endif ()
find_package(PkgConfig QUIET)
pkg_check_modules(GTK IMPORTED_TARGET ${GTK_PC_MODULE})
set(GTK_VERSION_OK TRUE)
if (GTK_VERSION)
if (GTK_FIND_VERSION_EXACT)
if (NOT("${GTK_FIND_VERSION}" VERSION_EQUAL "${GTK_VERSION}"))
set(GTK_VERSION_OK FALSE)
endif ()
else ()
if ("${GTK_VERSION}" VERSION_LESS "${GTK_FIND_VERSION}")
set(GTK_VERSION_OK FALSE)
endif ()
endif ()
endif ()
# Set all the GTK_SUPPORTS_<target> variables to FALSE initially.
foreach (gtk_target broadway quartz wayland win32 x11)
string(TOUPPER "GTK_SUPPORTS_${gtk_target}" gtk_target)
set(${gtk_target} FALSE)
endforeach ()
if (GTK_VERSION AND GTK_VERSION_OK)
# Fetch the "targets" variable and set GTK_SUPPORTS_<target>.
pkg_get_variable(GTK_TARGETS ${GTK_PC_MODULE} targets)
separate_arguments(GTK_TARGETS)
foreach (gtk_target ${GTK_TARGETS})
string(TOUPPER "GTK_SUPPORTS_${gtk_target}" gtk_target)
set(${gtk_target} TRUE)
endforeach ()
endif ()
if (TARGET PkgConfig::GTK AND NOT TARGET GTK::GTK)
add_library(GTK::GTK INTERFACE IMPORTED GLOBAL)
set_property(TARGET GTK::GTK PROPERTY
INTERFACE_LINK_LIBRARIES PkgConfig::GTK
)
endif ()
# Try to find additional components
foreach (gtk_component ${GTK_FIND_COMPONENTS})
if (NOT "${gtk_component}" STREQUAL unix-print)
message(FATAL_ERROR "Invalid component name: ${gtk_component}")
endif ()
pkg_check_modules(GTK_UNIX_PRINT IMPORTED_TARGET "${GTK_PC_UNIX_PRINT_MODULE}")
if (GTK_FIND_REQUIRED_unix-print AND NOT GTK_UNIX_PRINT_FOUND)
message(FATAL_ERROR "Component unix-print not found")
endif ()
if (TARGET PkgConfig::GTK_UNIX_PRINT AND NOT TARGET GTK::UnixPrint)
add_library(GTK::UnixPrint INTERFACE IMPORTED GLOBAL)
set_property(TARGET GTK::UnixPrint PROPERTY
INTERFACE_LINK_LIBRARIES PkgConfig::GTK_UNIX_PRINT)
endif ()
endforeach ()
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GTK DEFAULT_MSG GTK_VERSION GTK_VERSION_OK)

102
defines.h
View File

@@ -1,102 +0,0 @@
#ifndef VMIX_DEFINES_H
#define VMIX_DEFINES_H
#define APP_NAME "vimix"
#define APP_TITLE " -- Video Live Mixer"
#define APP_SETTINGS "vimix.xml"
#define XML_VERSION_MAJOR 0
#define XML_VERSION_MINOR 2
#define MAX_RECENT_HISTORY 20
#define MAX_SESSION_LEVEL 3
#define MINI(a, b) (((a) < (b)) ? (a) : (b))
#define MAXI(a, b) (((a) > (b)) ? (a) : (b))
#define ABS(a) (((a) < 0) ? -(a) : (a))
#define ABS_DIFF(a, b) ( (a) < (b) ? (b - a) : (a - b) )
#define SIGN(a) (((a) < 0) ? -1.0 : 1.0)
#define SQUARE(a) ((a) * (a))
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
#define EPSILON 0.00001
#define LOG100(val) (50.0/log(10.0)*log((float)val + 1.0))
#define EXP100(val) (exp(log(10.0)/50.0*(float)(val))-1.0)
#define EUCLIDEAN(P1, P2) sqrt((P1.x() - P2.x()) * (P1.x() - P2.x()) + (P1.y() - P2.y()) * (P1.y() - P2.y()))
#define ROUND(val, factor) float( int( val * factor ) ) / factor;
#define SCENE_UNIT 5.f
#define MIN_SCALE 0.01f
#define MAX_SCALE 10.f
#define CLAMP_SCALE(x) SIGN(x) * CLAMP( ABS(x), MIN_SCALE, MAX_SCALE)
#define SCENE_DEPTH 14.f
#define MIN_DEPTH 0.f
#define MAX_DEPTH 12.f
#define DELTA_DEPTH 0.05f
#define DELTA_ALPHA 0.0005f
#define MIXING_DEFAULT_SCALE 2.4f
#define MIXING_MIN_SCALE 0.8f
#define MIXING_MAX_SCALE 7.0f
#define MIXING_LIMBO_SCALE 1.3f
#define MIXING_ICON_SCALE 0.15f, 0.15f, 1.f
#define GEOMETRY_DEFAULT_SCALE 1.4f
#define GEOMETRY_MIN_SCALE 0.4f
#define GEOMETRY_MAX_SCALE 7.0f
#define LAYER_DEFAULT_SCALE 0.6f
#define LAYER_MIN_SCALE 0.4f
#define LAYER_MAX_SCALE 1.7f
#define LAYER_PERSPECTIVE 2.0f
#define LAYER_BACKGROUND 2.f
#define LAYER_FOREGROUND 10.f
#define LAYER_STEP 0.25f
#define APPEARANCE_DEFAULT_SCALE 2.f
#define APPEARANCE_MIN_SCALE 0.4f
#define APPEARANCE_MAX_SCALE 7.0f
#define BRUSH_MIN_SIZE 0.05f
#define BRUSH_MAX_SIZE 2.f
#define BRUSH_MIN_PRESS 0.005f
#define BRUSH_MAX_PRESS 1.f
#define SHAPE_MIN_BLUR 0.f
#define SHAPE_MAX_BLUR 1.f
#define TRANSITION_DEFAULT_SCALE 5.0f
#define TRANSITION_MIN_DURATION 0.2f
#define TRANSITION_MAX_DURATION 10.f
#define ARROWS_MOVEMENT_FACTOR 4.f
#define IMGUI_TITLE_MAINWINDOW ICON_FA_CIRCLE_NOTCH " vimix"
#define IMGUI_TITLE_MEDIAPLAYER ICON_FA_FILM " Player"
#define IMGUI_TITLE_HISTORY ICON_FA_HISTORY " History"
#define IMGUI_TITLE_TOOLBOX ICON_FA_WRENCH " Development Tools"
#define IMGUI_TITLE_SHADEREDITOR ICON_FA_CODE " Code"
#define IMGUI_TITLE_PREVIEW ICON_FA_DESKTOP " Ouput"
#define IMGUI_TITLE_DELETE ICON_FA_BROOM " Delete?"
#define IMGUI_LABEL_RECENT_FILES " Select recent"
#define IMGUI_RIGHT_ALIGN -3.5f * ImGui::GetTextLineHeightWithSpacing()
#define IMGUI_COLOR_OVERLAY IM_COL32(5, 5, 5, 150)
#define IMGUI_COLOR_RECORD 1.0, 0.05, 0.05
#define IMGUI_COLOR_STREAM 0.05, 0.8, 1.0
#define IMGUI_NOTIFICATION_DURATION 2.5f
#ifdef APPLE
#define CTRL_MOD "Cmd+"
#else
#define CTRL_MOD "Ctrl+"
#endif
#define COLOR_BGROUND 0.2f, 0.2f, 0.2f
#define COLOR_NAVIGATOR 0.1f, 0.1f, 0.1f
#define COLOR_DEFAULT_SOURCE 0.7f, 0.7f, 0.7f
#define COLOR_HIGHLIGHT_SOURCE 1.f, 1.f, 1.f
#define COLOR_TRANSITION_SOURCE 1.f, 0.5f, 1.f
#define COLOR_TRANSITION_LINES 0.9f, 0.9f, 0.9f
#define COLOR_APPEARANCE_SOURCE 0.9f, 0.9f, 0.1f
#define COLOR_APPEARANCE_LIGHT 1.0f, 1.0f, 0.9f
#define COLOR_APPEARANCE_MASK 0.9f, 0.9f, 0.9f
#define COLOR_APPEARANCE_MASK_DISABLE 0.6f, 0.6f, 0.6f
#define COLOR_FRAME 0.75f, 0.2f, 0.75f
#define COLOR_FRAME_LIGHT 0.9f, 0.6f, 0.9f
#define COLOR_CIRCLE 0.75f, 0.2f, 0.75f
#define COLOR_CIRCLE_OVER 0.8f, 0.8f, 0.8f
#define COLOR_MIXING_GROUP 0.f, 0.95f, 0.2f
#define COLOR_LIMBO_CIRCLE 0.16f, 0.16f, 0.16f
#define COLOR_SLIDER_CIRCLE 0.11f, 0.11f, 0.11f
#define COLOR_STASH_CIRCLE 0.06f, 0.06f, 0.06f
#endif // VMIX_DEFINES_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 350 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 KiB

After

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 592 KiB

After

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 772 KiB

After

Width:  |  Height:  |  Size: 987 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 383 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 846 KiB

After

Width:  |  Height:  |  Size: 745 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 KiB

After

Width:  |  Height:  |  Size: 694 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 582 KiB

After

Width:  |  Height:  |  Size: 496 KiB

Some files were not shown because too many files have changed in this diff Show More