tinyzimmer / go-gst

Gstreamer bindings and utilities for golang
GNU Lesser General Public License v2.1
130 stars 37 forks source link

Wrong Type for video format #10

Closed Garionion closed 3 years ago

Garionion commented 3 years ago

Hey, I am using rawvideoparse and one thing, I have to do, is setting the property format. However, when i try to set this, all i get is Invalid type gint for property format i set this with:

err = videoparse.SetProperty("format", video.FormatBGRA)
if err != nil {
    log.Println(err)
}

(i have to set this as a property, not as capability)

I also have a similar problem with the framerate. it needs to be a GstFraction, so i used

err = videoparse.SetProperty("framerate", gst.Fraction(50,1))

and all i get is Unable to perform type conversion

Am I doing something wrong?

tinyzimmer commented 3 years ago

I'm not surprised about the fraction, and I'll have to think on that. One thing you could do though is see if it just takes a string of "50/1". It's probably a similar issue with the video format (which is an Enum).

The SetProperty is actually something provided by the glib bindings and it is really only meant to handle go types from my understanding. It remains mostly in-tact from when I originally forked it from gotk3, but I've made small changes.

The fraction makes sense because that's just a GoType helper that is also not fully implemented. I kinda hoped the enum would work since well...it's just an integer.

What this tells me is SetProperty over in the glib stuff needs to be smarter. And I'll have to think on the cleanest way to accomplish that. Off the top of my head I'm wondering if the wrapped Go objects should export some internal method for handling type conversation in that context.

Garionion commented 3 years ago

One thing you could do though is see if it just takes a string of "50/1".

Invalid type gchararray for property framerate … nope -.-

tinyzimmer commented 3 years ago

I'll hack on it tonight and post here if I come up with anything

tinyzimmer commented 3 years ago

Alright, I have it fixed at least for these use cases (and a few others I came across, e.g. caps, audioformats) on v0.2.14. There are other GstValues I don't have this implemented for yet (i.e. ranges), but I'll tackle those either tonight or over the weekend.

package main

import (
    "fmt"

    "github.com/tinyzimmer/go-gst/gst"
    "github.com/tinyzimmer/go-gst/gst/video"
)

func main() {
    gst.Init(nil)

    videoparse, err := gst.NewElement("rawvideoparse")
    if err != nil {
        panic(err)
    }
    videoparse.SetProperty("format", video.FormatBGRA)
    videoparse.SetProperty("framerate", gst.Fraction(50, 1))

    format, err := videoparse.GetProperty("format")
    if err != nil {
        panic(err)
    }
    framerate, err := videoparse.GetProperty("framerate")
    if err != nil {
        panic(err)
    }

    fmt.Println("format:", format)
    fmt.Println("framerate:", framerate)
        // format: BGRA
        // framerate: 50/1
}

Example of it working the same for caps if you are interested

package main

import (
    "fmt"

    "github.com/tinyzimmer/go-gst/gst"
)

func main() {
    gst.Init(nil)

    capsfilter, err := gst.NewElement("capsfilter")
    if err != nil {
        panic(err)
    }
    capsfilter.SetProperty("caps", gst.NewCapsFromString("video/x-raw,framerate=50/1"))

    caps, err := capsfilter.GetProperty("caps")
    if err != nil {
        panic(err)
    }

    fmt.Println("caps:", caps)
        // caps: video/x-raw, framerate=(fraction)50/1
}
Garionion commented 3 years ago

Thank you very much, works like a charm :)

tinyzimmer commented 3 years ago

Glad to hear 😄. I'll keep the issue open to remind myself about the other values.

Garionion commented 3 years ago

so … i also need some more types e.g. from GstBaseTextOverlay but those aren't present in this library (yet), at least i did not find those. I would like to help, but I have no idea how or even where exactly to put them.

tinyzimmer commented 3 years ago

Which ones do you need. Those base-text-overlay-* ones? I am equally at a loss at where the hell those would even go...

tinyzimmer commented 3 years ago

Can you provide some more context? I have ideas but I want to understand the use case better.

Garionion commented 3 years ago

Sure. We have (soon-ish) a ski-jumping contest which we live stream. We want to overlay graphics (current jumper, his evaluation …) on our Stream. Last Year I already used some gstreamer, but only to play graphic clips and overlay my python-generated images. As I get some values later than others, it would be nice to be able to start a graphic playout and update some text on-the-fly. therefore i decided to go full in on gstreamer and because of hacking in gstreamer in my python code it wasn't a nice codebase anymore, i decided to rewrite it (again), but this time in go ^^

I only need to overlay text and images. I could also use gst.NewPipelineFromString() since the types/fields/values i need to change are plain boolean and strings

to your question before that: as of now, i would (for the text overlay) only use Base-text-overlay-halign and Base-text-overlay-valign and (for the image) Gdk-pixbuf-positioning-mode .

tinyzimmer commented 3 years ago

Could you give me an example of code producing an error? I'm not able to play just this moment, but it looks like it could be a similar fix to the enums. It would probably end up in a new pango package, just a very small package.

