Click here to Skip to main content
15,886,799 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I need to cut a sequence/period of an avi video file. Let's say the video is 5min long. I need to create a new video for example from time 1min to 2min. only images are mandatory, I don't care about the audio (my videos are without audio)

by using
C#
iMediaSeek. GetDuration (out duration) and iMediaSeek. GetAvailable (out earliest, out latest)
I have created a sort of a help file (in text format) to get the number of Frames and their duration time. so I know which frame is set at a given time. The problem is, I have no Idea how to extract this frame and make a new avi video file of them.

**UPDATE:**

I managed to create a directshow Graph that read the video file and make a copy to a new file. though it still makes a 1:1 copy of the original file, I'm pretty sure that I can manage the task with
C#
IMediaSeeking SetPostion
as mentioned before.

To understand the work process of a Directshow graph, I have used GraphStudioNext.

Once I finish I'll post the final code as an answer.

What I have tried:

C#
private static void checkHR(int hr, string msg)
          {
             if (hr < 0)
             {
                LogFileManager.Instance.WriteToLogFile(msg, SwitchLevel.Info);
                DsError.ThrowExceptionForHR(hr);
             }
          }
          #region Member variables
    
          // graph builder interfaces
          private static ICaptureGraphBuilder2 pGraphBuilder;
          private static IReferenceClock pClock;
          #endregion    
    
          private static void BuildGraph(IGraphBuilder pGraph, string FileName, string NewFile)
          {
             const bool USEDAVISPLITTER = false;
             int hr = 0;
             Guid guid = new Guid(USEDAVISPLITTER ? "D3588AB0-0781-11CE-B03A-0020AF0BA770"   // AVISource
                                                   : "CEA8DEFF-0AF7-4DB9-9A38-FB3C3AEFC0DE"); // osAviSource
            
             //Graph builder
             pGraphBuilder = new CaptureGraphBuilder2() as ICaptureGraphBuilder2;
             hr = pGraphBuilder.SetFiltergraph(pGraph);
             checkHR(hr, "Can't SetFilterGraph");
             
             //Add AVI File Source
             IBaseFilter sourceFilter = (IBaseFilter)new AVIDec();
             sourceFilter.SetSyncSource(null);   //Set Graph Clock to Null
             hr = pGraph.AddFilter(sourceFilter, "AVI File Source");
             checkHR(hr, "Can't add AVI File Source to Graph");
             //set Source filename        
             try
             {
                sourceFilter = DXHelper.CreateFilter(guid);
             }
             catch (System.IO.FileNotFoundException)
             {
                // can try to register and try again
             }
             if (null == sourceFilter) sourceFilter = RegisterAviSplitter(guid);
             if (null != sourceFilter)
             {
                DsError.ThrowExceptionForHR(pGraph.AddFilter(sourceFilter, "sourceFilter"));
                IFileSourceFilter sourceFilter_src = sourceFilter as IFileSourceFilter;
                if (sourceFilter_src == null) checkHR(unchecked((int)0x80004002), "Cant't get IFileSourceFilter");
                hr = sourceFilter_src.Load(FileName, null);
                checkHR(hr, "Can't load file");
             }       
             
             //add AVI Mux
             IBaseFilter pAVIMux = (IBaseFilter)new AviDest();
             hr = pGraph.AddFilter(pAVIMux, "AVI MUX");
             checkHR(hr, "Can't add AVI Mux to graph");
    
             //connect AVI File Source and AVI Mux
             hr = pGraph.ConnectDirect(GetPin(sourceFilter, "Video 0"), GetPin(pAVIMux, "Input 01"), null);
             checkHR(hr, "Cant connect AVI File Source to AVI Mux");
    
             //add File writer
             IBaseFilter pFilewriter = (IBaseFilter)new FileWriter();
             hr = pGraph.AddFilter(pFilewriter, "File Writer");
             checkHR(hr, "Can't add File writer to graph");
             //Set destination filename
             IFileSinkFilter pFilewriter_sink = pFilewriter as IFileSinkFilter;
             if (pFilewriter_sink == null) checkHR(unchecked((int)0x8004002), "Can't get IFileSinkFilter");
             hr = pFilewriter_sink.SetFileName(NewFile, null);
             checkHR(hr, "Can't set filename");
    
             //connect AVI Mux and File writer         
             hr = pGraph.ConnectDirect(GetPin(pAVIMux, "AVI Out"), GetPin(pFilewriter, "in"), null);
             checkHR(hr, "Can't connect AVI Mux and File writer");
          }
          
          private static IBaseFilter RegisterAviSplitter(Guid guid)
          {
             string filePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\registerOSAviSplitter.bat";
             System.Diagnostics.Process reg = new System.Diagnostics.Process();
             //This file registers .dll files as command components in the registry.
             reg.StartInfo.FileName = filePath;
             reg.StartInfo.UseShellExecute = false;
             reg.StartInfo.CreateNoWindow = true;
             reg.StartInfo.RedirectStandardOutput = true;
             reg.Start();
             reg.WaitForExit(10 * 1000);
             reg.Close();
             System.Threading.Thread.Sleep(2000);
             return DXHelper.CreateFilterSafe(guid);
          }
    
          private static IPin GetPin(IBaseFilter filter, string pinname)
          {
             IEnumPins epins;
             int hr = filter.EnumPins(out epins);
             checkHR(hr, "Can't ennumerate pins");
             IntPtr fetched = Marshal.AllocCoTaskMem(4);
             IPin[] pins = new IPin[1];
             while(epins.Next(1, pins, fetched) == 0)
             {
                PinInfo pinfo;
                pins[0].QueryPinInfo(out pinfo);
                bool found = (pinfo.name == pinname);
                DsUtils.FreePinInfo(pinfo);
                if (found) return pins[0];
             }
             checkHR(-1, "Pin not found");
             return null;
          }
    
          public static void createNewFile(string fileName, string newFile)
          {
             try
             {
                IGraphBuilder graph = (IGraphBuilder)new FilterGraph();
                LogFileManager.Instance.WriteToLogFile("Building graph...", SwitchLevel.Info);
                BuildGraph(graph, fileName, "D:\\Test_Cut2.avi");
                LogFileManager.Instance.WriteToLogFile("Running...", SwitchLevel.Info);
                IMediaControl mediaControl = (IMediaControl)graph;
                IMediaEvent mediaEvent = (IMediaEvent)graph;
                int hr = mediaControl.Run();
                checkHR(hr, "Can't run the graph");
                bool stop = false;
                while(!stop)
                {
                   Thread.Sleep(500);
                   LogFileManager.Instance.WriteToLogFile(".", SwitchLevel.Info);
                   EventCode ev;
                   IntPtr p1, p2;
                   Application.DoEvents();
                   while (mediaEvent.GetEvent(out ev, out p1, out p2, 0) == 0)
                   {
                      if (ev == EventCode.Complete || ev == EventCode.UserAbort)
                      {
                         mediaControl.StopWhenReady();                         
                         LogFileManager.Instance.WriteToLogFile("Done!", SwitchLevel.Info);
                         stop = true;
                      }
                      else if (ev == EventCode.ErrorAbort)
                      {
                         LogFileManager.Instance.WriteToLogFile(string.Format("An error occured: HRESULT={0,X}", p1), SwitchLevel.Info);
                         mediaControl.Stop();
                         stop = true;
                      }
                      mediaEvent.FreeEventParams(ev, p1, p2);
                   }
                }
             }
             catch (COMException ex)
             {
                ErrorManager.Instance.HandleException(ex, SwitchLevel.Error);
                LogFileManager.Instance.WriteToLogFile("COM error: " + ex.ToString(), SwitchLevel.Info);            
             }
             catch (Exception ex)
             {
                LogFileManager.Instance.WriteToLogFile("Error: " + ex.ToString(), SwitchLevel.Info);
                ErrorManager.Instance.HandleException(ex, SwitchLevel.Error);
             }
          }
       }
