Home > Software engineering >  WPF / C#, how can we control the playback of synchronous frames of videos with SemaphoreSlim (signal
WPF / C#, how can we control the playback of synchronous frames of videos with SemaphoreSlim (signal

Time:11-14

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!

CodePudding user response:

I do not think a semaphore is the correct solution.

If you have a single process that should show a number of synchronized images, just use a single thread that picks images from each of the sources and sends the list of frames to the UI thread.

You could use a BlockingCollections for each video stream. i.e. use one task/thread for each video stream to generate frames, and use a fairly low 'upperBound' to block the generating thread one you have a sufficient buffer.

The single thread would then take one image from each collection. If an image is not yet available, the thread will block until it is. Once you have a image from each collection, send over all the images to the UI thread for updating.

I'm no WPF expert, but I think wpf keeps a separate rendering thread, so it might be possible for rendering to occur while the main thread is updating the images. If you need better guarantees you may have to merge all the images to one large image, or do some other OS-magic to block the rendering thread.

CodePudding user response:

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.

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