Search code examples
c#exceptionvideo-encodinghandbrake

Getting Handbrake encoder to work


I am unable to get Handbrake to work for me in C#. It keeps throwing the error "Value cannot be null." . As you can see in the screenshot below, I've tried a couple of things based on what I know. I would appreciate any of your guidance to help me get this work.

Handbrake Encoder error

What I've tried so far (points are not relevant to the error displayed..Just FYI) :

  1. Installed handbrake on my pc
  2. Added a reference to HandBrakeInterop.dll
  3. Copied all the other required dlls to my debug folder and even tried adding them as references
  4. Tried changing debug versions from x86/x64

Where am I not on the right track?

Here's my code :

      HandBrake.Interop.HandBrakeInstance objHb = new HandBrake.Interop.HandBrakeInstance();
    HandBrake.Interop.Model.EncodeJob objJob = new HandBrake.Interop.Model.EncodeJob();
    objJob.SourceType = HandBrake.Interop.Model.SourceType.File;
    EncodingProfile objProfile = new EncodingProfile();
    objProfile.OutputFormat = Container.Mp4;
    objProfile.VideoEncodeRateType = VideoEncodeRateType.ConstantQuality;
    objProfile.IPod5GSupport = true;
    objProfile.PreferredExtension = OutputExtension.Mp4;
   // objJob.EncodingProfile = objProfile;
    objJob.SourcePath = AppDomain.CurrentDomain.BaseDirectory + "1.flv";
    objJob.OutputPath = AppDomain.CurrentDomain.BaseDirectory + "1.mp4";
    //objHb.Titles.Add(new HandBrake.Interop.SourceData.Title());

    XmlSerializer objSerializer = new XmlSerializer(typeof(EncodingProfile));


   MemoryStream mem=new MemoryStream(File.ReadAllBytes(AppDomain.CurrentDomain.BaseDirectory + "normal.xml"));
 var objPresetFromFile =  objSerializer.Deserialize(mem) as EncodingProfile;
 objJob.EncodingProfile = objPresetFromFile;
    objJob.UseDefaultChapterNames = true;
   // objJob.Title = 1;



    //HandBrake.Interop.Model.Encoders
    objHb.EncodeProgress += objHb_EncodeProgress;
    objHb.EncodeCompleted += objHb_EncodeCompleted;
    HandBrake.ApplicationServices.Parsing.Title title = new HandBrake.ApplicationServices.Parsing.Title();// new HandBrake.Interop.SourceData.Title();
    objHb.Initialize(5);
    //objHb.Titles = new System.Collections.Generic.List<HandBrake.Interop.SourceData.Title>();
    objHb.StartScan(AppDomain.CurrentDomain.BaseDirectory + "1.flv", 0);
    objJob.SourceType = HandBrake.Interop.Model.SourceType.File;
    objJob.RangeType = HandBrake.Interop.Model.VideoRangeType.Frames;

    objHb.StartEncode(objJob);

    Console.ReadLine();

EDIT : As requested,here's more info from the exception copied to clipboard

System.ArgumentNullException was unhandled
  HResult=-2147467261
  Message=Value cannot be null.
Parameter name: source
  Source=System.Core
  ParamName=source
  StackTrace:
       at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
       at HandBrake.Interop.HandBrakeInstance.GetTitle(Int32 titleNumber) in c:\HandBrake\Hudson\workspace\Release_WindowsGui64\0.9.9\win\CS\HandBrake.Interop\HandBrakeInterop\HandBrakeInstance.cs:line 1604
       at HandBrake.Interop.HandBrakeInstance.GetTitleIndex(Int32 titleNumber) in c:\HandBrake\Hudson\workspace\Release_WindowsGui64\0.9.9\win\CS\HandBrake.Interop\HandBrakeInterop\HandBrakeInstance.cs:line 1614
       at HandBrake.Interop.HandBrakeInstance.StartEncode(EncodeJob job, Boolean preview, Int32 previewNumber, Int32 previewSeconds, Double overallSelectedLengthSeconds) in c:\HandBrake\Hudson\workspace\Release_WindowsGui64\0.9.9\win\CS\HandBrake.Interop\HandBrakeInterop\HandBrakeInstance.cs:line 411
       at HandBrake.Interop.HandBrakeInstance.StartEncode(EncodeJob jobToStart) in c:\HandBrake\Hudson\workspace\Release_WindowsGui64\0.9.9\win\CS\HandBrake.Interop\HandBrakeInterop\HandBrakeInstance.cs:line 395
       at HandBrakeTest.Program.Main(String[] args) in c:\Users\user\Documents\Visual Studio 2012\Projects\HandBrakeTest\HandBrakeTest\Program.cs:line 53
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 

