A Simple c# Wrapper for ffMpeg

Converting video in .Net is a pain in the butt. The .Net framework, as of 2/8/2010 does not have a nice, simple, conversion process that will take a file in the many different formats out there and drop it into a nicely formatted .flv file for easy slideshowpro (or other flash based package) streaming.

The alternative, that I have most often seen, is using ffMpeg to convert source video into .flv format. Unfortunately, this process can a bit arcane. While there are other libraries out there that wrap up ffMpeg with a nice shiny, albeit complex, bow. I figured there might be some use for a simple c# library that does a couple simple, but very useful processes.

The following class will convert your input file to a .flv file and create a preview image from 1/3 of the way into the movie. By getting the preview image from 1/3 into the movie, we can avoid a completely black preview image that can come from a video that fades in.

Getting Started:

Create a directory in your project that contains the ffMpeg.exe file, this file will be referenced by the class below. This class is also going to use the web.config or app.config to store the location of our ffMpeg.exe file and a couple key settings. To do this, you will need to import system.configuration into your project and include the following line:

	using System.Configuration;

To start with, we have a couple properties that expose the working path that ffMpeg will need while it is processing our video file along with a property that exposes the location of our exe file.

	private string _ffExe;
	public string ffExe
	{
		get
		{
			return _ffExe;
		}
		set
		{
			_ffExe = value;
		}
	}

	private string _WorkingPath;
	public string WorkingPath
	{
		get
		{
			return _WorkingPath;
		}
		set
		{
			_WorkingPath = value;
		}
	}

In our constructors, we will call an initialize method that will attempt to grab our exepath from web.config, assuming that one has not been entered into our second constructor.

Note: In this example, the class is name “Converter”

	public Converter()
	{
		Initialize();
	}
	public Converter(string ffmpegExePath)
	{
		_ffExe = ffmpegExePath;
		Initialize();
	}

Initialization

You will note that the we are testing to see if the ffmpeg.exe file location exists before attempting to pull it from web.config.

	//Make sure we have valid ffMpeg.exe file and working directory to do our dirty work.
	private void Initialize()
	{
		//first make sure we have a value for the ffexe file setting
		if (string.IsNullOrEmpty(_ffExe))
		{
			object o = ConfigurationManager.AppSettings["ffmpeg:ExeLocation"];
			if (o == null)
			{
				throw new Exception("Could not find the location of the ffmpeg exe file.  The path for ffmpeg.exe " +
				"can be passed in via a constructor of the ffmpeg class (this class) or by setting in the app.config or web.config file.  " +
				"in the appsettings section, the correct property name is: ffmpeg:ExeLocation");
			}
			else
			{
				if (string.IsNullOrEmpty(o.ToString()))
				{
					throw new Exception("No value was found in the app setting for ffmpeg:ExeLocation");
				}
				_ffExe = o.ToString();
			}
		}

		//Now see if ffmpeg.exe exists
		string workingpath = GetWorkingFile();
		if (string.IsNullOrEmpty(workingpath))
		{
			//ffmpeg doesn't exist at the location stated.
			throw new Exception("Could not find a copy of ffmpeg.exe");
		}
		_ffExe = workingpath;

		//now see if we have a temporary place to work
		if (string.IsNullOrEmpty(_WorkingPath))
		{
			object o = ConfigurationManager.AppSettings["ffmpeg:WorkingPath"];
			if (o != null)
			{
				_WorkingPath = o.ToString();
			}
			else
			{
				_WorkingPath = string.Empty;
			}
		}
	}

	private string GetWorkingFile()
	{
		//try the stated directory
		if (File.Exists(_ffExe))
		{
			return _ffExe;
		}

		//oops, that didn't work, try the base directory
		if (File.Exists(Path.GetFileName(_ffExe)))
		{
			return Path.GetFileName(_ffExe);
		}

		//well, now we are really unlucky, let's just return null
		return null;
	}

Loading Files Without Creating Locks

These subs might be a bit out of place but are useful as they create our memory resident objects without throwing a lock on the source file. This means that we can run more than one process at once, or delete our file without screwing things up for another process.

	public static System.Drawing.Image LoadImageFromFile(string fileName)
	{
		System.Drawing.Image theImage = null;
		using (FileStream fileStream = new FileStream(fileName, FileMode.Open,
		FileAccess.Read))
		{
			byte[] img;
			img = new byte[fileStream.Length];
			fileStream.Read(img, 0, img.Length);
			fileStream.Close();
			theImage = System.Drawing.Image.FromStream(new MemoryStream(img));
			img = null;
		}
		GC.Collect();
		return theImage;
	}

	public static MemoryStream LoadMemoryStreamFromFile(string fileName)
	{
		MemoryStream ms = null;
		using (FileStream fileStream = new FileStream(fileName, FileMode.Open,
		FileAccess.Read))
		{
			byte[] fil;
			fil = new byte[fileStream.Length];
			fileStream.Read(fil, 0, fil.Length);
			fileStream.Close();
			ms = new MemoryStream(fil);
		}
		GC.Collect();
		return ms;
	}

Running the ffMpeg Process

Now that we have setup our class and have some basic helper subs to get us going, we need to actually have a method that will run the ffMpeg process.


	//Note the private call here and the argument for Parameters.  The private call is
	//being made here because, in this class, we don't really want to have this method
	//called from outside of the class -- this, however flies in the face of allowing the
	//parameters argument (why not just allow out the public call so that a developer can
	//put in the parameters from their own code?  I guess one could do it and it would probably
	//work fine but, for this implementation, I chose to leave it private.
	private string RunProcess(string Parameters)
	{
		//create a process info object so we can run our app
		ProcessStartInfo oInfo = new ProcessStartInfo(this._ffExe, Parameters);
		oInfo.UseShellExecute = false;
		oInfo.CreateNoWindow = true;

		//so we are going to redirect the output and error so that we can parse the return
		oInfo.RedirectStandardOutput = true;
		oInfo.RedirectStandardError = true;

		//Create the output and streamreader to get the output
		string output = null; StreamReader srOutput = null;

		//try the process
		try
		{
			//run the process
			Process proc = System.Diagnostics.Process.Start(oInfo);

			proc.WaitForExit();

			//get the output
			srOutput = proc.StandardError;

			//now put it in a string
			output = srOutput.ReadToEnd();

			proc.Close();
		}
		catch (Exception)
		{
			output = string.Empty;
		}
		finally
		{
			//now, if we succeded, close out the streamreader
			if (srOutput != null)
			{
				srOutput.Close();
				srOutput.Dispose();
			}
		}
		return output;
	}

Now let’s make some functional stuff

Now that we have called the ffMpeg process, we need to get some video info, get a video file, and a preview image. So, with that being said, onto more source code.

Getting the Details

So, we need to now parse the details of our ffMpeg.exe output and read some details about the video file.

	//We are going to take in memory stream for this file to allow for different input options.
	//Unfortunately, this means that we also need the file extension to pass it out to ffMpeg.
	public VideoFile GetVideoInfo(MemoryStream inputFile, string Filename)
	{
		//Create a temporary file for our use in ffMpeg
		string tempfile = Path.Combine(this.WorkingPath, System.Guid.NewGuid().ToString() + Path.GetExtension(Filename));
		FileStream fs = File.Create(tempfile);

		//write the memory stream to a file and close our the stream so it can be used again.
		inputFile.WriteTo(fs);
		fs.Flush();
		fs.Close();
		GC.Collect();

		//Video File is a class you will see further down this post.  It has some basic information about the video
		VideoFile vf = null;
		try
		{
			vf = new VideoFile(tempfile);
		}
		catch (Exception ex)
		{
			throw ex;
		}

		//And, without adieu, a call to our main method for this functionality.
		GetVideoInfo(vf);

		try
		{
			File.Delete(tempfile);
		}
		catch (Exception)
		{

		}

		return vf;
	}

	//This sub is just another overload to allow input of a direct path, we are just
	//using the videofile class to send in.  More on the video file class further down
	//the article.
	public VideoFile GetVideoInfo(string inputPath)
	{
		VideoFile vf = null;
		try
		{
			vf = new VideoFile(inputPath);
		}
		catch (Exception ex)
		{
			throw ex;
		}
		GetVideoInfo(vf);
		return vf;
	}

	//And now the important code for the GetVideoInfo
	public void GetVideoInfo(VideoFile input)
	{
		//set up the parameters for video info -- these will be passed into ffMpeg.exe
		string Params = string.Format("-i {0}", input.Path);
		string output = RunProcess(Params);
		input.RawInfo = output;

		//Use a regular expression to get the different properties from the video parsed out.
		Regex re = new Regex("[D|d]uration:.((\\d|:|\\.)*)");
		Match m = re.Match(input.RawInfo);

		if (m.Success)
		{
			string duration = m.Groups[1].Value;
			string[] timepieces = duration.Split(new char[] { ':', '.' });
			if (timepieces.Length == 4)
			{
				input.Duration = new TimeSpan(0, Convert.ToInt16(timepieces[0]), Convert.ToInt16(timepieces[1]), Convert.ToInt16(timepieces[2]), Convert.ToInt16(timepieces[3]));
			}
		}

		//get audio bit rate
		re = new Regex("[B|b]itrate:.((\\d|:)*)");
		m = re.Match(input.RawInfo);
		double kb = 0.0;
		if (m.Success)
		{
			Double.TryParse(m.Groups[1].Value, out kb);
		}
		input.BitRate = kb;

		//get the audio format
		re = new Regex("[A|a]udio:.*");
		m = re.Match(input.RawInfo);
		if (m.Success)
		{
			input.AudioFormat = m.Value;
		}

		//get the video format
		re = new Regex("[V|v]ideo:.*");
		m = re.Match(input.RawInfo);
		if (m.Success)
		{
			input.VideoFormat = m.Value;
		}

		//get the video format
		re = new Regex("(\\d{2,3})x(\\d{2,3})");
		m = re.Match(input.RawInfo);
		if (m.Success)
		{
			int width = 0; int height = 0;
			int.TryParse(m.Groups[1].Value, out width);
			int.TryParse(m.Groups[2].Value, out height);
			input.Width = width;
			input.Height = height;
		}
		input.infoGathered = true;
	}

Finally, do the conversion

