Question

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.

Was it helpful?

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.

OTHER TIPS

I had a quick look at the source code here. It appears that StartScan is actually an asynchronous call and you must wait for the ScanCompleted event to be raised before calling StartEncode.

What seems to be happening is that the Titles collection is null. This is populated in a private method PollScanProgress which seems to fire on a timer that checks for the progress of a scan. The timer is setup in StartScan.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top