Video transparan dengan shader

Main-main dengan OpenGL shader memang mengasikkan 😀 Katakanlah saya punya sebuah video yang ingin saya tampilkan dalam OpenGL scene. Saya bisa memakai ffmpeg tuk mengekstrak setiap frame yang ada di dalamnya. Frame ini lalu diubah menjadi OpenGL texture sehingga dapat ditampilkan dalam OpenGL scene. GPU dan drivernya zaman sekarang sudah mendukung NPOT Texture alias tekstur yang ukurannya tidak perlu angka dari dua pangkat sekian sehingga setiap frame dari video bisa langsung begitu saja digunakan tanpa harus diubah ukurannya terlebih dahulu.

Kalau cuma menampilkan yang seperti ini, vertex dan fragment shader berikut sudah cukup tuk digunakan. Katakanlah mpv berisi model-view-projection matrix, coord adalah koordinat kotak video (range 0.0-1.0), textureId adalah (tentunya) texture id dari video frame.

attribute vec3 coord;
uniform mat4 mpv;
varying vec2 texcoord;

void main() {
   gl_Position = mpv * vec4(coord, 1.0);
   texcoord = coord;
}
varying vec2 texcoord;
uniform sampler2D textureId;

void main() {
    gl_FragColor = texture2D(textureId, texcoord);
}

Beberapa hari belakangan saya mencari2 cara tuk menampilkan video yang transparan. Satu teknik yang terpikirkan langsung oleh saya adalah dengan menggunakan Chroma Key sehingga saya cukup mencari warna tertentu dan menghapusnya agar tidak ditampilkan. Namun kalau pakai cara ini, bagaimana menampilkan gambar yang tampil/hilang secara bertahap (err.. fade-in/out)?

Setelah merenung lebih lanjut, saya jadi terpikir kalau saya punya akses ke alpha channel saya bisa menggunakan nilai si alpha untuk membuat warna RGB menjadi transparan. Namun apa artinya si video harus menampung gambar dengan channel RGBA? apa format kompresi yang banyak dipakai (spesifiknya h264) itu mendukung RGBA? Untung saja saya menemukan sebuah pertanyaan di StackOverflow yang menjawab pertanyaan saya ini 😀

Intinya, video diubah menjadi seperti berikut ini.

Bagian atas video berisi channel RGB dan bagian bawah video adalah alpha masknya. Kalau sudah begini, si video sendiri bisa dikompresi dengan format apapun karena pada dasarnya si video hanya berisi RGB stream biasa. Namun kita perlu melakukan sedikit usaha tambahan pada saat menampilkannya: ambil setengah gambar atas dan setengah gambar bawah, ambil nilai RGB dari gambar atas, ambil nilai (katakanlah) R dari gambar bawah, gabungkan kedua nilai ini dg menjadikan nilai R dari gambar bawah sebagai nilai Alpha dari warna akhir.

Kalau kalimat terakhir di atas diubah menjadi fragment shader, hasilnya kira2 akan seperti berikut.

varying vec2 texcoord;
uniform sampler2D textureId;

void main() {
    vec2 tc_rgb = texcoord * vec2(1.0, 0.5);
    vec2 tc_alpha = tc_rgb + vec2(0.0, 0.5);
    vec4 frame = texture2D(textureId, tc_rgb)
    vec4 alpha = texture2D(textureId, tc_alpha);
    gl_FragColor = vec4(frame.rgb, alpha.r);
}

Voila! eiya, jangan lupa nyalakan GL_BLEND dan set blend function-nya agar alpha value-nya beneran terpakai.

Bongkar pasang berkas video

Contekan tuk bongkar pasang berkas video pakai ffmpeg

Ekstrak audio stream dan ubah formatnya menjadi AAC

$ ffmpeg -i input.avi -vn -ac 2 -ar 44100 -ab 196k -acodec libvo_aacenc audio.aac

Ekstrak frame dari video stream menjadi PNG sequence:

$ ffmpeg -i input.avi -r 30 -f image2 frame-%010d.png

Gabung sederet gambar PNG menjadi berkas video:

$ ffmpeg -f image2 -i frame-%010d.png -vcodec libx264 -b 128k -r 30 video.mp4

Gabung aliran video dan suara menjadi satu:

$ ffmpeg -i audio.aac -i video.mp4 -acodec copy -vcodec copy result.mp4

Konversi video ke format Flash

Rasanya dulu saya pernah melakukannya.. dan memang pernah =D

Setelah sebelumnya saya membuat screencast, maka agar dapat ditampilkan di web saya perlu mengubah formatnya menjadi format yang umum “diterima”, misalnya Flash Video alias FLV. Urusan konversi ini saya serahkan ke aplikasi mencoder.

Kira2 begini perintahnya..

$ mencoder apalah.avi -o apalah.flv -of lavf -nosound -ovc lavc -lavcopts 
  vcodec=flv:vbitrate=2500:mbd=2:mv0:trell:v4mv:cbp:last_pred=3

Jangan tanya saya apa arti dari setiap parameter yang ada, soalnya saya ngga tau =D Oya, saya sedang membuat screencast tanpa suara jadi memang seluruh opsi yang berhubungan dengan suara tidak saya gunakan.

Tuk ngubah2 kualitas, bisa diatur nilai dari bitrate yang ingin digunakan. Contoh di atas menggunakan bitrate 2500 yang cukup menghasilkan hasil yang bagus untuk kasus saya. Lihat contoh videonya di bawah ini. Garis-garis yang ada cukup terlihat bagus dan tidak terlalu berbercak.

Membuat screencast berkualitas tinggi dengan ffmpeg

Akhirnya saya menemukan salah satu cara untuk membuat screencast berkualitas tinggi, yaitu dengan bantuan ffmpeg. Intinya adalah kita menyuruh si ffmpeg tuk merekam langsung sesi X11 yang ada, nanti si ffmpeg akan langsung meng-encode si video ke file dalam format dan pengaturan yang menurut saya cukup bagus. Entah apa itu 😀

ffmpeg bawaan Ubuntu 8.04 ternyata tidak dapat menangkap layar sehingga kita perlu melakukan kompilasi sendiri. Tutorial kompilasi dapat dibaca di http://gnufied.org/2008/07/14/screencasting-from-linuxubuntu/

Setelah itu kita cukup menjalankan ffmpeg tuk menangkap citra di layar dan merekamnya.

$ ffmpeg -f oss -i /dev/dsp -f x11grab -s 1024x768 -r ntsc 
     -sameq -i :0.0 foo.avi

Opsi2 lain tentu bisa diatur sendiri. Contoh di atas saya ambil dari situs yg saya sebut sebelumnya.

Jika ingin menangkap sebagian layar, atur posisi dan ukuran dengan cara berikut.

$ ffmpeg ... -s LEBARxTINGGI -i :0.0+KIRI,ATAS ...

Jadi andai bagian yang ingin ditangkap adalah area dengan lebar 400×300 yang berada di posisi 100,200, maka ffmpeg dijalankan seperti berikut.

$ ffmpeg -f oss -i /dev/dsp -f x11grab -s 400x300 -r ntsc 
     -sameq -i :0.0+100,200 foo.avi

okeh.. masalah selanjutnya adalah bagaimana cara mengkonversi formatnya menjadi format yang lebih bersahabat dengan web (flv? h264?) dan tetap mempertahankan kualitas.. perburuan masih berlanjut =D