Posted
Updated 10-Jan-19 2:58am
v5

And here we go, for those who need to do something similar It's not that difficult as I thought in the beginning, when you know what you have to do! And that was exactly my main issue. I had no idea what to do or how to approach the task. So to get started in DirectShow you need to build a graph which do for you the task. I recommend to use GraphEdit provided with the SDK, or in my case GraphStudioNext. the best way to start is if you have any program that play's a video file or better an application that make a video file from a web cam. with the editor you can connect to that application and it will generate for you the graph used. Then you can create/simulate your own graph with your needs.

After that you only need to code your own graph. With the help of the documentations, the c++ code from the link provided by @RickZeeland in answer1 and your friend google, it's easy enough to know the basics.

and here is my code:

C#
sing System;
using System.Collections.Generic;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using DirectShowLib;

namespace Modules.File.Video
{
   public class AviSplitter
   {
      private static void checkHR(int hr, string msg)
      {
         if (hr < 0)
         {
            LogFileManager.Instance.WriteToLogFile(msg, SwitchLevel.Info);
            DsError.ThrowExceptionForHR(hr);
         }
      }

      #region Member variables
      public static double Duration { get; private set; }
      public static long NrFramesInAVI { get; private set; }

      // graph builder interfaces
      private static ICaptureGraphBuilder2 pGraphBuilder;
      private static IMediaSeeking pmediaSeeking;
      #endregion

