dvrms2mpgを作った

コマンドラインで、dvr-msファイルをmpgファイルに変換するツールdvrms2mpg

使い方: dvrms2mpg.exe dvrms1 dvrms2 ...

$ dvrms2mpg.exe test.dvr-ms 
Muxer: Leadtek MPEG Muxer
Muxer Audio In: Audio In
Muxer Video In: Video In
Muxer Out: Output
Sink: Leadtek TS Writer
Sink In: Input

build graph
Converting to test.dvr-ms.mpg
Converted test.dvr-ms.mpg

ソース: dvrms2mpg.cs

using System;
using System.IO;
using System.Configuration;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using DirectShowLib;

namespace dvrms2mpg {
  public class FilterManager {
    private Dictionary<string, IMoniker> monikers = new Dictionary<string, IMoniker>();
    
    public FilterManager() {
      IFilterMapper3 filterMapper = new FilterMapper2() as IFilterMapper3;
      ICreateDevEnum devEnum;
      filterMapper.GetICreateDevEnum(out devEnum);
      Guid[] categories = new Guid[] {
        FilterCategory.BDACPCAFilters,
        FilterCategory.LegacyAmFilterCategory,
        FilterCategory.AudioCompressorCategory,
        FilterCategory.VideoCompressorCategory,
        //FilterCategory.BDASourceFiltersCategory,
      };
      foreach (Guid category in categories) {
        IEnumMoniker monikers;
        devEnum.CreateClassEnumerator(category, out monikers, CDef.None);
        monikers.Reset();
        IntPtr count = new IntPtr();
        IMoniker[] moniker = new IMoniker[1];
        while (monikers.Next(1, moniker, count) == 0) {
          this.monikers[GetFilterName(moniker[0])] = moniker[0];
        }
        Marshal.ReleaseComObject(monikers);
      }
      Marshal.ReleaseComObject(devEnum);
    }
    
    public IBaseFilter Get(string name) {
      IMoniker moniker = monikers[name];
      if (moniker == null) return null;
      return GetFilter(moniker);
    }
    
    private string GetFilterName(IMoniker filterMoniker) {
      object bagObj;
      Guid propertyBagId = typeof(IPropertyBag).GUID;
      filterMoniker.BindToStorage(null, null, ref propertyBagId, out bagObj);
      IPropertyBag bag = bagObj as IPropertyBag;
      object nameObj;
      bag.Read("FriendlyName", out nameObj, null);
      string name = nameObj as string;
      Marshal.ReleaseComObject(bagObj);
      return name;
    }
    
    private IBaseFilter GetFilter(IMoniker filterMoniker) {
      object filterObj;
      Guid baseFilterId = typeof(IBaseFilter).GUID;
      filterMoniker.BindToObject(null, null, ref baseFilterId, out filterObj);
      IBaseFilter filter = filterObj as IBaseFilter;
      return filter;
    }
  }
  
  public class Converter {
    private FilterManager filters = new FilterManager();
    public void Convert(string dvrmsFileName) {
      if (!File.Exists(dvrmsFileName)) {
        Console.WriteLine(dvrmsFileName + " not found");
        return;
      }
      
      Console.WriteLine("build graph");
      string outputFileName = dvrmsFileName + ".mpg";
      IGraphBuilder graph = new FilterGraph() as IGraphBuilder;
      
      IBaseFilter source;
      graph.AddSourceFilter(dvrmsFileName, dvrmsFileName, out source);
      
      IBaseFilter decrypt1 = filters.Get("Decrypt/Tag");
      graph.AddFilter(decrypt1, "Decrypt/Tag_1");
      
      IBaseFilter decrypt3 = filters.Get("Decrypt/Tag");
      graph.AddFilter(decrypt3, "Decrypt/Tag_3");
      
      IBaseFilter muxer = filters.Get(Config("Muxer"));
      graph.AddFilter(muxer, "MPEG Muxer");
      
      IBaseFilter sink = filters.Get(Config("Sink"));
      (sink as IFileSinkFilter).SetFileName(outputFileName, null);
      graph.AddFilter(sink, "MPEG Writer");
      
      Connect(graph, source, "DVR Out - 1", decrypt1, "In(Enc/Tag)");
      Connect(graph, source, "DVR Out - 3", decrypt3, "In(Enc/Tag)");
      Connect(graph, decrypt1, "Out", muxer, Config("Muxer Audio In"));
      Connect(graph, decrypt3, "Out", muxer, Config("Muxer Video In"));
      Connect(graph, muxer, Config("Muxer Out"), sink, Config("Sink In"));
      
      Console.WriteLine("Converting to " + outputFileName);
      (graph as IMediaControl).Run();
      EventCode evCode;
      (graph as IMediaEvent).WaitForCompletion(-1, out evCode);
      
      Console.WriteLine("Converted " + outputFileName);
    }
    
    private string Config(string name) {
      return ConfigurationManager.AppSettings[name];
    }
    
    private void Connect(IGraphBuilder graph,
                         IBaseFilter srcFilter, string outPinName,
                         IBaseFilter destFilter, string inPinName) {
      IPin sPin, dPin;
      srcFilter.FindPin(outPinName, out sPin);
      destFilter.FindPin(inPinName, out dPin);
      graph.ConnectDirect(sPin, dPin, null);
    }
  }
  
  class Program {
    static void Main(string[] args) {
      foreach(string key in ConfigurationManager.AppSettings) {
        string value = ConfigurationManager.AppSettings[key];
        Console.WriteLine("{0}: {1}", key, value);
      }
      Console.WriteLine();
      
      Converter converter = new Converter();
      foreach (string dvrmsFileName in args) {
        try {
          converter.Convert(dvrmsFileName);
        } catch (Exception ex) {
          Console.WriteLine(ex);
        }
      }
    }
  }
}

設定: dvrms2mpg.exe.config

<?xml version="1.0"?>
<configuration>
<appSettings>
  <add key="Muxer" value="Leadtek MPEG Muxer" />
  <add key="Muxer Audio In" value="Audio In" />
  <add key="Muxer Video In" value="Video In" />
  <add key="Muxer Out" value="Output" />
  <add key="Sink" value="Leadtek TS Writer" />
  <add key="Sink In" value="Input" />
</appSettings>
</configuration>

使えるMuxerやWriterは環境によって違います。

ビルド

csc.exe /r:DirectShowLib-2005.dll /platform:x86 dvrms2mpg.cs

同様にAVI圧縮dvrms2aviも作ってみたけど、うまくエンコードが動かなかった。何がまずいだろうか。