How to get video duration from mp4, wmv, flv, mov videos - c#

Alright. Actually i need mostly the mp4 format. But if it is possible to get for other types as well that would be nice. I just need to read the duration of the file. How can i do that with C# 4.0 ?
So the thing i need is like this video is like : 13 minutes 12 seconds
I can use 3 third party exes too. Like they save the information about the file to a text file. I can parse that text file.
Thank you.

This answer about P/Invoke for Shell32 reminded me of the Windows API Code Pack to access common Windows Vista/7/2008/2008R2 APIs.
It was very easy, using the PropertyEdit demo in the included samples, to figure out the Shell32 API to get various media file properties, like duration.
I assume the same prerequisite applies for having the proper demultiplexers installed, but it was quite simple, as it only required adding references to Microsoft.WindowsAPICodePack.dll and Microsoft.WindowsAPICodePack.Shell.dll and the following code:
using Microsoft.WindowsAPICodePack.Shell;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;
using (ShellObject shell = ShellObject.FromParsingName(filePath))
{
// alternatively: shell.Properties.GetProperty("System.Media.Duration");
IShellProperty prop = shell.Properties.System.Media.Duration;
// Duration will be formatted as 00:44:08
string duration = prop.FormatForDisplay(PropertyDescriptionFormatOptions.None);
}
Other stuff
Some common properties for an MPEG-4/AAC audio media file:
System.Audio.Format = {00001610-0000-0010-8000-00AA00389B71}
System.Media.Duration = 00:44:08
System.Audio.EncodingBitrate = ?56kbps
System.Audio.SampleRate = ?32 kHz
System.Audio.SampleSize = ?16 bit
System.Audio.ChannelCount = 2 (stereo)
System.Audio.StreamNumber = 1
System.DRM.IsProtected = No
System.KindText = Music
System.Kind = Music
It's easy to iterate through all properties if you're looking for the available metadata:
using (ShellPropertyCollection properties = new ShellPropertyCollection(filePath))
{
foreach (IShellProperty prop in properties)
{
string value = (prop.ValueAsObject == null) ? "" : prop.FormatForDisplay(PropertyDescriptionFormatOptions.None);
Console.WriteLine("{0} = {1}", prop.CanonicalName, value);
}
}

You could also use windows media player, although it don't support alle file types you requested
using WMPLib;
public Double Duration(String file)
{
WindowsMediaPlayer wmp = new WindowsMediaPlayerClass();
IWMPMedia mediainfo = wmp.newMedia(file);
return mediainfo.duration;
}
}

You can use DirectShow API MediaDet object, through DirectShow.NET wrapper library. See Getting length of video for code sample, get_StreamLength gets you the duration in seconds. This assumes Windows has MPEG-4 demultiplexer installed (requires third party components with Windows prior to 7, I believe the same applies to another answer by cezor, there are free to redistribute components though).

IMHO you could use MediaInfo which gives you a lot of information about media files.
There is a CLI for it so you can use it from your code and get info you need.
You can take a look at this link.

I think you are looking for FFMPEG - https://ffmpeg.org/
there are also some free alternatives that you can read about them in this question - Using FFmpeg in .net?
FFMpeg.NET
FFMpeg-Sharp
FFLib.NET
you can see this link for examples of using FFMPEG and finding the duration - http://jasonjano.wordpress.com/2010/02/09/a-simple-c-wrapper-for-ffmpeg/
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]));
}
}
}

FFMPEG project has a tool, called ffprobe which can provide you the information you need about your multimedia files and ouput the information in a nicely formated JSON.
Take a look at this answer for an example.

Using Windows Media Player Component also, we can get the duration of the video.
Following code snippet may help you guys :
using WMPLib;
// ...
var player = new WindowsMediaPlayer();
var clip = player.newMedia(filePath);
Console.WriteLine(TimeSpan.FromSeconds(clip.duration));
and don't forget to add the reference of wmp.dll which will be
present in System32 folder.

I found the NReco.VideoInfo library to be the best option and far simpler than some of those above. It's a simple as giving the library a file path and it spits out the metadata:
var ffProbe = new FFProbe();
var videoInfo = ffProbe.GetMediaInfo(blob.Uri.AbsoluteUri);
return videoInfo.Duration.TotalMilliseconds;

