Getting Handbrake encoder to work
Asked Answered
H

2

5

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.

Hopi answered 26/5, 2013 at 18:45 Comment(1)
There is a link on that popup: "Copy exception details to clipboard". Paste that information into your question.Leal
P
7

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.

Printer answered 27/5, 2013 at 17:48 Comment(11)
WOW,What more can I ask for! Thank you bro. I tried Vidcoder and looked into it's source code too. Infact, for usability, I prefer Vidcoder over handbrake. The implementation of the piping and queue system are way too good. I wish VidCoder had an API.Thank you for all your great work. i've recommended VidCoder on the Handbrake forum too.Hopi
@Printer - excellent post. I tried the example you linked but I get this error: Unable to load DLL 'hb.dll': The specified module could not be found. Any thoughts? Thanks in advanceTisatisane
@Tisatisane hb.dll is there in the zip file. Maybe copy it to your output directory?Printer
@RandomEngy, Your sample helped a lot, Thank you. would you remember the corresponding Handbreak release version? I do not find "HandBrakeInterop, Version=1.41.0.0" in the Handbreak package, I see either "'HandBrakeInterop, Version=1.40.0.0" or 'HandBrakeInterop, Version=1.51.0.0"...Mccartan
@RandomEngy, the reason I need this, I want to use the latest version without making any code changes...somehow 'HandBrakeInterop, Version=1.40.0.0" and 'HandBrakeInterop, Version=1.51.0.0" is not working. The only thing works is what ever you have in the sample.Mccartan
@KarthikeyanVijayakumar Edited answer with new minimal project.Printer
@RandomEngy, In terms of converting .avi and .wmv - what difference would the new version make? I am trying to build a console application that would process the .avi/.wmv.Mccartan
@KarthikeyanVijayakumar There are many changes. You can read in detail here: handbrake.fr/news.phpPrinter
@Printer how to use WebOptimize option available in handbroke tool in the above available code?Emelineemelita
@Printer json version doesn't workEmelineemelita
@Printer it doesn't work with 64 bit solutionEmelineemelita
L
1

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.

Leal answered 27/5, 2013 at 6:8 Comment(5)
Thank you Mike..it seems like this took me one more step further. But, I'm still getting another exception..Hopi
@Hopi I can't tell what exactly is going wrong now, but perhaps the events HandBrakeUtils.MessageLogged and HandBrakeUtils.ErrorLogged may provide additional information.Leal
Thank you for all your efforts too Mike. I really appreciate it.Hopi
I'm developing some code based on your HBInteropExampleV3.zip code, but taking the conversion parameters from logs of actual jobs on my machine as suggested. The problem I have is that your utility JsonEncodeObject and sample JSON use numbers for all the codec specifications, whereas my logs use meaningful strings like "x264". Do you either have updated code which will accept the strings, or can you please point me to a lookup list so that I can select the right replacements? Thanks, AndrewEastnortheast
I actually managed to answer this for myself by inspecting the HandBrakeEncoderHelpers.VideoEncoders and HandBrakeEncoderHelpers.AudioEncoders lists. It helps that the sample file is doing a very similar job to my usual case (conversion of HD video to MP4), so in most cases I can just use the values in the sample. ThankEastnortheast

© 2022 - 2024 — McMap. All rights reserved.