Refactoring #3
@@ -0,0 +1,127 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
mxladapter "mxl-player/internal/adapter/mxl"
|
||||||
|
"mxl-player/internal/output"
|
||||||
|
"mxl-player/internal/playback"
|
||||||
|
)
|
||||||
|
|
||||||
|
type playerPlayback struct {
|
||||||
|
Controller *playback.SessionController
|
||||||
|
Commands chan playback.SessionCommand
|
||||||
|
Video *playback.VideoBridge
|
||||||
|
Status *playback.StatusStore
|
||||||
|
Audio *output.SDLAudioSink
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPlayerPlayback(
|
||||||
|
audioDevice uint32,
|
||||||
|
retry playback.RetryPolicy,
|
||||||
|
) (*playerPlayback, error) {
|
||||||
|
videoBridge := playback.NewVideoBridge()
|
||||||
|
statusStore := playback.NewStatusStore()
|
||||||
|
audioSink := output.NewSDLAudioSink(audioDevice)
|
||||||
|
|
||||||
|
observe := func(status playback.Status) {
|
||||||
|
statusStore.Observe(status)
|
||||||
|
|
||||||
|
if status.Err != nil {
|
||||||
|
log.Printf(
|
||||||
|
"%s: state=%v attempt=%d failed=%d: %v",
|
||||||
|
status.Unit,
|
||||||
|
status.State,
|
||||||
|
status.Attempt,
|
||||||
|
status.FailedAttempts,
|
||||||
|
status.Err,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf(
|
||||||
|
"%s: state=%v attempt=%d failed=%d",
|
||||||
|
status.Unit,
|
||||||
|
status.State,
|
||||||
|
status.Attempt,
|
||||||
|
status.FailedAttempts,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
videoWorker, err := playback.NewVideoWorker(
|
||||||
|
mxladapter.VideoFactory{},
|
||||||
|
videoBridge,
|
||||||
|
retry,
|
||||||
|
mxladapter.ShouldRetry,
|
||||||
|
observe,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
_ = audioSink.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
videoSlot, err := playback.NewVideoSlot(videoWorker)
|
||||||
|
if err != nil {
|
||||||
|
_ = audioSink.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
audioWorker, err := playback.NewAudioWorker(
|
||||||
|
mxladapter.AudioFactory{},
|
||||||
|
audioSink,
|
||||||
|
retry,
|
||||||
|
mxladapter.ShouldRetry,
|
||||||
|
observe,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
_ = audioSink.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
audioSlot, err := playback.NewAudioSlot(audioWorker)
|
||||||
|
if err != nil {
|
||||||
|
_ = audioSink.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
syncWorker, err := playback.NewSyncWorker(
|
||||||
|
mxladapter.SyncFactory{},
|
||||||
|
videoBridge,
|
||||||
|
audioSink,
|
||||||
|
retry,
|
||||||
|
mxladapter.ShouldRetry,
|
||||||
|
observe,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
_ = audioSink.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
syncSlot, err := playback.NewSyncSlot(syncWorker)
|
||||||
|
if err != nil {
|
||||||
|
_ = audioSink.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
controller, err := playback.NewSessionController(
|
||||||
|
videoSlot,
|
||||||
|
audioSlot,
|
||||||
|
syncSlot,
|
||||||
|
func(video, audio playback.FeedConfig) bool {
|
||||||
|
return video.Domain == audio.Domain
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
_ = audioSink.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &playerPlayback{
|
||||||
|
Controller: controller,
|
||||||
|
Commands: make(chan playback.SessionCommand, 32),
|
||||||
|
Video: videoBridge,
|
||||||
|
Status: statusStore,
|
||||||
|
Audio: audioSink,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *playerPlayback) Close() error {
|
||||||
|
return p.Audio.Close()
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"mxl-player/internal/playback"
|
||||||
|
)
|
||||||
|
|
||||||
|
func validRuntimeRetryPolicy() playback.RetryPolicy {
|
||||||
|
return playback.RetryPolicy{
|
||||||
|
MaxAttempts: 3,
|
||||||
|
InitialDelay: time.Millisecond,
|
||||||
|
MaxDelay: time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewPlayerPlaybackBuildsCompleteRuntime(t *testing.T) {
|
||||||
|
runtime, err := newPlayerPlayback(123, validRuntimeRetryPolicy())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("newPlayerPlayback() error = %v", err)
|
||||||
|
}
|
||||||
|
if runtime.Controller == nil || runtime.Video == nil || runtime.Status == nil || runtime.Audio == nil {
|
||||||
|
t.Fatalf("newPlayerPlayback() = %#v", runtime)
|
||||||
|
}
|
||||||
|
if runtime.Commands == nil || cap(runtime.Commands) != 32 {
|
||||||
|
t.Fatalf("command channel = %#v, capacity = %d", runtime.Commands, cap(runtime.Commands))
|
||||||
|
}
|
||||||
|
if err := runtime.Close(); err != nil {
|
||||||
|
t.Fatalf("Close() error = %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewPlayerPlaybackRejectsInvalidRetryPolicy(t *testing.T) {
|
||||||
|
runtime, err := newPlayerPlayback(123, playback.RetryPolicy{})
|
||||||
|
if runtime != nil {
|
||||||
|
t.Fatalf("newPlayerPlayback() runtime = %#v, want nil", runtime)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("newPlayerPlayback() error is nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user