0

I'm implementing a video player in a Xamarin Forms app just like the video player sample provided by Xamarin

https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/video-player/

I'm able to select a video from the phone gallery, set the video player source to the selected video, and play the video. How do I get the actual stream or bytes of the selected video so that I can upload it to Blob Storage?

I've tried

using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) ..........

where fileName is the path and file name of the selected video as set to the video player source. It doesn't work as the Android file name string is not found. (When invoking this from xamarin forms). I realize the file name will be different even when on iOS. How do I reach down into the platform specific implementations and get the file bytes or stream of the selected file? thanks

Patrick Goode
  • 1,412
  • 5
  • 20
  • 35
  • 1
    you would probably need to modify the code for each platforms Video Picker – Jason Mar 26 '19 at 21:31
  • Yeah I was thinking so. I'd have to expose a method in the interface and implement it in each platform. I guess I was looking for an easier way out. I'd still have to figure out in each platform how to get the bytes – Patrick Goode Mar 26 '19 at 21:39

2 Answers2

1

I would look into the libVLCSharp library which provides cross-platform .NET/Mono bindings for libVLC. They provide good support for Xamarin.Forms and the features you most likely need to implement the stream handling functionality. What you're trying to achieve won't be simple but it should be perfectly doable.

First, you should check out the documentation for Stream output:

Stream output is the name of the feature of VLC that allows to output any stream read by VLC to a file or as a network stream instead of displaying it.

Related tutorial: Stream to memory (smem) tutorial.

That should get you started but there will for sure be many caveats along the way. For example, if you try to play the video while capturing the bytes to be uploaded somewhere, you'll have to respect VERY tight timeframes. In case you take too long to process the stream, it will slow down the playback and the user experience suffers.


Edit: Another option you could look into is to interact directly with the MediaPlayer class of libVLC, as explained in this answer. The sample code is in C++ but the method names are very similar in the .NET bindings.

For example, the following piece of code:

libvlc_video_set_callbacks(mplayer,
                           lock_frame, unlock_frame,
                           0, user_data);

can be implemented with libVLCSharp by calling the SetVideoCallbacks(LibVLCVideoLockCb lockCb, LibVLCVideoUnlockCb unlockCb, LibVLCVideoDisplayCb displayCb) method in the binding library, as defined here.

Timo Salomäki
  • 7,099
  • 3
  • 25
  • 40
  • Thank you for this. I will go back to it if I can devote that kind of time, although I will try to implement something simpler if I can – Patrick Goode Mar 27 '19 at 03:08
0

You can do this pretty simply by using a DependencyService. You will need to adjust the below code to cater for a folder location that you're working with but, do this.

Change all of the "Test" namespaces to you're own project.

Add an interface into your shared project called IFileSystem that looks like this ...

using System;

namespace Test.Interfaces
{
    public interface IFileSystem
    {
        byte[] GetFileInBytes(string fileName);
    }
}

Create a dependency service down in each platform project. For this, I'm only supplying iOS and Android but as you'll see, the logic for both is essentially exactly the same, only the namespace differs ...

iOS

using System;
using System.IO;
using Test.Interfaces;
using Test.iOS.DependencyServices;
using Xamarin.Forms;

[assembly: Dependency(typeof(FileSystem))]
namespace Test.iOS.DependencyServices
{
    public class FileSystem : IFileSystem
    {
        public byte[] GetFileInBytes(string fileName)
        {
            var folder = Environment.GetFolderPath(Environment.SpecialFolder.MyVideos);
            fileName = Path.Combine(folder, fileName);
            return File.Exists(fileName) ? File.ReadAllBytes(fileName) : null;
        }
    }
}

Android

using System;
using System.IO;
using Test.Interfaces;
using Test.Droid.DependencyServices;
using Xamarin.Forms;

[assembly: Dependency(typeof(FileSystem))]
namespace Test.Droid.DependencyServices
{
    public class FileSystem : IFileSystem
    {
        public byte[] GetFileInBytes(string fileName)
        {
            var folder = Environment.GetFolderPath(Environment.SpecialFolder.MyVideos);
            fileName = Path.Combine(folder, fileName);
            return File.Exists(fileName) ? File.ReadAllBytes(fileName) : null;
        }
    }
}

... now call that from anywhere in your shared project.

var bytes = DependencyService.Get<IFileSystem>().GetFileInBytes("Test.mp4");

That should work for you, again though, you need to adjust the folder path to your appropriate location for each platform project. Essentially, this line is the one that may need to change ...

var folder = Environment.GetFolderPath(Environment.SpecialFolder.MyVideos);

Alternatively, change that code to suit your requirements. If the file path you've been given contains the fully qualified location, then remove the logic to add the folder altogether.

Here's hoping that works for you.

Skin
  • 9,085
  • 2
  • 13
  • 29
  • Thanks, this approach seems logical. However, in the Xamarin Forms video player, when you pick the video, the result is a URI. It seems impossible to get the actual path from the URI https://stackoverflow.com/questions/46975574/how-to-get-actual-path-from-uri-xamarin-android – Patrick Goode Mar 27 '19 at 03:36
  • content://com.android.providers.media.documents/document/video%3A1710 – Patrick Goode Mar 27 '19 at 03:45
  • Looks like that URI will depend on where the user actually picks the video from? Craziness. Who would've thought it would take hours to pick and upload a video – Patrick Goode Mar 27 '19 at 03:46
  • @PatrickGoode it looks like there are a few other examples on stack about how to convert the content uri to a file path. I suggest looking for that because when you get that, you’ll then be able to get it as a byte array. – Skin Mar 27 '19 at 03:54
  • Yes I'm looking for that. Thanks! – Patrick Goode Mar 27 '19 at 03:55
  • @PatrickGoode try this ... Uri uri = new Uri(yourString) ... there’s an absolute path property that might work. – Skin Mar 27 '19 at 03:55
  • Thanks, but it absolute uri just trims down to /document/video%3A1710. I'll try the answers in the linked question above – Patrick Goode Mar 27 '19 at 04:05
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/190741/discussion-between-patrick-goode-and-skin). – Patrick Goode Mar 27 '19 at 05:01