LoginSignup
4
2

More than 5 years have passed since last update.

[UWP]MediaElementの再生位置を取得する

Last updated at Posted at 2016-04-17

追記

Windows10 Anniversary Update 以後では MediaPlayer 及び MediaPlayerElement を利用することで、メディアトランスポートやバックグラウンド再生が簡単にサポートできるようになりました。

動画ストリームをMediaSourceとして扱う必要がありますが、MediaElementで動画を扱うよりもコントロールが容易になります。

後方互換性が要件が必須な場合を除いては、MediaPlayer/MediaPlayerElementの利用をオススメします。

はじめに

ここで扱うのは Windows.UI.Xaml.Controls.MediaElement です。
System.Windows.Controls.MediaElement ではないのでご注意ください。

MediaElement.Positionプロパティについて

MediaElementの再生位置はPositionプロパティを通じて行いますが、MediaElement上で再生を開始してもPositionプロパティへの操作は内部からは行われません。そのためPositionプロパティのプロパティ変更通知もトリガーされません。

Positionプロパティについて
https://msdn.microsoft.com/ja-jp/library/system.windows.controls.mediaelement.position(v=vs.110).aspx

このページによると、Clockプロパティが設定されてない時にPositionが設定されたとき再生位置を指定できる。とありますが、読み取りについては記述がありません。

また、Positionプロパティの変更を伝えるイベントも存在しないようです。

そうなると再生に合わせてPositionプロパティの値を取得するには、Timer等でPositionの内容をモニターして定期的に取り出す処理が必要になります。

MediaElementの再生状況を知る方法

基本的にはMediaElement.CurrentStateプロパティまたは、CurrentStateChangedイベントを通じて現在の再生状況を把握することが出来ます。

TimerでPositionプロパティをモニターする際、メディアのクローズ後やポーズ中にモニター処理が走らないように工夫できると良さそうですね。

Positionプロパティを定期読み取りするビヘイビア

MediaElementPositionExtractBehavior.cs
using System;
using System.Threading;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Microsoft.Xaml.Interactivity;

namespace MyApp.Views.Behaviors
{
    public class MediaElementExtractPositionBehavior : Behavior<MediaElement>
    {

        #region Position Property

        public static readonly DependencyProperty PositionProperty =
            DependencyProperty.Register("Position"
                    , typeof(TimeSpan)
                    , typeof(MediaElementExtractPositionBehavior)
                    , new PropertyMetadata(default(TimeSpan))
                );

        public TimeSpan Position
        {
            get { return (TimeSpan)GetValue(PositionProperty); }
            set { SetValue(PositionProperty, value); }
        }

        #endregion


        #region Interval Property

        public static readonly DependencyProperty IntervalProperty =
            DependencyProperty.Register("Interval"
                    , typeof(TimeSpan)
                    , typeof(MediaElementExtractPositionBehavior)
                    , new PropertyMetadata(default(TimeSpan))
                );

        public TimeSpan Interval
        {
            get { return (TimeSpan)GetValue(IntervalProperty); }
            set { SetValue(IntervalProperty, value); }
        }

        #endregion


        #region MediaElement Event Handling

        protected override void OnAttached()
        {
            base.OnAttached();

            this.AssociatedObject.Loaded += AssociatedObject_Loaded;

        }


        private void AssociatedObject_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            this.AssociatedObject.CurrentStateChanged += AssociatedObject_CurrentStateChanged;
            this.AssociatedObject.SeekCompleted += AssociatedObject_SeekCompleted;


            this.AssociatedObject.Loaded -= AssociatedObject_Loaded;
            this.AssociatedObject.Unloaded += AssociatedObject_Unloaded;
        }

        private void AssociatedObject_SeekCompleted(object sender, RoutedEventArgs e)
        {
            this.ExtractPosition();
        }

        private void AssociatedObject_CurrentStateChanged(object sender, RoutedEventArgs e)
        {
            var mediaElem = (MediaElement)sender;

            switch (mediaElem.CurrentState)
            {
                case Windows.UI.Xaml.Media.MediaElementState.Closed:
                    TimerExit();
                    break;
                case Windows.UI.Xaml.Media.MediaElementState.Opening:
                    RefreshTimerSetting();
                    break;
                case Windows.UI.Xaml.Media.MediaElementState.Buffering:
                    break;
                case Windows.UI.Xaml.Media.MediaElementState.Playing:
                    RefreshTimerSetting();
                    break;
                case Windows.UI.Xaml.Media.MediaElementState.Paused:
                    TimerExit();
                    break;
                case Windows.UI.Xaml.Media.MediaElementState.Stopped:
                    TimerExit();
                    break;
                default:
                    break;
            }
        }

        private void AssociatedObject_Unloaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            TimerExit();
        }


        protected override void OnDetaching()
        {
            base.OnDetaching();

            TimerExit();
        }


        #endregion


        #region Management Timer


        private Timer _ExtractTimingTimer;


        private void RefreshTimerSetting()
        {
            TimerExit();

            _ExtractTimingTimer = new Timer((x) =>
            {
                var me = (MediaElementExtractPositionBehavior)x;

                me.ExtractPosition();

            }, this, 0, (int)Interval.TotalMilliseconds);
        }

        private void TimerExit()
        {
            _ExtractTimingTimer?.Dispose();
            _ExtractTimingTimer = null;
        }


        private async void ExtractPosition()
        {
            var mediaElem = this.AssociatedObject as MediaElement;

            await this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Low,
                    () => this.Position = mediaElem.Position);
        }


        #endregion
    }
}

気軽にTimerスレッドを作ったり消したりしていますが、気になる場合にはCurrentStateのOpeningとClosedでだけ作成と削除を行えば問題ないと思います。

SeekCompletedイベントにも反応するようにしていますが、シーク後にもPlayingステートへの変更が呼ばれると思う(動作未確認)ので不要かもしれません。

Xamlで利用する方法は以下の通り

MediaElementWithGetPosition
<Page
    x:Class="MyApp.Views.VideoPlayerPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:i="using:Microsoft.Xaml.Interactivity"
    xmlns:core="using:Microsoft.Xaml.Interactions.Core"
    xmlns:mybehaviors="MyApp.Views.Behaviors"
    mc:Ignorable="d">

    <Grid>
        <MediaElement Source="smile.mp4">
            <i:Interaction.Behaviors>
                <mybehaviors:MediaElementExtractPositionBehavior 
                    Position="{Binding CurrentVideoPosition, Mode=TwoWay}"
                    Interval="00:00:00.016" 
                    />
            </i:Interaction.Behaviors>
        </MediaElement>
    </Grid>
</Page>

PositionにBindingしているCurrentVideoPositionは、VideoPlayerPageのDataContextに設定するVideoPlayerPageViewModel上で定義します。

IntervalにはTimeSpan型の固定値を渡しています。この例では0.016秒(約1/60秒)ごとにPositionプロパティを読み込むことになります。

他によくわからないところはコメントもらえれば補足します。

参考

MediaElement
https://msdn.microsoft.com/ja-jp/library/windows/apps/windows.ui.xaml.controls.mediaelement

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2