A subtle process as to compress a video in Xamarin in order to reduce its size. This involves a tradeoff with the quality of the video, but that is easily compensated by the ease the process provides when you are buffering a video through the internet.
Step 1- Create an empty Xamarin shared Project, Let’s say “CompressedVideoDemo”.
Step 2- Make a folder named DS in the shared project and create Dependency Services class.
namespace CompressedVideoDemo.DS
{
public static class DependencyServices
{
public static IMobileFeature MobileFeature
{
get
{
return DependencyService.Get<IMobileFeature>(DependencyFetchTarget.GlobalInstance);
}
}
}
}
Step 3- Create an Interface which will contain the prototypes of all the functions that we need to perform as to compress a video, let’s name it ”IMobileFeature”.Make 4 functions in, namely Record video, Select video, Compressed video, Play video.
namespace CompressedVideoDemo.Interface
{
public interface IMobileFeature
{
Task<bool> CompressVideo(string inputPath,string outputPath);
}
}
Step 4- For Android - Create an IntentHelper class which provides functions for compression of video, recording, playing video and callbacks their response.
internal class IntentHelper
{
static readonly Dictionary<int, Action<Result, Intent>> _CallbackDictionary = new Dictionary<int, Action<Result, Intent>>();
public struct RequestCodes
{
public const int CompressVideo = 106;
public const int SelectVideo = 105;
}
static Java.IO.File _destFile;
static Action<string> _callback;
static Activity CurrentActivity
{
get
{
return (Xamarin.Forms.Forms.Context as MainActivity);
}
}
#region Public Methods
public static void StartIntent(Intent intent, int requestCode, Action<Result, Intent> callback)
{
if (_CallbackDictionary.ContainsKey(requestCode))
_CallbackDictionary.Remove(requestCode);
_CallbackDictionary.Add(requestCode, callback);
(Xamarin.Forms.Forms.Context as Activity).StartActivityForResult(intent, requestCode);
}
public static void ActivityResult(int requestCode, Result resultCode, Intent data)
{
if (_callback == null)
return;
if (resultCode == Result.Ok)
{
else if (requestCode == RequestCodes.CompressVideo)
{
}
}
}
public static void CompressVideo(string inputPath, string outputPath, Action<string> callback)
{
Activity activity = new Activity();
_callback = callback;
ProgressDialog progress = new ProgressDialog(Forms.Context);
progress.Indeterminate = true;
progress.SetProgressStyle(ProgressDialogStyle.Spinner);
progress.SetMessage("Compressing Video. Please wait...");
progress.SetCancelable(false);
progress.Show();
Task.Run(() =>
{
var _workingDirectory = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
var sourceMp4 = inputPath;
var destinationPath1 = outputPath;
FFMpeg ffmpeg = new FFMpeg(Android.App.Application.Context, _workingDirectory);
TransposeVideoFilter vfTranspose = new TransposeVideoFilter(TransposeVideoFilter.NINETY_CLOCKWISE);
var filters = new List<VideoFilter>();
filters.Add(vfTranspose);
var sourceClip = new Clip(System.IO.Path.Combine(_workingDirectory, sourceMp4)) { videoFilter = VideoFilter.Build(filters) };
var br = System.Environment.NewLine;
var onComplete = new MyCommand((_) =>
{
_callback(destinationPath1);
progress.Dismiss();
});
var onMessage = new MyCommand((message) =>
{
System.Console.WriteLine(message);
});
var callbacks = new FFMpegCallbacks(onComplete, onMessage);
string[] cmds = new string[] {
"-y",
"-i",
sourceClip.path,
"-strict", "experimental",
"-vcodec", "libx264",
"-preset", "ultrafast",
"-crf","30", "-acodec","aac", "-ar", "44100" ,
"-q:v", "20",
"-vf",sourceClip.videoFilter,
destinationPath1 ,
};
ffmpeg.Execute(cmds, callbacks);
});
}
Step 5- For Android - Create Dependency class MobileFeature in Android which implements IMobileFeature class made in the shared project. Do not forget to include the dependency information while implementing the interface.
[assembly: Dependency(typeof(CompressedVideoDemo.Droid.DS.MobileFeature))]
public class MobileFeature : IMobileFeature
{
static TaskCompletionSource<string> _tcsVideo;
static bool isBusy = false;
static int duration;
static Activity CurrentActivity
{
get
{
return (Forms.Context as MainActivity);
}
}
public static void HandleActivity(int requestCode, Result resultCode, Intent data)
{
isBusy = false;
if (requestCode == IntentHelper.RequestCodes.SelectVideo)
{
if (resultCode == Result.Ok)
{
var path = ImageFilePath.GetPath(Forms.Context, data.Data);
if (!System.IO.File.Exists(path))
{
Toast.MakeText(Forms.Context, "Invalid file source", ToastLength.Long).Show();
_tcsVideo.SetResult(null);
return;
}
}
else
_tcsVideo.SetResult(null);
}
}
public Task<bool> CompressVideo(string path,string path2)
{
var task = new TaskCompletionSource<bool>();
try
{
IntentHelper.CompressVideo( path,path2, (u) =>
{
task.SetResult(true);
});
}
catch (Exception ex)
{
task.SetResult(false);
}
return task.Task;
}
}
}
Step 6- Implement the IMobileFeature interface in iOS as well and include the following code in it with the same method "CompressVideo".
public Task<string> CompressVideo(int duration)
{
var task = new TaskCompletionSource<string>();
try
{
Services.Media.RecordVideo(GetController(),//this method provides recorded video
duration, (data) =>
{
if (data == null)
{
task.SetResult(null);
return;
}
//mediaUrl here is the Url for your video to be compressed.
//here, i m using a video from the recorder
var mediaUrl = data.ValueForKey(new NSString("UIImagePickerControllerMediaURL")) as NSUrl;
var export = new AVAssetExportSession(AVAsset.FromUrl(mediaUrl), AVAssetExportSession.PresetMediumQuality);
string videoFilename = System.IO.Path.Combine(GetVideoDirectoryPath(), GetUniqueFileName(".mp4"));
export.OutputUrl = NSUrl.FromFilename(videoFilename);
export.OutputFileType = AVFileType.Mpeg4;
export.ShouldOptimizeForNetworkUse = true;
export.ExportAsynchronously(() =>
{
if (export.Status == AVAssetExportSessionStatus.Completed)
{
var videoData = NSData.FromUrl(NSUrl.FromString(export.OutputUrl.ToString()));
NSError err = null;
if (videoData.Save(videoFilename, false, out err))
{
task.SetResult(videoFilename);
}
else
{
task.SetResult(null);
}
}
else
tcs.SetResult(null);
});
});
}
catch (Exception ex)
{
task.SetException(ex);
}
return task.Task;
}
Step 7- Here is how to call the Dependency device in the shared project.
_destFile = Helpers.FileHelper.CreateNewVideoPath();
private async void ChooseVideoAndCompressBtn_Clicked(object Sender, EventArgs e)
{
var actions = new string[] { "Open Camera", "Open Gallery" };
var action = await App.Current.MainPage.DisplayActionSheet("Choose Video", "Cancel", null, actions);
if (actions[0].Equals(action))
{
viewModel.VideoPath = await DependencyServices.mobileFeature.RecordVideo();
await App.Current.MainPage.DisplayAlert("Success", "Video is saved to gallery", "ok");
CompressVideo();
}
else if (actions[1].Equals(action))
{
viewModel.VideoPath = await DependencyServices.MobileFeature.SelectVideo();
CompressVideo();
}
}
private async void CompressVideo()
{
if (!File.Exists(viewModel.VideoPath))
return;
var isCompressCmd = await App.Current.MainPage.DisplayAlert("Action", "Do You want to compress this video?", "Ok", "Cancel");
if (isCompressCmd)
{
var isCompressed = await DependencyServices.MobileFeature.CompressVideo(viewModel.VideoPath, _destFile);
if (isCompressed)
{
viewModel.CompressVideoPath = _destFile;
await App.Current.MainPage.DisplayAlert("", "Video compressed successfully", "Ok");
}
else
await App.Current.MainPage.DisplayAlert("", "Video not compressed", "Ok");
}
}
Find the sample for the same here.