1

I'm trying to create a media player (with Media.MediaPlayer() class) and for this I am using a thread to process the songs that are loaded by the user using the OpenFileDialog. I am using the next code to start the process the songs:

    public static List<MediaFile> MediaList = new List<MediaFile>();
    public static Queue<String> MediaFilesQueue = new Queue<String>();



public static void AddMediaFilesToMediaList()
        {

            String pathToFile;


            while (MediaFilesQueue.Count > 0)
          // all the files are loaded into the Queue before processing
            {
            pathToFile = MediaFilesQueue.Dequeue();
            MediaData.MediaList.Add(new MediaFile(pathToFile));

            MediaFileCreator mfCreator = 
                           new MediaFileCreator(MediaData.MediaList.Count - 1);
            mfCreator.CreateNewMediaFile();


            }

}

And this is the MediaFileCreator class:

public class MediaFileCreator
    {

        private int IndexOfMediaFileCurrentlyProcessed;


        public MediaFileCreator(int idx)
        {
            IndexOfMediaFileCurrentlyProcessed = idx;
        }

        public void CreateNewMediaFile()
        {


            var indexOfMediaFileCurrentlyProcessed = IndexOfMediaFileCurrentlyProcessed;

            var tempMediaFile = MediaData.MediaList[indexOfMediaFileCurrentlyProcessed];

            var tempMediaPlayer = new MediaPlayer();
            var waitHandle = new AutoResetEvent(false); 

            //EventHandler eventHandler = delegate(object sender, EventArgs args)
            //{
            //    waitHandle.Set();
            //};


            tempMediaPlayer.MediaOpened += (sender, args) => waitHandle.Set();

            tempMediaPlayer.Open(new Uri(tempMediaFile.PathToFile));

            waitHandle.WaitOne();


            //while (!tempMediaPlayer.NaturalDuration.HasTimeSpan)
            //{
            //    Thread.Sleep(100);
            //}


            var tempTimeSpan = tempMediaPlayer.NaturalDuration.TimeSpan;
            var hasVideo = tempMediaPlayer.HasVideo;
            var hasAudio = tempMediaPlayer.HasAudio;

            MediaData.MediaList[indexOfMediaFileCurrentlyProcessed].HasVideo = hasVideo;
            MediaData.MediaList[indexOfMediaFileCurrentlyProcessed].HasAudio = hasAudio;
            MediaData.MediaList[indexOfMediaFileCurrentlyProcessed].TimeSpanOfMediaFile
                                                                        = tempTimeSpan;


        } 

And this is the MediaFile class:

 public class MediaFile
    {
        public bool HasAudio = false;
        public bool HasVideo = false;

        public TimeSpan TimeSpanOfMediaFile;

        public String PathToFile = null;

        public MediaFile(String pathToFile)
        {
            PathToFile = pathToFile;
        }
    }

My problem is that the program stops at waitHandle.WaitOne(); and tries to run that line again and again. I tried other variants like the one in the first commented section by attaching that event handler to the Open event, but the result was the same: waitHandle.Set(); never executes and the value of the waitHandle is always false. The only option that I managed to get working is the the solution from the second commented section: blocking the thread (with Thread.Sleep) until the file is completely loaded (the file is loaded for example when the TimeSpan is initialised) ... which is time lost and performance decrease for my application. The problem is obviously not with the event itself, because the event triggers if it's run on the main thread (the method AddMediaFilesToMediaList() is called from a BackgroundWorker Thread that launches the method inside a new thread when it detects that there are elements in the Queue; the AddMediaFilesToMediaList() thread is created with new Thread() ) and obviously the file is loading because the TimeSpan is initialized. I really want to make the application work using the waitHandle or something like that. I don't want to use the Thread.Sleep() because it's ugly and my application also has a memory leak when I try to load a lot of files (i takes over 1.2 GB of memory and the it stops with errors (OutOfMemory) - I tried to load 2048 songs in it) and I believe that it could be because of the Thread.Sleep(). Even if it isn't, it will be much more easy to debug without the problem of the Thread.Sleep().

So how can I make the waitHandle to work ? How can I make the waitHandle.Set(); to run ? And if anyone has any idea about where that that excessive memory usage could come from, it would be great ! (I personally believe that it's the fault of Thread.Sleep() but I don't know how to get rid of it).

Edit : The reason I use an MediaFileCreator object is that initially I wanted to use a thread pool of 2 to 4 threads to process the media files, but I had the same problem so I removed the ThreadPool and I tried with the code posted above, but the same problem occurred.

Edit : I managed to get it working using a second thread to wait for the event (not the cleanest code right now, but I'll make it right).

public class MediaFileCreator
{

    private AutoResetEvent openedEvent = new AutoResetEvent(false);


    public MediaFile CreateNewMediaFile(string filename)
    {

        var mFile = new MediaFile(filename);
        var thread = new Thread(WaitForEvent);
        const int maxTimeToWait = 2000;
        openedEvent.Reset();
        thread.Start(mFile);

        var mediaPlayer = new MediaPlayer();

        mediaPlayer.Open(new Uri(mFile.PathToFile));

        openedEvent.WaitOne(maxTimeToWait);

        var fromThread = Dispatcher.FromThread(Thread.CurrentThread);
        if (fromThread != null) fromThread.InvokeShutdown();

        return mFile;

    }

    private void WaitForEvent(object context)
    {

        var mFile = (MediaFile)context;
        var mediaPlayer = new MediaPlayer();

        mediaPlayer.MediaOpened +=
            delegate
            {
                if (mediaPlayer.NaturalDuration.HasTimeSpan)
                    mFile.TimeSpanOfMediaFile = mediaPlayer.NaturalDuration.TimeSpan;
                mFile.HasAudio = mediaPlayer.HasAudio;
                mFile.HasVideo = mediaPlayer.HasVideo;
                mFile.Success = true;
                mediaPlayer.Close();
                openedEvent.Set();
            };

        mediaPlayer.MediaFailed +=
            delegate
            {
                mFile.Failure = true;
                mediaPlayer.Close();
                openedEvent.Set();
            };

        mediaPlayer.Open(new Uri(mFile.PathToFile));

        Dispatcher.Run();
    }
}
4

1 回答 1

3

有一件事是肯定的:Thread.Sleep电话与你的记忆问题无关。

我建议您多清理一下代码。您无需为加载的每个文件都创建一个新文件MediaCreator和一个新文件。MediaPlayer这可能是内存使用的很大一部分,因为您正在创建所有这些MediaPlayer对象而不是关闭它们。垃圾收集器最终会清理它们,但同时它会让你的内存使用看起来很大。

考虑一下:

public static void AddMediaFilesToMediaList()
{
    MediaFileCreator mfCreator = new MediaFileCreator();

    while (MediaFilesQueue.Count > 0)
    {
        // all the files are loaded into the Queue before processing
        string pathToFile = MediaFilesQueue.Dequeue();

        MediaFile mf = mfCreator.CreateNewMediaFile(pathToFile);

        MediaData.MediaList.Add(mf);
    }
}

public class MediaFileCreator
{
    private MediaPlayer player = new MediaPlayer();
    private ManualResetEvent openedEvent = new ManualResetEvent(false);

    public MediaFileCreator()
    {
        player.MediaOpened = MediaOpened;
    }

    private void MediaOpened(object sender, EventArgs args)
    {
        openedEvent.Set();
    }

    public MediaFile CreateNewMediaFile(string filename)
    {
        openedEvent.Reset();

        player.Open(new Uri(tempMediaFile.PathToFile));

        // wait for it to load
        openedEvent.WaitOne();

        MediaFile mf = new MediaFile(filename);
        mf.HasVideo = player.HasVideo;
        mf.HasAudio = player.HasAudio;
        mf.TimeSpanOfMediaFile = player.NaturalDuration.TimeSpan;

        player.Close();

        return mf;
    }
}

这大大简化了事情并且只使用了一个MediaPlayer对象,这应该会减少相当多的内存使用量。

我真的不知道你AutoResetEvent不工作的问题是什么。看起来不错。

在这个新代码中,您可以在调用上放置一个断点waitHandle.Set以查看它是否真的被命中。我不知道MediaOpened事件究竟是什么时候触发的,也不知道事件引发时玩家的状态应该是什么。文档对此保持沉默。

问题可能是MediaPlayer希望其代码在 UI 线程上执行。我对 WPF 控件还不够熟悉。您可能会调用VerifyAccess以确定您的线程是否可以访问该对象。

于 2013-08-09T23:19:35.277 回答