Xamarin Forms Video Player Sample - Get Video Bytes for Uploading - xamarin

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

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.

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.

Related

Xamarin Copy File Cross Platform

Is it possible to copy a file in Xamarin Cross Platform? e.g. copy a SQLlite db3 from one directory to another. All i can get is the path of the file.
I found solutions for Xamarain.Android and iOS but I want to have it combined in one Portable class.
EDIT
Maybe there are better solutions out there but here is what I got with PCL Sotrage.
IFile file = FileSystem.Current.GetFileFromPathAsync(src).Result;
IFolder rootFolder = FileSystem.Current.GetFolderFromPathAsync(dest).Result;
IFolder folder = rootFolder.CreateFolderAsync("MySubFolder", CreationCollisionOption.OpenIfExists).Result;
IFile newFile = folder.CreateFileAsync("TodoItem.db3", CreationCollisionOption.GenerateUniqueName).Result;
Stream str = file.OpenAsync(FileAccess.ReadAndWrite).Result;
Stream newStr = newFile.OpenAsync(FileAccess.ReadAndWrite).Result;
byte[] buffer = new byte[str.Length];
int n;
while ((n = str.Read(buffer, 0, buffer.Length)) != 0)
newStr.Write(buffer, 0, n);
str.Dispose();
newStr.Dispose();
I think a good solution is to use DependencyService.
For Example, in PCL create an Interface
public interface IFile {
void Copy ( string fromFile, string toFile );
}
In Android the platform-specific implementation
[assembly: Xamarin.Forms.Dependency (typeof (FileImplementation))]
namespace File.Droid {
public class FileImplementation : IFile
{
public FileImplementation() {}
public void Copy(string fromFile, string toFile)
{
System.IO.File.Copy(fromFile, toFile);
}
}
}
Then in your PCL you can call
DependencyService.Get<IFile>().Copy("myfile", "newfile");
With .NETStandard you could use System.IO directly in your PCL Project
The cool new way to do it would be to use a .Net Standard library instead of a PCL. .Net Standard 1.6 is supported on Xamarin.IOS and Xamarin.Android and include System.IO.File unlike PCL profiles.
You could also use a Shared Project instead of a PCL, since both X.IOS and X.Android include System.IO.File support, it would work pretty seamlessly in a Shared Project.
Otherwise, If you are using Xamarin Forms, I think Alessandro's DependencyService solution is a good one, and even if you aren't using Forms, the interface abstraction idea applies.
To avoid having to deal with different filesystems and their quirks on each platform, you can use the PCLStorage library to do quite a wide array of operations on all Xamarin.Forms platforms.
Take a look at it here: https://github.com/dsplaisted/PCLStorage

File Activation in Windows Store app in C#?

I m tryin to make metro Media Player ,but I have a problem in file activation i.e when I open mp3 file from Windows explorer, it open my app But Doesn,t get Arguments, how can I get file or name of file in text block for which my app is launched,i saw many methods in different Websites,But I can't Solve,
here is a really good explanation on how to do this:
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh779669.aspx
you can get the file name and file size here:
protected override void OnFileActivated(FileActivatedEventArgs args)
{
// TODO: Handle file activation
// The number of files received is args.Files.Size
// The first file is args.Files[0].Name
}

What do I put as cast.receiver.RemoteMedia.NAMESPACE?

