How to link another folder/module in cmake?

I’m working on a new filter to make a graphical part to the gps text one but I’m now stuck on one probably cmake-related thing and can’t figure it out with google:

I made the filter_gpsgraphic.cpp in the \src\modules\qt folder which with basic code compiles ok (I’m using cmake with ninja). But when trying to include and re-use the gps parsing function from the \src\modules\xml folder I get “undefined reference” errors to all the functions declared in gps_parser.h defined in the gps_parser.c.

In my .cpp I did #include "../xml/gps_parser.h" to include the header – Is this correct/preferred? Or should I change the makefile to link to the xml folder directly? (how do I do this?)

The gps_parser.h is found and parsed by cmake as I had some errors regarding the libxml inside that I fixed by adding this line PkgConfig::xml to the CMakeList in the qt folder (under the target_link_libraries() call). – is this ok? Or should I somehow include it indirectly from the xml folder/mltxml.lib?

But it seems that it doesn’t find the gps_parser.c objects so at this point I think I should link from the QT to the XML folder (or the resulting mltxml library) somehow using the CMakeList but I have no idea how. I tried adding mlt mltxml xml and some other combinations of this in the same target_link_libraries but I still get this output when building:

$ cmake --build .
[1/3] Automatic MOC for target mltqt
[2/2] Linking CXX shared module out\lib\mlt\libmltqt.dll
FAILED: out/lib/mlt/libmltqt.dll
cmd.exe /C "cd . && C:\Qt\Tools\mingw810_64\bin\g++.exe -Ic:/Projects/Shotcut/include -DHAVE_STRUCT_TIMESPEC  -Lc:/Projects/Shotcut/lib -Lc:/Projects/Shotcut -shared -o out\lib\mlt\libmltqt.dll -Wl,--major-image-version,0,--minor-image-version,0 src/modules/qt/CMakeFiles/mltqt.dir/mltqt_autogen/mocs_compilation.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/factory.c.obj src/modules/qt/CMakeFiles/mltqt.dir/producer_qimage.c.obj src/modules/qt/CMakeFiles/mltqt.dir/producer_kdenlivetitle.c.obj src/modules/qt/CMakeFiles/mltqt.dir/common.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/graph.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/qimage_wrapper.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/kdenlivetitle_wrapper.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/filter_audiolevelgraph.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/filter_audiowaveform.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/filter_qtext.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/filter_qtblend.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/filter_qtcrop.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/producer_qtext.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/transition_qtblend.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/consumer_qglsl.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/filter_typewriter.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/typewriter.cpp.obj src/modules/qt/CMakeFiles/mltqt.dir/transition_vqm.cpp.obj  out/lib/libmlt++-7.dll.a  out/lib/libmlt-7.dll.a  -lm  C:/Qt/5.15.2/mingw81_64/lib/libQt5Xml.a  C:/Qt/5.15.2/mingw81_64/lib/libQt5Svg.a  C:/Qt/5.15.2/mingw81_64/lib/libQt5Widgets.a  C:/Qt/5.15.2/mingw81_64/lib/libQt5Gui.a  C:/msys64_v2/mingw64/lib/libxml2.dll.a  C:/Qt/5.15.2/mingw81_64/lib/libQt5Core.a  -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 && cd ."
src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj:filter_gpsgraphic.cpp:(.text+0x574): undefined reference to `binary_search_gps(gps_private_data, long long, char)'
src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj:filter_gpsgraphic.cpp:(.text+0x63f): undefined reference to `binary_search_gps(gps_private_data, long long, char)'
src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj:filter_gpsgraphic.cpp:(.text+0xbb9): undefined reference to `binary_search_gps(gps_private_data, long long, char)'
src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj:filter_gpsgraphic.cpp:(.text+0xc84): undefined reference to `binary_search_gps(gps_private_data, long long, char)'
src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj:filter_gpsgraphic.cpp:(.text+0x10fe): undefined reference to `datetimeXMLstring_to_mseconds(char const*, char*)'
src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj:filter_gpsgraphic.cpp:(.text+0x11ad): undefined reference to `datetimeXMLstring_to_mseconds(char const*, char*)'
src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj:filter_gpsgraphic.cpp:(.text+0x12a0): undefined reference to `mseconds_to_timestring(long long, char*, char*)'
src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj:filter_gpsgraphic.cpp:(.text+0x12c2): undefined reference to `mseconds_to_timestring(long long, char*, char*)'
src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj:filter_gpsgraphic.cpp:(.text+0x1385): undefined reference to `process_gps_smoothing(gps_private_data, char)'
src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj:filter_gpsgraphic.cpp:(.text+0x1448): undefined reference to `recalculate_gps_data(gps_private_data)'
src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj:filter_gpsgraphic.cpp:(.text+0x1491): undefined reference to `mseconds_to_timestring(long long, char*, char*)'
src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj:filter_gpsgraphic.cpp:(.text+0x1776): undefined reference to `xml_parse_file(gps_private_data)'
src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj:filter_gpsgraphic.cpp:(.text+0x1835): undefined reference to `get_first_gps_time(gps_private_data)'
src/modules/qt/CMakeFiles/mltqt.dir/filter_gpsgraphic.cpp.obj:filter_gpsgraphic.cpp:(.text+0x18e6): undefined reference to `get_last_gps_time(gps_private_data)'
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.

