diff --git a/internal/source/source.go b/internal/source/source.go index e5186f0..7bed226 100644 --- a/internal/source/source.go +++ b/internal/source/source.go @@ -69,7 +69,7 @@ func Open(domain, flowID string) (*Source, error) { if err := json.Unmarshal([]byte(def), &fd); err != nil { r.Close() inst.Close() - return nil, wrapError("parse flow definition JSON", ErrorKindInvalidConfig, err) + return nil, wrapError("parse video flow definition JSON", ErrorKindInvalidConfig, err) } if fd.FrameWidth == 0 || fd.FrameHeight == 0 { r.Close() @@ -311,19 +311,19 @@ type SyncSource struct { func OpenSameDomainSync(domain, videoFlow, audioFlow string) (*SyncSource, error) { inst, err := mxl.NewInstance(domain, "") if err != nil { - return nil, fmt.Errorf("NewInstance: %w", err) + return nil, wrapError("new MXL instance", ErrorKindUnavailable, err) } vr, err := inst.NewReader(videoFlow) if err != nil { inst.Close() - return nil, fmt.Errorf("NewReader(video): %w", err) + return nil, wrapError("open video reader", ErrorKindUnavailable, err) } ar, err := inst.NewReader(audioFlow) if err != nil { vr.Close() inst.Close() - return nil, fmt.Errorf("NewReader(audio): %w", err) + return nil, wrapError("open audio reader", ErrorKindUnavailable, err) } vInfo, err := vr.Info() @@ -331,26 +331,56 @@ func OpenSameDomainSync(domain, videoFlow, audioFlow string) (*SyncSource, error ar.Close() vr.Close() inst.Close() - return nil, fmt.Errorf("Info(video): %w", err) + return nil, wrapError("get video info", ErrorKindUnavailable, err) } if !vInfo.Config.Common.Format.IsDiscrete() { ar.Close() vr.Close() inst.Close() - return nil, fmt.Errorf("video flow is not discrete") + return nil, wrapError( + "validate video flow", + ErrorKindInvalidConfig, + errors.New("video flow is continuous"), + ) } aInfo, err := ar.Info() if err != nil { ar.Close() vr.Close() inst.Close() - return nil, fmt.Errorf("Info(audio): %w", err) + return nil, wrapError("get audio info", ErrorKindUnavailable, err) } if aInfo.Config.Common.Format.IsDiscrete() { ar.Close() vr.Close() inst.Close() - return nil, fmt.Errorf("audio flow is not continuous") + return nil, wrapError( + "validate audio flow", + ErrorKindInvalidConfig, + errors.New("audio flow is discrete"), + ) + } + channels := uint64(aInfo.Config.Continuous.ChannelCount) + if channels == 0 { + ar.Close() + vr.Close() + inst.Close() + return nil, wrapError( + "validate audio flow", + ErrorKindInvalidConfig, + errors.New("audio flow has no channels"), + ) + } + aRate := aInfo.Config.Common.GrainRate + if aRate.Num <= 0 || aRate.Den <= 0 { + ar.Close() + vr.Close() + inst.Close() + return nil, wrapError( + "validate audio flow", + ErrorKindInvalidConfig, + fmt.Errorf("invalid audio rate: %d/%d", aRate.Num, aRate.Den), + ) } def, err := inst.FlowDef(videoFlow) @@ -358,14 +388,38 @@ func OpenSameDomainSync(domain, videoFlow, audioFlow string) (*SyncSource, error ar.Close() vr.Close() inst.Close() - return nil, fmt.Errorf("FlowDef: %w", err) + return nil, wrapError("read video flow definition", ErrorKindUnavailable, err) } var fd flowDef if err := json.Unmarshal([]byte(def), &fd); err != nil { ar.Close() vr.Close() inst.Close() - return nil, fmt.Errorf("parse flow def: %w", err) + return nil, wrapError("parse video flow definition JSON", ErrorKindInvalidConfig, err) + } + if fd.FrameWidth <= 0 || fd.FrameHeight <= 0 { + ar.Close() + vr.Close() + inst.Close() + return nil, wrapError( + "validate video flow", + ErrorKindInvalidConfig, + fmt.Errorf( + "invalid video dimensions: %dx%d", + fd.FrameWidth, + fd.FrameHeight, + ), + ) + } + if len(vInfo.Config.Discrete.SliceSizes) == 0 { + ar.Close() + vr.Close() + inst.Close() + return nil, wrapError( + "validate video flow", + ErrorKindInvalidConfig, + errors.New("video flow has no slice sizes"), + ) } vRate := vInfo.Config.Common.GrainRate @@ -374,7 +428,11 @@ func OpenSameDomainSync(domain, videoFlow, audioFlow string) (*SyncSource, error ar.Close() vr.Close() inst.Close() - return nil, fmt.Errorf("invalid video rate %d/%d", vRate.Num, vRate.Den) + return nil, wrapError( + "validate video flow", + ErrorKindInvalidConfig, + fmt.Errorf("invalid grain rate: %d/%d", vRate.Num, vRate.Den), + ) } group, err := inst.NewSyncGroup() @@ -382,21 +440,33 @@ func OpenSameDomainSync(domain, videoFlow, audioFlow string) (*SyncSource, error ar.Close() vr.Close() inst.Close() - return nil, fmt.Errorf("NewSyncGroup: %w", err) + return nil, wrapError( + "create native sync group", + ErrorKindUnavailable, + err, + ) } if err := group.AddReader(vr); err != nil { group.Close() ar.Close() vr.Close() inst.Close() - return nil, fmt.Errorf("AddReader(video): %w", err) + return nil, wrapError( + "add video reader to native sync group", + ErrorKindUnavailable, + err, + ) } if err := group.AddReader(ar); err != nil { group.Close() ar.Close() vr.Close() inst.Close() - return nil, fmt.Errorf("AddReader(audio): %w", err) + return nil, wrapError( + "add audio reader to native sync group", + ErrorKindUnavailable, + err, + ) } return &SyncSource{ @@ -405,8 +475,8 @@ func OpenSameDomainSync(domain, videoFlow, audioFlow string) (*SyncSource, error ar: ar, group: group, rate: vRate, - aRate: aInfo.Config.Common.GrainRate, - chans: uint64(aInfo.Config.Continuous.ChannelCount), + aRate: aRate, + chans: channels, idx: idx, width: uint32(fd.FrameWidth), height: uint32(fd.FrameHeight),