Files
RemoteDesk/transport/hysteria2-agent/media_ring_unix.go
T
曾志威 19a8e03a83
ci / rust (push) Canceled after 0s
ci / web (push) Canceled after 0s
ci / package-preview (push) Canceled after 0s
ci / package-installer (push) Canceled after 0s
ci / linux-agent (push) Canceled after 0s
ci / edge-service (push) Canceled after 0s
ci / coturn-pop (push) Canceled after 0s
ci / package-windows-host (push) Canceled after 0s
Document all-Rust migration and extend native media stack
2026-08-14 14:31:57 +08:00

106 lines
3.2 KiB
Go

//go:build !windows
package main
import (
"encoding/binary"
"errors"
"fmt"
"os"
"sync/atomic"
"syscall"
"unsafe"
)
const (
ringMagic = "RDMRING1"
ringVersion = uint32(1)
ringHeaderBytes = 40
slotHeaderBytes = 24
maxRingSlots = uint32(256)
maxRingSlotBytes = uint32(16 * 1024 * 1024)
)
type mappedEncodedRing struct {
file *os.File
path string
data []byte
slotCount uint32
slotBytes uint32
}
func openEncodedRing(path string) (EncodedRing, error) {
file, err := os.OpenFile(path, os.O_RDWR, 0)
if err != nil {
return nil, fmt.Errorf("open encoded ring: %w", err)
}
stat, err := file.Stat()
if err != nil {
_ = file.Close()
return nil, err
}
if stat.Size() < ringHeaderBytes {
_ = file.Close()
return nil, errors.New("encoded ring is shorter than its header")
}
data, err := syscall.Mmap(int(file.Fd()), 0, int(stat.Size()), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED)
if err != nil {
_ = file.Close()
return nil, fmt.Errorf("map encoded ring: %w", err)
}
if string(data[:8]) != ringMagic || binary.LittleEndian.Uint32(data[8:12]) != ringVersion {
_ = syscall.Munmap(data)
_ = file.Close()
return nil, errors.New("encoded ring header is invalid")
}
slots, slotBytes := binary.LittleEndian.Uint32(data[12:16]), binary.LittleEndian.Uint32(data[16:20])
expected := uint64(ringHeaderBytes) + uint64(slots)*uint64(slotHeaderBytes+slotBytes)
if slots < 2 || slots > maxRingSlots || slots&(slots-1) != 0 || slotBytes == 0 || slotBytes > maxRingSlotBytes || expected != uint64(len(data)) {
_ = syscall.Munmap(data)
_ = file.Close()
return nil, errors.New("encoded ring geometry is invalid")
}
return &mappedEncodedRing{file: file, path: path, data: data, slotCount: slots, slotBytes: slotBytes}, nil
}
func openAudioRing(path string) (AudioRing, error) {
ring, err := openEncodedRing(path)
if err != nil {
return nil, err
}
return ring, nil
}
func (r *mappedEncodedRing) Pop() (*EncodedRingRecord, error) {
read := atomic.LoadUint64((*uint64)(unsafe.Pointer(&r.data[32])))
write := atomic.LoadUint64((*uint64)(unsafe.Pointer(&r.data[24])))
if read == write {
return nil, nil
}
start := ringHeaderBytes + int(read%uint64(r.slotCount))*int(slotHeaderBytes+r.slotBytes)
length := binary.LittleEndian.Uint32(r.data[start+20 : start+24])
if length == 0 || length > r.slotBytes {
return nil, errors.New("encoded ring record length is invalid")
}
record := &EncodedRingRecord{Sequence: binary.LittleEndian.Uint64(r.data[start : start+8]), PTS: binary.LittleEndian.Uint64(r.data[start+8 : start+16]), Kind: binary.LittleEndian.Uint32(r.data[start+16 : start+20]), Payload: append([]byte(nil), r.data[start+slotHeaderBytes:start+slotHeaderBytes+int(length)]...)}
record.KeyFrame = record.Kind == 2
if record.Sequence == 0 {
return nil, errors.New("encoded ring record sequence is invalid")
}
atomic.StoreUint64((*uint64)(unsafe.Pointer(&r.data[32])), read+1)
return record, nil
}
func (r *mappedEncodedRing) Close() error {
err := syscall.Munmap(r.data)
if r.file != nil {
if closeErr := r.file.Close(); err == nil {
err = closeErr
}
}
if removeErr := os.Remove(r.path); err == nil && removeErr != nil && !errors.Is(removeErr, os.ErrNotExist) {
err = removeErr
}
return err
}