在WPF / C#中,如何使用SemaphoreSlim(信号)控制视频同步帧的播放?

vhmi4jdf  于 2022-11-18  发布在  C#
关注(0)|答案(2)|浏览(224)

项目详细信息:在这个WPF项目中,视频显示在一些结构上。实际上,它是一种模拟器,模拟视频在安装在结构或建筑物上的LED上的显示。

private async Task PlayBinAsync()
    {
        InitBinList();

        if (animPlayingMode == AnimPlayingMode.ParallelSynchronous)
        {
            List<Task> runningTasks = new List<Task>();
            Coordinator.Clear();
            for (int i = 0; i < Products.Count; i++)
            {
                Coordinator.Add(0);
                Task runningTask = ReadDisplayBinFrames(Products[i], true);
                runningTasks.Add(runningTask);

                #if DEBUG
                Debug.WriteLine($"LedProducts Count: {Products[i].LastFrameRead} of Product {i}");
                #endif
            }

            await Task.WhenAll(runningTasks);
        }
        
    }

public async Task ReadDisplayBinFrames(Product product, bool PlayMode)
    {
        BinFile.SetPlayMode(PlayMode);
        while (BinFile.IsPlayMode)
        {
            for (int currentFrameNumber = product.LastFrameRead; currentFrameNumber <= product.BinFiles.TotalGame; currentFrameNumber++)
            {
                await Task.Run(() =>
                {
                    product.BinFiles.GetSphereColorFromBin(product.BinFiles.GetFrame(currentFrameNumber), product.Wiring);
                    product.LastFrameRead = currentFrameNumber;
                    #if DEBUG
                    Debug.WriteLine($"LastFrameRead {product.LastFrameRead}");
                    #endif
                    product.Wiring.SetSphereColor(product.DelayPlay);
                });
                Coordinator[product.ProductId] += 1;
                await AllowPlayAsync(Coordinator);
                if (currentFrameNumber >= product.BinFiles.TotalGame)
                {
                    product.LastFrameRead = 0;
                }

                if (animPlayingMode == AnimPlayingMode.SerialAsync)
                {
                    BinFile.SetPlayMode(false);
                }
            }
        }
    }
private async Task AllowPlayAsync(List<int> input)
    {
        if (input.All(o => o == input[0]))
        {
            signal.Release();
        }
        else
        {
            await signal.WaitAsync();
        }
    }
List<int> Coordinator = new List<int>();
private SemaphoreSlim signal = new SemaphoreSlim(0, 1);

项目要求:

1-我希望所有的结构或建筑物运行的视频一致,甚至没有一个帧落后或一个帧前进。2-该项目应连续运行,不应停止播放视频,直到它停止。
为了使这些帧在结构上一起执行,我将执行的帧数存储在一个列表中,并使用AllowPlayAsync方法等待所有帧相遇并成为一个帧。
但不幸的是,它不能正常工作!

56lgkhnf

56lgkhnf1#

我不认为信号量是正确的解决办法。
如果您有一个进程需要显示多个同步图像,只需使用一个线程从每个源中选取图像,并将帧列表发送到UI线程。
您可以为每个视频流使用BlockingCollections。即,为每个视频流使用一个任务/线程来生成帧,并使用一个相当低的“上限”来阻止有足够缓冲区的生成线程。
然后,单个线程将从每个集合中获取一个图像。如果图像还不可用,则线程将阻塞,直到图像可用为止。一旦从每个集合中获取了图像,就将所有图像发送到UI线程以进行更新。
我不是WPFMaven,但我认为wpf有一个单独的渲染线程,所以当主线程更新图像时,可能会发生渲染。如果你需要更好的保证,你可能必须将所有图像合并成一个大图像,或者做一些其他操作系统的魔术来阻止渲染线程。

rsaldnfx

rsaldnfx2#

经过大量的搜索,我修改和优化了前面的代码如下:此代码运行良好并且准确,但是如果我希望其中一个产品单独工作或与其他产品分开工作,它将无法工作,并且会立即与其他产品协调。我决定使用**“Products list”而不是“Coordinator list”,但是由于未知原因,在运行AllowPlayAsync**方法几帧后,它会停留在等待模式中,并且不会从该模式中退出。

List<int> Coordinator = null;
private static SemaphoreSlim signal = new SemaphoreSlim(0);
public static ObservableCollection<Product> Products = new ObservableCollection<Product>();

private void ImportProducts()
        {
            // After Import Products
            // The number of members in the Coordinator list should be equal to the number of members in the Products list.
            Coordinator = new List<int>(new int[Products.Count]);
        }

public async Task ReadDisplayBinFrames(Product product, bool PlayMode)
        {
            product.BinFiles.IsPlayMode = PlayMode;
            for (int currentFrameNumber = product.LastFrameRead; currentFrameNumber <= product.BinFiles.TotalGame && product.BinFiles.IsPlayMode; currentFrameNumber++)
            {
                await Task.Run(() =>
                {
                    Coordinator[product.ProductId] = currentFrameNumber;
                    AllowPlayAsync();
                    product.BinFiles.GetSphereColorFromBin(product.BinFiles.GetFrame(currentFrameNumber), product.Wiring);
#if DEBUG
                    Debug.WriteLine($"LastFrameRead {product.LastFrameRead}");
#endif
                    product.Wiring.SetSphereColor(product.DelayPlay);
                    product.LastFrameRead = currentFrameNumber;

                    if (currentFrameNumber >= product.BinFiles.TotalGame)
                    {
                        product.LastFrameRead = currentFrameNumber = 0;
                    }
                    Application.Current.Dispatcher.InvokeAsync(() =>
                    {
                        if (animPlayingMode == AnimPlayingMode.SerialAsync)
                        {
                            product.BinFiles.IsPlayMode = false;
                        }
                    });
                });
            }
        }

private void AllowPlayAsync()
        {
            while (true)
            {
                if (Coordinator.ToList().All(o => o == Coordinator[0]))
                {
                    signal.Release();
                    return;
                }
                else
                {
                    signal.Wait();
                }
            }
        }

相关问题