So, here is the conversion meat and preview imaging.

	//Note the ouputpackage object output.  The output package class is detailed below.
	//this overload does much the same as the one above does, in that, it offers the ability
	//to input a memory stream vs. a filename or our videofile input class.
	public OutputPackage ConvertToFLV(MemoryStream inputFile, string Filename)
	{
		string tempfile = Path.Combine(this.WorkingPath, System.Guid.NewGuid().ToString() + Path.GetExtension(Filename));
		FileStream fs = File.Create(tempfile);
		inputFile.WriteTo(fs);
		fs.Flush();
		fs.Close();
		GC.Collect();

		VideoFile vf = null;
		try
		{
			vf = new VideoFile(tempfile);
		}
		catch (Exception ex)
		{
			throw ex;
		}

		OutputPackage oo = ConvertToFLV(vf);

		try{
			File.Delete(tempfile);
		} catch(Exception) {

		}

		return oo;
	}
	public OutputPackage ConvertToFLV(string inputPath)
	{
		VideoFile vf = null;
		try
		{
			vf = new VideoFile(inputPath);
		}
		catch (Exception ex)
		{
			throw ex;
		}

		OutputPackage oo = ConvertToFLV(vf);
		return oo;
	}

	//The actually important code, rather than an overload.
	public OutputPackage ConvertToFLV(VideoFile input)
	{
		//check to see if we have already gathered our info so we know
		//where to get our preview image from.
		if (!input.infoGathered)
		{
			GetVideoInfo(input);
		}

		//Create our output object
		OutputPackage ou = new OutputPackage();

		//set up the parameters for getting a previewimage
		string filename = System.Guid.NewGuid().ToString() + ".jpg";
		int secs;

		//divide the duration in 3 to get a preview image in the middle of the clip
		//instead of a black image from the beginning.
		secs = (int)Math.Round(TimeSpan.FromTicks(input.Duration.Ticks / 3).TotalSeconds, 0);

		string finalpath = Path.Combine(this.WorkingPath, filename);

		//These are the parameters for setting up a preview image that must be passed to ffmpeg.
		//Note that we are asking for a jpeg image at our specified seconds.
		string Params = string.Format("-i {0} {1} -vcodec mjpeg -ss {2} -vframes 1 -an -f rawvideo", input.Path, finalpath, secs);
		string output = RunProcess(Params);

		ou.RawOutput = output;

		//Ok, so hopefully we now have a preview file.  If the file
		//did not get created properly, try again at the first frame.
		if (File.Exists(finalpath))
		{
			//load that file into our output package and attempt to delete the file
			//since we no longer need it.
			ou.PreviewImage = LoadImageFromFile(finalpath);
			try
			{
				File.Delete(finalpath);
			}
			catch (Exception) { }
		} else { //try running again at frame 1 to get something
			Params = string.Format("-i {0} {1} -vcodec mjpeg -ss {2} -vframes 1 -an -f rawvideo", input.Path, finalpath, 1);
			output = RunProcess(Params);

			ou.RawOutput = output;

			if (File.Exists(finalpath)) {
				ou.PreviewImage = LoadImageFromFile(finalpath);
				try
				{
					File.Delete(finalpath);
				}
				catch (Exception) { }
			}
		}

		finalpath = Path.Combine(this.WorkingPath, filename);
		filename = System.Guid.NewGuid().ToString() + ".flv";

		//Now we are going to actually create the converted file.  Note that we are asking for
		//a video at 22khz 64bit.  This can be changed by a couple quick alterations to this line,
		//or by extending out this class to offer multiple different conversions.
		Params = string.Format("-i {0} -y -ar 22050 -ab 64 -f flv {1}", input.Path, finalpath);
		output = RunProcess(Params);

		//Check to see if our conversion file exists and then load the converted
		//file into our output package.  If the file does exist and we are able to
		//load it into our output package, we can delete the work file.
		if (File.Exists(finalpath))
		{
			ou.VideoStream = LoadMemoryStreamFromFile(finalpath);
			try
			{
				File.Delete(finalpath);
			}
			catch (Exception) { }
		}
		return ou;
	}

Supplementary Classes

Throughout this article, you will have noticed the use of “VideoFile” and “OutputPackage”. These classes are just informational classes that provide simple ways of accessing everything.
Here they are:

	public class VideoFile
	{
		#region Properties
		private string _Path;
		public string Path
		{
			get
			{
				return _Path;
			}
			set
			{
				_Path = value;
			}
		}

		public TimeSpan Duration { get; set; }
		public double BitRate { get; set; }
		public string AudioFormat { get; set; }
		public string VideoFormat { get; set; }
		public int Height { get; set; }
		public int Width { get; set; }
		public string RawInfo { get; set; }
		public bool infoGathered {get; set;}
		#endregion

		#region Constructors
		public VideoFile(string path)
		{
			_Path = path;
			Initialize();
		}
		#endregion

		#region Initialization
		private void Initialize()
		{
			this.infoGathered = false;
			//first make sure we have a value for the video file setting
			if (string.IsNullOrEmpty(_Path))
			{
				throw new Exception("Could not find the location of the video file");
			}

			//Now see if the video file exists
			if (!File.Exists(_Path))
			{
				throw new Exception("The video file " + _Path + " does not exist.");
			}
		}
		#endregion
	}

	public class OutputPackage
	{
		public MemoryStream VideoStream { get; set; }
		public System.Drawing.Image PreviewImage { get; set; }
		public string RawOutput { get; set; }
		public bool Success { get; set; }
	}

Full Code for the class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;
using System.Diagnostics;
using System.Configuration;
using System.Text.RegularExpressions;

namespace ffMpeg
{
	public class Converter
	{
		#region Properties
		private string _ffExe;
		public string ffExe
		{
			get
			{
				return _ffExe;
			}
			set
			{
				_ffExe = value;
			}
		}

		private string _WorkingPath;
		public string WorkingPath
		{
			get
			{
				return _WorkingPath;
			}
			set
			{
				_WorkingPath = value;
			}
		}

		#endregion

		#region Constructors
		public Converter()
		{
			Initialize();
		}
		public Converter(string ffmpegExePath)
		{
			_ffExe = ffmpegExePath;
			Initialize();
		}
		#endregion

		#region Initialization
		private void Initialize()
		{
			//first make sure we have a value for the ffexe file setting
			if (string.IsNullOrEmpty(_ffExe))
			{
				object o = ConfigurationManager.AppSettings["ffmpeg:ExeLocation"];
				if (o == null)
				{
					throw new Exception("Could not find the location of the ffmpeg exe file.  The path for ffmpeg.exe " +
					"can be passed in via a constructor of the ffmpeg class (this class) or by setting in the app.config or web.config file.  " +
					"in the appsettings section, the correct property name is: ffmpeg:ExeLocation");
				}
				else
				{
					if (string.IsNullOrEmpty(o.ToString()))
					{
						throw new Exception("No value was found in the app setting for ffmpeg:ExeLocation");
					}
					_ffExe = o.ToString();
				}
			}

			//Now see if ffmpeg.exe exists
			string workingpath = GetWorkingFile();
			if (string.IsNullOrEmpty(workingpath))
			{
				//ffmpeg doesn't exist at the location stated.
				throw new Exception("Could not find a copy of ffmpeg.exe");
			}
			_ffExe = workingpath;

			//now see if we have a temporary place to work
			if (string.IsNullOrEmpty(_WorkingPath))
			{
				object o = ConfigurationManager.AppSettings["ffmpeg:WorkingPath"];
				if (o != null)
				{
					_WorkingPath = o.ToString();
				}
				else
				{
					_WorkingPath = string.Empty;
				}
			}
		}

