Sunday 9 February 2014

AForge, FFmpeg and H.264 Codec - Default Settings Problems

If you've read my previous blog post you;d be aware that I'm currently in the process of creating mp4 videos encoded with the H.264 codec and to do that, I'm using AForge.NET. Unfortunately, the functionality to encode in H.264 isn't readily available and I went through the steps to enable it in that previous blog post.

However, nothing is ever as easy as it should be and as soon as I had enabled it, I got the following error message:
"broken ffmpeg default settings detected"

After a bit of research I found the cause of the problem and unsurprisingly, it's exactly what it says on the tin. The defaults settings being sent to the codec are broken. In actual fact, the default settings set by the FFmpeg library (that's the library which AForge.NET wraps around) are a load of rubbish. If we want to get this working then we're going to need to set some sensible defaults.

If you open up the Video.FFMPEG project from the AForge.NET solution (the one found here) and open up VideoFileWriter.cpp and find the add_video_stream method, you should see an if statement that looks like this:


if (codecContex->codec_id == libffmpeg::CODEC_ID_MPEG1VIDEO)
{
    codecContex->mb_decision = 2;
}


We can now add to this if statement and set up some default values which will work like so:


if (codecContex->codec_id == libffmpeg::CODEC_ID_MPEG1VIDEO)
{
    codecContex->mb_decision = 2;
}
else if(codecContex->codec_id == libffmpeg::CODEC_ID_H264)
{
    codecContex->bit_rate_tolerance = 0;
    codecContex->rc_max_rate = 0;
    codecContex->rc_buffer_size = 0;
    codecContex->gop_size = 40;
    codecContex->max_b_frames = 3;
    codecContex->b_frame_strategy = 1;
    codecContex->coder_type = 1;
    codecContex->me_cmp = 1;
    codecContex->me_range = 16;
    codecContex->qmin = 10;
    codecContex->qmax = 51;
    codecContex->scenechange_threshold = 40;
    codecContex->flags |= CODEC_FLAG_LOOP_FILTER;
    codecContex->me_subpel_quality = 5;
    codecContex->i_quant_factor = 0.71;
    codecContex->qcompress = 0.6;
    codecContex->max_qdiff = 4;
    codecContex->directpred = 1;
    codecContex->flags2 |= CODEC_FLAG2_FASTPSKIP;
}


If you now compile that and use the resulting DLL in your project, you'll see the error has gone!

But.... as always, it's not that simple! I got to this stage and when I was just using a simple bitmap image to create a very simple (and very short) video I'd get the following warning during every frame that I sent to be encoded:
non-strictly-monotonic PTS

However, it didn't seem to have any effect, my video file was still created and played so I thought it wouldn't really matter. I was wrong.

When I put the DLL into my final project that involves creating much larger movies, the program would just randomly crash. I say randomly because there was no real consistency to it. At different times during the writing process the WriteVideoFrame method would throw an exception and that'd be the end of that.

On that basis, I thought it be best that I resolve this "PTS" warning and see if that solves the problem. But, what on earth is a "non-strictly-monotonic PTS"? That's a good question which I hope to answer in my next blog after I've fully understood it myself!

5 comments: