Any plans for gpx location/speed overlay feature?

I would say now the bash from Git for Windows installer is a bare bones way if you only want to use the mingw compiler from Qt Creator. If you never need anything more in your scripting or shell environment I strongly suggest to use msys2 with its mingw64 shell environment. (the ability of msys2 to have different shell environments is a little confusing to beginners; so I give you this hint)

1 Like

I got it working (to recreate the libmltplus.dll) after brian’s suggestion and a bit of work updating paths and stuff. Thank you for that, when I followed the instructions on the page I was not aware that shotcut and mlt-framework were completely different projects so I didn’t think I needed that part regarding dependecies.

msys2 looks interesting, but I’ll stop here with installing extra stuff, at this point the work trying to figure out how to make it happen wouldn’t be worth skipping 1 command and 1 manual copy-paste.

1 Like

Good to hear you had success.

Is the GPS data stored in a separate file? Or is it embedded in the video file somehow?

If stored in a separate file, you might need to make a new filter to read the data from the file. But how will you synchronize the gps with the video (unless they both started recording at exactly the same instant)?

If stored in the video file, you will need to look at the avformat module to see how to extract the embedded data.

1 Like

I think the separate .gpx/.tcx file is the more widely used and non-proprietary method so that’s what I want to work with even if syncing is a big issue.

For Gopros in particular they embed the location (if enabled) + other metadata (gyro, accelerometer + others) in a particular way called GPMF and while that sounds nice and avoids the sync issues part, well, I’m not a big fan of it (in practice, besides the battery penalty, the gps accuracy on my gopro is very hit or miss, and even when you get a good fix it’s usable, but not the best - using a phone to track gps is considerably better, or in my case a fitness watch to not care about battery).
Supporting gopro’s embedded metadata could be a nice addition to shotcut but it’s not a priority for me.

1 Like

I think that would be appreciated by users if it were possible. If you’re not looking at this route I guess this means GoPro users (who decide on inclusion of the metadata) will then have to use something like https://goprotelemetryextractor.com as an additional step?

1 Like

OK. So thinking out loud here, I think your approach would be:

  • Make a new filter in the MLT XML module
  • The filter would have the following parameters:
    • gpx file selection (open a file dialog)
    • start time (allow the user to choose what time in the file matches the beginning of the video)
    • a rectangle selection area to choose where the information be displayed (similar to Text: simple)
    • a way to choose the data to be displayed. Could be a drop-down box or maybe a text box with keywords like Text: Simple)
  • For each video frame, look up the information from the file that best matches the time of the frame (or maybe interpolate between two data points)
  • Encapsulate the existing text filter inside this new filter to do the actual text rendering (see how dynamictext filter works)
1 Like

Yes.

Yes, this is roughly what I had in mind. Let’s see if I can make some of these steps work :smiley:

1 Like

I start every new edit with a time-sync of my multiple simultaneous shots of the same scene. (The cameras are each started and stopped manually.)
It is tedious, but not impossible.

Sync is done by dragging clips on the Timeline.

I am concerned about starting a situation where some types of clip are synced one way, and clips of other types are synced in a different way.

@brian, could “Time Offset” be a Property of the gpx clip, defaulting to the Timeline location where the clip is initially dropped, and then changing either by manual input or by moving the clip/

Second Idea:
Could the Time Offset (as above) be not Timeline-relative, but clip-relative, defaulting to zero, unchanged by moving the clip, manually changed in Properties, and specifying the relatave actual start of gpx data relative to the visual-on-the-Timeline position of the gpx clip?

1 Like

This is assuming separate data, as in…

1 Like

I see. I definitely think bringing .gpx into Shotcut would be a bonus even as metadata alone, certainly interesting.

In regards to Dashware and double rendering, one thing that I do is, bring in the metadata, choose the required charts placed upon a black (green-screen is possible) background and export. Bring that video and match with the related GoPro video into Shotcut in conjunction with Chroma. This at least means I can manage the video quality my way than relying on Dashware’s output.