		private string GetWorkingFile()
		{
			//try the stated directory
			if (File.Exists(_ffExe))
			{
				return _ffExe;
			}

			//oops, that didn't work, try the base directory
			if (File.Exists(Path.GetFileName(_ffExe)))
			{
				return Path.GetFileName(_ffExe);
			}

			//well, now we are really unlucky, let's just return null
			return null;
		}
		#endregion

		#region Get the File without creating a file lock
		public static System.Drawing.Image LoadImageFromFile(string fileName)
		{
			System.Drawing.Image theImage = null;
			using (FileStream fileStream = new FileStream(fileName, FileMode.Open,
			FileAccess.Read))
			{
				byte[] img;
				img = new byte[fileStream.Length];
				fileStream.Read(img, 0, img.Length);
				fileStream.Close();
				theImage = System.Drawing.Image.FromStream(new MemoryStream(img));
				img = null;
			}
			GC.Collect();
			return theImage;
		}

		public static MemoryStream LoadMemoryStreamFromFile(string fileName)
		{
			MemoryStream ms = null;
			using (FileStream fileStream = new FileStream(fileName, FileMode.Open,
			FileAccess.Read))
			{
				byte[] fil;
				fil = new byte[fileStream.Length];
				fileStream.Read(fil, 0, fil.Length);
				fileStream.Close();
				ms = new MemoryStream(fil);
			}
			GC.Collect();
			return ms;
		}
		#endregion

		#region Run the process
		private string RunProcess(string Parameters)
		{
			//create a process info
			ProcessStartInfo oInfo = new ProcessStartInfo(this._ffExe, Parameters);
			oInfo.UseShellExecute = false;
			oInfo.CreateNoWindow = true;
			oInfo.RedirectStandardOutput = true;
			oInfo.RedirectStandardError = true;

			//Create the output and streamreader to get the output
			string output = null; StreamReader srOutput = null;

			//try the process
			try
			{
				//run the process
				Process proc = System.Diagnostics.Process.Start(oInfo);

				proc.WaitForExit();

				//get the output
				srOutput = proc.StandardError;

				//now put it in a string
				output = srOutput.ReadToEnd();

				proc.Close();
			}
			catch (Exception)
			{
				output = string.Empty;
			}
			finally
			{
				//now, if we succeded, close out the streamreader
				if (srOutput != null)
				{
					srOutput.Close();
					srOutput.Dispose();
				}
			}
			return output;
		}
		#endregion

		#region GetVideoInfo
		public VideoFile GetVideoInfo(MemoryStream inputFile, string Filename)
		{
			string tempfile = Path.Combine(this.WorkingPath, System.Guid.NewGuid().ToString() + Path.GetExtension(Filename));
			FileStream fs = File.Create(tempfile);
			inputFile.WriteTo(fs);
			fs.Flush();
			fs.Close();
			GC.Collect();

			VideoFile vf = null;
			try
			{
				vf = new VideoFile(tempfile);
			}
			catch (Exception ex)
			{
				throw ex;
			}

			GetVideoInfo(vf);

			try
			{
				File.Delete(tempfile);
			}
			catch (Exception)
			{

			}

			return vf;
		}
		public VideoFile GetVideoInfo(string inputPath)
		{
			VideoFile vf = null;
			try
			{
				vf = new VideoFile(inputPath);
			}
			catch (Exception ex)
			{
				throw ex;
			}
			GetVideoInfo(vf);
			return vf;
		}
		public void GetVideoInfo(VideoFile input)
		{
			//set up the parameters for video info
			string Params = string.Format("-i {0}", input.Path);
			string output = RunProcess(Params);
			input.RawInfo = output;

			//get duration
			Regex re = new Regex("[D|d]uration:.((\\d|:|\\.)*)");
			Match m = re.Match(input.RawInfo);

			if (m.Success)
			{
				string duration = m.Groups[1].Value;
				string[] timepieces = duration.Split(new char[] { ':', '.' });
				if (timepieces.Length == 4)
				{
					input.Duration = new TimeSpan(0, Convert.ToInt16(timepieces[0]), Convert.ToInt16(timepieces[1]), Convert.ToInt16(timepieces[2]), Convert.ToInt16(timepieces[3]));
				}
			}

			//get audio bit rate
			re = new Regex("[B|b]itrate:.((\\d|:)*)");
			m = re.Match(input.RawInfo);
			double kb = 0.0;
			if (m.Success)
			{
				Double.TryParse(m.Groups[1].Value, out kb);
			}
			input.BitRate = kb;

			//get the audio format
			re = new Regex("[A|a]udio:.*");
			m = re.Match(input.RawInfo);
			if (m.Success)
			{
				input.AudioFormat = m.Value;
			}

			//get the video format
			re = new Regex("[V|v]ideo:.*");
			m = re.Match(input.RawInfo);
			if (m.Success)
			{
				input.VideoFormat = m.Value;
			}

			//get the video format
			re = new Regex("(\\d{2,3})x(\\d{2,3})");
			m = re.Match(input.RawInfo);
			if (m.Success)
			{
				int width = 0; int height = 0;
				int.TryParse(m.Groups[1].Value, out width);
				int.TryParse(m.Groups[2].Value, out height);
				input.Width = width;
				input.Height = height;
			}
			input.infoGathered = true;
		}
		#endregion

