Batch Video Conversion for Home Players

Owning a large video library today is not uncommon, and usually they try to collect everything in the best quality. However, the other side of the coin is incompatibility with old household players, ancient laptops and other portable gadgets.

I came across this for the commonplace reason for domestic comfort. Having a good media station under Windows, with a connected 60 "plasma and six-channel sound, tuned with the soul, with written scripts for AutoHotkey and rewritten to WinLIRC's own USB IR receiver , I realized that I can’t watch downloaded movies every night, since I want to do this without getting out from under the covers in the bedroom. Where there is a simple 40 "harvester bought on sale, on occasion, in the store of one of the major retail chains. The capabilities of a household player embedded in an LCD TV turned out to be enough to read flash drives and SD cards of any size, but in formats It turned out to be limited by AVI and MPEG-4-ASP codecs and weaker ones.The
solution was simple - to overtake a couple of hundreds of unwatched movies in AVI, and then automatically create a Lite version at the end of the torrent download by rtorrent on the home server. run on both Windows and Linux, since the server is running Gentoo.

However, technically it was not so easy to carry out all this with a snap. Screw converters with blackjack and other tinsel were swept away immediately, without attempting to install, and the rest was only Avidemuh , which has both a cross-platform implementation and a normal CLI interface. It only remained to automate the process and let the equipment do what it was supposed to do - to work.

Avidemux Script Automation

In fact, the CLI interface of Avidemux turned out to be rather flawed, but it saves the ability to write the project configuration to a script and then execute it with the run option:

avidemux2_cli --run script

It’s quite simple to look at the script format, for this it’s enough to run the Avidemux GUI version, make all the necessary settings, and then save the project to a file. This file contains all the settings of codecs and conversion filters.

Actually changing the container using Avidemux can be done without transcoding the video, but in my case there was a need to change the resolution of the output file and limit its width to 720 pixels. However, I did not find any other way to set the output resolution, except for explicitly specifying the width and height in pixels, so the idea of ​​using one script for all files was no longer needed. Had to use MediaInfo , which is also available for Linux, and get the dimensions from it, and most importantly, the aspect of the image displayed on the screen, and based on the set width, calculate the height. In addition, the idea came up to slightly stretch "widescreen" films in height to compensate for the visual compression of a TV hanging at a slight angle.

Thus an awk script appeared (the choice of awk is due to the compactness of its implementation for windows, compared, for example, with pearl). The script looks for all files using the * .mkv mask (the search string is easy to change for any platform), then calls MediaInfo for each found file and creates a script for AviDemux, and also adds a command to call the converter into the batch file. In the future, this script is easily redone to convert a single selected file and connect it to the download manager.

Script source

The source code removed the settings of the selected video codec, as they take up more space than the entire script and can be easily obtained from the AviDemux project. In this case, you must escape all double quotes with a backslash character (in this awk is similar to si).