I had the same problem and we built a wrapper for ffprobe Alturos.VideoInfo.
You can use it simply by installing the nuget package. Also the ffprobe binary is required.
PM> install-package Alturos.VideoInfo
Example
var videoFilePath = "myVideo.mp4";
var videoAnalyer = new VideoAnalyzer("ffprobe.exe");
var analyzeResult = videoAnalyer.GetVideoInfo(videoFilePath);
var duration = analyzeResult.VideoInfo.Format.Duration;

StreamReader errorreader;
string InterviewID = txtToolsInterviewID.Text;
Process ffmpeg = new Process();
ffmpeg.StartInfo.UseShellExecute = false;
ffmpeg.StartInfo.ErrorDialog = false;
ffmpeg.StartInfo.RedirectStandardError = true;
ffmpeg.StartInfo.FileName = Server.MapPath("ffmpeg.exe");
ffmpeg.StartInfo.Arguments = "-i " + Server.MapPath("videos") + "\\226.flv";
ffmpeg.Start();
errorreader = ffmpeg.StandardError;
ffmpeg.WaitForExit();
string result = errorreader.ReadToEnd();
string duration = result.Substring(result.IndexOf("Duration: ") + ("Duration: ").Length, ("00:00:00.00").Length);

Related

C# Vlc Forms volume stabilization problem

