0
votes

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

https://docs.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

2
you would probably need to modify the code for each platforms Video PickerJason
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 bytesPatrick Goode

2 Answers

0
votes

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.

1
votes

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.