MediaElement在视频启动时冻结

MediaElement freezes upon launch of video
2020-10-22
  •  译文(汉语)
  •  原文(英语)

我正在设计一个旨在通过MediaElement通过WPF窗口播放音频和视频文件的项目.这是窗口的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#有点新,所以我不确定如何在没有复制粘贴方法的情况下创建所需的窗口.

速聊1:
视频是否可以在您的系统上流畅播放,例如在Windows Media Player中播放?另外,我认为您需要添加更多有关这些内容的详细信息.用什么代码创建MediaWindow?
速聊2:
看到我的第二次编辑.有帮助吗?
速聊3:
我不确定"纯WPF应用程序"的概念是否像您认为的那样适用.当您使用WPF模板在Visual Studio中创建新项目时,它将创建一个默认窗口和一个将其打开的Application类,并将所有适当的引用添加到WPF程序集.为方便起见,它是自动化的.听起来您所做的主要是绕过自动化,可能还没有使用Application类.您是在创建Application类还是直接从程序的main()函数打开MediaWindow?
速聊4:
MediaWindow直接从main()打开我的.至于"纯WPF",我的意思是不是在整个项目中使用GUI,而是使用控制台,并使用GUI对其进行补充.
速聊5:
我不知道您所说的"我改用控制台" 是什么意思.您是指控制台模式的应用程序吗?就像在某个时候,用户输入一个命令,提示您打开一个窗口来播放视频,然后您返回控制台?
解决过程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();
    }
}

我想这会从控制台显示视频播放器窗口.

速聊1:
速聊2:
我现在正在工作,所以无法尝试,但是使用此方法将无法在MediaWindow类中调用其他函数,对吗?在这种情况下,将需要一个暂停和停止方法,这可以通过操纵线程来完成吗?就像,暂停/暂停/关闭线程会对显示的窗口产生所需的效果吗?
速聊3:
实际上,这非常简单:pastebin.com/BuEBDXDp您无需中止线程-使用鼠标或Close方法关闭窗口,它将停止.
解决过程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方法,该方法是基本委托类的一部分.这产生了一个新线程,该线程在委托指向的函数中运行.

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

速聊1:
这是一个非常详尽的答案,谢谢:)下班回家后,我将立即开始工作.但是,我还有一个问题.有了它,似乎在启动GUI窗口后立即操纵它是一个巨大的痛苦,因此,由于线程问题,让它全部成为GUI而不是混合控制台/ GUI会更好,因为?
速聊2:
操作GUI窗口并不难.只需从控制台端使用SendMessage,然后在视频窗口中添加消息事件处理程序即可.话虽这么说,是的,我认为纯图形用户界面应用会是一个更好的主意,但不仅仅是因为线程问题.

I'm working on a project that is designed to play both audio and video files through a WPF Window through a MediaElement. This is the xaml for the window:

<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>

This creates the window with no borders, that I plan on full-screening in the future. For now though, I want more room on my desktop. Here is my code for controlling my 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;
        }

This MediaWindow is created outside of the object, just by a simple MediaWindow mw = new MediaWindow("C:\\test.mp4");

No matter how i've moved stuff around in my code, upon launch EVERY time the GUI is unresponsive, but sound plays. I can hear the video in the background, but there is a broken window on my screen. Just a black box.

The biggest issue is that just the other day it was working fine, and suddenly it broke, and I have no clue what happened. I'm kinda new to c#, so I dont know a TON about what's going on, but I've worked with java for several years so I'm not totally new. Can anyone point out what I'm doing wrong? i can provide any other details but I think i got everything necessary to answer. Thank you for any help, this has been bothering me all day with no fix!

EDIT: Turns out, if I use

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

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

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

instead, it will run the GUI. However, that hangs up the console. Originally I fixed that hang up by using this.Show(), but now that's not working. I know that moving the whole project into a WPF project would fix this, however I'm really trying not to for other reasons. Only win32 for now. Any ideas why this is happening and how to fix it? I do have [STAThread] over my main function if that makes a difference.

EDIT 2: This video file I'm playing is movie length, and runs perfectly in any other software to prevent that from being an issue with development. As for the MediaWindow creation. What I did is made a win32 console project and set up the user commands there. I then made a new WPF project, and created an xaml gui window. I took those code files, and copied them into the win32 project, and call it to launch in the main method with the MediaWindow mw = new MediaWindow("C:\\test.mp4"); I did it this way because for now I'm trying to keep away from using a pure WPF application, and because I'm kinda new to C# so I wasnt sure how to create the window I wanted without my copy paste method.