I am using Vlc.DotNet.Forms package in my project. I open one video on form. Everything okay. But If I want to open a video again. When I change the volume, the volume of both videos changes.
My Code Example:
public VlcControl control;
public void Player(string url,int volume){
this.control = new VlcControl();
var currentAssembly = Assembly.GetEntryAssembly();
var currentDirectory = new FileInfo(currentAssembly.Location).DirectoryName;
var libDirectory = new DirectoryInfo(Path.Combine(currentDirectory, "libvlc", IntPtr.Size == 4 ? "win-x86" : "win-x64"));
control.BeginInit();
control.VlcLibDirectory = libDirectory;
control.Dock = DockStyle.Fill;
control.EndInit();
this.Controls.Add(control);
control.SetMedia(new Uri(#"" + url + ""));
control.Audio.Volume=volume;
control.Play();
}
For example,
Player("C:\\test\video1.mp4",50);
Player("C:\\test\video2.mp4",75);
The first video opens and the volume becomes 50. Then when I open the second video, the volume of both videos is 75.
I use these packages
VideoLAN.LibVLC.Windows (Version 3.0.0) https://www.nuget.org/packages/VideoLAN.LibVLC.Windows
Vlc.DotNet.Forms (Version 3.0.0) https://github.com/ZeBobo5
It seems that the previous answer did not work for me, instead I used the following:
vlcControl.Play();
var directsound = vlcControl.Audio.Outputs.All.FirstOrDefault(x => x.Name == "directsound");
if (directsound != null)
{
vlcControl.Audio.Outputs.Current = directsound;
vlcControl.Audio.IsMute = true;
}
Setting the directsound just after having started the player.
You can then toggle mute with:
vlcControl.Audio.ToggleMute();
Setting the volume is similar with:
vlcControl.Audio.Volume = 50;
This seems to be a known issue of libvlc 3.x.
The workaround is to use a different audio output. In Vlc.DotNet, you can do so like this:
control.VlcMediaplayerOptions = new []{ "--aout=directsound" };
Before control.EndInit();.
References:
https://forum.videolan.org/viewtopic.php?t=147229
https://github.com/ZeBobo5/Vlc.DotNet/issues/524

Concatenation error: Cannot concatenate media using NReco Video Converter for .NET (C#)

I want to concatenate two videos into one using Video Converter for .NET (C#) FFMpeg wrapper
but I have got "cannot concatenate media" exception.. am i missing something or not..
Here is my Source Code:
var ffMpeg = new NReco.VideoConverter.FFMpegConverter();
NReco.VideoConverter.ConcatSettings set = new NReco.VideoConverter.ConcatSettings();
string videoRootPath = #"E:\Sam\Recording Feature\WebApplication2\WebApplication2\Video\";
string tobename = "test";
string[] _fileNames = { #"E:\Sam\Recording Feature\WebApplication2\WebApplication2\Video\1.mov",
#"E:\Sam\Recording Feature\WebApplication2\WebApplication2\Video\2.mov"};
ffMpeg.ConcatMedia(_fileNames, videoRootPath + tobename + ".mov", NReco.VideoConverter.Format.mov, set);
Please Help
The most common reason why files cannot be merged into one resulting file is different frame size and/or frame rate (all input files should have the same frame size and rate).
Also note that ContactMedia just invokes FFMpeg with the following parameters:
ffmpeg.exe -i [file1] -i [file2] -i [file3] -filter_complex "concat=n=3:v=1:a=1 [v] [a]" -map "[v]" -map "[a]" [outputFile]
You may run it from command line for your files and see what is wrong. Note that if you want to use another approach for merging video files (FFMpeg supports several ways how to do that) you may use Invoke or ConvertMedia methods of FFMpegConverter class.
Have you used Splicer?
below is the link for reference.
https://splicer.codeplex.com/discussions/66271
string video1 = #"D:\Video\vid1.mp4";
string video2 = #"D:\Video\vid2.mp4";
string newvid = #"D:\Video\finalvid.mp4";
var ffMpeg = new FFMpegConverter();
ConcatSettings set = new ConcatSettings();
ffMpeg.ConcatMedia(new string[] { video1,video2 }, newvid, Format.mp4, set);

Example of QRCode ZXing

I need to create a qrreader with windows phone.
Xzing examples only print to video the qr string captured,
I need an example of how to understand if this string is a vcard and, consequently, save it in contact, or if it is a link and open it in the browser.
private void ScanPreviewBuffer()
{
try
{
_photoCamera.GetPreviewBufferY(_luminance.PreviewBufferY);
var binarizer = new HybridBinarizer(_luminance);
var binBitmap = new BinaryBitmap(binarizer);
var result = _reader.decode(binBitmap);
Dispatcher.BeginInvoke(() => CheckQr(result.Text));
}
catch { }
}
private void CheckQr(string qrString)
{
VibrateController vibrate = VibrateController.Default;
vibrate.Start(TimeSpan.FromMilliseconds(500));
MessageBox.Show(qrString);
/* CONTROLS HERE */
}
Obviously you have to start by parsing the qrString content to get what you want, i think we'll both agree on that point ;)
So the main issues are :
Determining formats (url or vcard)
Parsing them (if needed)
Using them to trigger wanted actions
1. About vCard
To determine if you qrString holds a vCard, maybe you could just try to match (with string.Contains or string.StartsWith methods) the vCard header which is BEGIN:VCARD and always seems to be the same from one version to another (see wikipedia).
For Windows Phone 7, there's no builtin features to parse vCards, so you have to do it by yourself or you could try to use the vCard library For Windows Phone. It would be used this way :
byte[] byteArray = Encoding.UTF8.GetBytes(qrString);
using (StreamReader reader = new StreamReader(new MemoryStream(byteArray)))
{
vCard card = new vCard(reader);
// access here card.PropertyFromvCard to get the information you need
}
There's not so much documentation about it, but sources are available on codeplex, so you'll probably find all the property names and samples you need.
For Windows Phone 8, the builtin ContactInformation.ParseVcardAsync method could help you to parse your qrString content (here is an official tutorial)
Then you need to finally create your contact :
If you're developping your App on Windows Phone 7, there's no way to create a contact directly from your application. You need to use the "save contact task" and pre-populate the fields you need. Here's an example :
SaveContactTask saveContactTask = new SaveContactTask();
saveContactTask.Completed += new EventHandler<SaveContactResult>(saveContactTask_Completed);
saveContactTask.FirstName = "John"; // card.PropertyFromvCard and so on...
saveContactTask.LastName = "Doe";
saveContactTask.MobilePhone = "2065550123";
saveContactTask.Show();
If you're developping on Windows Phone 8 (and it doesn't seem to be the case given your question tags), you can create a Custom contact store and write directly into it
2. About URLs
To know if you're dealing with an URL or not, i would advice you to follow suggestions coming with this SO answer. To make a long story short, here's the code you could use or at least something similar :
static bool IsValidUrl(string qrString)
{
Uri uri;
return Uri.TryCreate(urlString, UriKind.Absolute, out uri)
&& (uri.Scheme == Uri.UriSchemeHttp
|| uri.Scheme == Uri.UriSchemeHttps
|| uri.Scheme == Uri.UriSchemeFtp
|| uri.Scheme == Uri.UriSchemeMailto
/*...*/);
}
And finally to open your URL into a web browser (if it is a valid one of course), you could use the WebBrowser task or embed a true WebBrowser into your application with the WebBrowser control and make good use of it.
ZXing has a class called ResultParser with a static method parseResult.
The ResultParser supports some common content formats like vCard, vEvent, URL, etc.
It gives you as a result an instance of AddressBookParsedResult for vCard content back.
ParsedResult parsedResult = ResultParser.parseResult(result);

.Net library to move / copy a file while preserving timestamps

Does anyone know of a .Net library where a file can be copied / pasted or moved without changing any of the timestamps. The functionality I am looking for is contained in a program called robocopy.exe, but I would like this functionality without having to share that binary.
Thoughts?
public static void CopyFileExactly(string copyFromPath, string copyToPath)
{
var origin = new FileInfo(copyFromPath);
origin.CopyTo(copyToPath, true);
var destination = new FileInfo(copyToPath);
destination.CreationTime = origin.CreationTime;
destination.LastWriteTime = origin.LastWriteTime;
destination.LastAccessTime = origin.LastAccessTime;
}
When executing without administrative privileges Roy's answer will throw an exception (UnauthorizedAccessException) when attempting to overwrite existing read only files or when attempting to set the timestamps on copied read only files.
The following solution is based on Roy's answer but extends it to overwrite read only files and to change the timestamps on copied read only files while preserving the read only attribute of the file all while still executing without admin privilege.
public static void CopyFileExactly(string copyFromPath, string copyToPath)
{
if (File.Exists(copyToPath))
{
var target = new FileInfo(copyToPath);
if (target.IsReadOnly)
target.IsReadOnly = false;
}
var origin = new FileInfo(copyFromPath);
origin.CopyTo(copyToPath, true);
var destination = new FileInfo(copyToPath);
if (destination.IsReadOnly)
{
destination.IsReadOnly = false;
destination.CreationTime = origin.CreationTime;
destination.LastWriteTime = origin.LastWriteTime;
destination.LastAccessTime = origin.LastAccessTime;
destination.IsReadOnly = true;
}
else
{
destination.CreationTime = origin.CreationTime;
destination.LastWriteTime = origin.LastWriteTime;
destination.LastAccessTime = origin.LastAccessTime;
}
}
You can read and write all the timestamps there are, using the FileInfo class:
CreationTime
LastAccessTime
LastWriteTime
You should be able to read the values you need, make whatever changes you wish and then restore the previous values by using the properties of FileInfo.

Reading WMV resolution in C#

I'm trying to read the image size of a WMV file in C#.
I've tried using what is described here:
How do I get the duration of a video file using C#?
but the only attribute that has a value is Duration.
Any ideas ?
Thanks.
Only way I've seen it done is by playing it and attaching to the open event:
static WindowsMediaPlayerClass player;
static void Main()
{
player = new WindowsMediaPlayerClass();
IWMPMedia mediaInfo = player.newMedia("test.wmv");
player.OpenStateChange += new _WMPOCXEvents_OpenStateChangeEventHandler(player_OpenStateChange);
player.currentMedia = mediaInfo;
//...
Console.WriteLine("Done.");
Console.ReadKey();
}
private static void player_OpenStateChange(int state)
{
if (state == (int)WMPOpenState.wmposMediaOpen)
{
Console.WriteLine( "height = " + player.currentMedia.imageSourceHeight);
Console.WriteLine( "width = " + player.currentMedia.imageSourceWidth);
}
}
You'll want to dispose of any resources before exiting.
You use the code from the linked example, but you explicitly do a function call to get the height and width.
Example:
using WMPLib; // this file is called Interop.WMPLib.dll
WindowsMediaPlayerClass wmp = new WindowsMediaPlayerClass();
IWMPMedia mediaInfo = wmp.newMedia("myfile.wmv");
long height, width;
mediaInfo.get_imageSourceHeight(height);
mediaInfo.get_imageSourceWidth(width);
I prefer to use the free NReco.VideoInfo.dll. Mainly because I hate Windows Media Player. I have found that WMP is unreliable.
Here is the download link: http://www.nrecosite.com/video_info_net.aspx It's useful for other stuff too.
var ffProbe = new NReco.VideoInfo.FFProbe();
var videoInfo = ffProbe.GetMediaInfo(pathToFile);
Int32 tmpHeight = videoInfo.Streams[0].Height;
Int32 tmpWidth = videoInfo.Streams[0].Width;

Categories

Resources