Search code examples
c#wpfasync-awaitparallel-processingsemaphore

WPF / C#, how can we control the playback of synchronous frames of videos with SemaphoreSlim (signal)?


Project Details: In this WPF project, video is displayed on the number of structures. In fact, it is a kind of simulator that simulates the display of video on the LEDs installed on the structure or building.

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

Project requirements:

1- I want all the structures or buildings to run the videos in unison and not even one frame behind or one frame forward. 2- The project should run continuously and should not stop playing the video until it stops.

In order for the frames to be executed together on the structures, I store the number of executed frames in a list and wait for all the frames to meet and become one using the AllowPlayAsync method. It is equalized, the work continues.

But unfortunately it does not work properly!


Solution

  • After searching a lot, I modified and optimized the previous code as follows: This code works well and accurately, but if I want one of the products to work separately or separately from other products, it will not work and it will immediately coordinate with other products. I decided to use the "Products list" instead of the "Coordinator list", but for unknown reasons, after running the AllowPlayAsync method for a few frames, it gets stuck in the wait mode and does not exit from this mode.

    private volatile static SemaphoreSlim signal = new SemaphoreSlim(0);
    public volatile static ObservableCollection<Product> Products;
    
    private void ImportProducts()
            {
                // After Import Products
                // The number of members in the Products Collection should be equal to the number of members in the Products list.
                Products = new ObservableCollection<Product>();
            }
    
    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(() =>
                        {
                            product.LastFrameRead = currentFrameNumber;
                            AllowPlayAsync();
    
                            product.BinFiles.GetSphereColorFromBin(product.BinFiles.GetFrame(currentFrameNumber), product.Wiring);
    #if DEBUG
                            Debug.WriteLine($"LastFrameRead {product.LastFrameRead} - PlayMode: {product.BinFiles.IsPlayMode}");
    #endif
    
                            SetSphereColor(product.Wiring,product.DelayPlay);
    
                            if (currentFrameNumber >= product.BinFiles.TotalGame)
                            {
                                product.LastFrameRead = currentFrameNumber = -1;
                            }
    
                            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();
                    }
                }
            }