GithubHelp home page GithubHelp logo

go-gst / go-gst Goto Github PK

View Code? Open in Web Editor NEW
116.0 4.0 54.0 4.97 MB

Gstreamer bindings and utilities for golang

License: GNU Lesser General Public License v2.1

C 1.79% Go 98.19% Shell 0.02%
bindings cgo golang-package gst gstreamer

go-gst's Introduction

banner

go-gst: Go bindings for the GStreamer C libraries

godoc reference GoReportCard

See pkg.go.dev references for documentation and examples.

Please make sure that you have followed the official gstreamer installation instructions before attempting to use the bindings or file an issue.

The bindings are not structured in a way to make version matching with GStreamer easy. We use github actions to verify against the latest supported GStreamer version that is supported by the action https://github.com/blinemedical/setup-gstreamer. Newer GStreamer versions will also work. Always try to use the latest version of GStreamer.

Requirements

For building applications with this library you need the following:

  • cgo: You must set CGO_ENABLED=1 in your environment when building.
  • gcc and pkg-config
  • GStreamer development files (the method for obtaining these will differ depending on your OS)
    • The core gst package utilizes GStreamer core
    • Subpackages (e.g. app, video) will require development files from their corresponding GStreamer packages
      • Look at pkg_config.go in the imported package to see which C libraries are needed.

Windows

Compiling on Windows may require some more dancing around than on macOS or Linux. First, make sure you have mingw and pkgconfig installed (links are for the Chocolatey packages). Next, go to the GStreamer downloads page and download the latest "development installer" for your MinGW architecture. When running your applications on another Windows system, they will need to have the "runtime" installed as well.

Finally, to compile the application you'll have to manually set your PKG_CONFIG_PATH to where you installed the GStreamer development files. For example, if you installed GStreamer to C:\gstreamer:

PS> $env:PKG_CONFIG_PATH='C:\gstreamer\1.0\mingw_x86_64\lib\pkgconfig'
PS> go build .

Quickstart

For more examples see the examples folder here.

// This is the same as the `launch` example. See the godoc and other examples for more 
// in-depth usage of the bindings.
package main

