Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Media Player Control for WPF that can Play Almost Any Media

0.00/5 (No votes)
18 Feb 2020 2  
A Media player control for WPF, based on MPV player that can play almost any media file.
This article will show you how to create a Media player control for WPF, which is based on MPV player that can play almost any media file. The provided code is proof of concept that can be foundation for a fully featured media player.

Image 1

Introduction, Problems

When WPF came out, it was revolutionary in terms of media. You could easily embed video files to your application, because it came with a MediaPlayer control. This media player control relies on DirectX and the Windows Media player infrastructure for codecs. This means in theory (and in practice, if you’re lucky), you can play any video file that you have a codec for.

Unfortunately, the currently popular and standard (H.264, H.265, WebM) codecs might not be installed on any system, so if you want to ship media files with your application, you will have to install codecs too, which might not be ideal in every case.

Suppose you have to include a video in your app, but want to have multiple audio tracks and subtitles with it. It’s not a problem for the MKV or WEBM containers, but unfortunately WPF doesn’t support multiple audio stream and subtitle handling, so you either create your own subtitle loading/displaying and audio playing engine that can do the same thing from multiple files.

My Solution

Luckily, we have multiple options to solve the problems mentioned in the introduction, each with a different cost.

A good starting point would be to implement a player using FFmpeg.

FFmpeg is the leading multimedia framework, able to decode, encode, transcode, mux, demux, stream, filter and play pretty much anything that humans and machines have created. It supports the most obscure ancient formats up to the cutting edge. No matter if they were designed by some standards committee, the community or a corporation.

It’s highly portable, runs on Linux, OS-X and Windows, and It’s extremely configurable. However, the big drawback with this approach is that you would have to implement most of your player in C/C++ and you would have to solve rendering on your own, because FFmpeg is “just” a codec.

However, there are existing video players that use FFmpeg for video decoding and support embedding.

Such a player is MPV, which is a modern and actively developed fork of Mplayer, which was one of the first FFmpeg video players.

The MPV player provides a simple C style API and can compile all of the needed code into a single DLL file that is sufficient to ship with your application. The functions in the DLL file can be accessed via Platform invoke, so it’s easy to use in a C# application.

Getting MPV

To get started, you will to get the mpv-1.dll file. This is the embeddable form of the player and can be downloaded from this link.

The builds target X64 systems, so if it’s still a requirement that your app needs to run on X86, then you will need to compile it manually. Also it’s worth mentioning that for customization and because of code control purposes, you might want to compile your own version from source for your release version.

Compiling MPV from source on Windows is quite a challenge, because it depends on a lot of libraries. However you can download the official build environment that they use to compile the Windows version from the following GIT repository: https://github.com/shinchiro/mpv-winbuild-cmake.

This comes with handy instructions on how to set up your development machine. Or if you are really determined, you can compile it from scratch with the help of this document.

The Implementation & MPV’s API

For the platform invoke part, I didn’t reinvent the wheel. I reused Aurel Hudec’s excellent Mpv.NET library project. For the platform invoke part, it uses delegates and dynamic DLL loading, so you can compile your application for Any CPU as long as you provide a proper path for both the X86 and X64 DLL file.

MPV runs in a separate thread in your application. This means that some calls are not executed instantly. E.g., you can’t get the number of streams directly after loading a file, because the player has to process it. I solved my control issues by adding some delays between my API calls.

MPV’s Client API is very straightforward. You Initialize the player and after that, you can send commands to the player via an API call. You can get and set various player properties via an other call and there’s an other call for getting and setting player options.

You can also observe property changes too. In the current implementation of my control, the only observed property is the position in the file that is used to update a slider.
Mpv.NET provides the MpvPlayer class that offers basic functionality, but you can access all the underlying API methods as handy C# wrappers. By using this, you can extend the player functionality.

I used the API calls interface in my control to implement chapter, subtitle and audio track selection.

The Control

I implemented the control in two parts. MpvPlayer is the player controller and MpvDisplay is responsible for displaying the video. This two part solution allows flexible app layout creation. Both controls are directly inherited from WPF’s Control class, so they are custom controls. I choose this implementation strategy, because I wanted to make public only those properties that are really relevant for the given control context.

The MpvDisplay control is a wrapper around the WindowsFormsHost element, which hosts a standard Windows Forms Panel control. This is required for a Handle. This handle specifies the surface where MPV will render the video. The usage of WindowsFormsHost is necessary, because WPF controls don’t have a Handle, only the Window has a handle. If we would specify that for MPV, it would overwrite the whole window content with the video.

The MpvPlayer control is the controller for MPV. It supports the following features:

  • Play/Pause
  • Stop
  • Volume adjustment
  • Seeking
  • Chapter selection (if the file has chapters. Notably MKV and MP4 files)
  • Audio track selection
  • Subtitle track selection

This control only works if you have bound a MpvDisplay control to its Display dependency property. It uses icons from the https://materialdesignicons.com/ website.

Image 2

Using the Code

The demo application shows how easily usable the control is. The XAML part of the main window is as shown below:

<Window

    x:Class="Mpv.Net.WpfTestApp.MainWindow"

    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:mpv="clr-namespace:Mpv.Net.Wpf;assembly=Mpv.Net.Wpf"

    Title="MainWindow"

    Closing="Window_Closing"

    Width="800"

    Height="450"

    mc:Ignorable="d">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <!-- Main menu for file selection -->
        <Menu>
            <MenuItem Header="File">
                <MenuItem

                    Click="MenuItem_Click"

                    Header="Load file..." />
            </MenuItem>
        </Menu>
        <!-- Mpv controls. Display is the video output -->
        <mpv:MpvDisplay

            x:Name="Display"

            Grid.Row="1" />
        <!-- Player controller. Main interaction -->
        <mpv:MpvPlayer

            x:Name="Player"

            Grid.Row="2"

            Display="{Binding ElementName=Display}" />
    </Grid>
</Window>

The code behind file is as follows:

using Microsoft.Win32;
using System.Threading.Tasks;
using System.Windows;
using System.ComponentModel;

namespace Mpv.Net.WpfTestApp
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        //Show a file select dialog and load the selected file
        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            var dialog = new OpenFileDialog();
            if (dialog.ShowDialog() == true)
            {
                Player.LoadFile(dialog.FileName);
            }
        }

        // When closing the window stop played video
        // and Dispose native dll resources
        // A wait is necessary after stop, because
        // it doesn't happen instantaneously
        private async void Window_Closing(object sender, CancelEventArgs e)
        {
            if (Player != null)
            {
                Player.Stop();
                await Task.Delay(1000);
                Player.Dispose();
            }
        }
    }
}

Call to Action

The control in its form is a proof of concept, not a ready solution for production, however it has some great potentials in it, so if you’re interested in the code or want to contribute to the project, you can get the code from my github repository which can be found at https://github.com/webmaster442/Mpv.Net.Wpf.

Points of Interest

History

  • 18th February, 2020: Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here