Files
go-gst/gst/gstwebrtc/session_description.go
Chris Bainbridge 2208901b27 webrtc: fix nil deref marshal *SessionDescription
When the SessionDescription pointer is nil, ToGValue() will crash
dereferencing the pointer. Fix this by avoiding the dereference.

The ability to marshal a NULL value is required for compatibility with
other languages. This change has been tested with the custom signalling
from gst-plugins-rs, in which a signaller may emit a `session-requested`
signal with an optional offer SDP.
2024-10-28 09:45:02 +00:00

125 lines
3.4 KiB
Go

package gstwebrtc
// #include "gst.go.h"
import "C"
import (
"runtime"
"unsafe"
"github.com/go-gst/go-glib/glib"
"github.com/go-gst/go-gst/gst/gstsdp"
)
func init() {
tm := []glib.TypeMarshaler{
{T: glib.Type(C.GST_TYPE_WEBRTC_SESSION_DESCRIPTION), F: marshalSessionDescription},
}
glib.RegisterGValueMarshalers(tm)
}
type SessionDescription struct {
ptr *C.GstWebRTCSessionDescription
}
func NewSessionDescription(t SDPType, sdp *gstsdp.Message) *SessionDescription {
sd := C.gst_webrtc_session_description_new(
C.GstWebRTCSDPType(t),
(*C.GstSDPMessage)(sdp.UnownedCopy().Instance()),
)
return wrapSessionDescriptionAndFinalize(sd)
}
func wrapSessionDescriptionAndFinalize(sdp *C.GstWebRTCSessionDescription) *SessionDescription {
sd := &SessionDescription{
ptr: sdp,
}
// this requires that we copy the SDP message before passing it to any transfer-ownership function
runtime.SetFinalizer(sd, func(sd *SessionDescription) {
sd.Free()
})
return sd
}
// W3RTCSessionDescription is used to marshal/unmarshal SessionDescription to/from JSON.
//
// We cannot implement the json.(Un-)Marshaler interfaces on SessionDescription directly because
// the finalizer would run and free the memory, because the value would have to be copied.
//
// it complies with the WebRTC spec for SessionDescription, see https://www.w3.org/TR/webrtc/#rtcsessiondescription-class
type W3RTCSessionDescription struct {
Type string `json:"type"`
Sdp string `json:"sdp"`
}
// ToGstSDP converts a W3RTCSessionDescription to a SessionDescription
func (w3SDP *W3RTCSessionDescription) ToGstSDP() (*SessionDescription, error) {
sdp, err := gstsdp.ParseSDPMessage(w3SDP.Sdp)
if err != nil {
return nil, err
}
return NewSessionDescription(SDPTypeFromString(w3SDP.Type), sdp), nil
}
// ToW3SDP returns a W3RTCSessionDescription that can be marshaled to JSON
func (sd *SessionDescription) ToW3SDP() W3RTCSessionDescription {
jsonSDP := W3RTCSessionDescription{
Type: SDPType(sd.ptr._type).String(),
Sdp: gstsdp.NewMessageFromUnsafe(unsafe.Pointer(sd.ptr.sdp)).String(),
}
return jsonSDP
}
func (sd *SessionDescription) Free() {
C.gst_webrtc_session_description_free(sd.ptr)
}
// UnownedCopy creates a new copy of the SessionDescription that will not be finalized
//
// this is needed for passing the SessionDescription to other functions that will take ownership of it.
//
// used in the bindings, should not be called by application code
func (sd *SessionDescription) UnownedCopy() *SessionDescription {
newSD := C.gst_webrtc_session_description_copy(sd.ptr)
return &SessionDescription{
ptr: newSD,
}
}
// Copy creates a new copy of the SessionDescription
func (sd *SessionDescription) Copy() *SessionDescription {
return wrapSessionDescriptionAndFinalize(sd.UnownedCopy().ptr)
}
// ToGValue implements glib.ValueTransformer
func (sd *SessionDescription) ToGValue() (*glib.Value, error) {
val, err := glib.ValueInit(glib.Type(C.GST_TYPE_WEBRTC_SESSION_DESCRIPTION))
if err != nil {
return nil, err
}
var ptr *C.GstWebRTCSessionDescription
if sd != nil {
ptr = sd.ptr
}
val.SetBoxed(unsafe.Pointer(ptr))
return val, nil
}
func marshalSessionDescription(p unsafe.Pointer) (interface{}, error) {
c := C.g_value_get_boxed((*C.GValue)(p))
// we don't own this memory, so we need to copy it to prevent other code from freeing it
ref := &SessionDescription{
ptr: (*C.GstWebRTCSessionDescription)(c),
}
return ref.Copy(), nil
}