BEGIN {
# настройки
	# ширина и высота дисплея, на котором будут проигрываться полученные файлы
	WIDTH = 720
	HEIGHT = 404
	# следует ли образать края чтобы подогнать видео под размер экрана
	CROP = 1
	# макс. коэффициент растяжения для устранения/уменьшения ченых полос
	STRETCH = 0.15
	# имя командного файла,  в который будут записаны команды для каждого видео
	BATCH = "run.cmd"
	# имена исполняемых файлов
	AVIDEMUX = "P:\\myProgs\\AVIDemux.2.5.6\\avidemux2_cli.exe"
	MEDIAINFO = "P:\\myProgs\\MediaInfo_CLI_0.7.59\\MediaInfo.exe"
	# команда, формирующая список нужных файлов (dir, ls, find)
	LIST = "dir /b *.mkv"
# конец настроек

	ASPECT = WIDTH / HEIGHT
	STRETCH += 1
	while((LIST | getline fn) > 0) {
		printf("FILE: %s\r\n", fn);
		InfoCommand = MEDIAINFO " --Output=Video;%Width%/%Height%/%AspectRatio%/%DisplayAspectRatio% \""fn"\""
		if((InfoCommand | getline tmp) > 0) {
			if (match(tmp, /([0-9]+)\/([0-9]+)\/([0-9.]+)\/([0-9.]+)/, m)) {
				w = int(m[1])
				h = int(m[2])
				a = 0.0 + m[3]
				d = 0.0 + m[4]
				if (a != d) {
					printf("\tAspect/Display are not equal %d %d\r\n", a, d);
				}
				# correct dimentions to square pixels
				cw = w
				ch = w / d
				# stretch to fit display
				if (cw / ch > ASPECT) {
					sh = cw / d * STRETCH
					printf("\timage is wider than display, do vertical stretch\r\n")
					if (sh > cw / ASPECT) {
						sh = cw / ASPECT
					}
					sw = cw
					# crop to fit display
					if (CROP && (sw / sh > ASPECT)) {
						printf("\timage is still wider than display, do horisontal crop\r\n")
						sw = sh * ASPECT
					}
					fw = (sw < WIDTH) ? rint(sw, 8) : rint(WIDTH,  8)
					fh = rint(sh / sw * fw, 4)
				}
				else if (cw / ch < ASPECT) {
					sw = ch * d * STRETCH
					printf("\timage is higher than display, do horisontal stretch\r\n")
					if (sw > ch * ASPECT) {
						sw = ch * ASPECT
					}
					sh = ch
					# crop
					if (CROP && (sw / sh > ASPECT)) {
						printf("\timage is still higher than display, do horisontal crop\r\n")
						sh = sw / ASPECT
					}
					fh = (sh < HEIGHT) ? rint(sh, 4) : rint(HEIGHT,  4)
					fw = rint(sw / sh * fh, 8)
				}
				rw = cw - sw
				rh = ch - sh
				printf("\t%dx%d -stretch-crop-> %d(%+d)x%d(%+d) -display-fit-> %dx%d\r\n", w, h, sw, -rw, sh, -rh, fw, fh);
				# calc 4 crop values
				if (rw < 0) rw = 0
				if (rh < 0) rh = 0
				rwl = int(rw / 2)
				rwr = int(rw - rwl)
				rht = int(rh / 2)
				rhb = int(rh - rht)
				# make new filenames
				rsfn = fn
				avifn = fn
				sub(/\.[A-Za-z0-9_]+$/, ".rs", rsfn)
				if (rsfn == fn) rsfn = rsfn ".rs"
				sub(/\.[A-Za-z0-9_]+$/, ".avi", avifn)
				if (avifn == fn) avifn = avifn ".avi"
				# avidemux sript does not like "\" in path
				gsub(/\\/, "/", fn)
				gsub(/\\/, "/", avifn)
				# save run script
				printf("//AD  <- Needed to identify//\r\n")>rsfn
				printf("var app = new Avidemux();\r\napp.load(\"%s\");\r\napp.video.setPostProc(3,3,0);\r\napp.video.addFilter(\"crop\",\"left=%d\",\"right=%d\",\"top=%d\",\"bottom=%d\");\r\napp.video.addFilter(\"resize\",\"w=%d\",\"h=%d\",\"algo=0\");\r\n", fn, rwl, rwr, rht, rhb, fw, fh)>rsfn
				printf("app.video.codecPlugin(\"ЗДЕСЬ*ДОЛЖНЫ*БЫТЬ*НАСТРОЙКИ*КОДЕКА\");\r\n")>rsfn
				printf("app.audio.reset();\r\napp.audio.codec(\"Lame\",128,20,\"80 00 00 00 00 00 00 00 02 00 00 00 05 00 00 00 00 00 00 00 \");\r\napp.audio.normalizeMode=1;\r\napp.audio.normalizeValue=0;\r\napp.audio.delay=0;\r\napp.audio.mixer=\"STEREO\";\r\napp.audio.drc=true;\r\napp.setContainer(\"AVI\");\r\n")>rsfn
				printf("app.save(\"%s\");\r\napp.Exit();\r\n", avifn)>rsfn
				# add to batch
				printf("%s --nogui --run \"%s\"\r\n", AVIDEMUX, rsfn)>BATCH
			}
		}
		close(InfoCommand)
	}
	close(LIST)
}

function rint(v, b) {
	# floor value to specified base
	return int(int(v + 0.5) / b) * b
}