Any ideas?

You cannot reuse functions from another module. Modules are isolated. You will need to first use the gpstext filter to add properties to the frame. Then, in the new filter read these properties from the frame to get access to the GPS data. Instead of putting all of the GPS data on each frame, think about the data only needed for the frame’s time. You will need to preface the frame property name with the name of your filter plus a . separator. You can choose to make the frame property a data property that is another mlt_properties object, and you can make a hierarchy. It is also possible to make list-style properties using numeric keys, which is a pattern that can be found elsewhere in the code.

Also, consider a completely different approach. The next version of Shotcut will include direct support for Glaxnimate and Lottie vector animations including launching Glaxnimate for editing. Both of these are JSON formats. You could make something outside of MLT that generates a JSON file that can be edited with Glaxnimate. Then, it is much easier to change the color, stroke, positioning, and size of these graphics. You can write the utility first as standalone Qt program, and then we figure out a way to include it in Shotcut’s GPS Text filter UI such as a Generate Animation… button.

1 Like

Oh, this is unfortunate, the way I was thinking of making the graphic part is to be completely independent of the text one, a separate filter to add with its own settings (including file loading), especially as I don’t want to limit to only one gps text per clip.

But I’m confused, if a filter from any other module would want to load a file using xml it wouldn’t be possible?

The problem I see here is that for all the graphs I need the entire GPS information from the file (to draw the full GPS track for example). And also I’d need to pass even more data to be able to show the “current” live location on that track.

I probably should have mentioned what exactly I want the filter to do:
-main goal: draw a 2D line of the gps track with a dot that tracks current location
-secondary: have simple graphs of the altitude, heart rate, speed, etc from the gps file
I don’t plan on any complex drawings of speedometer and animating hearts to complement the currently active gps-text value (for something like this I think your frame idea would be perfect as I only need the current value, no need for other context).

I’ll need to look into this as I haven’t used it yet and don’t understand it’s capabilities, but it sounds that
using the frame as a medium to transfer is very temporary/fleeting, is there another way to share data between active filters in a more “permanent way”? Thinking more of a shared data instead of passing around.
Is there a way to “make data available” to other filters instead of relying on frames? Basically instead of passing stuff around, I want to share a pointer to a stucture (the gps text’s private_data struct would have all the information I’d need to read, or even just the processed gps data array from it) and read what I need from it.

Or, another idea, can I allocate some data and have it exist independent of any specific filter or module in such a way that I can read it from everywhere? I’m thinking of moving the gps file load and process to a separate filter that only does parsing and processing only - then the gps text and the gps graph could only select one already loaded-file from a drop-down-like list and only work on the final filter-specific part (drawing text or graphs). This would also help with RAM usage as I would be able to reuse the same file in different sub-clips without having to load it in RAM for every instance. Hmm, this one got a bit more complicated than what I planned when I started writing the paragraph.

Those look awesome but I’m afraid I’m thinking way less fancy for my graphical part, I did some small tests with the graph.cpp graph implementation that’s already here as it’s quite awesome and does almost everything I had in plan for the altitude, speed, hr graphs (including positioning, color/colors, thickness and orientation; I also see there’s 3 types of graphs and I might add an option to choose between them) it’s only missing a way to draw 2d data independently but I think I can work that one out quite easily from what’s already implemented.

No, it is possible but not using any functions from the libmltxml module. If you add a filter in the qt module, it can use Qt’s XML functions. I did mention that when you first started working on gpstext. If it is another module, then it depends on the policy around dependencies.

is there another way to share data between active filters in a more “permanent way”?

Only by embedding one service in another as you have done in gpstext. You can make your new filter embed gpstext similar to how you make gpstext embed qtext. Then, you can put a pointer into a data property to retrieve it from the parent. You can also add a property to gpstext to tell it not to load a text filter as you will only use it for parsing. For example, you can make it so that when filter_gpstext_init's arg is NULL.

I bet now you are wishing you had used Qt’s XML parser and made gpstext a part of the qt module. I do not disagree. Feel free to make that change. However, do be aware that Qt has some deprecated XML classes, and you need to avoid those.

can I allocate some data and have it exist independent of any specific filter or module in such a way that I can read it from everywhere?

No

Ok, this makes sense now.

Indeed I do. I don’t remember exactly why I chose libxml over qt, I think I might have had an easier time compiling in xml and/or qt felt way more complicated.

I’ll try this method first as the embed one I’m thinking won’t work as is and will need changes anyway in the original filter (gps processing depends on a lot of info from the UI so I’d need to duplicate a lot of functionality to pass by what I need).