using System.IO; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; using Azaion.Annotator.DTO; using LibVLCSharp.Shared; using MediatR; using Microsoft.Extensions.Logging; using MediaPlayer = LibVLCSharp.Shared.MediaPlayer; namespace Azaion.Annotator; public class MainWindowEventHandler : INotificationHandler, INotificationHandler, INotificationHandler, INotificationHandler { private readonly LibVLC _libVLC; private readonly MediaPlayer _mediaPlayer; private readonly MainWindow _mainWindow; private readonly FormState _formState; private readonly Config _config; private readonly IMediator _mediator; private readonly IGalleryManager _galleryManager; private readonly DatasetExplorer _datasetExplorer; private readonly ILogger _logger; private const int STEP = 20; private const int LARGE_STEP = 5000; private const int RESULT_WIDTH = 1280; private readonly Dictionary _keysControlEnumDict = new() { { Key.Space, PlaybackControlEnum.Pause }, { Key.Left, PlaybackControlEnum.PreviousFrame }, { Key.Right, PlaybackControlEnum.NextFrame }, { Key.Enter, PlaybackControlEnum.SaveAnnotations }, { Key.Delete, PlaybackControlEnum.RemoveSelectedAnns }, { Key.X, PlaybackControlEnum.RemoveAllAnns }, { Key.PageUp, PlaybackControlEnum.Previous }, { Key.PageDown, PlaybackControlEnum.Next }, }; public MainWindowEventHandler(LibVLC libVLC, MediaPlayer mediaPlayer, MainWindow mainWindow, FormState formState, Config config, IMediator mediator, IGalleryManager galleryManager, DatasetExplorer datasetExplorer, ILogger logger) { _libVLC = libVLC; _mediaPlayer = mediaPlayer; _mainWindow = mainWindow; _formState = formState; _config = config; _mediator = mediator; _galleryManager = galleryManager; _datasetExplorer = datasetExplorer; _logger = logger; } public async Task Handle(AnnClassSelectedEvent notification, CancellationToken cancellationToken) { SelectClass(notification.AnnotationClass); await Task.CompletedTask; } private void SelectClass(AnnotationClass annClass) { _mainWindow.Editor.CurrentAnnClass = annClass; foreach (var ann in _mainWindow.Editor.CurrentAnns.Where(x => x.IsSelected)) ann.AnnotationClass = annClass; _mainWindow.LvClasses.SelectedIndex = annClass.Id; } public async Task Handle(KeyEvent notification, CancellationToken cancellationToken) { if (_formState.ActiveWindow != WindowsEnum.Main) return; var key = notification.Args.Key; var keyNumber = (int?)null; if ((int)key >= (int)Key.D1 && (int)key <= (int)Key.D9) keyNumber = key - Key.D1; if ((int)key >= (int)Key.NumPad1 && (int)key <= (int)Key.NumPad9) keyNumber = key - Key.NumPad1; if (keyNumber.HasValue) SelectClass((AnnotationClass)_mainWindow.LvClasses.Items[keyNumber.Value]); if (_keysControlEnumDict.TryGetValue(key, out var value)) await ControlPlayback(value); if (key == Key.A) _mainWindow.AutoDetect(null!, null!); await VolumeControl(key); } private async Task VolumeControl(Key key) { switch (key) { case Key.VolumeMute when _mediaPlayer.Volume == 0: await ControlPlayback(PlaybackControlEnum.TurnOnVolume); break; case Key.VolumeMute: await ControlPlayback(PlaybackControlEnum.TurnOffVolume); break; case Key.Up: case Key.VolumeUp: var vUp = Math.Min(100, _mediaPlayer.Volume + 5); ChangeVolume(vUp); _mainWindow.Volume.Value = vUp; break; case Key.Down: case Key.VolumeDown: var vDown = Math.Max(0, _mediaPlayer.Volume - 5); ChangeVolume(vDown); _mainWindow.Volume.Value = vDown; break; } } public async Task Handle(PlaybackControlEvent notification, CancellationToken cancellationToken) { await ControlPlayback(notification.PlaybackControl); _mainWindow.VideoView.Focus(); } private async Task ControlPlayback(PlaybackControlEnum controlEnum) { try { var isCtrlPressed = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl); var step = isCtrlPressed ? LARGE_STEP : STEP; switch (controlEnum) { case PlaybackControlEnum.Play: Play(); break; case PlaybackControlEnum.Pause: _mediaPlayer.Pause(); if (!_mediaPlayer.IsPlaying) _mainWindow.BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.AnnotationHelp]); if (_formState.BackgroundShown) { _mainWindow.Editor.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0)); _formState.BackgroundShown = false; } break; case PlaybackControlEnum.Stop: _mediaPlayer.Stop(); break; case PlaybackControlEnum.PreviousFrame: _mainWindow.SeekTo(_mediaPlayer.Time - step); break; case PlaybackControlEnum.NextFrame: _mainWindow.SeekTo(_mediaPlayer.Time + step); break; case PlaybackControlEnum.SaveAnnotations: await SaveAnnotations(); break; case PlaybackControlEnum.RemoveSelectedAnns: _mainWindow.Editor.RemoveSelectedAnns(); break; case PlaybackControlEnum.RemoveAllAnns: _mainWindow.Editor.RemoveAllAnns(); break; case PlaybackControlEnum.TurnOnVolume: _mainWindow.TurnOnVolumeBtn.Visibility = Visibility.Collapsed; _mainWindow.TurnOffVolumeBtn.Visibility = Visibility.Visible; _mediaPlayer.Volume = _formState.CurrentVolume; break; case PlaybackControlEnum.TurnOffVolume: _mainWindow.TurnOffVolumeBtn.Visibility = Visibility.Collapsed; _mainWindow.TurnOnVolumeBtn.Visibility = Visibility.Visible; _formState.CurrentVolume = _mediaPlayer.Volume; _mediaPlayer.Volume = 0; break; case PlaybackControlEnum.Previous: NextMedia(isPrevious: true); break; case PlaybackControlEnum.Next: NextMedia(); break; case PlaybackControlEnum.None: break; default: throw new ArgumentOutOfRangeException(nameof(controlEnum), controlEnum, null); } } catch (Exception e) { _logger.LogError(e, e.Message); throw; } } private void NextMedia(bool isPrevious = false) { var increment = isPrevious ? -1 : 1; var check = isPrevious ? -1 : _mainWindow.LvFiles.Items.Count; if (_mainWindow.LvFiles.SelectedIndex + increment == check) return; _mainWindow.LvFiles.SelectedIndex += increment; Play(); } public async Task Handle(VolumeChangedEvent notification, CancellationToken cancellationToken) { ChangeVolume(notification.Volume); await Task.CompletedTask; } private void ChangeVolume(int volume) { _formState.CurrentVolume = volume; _mediaPlayer.Volume = volume; } private void Play() { if (_mainWindow.LvFiles.SelectedItem == null) return; var mediaInfo = (MediaFileInfo)_mainWindow.LvFiles.SelectedItem; _formState.CurrentMedia = mediaInfo; _mediaPlayer.Stop(); _mainWindow.Title = $"Azaion Annotator - {mediaInfo.Name}"; _mainWindow.BlinkHelp(HelpTexts.HelpTextsDict[HelpTextEnum.PauseForAnnotations]); _mediaPlayer.Play(new Media(_libVLC, mediaInfo.Path)); } private async Task SaveAnnotations() { var annGridSelectedIndex = _mainWindow.DgAnnotations.SelectedIndex; if (_formState.CurrentMedia == null) return; var time = TimeSpan.FromMilliseconds(_mediaPlayer.Time); var fName = _formState.GetTimeName(time); var currentAnns = _mainWindow.Editor.CurrentAnns .Select(x => new YoloLabel(x.Info, _mainWindow.Editor.RenderSize, _formState.BackgroundShown ? _mainWindow.Editor.RenderSize : _formState.CurrentVideoSize)) .ToList(); await YoloLabel.WriteToFile(currentAnns, Path.Combine(_config.LabelsDirectory, $"{fName}.txt")); await _mainWindow.AddAnnotations(time, currentAnns); _formState.CurrentMedia.HasAnnotations = _mainWindow.Annotations.Count != 0; _mainWindow.LvFiles.Items.Refresh(); var isVideo = _formState.CurrentMedia.MediaType == MediaTypes.Video; var destinationPath = Path.Combine(_config.ImagesDirectory, $"{fName}{(isVideo ? ".jpg" : Path.GetExtension(_formState.CurrentMedia.Path))}"); _mainWindow.Editor.RemoveAllAnns(); if (isVideo) { if (_formState.BackgroundShown) { //no need to save image, it's already there, just remove background _mainWindow.Editor.Background = new SolidColorBrush(Color.FromArgb(1, 0, 0, 0)); _formState.BackgroundShown = false; var annGrid = _mainWindow.DgAnnotations; annGrid.SelectedIndex = Math.Min(annGrid.Items.Count, annGridSelectedIndex + 1); _mainWindow.OpenAnnotationResult((AnnotationResult)annGrid.SelectedItem); } else { var resultHeight = (uint)Math.Round(RESULT_WIDTH / _formState.CurrentVideoSize.Width * _formState.CurrentVideoSize.Height); _mediaPlayer.TakeSnapshot(0, destinationPath, RESULT_WIDTH, resultHeight); _mediaPlayer.Play(); } } else { File.Copy(_formState.CurrentMedia.Path, destinationPath, overwrite: true); NextMedia(); } var thumbnailDto = await _galleryManager.CreateThumbnail(destinationPath); if (thumbnailDto != null) _datasetExplorer.AddThumbnail(thumbnailDto, currentAnns.Select(x => x.ClassNumber)); } }