Edit :

  1. Moved handbrakeinstance and encodingjob's instance outside main and made them static
  2. Calling StartEncode in scan completed event

Now, the exception is Nullreferenceexception with this in the details :

System.NullReferenceException was unhandled by user code
  HResult=-2147467261
  Message=Object reference not set to an instance of an object.
  Source=HandBrakeInterop
  StackTrace:
       at HandBrake.Interop.InteropUtilities.ReadStructure[T](IntPtr structPtr) in c:\HandBrake\Hudson\workspace\Release_WindowsGui64\0.9.9\win\CS\HandBrake.Interop\HandBrakeInterop\InteropUtilities.cs:line 31
       at HandBrake.Interop.HandBrakeInstance.StartEncode(EncodeJob job, Boolean preview, Int32 previewNumber, Int32 previewSeconds, Double overallSelectedLengthSeconds) in c:\HandBrake\Hudson\workspace\Release_WindowsGui64\0.9.9\win\CS\HandBrake.Interop\HandBrakeInterop\HandBrakeInstance.cs:line 412
       at HandBrake.Interop.HandBrakeInstance.StartEncode(EncodeJob jobToStart) in c:\HandBrake\Hudson\workspace\Release_WindowsGui64\0.9.9\win\CS\HandBrake.Interop\HandBrakeInterop\HandBrakeInstance.cs:line 395
       at HandBrakeTest.Program.objHb_ScanCompleted(Object sender, EventArgs e) in c:\Users\user\Documents\Visual Studio 2012\Projects\HandBrakeTest\HandBrakeTest\Program.cs:line 65
       at HandBrake.Interop.HandBrakeInstance.PollScanProgress() in c:\HandBrake\Hudson\workspace\Release_WindowsGui64\0.9.9\win\CS\HandBrake.Interop\HandBrakeInterop\HandBrakeInstance.cs:line 811
       at HandBrake.Interop.HandBrakeInstance.<StartScan>b__2(Object o, ElapsedEventArgs e) in c:\HandBrake\Hudson\workspace\Release_WindowsGui64\0.9.9\win\CS\HandBrake.Interop\HandBrakeInterop\HandBrakeInstance.cs:line 752
       at System.Timers.Timer.MyTimerCallback(Object state)
  InnerException: 

Thanks in advance.


Solution

  • Yeah HandBrakeInterop isn't the easiest API to just write some code for; it's more geared toward full encoder GUIs right now; it doesn't fall back on defaults very well. You had a really good idea in just taking the EncodingProfile from the XML.

    Here's a working, minimal project that interacts with HandBrakeInterop: http://engy.us/misc/HandBrakeInteropExample.zip

    Some relevant bits:

    instance = new HandBrakeInstance();
    instance.Initialize(verbosity: 1);
    instance.ScanCompleted += instance_ScanCompleted;
    instance.StartScan(SourceFile, previewCount: 10);
    
    • 1 is the default verbosity. The "5" you were passing in isn't valid.
    • You'll probably want to pass in a real value for the previewCount on StartScan: it uses these static previews to do things like automatic cropping detection and combing detection.
    var job = new EncodeJob
        {
            EncodingProfile = profile,
            RangeType = VideoRangeType.All,
            Title = 1,
            SourcePath = SourceFile,
            OutputPath = AppDomain.CurrentDomain.BaseDirectory + "Output.mp4",
            ChosenAudioTracks = new List { 1 },
            Subtitles = new Subtitles
                {
                    SourceSubtitles = new List(),
                    SrtSubtitles = new List()
                }
        };
    
    • You need to pass in which title to encode (1-based index). For files this is rarely relevant; the feature is designed for DVD/Blu-ray scans.
    • You have to pass in both a RangeType and the specifics for the range: for example if you pick a RangeType of Frames then you need to specify FramesStart and FramesEnd. RangeType.All is easiest since you don't need to specify anything extra.
    • You need to specify a list of the chosen audio tracks indices (1-based). Not normally useful for files, but for DVD/Blu-ray discs that have multiple languages.
    • Right now you have to give it something under Subtitles or it will crash out. This is actually a bug; there were a few cases I wasn't guarding correctly. I'll fix this up in the future.

    Check out VidCoder if you want to see a full reference implementation of the API. HandBrake has started to use it some but it hasn't gotten around to all the features like static previews and pause/resume.

    Update: The code required now looks completely different and targets HandBrake.ApplicationServices.dll. New minimal project: http://engy.us/misc/HBInteropExampleV3.zip

    A notable difference is that the encode job is now specified in JSON. This is the exact same JSON blob that is spit out in the encode log in HandBrake/VidCoder, so you can steal it from there and tweak it.