On the receiver examples I always see cast.receiver.RemoteMedia.NAMESPACE used. Is that supposed to be replaced by my own name? I tried using 'ramp' I also tried 'myownnamespace' and 'ramp','myown' and all of those with brackets around them. Any time I change it from cast.receiver.RemoteMedia.NAMESPACE my code stops working. Below is the code I am talking about:
var receiver = new cast.receiver.Receiver(
'YOUR_APP_ID_HERE',
[cast.receiver.RemoteMedia.NAMESPACE],
"",
5);
var remoteMedia = new cast.receiver.RemoteMedia();
remoteMedia.addChannelFactory(
receiver.createChannelFactory(cast.receiver.RemoteMedia.NAMESPACE));
I also tried something I found on the document, didn't work either.
var receiver = new cast.receiver.Receiver('myappid', ['ramp', 'other']);
var rampHandler = new cast.receiver.RemoteMedia();
rampHandler.addChannelFactory(receiver.createChannelFactory('ramp'));
var channelHandler = new cast.receiver.ChannelHandler('other');
channelHandler.addChannelFactory(receiver.createChannelFactory('other'));
So what values should I be putting there? should my android app reference those values somewhere?
Thanks.
The "cast.receiver.RemoteMedia.NAMESPACE" is for media playback using the HTML5 video tag. That channel will use the RAMP protocol for media controls. The Cast SDK provides utility classes to manage a media channel (see MediaProtocolMessageStream, MediaProtocolCommand).
If you don't want to play media then you can create your own channel with its own namespace. Look at the Tic-Tac-Toe sample app. Your channel prototype should declare your namespace in JavaScript and then that is used to initialize the receiver and add your custom channel handler.
If your app plays media then you should just have to change the 'YOUR_APP_ID_HERE' in the receiver and use the same app id in your Android code to start a session.

how to find out the FileType of an audio-file?

i'm using Core-Audio/AudioToolbox (Extended Audio File services) to read audio files on OSX.
for my specific application, i need to find out, whether the file i opened successfully using ExtAudioFileOpenURL() is a CAF-file.
unfortunately i seem to miss how to do this properly, as i cannot retrieve the AudioFileTypeID from an ExtAudioFileRef.
(when writing such a file, i can define the type by passing the AudioFileTypeID to ExtAudioFileCreateWithURL; what about the reverse?
like with my other question, it turned out that i have to use the normal AudioAPI (rather than ExtAudio API)
static bool isCAF(const AudioFileID*file) {
/* trying to read format (is it caf?) */
UInt32 format=0;
UInt32 formatSize=sizeof(format);
AudioFileGetProperty (*file, kAudioFilePropertyFileFormat, &formatSize, &format);
return (kAudioFileCAFType==format);
}

how to generate MSTEST results in Static folders

Is there a way to control the name of the MSTEST video recoding file names or the folder names with the test name. It seems to generate different guid everytime and thus very difficult to map the test with its corresponding video recording files.
The only solution I can see is to read the TRX file and map the guid to Test Name.
Any suggestions ??
If you're not opposed to doing it by hand, it's pretty easy. I encountered the same problem, and needed them to be somewhere predictable so I could email links to the videos. In the end my solution just ended up being to code in the functionality by hand. It's a bit involved, but not too difficult.
First, you'll need to have Expression Encoder 4 installed.
Then you'll need to add these references to your project:
Microsoft.Expression.Encoder
Microsoft.Expression.Encoder.Api2
Microsoft.Expression.Encoder.Types
Microsoft.Expression.Encoder.Utilities
Next, you need to add the following inclusion statements:
using Microsoft.Expression.Encoder.Profiles;
using Microsoft.Expression.Encoder.ScreenCapture;
Then you can use [TestInitialize] and [TestCleanup] to define the correct behavior. These methods will run at the beginning and end of each test respectively. This can be done something like this:
[TestInitialize]
public void startVideoCapture()
{
screenCapJob.CaptureRectangle = RectangleSelectionUtilities.GetScreenRect(0);
screenCapJob.CaptureMouseCursor = true;
screenCapJob.ShowFlashingBoundary = false;
screenCapJob.OutputScreenCaptureFileName = "path you want to save to";
screenCapJob.Start();
}
[TestCleanup]
public void stopVideoCapture()
{
screenCapJob.Stop();
}
Obviously this code needs some error and edge case handling, but it should get you started.
You should also know that the free version of Expression Encoder 4 limits you to 10 minutes per video file, so you may want to make a timer that will start a new video for you when it hits 10 minutes.

Resources