libvlcsharp icon indicating copy to clipboard operation
libvlcsharp copied to clipboard

Adding a MediaPlayerElement to Maui - Updated

Open jonx opened this issue 1 year ago • 13 comments

Description of Change

This PR includes the implementation of a MediaPlayerElement for Maui. The changes include:

  • Added new MediaPlayerElement control to LibVCLSharp.MAUI
  • Added the sample LibVLCSharp.MAUI.Sample.MediaElement based on the Forms sample
  • Moved the MAUI samples to a new MAUI samples folder

Issues Resolved

  • fixes #637

Other Changes

None

Platforms Affected

  • Core (all platforms)
  • Android

Behavioral/Visual Changes

None

Before/After Screenshots

Screenshot 2024-09-30 143731

Testing Procedure

I was not able to test this on iOS

PR Checklist

  • [X] Rebased on top of the target branch at time of PR
  • [X] Changes adhere to coding standard

jonx avatar Sep 30 '24 12:09 jonx

CI complaining with some weird errors, SDK related likely.. I think I had similar issues when doing the Uno part.. will investigate https://gist.github.com/mfkl/c91642258c79f752c9541da5094c6777

And as before, I cannot reproduce locally.

mfkl avatar Oct 01 '24 04:10 mfkl

On your branch, CI restores only this:

2024-09-30T13:37:22.7862753Z Restore-NuGet-Packages
2024-09-30T13:37:22.7863433Z ========================================
2024-09-30T13:37:26.2608946Z D:\a\1\s\src\LibVLCSharp.Android.AWindow\LibVLCSharp.Android.AWindow.csproj : warning NU1503: Skipping restore for project 'D:\a\1\s\src\LibVLCSharp.Android.AWindow\LibVLCSharp.Android.AWindow.csproj'. The project file may be invalid or missing targets required for restore. [D:\a\1\s\src\LibVLCSharp.sln]
2024-09-30T13:37:26.2632321Z   Determining projects to restore...
2024-09-30T13:38:40.4303206Z   Restored D:\a\1\s\samples\MAUI\LibVLCSharp.MAUI.Sample.MediaElement\LibVLCSharp.MAUI.Sample.MediaElement.csproj (in 1.18 min).
2024-09-30T13:38:40.4305617Z   Restored D:\a\1\s\samples\LibVLCSharp.MAUI.Sample\LibVLCSharp.MAUI.Sample.csproj (in 1.18 min).
2024-09-30T13:38:40.7125951Z   Restored D:\a\1\s\src\LibVLCSharp.MAUI\LibVLCSharp.MAUI.csproj (in 265 ms).
2024-09-30T13:38:40.7182494Z   Restored D:\a\1\s\src\LibVLCSharp.Android.AWindowModern\LibVLCSharp.Android.AWindowModern.csproj (in 1 ms).
2024-09-30T13:40:42.2630528Z   Restored D:\a\1\s\src\LibVLCSharp\LibVLCSharp.csproj (in 2.03 min).
2024-09-30T13:40:42.3372853Z 

On current 3.x, it restores all projects. Maybe something with the libvlcsharp.sln changes?

mfkl avatar Oct 01 '24 05:10 mfkl

It's because I removed everything from build manager except what I need. Let's see what I can do.

jonx avatar Oct 01 '24 14:10 jonx

@jonx

Hi there!

I tested your code, and overall it works pretty well. However, I encountered some issues when playing network streams, specifically with HLS (m3u8) or DASH (mpd). When I try to seek using the progress bar, it doesn’t work as expected. After dragging to a specific position, the playback either jumps back or resets to the starting point. Interestingly, if I long-press the progress bar at the desired position, it sometimes successfully seeks to that spot, but the overall seeking behavior is very inconsistent. This happens on both Android and iOS.

I'm not sure if the issue is with LibVLCSharp or MediaElement. For reference, I’ve tested the same HLS or DASH streams in the VLC apps from Google Play and App Store, and they work perfectly.

Thanks for your help!

mrsleepman avatar Oct 03 '24 16:10 mrsleepman

Hi @mrsleepman,

Thank you for trying out this code and for sharing your feedback! I'm very happy to hear that it "mostly" works on both Android and iOS.

As you might know, this MediaElement control is meant to provide the best plug&play experience out of the box but will probably never support all the advanced features you would find in an advanced custom made video player.

That being said, I’ve seen similar reports regarding seeking issues with HLS and DASH streams. These types of streams handle seeking differently, often requiring the player to jump between segments, which can make precise seeking challenging.

And this code is a port from existing implementations, so it might inherit some underlying issues, I'm not sure yet, I have to investigate. Maybe one option to improve this could be using a queue to prioritize seek operations or implementing a debouncing mechanism to skip unnecessary seeks. However, this could be a lot of work and these potential solutions that would need further testing.

But I will have a look.

If you have any links to specific streams where this issue is particularly noticeable, feel free to share them. It would help me try to reproduce the issue more accurately.

Thanks again for your input!

jonx avatar Oct 03 '24 20:10 jonx

@jonx

Hi there!

