MediaElement 在视频启动时冻结

MediaElement freezes upon launch of video

我正在开发一个项目,该项目旨在通过 WPF Window 通过 MediaElement 播放音频和视频文件。这是 window 的 xaml:

<Window x:Class="HomeSystem_CSharp.MediaWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MediaWindow" MinHeight="480" MinWidth="720" WindowStyle="None" ResizeMode="NoResize" Visibility="Visible" Cursor="None">
    <Grid Background="Black">
        <MediaElement LoadedBehavior="Manual" HorizontalAlignment="Stretch" Name="video" VerticalAlignment="Stretch" Cursor="None" MinHeight="480" MinWidth="720"/>
    </Grid>
</Window>

这将创建无边框的 window,我计划在将来进行全屏显示。不过现在,我想在桌面上留出更多空间。这是我控制 MediaElement 的代码:

private bool playing = false;

        public MediaWindow(string dir)
        {
            InitializeComponent();

            video.Source = new Uri(dir);
            play();
        }

        public void play()
        {
            if (playing)
                return;

            if (!this.IsVisible)
                this.Show();

            video.Play();

            playing = true;
        }

这个媒体Window是在对象之外创建的,只是通过一个简单的MediaWindow mw = new MediaWindow("C:\test.mp4");

无论我如何移动代码中的内容,每次启动时 GUI 都没有响应,但会播放声音。我可以听到背景中的视频,但我的屏幕上有一个损坏的 window。只是一个黑盒子。

最大的问题是前几天还好好的,突然就坏了,不知道怎么回事。我是 c# 的新手,所以我不太了解正在发生的事情,但我已经与 java 合作多年,所以我并不完全是新手。谁能指出我做错了什么?我可以提供任何其他细节,但我想我已经掌握了所有必要的答案。感谢您的帮助,这一直困扰着我一整天都没有解决!

编辑:事实证明,如果我使用

public void play()
        {
            if (playing)
                return;

            //if (!this.IsVisible)
            //    this.Show();

            video.Play();
            new Application().Run(this);
            playing = true;
        }

相反,它将 运行 GUI。但是,这会挂断控制台。最初我使用 this.Show() 解决了挂断问题,但现在它不起作用了。我知道将整个项目移动到一个 WPF 项目中可以解决这个问题,但我真的不想因为其他原因这样做。目前只有win32。任何想法为什么会发生这种情况以及如何解决它?如果有区别的话,我的主要功能确实有 [STAThread]

编辑 2: 我正在播放的这个视频文件是电影长度,运行s 在任何其他软件中都是完美的,以防止它成为开发问题。至于MediaWindow的创作。我所做的是制作一个 win32 控制台项目并在那里设置用户命令。然后我创建了一个新的 WPF 项目,并创建了一个 xaml gui window。我获取了这些代码文件,并将它们复制到 win32 项目中,并调用它以使用 MediaWindow mw = new MediaWindow("C:\test.mp4"); 在 main 方法中启动我这样做是因为现在我正试图避免使用纯 WPF应用程序,因为我对 C# 有点陌生,所以我不确定如何在没有复制粘贴方法的情况下创建我想要的 window。

好的,整个混合 console/GUI 对我来说是个新事物,但我只是假设确实需要那样做。

Application.Run 方法在应用程序关闭之前不会 return。这就是您的控制台被锁定的原因。

不要在 window 中创建 Application 对象。从外部进行。此外,生成另一个线程以启动视频播放。这将使您的控制台响应。

我不会费力地描述线程和委托等等...如果需要,您可以查阅。我将介绍您需要为这个特定示例执行的操作。在启动视频的 class 中的某处,但不在方法中,定义这样的委托类型:

delegate void LaunchVideo(String s);

委托本质上是一种指向函数的指针,具有特定的 return 值和参数定义。在这里,我们将委托类型定义为一个接受字符串参数且 return 什么都没有的函数。接下来,在您要播放视频的代码中执行以下操作:

LaunchVideo lv = new delegate(String vidfile)
{
    Application app = new Application();
    app.Run(new MediaWindow(vidfile));
};

IAsyncResult result = lv.BeginInvoke( "C:\vid.mp4", myVideoCompleted, null );

这将创建委托变量并将其指向创建应用程序并启动视频播放的匿名函数。然后它调用委托的 BeginInvoke 方法,这是基本委托 class 的一部分。这会在委托指向的函数中生成一个新线程 运行。

请注意,像这样使用 window 参数调用 Application.Run 将打开 window,但不会调用 play() 方法。您可能希望将该代码移至构造函数,或在构造函数中添加对其的调用。

请注意,除非使用 lock 函数确保线程安全,否则您的主线程无法安全地调用在被调用线程中创建的对象中的方法。

如果您需要 "open" 和 "play" 是单独控制的事件,它们都由控制台调用,那么您必须想办法将消息从控制台线程传递到window 线程。

BeginInvoke 的参数列表始终以您正在调用的函数期望的任何参数开始。所以在这种情况下,这是带有视频文件名的字符串。接下来是回调函数的名称,当调用的函数退出时将调用该回调函数。这是一个带有 AsyncResult 参数的 void 函数,如下所示:

void myVideoCompleted(AsyncResult result)
{
    // Do whatever here...  Be aware this is still on the other thread
}

如果您不需要最后调用任何东西,您可以使用 'null' 代替函数名。请注意,如果您使用回调函数,它会在 BeginInvoke 启动的新线程上运行,而不是调用它的线程。

BeginInvoke 的最后一个参数是一个对象,它将通过 AsyncResult 参数的 AsyncState 成员传递给回调函数。如果你没有使用回调或者你没有回调需要的参数,你可以传递 'null'。

您还可以调用委托的 EndInvoke 方法来取回函数可能 return 编辑的任何结果。但是,请注意,如果调用的函数尚未完成,这将阻塞。在这种情况下,您无需担心结果。

No matter how i've moved stuff around in my code, upon launch EVERY time the GUI is unresponsive, but sound plays.

我已经成功地重现了这一点。您的描述中缺少的一件重要事情是您在 main() 方法中创建和显示 window 的确切方式。例如,以下冻结视频而保留声音播放:

[STAThread]
static void Main(string[] args)
{
    var w = new MediaWindow();
    w.Show();
    Console.ReadLine();
}

下一个 "freezes" 控制台,直到您关闭 window:

[STAThread]
static void Main(string[] args)
{
    var w = new MediaWindow();
    w.ShowDialog();
    Console.ReadLine();
}

这让你们都工作:

static void Main(string[] args)
{
    var thread = new Thread(ShowMediaWindow);
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();

    while (true) // Just to test console working
    {
        Console.Write("\r" + DateTime.Now);
        Thread.Sleep(100);
    }
}

static void ShowMediaWindow()
{
    new MediaWindow().ShowDialog();
}

如您所见,控制台和 WPF window 无法在单个线程中正常工作。

windowclass就这么简单,顺便说一句(XAML和你的差不多):

public partial class MediaWindow : Window
{
    public MediaWindow()
    {
        InitializeComponent();
        video.Source = new Uri(@"C:\video\test.asf");
        Play();
    }

    public void Play()
    {
        video.Play();
    }
}

我想这足以从控制台显示视频播放器 window。