      private static void BuildGraph(IGraphBuilder pGraph, string FileName, string NewFile)
      {
         const bool USEDAVISPLITTER = false;
         int hr = 0;
         Guid guid = new Guid(USEDAVISPLITTER ? "D3588AB0-0781-11CE-B03A-0020AF0BA770"   // AVISource
                                               : "CEA8DEFF-0AF7-4DB9-9A38-FB3C3AEFC0DE"); // osAviSource

         //Graph builder
         pGraphBuilder = new CaptureGraphBuilder2() as ICaptureGraphBuilder2;
         hr = pGraphBuilder.SetFiltergraph(pGraph);
         checkHR(hr, "Can't SetFilterGraph");

         //Add AVI File Source
         IBaseFilter sourceFilter = (IBaseFilter)new AVIDec();
         //sourceFilter.SetSyncSource(null);   //Set Graph Clock to Null
         hr = pGraph.AddFilter(sourceFilter, "AVI File Source");
         checkHR(hr, "Can't add AVI File Source to Graph");
         //set Source filename        
         try
         {
            sourceFilter = DXHelper.CreateFilter(guid);
         }
         catch (System.IO.FileNotFoundException)
         {
            // can try to register and try again
         }
         if (null == sourceFilter) sourceFilter = RegisterAviSplitter(guid);
         if (null != sourceFilter)
         {
            DsError.ThrowExceptionForHR(pGraph.AddFilter(sourceFilter, "sourceFilter"));
            IFileSourceFilter sourceFilter_src = sourceFilter as IFileSourceFilter;
            if (sourceFilter_src == null) checkHR(unchecked((int)0x80004002), "Cant't get IFileSourceFilter");
            hr = sourceFilter_src.Load(FileName, null);
            checkHR(hr, "Can't load file");
         }

         //add AVI Mux
         IBaseFilter pAVIMux = (IBaseFilter)new AviDest();
         //pAVIMux.SetSyncSource(null);
         hr = pGraph.AddFilter(pAVIMux, "AVI MUX");
         checkHR(hr, "Can't add AVI Mux to graph");

         //connect AVI File Source and AVI Mux
         hr = pGraph.ConnectDirect(GetPin(sourceFilter, "Video 0"), GetPin(pAVIMux, "Input 01"), null);
         checkHR(hr, "Cant connect AVI File Source to AVI Mux");

         //add File writer
         IBaseFilter pFilewriter = (IBaseFilter)new FileWriter();
         //pFilewriter.SetSyncSource(null);
         hr = pGraph.AddFilter(pFilewriter, "File Writer");
         checkHR(hr, "Can't add File writer to graph");
         //Set destination filename
         IFileSinkFilter pFilewriter_sink = pFilewriter as IFileSinkFilter;
         if (pFilewriter_sink == null) checkHR(unchecked((int)0x8004002), "Can't get IFileSinkFilter");
         hr = pFilewriter_sink.SetFileName(NewFile, null);
         checkHR(hr, "Can't set filename");

         //connect AVI Mux and File writer         
         hr = pGraph.ConnectDirect(GetPin(pAVIMux, "AVI Out"), GetPin(pFilewriter, "in"), null);
         checkHR(hr, "Can't connect AVI Mux and File writer");
      }

