【问题标题】:UWP waiting for MediaPlayer to complete before continuingUWP 在继续之前等待 MediaPlayer 完成
【发布时间】:2017-05-03 19:55:28
【问题描述】:

我希望使用媒体播放器给出一些声音指示。计算机试图说:
"在文本框中输入您的姓名缩写,然后按 Enter"

发生四件事中的一件

1) 事件 MediaPlayer.OnMediaPlayerFailed
2) 事件 MediaPlayer.OnMediaPlayerEnded
3) 用户完成任务并在 MediaPlayer 仍在播放时按 Enter
4)用户什么都不做,我们希望超时

MediaPlayer.Play() 在开始播放录音之前将控制权返回到下一条指令。 UI 线程必须等待。但是怎么做?我决定使用 Task.Run / Task.Factory.Run / Task.Factory.RunStart 开始一个新任务。然后在 UI 线程上等待(timeout_interval)。但我只是把同样的问题交给了新的线程/任务。 MediaPlayer.Play() 实际上是新任务的最后一行,并在声音结束之前完成。在执行 OnMediaXxxx 之前,我需要保持任务处于活动状态。然后这两个事件将完成任务或取消 UI.Wait() 我一直在用 CancellationTokens、CancellationTokenSources 和无数不同的等待方式兜圈子。

解决办法是什么?我正在尝试做的似乎不起作用

【问题讨论】:

  • 我相信有多种方法可以做到这一点。其中之一是您可以先禁用文本框,然后在媒体完全播放后启用它,方法是等待线程 5 秒后MediaPlayer 开始播放声音。

标签: uwp media-player wait cancellationtokensource cancellation-token


【解决方案1】:

我现在秃顶了……但成功了。这一切都归结为等待录制完成。所以我在 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);

【讨论】:

    【解决方案2】:

    我根据您的回答创建了一个 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();            
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-09-16
      • 1970-01-01
      • 1970-01-01
      • 2020-02-24
      • 1970-01-01
      • 2019-03-12
      • 2021-12-26
      相关资源
      最近更新 更多