UWP ожидает завершения работы MediaPlayer, прежде чем продолжить

Я хочу дать звуковые инструкции с помощью медиаплеера. Компьютер пытается сказать:
"Введите свои инициалы в текстовое поле и нажмите Enter"

Происходит одно из четырех

1) Событие MediaPlayer.OnMediaPlayerFailed
2) Событие MediaPlayer.OnMediaPlayerEnded
3) Пользователь завершает задачу и нажимает клавишу ввода во время воспроизведения MediaPlayer
4) Пользователь ничего не делает, и мы хотим, чтобы тайм-аут

MediaPlayer.Play() возвращает управление следующей инструкции еще до начала воспроизведения записи. Поток пользовательского интерфейса должен ждать. Но как? Я решил, что буду запускать новую задачу с помощью Task.Run/Task.Factory.Run/Task.Factory.RunStart. А затем подождите (timeout_interval) в потоке пользовательского интерфейса. Но я только что передал ту же проблему в новый поток/задачу. MediaPlayer.Play() фактически является последней строкой новой задачи и завершается до окончания звука. Мне нужно поддерживать задачу до тех пор, пока не будет выполнен OnMediaXxxx. Затем два события завершат задачу или отменят UI.Wait(). Я ходил по кругу с CancellationTokens, CancellationTokenSources и множеством различных способов ожидания.

Какое решение? То, что я пытаюсь сделать, похоже, не работает


person Paulustrious    schedule 03.05.2017    source источник
comment
Я уверен, что есть несколько способов сделать это. Один из них заключается в том, что вы можете изначально отключить текстовое поле и включить его, когда носитель полностью воспроизведется, подождав поток, скажем, 5 секунд после того, как MediaPlayer начнет воспроизводить звук.   -  person Pratyay    schedule 04.05.2017


Ответы (2)


Я сейчас лысый... но успешный. Все сводилось к ожиданию окончания записи. Так что я ждал после MediaPlay, но это не сработало. Если бы я попытался воспроизвести два последовательных звука, я бы не смог заставить его дождаться окончания первого, прежде чем он начнет второй. Но это сработало...

static private void PlayAndWait(StorageFile file, double volume)
    {
        playCancellationTokenSource = new CancellationTokenSource();
        playTask = new Task(() => PlayFile(file, volume), playCancellationTokenSource.Token);
        playTask.Start();
        Task.WaitAny(new Task[] { playTask }, 15_0000);
    }

static private void PlayFile(StorageFile file, double volume)
    {
        if (MRecording.recordingStatus != RecordingStatus.Idle)
            throw new LogicException("PlayStorageFile: RecordingStatus not Idle");
        FileNowPlaying = file;
        var mediaPlayer = new MediaPlayer { AutoPlay = false, AudioCategory = MediaPlayerAudioCategory.Media };
        mediaPlayer.MediaFailed += OnMediaPlayerFailed; // one of these two should release the mutex
        mediaPlayer.MediaEnded += OnMediaPlayerEnded;
        mediaPlayer.AudioCategory = Windows.Media.Playback.MediaPlayerAudioCategory.Media;

        MediaSource mediaSource = MediaSource.CreateFromStorageFile(file);
        var mediaPlaybackItem = new MediaPlaybackItem(mediaSource);
        mediaPlayer.Source = mediaPlaybackItem;
        mediaPlayer.Volume = volume;
        MRecording.recordingStatus = RecordingStatus.Playing;
        try
        {
            FileNowPlaying = file;
            mediaPlayer.Play();
            playTask.Wait(playCancellationTokenSource.Token);
        }
        catch (OperationCanceledException)
        { }
        catch (Exception e)
        {
            throw new LogicException("PlayFile", e);
        }
        FileNowPlaying = null;
        recordingStatus = RecordingStatus.Idle;
    }
    static void OnMediaPlayerFailed(object sender, MediaPlayerFailedEventArgs e) => PlayCleanUp();
static void OnMediaPlayerEnded(object sender, MediaPlayerFailedEventArgs e) => PlayCleanUp();
static void PlayCleanUp()
    {
        recordingStatus = RecordingStatus.Idle;
        if (playTask == null)
            throw new LogicException("playTask null");

        string msg = string.Empty;
        AggregateException exceptions = playTask.Exception;
        if (exceptions != null)
            foreach (Exception e in exceptions.InnerExceptions)
            {
                msg += e.Message + Statics.CRLF2;
                throw new LogicException("PlayCleanUp exception: " + msg);
            }
        playCancellationTokenSource.Cancel();
    }

Итак, теперь я могу сделать следующее

PlayAndWait(StorageFile1, volume:1.0);
PlayAndWait(StorageFile2, volume:0.5);
PlayAndWait(StorageFile3, volume:0.7);
person Paulustrious    schedule 04.05.2017

Я создал метод расширения MediaPlayer на основе вашего ответа, который может оказаться полезным для других.

public static class MediaPlayerExtensions
{
    private static CancellationTokenSource _cancellationTokenSource;

    public static async Task PlayAsync(this MediaPlayer mediaPlayer)
    {
        mediaPlayer.MediaEnded -= MediaPlayer_MediaEnded;
        mediaPlayer.MediaEnded += MediaPlayer_MediaEnded;

        mediaPlayer.Play();

        _cancellationTokenSource = new CancellationTokenSource();

        await Task.Run(() =>
        {
            WaitHandle.WaitAny(new[] { _cancellationTokenSource.Token.WaitHandle });
        });
    }

    private static void MediaPlayer_MediaEnded(MediaPlayer sender, object args)
    {
        _cancellationTokenSource.Cancel();            
    }
}

Использование:

private static async Task Play(InMemoryRandomAccessStream stream)
{
    var player = new MediaPlayer();
    player.Source = MediaSource.CreateFromStream(stream, "");

    await player.PlayAsync();            
}
person Matt Searles    schedule 23.01.2019