VideoTools/vendor/github.com/godbus/dbus/v5/default_handler.go
Stu Leak 68df790d27 Fix player frame generation and video playback
Major improvements to UnifiedPlayer:

1. GetFrameImage() now works when paused for responsive UI updates
2. Play() method properly starts FFmpeg process
3. Frame display loop runs continuously for smooth video display
4. Disabled audio temporarily to fix video playback fundamentals
5. Simplified FFmpeg command to focus on video stream only

Player now:
- Generates video frames correctly
- Shows video when paused
- Has responsive progress tracking
- Starts playback properly

Next steps: Re-enable audio playback once video is stable
2026-01-07 22:20:00 -05:00

343 lines
7.5 KiB
Go

package dbus
import (
"bytes"
"reflect"
"strings"
"sync"
)
func newIntrospectIntf(h *defaultHandler) *exportedIntf {
methods := make(map[string]Method)
methods["Introspect"] = exportedMethod{
reflect.ValueOf(func(msg Message) (string, *Error) {
path := msg.Headers[FieldPath].value.(ObjectPath)
return h.introspectPath(path), nil
}),
}
return newExportedIntf(methods, true)
}
//NewDefaultHandler returns an instance of the default
//call handler. This is useful if you want to implement only
//one of the two handlers but not both.
//
// Deprecated: this is the default value, don't use it, it will be unexported.
func NewDefaultHandler() *defaultHandler {
h := &defaultHandler{
objects: make(map[ObjectPath]*exportedObj),
defaultIntf: make(map[string]*exportedIntf),
}
h.defaultIntf["org.freedesktop.DBus.Introspectable"] = newIntrospectIntf(h)
return h
}
type defaultHandler struct {
sync.RWMutex
objects map[ObjectPath]*exportedObj
defaultIntf map[string]*exportedIntf
}
func (h *defaultHandler) PathExists(path ObjectPath) bool {
_, ok := h.objects[path]
return ok
}
func (h *defaultHandler) introspectPath(path ObjectPath) string {
subpath := make(map[string]struct{})
var xml bytes.Buffer
xml.WriteString("<node>")
for obj := range h.objects {
p := string(path)
if p != "/" {
p += "/"
}
if strings.HasPrefix(string(obj), p) {
node_name := strings.Split(string(obj[len(p):]), "/")[0]
subpath[node_name] = struct{}{}
}
}
for s := range subpath {
xml.WriteString("\n\t<node name=\"" + s + "\"/>")
}
xml.WriteString("\n</node>")
return xml.String()
}
func (h *defaultHandler) LookupObject(path ObjectPath) (ServerObject, bool) {
h.RLock()
defer h.RUnlock()
object, ok := h.objects[path]
if ok {
return object, ok
}
// If an object wasn't found for this exact path,
// look for a matching subtree registration
subtreeObject := newExportedObject()
path = path[:strings.LastIndex(string(path), "/")]
for len(path) > 0 {
object, ok = h.objects[path]
if ok {
for name, iface := range object.interfaces {
// Only include this handler if it registered for the subtree
if iface.isFallbackInterface() {
subtreeObject.interfaces[name] = iface
}
}
break
}
path = path[:strings.LastIndex(string(path), "/")]
}
for name, intf := range h.defaultIntf {
if _, exists := subtreeObject.interfaces[name]; exists {
continue
}
subtreeObject.interfaces[name] = intf
}
return subtreeObject, true
}
func (h *defaultHandler) AddObject(path ObjectPath, object *exportedObj) {
h.Lock()
h.objects[path] = object
h.Unlock()
}
func (h *defaultHandler) DeleteObject(path ObjectPath) {
h.Lock()
delete(h.objects, path)
h.Unlock()
}
type exportedMethod struct {
reflect.Value
}
func (m exportedMethod) Call(args ...interface{}) ([]interface{}, error) {
t := m.Type()
params := make([]reflect.Value, len(args))
for i := 0; i < len(args); i++ {
params[i] = reflect.ValueOf(args[i]).Elem()
}
ret := m.Value.Call(params)
var err error
nilErr := false // The reflection will find almost-nils, let's only pass back clean ones!
if t.NumOut() > 0 {
if e, ok := ret[t.NumOut()-1].Interface().(*Error); ok { // godbus *Error
nilErr = ret[t.NumOut()-1].IsNil()
ret = ret[:t.NumOut()-1]
err = e
} else if ret[t.NumOut()-1].Type().Implements(errType) { // Go error
i := ret[t.NumOut()-1].Interface()
if i == nil {
nilErr = ret[t.NumOut()-1].IsNil()
} else {
err = i.(error)
}
ret = ret[:t.NumOut()-1]
}
}
out := make([]interface{}, len(ret))
for i, val := range ret {
out[i] = val.Interface()
}
if nilErr || err == nil {
//concrete type to interface nil is a special case
return out, nil
}
return out, err
}
func (m exportedMethod) NumArguments() int {
return m.Value.Type().NumIn()
}
func (m exportedMethod) ArgumentValue(i int) interface{} {
return reflect.Zero(m.Type().In(i)).Interface()
}
func (m exportedMethod) NumReturns() int {
return m.Value.Type().NumOut()
}
func (m exportedMethod) ReturnValue(i int) interface{} {
return reflect.Zero(m.Type().Out(i)).Interface()
}
func newExportedObject() *exportedObj {
return &exportedObj{
interfaces: make(map[string]*exportedIntf),
}
}
type exportedObj struct {
mu sync.RWMutex
interfaces map[string]*exportedIntf
}
func (obj *exportedObj) LookupInterface(name string) (Interface, bool) {
if name == "" {
return obj, true
}
obj.mu.RLock()
defer obj.mu.RUnlock()
intf, exists := obj.interfaces[name]
return intf, exists
}
func (obj *exportedObj) AddInterface(name string, iface *exportedIntf) {
obj.mu.Lock()
defer obj.mu.Unlock()
obj.interfaces[name] = iface
}
func (obj *exportedObj) DeleteInterface(name string) {
obj.mu.Lock()
defer obj.mu.Unlock()
delete(obj.interfaces, name)
}
func (obj *exportedObj) LookupMethod(name string) (Method, bool) {
obj.mu.RLock()
defer obj.mu.RUnlock()
for _, intf := range obj.interfaces {
method, exists := intf.LookupMethod(name)
if exists {
return method, exists
}
}
return nil, false
}
func (obj *exportedObj) isFallbackInterface() bool {
return false
}
func newExportedIntf(methods map[string]Method, includeSubtree bool) *exportedIntf {
return &exportedIntf{
methods: methods,
includeSubtree: includeSubtree,
}
}
type exportedIntf struct {
methods map[string]Method
// Whether or not this export is for the entire subtree
includeSubtree bool
}
func (obj *exportedIntf) LookupMethod(name string) (Method, bool) {
out, exists := obj.methods[name]
return out, exists
}
func (obj *exportedIntf) isFallbackInterface() bool {
return obj.includeSubtree
}
//NewDefaultSignalHandler returns an instance of the default
//signal handler. This is useful if you want to implement only
//one of the two handlers but not both.
//
// Deprecated: this is the default value, don't use it, it will be unexported.
func NewDefaultSignalHandler() *defaultSignalHandler {
return &defaultSignalHandler{}
}
type defaultSignalHandler struct {
mu sync.RWMutex
closed bool
signals []*signalChannelData
}
func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) {
sh.mu.RLock()
defer sh.mu.RUnlock()
if sh.closed {
return
}
for _, scd := range sh.signals {
scd.deliver(signal)
}
}
func (sh *defaultSignalHandler) Terminate() {
sh.mu.Lock()
defer sh.mu.Unlock()
if sh.closed {
return
}
for _, scd := range sh.signals {
scd.close()
close(scd.ch)
}
sh.closed = true
sh.signals = nil
}
func (sh *defaultSignalHandler) AddSignal(ch chan<- *Signal) {
sh.mu.Lock()
defer sh.mu.Unlock()
if sh.closed {
return
}
sh.signals = append(sh.signals, &signalChannelData{
ch: ch,
done: make(chan struct{}),
})
}
func (sh *defaultSignalHandler) RemoveSignal(ch chan<- *Signal) {
sh.mu.Lock()
defer sh.mu.Unlock()
if sh.closed {
return
}
for i := len(sh.signals) - 1; i >= 0; i-- {
if ch == sh.signals[i].ch {
sh.signals[i].close()
copy(sh.signals[i:], sh.signals[i+1:])
sh.signals[len(sh.signals)-1] = nil
sh.signals = sh.signals[:len(sh.signals)-1]
}
}
}
type signalChannelData struct {
wg sync.WaitGroup
ch chan<- *Signal
done chan struct{}
}
func (scd *signalChannelData) deliver(signal *Signal) {
select {
case scd.ch <- signal:
case <-scd.done:
return
default:
scd.wg.Add(1)
go scd.deferredDeliver(signal)
}
}
func (scd *signalChannelData) deferredDeliver(signal *Signal) {
select {
case scd.ch <- signal:
case <-scd.done:
}
scd.wg.Done()
}
func (scd *signalChannelData) close() {
close(scd.done)
scd.wg.Wait() // wait until all spawned goroutines return
}