4 Likes

That’s a good idea, didn’t think of it but it’s a great workaround.

1 Like

I’ve only done it on one or two occasions but yes it works well. Looking at the view-count of this thread I definitely think there are users interested in wanting to add metadata overlays to their videos.

1 Like

Hey, I’ve been working on this for the past week and have a basic working version of the filter but I have a few technical questions:

  1. what is the purpose of the .yml file? do I need to update it with every single element I add in the ui.qml?
  2. why are there so many places where default values are set for UI elements (the filter_init, the ui.qml in filter.isNew and in the .yml file). I see the filter_init is the one that takes precedence, are others necessary?
  3. I want to use the ClockSpinner element used in the timer filter but I get this error and the UI fails to render at all. I suspect I’m missing the definition file for it but I have imported all the stuff the timer/ui.qml file imports yet I still have the error:
    [Warning] <> file:///C:/Projects/Shotcut/share/shotcut/qml/filters/dynamicgpstext/ui.qml:265:13: ClockSpinner is not a type
  4. how can I call a destructor on filter deletion to free my malloc-ed data? I’m using the filter_dynamictext.c code as the base and I allocate the array in the filter_process() function after the user loads a .gpx file but I keep this data between the calls so I don’t re-read the file for every frame/ui refresh.
  5. how is filter-data and memory shared between different instances of a filter? If I add the filter twice on a clip (or have 2 overlapping clips on different video tracks) they reuse the same pointer I dynamically allocated for gps data, can I change this behaviour so each filter is a completely different instance with it’s own memory and init/destructors?
    5.a) a little variation on 5., if I split the clip in 2 with the filter applied, is it different? or what if
    b) I apply the filter on 2 completely different clips, are they still shared?

for 5) I tried debuging a bit with logs and it seems like the memory is reused with the only difference being the UI that sends different data (via get_property) to the backend. Is this correct for all cases? Can I change this behaviour with a flag or something? Or can I at least restrict it so it can never have overlapping filters at the same time? (by overlapping I mean both: applying the filter twice on the same clip and having 2 different clips overlapping in 2 video tracks)

1 Like

The properties that you use in the Shotcut ui.qml file should be documented in the MLT .yml file. But you should not think of those as being directly connected. MLT is a stand-alone library and test application that is used by many applications (Shotcut is only one of many).

The .yml file serves as the documentation for the service. It is used in many ways including:

  • Developers can visually inspect it to understand how a service works
  • We use the YML to generate the online documentation: MLT - Documentation
  • Some applications query the metadata at runtime to determine what properties to expose in their user interface (Shotcut chose not to do this).

In general, for a feature to be accepted in MLT, it must have appropriate metadata documentation in a .yml file.

You can test your metadata using the melt command with the “-query filters” parameter:
https://mltframework.org/docs/melt/

In MLT, filter_init ensures that the filter is created with sane defaults no matter what application is using it. The .yml file is documentation only and it only identifies what the default values are - it does not set them.

In Shotcut, ui.qml is where Shotcut sets initial values based on the specific needs/preferences for that application.

If you want a QML component to be shared among multiple modules, then it will have to moved into the shared library and exposed to the entire QML engine:

Don’t forget to add it to the index:

And also make sure that the timer filter is modified to import it from the Controls module.

There are two options

  1. Save it as a property in the filter using mlt_properties_set_data() including a destructor. Property names that start with an underscore are not serialized in XML. So basically, any property that is not part of the public interface should start with an underscore. Here is an example, but there are many more if you search: mlt/src/modules/plus/producer_blipflash.c at 2c24715b186d53348d164c4d3984a6ea19ed9c85 ¡ mltframework/mlt ¡ GitHub

  2. Create a private struct for your filter subclass and add as many private members as you want. Create the private structure and store it on the “child” pointer of the parent filter. Then, destroy it when the filter is closed with the filter_close callback. This is my preferred method because it is more object oriented and is easy to expand. Here is an example filter that you can insepect to see how it works: mlt/src/modules/plus/filter_dynamic_loudness.c at master · mltframework/mlt · GitHub

