mirror of https://github.com/jetkvm/kvm.git
178 lines
5.6 KiB
Go
178 lines
5.6 KiB
Go
package audio
|
|
|
|
import (
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/jetkvm/kvm/internal/logging"
|
|
"github.com/rs/zerolog"
|
|
)
|
|
|
|
// AudioOutputManager manages audio output stream using IPC mode
|
|
type AudioOutputManager struct {
|
|
metrics AudioOutputMetrics
|
|
|
|
streamer *AudioOutputStreamer
|
|
logger zerolog.Logger
|
|
running int32
|
|
}
|
|
|
|
// AudioOutputMetrics tracks output-specific metrics
|
|
type AudioOutputMetrics struct {
|
|
FramesReceived int64
|
|
FramesDropped int64
|
|
BytesProcessed int64
|
|
ConnectionDrops int64
|
|
LastFrameTime time.Time
|
|
AverageLatency time.Duration
|
|
}
|
|
|
|
// NewAudioOutputManager creates a new audio output manager
|
|
func NewAudioOutputManager() *AudioOutputManager {
|
|
streamer, err := NewAudioOutputStreamer()
|
|
if err != nil {
|
|
// Log error but continue with nil streamer - will be handled gracefully
|
|
logger := logging.GetDefaultLogger().With().Str("component", AudioOutputManagerComponent).Logger()
|
|
logger.Error().Err(err).Msg("Failed to create audio output streamer")
|
|
}
|
|
|
|
return &AudioOutputManager{
|
|
streamer: streamer,
|
|
logger: logging.GetDefaultLogger().With().Str("component", AudioOutputManagerComponent).Logger(),
|
|
}
|
|
}
|
|
|
|
// Start starts the audio output manager
|
|
func (aom *AudioOutputManager) Start() error {
|
|
if !atomic.CompareAndSwapInt32(&aom.running, 0, 1) {
|
|
return nil // Already running
|
|
}
|
|
|
|
aom.logger.Info().Str("component", AudioOutputManagerComponent).Msg("starting component")
|
|
|
|
if aom.streamer == nil {
|
|
// Try to recreate streamer if it was nil
|
|
streamer, err := NewAudioOutputStreamer()
|
|
if err != nil {
|
|
atomic.StoreInt32(&aom.running, 0)
|
|
aom.logger.Error().Err(err).Str("component", AudioOutputManagerComponent).Msg("failed to create audio output streamer")
|
|
return err
|
|
}
|
|
aom.streamer = streamer
|
|
}
|
|
|
|
err := aom.streamer.Start()
|
|
if err != nil {
|
|
atomic.StoreInt32(&aom.running, 0)
|
|
// Reset metrics on failed start
|
|
aom.resetMetrics()
|
|
aom.logger.Error().Err(err).Str("component", AudioOutputManagerComponent).Msg("failed to start component")
|
|
return err
|
|
}
|
|
|
|
aom.logger.Info().Str("component", AudioOutputManagerComponent).Msg("component started successfully")
|
|
return nil
|
|
}
|
|
|
|
// Stop stops the audio output manager
|
|
func (aom *AudioOutputManager) Stop() {
|
|
if !atomic.CompareAndSwapInt32(&aom.running, 1, 0) {
|
|
return // Already stopped
|
|
}
|
|
|
|
aom.logger.Info().Str("component", AudioOutputManagerComponent).Msg("stopping component")
|
|
|
|
if aom.streamer != nil {
|
|
aom.streamer.Stop()
|
|
}
|
|
|
|
aom.logger.Info().Str("component", AudioOutputManagerComponent).Msg("component stopped")
|
|
}
|
|
|
|
// resetMetrics resets all metrics to zero
|
|
func (aom *AudioOutputManager) resetMetrics() {
|
|
atomic.StoreInt64(&aom.metrics.FramesReceived, 0)
|
|
atomic.StoreInt64(&aom.metrics.FramesDropped, 0)
|
|
atomic.StoreInt64(&aom.metrics.BytesProcessed, 0)
|
|
atomic.StoreInt64(&aom.metrics.ConnectionDrops, 0)
|
|
}
|
|
|
|
// IsRunning returns whether the audio output manager is running
|
|
func (aom *AudioOutputManager) IsRunning() bool {
|
|
return atomic.LoadInt32(&aom.running) == 1
|
|
}
|
|
|
|
// 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.metrics.FramesReceived),
|
|
FramesDropped: atomic.LoadInt64(&aom.metrics.FramesDropped),
|
|
BytesProcessed: atomic.LoadInt64(&aom.metrics.BytesProcessed),
|
|
ConnectionDrops: atomic.LoadInt64(&aom.metrics.ConnectionDrops),
|
|
AverageLatency: aom.metrics.AverageLatency,
|
|
LastFrameTime: aom.metrics.LastFrameTime,
|
|
}
|
|
}
|
|
|
|
// 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_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
|
|
}
|