added appsink_src.nim gst example
This commit is contained in:
parent
8356dd3a02
commit
be0325d0ab
4 changed files with 272 additions and 4 deletions
171
examples/gtk3/appsink_src.nim
Normal file
171
examples/gtk3/appsink_src.nim
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
## gcc -Wall appsink-src.c -o appsink-src $(pkg-config --cflags --libs gstreamer-1.0 gstreamer-audio-1.0 gstreamer-app-1.0)
|
||||||
|
## GStreamer
|
||||||
|
##
|
||||||
|
## appsink-src.c: example for using appsink and appsrc.
|
||||||
|
##
|
||||||
|
## Copyright (C) 2008 Wim Taymans <wim.taymans@gmail.com>
|
||||||
|
##
|
||||||
|
|
||||||
|
import gintro/[gtk, gst, gstapp, gobject, glib]
|
||||||
|
from std/os import paramCount, paramStr
|
||||||
|
from std/strutils import `%`
|
||||||
|
|
||||||
|
proc toBoolVal(b: bool): Value =
|
||||||
|
let gtype = typeFromName("gboolean")
|
||||||
|
discard init(result, gtype)
|
||||||
|
setBoolean(result, b)
|
||||||
|
|
||||||
|
proc toUIntVal(i: int): Value =
|
||||||
|
let gtype = typeFromName("guint")
|
||||||
|
discard init(result, gtype)
|
||||||
|
setUint(result, i)
|
||||||
|
|
||||||
|
## these are the caps we are going to pass through the appsink and appsrc
|
||||||
|
const
|
||||||
|
audioCaps = "audio/x-raw,format=S16LE,channels=1,rate=8000, layout=interleaved"
|
||||||
|
|
||||||
|
type
|
||||||
|
ProgramData = ref object
|
||||||
|
loop: MainLoop
|
||||||
|
source: gst.Element
|
||||||
|
sink: gst.Element
|
||||||
|
|
||||||
|
## called when the appsink notifies us that there is a new buffer ready for processing
|
||||||
|
proc onNewSampleFromSink(elt: AppSink; data: ProgramData): gst.FlowReturn =
|
||||||
|
var
|
||||||
|
sample: gst.Sample
|
||||||
|
appBuffer: gst.Buffer
|
||||||
|
buffer: gst.Buffer
|
||||||
|
source: gst.Element
|
||||||
|
## get the sample from appsink
|
||||||
|
sample = pullSample(cast[AppSink](elt))
|
||||||
|
buffer = sample.getBuffer
|
||||||
|
## make a copy
|
||||||
|
appBuffer = buffer.copy()
|
||||||
|
## we don't need the appsink sample anymore
|
||||||
|
# gst_sample_unref(sample)
|
||||||
|
## get source an push new buffer
|
||||||
|
source = getByName(cast[gst.Bin](data.sink), "testsource")
|
||||||
|
result = pushBuffer(cast[AppSrc](source), appBuffer)
|
||||||
|
# gst_object_unref(source)
|
||||||
|
|
||||||
|
## called when we get a GstMessage from the source pipeline when we get EOS, we notify the appsrc of it.
|
||||||
|
#BusFunc* = proc (bus: ptr Bus00; message: ptr Message00; userData: pointer): gboolean {.cdecl.}
|
||||||
|
proc onSourceMessage(bus: gst.Bus; message: gst.Message; data: ProgramData): bool =
|
||||||
|
var source: gst.Element
|
||||||
|
var h = message.impl.type
|
||||||
|
if h == {MessageFlag.eos}:
|
||||||
|
echo("The source got dry")
|
||||||
|
source = getByName(cast[gst.Bin](data.sink), "testsource")
|
||||||
|
discard endOfStream(cast[AppSrc](source))
|
||||||
|
# gst_object_unref(source)
|
||||||
|
elif h == {MessageFlag.error}:
|
||||||
|
echo("Received error")
|
||||||
|
glib.quit(data.loop)
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
return true
|
||||||
|
|
||||||
|
## called when we get a GstMessage from the sink pipeline when we get EOS, we
|
||||||
|
## exit the mainloop and this testapp.
|
||||||
|
proc onSinkMessage(bus: gst.Bus; message: gst.Message; data: ProgramData): bool =
|
||||||
|
var h = message.impl.type
|
||||||
|
if h == {MessageFlag.eos}:
|
||||||
|
echo("Finished playback")
|
||||||
|
glib.quit(data.loop)
|
||||||
|
elif h == {MessageFlag.error}:
|
||||||
|
echo("Received error")
|
||||||
|
glib.quit(data.loop)
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
return true
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
## Parse arguments
|
||||||
|
let argc = paramCount() + 1
|
||||||
|
var argv = newSeq[string](argc)
|
||||||
|
for i in 0 ..< argc:
|
||||||
|
argv[i] = paramStr(i)
|
||||||
|
|
||||||
|
var
|
||||||
|
filename: string
|
||||||
|
data: ProgramData
|
||||||
|
str: string
|
||||||
|
bus: gst.Bus
|
||||||
|
testsink: gstApp.AppSink
|
||||||
|
testsource: gst.Element
|
||||||
|
|
||||||
|
gst.init()
|
||||||
|
if argc == 2:
|
||||||
|
filename = argv[1]
|
||||||
|
else:
|
||||||
|
filename = "/usr/share/sounds/ekiga/ring.wav"
|
||||||
|
if not fileTest(filename, FileTest.exists):
|
||||||
|
echo("File $1 does not exist" % [filename])
|
||||||
|
# g_free(filename)
|
||||||
|
quit(QuitFailure)
|
||||||
|
data = new ProgramData #g_new0(ProgramData, 1)
|
||||||
|
data.loop = glib.newMainLoop(nil, false)
|
||||||
|
## setting up source pipeline, we read from a file and convert to our desired caps.
|
||||||
|
str = "filesrc location=\"$1\" ! wavparse ! audioconvert ! audioresample ! appsink caps=\"$2\" name=testsink" % [filename, audioCaps]
|
||||||
|
#g_free(filename)
|
||||||
|
data.source = gst.parseLaunch(str) #, nil)
|
||||||
|
#g_free(string)
|
||||||
|
if data.source == nil:
|
||||||
|
echo("Bad source")
|
||||||
|
# g_main_loop_unref(data.loop)
|
||||||
|
# g_free(data)
|
||||||
|
quit(QuitFailure)
|
||||||
|
bus = gst.getBus(data.source)
|
||||||
|
gst.addWatch(bus, glib.PRIORITY_DEFAULT.int, onSourceMessage, data)
|
||||||
|
# gst_object_unref(bus)
|
||||||
|
## we use appsink in push mode, it sends us a signal when data is available
|
||||||
|
## and we pull out the data in the signal callback. We want the appsink to
|
||||||
|
## push as fast as it can, hence the sync=false
|
||||||
|
testsink = cast[AppSink](getByName(cast[gst.Bin](data.source), "testsink"))
|
||||||
|
# g_object_set(G_OBJECT(testsink), "emit-signals", TRUE, "sync", FALSE, nil)
|
||||||
|
setProperty(testsink, "emit-signals", toBoolVal(true))
|
||||||
|
setProperty(testsink, "sync", toBoolVal(false))
|
||||||
|
connect(testsink, "new-sample", onNewSampleFromSink, data)
|
||||||
|
#gst_object_unref(testsink)
|
||||||
|
## setting up sink pipeline, we push audio data into this pipeline that will
|
||||||
|
## then play it back using the default audio sink. We have no blocking
|
||||||
|
## behaviour on the src which means that we will push the entire file into
|
||||||
|
## memory.
|
||||||
|
str = "appsrc name=testsource caps=\"$1\" ! autoaudiosink" % [audioCaps]
|
||||||
|
data.sink = gst.parseLaunch(str) # , nil) was GError
|
||||||
|
#g_free(string)
|
||||||
|
if data.sink == nil:
|
||||||
|
echo("Bad sink")
|
||||||
|
#gst_object_unref(data.source)
|
||||||
|
#g_main_loop_unref(data.loop)
|
||||||
|
#g_free(data)
|
||||||
|
quit(QuitFailure)
|
||||||
|
testsource = gst.getByName(cast[gst.Bin](data.sink), "testsource")
|
||||||
|
## configure for time-based format
|
||||||
|
setProperty(testsource, "format", toUIntVal(Format.time.ord)) # caution, is the enum signed or unsigned?
|
||||||
|
#g_object_set(testsource, "format", GST_FORMAT_TIME, nil)
|
||||||
|
## uncomment the next line to block when appsrc has buffered enough
|
||||||
|
## g_object_set (testsource, "block", TRUE, NULL);
|
||||||
|
#gst_object_unref(testsource)
|
||||||
|
bus = getBus(data.sink)
|
||||||
|
addWatch(bus, glib.PRIORITY_DEFAULT.int, onSinkMessage, data)
|
||||||
|
#gst_object_unref(bus)
|
||||||
|
## launching things
|
||||||
|
discard setState(data.sink, State.playing)
|
||||||
|
discard setState(data.source, State.playing)
|
||||||
|
## let's run !, this loop will quit when the sink pipeline goes EOS or when an
|
||||||
|
## error occurs in the source or sink pipelines.
|
||||||
|
echo("Let\'s run!")
|
||||||
|
glib.run(data.loop)
|
||||||
|
echo("Going out")
|
||||||
|
discard setState(data.source, State.null)
|
||||||
|
discard setState(data.sink, State.null)
|
||||||
|
# gst_object_unref(data.source)
|
||||||
|
# gst_object_unref(data.sink)
|
||||||
|
# g_main_loop_unref(data.loop)
|
||||||
|
# g_free(data)
|
||||||
|
quit(QuitSuccess)
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Package
|
# Package
|
||||||
|
|
||||||
version = "0.8.5"
|
version = "0.8.6"
|
||||||
author = "Stefan Salewski"
|
author = "Stefan Salewski"
|
||||||
description = "High level GObject-Introspection based GTK3/GTK4 bindings"
|
description = "High level GObject-Introspection based GTK3/GTK4 bindings"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
||||||
85
gintro/gimplgst.nim
Normal file
85
gintro/gimplgst.nim
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
import macros
|
||||||
|
from strutils import `%`
|
||||||
|
|
||||||
|
# mostly a copy of gimplglib.nim
|
||||||
|
##########################################################
|
||||||
|
#proc gst_bus_add_watch_full(self: ptr Bus00; priority: int32; `func`: BusFunc;
|
||||||
|
# userData: pointer; notify: DestroyNotify): uint32 {.
|
||||||
|
# importc, libprag.}
|
||||||
|
|
||||||
|
#guint gst_bus_add_watch (GstBus *bus, GstBusFunc func, gpointer user_data);
|
||||||
|
#guint gst_bus_add_watch_full (GstBus *bus, gint priority, GstBusFunc func, gpointer user_data, GDestroyNotify notify);
|
||||||
|
|
||||||
|
#BusFunc* = proc (bus: ptr Bus00; message: ptr Message00; userData: pointer): gboolean {.cdecl.}
|
||||||
|
|
||||||
|
macro mAddWatchFull(bus: gst.Bus; pri: cint; p: untyped; arg: typed; ignoreArg: bool): untyped =
|
||||||
|
var IdleID {.compileTime, global.}: int
|
||||||
|
inc(IdleID)
|
||||||
|
let ats = $getTypeInst(arg).toStrLit
|
||||||
|
let procName = "gstfunc_" & $IdleID
|
||||||
|
let procNameCdecl = "gstfunc_cdecl_" & $IdleID
|
||||||
|
|
||||||
|
var r1s = """
|
||||||
|
|
||||||
|
proc $1(self: ptr gst.Bus00; msg: ptr gst.Message00; userData: pointer): gboolean {.cdecl.} =
|
||||||
|
var bus1 {.global.}: gst.Bus
|
||||||
|
if bus1.isNil:
|
||||||
|
new bus1
|
||||||
|
GC_ref(bus1) # never call destroy/finalizer on this global variable
|
||||||
|
bus1.ignoreFinalizer = true
|
||||||
|
bus1.impl = self
|
||||||
|
|
||||||
|
var msg1 {.global.}: gst.Message
|
||||||
|
if msg1.isNil:
|
||||||
|
new msg1
|
||||||
|
GC_ref(msg1) # never call destroy/finalizer on this global variable
|
||||||
|
msg1.ignoreFinalizer = true
|
||||||
|
msg1.impl = msg
|
||||||
|
|
||||||
|
$2(bus1, msg1"""
|
||||||
|
|
||||||
|
if not ignoreArg.boolVal:
|
||||||
|
if getTypeInst(arg).typeKind == ntyRef:
|
||||||
|
r1s.add(", cast[$3](userdata)")
|
||||||
|
else:
|
||||||
|
r1s.add(", cast[$3](userdata)")
|
||||||
|
r1s.add(").gboolean\n")
|
||||||
|
|
||||||
|
r1s = r1s % [$procNameCdecl, $p, ats]
|
||||||
|
|
||||||
|
let r2s =
|
||||||
|
if ignoreArg.boolVal:
|
||||||
|
|
||||||
|
"""
|
||||||
|
proc $1(self: gst.Bus; pri: cint): cuint {.discardable.} =
|
||||||
|
gst_bus_add_watch_full(cast[ptr gst.Bus00](self.impl), pri, $5, nil, nil)
|
||||||
|
|
||||||
|
$1($5, $3, $4)
|
||||||
|
""" % [$procName, ats, $pri, "cond", $procNameCdecl, $(arg.toStrLit), $(bus.toStrLit)]
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
"""
|
||||||
|
proc $1(self: gst.Bus; pri: cint; a: $2): cuint {.discardable.} =
|
||||||
|
when (a is ref object):
|
||||||
|
GC_ref(a)
|
||||||
|
return gst_bus_add_watch_full(cast[ptr gst.Bus00](self.impl), pri, $5, cast[pointer](a), nil)
|
||||||
|
else:
|
||||||
|
var ar: ref $2
|
||||||
|
new(ar)
|
||||||
|
#deepCopy(ar[], a)
|
||||||
|
ar[] = a
|
||||||
|
GC_ref(ar)
|
||||||
|
return gst_bus_add_watch_full(cast[ptr gst.Bus00](self.impl), pri, $5, cast[pointer](ar[]), nil)
|
||||||
|
$1($7, $3, $6)
|
||||||
|
""" % [$procName, ats, $(pri.toStrLit), "fixedCond", $procNameCdecl, $(arg.toStrLit), $(bus.toStrLit)]
|
||||||
|
|
||||||
|
#echo r1s
|
||||||
|
#echo r2s
|
||||||
|
result = parseStmt(r1s & r2s)
|
||||||
|
|
||||||
|
template addWatch*(bus: gst.Bus; pri: int; p: untyped; arg: typed): untyped =
|
||||||
|
maddWatchFull(bus, pri.cint, p, arg, false)
|
||||||
|
|
||||||
|
template addWatch*(bus: gst.Bus; pri: int; p: untyped): untyped =
|
||||||
|
maddWatchFull(bus, pri.cint, p, "", true)
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# High level gobject-introspection based GTK3/GTK4 bindings for the Nim programming language
|
# High level gobject-introspection based GTK3/GTK4 bindings for the Nim programming language
|
||||||
# nimpretty --maxLineLen:130 gen.nim
|
# nimpretty --maxLineLen:130 gen.nim
|
||||||
# v 0.8.6 2021-JAN-11
|
# v 0.8.6 2021-JAN-13
|
||||||
# (c) S. Salewski 2018
|
# (c) S. Salewski 2018
|
||||||
|
|
||||||
# usefull for finding death code:
|
# usefull for finding death code:
|
||||||
|
|
@ -1421,7 +1421,7 @@ proc writeMethod(info: GIBaseInfo; minfo: GIFunctionInfo) =
|
||||||
# export only low level symbols that are needed internally -- maybe we should export only a private name for them
|
# export only low level symbols that are needed internally -- maybe we should export only a private name for them
|
||||||
if run == 0:
|
if run == 0:
|
||||||
if sym in ["g_quark_from_static_string", "g_error_free", "g_object_get_qdata", "g_object_ref_sink", "g_object_unref",
|
if sym in ["g_quark_from_static_string", "g_error_free", "g_object_get_qdata", "g_object_ref_sink", "g_object_unref",
|
||||||
"g_timeout_add_full", "g_object_is_floating",
|
"g_timeout_add_full", "g_object_is_floating", "gst_bus_add_watch_full",
|
||||||
"g_type_from_name", "g_type_check_instance_is_a", "g_idle_add_full", "g_quark_try_string", "vte_regex_unref",
|
"g_type_from_name", "g_type_check_instance_is_a", "g_idle_add_full", "g_quark_try_string", "vte_regex_unref",
|
||||||
"gtk_builder_get_object", "g_action_map_add_action", "gtk_drawing_area_set_draw_func", "g_io_add_watch_full"]:
|
"gtk_builder_get_object", "g_action_map_add_action", "gtk_drawing_area_set_draw_func", "g_io_add_watch_full"]:
|
||||||
methodBuffer.write("\nproc " & sym & EM & pars.plist)
|
methodBuffer.write("\nproc " & sym & EM & pars.plist)
|
||||||
|
|
@ -2942,6 +2942,17 @@ proc fromStringStructure*(string: cstring; `end`: var string): Structure =
|
||||||
result.impl = impl0
|
result.impl = impl0
|
||||||
`end` = $(end_00)
|
`end` = $(end_00)
|
||||||
|
|
||||||
|
# https://discourse.gnome.org/t/gst-buffer-copy-is-missing-in-gir-file/5327
|
||||||
|
# GstMiniObject * gst_mini_object_copy (const GstMiniObject *mini_object);
|
||||||
|
proc gst_buffer_copy(self: ptr Buffer00): ptr Buffer00 {.
|
||||||
|
importc: "gst_mini_object_copy", libprag.}
|
||||||
|
|
||||||
|
proc copy*(self: Buffer): Buffer =
|
||||||
|
fnew(result, gBoxedFreeGstBuffer)
|
||||||
|
result.impl = gst_buffer_copy(cast[ptr Buffer00](self.impl))
|
||||||
|
|
||||||
|
include gimplgst
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
const Pango_EPI = """
|
const Pango_EPI = """
|
||||||
|
|
@ -3590,6 +3601,7 @@ proc init* =
|
||||||
output.write("include gimplgobj\n")
|
output.write("include gimplgobj\n")
|
||||||
output.write("include gimplgtk\n")
|
output.write("include gimplgtk\n")
|
||||||
buildableList.add("MenuModel")
|
buildableList.add("MenuModel")
|
||||||
|
buildableList.add("FileChooserNative")
|
||||||
for i in buildableList:
|
for i in buildableList:
|
||||||
var prefix = "gtk"
|
var prefix = "gtk"
|
||||||
var modname = fixedModName(moduleNamespace)
|
var modname = fixedModName(moduleNamespace)
|
||||||
|
|
@ -3808,7 +3820,7 @@ launch()
|
||||||
# if not xcallerAlloc.contains(el):
|
# if not xcallerAlloc.contains(el):
|
||||||
# echo el
|
# echo el
|
||||||
|
|
||||||
# 3741 lines Extern import writeConst gBaseInfoGetNamespace atk writeStruct attribute destroy writeMethode builder
|
# 3741 lines Extern import writeConst gBaseInfoGetNamespace atk writeStruct attribute destroy writeMethode builder include EM
|
||||||
# gtk_icon_view_get_tooltip_context bug Candidate
|
# gtk_icon_view_get_tooltip_context bug Candidate
|
||||||
# gtk_tree_view_get_cursor bug
|
# gtk_tree_view_get_cursor bug
|
||||||
#
|
#
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue