ECMAScript, Avidemux e vídeos da Lilica

Mais uma vez a Lilica fez showzinho nesse sábado. Mas sabe como é? Colocar dúzias de filminhos de 30 segundos no seuTubo é um porre, e no final ninguém vê nenhum deles. (eu ia por o vídeo no final, mas como tem um script enoooooooorme por lá, ninguém ia ver, então resolvi por ele aqui mesmo):

Vídeo da apresentação da Lilica com os comandos mais "legais".

Resolvi então "brincar" de editar os vídeos, pra fazer um videozão resumo do show todo que ficasse mais bacana. Mas que ferramenta usar????


Avidemux

Bom, de cara eu pensei no avidemux. É que tempos atrás quando procurei por um jeito de embutir legendas em vídeos no linux, eu acabei me deparando com ele (no windows eu usava o virtualdub, mas como meu notebook foi doado e eu não uso software pirata, agora estou usando o ubuntu em casa). Quem conhece o virtualdub vai gostar: é praticamente a mesma coisa, só que com uma interface mais amigável. E parece que existe pra windows também.

Em termos de funcionalidades, não chega a ser o bambambam, mas faz quase tudo. Claro que nada é de graça! Dá uma trabalheira fazer qualquer coisa nele. Mas ontem, nessa mexida toda, descobri uma coisa fantástica: Dá pra fazer scripts em ECMAScript que automatizam todo o processo de edição de vídeos!


ECMAScript

ECMAScript, pra quem não sabe, é o famoso Javascript. Quer dizer, quando roda dentro de um browser da Netscape, chama Javascript. Quando roda no IE, chama JScript, e quando roda em qualquer outro lugar chama ECMAScript. Mas é a mesma porcaria!

Scripts pro avidemux são super simples (as opções são bem limitadas) e o único inconveniente que eu achei é que eu tenho de saber o número de frames de cada arquivo com antecedência. Deve ter outro jeito, mas não achei :(. Quem quiser aprender, tem um tutorialzinho bacana em http://www.avidemux.org/admWiki/index.php?title=Scripting_tutorial.

A única coisa chata são os codecs, que pedem uns parâmetros em formatos estronhos! Mas consegui uma manha pra configurá-los: Eu crio um projeto "dummy" pela interface gráfica e mando salvar o projeto. O resultado é um script que eu posso usar como "cola" no meu. No final ficou até bacana. Dá pra otimizar ainda, mas por enquanto ficou assim:

//AD  <- Needed to identify//
 
var app = new Avidemux();
var dir = "/home/girino/Desktop/backup_foto_video/show_lilica_14_dez_2007";
var tmp_prefix = "/tmp/asdf_";
 
var silence_file = dir + "/silence.mp3";
 
var movies = new Array(
dir+"/01-cumprimenta.avi",
dir+"/02-rastejando.avi",
dir+"/03-morta.avi",
dir+"/04-urso_sem_guia.avi",
dir+"/05-urso_com_guia.avi",
dir+"/06-rolando.avi"
);
var sizes = new Array(
199,
622,
1522,
367,
406,
537
);
var images = new Array(
dir+"/slides/01-cumprimenta.png",
dir+"/slides/02-Rastejando.png",
dir+"/slides/03-Morta.png",
dir+"/slides/04-urso1.png",
dir+"/slides/05-urso2.png",
dir+"/slides/06-rolando.png",
dir+"/slides/XX-final.png"
);
 
var final_list = new Array();
var final_sizes = new Array();
var final_fade = new Array();
 
// *** functions *** //
// prepares fade out scenes
function record_temp_video(video, pos, temp, size) {
	print("**** record_temp_video begin");
	print("**here0 = " + video);
        app.load(video);
	print("**here1");
        app.clearSegments();
        for (var i = 0; i < size; i++) {
                app.addSegment(0,pos,1);
        }
	print("**here2");
        app.video.setFps1000(25000);
        app.video.codec("Mjpeg","CQ=4","8 5a 00 00 00 00 00 00 00 ");
        app.audio.reset();
	print("**here3");
        app.audio.load("MP3",silence_file);
        app.audio.codec("lame",64,12,"00 00 00 00 03 00 00 00 02 00 00 00 ");
        app.audio.normalizeMode=0;
        app.audio.normalizeValue=0;
        app.audio.delay=0;
        app.audio.mixer("MONO");
        app.audio.resample=32000;
        app.setContainer("AVI");
	print("**here4");
        app.save(temp);
	print("**** record_temp_video end");
}
 
var tmp_counter = 0
function make_movie(image, length) {
	var tmp_name = tmp_prefix + tmp_counter;
	tmp_counter++;
 
	print("*** make_movie begin");
	// makes the temp video
	record_temp_video(image, 0, tmp_name, length);
	print("*** make_movie end");
 
	return tmp_name;
}
 
function make_fade_out(movie, size, length) {
	var tmp_name = tmp_prefix + tmp_counter;
	tmp_counter++;
 
	print("*** make_fade_out begin");
	// makes the temp video
	record_temp_video(movie, size-1, tmp_name, length);
	print("*** make_fade_out end");
 
	return tmp_name;
}
 
// initializes with 10 frame fade in of first image
final_list[final_list.length] = make_movie(images[0], 10);
final_sizes[final_sizes.length] = 10;
 
var i = 0;
for (i = 0; i < movies.length; i++) {
	// title image
	print("*** i = " + i);
	print("*** images[i] = " + images[i]);
	final_list[final_list.length] = make_movie(images[i], 40);
	final_sizes[final_sizes.length] = 40;
	final_fade[final_fade.length] = 1;
	// movie
	final_list[final_list.length] = movies[i];
	final_sizes[final_sizes.length] = sizes[i];
	final_fade[final_fade.length] = 1;
	// fade out
	final_list[final_list.length] = make_fade_out(movies[i], sizes[i], 10);
	final_sizes[final_sizes.length] = 10;
	final_fade[final_fade.length] = 0;
}
 
// ends with credits
final_list[final_list.length] = make_movie(images[i], 50);
final_sizes[final_sizes.length] = 50;
final_fade[final_fade.length] = 1;
 
// now adds them all in the right order
app.load(final_list[0]);
for (i = 1; i < final_list.length; i++) {
	app.append(final_list[i]);
}
 
// appends segments
app.clearSegments();
for (i = 0; i < final_list.length; i++) {
	print("*** adding:");
	print(i + " : " + final_sizes[i]);
	app.addSegment(i,0,final_sizes[i]);
}
 
// filters
var pos = 0;
// first filter is fade in
pos = pos + final_sizes[0];
app.video.addFilter("fade",
			"startFade="+(pos-10),
			"endFade="+pos,
			"inOut=1",
			"toBlack=1");
for (i = 1; i < final_list.length; i++) {
	pos = pos + final_sizes[i];
	if (final_fade[i] == 1) {
		app.video.addFilter("fade",
				"startFade="+(pos-10),
				"endFade="+pos,
				"inOut=0",
				"toBlack=0");
	}
}
// final fade to black
app.video.addFilter("fade",
		"startFade="+(pos-10),
		"endFade="+pos,
		"inOut=0",
		"toBlack=1");
 
// save video
app.video.codec("FFMpeg4","CBR=1000000","140 05 00 00 00 00 01 00 00 02 00 00 00 1f 00 00 00 " +
                                            "03 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 " +
                                            "00 00 00 00 01 00 00 00 00 00 00 00 cd cc 4c 3d " +
                                            "01 00 00 00 0a d7 23 3c 01 00 00 00 00 00 00 3f " +
                                            "00 00 00 3f 00 00 00 00 00 00 00 00 00 00 00 00 " +
                                            "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " +
                                            "02 00 00 00 40 1f 00 00 00 00 00 00 00 00 00 00 " +
                                            "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " +
                                            "00 00 00 00 00 00 00 00 00 00 00 00 ");
 
// and audio
app.audio.reset();
app.audio.codec("lame",64,12,"00 00 00 00 03 00 00 00 02 00 00 00 ");
app.audio.normalizeMode=0;
app.audio.normalizeValue=0;
app.audio.delay=0;
app.audio.mixer("MONO");
 
// and save
app.setContainer("AVI");
app.save(dir + "/saida.avi");
setSuccess(1);
app.Exit();
 
//DEBUG
print("**** BEGIN ****");
print(movies.length);
print(final_list.length);
print(final_list);
print(final_sizes);
print("**** END ****");
 
 
//End of script

O resultado, vocês já viram lá em cima!

--girino 00:35, 19 Dezembro 2007 (BRST)

Leave a Reply