		#region Convert to FLV
		public OutputPackage ConvertToFLV(MemoryStream inputFile, string Filename)
		{
			string tempfile = Path.Combine(this.WorkingPath, System.Guid.NewGuid().ToString() + Path.GetExtension(Filename));
			FileStream fs = File.Create(tempfile);
			inputFile.WriteTo(fs);
			fs.Flush();
			fs.Close();
			GC.Collect();

			VideoFile vf = null;
			try
			{
				vf = new VideoFile(tempfile);
			}
			catch (Exception ex)
			{
				throw ex;
			}

			OutputPackage oo = ConvertToFLV(vf);

			try{
				File.Delete(tempfile);
			} catch(Exception) {

			}

			return oo;
		}
		public OutputPackage ConvertToFLV(string inputPath)
		{
			VideoFile vf = null;
			try
			{
				vf = new VideoFile(inputPath);
			}
			catch (Exception ex)
			{
				throw ex;
			}

			OutputPackage oo = ConvertToFLV(vf);
			return oo;
		}
		public OutputPackage ConvertToFLV(VideoFile input)
		{
			if (!input.infoGathered)
			{
				GetVideoInfo(input);
			}
			OutputPackage ou = new OutputPackage();

			//set up the parameters for getting a previewimage
			string filename = System.Guid.NewGuid().ToString() + ".jpg";
			int secs;

			//divide the duration in 3 to get a preview image in the middle of the clip
			//instead of a black image from the beginning.
			secs = (int)Math.Round(TimeSpan.FromTicks(input.Duration.Ticks / 3).TotalSeconds, 0);

			string finalpath = Path.Combine(this.WorkingPath, filename);
			string Params = string.Format("-i {0} {1} -vcodec mjpeg -ss {2} -vframes 1 -an -f rawvideo", input.Path, finalpath, secs);
			string output = RunProcess(Params);

			ou.RawOutput = output;

			if (File.Exists(finalpath))
			{
				ou.PreviewImage = LoadImageFromFile(finalpath);
				try
				{
					File.Delete(finalpath);
				}
				catch (Exception) { }
			} else { //try running again at frame 1 to get something
				Params = string.Format("-i {0} {1} -vcodec mjpeg -ss {2} -vframes 1 -an -f rawvideo", input.Path, finalpath, 1);
				output = RunProcess(Params);

				ou.RawOutput = output;

				if (File.Exists(finalpath)) {
					ou.PreviewImage = LoadImageFromFile(finalpath);
					try
					{
						File.Delete(finalpath);
					}
					catch (Exception) { }
				}
			}

			finalpath = Path.Combine(this.WorkingPath, filename);
			filename = System.Guid.NewGuid().ToString() + ".flv";
			Params = string.Format("-i {0} -y -ar 22050 -ab 64 -f flv {1}", input.Path, finalpath);
			output = RunProcess(Params);

			if (File.Exists(finalpath))
			{
				ou.VideoStream = LoadMemoryStreamFromFile(finalpath);
				try
				{
					File.Delete(finalpath);
				}
				catch (Exception) { }
			}
			return ou;
		}
		#endregion
	}

	public class VideoFile
	{
		#region Properties
		private string _Path;
		public string Path
		{
			get
			{
				return _Path;
			}
			set
			{
				_Path = value;
			}
		}

		public TimeSpan Duration { get; set; }
		public double BitRate { get; set; }
		public string AudioFormat { get; set; }
		public string VideoFormat { get; set; }
		public int Height { get; set; }
		public int Width { get; set; }
		public string RawInfo { get; set; }
		public bool infoGathered {get; set;}
		#endregion

		#region Constructors
		public VideoFile(string path)
		{
			_Path = path;
			Initialize();
		}
		#endregion

		#region Initialization
		private void Initialize()
		{
			this.infoGathered = false;
			//first make sure we have a value for the video file setting
			if (string.IsNullOrEmpty(_Path))
			{
				throw new Exception("Could not find the location of the video file");
			}

			//Now see if the video file exists
			if (!File.Exists(_Path))
			{
				throw new Exception("The video file " + _Path + " does not exist.");
			}
		}
		#endregion
	}

	public class OutputPackage
	{
		public MemoryStream VideoStream { get; set; }
		public System.Drawing.Image PreviewImage { get; set; }
		public string RawOutput { get; set; }
		public bool Success { get; set; }
	}
}

