mirror of https://github.com/jetkvm/kvm.git
166 lines
5.0 KiB
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
|
|
}
|