Garionion commented 3 years ago

Yes, sure

package main

import (
    "fmt"
    "github.com/tinyzimmer/go-gst/gst"
)

func main() {
    gst.Init(nil)

    text, err := gst.NewElement("textoverlay")
    if err != nil {
        fmt.Println(err)
    }
    setPropertyWrapper(text,"halignment", 0) //left
    setPropertyWrapper(text,"valignment", 2) //top
    setPropertyWrapper(text,"text", "hello world")

    image, err := gst.NewElement("gdkpixbufoverlay")
    if err != nil {
        fmt.Println(err)
    }
    setPropertyWrapper(image,"positioning-mode", 1) // pixels-absolute

}

func setPropertyWrapper(element *gst.Element, name string, value interface{}) {
    err := element.SetProperty(name, value)
    if err != nil {
        fmt.Printf("Failed to set Property: %v\n", err)
    }
}

with exception of the property text all SetProperty produce an error:

go run ./main.go
Failed to set Property: Invalid type gint for property halignment
Failed to set Property: Invalid type gint for property valignment
Failed to set Property: Invalid type gint for property positioning-mode
tinyzimmer commented 3 years ago

Thanks for the examples. I ended up having to reset my PC last night from some update hell, but I'll try to take a swing at it some time today.

tinyzimmer commented 3 years ago

So what I kinda expected is that the headers related to specific plugins are not included in packages. So even if there was a good place to bind them here, you'd have a hard time finding the headers you need.

What's happening here is you are trying to set enums. The workarounds I did for fractions and caps and stuff are a separate matter, but here is the issue with the glib bindings trying to allocate them as integers instead of enums. You can get around this explicitly in your code like this:

package main

import (
    "fmt"

    "github.com/tinyzimmer/go-glib/glib"
    "github.com/tinyzimmer/go-gst/gst"
)

func main() {
    gst.Init(nil)

    text, err := gst.NewElement("textoverlay")
    if err != nil {
        fmt.Println(err)
    }
    setPropertyWrapper(text, "halignment", 0) //left
    setPropertyWrapper(text, "valignment", 2) //top
    setPropertyWrapper(text, "text", "hello world")

    image, err := gst.NewElement("gdkpixbufoverlay")
    if err != nil {
        fmt.Println(err)
    }
    setPropertyWrapper(image, "positioning-mode", 1) // pixels-absolute

}

func setPropertyWrapper(element *gst.Element, name string, value interface{}) {
    var err error
    switch x := value.(type) {
    case int:
                // Obviously not every integer you work with would be an enum, but this
                // is an example of allocating the GValue manually instead of letting the
                // bindings guess.
                //
                // SetProperty is basically just doing a switch statement like this and guessing
                // on go types, while the gstreamer bindings implement the conversion for objects
                // like fractions, ranges, caps, etc. that the glib bindings defer to instead in those cases
        val, err := glib.ValueInit(glib.TYPE_ENUM)
        if err != nil {
            fmt.Printf("Failed to allocate GValue")
            return
        }
                val.SetEnum(x)
        err = element.SetPropertyValue(name, val)
    default:
        err = element.SetProperty(name, x)
    }
    if err != nil {
        fmt.Printf("Failed to set Property: %v\n", err)
    }
}

But over in IRC land I find out now there is a gst_util_set_object_arg() that I should absolutely be binding. It can take a string and will handle the conversion and stuff internally if I am understanding correctly. I'll export a method for it a new tag soon. Probably as a method on the Object like Object.SetArg(name, value string) (and everything that gets promoted from it - elements, pipelines, etc.).

tinyzimmer commented 3 years ago

But....what's important for now, and probably all future issues like this. Is as of v0.2.17 you can now do this:

package main

import (
    "fmt"

    "github.com/tinyzimmer/go-gst/gst"
)

func main() {
    gst.Init(nil)

    text, err := gst.NewElement("textoverlay")
    if err != nil {
        fmt.Println(err)
    }
    text.SetArg("halignment", "0")
    text.SetArg("valignment", "2")
    text.SetArg("text", "hello world")

    fmt.Println(text.GetProperty("halignment"))
    fmt.Println(text.GetProperty("valignment"))
    fmt.Println(text.GetProperty("text"))

    image, err := gst.NewElement("gdkpixbufoverlay")
    if err != nil {
        fmt.Println(err)
    }
    image.SetArg("positioning-mode", "1")

    fmt.Println(image.GetProperty("positioning-mode"))
}

Which produces:

0 <nil>
2 <nil>
hello world <nil>
1 <nil>

The caveat with SetArg is you don't get an error if anything didn't work. You'd either just want to trust it, or do an extra retrieval to make sure it got set correctly.

Garionion commented 3 years ago

ok, wow, this is great :smile: SetArg() solves all my problems

tinyzimmer commented 3 years ago

Great! Will probably close this issue then since I've implemented the rest of the custom GstValues, and SetArg should handle all high level use cases.