Nizar Belhiba Ответов: 2

Создание/обрезка видеофайла с помощью directshow


Мне нужно, чтобы сократить последовательность/срок AVI видео файл. Допустим, видео длится 5 минут. Мне нужно создать новое видео, например, от 1 минуты до 2 минут. только изображения обязательны, я не забочусь об аудио (мои видео без звука)

пользуясь
iMediaSeek. GetDuration (out duration) and iMediaSeek. GetAvailable (out earliest, out latest)
Я создал своего рода файл справки (в текстовом формате), чтобы получить количество кадров и их продолжительность. поэтому я знаю, какой кадр установлен в данный момент времени. Проблема в том, что я понятия не имею, как извлечь этот кадр и сделать из него новый avi-видеофайл.

**ОБНОВЛЕНИЕ:**

Мне удалось создать график directshow, который считывает видеофайл и делает копию в новый файл. хотя он все еще делает копию исходного файла 1:1, я почти уверен, что смогу справиться с этой задачей с помощью
IMediaSeeking SetPostion
как уже упоминалось ранее.

Чтобы понять процесс работы графа Directshow, я использовал GraphStudioNext.

Как только я закончу, я отправлю окончательный код в качестве ответа.

Что я уже пробовал:

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);
             }
          }
       }

2 Ответов

Рейтинг:
2

RickZeeland

Смотрите ответы здесь: Как обрезать видео с помощью Directshow!?[^]

А здесь: Управление временной шкалой видео C# для DirectShow &VLC, таких как Adobe AfterEffects[^]

Он также может потребоваться для установки более старой версии DirectX, например DirectX 9 SDK.
Это все еще должно быть доступно для скачивания как DXSDK_Jun10.exe.


Nizar Belhiba

Извините, что я уже нашел этот поток, но пример проекта DirectShow находится на языке c++, и я уже несколько недель пытаюсь преобразовать его в c#.
Во-вторых, к сожалению, я должен использовать DirectShow, а не ffmpeg, и во втором ответе подсказка использовать IMediaSeeking SetPostion, как я уже упоминал в своем вопросе, может быть, хороша, но, к сожалению, недостаточно хороша для меня, мне нужно больше объяснений.

И, к сожалению, статья в ссылке, которую вы предоставили относительно c#, была удалена.

Во всяком случае, я нашел и решение, я опубликовал то, что я сделал до сих пор. Все еще работаю, чтобы закончить его, как только это будет сделано, я опубликую код в качестве ответа

Рейтинг:
18

Nizar Belhiba

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.

После этого вам нужно только закодировать свой собственный график. С помощью документации, кода c++ из ссылки, предоставленной @RickZeeland в answer1 и вашим другом google, достаточно легко узнать основы.

а вот мой код:

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);
            LogFileMan


RickZeeland

Молодец, и спасибо, что поделились информацией !