Data is not shared between instances of a filter. For every filter that is created, filter_init() is called again and a new instance is created. The two instances do not know about each other in any way. In Shotcut, if the user splits a clip, we create a new instance of all filters and copy the parameters over to make them match.

Why would you restrict this? If the user has a need to apply the filter twice, then we should let them.

1 Like

Thank you for the detalied answers.

For the last question I don’t necessarily want to restrict it but the way it is now, the data is somehow shared, this is what I mean:

in my backend file (dynamicgpstext.c) I have 2 file-level globals:

static char last_filename[255]; 	
static gps_point* gps_points = NULL;

the logic I have is: if the user opens a new .gpx/.tcx file (let’s say loc1.gpx), and the filename differs from the last one (at the begining it’s \0) then I free the previous gps_points and proceed to read the new file that the user selected and re-populate the gps_points array (at this point we have last_filename=“loc1.gpx”).
This last_filename comparison happens every time filter_process() is called (I compare it with mlt_properties_get()) so it needs to hit the “cached” file’s gps_points all the time.
And this works ok if I have only 1 “gps text” filter in my project.

Now: if I select another clip in timeline and add my gps text filter to that one too (not copy paste, I click +, select the filter from the list) and then open a different gpx file (“loc2.gpx”) it gets compared to last_filename which is “loc1.gpx” (this is why I assumed memory is shared between the filter instances), check fails, old gps_points gets free’d and reads loc2.gpx and populates gps_points with the new file’s contents.

If i move the playhead to clip1 this happens again, loc1.gpx != last_filename and the file is re-read.

PS: I know global vars are frowned upon but considering I need the gps_points to survive between calls and I use the array in almost all the functions in the file I think it’s the best alternative. It has file level scope so it doesn’t interfere with anyone else’s code.

1 Like

File level globals will not be accepted and are causing the behavior you describe. You will need to use one of the two options I offered to your question #4 above. All state for a filter instance needs to be saved with that filter instance and destroyed with that filter instance. That can be accomplished by saving the state in the properties or using the child parameter for inheritance (as I described above).

1 Like

Oh, well that explains it.

1 Like

Another question (sorry for asking so many, I usually can google my way out but not much luck on these).

Can I arbitrarrily update stuff in UI from within the .c file using their ui.qml id (or something else)? I can do it the other way by setting a filter.set inside an onClicked or onChanged then reading that property in .c but for the .c -> UI direction I don’t know how. Is the Component.onCompleted used for this? I tried to do an ID.text = filter.get(‘stuff’) but it doesn’t seem to work.

Also, is there a better way to do this or is it ok like this: I have a button that when clicked it syncs the start time of the gps entries to the start of video time. So I do a filter.set(‘sync_gps’, 1) in the button’s onClicked. In the .c I read that value every filter_process and if it’s 1 I do my sync and set it back to 0.

1 Like

No. You can update properties in the backend, then there are many things that will update the video including while playing or things asking the engine to repaint. You can receive frame updates in QML and update your UI from that. See in other filters

    Connections {
        target: producer
        onPositionChanged: {
            setControls()
        }
    }
1 Like

It sounds like you are trying to use the property system to send a command or call a function. That is not the intended use. The properties should reflect the state of the filter. For your sync_gps property, how will a user set that on the command line using melt? It won’t really work. You should probably have a property that represents the start time or offset and have the UI set the value.

I recommend that during development you stop using Shotcut as your UI and start using melt. Using melt will help you work within the boundaries of the framework as it was designed. After you have the filter working with melt, then it will be obvious what to do in Shotcut to expose it there.

1 Like