77 responses to this post.

  1. Posted by ucsharp on February 9, 2010 at 10:28 am

    Good work!

    Reply

  2. Posted by csharper on February 12, 2010 at 8:41 pm

    Wow! This is exactly what I need. Thanks for the post!

    Reply

  3. this is great. saved me lots of time

    Reply

  4. Posted by Tanveer on February 23, 2010 at 7:41 am

    Good Work! can you please give and Example of Using the above code

    Reply

    • Sure, what would you like to see?

      Reply

      • Posted by Rony on March 18, 2010 at 1:46 pm

        Hi, thanks for the code !

        If you could put a simple usage example it could be great!
        (Opening some local video file and using your class to save it back as
        FLV on the HD)

        Thanks.

      • Posted by Rony on March 18, 2010 at 2:19 pm

        Ok, here is something I did –

        _converter = new Converter(“./ffmpeg/ffmpeg.exe”);
        OutputPackage oo = _converter.ConvertToFLV(“./video2.mpg”);

        FileStream outStream = File.OpenWrite(“./newVideo.flv”);
        oo.VideoStream.WriteTo(outStream);
        outStream.Flush();
        outStream.Close();
        oo.PreviewImage.Save(“./thumbnail.jpg”);

        Thanks again.

      • Posted by Alex on October 30, 2010 at 3:51 pm

        this is excellent code. Can you please provide sample code on how to use this class? I have everything set but i’m having a problem getting to return the images. Thanks in advance.

  5. Good work. I need some more information regarding image viewer. I tried it in “C” language, but it did not work properly.. Can any one post the coding for that?

    Reply

  6. Posted by Milky Joe on March 2, 2010 at 2:31 pm

    Nice!

    Is there a download available for this, or have I simply overlooked the link?

    Reply

    • Yeah, the code is all at the bottom of the article, if you copy it out and drip it into a cs file, along with a copy of ffmpeg you should be good to go. Let me know if there is any additional assistance needed.

      Reply

  7. Good, so i not config web.config. Help me please.

    Reply

    • You can hard code the values you want or, in many cases, use the app.config file which is going to be output — in an exe file — as MyFile.exe.config.

      Reply

  8. This looks awesome, however I’m stuck with how to setup the config file.
    I’m not terribly good with c# and dont understand how to format my config file to refer to your AppSettings i.e “ffmpeg:ExeLocation”.

    Can you post an example?

    Reply

    • Note: I you will need to throw open and close carats on the lines below. (They should look like html tags when you are done.

      configuration
      appSettings
      add key="MySite1.MyDomain.com" value="1"/
      add key="www.MySite1.MyDomain.com" value="1"/
      add key="MySite2.MyDomain.com" value="2"/
      add key="localhost" value="1"/
      /appSettings
      /configuration

      So, you will notice the format of the key above is just an xml node inside an appSettings node. If you look at the web.config file of your project, or the app.config file of your class or winforms app you will see an tag. Of course, if there are already settings in the appSettings tag you can just throw your new node inside but, otherwise, you can just open up your appSettings tag by changing the single line to and then putting the line inside of the two tags, as shown above.

      Let me know if that helps, I can probably throw up a blog, or another reply, about getting this working. You can also, just create a

      const string ffmpegloc = "c:\inetpub\wwwroot\MySite\ffmpeg\";

      line inside your project and then, where the app setting gets called, just change to the above variable.

      Reply

      • If you could pop a blog up explainging how to get this working it would be ace!

        I’ve compiled the class and am attempting to run it from VB.net. FFMpeg launches attempts to create a JPG and then just quits – Can’t quite figure out why? What version of FFMpeg should I be using? I can mine from (http://ffmpeg.arrozcru.org/autobuilds/).

        Thanks!

  9. Is it possible to create a FFMpeg player using this wrapper?

    Reply

  10. Posted by Revanth on April 5, 2010 at 7:25 am

    Hi,

    Thanks for great post. It is very helpful for me.

    I am able to convert video files to flv format. But thumbnail is not generating. It is giving Object Reference Error. at line

    oo.PreviewImage.Save(“thumbnail.jpg”);

    I have tried using Server.MapPath also. but no use.

    Could you please help me in this situation.

    Thanks in Advance
    Revanth Arji

    Reply

  11. Posted by Jason on April 13, 2010 at 10:48 pm

    The code is great, but had to be modified so that it would still work when the paths contained spaces

    Reply

  12. Posted by Sejer on April 14, 2010 at 6:18 am

    Great code,

    But a small tutorial how to get i to work, would be great.

    Reply

  13. Posted by Eddie on April 14, 2010 at 3:32 pm

    Hi, this is excellent article!

    However, in a non-dedicated (shared) web server environment, for security reason, the web hosting company won’t allow launching external process. Hence, “System.Diagnostics.Process.Start( )” throws “Access Denied” exception.

    Thoughts on possible workaround?

    Thx.

    Reply

  14. Posted by Eddie on April 15, 2010 at 12:09 am

    Hi, great article – thx 4 sharing.

    One thing I notice is that if you are hosting your site in a non-dedicated web server (shared), the hosting company usually doesn’t allow code to launch external process, for security reason.

    Hence, System.Diagnostics.Process.Start( ) will throw “Access Denied” exception.

    Any thoughts/inputs on this?

    Reply

  15. Posted by Dave on April 20, 2010 at 5:37 pm

    I’m getting “Object reference not set to an instance of an object.” on the line:

    oo.VideoStream.WriteTo(outStream);

    (the oo.VideoStream also has the same error, when I ToString() it)

    Not sure if this is a noob error or what. Maybe it’s the way the File.OpenWrite is behaving on my server?

    Thanks for the code post.

    Reply

  16. Hey…Thanks a lot dude. This information will make my work more easier than ever. Please do keep on posting like this..

    Reply

  17. Posted by jklanka on May 5, 2010 at 10:10 am

    Hi,

    This is a great article. Thanks a lot.

    instead of ConvertToFLV, I need to use ConvertToTS

    How can I do that. Please help to convert any file into .TS format.

    Thanks

    Reply

  18. I think you may have transposed some lines.

    In the overload that takes a file path you need to ensure you set the filename to *.flv before creating the params string. Otherwise it gets converted into a file with a *.jpg extension.

    Should be more like

    filename = System.Guid.NewGuid().ToString() + “.flv”;

    finalpath = Path.Combine(this.WorkingPath, filename);

    Params = string.Format(“-i {0} -y -ar 22050 -ab 64 -f flv {1}”, input.Path, finalpath);
    output = RunProcess(Params);

    Reply

  19. Very nice.
    If I may, here are my suggestions.

    -It would be very nice if ability to set audio and video bitrate in converted video as well as output size such as cif,qcif,vga etc

    -More conversation choices like convertotmpeg4, converto3gp or converttowmv

    -One of the most desired feature would be getting thumbnail from certain time in the source video.

    Reply

  20. Thanks just install VS2010 now :)
    Excited!!!

    Reply

  21. Posted by Mikey on May 26, 2010 at 11:19 am

    This is great work indeed, however…

    FFMPEG stops processing 1/2 through the video and just sits in the task manager with 0% processor time – Anyone got any ideas? It almost seems to me to be some kind of buffering issue?

    Ideas anyone?

    Reply

    • Posted by StNickolay on August 2, 2010 at 9:07 am

      Also have such problem, and also process fuzz on generate preview .jpg….
      And else one question, is anyone posibility to monitor converting process, to implement progress bar?

      Reply

  22. My image is not generated .will you please give me example. how to create image.

    Reply

  23. Posted by Kishoj on June 21, 2010 at 6:36 am

    Could you just provide me the code for this example???

    Reply

  24. Posted by matt on August 6, 2010 at 12:42 am

    While trying to get this to work, I also found that ffmpeg stops randomly throughout encoding. If you close the program down ffmpeg will continue behind the scenes and finish the job. I’ve tracked it down to oInfo.RedirectStandardError = true; in the “RunProcess” function. Turning this to false will allow ffmpeg to finish it’s job but then there is no output generated at all, so I can’t get any of the video information back from ffmpeg. I’ve tried all sorts of solutions provided on msdn but nothing seams to work.

    Any input would be appreciated!

    Reply

    • oInfo.RedirectStandardOutput = true;
      oInfo.RedirectStandardError = true;

      //Create the output and streamreader to get the output
      string output = null; StreamReader srOutput = null;

      //try the process
      try
      {
      //run the process
      //Process proc = System.Diagnostics.Process.Start(oInfo); //*************change this line following
      Process proc = new Process();
      proc.StartInfo = oInfo;
      proc.Start();
      ……………….
      ……..

      Reply

  25. Posted by Jason on September 18, 2010 at 9:02 pm

    As an FYI:

    oInfo.RedirectStandardError = true;

    in your code is what’s causing it to hang up. It does some work, but is never returning, and ends up orphaning the conhost.exe and the app appears to just hang.

    If you comment *out* that line, everything works fine.

    Reply

  26. Posted by Jason on September 18, 2010 at 9:18 pm

    Also, for those of you wishing to use this with other formats, look to this line:

    Params = string.Format(“-i {0} -y -ar 22050 -ab 64 -f flv {1}”, input.Path, finalpath);

    The string contains everything you want to know about it. By default, FFmpeg is smart-ish where you COULD do something as simple as :

    -i {0} {1} and name your output file with the right file extension (like .mp4 or .webm or .ogv).

    But — that’s the string you want to tweak. Head over to the FFmpeg DOCs page and read up on all its command switches.

    Reply

  27. Posted by jason on September 18, 2010 at 11:15 pm

    I’ve also noticed that it’s not pulling from the standard output, all my video info is coming back empty. Don’t know if it’s something with FFmpeg or not – because my attempts (before playing with your code) in trying to capture the std output came up empty as well.

    Reply

    • The stdout is use for streaming out data.
      If you wan’t to get the ffmpeg output you’ve to use stderr as ffmpeg uses this to put out progress information

      Reply

  28. Follow teh follwoing steps to do it vorrectly

    1.In Web.config file copy the following code.

    after copying the above mentioned class then do as follows

    string path = Server.MapPath(“Video”);
    path = path + “\\SANAA.mpg”;
    VideoFile vf = new VideoFile(path);
    byte[] video = new byte[path.Length];
    string path1 = Server.MapPath(“FFMPEG”);
    path1 = path1 + “\\ffmpeg.exe”;
    string path2 = Server.MapPath(“CreatedImages”);
    Converter fileconvertor = new Converter(path1);
    OutputPackage oo = fileconvertor.ConvertToFLV(path);
    FileStream outStream = File.OpenWrite(path2 + “\\newVideo.flv”);
    oo.VideoStream.WriteTo(outStream);
    outStream.Flush();
    outStream.Close();
    oo.PreviewImage.Save(path2 + “\\thumbnail.jpg”);

    Reply

  29. Posted by Moatez on October 5, 2010 at 12:33 am

    Thanks for the excellent work !

    From asp.net web application, I need to merge 2 files an flv (video) and an mp3 file (audio) to a single FLV output file. How can I do this ? any example would be appreciated…

    Thanks

    Reply

  30. Posted by yarik on October 15, 2010 at 4:10 pm

    How I configure IIS 7 to execute ffmpeg.exe from website?

    Reply

  31. Posted by Ace on October 29, 2010 at 9:04 pm

    Can you please provide usage code for this article?

    Thanks

    Reply

  32. Posted by Alex on October 30, 2010 at 2:57 pm

    Can someone please provide sample code on how to use this class? I have everything set but i’m having a problem getting to return the images. Thanks in advance.

    Reply

  33. Posted by Alex on October 30, 2010 at 3:04 pm

    by the way this is excellent code.

    Reply

  34. Hi,
    can you post me some implementation of the above code, i tried
    converter = new Converter(“./ffmpeg/ffmpeg.exe”);
    OutputPackage oo = _converter.ConvertToFLV(“./video2.mpg”);

    FileStream outStream = File.OpenWrite(“./newVideo.flv”);
    oo.VideoStream.WriteTo(outStream);
    outStream.Flush();
    outStream.Close();
    oo.PreviewImage.Save(“./thumbnail.jpg”);

    which i got above but its not working

    Reply

  35. Posted by Dina on December 2, 2010 at 7:55 am

    hi , great work
    but my host doesn’t support executable files
    and i search alot for ffmpeg dll with documentation but i couldn’t
    find anything useful
    i wonder if you have any ideas to help me?
    thanx

    Reply

  36. Posted by Plaguebreath on December 14, 2010 at 7:47 am

    Hello, your code it’s nice and polite but I have to point you to a problem about the launch on ffmpeg with the process as you doing.
    Just for example let’s say that we have a directory or a filename with some japanese or german char inside it, try execute it and you will see that the conversion fail, seems that parameters string never passed in the format ffmpeg like most (UTF-8) and just as output you will get that your path/filename with non recognized(?) caracter just fail the operation.

    Reply

  37. Posted by cedx on December 14, 2010 at 11:07 pm

    Very good job
    I was looking for something like that long time ago
    I am new in C# but I can understand your code..
    anyway thanks a lot!! I’ll keep an eye on your blog :D

    Reply

  38. Posted by Paul on January 20, 2011 at 8:57 pm

    Hi nice code, could you show me an example of how to join .flv files together. Here is an example i cannot get it to work.
    http://ffmpeg.org/faq.html#SEC27

    Thanks in advance!

    Reply

  39. Posted by Andy on February 10, 2011 at 9:54 pm

    I noticed that when you’re trying to process videos that are over 8-9 MB, the program just hangs. I have figured out what the problem was. In your RunProcess(string Parameters) function, you have proc.WaitForExit() before the StandardError.ReadToEnd which causes a deadlock.

    Reference from MSDN:
    A deadlock condition can result if the parent process calls WaitForExit before StandardOutput.ReadToEnd and the child process writes enough text to fill the redirected stream. The parent process would wait indefinitely for the child process to exit. The child process would wait indefinitely for the parent to read from the full StandardOutput stream.

    So if you just put proc.WaitForExit() after output = srOutput.ReadToEnd() , that should fix the deadlock issue.

    Reply

  40. plz tel me how to use this code in my application? and is this live encoding when i m capturing from cam? or i have to pass path of video file?

    Reply

  41. Posted by jay on March 3, 2011 at 5:19 pm

    Hello guys,
    i need help to achieve functionality to add images in video at footer.
    how can i do that ?
    please help me i have search and tried with FFMPEG but not getting success.

    Reply

  42. Posted by swati on April 4, 2011 at 10:11 am

    Hi,

    I am also working as ur code does.
    really good work but if i am creating an application for an organization then i believe i should not use ffmpeg.exe along with my project’s exe file.there will be two exe’s.

    Moreover i am placing the exe at the applicatiom’s startup path ie inside DEBUG folder inside BIN.
    If i distribute my application’s exe then i have to distribute ffmpeg.exe too.
    Am i right??

    Could you suggest me what should i do to avoid such problems

    Reply

  43. [...] der ffmpeg.exe die VideoInformationen auslesen kann oder ein Video konvertieren kann. Code wurde hier [...]

    Reply

  44. Posted by Peter on April 21, 2011 at 9:18 pm

    Blank thumbnails when commenting out [oInfo.RedirectStandardError = true;]

    When this line is not commented out, the program sometimes hang’s, but the thumbnail is created. When I comment it out, it gives back a black thumbnail?

    Any idea’s?

    Reply

  45. Posted by Jimmus on April 26, 2011 at 4:30 pm

    Hi.
    How Can I start decoding file?
    I have something like that:
    OpenFileDialog otworz = new OpenFileDialog();
    if (otworz.ShowDialog() == DialogResult.OK)
    {
    filename = System.IO.Path.GetFileName(otworz.FileName);
    path = System.IO.Path.GetDirectoryName(otworz.FileName);
    path += filename;
    }
    GetVideoInfo(LoadMemoryStreamFromFile(path), path);

    But there I have a null video file all time!

    Reply

  46. Posted by sagnik mukherjee on May 5, 2011 at 1:29 pm

    A Suggestion for All :

    In case of Getting Video Thumbnail, we can change -f rawvideo (-f Force format option) into -f image2. It reduces the size of thumbnail file. Normally for a jpeg file (400×300) of size 800kb comes into 6kb.

    Example :

    ffmpeg.exe -i “Input” “Output.jpg” -vcodec mjpeg -ss {2} -vframes 1 -an -f image2″;

    Reply

  47. [...] I get started I’ll mention Jason Jano’s excellent article about a simple FFmpeg C# wrapper that inspired me creating my [...]

    Reply

  48. Unfortunately I ran into some random bug where the program never exited.
    The WaitToExit() never catched the exit, even if the process was done….

    WRONG CODE (Copy past from above)
    try
    23 {
    24 //run the process
    25 Process proc = System.Diagnostics.Process.Start(oInfo);
    26
    27 proc.WaitForExit();
    28
    29 //get the output
    30 srOutput = proc.StandardError;
    31
    32 //now put it in a string
    33 output = srOutput.ReadToEnd();
    34
    35 proc.Close();
    36 }

    not sure if this is a MS bug, but anyway, here is the fix for this random issue (which only happened on my system when I looped over large amount of files)

    FIXED CODE
    try
    {
    //run the process
    Process proc = new Process {StartInfo = oInfo};
    proc.Start();
    output = proc.StandardError.ReadToEnd();
    proc.WaitForExit();

    proc.Close();
    }

    Thing is, randomly, the WaitForExit(), even if the process is done, sometimes keeps waiting till you catched the output.. so the ReadToEnd() has to be BEFORE you waitForExit().

    The srOutput i simply removed.. don’t need that

    Reply

  49. oInfo.RedirectStandardError = true;

    Some users think this line is causing the error. It is not. This line of code works fine.
    But the point is, you must catch your output before the WaitToExit() and not afterwards! Once the process has exited, of course you can only catch an empty string.

    Reply

  50. hi Friend

    it doesn’t supports the 3gp codec. 3gp is a nokia codec used in their mobile phones.

    Any idea about this…………………….

    Waiting For U’r Replay

    Reply

  51. Hello Jason and thanks for your great tool.

    I was wondering if I can create a function that will grab the conversion progress asynchronously and so will indicate user with current progress percentage?

    Thanks a lot!

    Reply

  52. Posted by Steven on September 27, 2011 at 4:20 pm

    Thank you very much for this library!

    Reply

  53. Posted by Bkr on December 16, 2011 at 9:26 pm

    Hello,

    Server is installed but turned into 0 kb file size. Do I need to give permission on the server?

    Reply

  54. Problem solved :) I did AppPool 32-bit Application is true

    Reply

  55. Posted by Manjunath T on January 20, 2012 at 5:06 am

    For me this proc.WaitForExit(); get hanged, no idea why.
    Only after I end the process in the task bar the execution goes further.
    What should be the reason and solution?

    Reply

  56. Posted by Sandeep Beniwal on April 20, 2012 at 6:30 am

    Please provide me full code in asp.net 3.5 with videoconvert.aspx and videoconvert.aspx.cs file.
    Thanks in advanced

    Reply

  57. Posted by Sandeep Beniwal on April 20, 2012 at 6:31 am

    Please provide me full code of converting video file of any format into .flv in asp.net 3.5 with videoconvert.aspx and videoconvert.aspx.cs file.
    Thanks in advanced

    Reply

  58. [...] used in the application. Fortunately again, I came across JasonJano’s Blog where he posted a simple c# wrapper for ffmpeg. Lucky me! It just made my work easier. The code does just exactly what I needed so why re-invent [...]

    Reply

  59. Posted by Amrita on June 14, 2012 at 2:24 pm

    What if I need other information from the metadata like resolution,frame rate,aspect ratio . Where can I find regular expressions for those information.How did you create the regular expressions.

    Reply

  60. This is amazing! Thank you!

    Reply

  61. Posted by Ayan on July 3, 2012 at 3:10 pm

    Cat is not working in windows .Can you have a solution please .I done a Google search and it is saying we need to use batch file and copy it .but can it be dynamic ?

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: