Friday, October 03, 2008

Video Encoding with mencoder is a nightmare

I have been spending a lot of time recently working on defining exactly how to accomplish taking my HD feed of television programing, and automatically encoding it to remove commercials and use x264 as the H264 encoding engine. To this end I have found some lacking in the original mencoder documentation. I will try to document each of the filters that I am using in my attempt to complete my task and hopefully help others to not have the same issue(s).

Conventions:
Text which requires replacement will be marked like a variable with a dollar sign ($).
definitions will be preceded by a double hyphen (--)


Part 1. In this section I will provide an automated method to crop a video to remove all of the black bar area. The remaining video area will be the correct aspect ratio...etc. as mencoder corrects this for us.

Step 1 is to get the crop value. The easiest method is to use mencoders brother mplayer and the cropdetect filter

mplayer -vf cropdetect $videofile

cropdetect -- outputs to console the current crop values to remove the black area around an image as mplayer plays. output will be similar to [CROP] Crop area: X: 0..1738 Y: 0..1079 (-vf crop=1728:1072:6:4). (units pixel)

Now you can just copy paste the -vf crop line and run mencoder with the crop filter.

crop=w:h:x:y -- crops a video frame to w by h and offsets the frame by x:y from the top left corner. Keep in mind you must be re-encoding the video otherwise crop is meaningless. (units pixel) Crop can be used in both mencoder and mplayer...so you can test the crop values by doing

mplayer -vf crop=$cropvalues and see how the frame will look.
and of course to encode
mencoder $infile -vf crop=$cropvalues -ovc $video_codec -oac $audio_codec -of $output_formatdefaultavi -o $outfile

Step 2 Now how do we string these two items together? cropdetect and crop?
Answer: Scripting, woo.

As I am using windows and my HTPC is windows I am writing my scripts in batch but, you should easily be able to convert to perl/bash...etc.

1. Let's get the crop detected values into a text file that we can find within (this is far easier with GNU tools such as grep than Windows find. But here is the line I use

mplayer -vf cropdetect -ao null -vo null -frames 5 $infile | find "-vf crop=" > crop

This parses 5 lines of the video, and find selects only the lines with crop them to print to the text file 'crop'. Next we need to extract the crop parameter

2. Extract the detected values

FOR /F "tokens=3 delims=(=)" %%G IN ('type "crop"') DO SET crop=%%G

What this line does is, for each line in the crop file extract separate items based upon the delims ((=)) and take the third item (tokens=3) then I set that item to crop

[CROP] Crop area: X: 0..1738 Y: 0..1079 (-vf crop=1728:1072:6:4). is broken into
1=[CROP] Crop area: X: 0..1738 Y: 0..1079
2=-vf crop=
3=1728:1072:6:4

3. use the crop values in your mencoder filter string

mencoder $infile -vf crop=$cropvalues -ovc $video_codec -oac $audio_codec -of $output_formatdefaultavi -o $outfile

Let's put it together

-----
mplayer -vf cropdetect -ao null -vo null -frames 5 $infile | find "-vf crop=" > crop
FOR /F "tokens=3 delims=(=)" %%G IN ('type "crop"') DO SET crop=%%G
--mplayer is a little weird and will sometimes take over the stdout redirection. Here is my replacement way to do this
FOR /F "tokens=3 delims=(=)" %%G IN ('mplayer -vf cropdetect -ao null -vo null -frames 5 $infile') DO SET crop=%%G

mencoder $infile -vf crop=%crop% -ovc $video_codec -oac $audio_codec -of $output_formatdefaultavi -o $outfile
-----