mirror of
https://github.com/go-gst/go-gst.git
synced 2025-09-27 04:15:56 +08:00
509 lines
17 KiB
Go
509 lines
17 KiB
Go
package gst
|
|
|
|
// #include "gst.go.h"
|
|
import "C"
|
|
|
|
import (
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
"unsafe"
|
|
|
|
"github.com/go-gst/go-glib/glib"
|
|
)
|
|
|
|
// Message is a Go wrapper around a GstMessage. It provides convenience methods for
|
|
// unref-ing and parsing the underlying messages.
|
|
type Message struct {
|
|
msg *C.GstMessage
|
|
}
|
|
|
|
// FromGstMessageUnsafeNone wraps the given unsafe.Pointer in a message. A ref is taken
|
|
// on the message and a runtime finalizer placed on the object.
|
|
func FromGstMessageUnsafeNone(msg unsafe.Pointer) *Message {
|
|
gomsg := ToGstMessage(msg)
|
|
gomsg.Ref()
|
|
runtime.SetFinalizer(gomsg, (*Message).Unref)
|
|
return gomsg
|
|
}
|
|
|
|
// FromGstMessageUnsafeFull wraps the given unsafe.Pointer in a message. No ref is taken
|
|
// and a finalizer is placed on the resulting object.
|
|
func FromGstMessageUnsafeFull(msg unsafe.Pointer) *Message {
|
|
gomsg := ToGstMessage(msg)
|
|
runtime.SetFinalizer(gomsg, (*Message).Unref)
|
|
return gomsg
|
|
}
|
|
|
|
// ToGstMessage converts the given pointer into a Message without affecting the ref count or
|
|
// placing finalizers.
|
|
func ToGstMessage(msg unsafe.Pointer) *Message { return wrapMessage((*C.GstMessage)(msg)) }
|
|
|
|
// Instance returns the underlying GstMessage object.
|
|
func (m *Message) Instance() *C.GstMessage { return C.toGstMessage(unsafe.Pointer(m.msg)) }
|
|
|
|
// Unref will call `gst_message_unref` on the underlying GstMessage, freeing it from memory.
|
|
func (m *Message) Unref() { C.gst_message_unref(m.Instance()) }
|
|
|
|
// Ref will increase the ref count on this message. This increases the total amount of times
|
|
// Unref needs to be called before the object is freed from memory. It returns the underlying
|
|
// message object for convenience.
|
|
func (m *Message) Ref() *Message {
|
|
C.gst_message_ref(m.Instance())
|
|
return m
|
|
}
|
|
|
|
// Copy will copy this object into a new Message.
|
|
func (m *Message) Copy() *Message {
|
|
newNative := C.gst_message_copy(m.Instance())
|
|
return FromGstMessageUnsafeFull(unsafe.Pointer(newNative))
|
|
}
|
|
|
|
// Source returns the source of the message.
|
|
func (m *Message) Source() string { return C.GoString(m.Instance().src.name) }
|
|
|
|
// Type returns the MessageType of the message.
|
|
func (m *Message) Type() MessageType {
|
|
return MessageType(m.Instance()._type)
|
|
}
|
|
|
|
// TypeName returns a Go string of the GstMessageType name.
|
|
func (m *Message) TypeName() string {
|
|
return C.GoString(C.gst_message_type_get_name((C.GstMessageType)(m.Type())))
|
|
}
|
|
|
|
// GetStructure returns the GstStructure of this message, using the type of the message
|
|
// to determine the method to use. The returned structure must not be freed.
|
|
func (m *Message) GetStructure() *Structure {
|
|
var st *C.GstStructure
|
|
|
|
switch m.Type() {
|
|
case MessageError:
|
|
C.gst_message_parse_error_details(m.Instance(), (**C.GstStructure)(unsafe.Pointer(&st)))
|
|
case MessageInfo:
|
|
C.gst_message_parse_info_details(m.Instance(), (**C.GstStructure)(unsafe.Pointer(&st)))
|
|
case MessageWarning:
|
|
C.gst_message_parse_warning_details(m.Instance(), (**C.GstStructure)(unsafe.Pointer(&st)))
|
|
case MessageElement:
|
|
st = C.gst_message_get_structure(m.Instance())
|
|
case MessageApplication:
|
|
st = C.gst_message_get_structure(m.Instance())
|
|
}
|
|
|
|
// if no structure was returned, immediately return nil
|
|
if st == nil {
|
|
return nil
|
|
}
|
|
|
|
return structureFromGlibNone(st)
|
|
}
|
|
|
|
// parseToError returns a new GError from this message instance. There are multiple
|
|
// message types that parse to this interface.
|
|
func (m *Message) parseToError() *GError {
|
|
var gerr *C.GError
|
|
var debugInfo *C.gchar
|
|
|
|
switch m.Type() {
|
|
case MessageError:
|
|
C.gst_message_parse_error(m.Instance(), (**C.GError)(unsafe.Pointer(&gerr)), (**C.gchar)(unsafe.Pointer(&debugInfo)))
|
|
case MessageInfo:
|
|
C.gst_message_parse_info(m.Instance(), (**C.GError)(unsafe.Pointer(&gerr)), (**C.gchar)(unsafe.Pointer(&debugInfo)))
|
|
case MessageWarning:
|
|
C.gst_message_parse_warning(m.Instance(), (**C.GError)(unsafe.Pointer(&gerr)), (**C.gchar)(unsafe.Pointer(&debugInfo)))
|
|
}
|
|
|
|
// if error was nil return immediately
|
|
if gerr == nil {
|
|
return nil
|
|
}
|
|
|
|
// cleanup the C error immediately and let the garbage collector
|
|
// take over from here.
|
|
defer C.g_error_free((*C.GError)(gerr))
|
|
defer C.g_free((C.gpointer)(debugInfo))
|
|
return &GError{
|
|
errMsg: C.GoString(gerr.message),
|
|
structure: m.GetStructure(),
|
|
debugStr: strings.TrimSpace(C.GoString((*C.gchar)(debugInfo))),
|
|
}
|
|
}
|
|
|
|
// ParseInfo is identical to ParseError. The returned types are the same. However,
|
|
// this is intended for use with GstMessageType `GST_MESSAGE_INFO`.
|
|
func (m *Message) ParseInfo() *GError {
|
|
return m.parseToError()
|
|
}
|
|
|
|
// ParseWarning is identical to ParseError. The returned types are the same. However,
|
|
// this is intended for use with GstMessageType `GST_MESSAGE_WARNING`.
|
|
func (m *Message) ParseWarning() *GError {
|
|
return m.parseToError()
|
|
}
|
|
|
|
// ParseError will return a GError from the contents of this message. This will only work
|
|
// if the GstMessageType is `GST_MESSAGE_ERROR`.
|
|
func (m *Message) ParseError() *GError {
|
|
return m.parseToError()
|
|
}
|
|
|
|
// ParseStateChanged will return the old and new states as Go strings. This will only work
|
|
// if the GstMessageType is `GST_MESSAGE_STATE_CHANGED`.
|
|
func (m *Message) ParseStateChanged() (oldState, newState State) {
|
|
var gOldState, gNewState C.GstState
|
|
C.gst_message_parse_state_changed(m.Instance(), (*C.GstState)(unsafe.Pointer(&gOldState)), (*C.GstState)(unsafe.Pointer(&gNewState)), nil)
|
|
oldState = State(gOldState)
|
|
newState = State(gNewState)
|
|
return
|
|
}
|
|
|
|
// ParseTags extracts the tag list from the GstMessage. Tags are copied.
|
|
func (m *Message) ParseTags() *TagList {
|
|
var tagList *C.GstTagList
|
|
C.gst_message_parse_tag(m.Instance(), &tagList)
|
|
if tagList == nil {
|
|
return nil
|
|
}
|
|
return FromGstTagListUnsafeFull(unsafe.Pointer(tagList))
|
|
}
|
|
|
|
// ParseTOC extracts the TOC from the GstMessage. The TOC returned in the output argument is
|
|
// a copy.
|
|
func (m *Message) ParseTOC() (toc *TOC, updated bool) {
|
|
var gtoc *C.GstToc
|
|
var gupdated C.gboolean
|
|
C.gst_message_parse_toc(m.Instance(), >oc, &gupdated)
|
|
return FromGstTOCUnsafeFull(unsafe.Pointer(gtoc)), gobool(gupdated)
|
|
}
|
|
|
|
// ParseStreamStatus parses the stream status type of the message as well as the element
|
|
// that produced it.
|
|
func (m *Message) ParseStreamStatus() (StreamStatusType, *Element) {
|
|
var cElem *C.GstElement
|
|
var cStatusType C.GstStreamStatusType
|
|
C.gst_message_parse_stream_status(
|
|
m.Instance(),
|
|
(*C.GstStreamStatusType)(&cStatusType),
|
|
(**C.GstElement)(&cElem),
|
|
)
|
|
return StreamStatusType(cStatusType), FromGstElementUnsafeNone(unsafe.Pointer(cElem))
|
|
}
|
|
|
|
// ParseAsyncDone extracts the running time from the async task done message.
|
|
func (m *Message) ParseAsyncDone() ClockTime {
|
|
var clockTime C.GstClockTime
|
|
C.gst_message_parse_async_done(m.Instance(), &clockTime)
|
|
return ClockTime(clockTime)
|
|
}
|
|
|
|
// BufferingStats represents the buffering stats as retrieved from a GST_MESSAGE_TYPE_BUFFERING.
|
|
type BufferingStats struct {
|
|
// The buffering mode
|
|
BufferingMode BufferingMode
|
|
// The average input rate
|
|
AverageIn int
|
|
// The average output rate
|
|
AverageOut int
|
|
// Amount of time until buffering is complete
|
|
BufferingLeft time.Duration
|
|
}
|
|
|
|
// ParseBuffering extracts the buffering percent from the GstMessage.
|
|
func (m *Message) ParseBuffering() int {
|
|
var cInt C.gint
|
|
C.gst_message_parse_buffering(m.Instance(), &cInt)
|
|
return int(cInt)
|
|
}
|
|
|
|
// ParseBufferingStats extracts the buffering stats values from message.
|
|
func (m *Message) ParseBufferingStats() *BufferingStats {
|
|
var mode C.GstBufferingMode
|
|
var avgIn, avgOut C.gint
|
|
var bufLeft C.gint64
|
|
C.gst_message_parse_buffering_stats(
|
|
m.Instance(),
|
|
&mode, &avgIn, &avgOut, &bufLeft,
|
|
)
|
|
return &BufferingStats{
|
|
BufferingMode: BufferingMode(mode),
|
|
AverageIn: int(avgIn),
|
|
AverageOut: int(avgOut),
|
|
BufferingLeft: time.Duration(int64(bufLeft)) * time.Millisecond,
|
|
}
|
|
}
|
|
|
|
// StepStartValues represents the values inside a StepStart message.
|
|
type StepStartValues struct {
|
|
Active bool
|
|
Format Format
|
|
Amount uint64
|
|
Rate float64
|
|
Flush bool
|
|
Intermediate bool
|
|
}
|
|
|
|
// ParseStepStart extracts the values for the StepStart message.
|
|
func (m *Message) ParseStepStart() *StepStartValues {
|
|
var active, flush, intermediate C.gboolean
|
|
var amount C.guint64
|
|
var rate C.gdouble
|
|
var format C.GstFormat
|
|
C.gst_message_parse_step_start(
|
|
m.Instance(),
|
|
&active, &format, &amount, &rate, &flush, &intermediate,
|
|
)
|
|
return &StepStartValues{
|
|
Active: gobool(active),
|
|
Format: Format(format),
|
|
Amount: uint64(amount),
|
|
Rate: float64(rate),
|
|
Flush: gobool(flush),
|
|
Intermediate: gobool(intermediate),
|
|
}
|
|
}
|
|
|
|
// StepDoneValues represents the values inside a StepDone message.
|
|
type StepDoneValues struct {
|
|
Format Format
|
|
Amount uint64
|
|
Rate float64
|
|
Flush bool
|
|
Intermediate bool
|
|
Duration time.Duration
|
|
EOS bool
|
|
}
|
|
|
|
// ParseStepDone extracts the values for the StepDone message.
|
|
func (m *Message) ParseStepDone() *StepDoneValues {
|
|
var format C.GstFormat
|
|
var amount, duration C.guint64
|
|
var rate C.gdouble
|
|
var flush, intermediate, eos C.gboolean
|
|
C.gst_message_parse_step_done(
|
|
m.Instance(),
|
|
&format,
|
|
&amount,
|
|
&rate,
|
|
&flush,
|
|
&intermediate,
|
|
&duration,
|
|
&eos,
|
|
)
|
|
return &StepDoneValues{
|
|
Format: Format(format),
|
|
Amount: uint64(amount),
|
|
Rate: float64(rate),
|
|
Flush: gobool(flush),
|
|
Intermediate: gobool(intermediate),
|
|
Duration: time.Duration(uint64(duration)) * time.Nanosecond,
|
|
EOS: gobool(eos),
|
|
}
|
|
}
|
|
|
|
// ParseNewClock parses the new Clock in the message. The clock object returned
|
|
// remains valid until the message is freed.
|
|
func (m *Message) ParseNewClock() *Clock {
|
|
var clock *C.GstClock
|
|
C.gst_message_parse_new_clock(m.Instance(), &clock)
|
|
return FromGstClockUnsafeNone(unsafe.Pointer(clock))
|
|
}
|
|
|
|
// ParseClockProvide extracts the clock and ready flag from the GstMessage.
|
|
// The clock object returned remains valid until the message is freed.
|
|
func (m *Message) ParseClockProvide() (clock *Clock, ready bool) {
|
|
var gclock *C.GstClock
|
|
var gready C.gboolean
|
|
C.gst_message_parse_clock_provide(m.Instance(), &gclock, &gready)
|
|
return FromGstClockUnsafeNone(unsafe.Pointer(clock)), gobool(gready)
|
|
}
|
|
|
|
// ParseStructureChange extracts the change type and completion status from the GstMessage.
|
|
// If the returned bool is true, the change is still in progress.
|
|
func (m *Message) ParseStructureChange() (chgType StructureChangeType, owner *Element, busy bool) {
|
|
var gElem *C.GstElement
|
|
var gbusy C.gboolean
|
|
var gchgType C.GstStructureChangeType
|
|
C.gst_message_parse_structure_change(
|
|
m.Instance(),
|
|
&gchgType, &gElem, &gbusy,
|
|
)
|
|
return StructureChangeType(gchgType), wrapElement(toGObject(unsafe.Pointer(gElem))), gobool(gbusy)
|
|
}
|
|
|
|
// ParseSegmentStart extracts the position and format of the SegmentStart message.
|
|
func (m *Message) ParseSegmentStart() (Format, int64) {
|
|
var format C.GstFormat
|
|
var position C.gint64
|
|
C.gst_message_parse_segment_start(m.Instance(), &format, &position)
|
|
return Format(format), int64(position)
|
|
}
|
|
|
|
// ParseSegmentDone extracts the position and format of the SegmentDone message.
|
|
func (m *Message) ParseSegmentDone() (Format, int64) {
|
|
var format C.GstFormat
|
|
var position C.gint64
|
|
C.gst_message_parse_segment_done(m.Instance(), &format, &position)
|
|
return Format(format), int64(position)
|
|
}
|
|
|
|
// ParseRequestState parses the requests state from the message.
|
|
func (m *Message) ParseRequestState() State {
|
|
var state C.GstState
|
|
C.gst_message_parse_request_state(m.Instance(), &state)
|
|
return State(state)
|
|
}
|
|
|
|
// QoSValues represents the values inside a QoS message.
|
|
type QoSValues struct {
|
|
// If the message was generated by a live element
|
|
Live bool
|
|
// The running time of the buffer that generated the message
|
|
RunningTime time.Duration // should this be a uint64?
|
|
// The stream time of the buffer that generated the message
|
|
StreamTime time.Duration // should this be a uint64?
|
|
// The timestamps of the buffer that generated the message
|
|
Timestamp time.Duration // should this be a uint64?
|
|
// The duration of the buffer that generated the message
|
|
Duration time.Duration // should this be a uint64?
|
|
}
|
|
|
|
// ParseQoS extracts the timestamps and live status from the QoS message.
|
|
// The values reflect those of the dropped buffer. Values of ClockTimeNone
|
|
// or -1 mean unknown values.
|
|
func (m *Message) ParseQoS() *QoSValues {
|
|
var live C.gboolean
|
|
var runningTime, streamTime, timestamp, duration C.guint64
|
|
C.gst_message_parse_qos(
|
|
m.Instance(),
|
|
&live, &runningTime, &streamTime, ×tamp, &duration,
|
|
)
|
|
return &QoSValues{
|
|
Live: gobool(live),
|
|
RunningTime: time.Duration(runningTime),
|
|
StreamTime: time.Duration(streamTime),
|
|
Timestamp: time.Duration(timestamp),
|
|
Duration: time.Duration(duration),
|
|
}
|
|
}
|
|
|
|
// ParseProgress parses the progress type, code and text.
|
|
func (m *Message) ParseProgress() (progressType ProgressType, code, text string) {
|
|
codePtr := C.malloc(C.sizeof_char * 1024)
|
|
defer C.free(unsafe.Pointer(codePtr))
|
|
textPtr := C.malloc(C.sizeof_char * 1024)
|
|
defer C.free(unsafe.Pointer(textPtr))
|
|
var gpType C.GstProgressType
|
|
C.gst_message_parse_progress(
|
|
m.Instance(),
|
|
&gpType,
|
|
(**C.gchar)(unsafe.Pointer(codePtr)),
|
|
(**C.gchar)(unsafe.Pointer(textPtr)),
|
|
)
|
|
return ProgressType(gpType),
|
|
string(C.GoBytes(codePtr, C.sizeOfGCharArray((**C.gchar)(codePtr)))),
|
|
string(C.GoBytes(textPtr, C.sizeOfGCharArray((**C.gchar)(textPtr))))
|
|
}
|
|
|
|
// ParseResetTime extracts the running-time from the ResetTime message.
|
|
func (m *Message) ParseResetTime() ClockTime {
|
|
var clockTime C.GstClockTime
|
|
C.gst_message_parse_reset_time(m.Instance(), &clockTime)
|
|
return ClockTime(clockTime)
|
|
}
|
|
|
|
// ParseDeviceAdded parses a device-added message. The device-added message is
|
|
// produced by GstDeviceProvider or a GstDeviceMonitor. It announces the appearance
|
|
// of monitored devices.
|
|
func (m *Message) ParseDeviceAdded() *Device {
|
|
var device *C.GstDevice
|
|
C.gst_message_parse_device_added(m.Instance(), &device)
|
|
return FromGstDeviceUnsafeFull(unsafe.Pointer(device))
|
|
}
|
|
|
|
// ParseDeviceRemoved parses a device-removed message. The device-removed message
|
|
// is produced by GstDeviceProvider or a GstDeviceMonitor. It announces the disappearance
|
|
// of monitored devices.
|
|
func (m *Message) ParseDeviceRemoved() *Device {
|
|
var device *C.GstDevice
|
|
C.gst_message_parse_device_removed(m.Instance(), &device)
|
|
return FromGstDeviceUnsafeFull(unsafe.Pointer(device))
|
|
}
|
|
|
|
// ParseDeviceChanged Parses a device-changed message. The device-changed message is
|
|
// produced by GstDeviceProvider or a GstDeviceMonitor. It announces that a device's properties
|
|
// have changed.
|
|
// The first device returned is the updated Device, and the second changedDevice represents
|
|
// the old state of the device.
|
|
func (m *Message) ParseDeviceChanged() (newDevice, oldDevice *Device) {
|
|
var gstNewDevice, gstOldDevice *C.GstDevice
|
|
C.gst_message_parse_device_changed(m.Instance(), &gstNewDevice, &gstOldDevice)
|
|
return FromGstDeviceUnsafeFull(unsafe.Pointer(gstNewDevice)),
|
|
FromGstDeviceUnsafeFull(unsafe.Pointer(gstOldDevice))
|
|
}
|
|
|
|
// ParsePropertyNotify parses a property-notify message. These will be posted on the bus only
|
|
// when set up with Element.AddPropertyNotifyWatch (TODO) or Element.AddPropertyDeepNotifyWatch (TODO).
|
|
func (m *Message) ParsePropertyNotify() (obj *Object, propertName string, propertyValue *glib.Value) {
|
|
var gstobj *C.GstObject
|
|
var gval *C.GValue
|
|
namePtr := C.malloc(C.sizeof_char * 1024)
|
|
defer C.free(unsafe.Pointer(namePtr))
|
|
C.gst_message_parse_property_notify(
|
|
m.Instance(),
|
|
&gstobj, (**C.gchar)(unsafe.Pointer(namePtr)), &gval,
|
|
)
|
|
return wrapObject(toGObject(unsafe.Pointer(gstobj))),
|
|
string(C.GoBytes(namePtr, C.sizeOfGCharArray((**C.gchar)(namePtr)))),
|
|
glib.ValueFromNative(unsafe.Pointer(gval))
|
|
}
|
|
|
|
// ParseStreamCollection parses a stream-collection message.
|
|
func (m *Message) ParseStreamCollection() *StreamCollection {
|
|
var collection *C.GstStreamCollection
|
|
C.gst_message_parse_stream_collection(
|
|
m.Instance(),
|
|
&collection,
|
|
)
|
|
return FromGstStreamCollectionUnsafeFull(unsafe.Pointer(collection))
|
|
}
|
|
|
|
// ParseStreamsSelected parses a streams-selected message.
|
|
func (m *Message) ParseStreamsSelected() *StreamCollection {
|
|
var collection *C.GstStreamCollection
|
|
C.gst_message_parse_streams_selected(
|
|
m.Instance(),
|
|
&collection,
|
|
)
|
|
return FromGstStreamCollectionUnsafeFull(unsafe.Pointer(collection))
|
|
}
|
|
|
|
// NumRedirectEntries returns the number of redirect entries in a MessageRedirect.
|
|
func (m *Message) NumRedirectEntries() int64 {
|
|
return int64(C.gst_message_get_num_redirect_entries(m.Instance()))
|
|
}
|
|
|
|
// ParseRedirectEntryAt parses the redirect entry at the given index. Total indices can be retrieved
|
|
// with NumRedirectEntries().
|
|
func (m *Message) ParseRedirectEntryAt(idx int64) (location string, tags *TagList, structure *Structure) {
|
|
locPtr := C.malloc(C.sizeof_char * 1024)
|
|
defer C.free(unsafe.Pointer(locPtr))
|
|
var tagList *C.GstTagList
|
|
var entryStruct *C.GstStructure
|
|
C.gst_message_parse_redirect_entry(
|
|
m.Instance(),
|
|
C.gsize(idx),
|
|
(**C.char)(locPtr),
|
|
&tagList,
|
|
&entryStruct,
|
|
)
|
|
return string(C.GoBytes(locPtr, C.sizeOfGCharArray((**C.gchar)(locPtr)))),
|
|
FromGstTagListUnsafeNone(unsafe.Pointer(tagList)), wrapStructure(entryStruct)
|
|
}
|
|
|
|
// ParseHaveContext parses the context from a HaveContext message.
|
|
func (m *Message) ParseHaveContext() *Context {
|
|
var ctx *C.GstContext
|
|
C.gst_message_parse_have_context(m.Instance(), &ctx)
|
|
return FromGstContextUnsafeFull(unsafe.Pointer(ctx))
|
|
}
|