Closed demotomohiro closed 3 years ago
Thanks for reporting. I don't know much about libnice myself but I will try to investigate that issue soon.
OK, you may try with nimble install gintro@#head
For this issue it seems that the bug was only that NiceCandidate was not marked as "light" object which needs no proxy object. We have a list of that entities in gen.nim generator script and I added one line for this fix. So that type has fields now, and we generally use plain stack variables of that type and do not allocate it with a function on the heap. I hope it will work for you now. (We can not avoid these need for manually fixes, I had a discussion with Mr. Droege from Rust team about these issues.)
And we have support for optional out var parameters now. That was some more work, and it is not much tested yet. A lot of code has changed. There is at least one known bug -- a few procs have optional seq parameter, that would crash if no value is provided. We will fix later.
Thank you for your fix!
But it still doesn't work.
This is gintro generated code that define Candidate
type with Address00
type:
type
Candidate* {.pure, byRef.} = object
`type`*: CandidateType
transport*: CandidateTransport
`addr`*: Address00
baseAddr*: Address00
priority*: uint32
streamId*: uint32
componentId*: uint32
foundation*: array[33, int8]
username*: cstring
password*: cstring
And this is gintro generated code that define Address00
type and Address
type:
type
Address00* {.pure.} = object
Address* = ref object
impl*: ptr Address00
ignoreFinalizer*: bool
nice
module exports procs that takes Address
but it doesn't export procs that takes Address00
.
And I cannot use procs that get port or address string from Address
to addr
or baseAddr
fields of Candidate
type.
I think NiceAddress
is also need to be marked as "light" object like NiceCandidate
.
This is gintro generated code that declares nice_agent_get_selected_pair
proc and getSelectedPair
proc.
proc nice_agent_get_selected_pair(self: ptr Agent00; streamId: uint32; componentId: uint32;
local: Candidate; remote: Candidate): gboolean {.
importc, libprag.}
proc getSelectedPair*(self: Agent; streamId: int; componentId: int;
local: Candidate; remote: Candidate): bool =
toBool(nice_agent_get_selected_pair(cast[ptr Agent00](self.impl), uint32(streamId), uint32(componentId), local, remote))
But nice_agent_get_selected_pair
function has local
and remote
paramaters as NiceCandidate **
(pointer to pointer to NiceCandidate) type.
https://libnice.freedesktop.org/libnice/NiceAgent.html#nice-agent-get-selected-pair
nice_agent_get_selected_pair
function returns the address of existing NiceCandidate
object by writing it to local
and remote
arguments.
So when I use nice_agent_get_selected_pair
function to get NiceCandidate
, I have to declare 2 pointers to NiceCandidate
and pass addresses of these 2 pointers to nice_agent_get_selected_pair
.
Thus, nice_agent_get_selected_pair
proc and getSelectedPair
proc should be declared like this:
proc nice_agent_get_selected_pair(self: ptr Agent00; streamId: uint32; componentId: uint32;
local: var ptr Candidate; remote: var ptr Candidate): gboolean {.
importc, libprag.}
proc getSelectedPair*(self: Agent; streamId: int; componentId: int;
local: var ptr Candidate; remote: var ptr Candidate): bool =
toBool(nice_agent_get_selected_pair(cast[ptr Agent00](self.impl), uint32(streamId), uint32(componentId), local, remote))
And getSelectedPair
is used like this:
var local, remote: ptr Candidate
let ret = agent.getSelectedPair(streamId, componentId, local.addr, remote.addr)
let port = local.`addr`.port
# local and remote must not be freed.
OK, I have finished the GtkLabel example in the GTK4 book finally (http://ssalewski.de/gtkprogramming.html#_label) so I can work again on your issue.
You are right of course, the nice.Address data type has to be a "light" type with fields and without a proxy object. That type is reported by gobject-introspection as struct with zero fields. But it has a field of type union. So we have to create that type manually. All that is not that easy and straight forward, so I will convert your linked C code example from above to Nim and test it myself. I will try to do it in the next few days.
I will convert your linked C code example
That is really hard :-)
I think I will finally get it working, but I wonder if it really makes much sense to write so much low level code tightly coupled to a low level C lib in Nim.
nice.Address
type can be defined like this in Nim:
import nativesockets
type
# See https://gitlab.freedesktop.org/libnice/libnice/-/blob/master/agent/address.h
Address* {.union.} = object
`addr`*: SockAddr
ip4*: Sockaddr_in
ip6*: Sockaddr_in6
SockAddr
, Sockaddr_in
and Sockaddr_in6
are defined in winlean
module on windows and in posix
module in other os.
And nativesockets module export these types from winlean or posix module.
nice.Candidate
object type contains nice.Address
type.
I think Nim compiler need to know sizeof nice.Address
so that memory layout of nice.Candidate
object type in Nim and NiceCandidate
type in libnice C code are match and we can access each fields of nice.Candidate
correctly.
If libnice provided setters and getters to access each fields of NiceCandidate
like void nice_candidate_set_addr(NiceCandidate *candidate, const NiceAddress *addr)
or NiceAddress* nice_candidate_get_addr(const NiceCandidate *candidate)
, we would not need to define each fields of nice.Address
or nice.Candidate
.
Thanks.
Yesterday I tried to get GSList support working, as that C example used GSList. Have still to test. nice.Candidate in now again a heavy object with proxy, but for Candidate00 we generate fields, so we can access fields like cand.impl.priority = uint32(parseInt(tokens[1])). Ugly, but we can create access functions manually later.
Then there is glib.ioAddWatch() which adds a callback, I think I have to create a macro for this.
I hope I will get is working this weekend.
OK, you can try again :-)
The example program
https://github.com/StefanSalewski/gintro/blob/master/examples/gtk3/simple_example.nim
looks fine now, compiles and seems to have similar output as C version. But again I can not test on windows or test real function of that program as I have currently only one Linux computer available.
I added some new macros in files gimpglib.nim and gimplnice.nim. Macros is always hard, seems to work, but maybe you can improve them yourself. And you may test with and without the last optional parameter and for different types of the optional parameter. I have not done all that tests. For NiceAddress I used your suggestion, and I had some discussion with Mr. Droege, see https://discourse.gnome.org/t/size-of-nicecandidate-of-libnice/4913
Thank you for your hard work!
I tried to run simple_example.nim but parsing remote candidate generate error. I will try to fix it and send PR.
You can test with only one computer.
Open 2 consoles and run example program on each console.
one program need to be run with argument "0" and another program run with argument "1".
Then, copy output text to text editor, make it one line and copy it to another console.
If you use Vim, you can remove new lines without inserting space with gJ
after selecting text with visual mode.
If you run 2 libnice programs at same time on one PC, they choose different port (looks like they choose port randomly) and they can communicate each other after they got remote candidates.
Thanks for instructions to test it. I have just launched the C version, that one works. Will try to fix the Nim version today.
Found a few typos.
Main bug is use of
grep -A20 "seq2GSList\*" ~/.nimble/pkgs/gintro-#head/gintro/gobject.nim
proc seq2GSList*[T](s: seq[T]): ptr glib.SList =
var l: ptr glib.SList
var i = s.len
while i > 0:
dec(i)
when T is gobject.Object: # or T is pango.Item:
l = g_slist_prepend(l, s[i].impl)
elif T is cstring:
l = g_slist_prepend(l, s[i]) # cstring
else:
discard # make it compile for these cases, of course will not work
return l
I think it was never tested before, and obviously do not work for NiceCandidate as NiceCandidate in not a GObject.
Will try to fix it today. If I remember correctly I used the discard statement because it was unclear which objects have the impl field, so I restrict to strings and GObjects that time.
I have fixed the typos, but when I try to make seq2GSList() working compile fails with message:
Error: template instantiation too nested
That is very ugly, I have no idea which really causes that error.
For now I will send you the file with fixed typos:
## https://gitlab.freedesktop.org/libnice/libnice/-/blob/master/examples/simple-example.c
## nim c --gc:arc simple_example.nim
## Example using libnice to negotiate a UDP connection between two clients,
## possibly on the same network or behind different NATs and/or stateful
## firewalls.
##
## Run two clients, one controlling and one controlled:
## simple-example 0 $(host -4 -t A stun.stunprotocol.org | awk '{ print $4 }')
## simple-example 1 $(host -4 -t A stun.stunprotocol.org | awk '{ print $4 }')
##
# https://forum.nim-lang.org/t/3752
when (compiles do: import gintro/gtk):
import gintro/[gtk, glib, gobject, gio, nice]
else:
import gintro/[dummygtk, glib, gobject, gio, nice] # For windows with glib but no gtk
from strutils import `%`, split
from os import paramCount, paramStr
from strutils import parseInt
from posix import INET6_ADDRSTRLEN
when defined(windows):
import winlean
else:
discard # import posix
var
gloop: glib.MainLoop
ioStdin: glib.IOChannel
streamId: int
candidateTypeName = ["host", "srflx", "prflx", "relay"]
stateName = ["disconnected", "gathering", "connecting", "connected", "ready", "failed"]
proc gMsg(s: string) =
stdout.write(s)
flushFile(stdout)
proc gDebug(s: string) =
stdout.write(s)
flushFile(stdout)
proc gError(s: string) =
stderr.write(s)
flushFile(stderr)
proc toStringVal(s: string): Value =
let gtype = gStringGetType()
discard init(result, gtype)
setString(result, s)
proc toIntVal(i: int): Value =
let gtype = typeFromName("gint")
discard init(result, gtype)
setInt(result, i)
proc printLocalData(agent: nice.Agent; streamId: int; componentId: int): int =
var
localUfrag: string
localPassword: string
ipaddr = newString(INET6_ADDRSTRLEN).cstring # not really nice as that cstring is used as out parameter by toString()
cands: seq[Candidate]
result = QuitFailure
block gotoEnd:
if not getLocalCredentials(agent, streamId, localUfrag, localPassword):
break gotoEnd
cands = getLocalCandidates(agent, streamId, componentId)
if cands.len == 0:
break gotoEnd
stdout.write(localUfrag, ' ', localPassword)
for el in cands:
toString(el.impl.`addr`, ipaddr)
## (foundation),(prio),(addr),(port),(type)
stdout.write(' ', cast[cstring](addr el.impl.foundation), ',', el.impl.priority, ',', ipaddr, ',', getPort(el.impl.`addr`),
',', candidateTypeName[el.impl.`type`.ord])
echo ""
# end label
result = QuitSuccess
return result
proc parseCandidate(scand: string; streamId: int): nice.Candidate =
var
cand: nice.Candidate = nil
ntype: nice.CandidateType = CandidateType.host # that initialization is never used!
tokens = scand.split(',', 5)
block gotoEnd:
for i in 0 .. 4:
if tokens[i] == "":
break gotoEnd
#[ should work too
for i, el in candidateTypeName:
var h = false
if tokens[4] == el:
ntype = i
h = true
if not h:
break gotoEnd
]#
var i: int
while i < candidateTypeName.len:
if tokens[4] == candidateTypeName[i]:
ntype = CandidateType(i)
break # missing in initial release!
inc(i)
if i == candidateTypeName.len:
break gotoEnd
cand = newCandidate(ntype)
#cand.impl.componentId = 1
cand.setComponentID(1) # we have a few manually created getters and setters already
cand.impl.streamId = uint32(streamId)
cand.impl.transport = nice.CandidateTransport.udp
cand.setFoundation(tokens[0])
cand.impl.priority = uint32(parseInt(tokens[1]))
if not setFromString(cand.impl.`addr`, tokens[2]):
gMsg("failed to parse addr: " & tokens[2])
cand = nil
break gotoEnd
setPort(cand.impl.`addr`, parseInt(tokens[3]))
# end label
return cand
proc parseRemoteData(agent: Agent; streamId: int; componentId: int; line: string): cint =
var
remoteCandidates: seq[Candidate]
lineArgv: seq[string]
ufrag: string
passwd: string
result = QuitFailure
lineArgv = split(line, {' ', '\t', '\n'})
block gotoEnd:
for i, x in lineArgv:
if x.len == 0:
continue
## first two args are remote ufrag and password
if ufrag == "":
ufrag = x
elif passwd == "":
passwd = x
else:
## Remaining args are serialized canidates (at least one is required)
var c: nice.Candidate = parseCandidate(x, streamId)
if c == nil:
gMsg("failed to parse candidate: " & x)
break gotoEnd
remoteCandidates.add(c) # caution prepend in C code!
if ufrag == "" or passwd == "" or remoteCandidates.len == 0:
gMsg("line must have at least ufrag, password, and one candidate")
break gotoEnd
if not setRemoteCredentials(agent, streamId, ufrag, passwd):
gMsg("failed to set remote credentials")
break gotoEnd
## Note: this will trigger the start of negotiation.
if setRemoteCandidates(agent, streamId, componentId, remoteCandidates) < 1:
gMsg("failed to set remote candidates")
break gotoEnd
# end label
result = QuitSuccess
return result
proc stdinRemoteInfoCb(source: glib.IOChannel; cond: glib.IOCondition; agent: nice.Agent): bool =
var
line: string
rval: cint
ret: bool = true
if readLine(source, line) == IOStatus.normal:
## Parse remote candidate list and set it on the agent
rval = parseRemoteData(agent, streamId, 1, line)
if rval == QuitSuccess:
## Return FALSE so we stop listening to stdin since we parsed the
## candidates correctly
ret = false
gDebug("waiting for state READY or FAILED signal...")
else:
write(stderr, "ERROR: failed to parse remote data\n")
echo("Enter remote data (single line, no wrapping):")
stdout.write("> ")
flushFile(stdout)
return ret
proc cbCandidateGatheringDone(agent: nice.Agent; streamId: int) =
gDebug("SIGNAL candidate gathering done\n")
## Candidate gathering is done. Send our local candidates on stdout
echo("Copy this line to remote client:")
stdout.write("\n ")
discard printLocalData(agent, streamId, 1)
echo("")
## Listen on stdin for the remote candidate list
echo("Enter remote data (single line, no wrapping):")
addWatch(ioStdin, glib.PRIORITY_DEFAULT, IOCondition.in , stdinRemoteInfoCb, agent)
stdout.write("> ")
flushFile(stdout)
proc cbNewSelectedPair(agent: nice.Agent; streamId: int; componentId: int; lfoundation: string; rfoundation: string) =
gDebug("SIGNAL: selected pair " & lfoundation & " " & rfoundation)
proc stdinSendDataCb(source: IOChannel; cond: IOCondition; agent: nice.Agent): bool =
var line: string
if readLine(source, line) == IOStatus.normal:
discard send(agent, streamId, 1, line.len, line)
stdout.write("> ")
flushFile(stdout)
else:
discard send(agent, streamId, 1, 1, "\x00")
## Ctrl-D was pressed.
quit(gloop)
return true
proc cbComponentStateChanged(agent: nice.Agent; streamId: int; componentId: int; state: int) =
gDebug("SIGNAL: state changed $1 $2 $3[$4]\n" % [$streamId, $componentId, stateName[state], $state])
if state == ComponentState.connected.ord:
var
local: Candidate
remote: nice.Candidate
## Get current selected candidate pair and print IP address used
if getSelectedPair(agent, streamId, componentId, local, remote):
var ipaddr = newString(INET6_ADDRSTRLEN).cstring
toString(local.impl.`addr`, ipaddr)
echo("\nNegotiation complete: ([$1]:$2,", [$ipaddr, $getPort(local.impl.`addr`)])
toString(remote.impl.`addr`, ipaddr)
echo(" [$1]:$2)" % [$ipaddr, $getPort(remote.impl.`addr`)])
## Listen to stdin and send data written to it
echo("\nSend lines to remote (Ctrl-D to quit):")
addWatch(ioStdin, glib.PRIORITY_DEFAULT, IOCondition.in , stdinSendDataCb, agent)
stdout.write("> ")
flushFile(stdout)
elif state == ComponentState.failed.ord:
quit(gloop)
proc cbNiceRecv(agent: nice.Agent; streamID: cuint; componentID: cuint; len: cuint; buf: cstring) = #{.cdecl.} =
if len == 1 and buf[0] == '\x00':
glib.quit(gloop)
discard stdout.writeBuffer(buf, len)
flushfile(stdout)
proc main =
var
agent: nice.Agent
stunAddr: string
stunPort: int
controlling: int
## Parse arguments
let argc = paramCount() + 1
var argv = newSeq[string](argc)
for i in 0 ..< argc:
argv[i] = paramStr(i)
if argc > 4 or argc < 2 or argv[1].len > 1:
write(stderr, "Usage: $1 0|1 stun_addr [stun_port]\n" % [argv[0]])
quit(QuitFailure)
controlling = argv[1][0].ord - '0'.ord
if controlling != 0 and controlling != 1:
write(stderr, "Usage: $1 0|1 stun_addr [stun_port]\n" % [argv[0]])
quit(QuitFailure)
if argc > 2:
stunAddr = argv[2]
if argc > 3:
stunPort = parseInt(argv[3])
else:
stunPort = 3478
gDebug("Using stun server \'[$1]:$2\'\n" % [$stunAddr, $stunPort])
gio.networkingInit()
gloop = newMainLoop(nil, false)
# https://gist.github.com/demotomohiro/c9d9ad7e17639c6acb9bd2be7c204f3f
when hostOS == "windows":
ioStdin = win32NewFd(system.stdin.getFileHandle)
else:
ioStdin = unixNew(system.stdin.getFileHandle)
## Create the nice agent
agent = nice.newAgent(glib.getContext(gloop), nice.CompatibilityRfc5245)
if agent == nil:
gError("Failed to create agent")
## Set the STUN settings and controlling mode
if stunAddr.len > 0:
setProperty(agent, "stun-server", toStringVal(stunAddr))
setProperty(agent, "stun-server-port", toIntVal(stunPort))
setProperty(agent, "controlling-mode", toIntVal(controlling))
## Connect to the signals
agent.connect("candidate-gathering-done", cbCandidateGatheringDone)
agent.connect("new-selected-pair", cbNewSelectedPair)
agent.connect("component-state-changed", cbComponentStateChanged)
## Create a new stream with one component
streamId = addStream(agent, 1)
if streamId == 0:
gError("Failed to add stream")
## Attach to the component to receive the data
## Without this call, candidates cannot be gathered
attachRecv(agent, streamId.cuint, 1, getContext(gloop), cbNiceRecv)
## Start gathering local candidates
if not gatherCandidates(agent, streamId):
gError("Failed to start candidate gathering")
gDebug("waiting for candidate-gathering-done signal...")
## Run the mainloop. Everything else will happen asynchronously
## when the candidates are done gathering.
run(gloop)
quit(QuitSuccess)
main()
Was mainly some small typos and a missing break statement:
diff simple_example.nim ~/gintro/examples/gtk3/simple_example.nim
71c71
< stdout.write(localUfrag, ' ', localPassword)
---
> stdout.write(localUfrag, localPassword)
75c75
< stdout.write(' ', cast[cstring](addr el.impl.foundation), ',', el.impl.priority, ',', ipaddr, ',', getPort(el.impl.`addr`),
---
> stdout.write(' ', cast[cstring](addr el.impl.foundation), ',', el.impl.priority, ',', ipaddr, ' ', getPort(el.impl.`addr`),
104d103
< break # missing in initial release!
142c141
< var c: nice.Candidate = parseCandidate(x, streamId)
---
> var c: nice.Candidate = parseCandidate(lineArgv[i], streamId)
144c143
< gMsg("failed to parse candidate: " & x)
---
> gMsg("failed to parse candidate: " & lineArgv[i])
185c184
< stdout.write("\n ")
---
> echo(" ")
But with
grep -A20 "seq2GSList\*" ~/.nimble/pkgs/gintro-#head/gintro/gobject.nim
proc seq2GSList*[T](s: seq[T]): ptr glib.SList =
var l: ptr glib.SList
var i = s.len
while i > 0:
dec(i)
when true:#T is gobject.Object: # or T is pango.Item:
l = g_slist_prepend(l, s[i].impl)
elif T is cstring:
l = g_slist_prepend(l, s[i]) # cstring
else:
discard # make it compile for these cases, of course will not work
return l
it fails to compile with
Error: template instantiation too nested
Will ask on Nim forum and try again later. Only idea is to try a non generic seq2GSList for NiceCandidate in nice.nim module. Will maybe try later.
Well I found two more errors, both will take some time to fix. Will try to do it tomorrow.
I have fixed the typos, but when I try to make seq2GSList() working compile fails with message:
Error: template instantiation too nested
That is very ugly, I have no idea which really causes that error.
I don't get that compile error.
I fixed seq2GSList
like following code.
Any types that has impl
field are prepend with g_slist_prepend(l, s[i].impl)
And when T
doesn't match any supported type, it is compile error.
It worked with my test code.
proc seq2GSList*[T](s: seq[T]): ptr glib.SList =
var l: ptr glib.SList
var i = s.len
while i > 0:
dec(i)
when T is gobject.Object or compiles(T.impl):
l = g_slist_prepend(l, s[i].impl)
elif T is cstring:
l = g_slist_prepend(l, s[i]) # cstring
else:
{.error: "seq2GSList was called with unsupported type".}
return l
getSelectedPair
proc in gintro/nice
is defined incorrectly.
This is how getSelectedPair
and nice_agent_get_selected_pair
are defined:
proc nice_agent_get_selected_pair(self: ptr Agent00; streamId: uint32; componentId: uint32;
local: ptr Candidate00; remote: ptr Candidate00): gboolean {.
importc, libprag.}
proc getSelectedPair*(self: Agent; streamId: int; componentId: int;
local: Candidate; remote: Candidate): bool =
toBool(nice_agent_get_selected_pair(cast[ptr Agent00](self.impl), uint32(streamId), uint32(componentId), cast[ptr Candidate00](local.impl), cast[ptr Candidate00](remote.impl)))
They need to be fixed like this:
proc nice_agent_get_selected_pair(self: ptr Agent00; streamId: uint32; componentId: uint32;
local: var ptr Candidate00; remote: var ptr Candidate00): gboolean {.
importc, libprag.}
proc getSelectedPair*(self: Agent; streamId: int; componentId: int;
local: Candidate; remote: Candidate): bool =
toBool(nice_agent_get_selected_pair(cast[ptr Agent00](self.impl), uint32(streamId), uint32(componentId), local.impl, remote.impl))
It is used like this code:
var
local = Candidate(ignoreFinalizer: true)
remote = Candidate(ignoreFinalizer: true)
if getSelectedPair(agent, streamId, componentId, local, remote):
nice_agent_get_selected_pair
function in libnice returns pointers to NiceCandidate object by taking pointers to a pointer to a NiceCandidate object (it is not a pointer to NiceCandidate) and write an address of NiceCandidate object to the pointer that given pointer to pointer to NiceCandidate points to.
https://libnice.freedesktop.org/libnice/NiceAgent.html#nice-agent-get-selected-pair
I told you I will fix it. Done.
Works for me. Please test with
nimble install gintro@#head
Thank you!
gintro/examples/gtk3/simple_example.nim
worked on termux.
But it doesn't work on windows.
libnice_simple_test.nim
in https://github.com/StefanSalewski/gintro/pull/103/files worked on both windows and termux.
So I think nice module is fine but simple_example.nim
need to be fix.
I will try to fix it on windows and send PR.
Unfortunately I can not help you for your Windows issue, I have no idea. One reason could be that gintro does not work for 32 bit OS, I think no one ever has tested gintro with 32 bit. But I assume that your windows is 64 bit.
For the "Error: template instantiation too nested" I found the reason, it is the "when (compiles do: import gintro/gtk):" statement. That one seems to be very broken, see
https://forum.nim-lang.org/t/7222
I will add your libnice_simple_test.nim to the gtk3 examples tomorrow.
It seems we don't need to check existance of gintro/gtk
module.
When writing command line program that doesn't use gtk, importing gintro/dummygtk
module works even if there is gintro/gtk
module.
I have installed gtk3 and gtk4 using (MSYS2)[https://www.msys2.org] with following command:
pacman -S mingw-w64-x86_64-gtk3 mingw-w64-x86_64-gtk4
Then I reinstalled gintro with nimble install gintro@#head
and I got gintro/gtk
and gintro/gtk4
module.
I can build and run examples/gtk3/t0.nim
, examples/gtk3/app0.nim
, examples/gtk4/app0.nim
and examples/gtk4/textview.nim
.
I changed following import statements in examples/gtk3/simple_example.nim
when (compiles do: import gintro/gtk):
import gintro/[gtk, glib, gobject, gio, nice]
else:
import gintro/[dummygtk, glib, gobject, gio, nice] # For windows with glib but no gtk
to
import gintro/[dummygtk, glib, gobject, gio, nice] # For windows with glib but no gtk
and it compils and works without error.
Thank you for supporting libnice!
libnice has
nice-agent-get-selected-pair
function andlocal
andremote
parameters areNiceCandidate **
type. https://libnice.freedesktop.org/libnice/NiceAgent.html#nice-agent-get-selected-pairBut
local
andremote
parameters are declared asptr Candidate00
in gintro/nice and cannot get pointer toNiceCandidate
. This is gintro/nice code that wrapnice-agent-get-selected-pair
function.nice_agent_get_selected_pair
function returns the address of existingNiceCandidate
object by writing it tolocal
andremote
arguments. So it may not be freed by caller. You can see how to usenice_agent_get_selected_pair
incb_component_state_changed
function in this libnice C example code.libnice doesn't provide functions to query a value of
NiceCandidate
's fields. So I need to access fields ofNiceCandidate
directly to get an address or other data inNiceCandidate
. Incb_component_state_changed
andprint_local_data
functions in simple-example.c in libnice, it readsNiceCandidate
's fields directly. https://gitlab.freedesktop.org/libnice/libnice/-/blob/master/examples/simple-example.c ButCandidate00
type in gintro/nice wrapsNiceCandidate
type without fields.I think this can be fixed by adding following type in gintro/nice:
and declare
nice_agent_get_selected_pair
like this:and
getSelectedPair
like this:or