      private static IBaseFilter RegisterAviSplitter(Guid guid)
      {
         string filePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\registerOSAviSplitter.bat";
         System.Diagnostics.Process reg = new System.Diagnostics.Process();
         //This file registers .dll files as command components in the registry.
         reg.StartInfo.FileName = filePath;
         reg.StartInfo.UseShellExecute = false;
         reg.StartInfo.CreateNoWindow = true;
         reg.StartInfo.RedirectStandardOutput = true;
         reg.Start();
         reg.WaitForExit(10 * 1000);
         reg.Close();
         System.Threading.Thread.Sleep(2000);
         return DXHelper.CreateFilterSafe(guid);
      }

      private static IPin GetPin(IBaseFilter filter, string pinname)
      {
         IEnumPins epins;
         int hr = filter.EnumPins(out epins);
         checkHR(hr, "Can't ennumerate pins");
         IntPtr fetched = Marshal.AllocCoTaskMem(4);
         IPin[] pins = new IPin[1];
         while (epins.Next(1, pins, fetched) == 0)
         {
            PinInfo pinfo;
            pins[0].QueryPinInfo(out pinfo);
            bool found = (pinfo.name == pinname);
            DsUtils.FreePinInfo(pinfo);
            if (found) return pins[0];
         }
         checkHR(-1, "Pin not found");
         return null;
      }

      private static double TimeOfFrame(long frame)
      {
         double time = (double)frame * Duration;
         return time / (double)NrFramesInAVI;
      }

      private static double TimeOfMediaTime(long mediaTime)
      {
         return ((double)mediaTime / 10000000.0);
      }

      private static long MediaTimeOfTime(double time)
      {
         return (long)(time * 10000000.0);
      }

      private static double getDuration()
      {
         if (pmediaSeeking == null) return 0.1;
         long duration; pmediaSeeking.GetDuration(out duration);
         return TimeOfMediaTime(duration);
      }

      private static long GetNbOfFrames()
      {
         long ret = 0;
         if (pmediaSeeking != null)
         {
            try
            {
               long earliest, latest;
               pmediaSeeking.SetTimeFormat(TimeFormat.Frame);
               pmediaSeeking.GetAvailable(out earliest, out latest);
               pmediaSeeking.SetTimeFormat(TimeFormat.MediaTime);
               return latest;
            }
            catch { }
         }
         return ret;
      }

