MXL Audio Flow reader
This commit is contained in:
@@ -19,14 +19,18 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
APP_NAME = "MXL Player"
|
APP_NAME = "MXL Player"
|
||||||
APP_VER = "0.0.1"
|
APP_VER = "0.1.0"
|
||||||
WIN_WIDTH int32 = 1920
|
WIN_WIDTH int32 = 1280
|
||||||
WIN_HEIGHT int32 = 1080
|
WIN_HEIGHT int32 = 720
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
videoFlowId := "5fbec3b1-1b0f-417d-9059-8b94a47197ed"
|
||||||
|
audioFlowId := "5fbec3b1-1b0f-417d-9059-8b94a47197ec"
|
||||||
mxlDomain := flag.String("d", "/dev/shm/mxl", "MXL domain")
|
mxlDomain := flag.String("d", "/dev/shm/mxl", "MXL domain")
|
||||||
mxlVideoFlowID := flag.String("v", "5fbec3b1-1b0f-417d-9059-8b94a47197ed", "MXL video flow UUID")
|
mxlVideoFlowID := flag.String("v", videoFlowId, "MXL video flow UUID")
|
||||||
|
mxlAudioFlowID := flag.String("a", audioFlowId, "MXL audio flow UUID")
|
||||||
|
fmt.Println(*mxlAudioFlowID)
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
|
|||||||
@@ -164,3 +164,109 @@ func (s *Source) Width() uint32 { return s.width }
|
|||||||
func (s *Source) Height() uint32 { return s.height }
|
func (s *Source) Height() uint32 { return s.height }
|
||||||
func (s *Source) GrainCount() uint32 { return s.info.Config.Discrete.GrainCount }
|
func (s *Source) GrainCount() uint32 { return s.info.Config.Discrete.GrainCount }
|
||||||
func (s *Source) Format() mxl.DataFormat { return s.info.Config.Common.Format }
|
func (s *Source) Format() mxl.DataFormat { return s.info.Config.Common.Format }
|
||||||
|
|
||||||
|
type AudioSource struct {
|
||||||
|
inst *mxl.Instance
|
||||||
|
r *mxl.Reader
|
||||||
|
info mxl.FlowInfo
|
||||||
|
rate mxl.Rational
|
||||||
|
chans uint64
|
||||||
|
idx uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type AudioFrame struct {
|
||||||
|
Index uint64
|
||||||
|
SampleCount uint64
|
||||||
|
Channels uint64
|
||||||
|
Samples [][]byte // per-channel byte slices (F32, deinterleaved)
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpenAudio(domain, flowID string) (*AudioSource, error) {
|
||||||
|
inst, err := mxl.NewInstance(domain, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("NewInstance: %w", err)
|
||||||
|
}
|
||||||
|
r, err := inst.NewReader(flowID)
|
||||||
|
if err != nil {
|
||||||
|
inst.Close()
|
||||||
|
return nil, fmt.Errorf("NewReader: %w", err)
|
||||||
|
}
|
||||||
|
info, err := r.Info()
|
||||||
|
if err != nil {
|
||||||
|
r.Close()
|
||||||
|
inst.Close()
|
||||||
|
return nil, fmt.Errorf("Info: %w", err)
|
||||||
|
}
|
||||||
|
if info.Config.Common.Format.IsDiscrete() {
|
||||||
|
r.Close()
|
||||||
|
inst.Close()
|
||||||
|
return nil, fmt.Errorf("flow is discrete (not audio)")
|
||||||
|
}
|
||||||
|
idx := info.Runtime.HeadIndex
|
||||||
|
if idx == 0 {
|
||||||
|
r.Close()
|
||||||
|
inst.Close()
|
||||||
|
return nil, fmt.Errorf("flow has no head yet (no producer?)")
|
||||||
|
}
|
||||||
|
return &AudioSource{
|
||||||
|
inst: inst,
|
||||||
|
r: r,
|
||||||
|
info: info,
|
||||||
|
rate: info.Config.Common.GrainRate,
|
||||||
|
chans: uint64(info.Config.Continuous.ChannelCount),
|
||||||
|
idx: idx,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AudioSource) NextAudio(ctx context.Context, batch uint64, timeout time.Duration) (AudioFrame, error) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return AudioFrame{}, ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
v, err := s.r.GetSamples(s.idx, int(batch), timeout)
|
||||||
|
switch {
|
||||||
|
case err == nil:
|
||||||
|
samples := make([][]byte, s.chans)
|
||||||
|
for ch := uint64(0); ch < s.chans; ch++ {
|
||||||
|
f1, f2, _ := v.ChannelFragments(ch)
|
||||||
|
if len(f2) > 0 {
|
||||||
|
samples[ch] = append(f1, f2...)
|
||||||
|
} else {
|
||||||
|
samples[ch] = f1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f := AudioFrame{
|
||||||
|
Index: s.idx,
|
||||||
|
SampleCount: batch,
|
||||||
|
Channels: s.chans,
|
||||||
|
Samples: samples,
|
||||||
|
}
|
||||||
|
s.idx += batch
|
||||||
|
return f, nil
|
||||||
|
case errors.Is(err, mxl.ErrOutOfRangeEarly):
|
||||||
|
select {
|
||||||
|
case <-time.After(10 * time.Millisecond):
|
||||||
|
case <-ctx.Done():
|
||||||
|
return AudioFrame{}, ctx.Err()
|
||||||
|
}
|
||||||
|
case errors.Is(err, mxl.ErrOutOfRangeLate):
|
||||||
|
rt, rerr := s.r.Runtime()
|
||||||
|
if rerr != nil {
|
||||||
|
return AudioFrame{}, fmt.Errorf("Runtime: %w", rerr)
|
||||||
|
}
|
||||||
|
s.idx = rt.HeadIndex
|
||||||
|
default:
|
||||||
|
return AudioFrame{}, fmt.Errorf("GetSamples: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AudioSource) Close() error {
|
||||||
|
_ = s.r.Close()
|
||||||
|
return s.inst.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *AudioSource) Rate() mxl.Rational { return s.rate }
|
||||||
|
func (s *AudioSource) Channels() uint64 { return s.chans }
|
||||||
|
|||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
VIDEO_ID="5fbec3b1-1b0f-417d-9059-8b94a47197ed"
|
||||||
|
AUDIO_ID="5fbec3b1-1b0f-417d-9059-8b94a47197ec"
|
||||||
|
VIDEO_URI=$1
|
||||||
|
if [[ -z "${VIDEO_URI}" ]] then
|
||||||
|
VIDEO_URI="${HOME}/Videos/test-vid/timelapse_spb.ts"
|
||||||
|
fi
|
||||||
|
export GST_PLUGIN_PATH="${HOME}/.gst-plugin:${GST_PLUGIN_PATH}"
|
||||||
|
mxl-gst-looping-filesrc -d /dev/shm/mxl -i "${VIDEO_URI}" --video-id "${VIDEO_ID}" --audio-id "${AUDIO_ID}" 2>/dev/null
|
||||||
|
# sleep 5
|
||||||
|
# kill -9 $(pidof "mxl-gst-looping-filesrc")
|
||||||
|
# echo -e "\ngst-looping-filesrc killed"
|
||||||
Reference in New Issue
Block a user