Talk1:
Does the video otherwise play smoothly on your system, say for example in Windows Media Player? Also, I think you need to add more detail about what this is all wrapped in. What code is creating your MediaWindow?
Talk2:
See my second edit. Does that help?
Talk3:
I'm not sure the concept of a "pure WPF application" is quite as applicable as you think. When you create a new project in Visual Studio using the WPF template, it creates a default window and an Application class that opens it, and adds all the appropriate references to the WPF assemblies. It's automated for convenience. It sounds like what you've done is mainly bypass the automation, and possibly the use of the Application class. Are you creating an Application class or are you opening your MediaWindow directly from the program's main() function?
Talk4:
I'm opening my MediaWindow directly from main(). As for "pure WPF" I mean instead of using a GUI for the whole project, I use a console instead, and use a GUI to compliment it.
Talk5:
I don't know what you mean by "I use a console instead". Do you mean a console-mode app? As in, at some point the user enters a command that prompts you to open a window to play the video, then you return back to the console?
Solutions1

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

I've managed to reproduce this. One important thing missing in your description is the exact way you create and show the window in your main() method. For example, the following freezes the video leaving the sound playing:

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

The next one "freezes" the console until you close the window:

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

And this gives you both working:

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();
}

As you can see, the console and the WPF window simply can't work properly in a single thread.

The window class is as simple as this, by the way (the XAML is mostly the same as yours):

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

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

I guess that'll do for showing a video player window from console.

Talk1:
Talk2:
I'm at work right now so I cant try this, however with this method there would be no way to call other functions within the MediaWindow class, correct? That being the case, there would need to be a pause and stop method, would that be possible to do by manipulating the thread? Like, would pausing/suspending/closing the thread have that desired effect on the window that's displayed?
Talk3:
Actually, it's very straightforward: pastebin.com/BuEBDXDp You don't need to abort the thread - close the window (with your mouse or with the Close method) and it will stop.
Solutions2

OK, the whole hybrid console/GUI thing is a new one on me but I'm just going to assume there's a real need to do things that way.

The Application.Run method doesn't return until the application closes. That's why your console is locked up.

Don't create the Application object inside the window. Do it externally. Also, spawn another thread to kick off video playback. This will leave your console responsive.

I'm not gonna get heavy-duty into describing threading and delegates and so forth... you can look that up if you want. I'm just gonna go over what you need to do for this specific example. Somewhere in the class that launches the video, but not in a method, define a delegate type like this:

delegate void LaunchVideo(String s);

A delegate is essentially kind of a pointer to a function with a certain definition of return value and parameters. Here we've defined the delegate type as a function that takes a String parameter and returns nothing. Next, at the point in your code where you want to play the video, do this:

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

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

This creates the delegate variable and points it at an anonymous function that creates the app and launch video playback. Then it calls the delegate's BeginInvoke method, which is part of the basic delegate class. This spawns a new thread running in the function pointed to by the delegate.

Note that calling Application.Run with a window parameter like this will open the window but it won't call the play() method. You may want to move that code to the constructor, or add a call to it in the constructor.

Be aware that your main thread cannot safely call methods in objects created in the invoked thread unless you use the lock function to make things thread safe.

If you need "open" and "play" to be separately controlled events which are both invoked by the console then you'll have to figure out a means to pass messages from the console thread to the window thread.

The parameter list for BeginInvoke always starts off with whatever parameters are expected by the function you're invoking. So in this case, that's the string with the video filename. Next is the name of a callback function which will be called when the invoked function exits. It's a void function that takes an AsyncResult parameter, like this:

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

You can use 'null' instead of a function name, if you don't need anything called at the end. Be aware that if you do use a callback function, it runs on the new thread started by BeginInvoke, not the thread that called it.

The last parameter to BeginInvoke is an object that will be passed through to the callback function via the AsyncState member of the AsyncResult parameter. You can pass 'null' if you're not using a callback or if you have no parameters which will be needed by the callback.

You can also call the EndInvoke method of the delegate to get back any results that may've been returned by the function. However, be aware that this will block if the invoked function isn't finished yet. In this case you have no results to worry about.

Talk1:
This is a fantastically detailed answer, thank you :) As soon as I get home from work I'll start working with this. I do have another question, however. With this it seems like it'd be a huge pain to manipulate the GUI window as soon as it's launched, so would just having it all be a GUI as opposed to a hybrid console/GUI be just better in general because of the threading issues?
Talk2:
It's not really that hard to manipulate the GUI window. Just use SendMessage from the console side, and add a message event handler on the video window. Having said that, yes I think a pure GUI app would be a better idea, but not so much just because of threading issues.
转载于:https://stackoverflow.com/questions/30553695/mediaelement-freezes-upon-launch-of-video

本人是.net程序员,因为英语不行,使用工具翻译,希望对有需要的人有所帮助
如果本文质量不好,还请谅解,毕竟这些操作还是比较费时的,英语较好的可以看原文

留言回复
我们只提供高质量资源,素材,源码,坚持 下了就能用 原则,让客户花了钱觉得值
上班时间 : 周一至周五9:00-17:30 期待您的加入