import (
    "fmt"
    "os"
    "strings"

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

func main() {
    // This example expects a simple `gst-launch-1.0` string as arguments
    if len(os.Args) == 1 {
        fmt.Println("Pipeline string cannot be empty")
        os.Exit(1)
    }

    // Initialize GStreamer with the arguments passed to the program. Gstreamer
    // and the bindings will automatically pop off any handled arguments leaving
    // nothing but a pipeline string (unless other invalid args are present).
    gst.Init(&os.Args)

    // Create a main loop. This is only required when utilizing signals via the bindings.
    // In this example, the AddWatch on the pipeline bus requires iterating on the main loop.
    mainLoop := glib.NewMainLoop(glib.MainContextDefault(), false)

    // Build a pipeline string from the cli arguments
    pipelineString := strings.Join(os.Args[1:], " ")

    /// Let GStreamer create a pipeline from the parsed launch syntax on the cli.
    pipeline, err := gst.NewPipelineFromString(pipelineString)
    if err != nil {
        fmt.Println(err)
        os.Exit(2)
    }

    // Add a message handler to the pipeline bus, printing interesting information to the console.
    pipeline.GetPipelineBus().AddWatch(func(msg *gst.Message) bool {
        switch msg.Type() {
        case gst.MessageEOS: // When end-of-stream is received flush the pipeling and stop the main loop
            pipeline.BlockSetState(gst.StateNull)
            mainLoop.Quit()
        case gst.MessageError: // Error messages are always fatal
            err := msg.ParseError()
            fmt.Println("ERROR:", err.Error())
            if debug := err.DebugString(); debug != "" {
                fmt.Println("DEBUG:", debug)
            }
            mainLoop.Quit()
        default:
            // All messages implement a Stringer. However, this is
            // typically an expensive thing to do and should be avoided.
            fmt.Println(msg)
        }
        return true
    })

    // Start the pipeline
    pipeline.SetState(gst.StatePlaying)

    // Block and iterate on the main loop
    mainLoop.Run()
}

Contributing

If you find any issues with the bindings or spot areas where things can be improved, feel free to open a PR or start an Issue. A few things to note:

  • Compilation times are insanely slow when working within the bindings.
  • There are a lot of quirks that make generators difficult to deal with for these bindings, so currently everything is hand written. If you have a need for a new binding, feel free to open an issue or create a PR. Writing CGo bindings is not as hard as it seems. (Take a look at #53 for inspiration)
  • More examples would be nice.
  • Support for writing GStreamer plugins and custom elements via the bindings is there, but not well documented.
  • go-gst follows semantic versioning, so it should always be forward compatible for minor versions. If we find an issue in a function and the only way to fix it is to change the function signature, we will break it in a minor version. That way you "get forced" to use the fixed version.

Please make sure that you use the latest version of GStreamer before submitting an issue. If you are using an older version of GStreamer, please try to reproduce the issue with the latest version before submitting an issue.

go-gst's People

Contributors

abrbird avatar aet avatar atishnazir avatar biglittlebigben avatar brucekim avatar danjenkins avatar frostbyte73 avatar jwmwalrus avatar metal3d avatar nassah221 avatar nielsavonds avatar robmcq avatar rswilli avatar solganik avatar thehamsta avatar tinyzimmer avatar vshashi01 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

go-gst's Issues

strange memory leak

Hi, I finally found where there was a major leak in my code. The problem with previous tests was that they operated in one frame of the flow and did not leave the pipeline to run on its own, take a look at the next test. Its structure is very simple - there is an outer loop that calls the bench function 3 times, and the bench function itself, which creates, starts and stops the pipeline 50 times. Inside the function bench() it seems that everything is in order - while it is running, the memory almost does not flow, no matter how large the number of repetitions you set. However, when the function bench ends, it does not return memory and its next call is simply allocated on top, taking up as much as the previous one. But this only happens between iterations 1 and 2; further iterations do not increase consumption, but memory also does not return. Now look at the commented part of the code - there, instead of immediately ending the pipeline, I tell it to end asynchronously in 500ms, taking into account a pause of 100ms, during which time 5 pipelines will have time to start, i.e. Now we have 5 working pipelines all the time, everything else is the same. Here we have different story - if before, we had ~40 MB at the first iteration, ~ 85 MB at the second, and this memory was not returned further no matter what tricks, now we have 156 MB and 232 MB, respectively. A further increase in simultaneously running pipelines leads to a proportional increase in the amount of non-recoverable memory. Thus, we have the formula: non-recoverable memory = (consumption of one pipeline * max number of simultaneously existing pipelines) * 2. And I repeat - memory is not returned, never. With a pipeline like this, this may not seem very important, but in reality the parallelism can be higher, and the pipelines are much more complex (replace VP8 to VP9, for example). How to deal with this leak?

Steps:

  • Run example and watch memory consumption before first message BENCH DONE and after it.
  • Watch, that memory do not returned after MAIN LOOP DONE message
  • Comment sync way code and uncomment async way, try to increase timeout before stop and watch to memory consumption
package main

import (
	"fmt"
	"runtime"
	"time"

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

func main() {
	gst.Init(nil)
	go glib.NewMainLoop(glib.MainContextDefault(), false).Run()
	go func() {
		for i := 0; i < 3; i++ {
			bench()
		}
		fmt.Println("\nMAIN LOOP DONE")
	}()
	for {
		time.Sleep(time.Second)
		runtime.GC()
	}
}

func bench() {
	for i := 0; i < 50; i++ {
		var p *gst.Pipeline
		var err error

		if p, err = gst.NewPipelineFromString(pipeline()); err != nil {
			panic(err)
		}
		err = p.BlockSetState(gst.StatePlaying)
		if err != nil {
			panic(err)
		}

		// sync way
		// time.Sleep(500 * time.Millisecond)
		// err = p.BlockSetState(gst.StateNull)
		// fmt.Print(".")

		// async way
		go func() {
			time.Sleep(500 * time.Millisecond)
			err = p.BlockSetState(gst.StateNull)
			if err != nil {
				panic(err)
			}
			fmt.Print(".")
		}()
		time.Sleep(100 * time.Millisecond)
	}
	time.Sleep(time.Second)
	fmt.Println("BENCH DONE")
}

func pipeline() string {
	return `rtpsession name=r
 videotestsrc
   ! video/x-raw,framerate=5/1
   ! videoscale
   ! video/x-raw,width=1280,height=720,clock-rate=90000
   ! videoconvert
   ! vp8enc
   ! rtpvp8pay  pt=96 mtu=1200
   ! application/x-rtp,media=video,clock-rate=90000,payload=96,encoding-name=VP8
   ! r.send_rtp_sink
 r.send_rtp_src
   ! fakesink`
}

How to set GstElement *(or *gst.Element) to a Property?

It looks like cgocheck may not be the only headache?

Hi, I'm trying to utilize the "splitmuxsink" to split a streaming(which may have different format), hence need to set different muxer and an appSink elements to the "splitmuxsink". Right now failed to do so.

To show the problem clearly, I picked some relevant codes into a snippet as below:

package main

import (
	"fmt"

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

func gstTest() {
	gst.Init(nil)

	var sink *gst.Element
	var err error
	if sink, err = gst.NewElement("splitmuxsink"); err != nil {
		fmt.Println("failed to create splitmuxsink: ", err)
		return
	}

	if err = sink.SetProperty("async-finalize", false); err != nil {
		fmt.Println("failed to set async-finalize to splitmuxsink: ", err)
		return
	}

	var mux *gst.Element
	if mux, err = gst.NewElement("webmmux"); err != nil {
		fmt.Println("failed to create webmmux: ", err)
		return
	}

	if err = sink.SetProperty("muxer", mux); err != nil {
		fmt.Println("failed to set muxer to splitmuxsink: ", err)
		return
	}

	/*
	       var appSink *app.Sink

	   	if appSink, err = app.NewAppSink(); err != nil {
	   		fmt.Println("failed to create appSink: ", err)
	   		return
	   	}	   	
	        if err = sink.SetProperty("sink", appSink); err != nil {
		        fmt.Println("failed to set sink to splitmuxsink: ", err)
		        return
	        }
	*/
}

Running the above codes will get error as "panic: runtime error: cgo argument has Go pointer to unpinned Go pointer", while It seems that glib/gvalue.go already convert the go pointer to a unsafe.Pointer.
Running it with GODEBUG=cgocheck=0 will get error returned by SetProperty(), "Invalid type gpointer for property muxer", which means gst.Element isn't equal to GstElement?

I see in github nobody sets property of a GstElement with golang at all. So wondering how to do that?

Thanks in advance!

warnings on macos

Running an app with go-gst gets these lines output

# command-line-arguments
ld: warning: duplicate -rpath '/Library/Frameworks/GStreamer.framework/Versions/1.0/lib/pkgconfig/../../lib' ignored
ld: warning: duplicate -rpath '/Library/Frameworks/GStreamer.framework/Versions/1.0/lib/pkgconfig/../../lib' ignored

Lets try and figure out how to not duplicate paths etc.

Or can we even d anything about this without just telling ld not to warn... which seems silly...

proxysink/proxysrc usage

Hey there, I'm trying to use the proxysink and proxysrc elements. Reading the C example, it looks like this should work (I've seen a similar python example as well)

	sp := p.sourceStr + " ! proxysink name=psink"
	p.logger.Info("source pipe description ", sp)
	sourcePipe, sourceErr := gst.NewPipelineFromString(sp)
	if sourceErr != nil {
		p.logger.WithError(sourceErr).Error("source error")
		return nil, sourceErr
	}
	psink, _ := sourcePipe.GetElementByName("psink")

       //  p.pSrc is pipe2.GetElementByName("psrc")

	setErr := p.pSrc.SetProperty("proxysink", psink)
	if setErr != nil {
		p.logger.WithError(setErr).Fatal("failed setting property")
		return nil, setErr
	}

but this crashes:

panic: runtime error: cgo argument has Go pointer to Go pointer

goroutine 15 [running]:
github.com/go-gst/go-glib/glib.(*Value).SetPointer.func1(0x140000126d0, 0x140000126c8)
        /Users/<snip>/vendor/github.com/go-gst/go-glib/glib/gvalue.go:557 +0x44
github.com/go-gst/go-glib/glib.(*Value).SetPointer(0x140000126d0, 0x140000126c8)
        /Users/<snip>/vendor/github.com/go-gst/go-glib/glib/gvalue.go:557 +0x24
github.com/go-gst/go-glib/glib.gValue({0x102fad240, 0x140000126c8})
        /Users/<snip>/vendor/github.com/go-gst/go-glib/glib/gvalue.go:284 +0x1194
github.com/go-gst/go-glib/glib.(*Object).SetProperty(0x140000125f0, {0x102d2f9b5, 0x9}, {0x102fad240, 0x140000126c8})
        /Users/<snip>/vendor/github.com/go-gst/go-glib/glib/gobject.go:215 +0xdc
gitlab.ocado.tech/advanced-technology/production/webcam_pipeline/gstreamer_pipeline.(*gstPipeline).createSource(0x14000116500)
        /Users/<snip>/gstreamer_pipeline/gst.go:166 +0x2c4
.....
Exiting.

after some fiddling, I got it to work with this workaround and the pipelines play correctly

	sp := p.sourceStr + " ! proxysink name=psink"
	p.logger.Info("source pipe description ", sp)
	sourcePipe, sourceErr := gst.NewPipelineFromString(sp)
	if sourceErr != nil {
		p.logger.WithError(sourceErr).Error("source error")
		return nil, sourceErr
	}
	psink, _ := sourcePipe.GetElementByName("psink")

	proxySink := psink.GObject()
       //  p.pSrc is pipe2.GetElementByName("psrc")
	propertyType, _ := p.pSrc.GetPropertyType("proxysink")

	val, typeErr := glib.ValueInit(propertyType)
	if typeErr != nil {
		p.logger.WithError(typeErr).Fatal("whew")
		return nil, typeErr
	}

	val.SetInstance(proxySink.Native())
	setErr := p.pSrc.SetPropertyValue("proxysink", val)
	if setErr != nil {
		p.logger.WithError(setErr).Fatal("failed setting property")
		return nil, setErr
	}

Doing that works, but if I stop (set the state to NULL) the initial pipeline (the one with proxysink) and call Clear on it (which I need to do to handle camera malfunctions) and it goes out of scope in go I get this warning

(<unknown>:70460): GLib-GObject-CRITICAL **: 21:56:01.223: g_object_unref: assertion 'G_IS_OBJECT (object)' failed

which sometimes panics the program

fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x2 addr=0xafd4e8709de77de3 pc=0x105a3ba14]

runtime stack:
runtime.throw({0x104eda852?, 0x105146780?})
        /opt/homebrew/opt/go/libexec/src/runtime/panic.go:1047 +0x40 fp=0x16f8dae50 sp=0x16f8dae20 pc=0x1045af9c0
runtime.sigpanic()
        /opt/homebrew/opt/go/libexec/src/runtime/signal_unix.go:825 +0x20c fp=0x16f8dae80 sp=0x16f8dae50 pc=0x1045c67dc

goroutine 3 [syscall]:
runtime.cgocall(0x104e816e0, 0x1400007bd08)
        /opt/homebrew/opt/go/libexec/src/runtime/cgocall.go:157 +0x54 fp=0x1400007bcd0 sp=0x1400007bc90 pc=0x10457b294
github.com/go-gst/go-glib/glib._Cfunc_g_object_unref(0x14e6b5590)
        _cgo_gotypes.go:3588 +0x34 fp=0x1400007bd00 sp=0x1400007bcd0 pc=0x104c69824
github.com/go-gst/go-glib/glib.(*Object).Unref.func1(0x140003a40c8)
        /Users/<snip>/vendor/github.com/go-gst/go-glib/glib/gobject.go:141 +0x4c fp=0x1400007bd40 sp=0x1400007bd00 pc=0x104c7649c
github.com/go-gst/go-glib/glib.(*Object).Unref(0x140003a40c8)
        /Users/<snip>/vendor/github.com/go-gst/go-glib/glib/gobject.go:141 +0x20 fp=0x1400007bd60 sp=0x1400007bd40 pc=0x104c76430
runtime.call16(0x0, 0x105146630, 0x14000122060, 0x10, 0x10, 0x10, 0x1400007be40)
        /opt/homebrew/opt/go/libexec/src/runtime/asm_arm64.s:478 +0x78 fp=0x1400007bd80 sp=0x1400007bd60 pc=0x1045e1988
runtime.runfinq()
        /opt/homebrew/opt/go/libexec/src/runtime/mfinal.go:255 +0x3d8 fp=0x1400007bfd0 sp=0x1400007bd80 pc=0x104590548
runtime.goexit()
        /opt/homebrew/opt/go/libexec/src/runtime/asm_arm64.s:1172 +0x4 fp=0x1400007bfd0 sp=0x1400007bfd0 pc=0x1045e3a74
created by runtime.createfing
        /opt/homebrew/opt/go/libexec/src/runtime/mfinal.go:163 +0x4c

Any ideas if I'm doing something wrong? cheers

clock code how to use go-gst

I have this code:

GstClock *clock = gst_system_clock_obtain();
g_object_set(clock, "clock-type", GST_CLOCK_TYPE_REALTIME, NULL);

...
gst_pipeline_use_clock(GST_PIPELINE_CAST(renderer->pipeline), clock);

How to setup in go-gst.

Error While Compiling Using Windows

Hello everyone,

I'm trying to run a simple code found in examples/launch, but I keep encountering the same error.

Steps I've taken:

1. Installed Golang go1.22.4 windows/amd64
Downloaded GStreamer installation files:
MSVC 64-bit (VS 2019, Release CRT)
1.24.5 runtime installer
1.24.5 development installer

2. Added the following to system environment variables in PATH:
C:\gstreamer\1.0\msvc_x86_64\bin
C:\gstreamer\1.0\msvc_x86_64\lib\gstreamer-1.0
3. Installed Chocolatey and packages mingw and pkgconfig
4. Downloaded and installed MinGW development and runtime installer (1.24.5) from the official GStreamer website
5. Set CGO_ENABLED=1 and $env:PKG_CONFIG_PATH='C:\gstreamer\1.0\mingw_x86_64\lib\pkgconfig'
6. Executed cd examples/launch && go build .
7. Received the following error:

Screenshot 2024-07-03 113100

`setArg` works for GstFormat, but `setProperty` and `setFormat` do not

When trying to set format to "time" only setArg works 😁

Using setProperty or setFormat I get these errors:

(<unknown>:32848): GStreamer-CRITICAL **: 19:37:01.370: gst_segment_to_running_time: assertion 'segment->format == format' failed

Here is my snippet, I guess you can use any other element instead of webrtcdsp (audiobuffersplit for example)

// This example shows how to use the appsrc element.
package main

import (
	"fmt"

	"github.com/go-gst/go-glib/glib"
	"github.com/go-gst/go-gst/examples"
	"github.com/go-gst/go-gst/gst"
	"github.com/go-gst/go-gst/gst/app"
)

func createPipeline() (*gst.Pipeline, error) {
	gst.Init(nil)

	// Create a pipeline
	pipeline, err := gst.NewPipeline("")
	if err != nil {
		return nil, err
	}

	// Create the elements
	elems, err := gst.NewElementMany("appsrc", "audioconvert", "audioresample", "webrtcdsp", "fakesink")
	if err != nil {
		return nil, err
	}

	// Add the elements to the pipeline and link them
	pipeline.AddMany(elems...)
	gst.ElementLinkMany(elems...)

	// Get the app sourrce from the first element returned
	src := app.SrcFromElement(elems[0])
	webrtcdsp := elems[3]
	webrtcdsp.SetProperty("echo-cancel", false)

	// Specify the format we want to provide as application into the pipeline
	// by creating a video info with the given format and creating caps from it for the appsrc element.
	src.SetCaps(gst.NewCapsFromString("audio/x-raw, format=S16LE, layout=interleaved, channels=1, rate=16000"))
	src.SetArg("format", "time")
	// src.SetFormat(gst.FormatTime)
	// src.SetProperty("format", gst.FormatTime)
	src.SetProperty("is-live", true)
	// src.SetProperty("stream-type", 0)
	// Initialize a frame counter
	var i int

	src.SetAutomaticEOS(false)
	// src.EndStream()

	// Since our appsrc element operates in pull mode (it asks us to provide data),
	// we add a handler for the need-data callback and provide new data from there.
	// In our case, we told gstreamer that we do 2 frames per second. While the
	// buffers of all elements of the pipeline are still empty, this will be called
	// a couple of times until all of them are filled. After this initial period,
	// this handler will be called (on average) twice per second.
	src.SetCallbacks(&app.SourceCallbacks{
		NeedDataFunc: func(self *app.Source, _ uint) {
			fmt.Println("Producing frame:", i)

			// Create a buffer that can hold exactly one video RGBA frame.
			buffer := gst.NewBufferWithSize(640)

			// For each frame we produce, we set the timestamp when it should be displayed
			// The autovideosink will use this information to display the frame at the right time.
			// buffer.SetPresentationTimestamp(gst.ClockTime(time.Duration(i) * 500 * time.Millisecond))

			// Produce an image frame for this iteration.
			// At this point, buffer is only a reference to an existing memory region somewhere.
			// When we want to access its content, we have to map it while requesting the required
			// mode of access (read, read/write).
			// See: https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/allocation.html
			//
			// There are convenience wrappers for building buffers directly from byte sequences as
			// well.
			// buffer.Map(gst.MapWrite)
			// buffer.Unmap()

			// Push the buffer onto the pipeline.
			self.PushBuffer(buffer)

			i++
		},
	})

	return pipeline, nil
}

func handleMessage(msg *gst.Message) error {
	switch msg.Type() {
	case gst.MessageEOS:
		return app.ErrEOS
	case gst.MessageError:
		gerr := msg.ParseError()
		if debug := gerr.DebugString(); debug != "" {
			fmt.Println(debug)
		}
		return gerr
	}
	return nil
}

func mainLoop(loop *glib.MainLoop, pipeline *gst.Pipeline) error {
	// Start the pipeline

	// Due to recent changes in the bindings - the finalizers might fire on the pipeline
	// prematurely when it's passed between scopes. So when you do this, it is safer to
	// take a reference that you dispose of when you are done. There is an alternative
	// to this method in other examples.
	pipeline.Ref()
	defer pipeline.Unref()

	pipeline.SetState(gst.StatePlaying)

	// Retrieve the bus from the pipeline and add a watch function
	pipeline.GetPipelineBus().AddWatch(func(msg *gst.Message) bool {
		if err := handleMessage(msg); err != nil {
			fmt.Println(err)
			loop.Quit()
			return false
		}
		return true
	})

	loop.Run()

	return nil
}

func main() {
	examples.RunLoop(func(loop *glib.MainLoop) error {
		var pipeline *gst.Pipeline
		var err error
		if pipeline, err = createPipeline(); err != nil {
			return err
		}
		return mainLoop(loop, pipeline)
	})
}

Figure out what's wrong with windows mingw 12

Github's runner image for windows now uses mingw 12... which causes us errors.

Look at #46 for more information.

Currently we're downgrading to mingw 11 but this action takes 12 minutes and isn't a cache-able thing...

Figure out what's up with using a later version of gcc/mingw on windows

unmarshaling nvdsbatchmeta

Also see https://forums.developer.nvidia.com/t/trying-to-unmarshal-nvdsbatchmeta-in-golang-some-help-required/289579 The original Python code is here, there a module named PYDS is taking care of the marshalling/unmarshalling of the NVIDEA GSTBuffer content... https://github.com/NVIDIA-AI-IOT/deepstream_python_apps/tree/master/apps/deepstream-rtsp-in-rtsp-out

But his is just in case you have some ideas. You don't have to mess with this, of course.

_Originally posted by @neilyoung

Go minimum version downgrade

From https://go.dev/doc/modules/gomod-ref

The go directive sets the minimum version of Go required to use this module. Before Go 1.21, the directive was advisory only; now it is a mandatory requirement: Go toolchains refuse to use modules declaring newer Go versions.

I don't think there's anything in go-glib or go-gst that sets a hard requirement to 1.22, could you consider downgrading this down to 1.21 or even 1.20? Outside of special cases, we're mostly using 1.21 atm and some 3rdparty libraries are still having hard times with 1.22 compatibility.

Meanwhile while waiting for 1.22 components to stabilize, a fork is always an option but might as well ask! Thanks

Running examples on macos leads to fail

Trying to run the examples on macos leads to:

../../gst/constants.go:42:31: cannot use (_Ciconst_GST_CLOCK_TIME_NONE) (untyped int constant -1) as ClockTime value in variable declaration (overflows)

C compiler warns "Wformat-security"

Every time I start up using this module I get given this output

# github.com/go-gst/go-gst/gst
../../gst/gst_debug.go:14:63: warning: format string is not a string literal (potentially insecure) [-Wformat-security]
../../gst/gst_debug.go:14:63: note: treat the string as an argument to avoid this

Let's see if we can get rid of them

Cross-Compile for aarch64

Hi,

I'm trying to cross-compile the binding for aarch64-target, but it gives an error from gcc:

github.com/go-gst/go-glib/glib

aarch64-none-linux-gnu-gcc: ERROR: unsafe header/library path used in cross-compilation: '-I/usr/include/libmount'

it shows that it's attempting to include headers from building machine, not the aarch64 target.
Even though there are other CGO parts in the project that are compiling well (pointing to the target's rootfs container),
I can't get this part to do so.

Any help is appreciated.

Setting properties with typed named constants

Running gstreamer and go-gst on a linux-based arm64 processor.

When building a basic pipeline (videotestsrc -> videoconvert -> autovideosink), attempting to set the pattern property on the videotestsrc returns the following error(s)

invalid type gchararray for property patternset property pattern: snow on videotestsrc

invalid type gchararray for property patternset property pattern: smpte on videotestsrc

invalid type gchararray for property patternset property pattern: black on videotestsrc

etc...

Code is just doing an element.Set("pattern", "snow"), works fine with horizontal-scroll so just seems to be an issue with that specific property.

String input works on the TextOverlay element, so appears to just be this element/property

Any ideas?

Handle up- and downcasting safely

This takes #79 and generalizes it.

We need to add some methods to convert the objects safely between the type hierarchie. E.g. a call to gst_element_get_static_pad always returns a GstPad aka *gst.Pad, but in some cases (e.g. bins) these pads are actually a GstGhostPad, and currently there is no safe way to convert them for the go application.

Similarly, gst_parse_launch returns a GstElement, but oftentimes a pipeline or bin is needed.

This is also be relevant for glib signals (see #77 (comment)) where we unmarshal elements and sometimes do not have unmarshaler for every plugin, e.g. GstMpegAudioParse, but in other times we need to downcast an unmarshaled *gst.Element to a *gst.Object because the signal handler expects it that way.

gtk4 integration

I am trying to make an customized video player using this project with gotk4 , I could not make it work , tried gotk4-gstreamer as well , still no avail .

Could you give me some directions and guidelines on this , I want to use this project to process rtsp streams and output gtk4paintablesinks , then feed it to gotk4 for final UI customization . Here is a discussion as well .

Or do you happen to know any golang GUI toolkit could fit my purpose better or easier ?

Thank you .

GetArrayValueFloat

It would be great to have helpers functions to get GValueArray of gdouble out of a structure field, we did this on a fork of tinyzimmer/go-gst GetStream@957f35c

Not an issue but a request for more examples

Hello,
I was hoping for a few more examples with attaching signals.
I'm using this pipeline. "uridecodebin3 name=source ! audioconvert ! volume name=vol ! autoaudiosink"

Note ** later the autoaudiosink is to be substituted with ! queue ! tee to attach to another running pipeline.

basically I'm attempting a one-shot player for effects sounds.

I'm using the '"about-to-finish" signal to seek back to start after setting the pipeline to pause the setting back to playing. However, using this signal cuts the audio short be a few seconds which indicates the end of filling the buffer.
Is there any method to keep the pipeline active without EOS? I guess rebuiling the pipeline is one example however I feel this is a work around an obvious use of signals and pad probe, re-linking? elements in the pipeline.

In addition, another example worth considering is the rtmp2sink element?
Works great on local LAN however, streaming on WAN the where network disconnections are common the pipeline crashes. Is there a method to monitor in ago routine for network error message and reconnect the rtmp2sink element and restart after X seconds/minutes after the error has occurred.

There is an example in python for the rtmp2sink however, go is the go!

restart rtmp2sink

Many Thanks

Audio problem originating from package name for Example

Example in question: https://github.com/go-gst/go-gst/blob/main/examples/decodebin/main.go

If you change the package to not be main, then the audio demo stops working.
The code itself does not give any errors and seems to run, but gstreamer none the less does not output any audio.

Reproduction steps:

  1. Start a new project with the example as your main.go
  2. replace
func main() {
	flag.StringVar(&srcFile, "f", "", "The file to decode")
	flag.Parse()
	if srcFile == "" {
		flag.Usage()
		os.Exit(1)
	}
	examples.RunLoop(func(loop *glib.MainLoop) error {
		pipeline, err := buildPipeline()
		if err != nil {
			return err
		}
		return runPipeline(loop, pipeline)
	})
}

with

func PlayFile(file string) {
        srcFile = file
	examples.RunLoop(func(loop *glib.MainLoop) error {
		pipeline, err := buildPipeline()
		if err != nil {
			return err
		}
		return runPipeline(loop, pipeline)
	})
}
  1. from a new main.go call PlayFile

My Device

  • Fedora
  • X11
  • Pipewire
  • go1.21.6 linux/amd6

possible misuse of unsafe.Pointer in gasyncresult.go

I tried to add these bindings to my app, and during compilation I got following error:

compilepkg: nogo: errors found by nogo during build-time code analysis:
/.../vendor/github.com/go-gst/go-glib/glib/__main__/vendor/github.com/go-gst/go-glib/glib/gasyncresult.go:86:55: possible misuse of unsafe.Pointer (unsafeptr)

I was able to silence this by adding // nolint:unsafeptr comment to this line. Please fix this or add this comment if this is a false positive.

Regression since v1.0.0

Getting this error:

# github.com/go-gst/go-gst/gst
gst/gst_debug.go:221:20: could not determine kind of name for C.gst_debug_message_get_id

Seems to be introdued with this commit: d0177da

Vesrion: GStreamer 1.20.3

Memory leak

Hello,
pipeline with appsrc to which flv video chunks are pushed with PushBuffer and NewBufferFromBytes() leaks memory. The memory continues to grow while the pipeline is running and stays after the pipeline has ended (EOS event followed with stateNull), and is never freed. The pipeline works and produces valid MP4 file. Gstreamer version is 1.22.0 and it is run on Debian 12.

appsrc (flv video)=>queue->flvdemux->audiotee->queue->aacparser->mp4mux->filesink
->videotee->queue->h264parser->mp4mux

memleak-bpfcc -p 15232 -o 5000 30

shows continous growing allocation of memory at :

54947840 bytes in 13415 allocations from stack
		0x000015480133d679	g_malloc+0x19 [libglib-2.0.so.0.7400.6]
		0x0000000000000001	[unknown]
		0x00001547c40280c0	[unknown]

debug/pprof at NewBufferFromBytes and wrapBuffer functions.
gst

SEGFAULT using glib.Value

When setting a property on a custom bin I sometimes get a SIGSEGV.

The error in the stack trace is reported to be at https://github.com/go-gst/go-glib/blob/main/glib/gvalue.go#L52 in the called C function in https://github.com/go-gst/go-glib/blob/b2d34240bcb44a1b341b2f094eb2ab33e1a2aa98/glib/glib.go.h#L126-L128

I will investigate and provide further details.

Currently I suspect that the reason is the conversion from C to golang, and if timed correctly it may interfere with some GC magic.

How to write bindings documentation needed

if we ever want to have more contributors, a small documentation would be needed:

e.g.:

  • when to use glib.TransferNone and when to use glib.TransferFull
  • when freeing the params is needed and when it is not
  • general "How to use pkgconfig for golang devs" maybe

this documentation doesn't need to be very long, but would quickstart a new developer that needs some existing functionality from gstreamer

Try to run an example on windows 11 fails

Try to run an example on Windows with Powershell and I saw the below error:
..\..\gst\control_source.go:44:162: cannot use _Ctype_ulong(time) (value of type _Ctype_ulong) as _Ctype_ulonglong value in variable declaration

./buildAll.sh failed in Ubuntu 22.04.3 LTS

Describe
When run ./buildAll.sh in Ubuntu 22.04.3 LTS got an error as follow:

./buildAll.sh 
# pkg-config --cflags  -- gstreamer-1.0 gstreamer-controller-1.0
Package gstreamer-1.0 was not found in the pkg-config search path.
Perhaps you should add the directory containing `gstreamer-1.0.pc'
to the PKG_CONFIG_PATH environment variable
No package 'gstreamer-1.0' found
Package gstreamer-controller-1.0 was not found in the pkg-config search path.
Perhaps you should add the directory containing `gstreamer-controller-1.0.pc'
to the PKG_CONFIG_PATH environment variable
No package 'gstreamer-controller-1.0' found
pkg-config: exit status 1

How to resolve:

sudo apt update
sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev

Hope this comment will help someone who got the same error :)

Handling SIGSEGV when in docker container

Related: https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3052

The linked issue contains a segmentation fault in gstreamer. It can always happen that gstreamer has some bugs, but in this case it looks like the go runtime handles the signal and discards it. That way the pipeline gets deadlocked.

Thread 20 (LWP 954686 "audiomixer0:src"):
#0  runtime.usleep () at /usr/lib/go/src/runtime/sys_linux_amd64.s:135
#1  0x0000000000473374 in runtime.raisebadsignal (sig=11, c=0xc0006b3310) at /usr/lib/go/src/runtime/signal_unix.go:972
#2  0x00000000004737a7 in runtime.badsignal (sig=11, c=0xc0006b3310) at /usr/lib/go/src/runtime/signal_unix.go:1076
#3  0x00000000004720e8 in runtime.sigtrampgo (sig=11, info=0xc0006b34b0, ctx=0xc0006b3380) at /usr/lib/go/src/runtime/signal_unix.go:468
#4  0x0000000000493cc6 in runtime.sigtramp () at /usr/lib/go/src/runtime/sys_linux_amd64.s:352
#5  <signal handler called>
#6  gst_audio_aggregator_aggregate (agg=0x7f5a8c007ea0, timeout=0) at ../gstreamer/subprojects/gst-plugins-base/gst-libs/gst/audio/gstaudioaggregator.c:2475
#7  0x00007f5af84d7c6a in gst_aggregator_aggregate_func (self=0x7f5a8c007ea0) at ../gstreamer/subprojects/gstreamer/libs/gst/base/gstaggregator.c:1431
#8  0x00007f5af8d54283 in gst_task_func (task=0x7f5a50024900) at ../gstreamer/subprojects/gstreamer/gst/gsttask.c:384
#9  0x00007f5af8b74483 in g_thread_pool_thread_proxy (data=<optimized out>) at ../glib/glib/gthreadpool.c:350
#10 0x00007f5af8b719a5 in g_thread_proxy (data=0x7f5a80001160) at ../glib/glib/gthread.c:831
#11 0x00007f5af879c9eb in start_thread (arg=<optimized out>) at pthread_create.c:444
#12 0x00007f5af8820654 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:100

This is not a cgo call, where golang will handle the signal and raise a panic, but instead a signal in a background thread created by gstreamer as a result of a cgo call.

Should we add some code to gst init that listens to SIGSEGV and panics? Should we add a helper method that does that and leave the handling to the user? Is such a signal handler even effective?

It could look something like this:

func CatchGstreamerSegfaults() {
	c := make(chan os.Signal, 1)

	signal.Notify(c, syscall.SIGSEGV)

	<-c

	panic("received segfault, exiting")
}

This however destroys the stack trace origin of the signal.

Docs are suggesting Unrefs that lead to segfaults

E.g.

go-gst/gst/gst_bin.go

Lines 125 to 134 in b9552df

// GetElementByName returns the element with the given name. Unref after usage.
func (b *Bin) GetElementByName(name string) (*Element, error) {
cName := C.CString(name)
defer C.free(unsafe.Pointer(cName))
elem := C.gst_bin_get_by_name((*C.GstBin)(b.Instance()), (*C.gchar)(cName))
if elem == nil {
return nil, fmt.Errorf("could not find element with name %s", name)
}
return wrapElement(glib.TransferFull(unsafe.Pointer(elem))), nil
}

The docs are mentioning Unref after use which conflicts with the TransferFull used in the function body and will lead to segfaults because of early frees.

video display support on MacOS

Hello,

I've tried to use autovideosink, glimagesink and osxvideosink with go-gst but I don't see a video window anywhere. If I use gst-launch-1.0, all sinks work fine.

Is this expected?

Small memory leak over time

The application I am writing has the following pprof inuse_objects after some ~20h runtime:

profile002

I am dynamically adding and removing pipeline elements, so it makes sense that there are a lot of elements over the course of this time span, although they should've been cleaned up when not used anymore. The function that creates the elements is the following:

type playbackSource struct {
	bin *gst.Bin

	// the elements in the source, needed so that we do not unref them:
	src,
	decode,
	fader,
	ac1,
	pitch,
	ac2,
	resampler,
	queue *gst.Element

	handle glib.SignalHandle

	settings playbackSettings

	controlSource *gst.InterpolationControlSource
}

// construct a filesrc -> decodebin -> volume -> audioconvert -> pitch -> audioconvert -> resampler -> queue -> <ghost pad> Bin
func newLocalSource(srcLoc string) (*playbackSource, error) {

	xmixLogger.Logger.Infof("creating new playback element")

	currentId := atomic.AddUint64(&id, 1)

	bin := gst.NewBin(fmt.Sprintf("playback%d", currentId))

	src, err := gst.NewElementWithProperties("filesrc", map[string]interface{}{
		"location": srcLoc,
	})

	if err != nil {
		return nil, err
	}

	decode, err := gst.NewElement("decodebin")

	if err != nil {
		return nil, err
	}

	fader, err := gst.NewElementWithName("volume", fmt.Sprintf("fader%d", currentId))

	if err != nil {
		return nil, err
	}

	ac1, err := gst.NewElement("audioconvert")

	if err != nil {
		return nil, err
	}

	pitch, err := gst.NewElement("pitch")

	if err != nil {
		return nil, err
	}

	ac2, err := gst.NewElement("audioconvert")

	if err != nil {
		return nil, err
	}

	resampler, err := gst.NewElement("audioresample")

	if err != nil {
		return nil, err
	}

	queue, err := gst.NewElement("queue")

	if err != nil {
		return nil, err
	}

	err = bin.AddMany(
		src,
		decode,
		fader,
		ac1,
		pitch,
		ac2,
		resampler,
		queue,
	)

	if err != nil {
		return nil, err
	}

	handle, err := decode.Connect("pad-added", func(e *gst.Element, p *gst.Pad) {
		err := e.Link(fader)

		if err != nil {
			panic(fmt.Sprintf("got error during linking: %v", err))
		}
	})

	if err != nil {
		return nil, err
	}

	err = src.Link(decode)

	if err != nil {
		return nil, err
	}

	err = gst.ElementLinkMany(fader, ac1, pitch, ac2, resampler, queue)

	if err != nil {
		return nil, err
	}

	queuePad := queue.GetStaticPad("src")

	ghostPad := gst.NewGhostPad("src", queuePad)

	if !bin.AddPad(ghostPad.Pad) {
		return nil, errors.New("could not add ghostpad to bin")
	}

	csource := gst.NewInterpolationControlSource()
	csource.SetInterpolationMode(gst.InterpolationModeLinear) // maybe we need to change this?
	binding := gst.NewDirectControlBinding(fader.Object, "volume", csource)
	fader.AddControlBinding(&binding.ControlBinding)

	return &playbackSource{
		bin: bin,

		src:       src,
		decode:    decode,
		fader:     fader,
		ac1:       ac1,
		pitch:     pitch,
		ac2:       ac2,
		queue:     queue,
		resampler: resampler,

		handle: handle,

		controlSource: csource,
	}, nil
}

Currently I fear that the connected pad-added signal holds a reference to the elements in the pipeline, so that the go GC doesn't clean them up. The code in question is the following:

        // this function has a reference to the elements above, which means the GO GC wont clean them up
	// which means a memory leak in the C part. Remove this handle when the playout is done to not leak memeory!
	//
	// we cannot remove it after the initial call since a pipeline restart requires that this fires again
	handle, err := decode.Connect("pad-added", func(e *gst.Element, p *gst.Pad) {
		err := e.Link(fader)

		if err != nil {
			panic(fmt.Sprintf("got error during linking: %v", err))
		}
	})

I was trying to investigate the way the signals work in the bindings, but I got lost deep in the cgo calls around here and here


My 2 cents: if "for whatever reason" the function of the pad callback stays in scope, the GC wont clean up the elements. I tried manually disconnecting using the following but this didn't help:

func (p *playbackSource) cleanup() {
	p.decode.HandlerDisconnect(p.handle)
}

How to setup autovideosink sync=false

My pipeline is setup as

pipeline, err := gst.NewPipeline("")
if err != nil {
	return nil, nil, err
}

// Create the elements
elems, err := gst.NewElementMany("appsrc", "queue", "h264parse", "avdec_h264", "autovideosink")
if err != nil {
	return nil, nil, err
}

// Add the elements to the pipeline and link them
pipeline.AddMany(elems...)
gst.ElementLinkMany(elems...)

but I tried many ways to setup sync=false to the autovidosink, it passed compile but reported a link error (-1)

There are many ways to construct the pipeline, but I am still unable to find a common way.

could not determine kind of name for C.gst_element_factory_make_with_properties

Hello friends, I get this error when using the library. I use Ubuntu. Please help me

go run main.go                  
£ github.com/go-gst/go-gst/gst
../../../go/pkg/mod/github.com/go-gst/go-gst§v0.0.0-20240207190302-04ec17f96d71/gst/gst_element_factory.go:70:10: could not determine kind of name for C.gst_element_factory_make_with_properties

'get-internal-session' unable to retrieve RTPSession in 'on-ssrc-active'

update: Sorry for the many edits @RSWilli

I've extended the base-example with a manager and on-ssrc-active.

Here is my pipeline. I forward an RTSP-stream towards an WHEP endpoint in MediaMTX server.

GST_DEBUG=4 ./pipeliner "rtspsrc location=rtsp://172.25.25.102:554/axis-media/media.amp name=src latency=15 drop-on-latency=true udp-buffer-size=10242888 ! rtph264depay ! h264parse ! rtspclientsink location=rtsp://localhost:7554/PI-10"
  • The strange thing is that i have the exact same code in python which are able to retrieve the session.

  • Are there any special dependencies I need when digging into rtspsrc =>rtpbin element which is not implemented in go-gst? (I'm quite new to go btw)

  • I've tested the code in both Ubuntu and Windows dev-envs, and I get the same error when trying to retrieve the get-internal-session

// This is the same as the `launch` example. See the godoc and other examples for more
// in-depth usage of the bindings.
package main

import (
	"fmt"
	"os"
	"reflect"
	"strings"

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

func main() {
	// This example expects a simple `gst-launch-1.0` string as arguments
	if len(os.Args) == 1 {
		fmt.Println("Pipeline string cannot be empty")
		os.Exit(1)
	}

	// Initialize GStreamer with the arguments passed to the program. Gstreamer
	// and the bindings will automatically pop off any handled arguments leaving
	// nothing but a pipeline string (unless other invalid args are present).
	gst.Init(&os.Args)

	// Create a main loop. This is only required when utilizing signals via the bindings.
	// In this example, the AddWatch on the pipeline bus requires iterating on the main loop.
	mainLoop := glib.NewMainLoop(glib.MainContextDefault(), false)

	// Build a pipeline string from the cli arguments
	pipelineString := strings.Join(os.Args[1:], " ")

	/// Let GStreamer create a pipeline from the parsed launch syntax on the cli.
	pipeline, err := gst.NewPipelineFromString(pipelineString)
	if err != nil {
		fmt.Println(err)
		os.Exit(2)
	}

	// Add manager to the pipeline
	srcElement, err := pipeline.GetElementByName("src")
	if err != nil {
		fmt.Println(err)
		os.Exit(2)
	}

	srcElement.Connect("new-manager", srcManager)

	// Add a message handler to the pipeline bus, printing interesting information to the console.
	pipeline.GetPipelineBus().AddWatch(func(msg *gst.Message) bool {
		switch msg.Type() {
		case gst.MessageEOS: // When end-of-stream is received flush the pipeling and stop the main loop
			pipeline.BlockSetState(gst.StateNull)
			mainLoop.Quit()
		case gst.MessageError: // Error messages are always fatal
			err := msg.ParseError()
			fmt.Println("ERROR:", err.Error())
			if debug := err.DebugString(); debug != "" {
				fmt.Println("DEBUG:", debug)
			}
			mainLoop.Quit()
		default:
			// All messages implement a Stringer. However, this is
			// typically an expensive thing to do and should be avoided.
			fmt.Println(msg)
		}
		return true
	})

	// Start the pipeline
	pipeline.SetState(gst.StatePlaying)

	// Block and iterate on the main loop
	mainLoop.Run()
}

func srcManager(element *gst.Element, manager *gst.Element) {
	fmt.Println("New manager detected:", manager)
	manager.Connect("on-ssrc-active", sourceActiveMonitorPeriodically)

}


func sourceActiveMonitorPeriodically(rtpBin *gst.Element, sessionID uint, ssrc uint32) {
    // Use reflection to determine the type of stats
    rtpBinType := reflect.TypeOf(rtpBin)
    fmt.Println("Type of rtpBin:", rtpBinType)

    fmt.Printf("Source active: sessionID: %v, ssrc: %v\n", sessionID, ssrc)

    // Debug prints for sessionID and rtpBin
    // fmt.Printf("Emitting get-internal-session for sessionID: %d\n", sessionID)
    fmt.Printf("Emitting get-session for sessionID: %d\n", sessionID)
    fmt.Printf("rtpBin: %v\n", rtpBin)

    // Get the session instance which manages the current source
    session, err := rtpBin.Emit("get-session", sessionID)
    if err != nil {
        fmt.Println("Error emitting 'get-session' signal:", err)
        fmt.Println("here is the session", session)
        return
    }

    // Get the session instance which manages the current source
    session, err := rtpBin.Emit("get-internal-session", sessionID)
    if err != nil {
        fmt.Println("Error emitting signal:", err)
        return
    }

    // Check if session is nil
    if session == nil {
        fmt.Println("Error: session is nil")
        return
    }

    fmt.Println("Session:", session)

}

example/plugins demo compile problem

Some demo in the plugins demo could not work, because of

  1. template name error: e.g fileSink --> FileSink
  2. go.mod defined problem: module version and library version

HandlerDisconnect crush

Hi. I investigated memory leaks in my application, decided to apply recommendations you suggested here, in the issues - I added the removal of the event handler using HandlerDisconnect and started getting critical messages like this

(test1:154528): GLib-GObject-CRITICAL **: 15:31:56.550: g_closure_ref: assertion 'closure->ref_count > 0' failed

(test1:154528): GLib-GObject-CRITICAL **: 15:31:56.550: g_closure_unref: assertion 'closure->ref_count > 0' failed

and then crush, although I can't figure out what I'm doing wrong - everything looks like in the examples. Here is my code

package main

import (
	"errors"
	"fmt"
	"time"

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

func main() {
	gstInit()
	go bench()
	go bench()
	go bench()
	go bench()
	go bench()
	go bench()
	go bench()
	go bench()
	go bench()
	go bench()
	go bench()
	go bench()
	go bench()
	go bench()
	bench()
}

func bench() {
	for {
		if err := test(); err != nil {
			fmt.Println("ERROR: ", err)
			return
		}
		fmt.Print(".")
		// runtime.GC()
	}
}

func gstInit() {
	gst.Init(&[]string{})
	mainLoop := glib.NewMainLoop(glib.MainContextDefault(), false)
	go mainLoop.Run()
}

func test() error {
	var pipeline *gst.Pipeline
	var err error
	strPipeline := `rtpsession name=r
   audiotestsrc
   ! opusenc
   ! rtpopuspay pt=96
   ! application/x-rtp,media=audio,clock-rate=48000,payload=96,encoding-name=OPUS
   ! r.send_rtp_sink
 r.send_rtp_src ! identity name=rtp-in-inspector
   ! udpsink name=udp_rtp_sink host=127.0.0.1 port=37244 async=false
 r.send_rtcp_src
   ! udpsink name=udp_rtcp_sink host=127.0.0.1 port=50388 async=false
`
	if pipeline, err = gst.NewPipelineFromString(strPipeline); err != nil {
		return err
	}
	if !pipeline.GetPipelineBus().AddWatch(handleEvent) {
		return errors.New("can't add watch")
	}
	defer pipeline.GetPipelineBus().RemoveWatch()

	err = pipeline.BlockSetState(gst.StatePlaying)
	if err != nil {
		return err
	}

	inspRTP, err := pipeline.GetElementByName("rtp-in-inspector")
	if err != nil {
		return err
	}

	h, err := inspRTP.Connect("handoff", func(self *gst.Element, buff *gst.Buffer) {})
	if err != nil {
		return err
	}
	defer inspRTP.HandlerDisconnect(h)

	time.Sleep(500 * time.Millisecond)

	return pipeline.BlockSetState(gst.StateNull)
}

func handleEvent(msg *gst.Message) bool {
	// fmt.Println(msg)
	return true
}

go.mod:

module test1

go 1.22

toolchain go1.22.2

require (
	github.com/go-gst/go-glib v1.0.1
	github.com/go-gst/go-gst v1.0.1-0.20240412193824-b9552df57e79
)

require github.com/mattn/go-pointer v0.0.1 // indirect

just compile it, run and wait a bit.

Also - memory still leaking, even if i remove inspRTP.Connect and manipulations with Bus at all.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.