【问题标题】:MediaElement freezes upon launch of videoMediaElement 在视频启动时冻结
【发布时间】:2015-08-13 17:50:52
【问题描述】:

我正在开发一个项目,该项目旨在通过 WPF 窗口通过 MediaElement 播放音频和视频文件。这是窗口的 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>

这将创建一个没有边框的窗口,我计划在未来全屏显示。不过现在,我希望桌面上有更多空间。这是我控制 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;
        }

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

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

最大的问题是前几天它工作正常,突然坏了,我不知道发生了什么。我对 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: 我正在播放的这个视频文件是电影长度,并且可以在任何其他软件中完美运行,以防止这成为开发问题。至于MediaWindow的创建。我所做的是制作了一个 win32 控制台项目并在那里设置用户命令。然后我创建了一个新的 WPF 项目,并创建了一个 xaml gui 窗口。我把这些代码文件复制到 win32 项目中,然后用MediaWindow mw = new MediaWindow("C:\\test.mp4"); 调用它以在 main 方法中启动我这样做是因为现在我试图避免使用纯 WPF 应用程序,而且因为我对 C# 有点陌生,所以我不确定如何在没有复制粘贴方法的情况下创建我想要的窗口。

【问题讨论】:

  • 视频能否在您的系统上流畅播放,例如在 Windows Media Player 中?另外,我认为您需要添加更多关于这一切的详细信息。什么代码正在创建您的 MediaWindow?
  • @MikeFulton 查看我的第二次编辑。这有帮助吗?
  • 我不确定“纯 WPF 应用程序”的概念是否像您想象的那样适用。当您使用 WPF 模板在 Visual Studio 中创建新项目时,它会创建一个默认窗口和一个打开它的 Application 类,并将所有适当的引用添加到 WPF 程序集。为方便起见,它是自动化的。听起来您所做的主要是绕过自动化,并可能使用 Application 类。您是在创建 Application 类还是直接从程序的 main() 函数打开 MediaWindow
  • 我直接从 main() 打开我的MediaWindow。至于“纯 WPF”,我的意思不是整个项目都使用 GUI,而是使用控制台,并使用 GUI 来补充它。
  • 我不知道您所说的“我使用控制台”是什么意思。你的意思是控制台模式的应用程序?例如,在某些时候,用户输入了一个命令,提示您打开一个窗口来播放视频,然后您返回控制台?

标签: c# wpf user-interface video freeze


【解决方案1】:

无论我如何在代码中移动内容,每次启动时 GUI 都没有响应,但会播放声音。

我设法重现了这一点。您的描述中缺少的一件重要事情是您在 main() 方法中创建和显示窗口的确切方式。例如,以下内容会冻结视频并继续播放声音:

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

下一个“冻结”控制台直到您关闭窗口:

[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 类就是这么简单,顺便说一下(XAML 和你的差不多):

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

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

我想这可以从控制台显示视频播放器窗口。

【讨论】:

  • 这里是 VS2013 解决方案的链接:drive.google.com/file/d/0B0EEu26dji2mSUlpS0pHQlVRcWM/…
  • 我现在正在工作,所以我不能尝试这个,但是使用这种方法将无法调用 MediaWindow 类中的其他函数,对吗?既然如此,就需要一个暂停和停止方法,这可以通过操纵线程来实现吗?比如,暂停/暂停/关闭线程会对显示的窗口产生预期的效果吗?
  • 其实很简单:pastebin.com/BuEBDXDp 你不需要中止线程——关闭窗口(用鼠标或关闭方法)它就会停止。
【解决方案2】:

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

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

不要在窗口内创建 Application 对象。在外面做。此外,生成另一个线程以启动视频播放。这将使您的控制台保持响应。

我不会过多地描述线程和委托等等......如果你愿意,你可以查一下。我只是想看看你需要为这个特定的例子做些什么。在启动视频的类中的某处,而不是在方法中,定义一个委托类型,如下所示:

delegate void LaunchVideo(String s);

委托本质上是一种指向具有特定返回值和参数定义的函数的指针。在这里,我们将委托类型定义为一个接受 String 参数并且不返回任何内容的函数。接下来,在代码中要播放视频的位置,执行以下操作:

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 方法,该方法是基本委托类的一部分。这会产生一个在委托指向的函数中运行的新线程。

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

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

如果您需要将“打开”和“播放”分别控制为由控制台调用的事件,那么您必须想办法将消息从控制台线程传递到窗口线程。

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 方法来取回该函数可能返回的任何结果。但是,请注意,如果调用的函数尚未完成,这将阻塞。在这种情况下,您无需担心结果。

【讨论】:

  • 这是一个非常详细的答案,谢谢 :) 下班回家后,我将开始处理这个问题。不过,我还有另一个问题。有了这个,一旦启动 GUI 窗口,操作它似乎会是一个巨大的痛苦,所以将它全部作为一个 GUI 而不是一个混合控制台/GUI 通常会更好,因为线程问题?
  • 操作 GUI 窗口并不难。只需在控制台端使用 SendMessage,并在视频窗口上添加消息事件处理程序。话虽如此,是的,我认为纯 GUI 应用程序会更好,但不仅仅是因为线程问题。
猜你喜欢
  • 2014-05-29
  • 1970-01-01
  • 2013-04-11
  • 2022-01-17
  • 2015-04-04
  • 2022-11-17
  • 2015-09-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多