kvm/internal/audio/output_manager.go

166 lines
5.0 KiB
Go

package audio
import (
"sync/atomic"
"github.com/jetkvm/kvm/internal/logging"
)
// AudioOutputManager manages audio output stream using IPC mode
type AudioOutputManager struct {
*BaseAudioManager
streamer *AudioOutputStreamer
framesReceived int64 // Output-specific metric
}
// AudioOutputMetrics tracks output-specific metrics
// Atomic fields MUST be first for ARM32 alignment (int64 fields need 8-byte alignment)
type AudioOutputMetrics struct {
// Atomic int64 field first for proper ARM32 alignment
FramesReceived int64 `json:"frames_received"` // Total frames received (output-specific)
// Embedded struct with atomic fields properly aligned
BaseAudioMetrics
}
// NewAudioOutputManager creates a new audio output manager
func NewAudioOutputManager() *AudioOutputManager {
logger := logging.GetDefaultLogger().With().Str("component", AudioOutputManagerComponent).Logger()
streamer, err := NewAudioOutputStreamer()
if err != nil {
// Log error but continue with nil streamer - will be handled gracefully
logger.Error().Err(err).Msg("Failed to create audio output streamer")
}
return &AudioOutputManager{
BaseAudioManager: NewBaseAudioManager(logger),
streamer: streamer,
}
}
// Start starts the audio output manager
func (aom *AudioOutputManager) Start() error {
if !aom.setRunning(true) {
return nil // Already running
}
aom.logComponentStart(AudioOutputManagerComponent)
if aom.streamer == nil {
// Try to recreate streamer if it was nil
streamer, err := NewAudioOutputStreamer()
if err != nil {
aom.setRunning(false)
aom.logComponentError(AudioOutputManagerComponent, err, "failed to create audio output streamer")
return err
}
aom.streamer = streamer
}
err := aom.streamer.Start()
if err != nil {
aom.setRunning(false)
// Reset metrics on failed start
aom.resetMetrics()
aom.logComponentError(AudioOutputManagerComponent, err, "failed to start component")
return err
}
aom.logComponentStarted(AudioOutputManagerComponent)
return nil
}
// Stop stops the audio output manager
func (aom *AudioOutputManager) Stop() {
if !aom.setRunning(false) {
return // Already stopped
}
aom.logComponentStop(AudioOutputManagerComponent)
if aom.streamer != nil {
aom.streamer.Stop()
}
aom.logComponentStopped(AudioOutputManagerComponent)
}
// resetMetrics resets all metrics to zero
func (aom *AudioOutputManager) resetMetrics() {
aom.BaseAudioManager.resetMetrics()
atomic.StoreInt64(&aom.framesReceived, 0)
}
// Note: IsRunning() is inherited from BaseAudioManager
// IsReady returns whether the audio output manager is ready to receive frames
func (aom *AudioOutputManager) IsReady() bool {
if !aom.IsRunning() || aom.streamer == nil {
return false
}
// For output, we consider it ready if the streamer is running
// This could be enhanced with connection status checks
return true
}
// GetMetrics returns current metrics
func (aom *AudioOutputManager) GetMetrics() AudioOutputMetrics {
return AudioOutputMetrics{
FramesReceived: atomic.LoadInt64(&aom.framesReceived),
BaseAudioMetrics: aom.getBaseMetrics(),
}
}
// GetComprehensiveMetrics returns detailed performance metrics
func (aom *AudioOutputManager) GetComprehensiveMetrics() map[string]interface{} {
baseMetrics := aom.GetMetrics()
comprehensiveMetrics := map[string]interface{}{
"manager": map[string]interface{}{
"frames_received": baseMetrics.FramesReceived,
"frames_processed": baseMetrics.FramesProcessed,
"frames_dropped": baseMetrics.FramesDropped,
"bytes_processed": baseMetrics.BytesProcessed,
"connection_drops": baseMetrics.ConnectionDrops,
"average_latency_ms": float64(baseMetrics.AverageLatency.Nanoseconds()) / 1e6,
"last_frame_time": baseMetrics.LastFrameTime,
"running": aom.IsRunning(),
"ready": aom.IsReady(),
},
}
if aom.streamer != nil {
processed, dropped, avgTime := aom.streamer.GetStats()
comprehensiveMetrics["streamer"] = map[string]interface{}{
"frames_processed": processed,
"frames_dropped": dropped,
"avg_processing_time_ms": float64(avgTime.Nanoseconds()) / 1e6,
}
if detailedStats := aom.streamer.GetDetailedStats(); detailedStats != nil {
comprehensiveMetrics["detailed"] = detailedStats
}
}
return comprehensiveMetrics
}
// LogPerformanceStats logs current performance statistics
func (aom *AudioOutputManager) LogPerformanceStats() {
metrics := aom.GetMetrics()
aom.logger.Info().
Int64("frames_received", metrics.FramesReceived).
Int64("frames_dropped", metrics.FramesDropped).
Int64("bytes_processed", metrics.BytesProcessed).
Int64("connection_drops", metrics.ConnectionDrops).
Float64("average_latency_ms", float64(metrics.AverageLatency.Nanoseconds())/1e6).
Bool("running", aom.IsRunning()).
Bool("ready", aom.IsReady()).
Msg("Audio output manager performance stats")
}
// GetStreamer returns the streamer for advanced operations
func (aom *AudioOutputManager) GetStreamer() *AudioOutputStreamer {
return aom.streamer
}