      public static void createNewFile(string fileName, string newFile, long startPos, long endPos)
      {
         try
         {
            IGraphBuilder graph = (IGraphBuilder)new FilterGraph();
            LogFileManager.Instance.WriteToLogFile("Building graph...", SwitchLevel.Info);

            BuildGraph(graph, fileName, "D:\\Test_Cut3.avi");
            LogFileManager.Instance.WriteToLogFile("Running...", SwitchLevel.Info);

            IMediaControl mediaControl = (IMediaControl)graph;
            IMediaEvent mediaEvent = (IMediaEvent)graph;


            pmediaSeeking = (IMediaSeeking)graph;
            NrFramesInAVI = GetNbOfFrames();
            Duration = getDuration();
            pmediaSeeking?.SetTimeFormat(TimeFormat.MediaTime);

            //Set Start Position
            long Starttime = MediaTimeOfTime(TimeOfFrame(startPos));
            LogFileManager.Instance.WriteToLogFile("Set Start at frame: " + startPos.ToString() + "[" + (Starttime / 10000000).ToString() + "])", SwitchLevel.Info);
            pmediaSeeking.SetPositions(Starttime, AMSeekingSeekingFlags.AbsolutePositioning, 0, AMSeekingSeekingFlags.NoPositioning);

            //set Stop Position
            long endtime = MediaTimeOfTime(TimeOfFrame(endPos));
            LogFileManager.Instance.WriteToLogFile("Set End at frame: " + endPos.ToString() + "[" + (Starttime / 10000000).ToString() + "])", SwitchLevel.Info);
            pmediaSeeking.SetPositions(endtime, AMSeekingSeekingFlags.NoPositioning, 0, AMSeekingSeekingFlags.AbsolutePositioning);

            int hr = mediaControl.Run();
            checkHR(hr, "Can't run the graph");

            bool stop = false;
            while (!stop)
            {
               Thread.Sleep(500);
               LogFileManager.Instance.WriteToLogFile(".", SwitchLevel.Info);
               EventCode ev;
               IntPtr p1, p2;
               Application.DoEvents();
               while (mediaEvent.GetEvent(out ev, out p1, out p2, 0) == 0)
               {
                  if (ev == EventCode.Complete || ev == EventCode.UserAbort)
                  {
                     mediaControl.StopWhenReady();
                     LogFileManager.Instance.WriteToLogFile("Done!", SwitchLevel.Info);
                     stop = true;
                  }
                  else if (ev == EventCode.ErrorAbort)
                  {
                     LogFileManager.Instance.WriteToLogFile(string.Format("An error occured: HRESULT={0,X}", p1), SwitchLevel.Info);
                     mediaControl.Stop();
                     stop = true;
                  }
                  mediaEvent.FreeEventParams(ev, p1, p2);
               }
            }
         }
         catch (COMException ex)
         {
            ErrorManager.Instance.HandleException(ex, SwitchLevel.Error);
            LogFileManager.Instance.WriteToLogFile("COM error: " + ex.ToString(), SwitchLevel.Info);
         }
         catch (Exception ex)
         {
            LogFileManager.Instance.WriteToLogFile("Error: " + ex.ToString(), SwitchLevel.Info);
            ErrorManager.Instance.HandleException(ex, SwitchLevel.Error);
         }
      }
   }
}
 
Share this answer
 
v2
Comments
RickZeeland 10-Jan-19 9:31am    
Well done, and thanks for sharing the information !
See answers here: How to trim the video using Directshow!?[^]

And here: C# Video TimeLine Control for DirectShow & VLC Like Adobe AfterEffects[^]

It also might be needed to install an older DirectX version, like the DirectX 9 SDK.
This should still be available for download as DXSDK_Jun10.exe.
 
Share this answer
 
v3
Comments
Nizar Belhiba 8-Jan-19 8:39am    
Sorry I have already found that Thread but the DirectShow sample Project is in c++, and I've been trying to convert it to c# since weeks.
Second, unfortunately I have to use DirectShow and not ffmpeg and in the second answer the hint to use IMediaSeeking SetPostion as I mentioned in my Question is maybe good but unfortunately not good enough to me, I need more explanation.

And unfortunately the article in the link you provided regarding c# has been deleted.

Anyway, I have found and a solution, I'have post what I have done so far. Still working to finish it, once it's done I'll post the code as an answer

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900