2020年5月3日日曜日

WPFで動画配信の再生(Vlc.DotNet.Wpf)

この記事ではRTMPの受信、再生をする方法として記載していますが、RTMPだけでなくHLSなどライブストリーム配信について同じ方法で対応可能かなと思います。


前回記事ではFFMpegを使用したRTMPの配信方法について記載しました。

WPFだろうが何だろうが、C#ではProcessとしてFFmpegを起動してあげれば配信可能なので、簡単かなと思います。

問題なのは再生の方かな。

1.再生用のWebページを作って、そのページをWPFで開く

サーバに配信用のPlayerとHTMLを置いて、そのページをWebBrowserコントロールで表示しようと思いました。
これなら、WPF側では制御いらないのでいいのかなと・・・

PlayerとしてはHTML5のvideoタグの利用をする方法と、FlowPlayerを利用する方法を試してみました。
WebBrowserコントロールはそのままではHTML5が表示できないので、レジストリを修正。
WPFのWebBrowserでHTML5を表示する

単体では動かせるけど、webBrowserコントロールで表示するとなると、動いてくれません。
やはり、javaやらflashなどを含めたページだから厳しいのかな・・・?

2.VlcControlの使用

結局うまくいったのは、Vlc.DotNet.Wpfを使用する方法です。

こちらのサイトを参考に導入しました。



必要なものは以下の4つ。
NuGetから取得できます。

  • VideoLAN.LibVLC.Windows
  • Vlc.DotNet.Core
  • Vlc.DotNet.Core.Interops
  • Vlc.DotNet.Wpf


VlcPlayerはXAMLのコードビハインドで以下のように書くだけ。

VlcLibDirectory = new DirectoryInfo(System.IO.Path.Combine(Directory.GetCurrentDirectory(), "libvlc", IntPtr.Size == 4 ? "win-x86" : "win-x64"));
string StreamParams = ":network-caching=2000";

var player = new VlcControl();
container.Content = player;
player.SourceProvider.CreatePlayer(VlcLibDirectory);
player.SourceProvider.MediaPlayer.Play(new Uri(RTMPのアドレス), StreamParams);

作成したVlcPlayerをContentControlなどのContentに入れてあげればOK。
導入は簡単だと思います。

残念な点があるとすれば、操作が基本的にXamlのコードビハインドになること。
そこで、ViewModelからバインドできるようにアレンジします。

3.VlcControlをViewModelから使用する

VlcContorlを操作するため、UserControlを作成し依存プロパティを追加します。

Xamlはこんな感じ。余計なものは省略しています。

VLCPlayer.xaml
<UserControl>
  <Grid>
    <ContentControl Name="PlayerContainer" />
  </Grid>
<UserControl>

で、UserControlのコードビハインド。
この例では依存プロパティとして「StreamPath」プロパティを追加しています。
StreamPathにViewModelからRTMPのパスを指定することで、再生先を切り替え出来ます。

VLCPlayer.xaml.cs

    public partial class VLCPlayer : UserControl
    {
        // Extra parameters to pass to the viewer media. I found a 2 seconds buffer cache makes playing much more stable.
        private const string StreamParams = ":network-caching=2000";

        public VlcControl vlcPlayer;
        VlcLibDirectory = new DirectoryInfo(System.IO.Path.Combine(Directory.GetCurrentDirectory(), "libvlc", IntPtr.Size == 4 ? "win-x86" : "win-x64"));

        #region StreamPath
        public static readonly DependencyProperty StreamPathProperty =
        DependencyProperty.Register("StreamPath",
                                    typeof(string),
                                    typeof(VLCPlayer),
                                    new FrameworkPropertyMetadata(string.Empty, new PropertyChangedCallback(OnStreamPathChanged)));

        public string StreamPath
        {
            get { return (string)GetValue(StreamPathProperty); }
            set { SetValue(StreamPathProperty, value); }
        }

        private static void OnStreamPathChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            VLCPlayer ctrl = obj as VLCPlayer;
            if (ctrl == null) return;

            ctrl.SetCamera();
        }
        #endregion


        public VLCPlayer()
  {
   InitializeComponent();
  }

        public void SetCamera()
        {
            if (vlcPlayer != null)
            {
                vlcPlayer.Dispose();
                vlcPlayer = null;
            }

            if (string.IsNullOrEmpty(this.StreamPath)) return;

            vlcPlayer = AddPlayer(this.StreamPath, PlayerContainer);
        }


        private VlcControl AddPlayer(string streamPath, ContentControl container)
        {
            var player = new VlcControl();

            container.Content = player;

            player.SourceProvider.CreatePlayer(VlcLibDirectory);
            player.SourceProvider.MediaPlayer.Play(new Uri(streamPath), StreamParams);

            return player;
        }

        private void UserControl_Unloaded(object sender, RoutedEventArgs e)
        {
            try
            {
                if (vlcPlayer != null)
                {
                    vlcPlayer.Dispose();
                    vlcPlayer = null;
                }
            }
            catch (Exception ex) { }
        }
    }