I spent some time debugging this issue today and found that it might be a logical problem. During the process of dragging the seek bar, the "SeekBar_ValueChanged" event gets triggered repeatedly. The longer the drag lasts, the more times "SeekBar_ValueChanged" is executed, which might be why the time isn’t being set correctly.

Here’s the current code where the event "SeekBar.ValueChanged" is bound:

SeekBar.ValueChanged += SeekBar_ValueChanged;

private void SeekBar_ValueChanged(object? sender, ValueChangedEventArgs e)
{
    // Each drag action triggers many event calls
    Console.WriteLine("SeekBar_ValueChanged");
    Show();
    Manager.Get<SeekBarManager>().SetSeekBarPosition(e.NewValue);
}

To resolve this, I tried testing it by using "SeekBar.DragStarted" and "SeekBar.DragCompleted", and it seems to work properly:

SeekBar.DragStarted += SeekBar_DragStarted;
SeekBar.DragCompleted += SeekBar_DragCompleted;
SeekBar.ValueChanged += SeekBar_ValueChanged;

private bool isDragging = false;

private void SeekBar_DragStarted(object? sender, EventArgs e)
{
    isDragging = true; 
}

private void SeekBar_DragCompleted(object? sender, EventArgs e)
{
    isDragging = false; 

    if (SeekBar != null)
    {
        Show();
        Manager.Get<SeekBarManager>().SetSeekBarPosition(SeekBar.Value);
    }
}

private void SeekBar_ValueChanged(object? sender, ValueChangedEventArgs e)
{
    if (isDragging)
    {
        Show();

        if (SeekBar != null)
        {
            var position = e.NewValue / SeekBar.Maximum;
            
            // I temporarily made Length public for testing
            var estimatedTime = TimeSpan.FromMilliseconds(position * Manager.Get<SeekBarManager>().Length);

            Console.WriteLine($"Dragging Time: {estimatedTime}");
        }
    }
}

Additionally, I made some adjustments to prevent conflicts between the player and the user while interacting with the seek bar:

seekBarManager.PositionChanged += (sender, e) =>
{
    if (!isDragging)
    {
        UpdateTime();
    }
};

Your original code was really cool and solved a lot of issues! I hope these changes from my testing can help resolve this particular problem.

mrsleepman avatar Oct 04 '24 15:10 mrsleepman

I have to test it (early next week) but it looks like indeed this will solve the time bug. Thanks for looking into this further.

Does this code solve the problem you reported while seeking in video streams?

jonx avatar Oct 04 '24 15:10 jonx

I tried to minimize changes based on your code and the time is now correctly updated after a manual seek.

jonx avatar Oct 04 '24 23:10 jonx

A compatible .NET SDK was not found.

Requested SDK version: 8.0.300
global.json file: D:\a\1\s\src\global.json

Installed SDKs:

Install the [8.0.300] .NET SDK or update [D:\a\1\s\src\global.json] to match an installed SDK.

CI breaking again for some reason, with a stupid error message.

mfkl avatar Oct 05 '24 05:10 mfkl

I tried to minimize changes based on your code and the time is now correctly updated after a manual seek.

I tested your latest code with the implementation that only adds the DragCompleted event. Unfortunately, this change alone isn’t enough to make it work correctly. The conflict between the ValueChanged event and the DragCompleted event still exists—they are both trying to control the seek bar, which leads to issues.

Here’s a DASH test stream I found on Google that I used for testing: https://cdn.bitmovin.com/content/assets/art-of-motion-dash-hls-progressive/mpds/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.mpd

mrsleepman avatar Oct 05 '24 08:10 mrsleepman

A compatible .NET SDK was not found.

Requested SDK version: 8.0.300
global.json file: D:\a\1\s\src\global.json

Installed SDKs:

Install the [8.0.300] .NET SDK or update [D:\a\1\s\src\global.json] to match an installed SDK.

CI breaking again for some reason, with a stupid error message.

For me, the way to resolve this issue was by modifying the global.json file to match the SDK version installed on my system. Here's the SDK version I have:

dotnet --list-sdks
8.0.400 [C:\Program Files\dotnet\sdk]
{
  "msbuild-sdks": {
    "MSBuild.Sdk.Extras": "3.0.44",
    "Uno.Sdk": "5.3.96"
  },
  "sdk": {
    "version": "8.0.400"
  }
}

This change made everything work correctly on my end. Hope this helps!

mrsleepman avatar Oct 05 '24 08:10 mrsleepman

Yep, thanks, but this PR makes no changes to the CI or global.json, so I'm not sure why the pipeline suddenly fails to install the proper SDK. I also cannot reproduce when building this branch in a pipeline from my fork. It was building fine before that last commit, so my guess is Azure Pipelines is buggy for this PR specifically..

Edit: https://github.com/microsoft/azure-pipelines-tasks/issues/20508

mfkl avatar Oct 07 '24 05:10 mfkl

Ok, last changes seem fine, thanks. CI is OK (besides the usual randomly crashing native test..).

Will test one last time on Android and iOS when I can, and merge.

If anyone feels like doing more testing in the meantime, be my guest!

mfkl avatar Oct 17 '24 07:10 mfkl

Thanks a lot @jonx! (for your work and patience)

Aiming for a release early next week.

mfkl avatar Nov 06 '24 08:11 mfkl