Generative Videoinstallationen faszinieren mich:
Videoschleifen
Immer wiederholend
Aber nicht immer gleich
Offline kann ich Videos zusammensetzen mit ffmpeg
Das macht aber keinen Spass
Die Videos können nicht verfremdet werden
Ich kann keine Live-Bilder von der Webcam einbinden
OpenGL war ursprünglich ein fester endlicher Automat
zur Darstellung von 3D Grafik
3D Grafik ist etwas mehr als 2D Grafik (wie Videos)
Damit kann ich mehrere Videos leicht überlagern
1: +-Bildschirm-+ 2: +--------+ ^ ^ 3: | Logo |----------+ | | 4: +--------+ | 5: +-> +----Logo----+ 6: +--------+ 7: | Webcam |------------> +---Webcam---+ 8: +--------+ 9: +-> +---Video1---+ 10: +--------+ | 11: | Video1 |----------+ 12: +--------+
Und das mit Hardwareunterstützung, statt die Berechnung in Perl zu machen
Die Render-Pipeline von OpenGL war ursprünglich fest vorgegeben
1: +-Bildschirm-+ 3. Rendering 2: +--------+ ^ ^ 3: | Textur2|----------+ | | 4: +--------+ | 5: +-> +---Textur1--+ 2. Texturing 6: +--------+ 7: | Textur2|------------> +---Textur2--+ 2. Texturing 8: +--------+ 9: +-> +---Textur3--+ 2. Texturing 10: +--------+ | 11: | Textur3|----------+ +---Objekt---+ 1. Raster 12: +--------+
Es gab nur wenige, vorgegebene Funktionen zum Kombinieren von Pixeln bei der Texturierung:
Addition, Multiplikation, Subtraktion, OR, XOR, AND
Maskierung (->Logo)
Seit OpenGL 2.0 (und jetzt OpenGL 3.0) kann man die komplette Pipeline selber programmieren ("Shader")
Jeder Bildpunkt im endgültigen Bild ist im Wesentlichen unabhängig von allen anderen Bildpunkten
Parallele Verarbeitung von vielen unabhängigen Bildpunkten von Perl aus
Interessante Verfremdungs- und Effektfilter
Schwarzweissfilter
Kantenerkennung
Chromakey / Bluescreen / Greenscreen
Statischen Hintergrund vom Bild abziehen
Crossfades zwischen Filmen
Zwei Filme anhand einer Maske/eines dritten Films ineinander übergehen lassen
1: AVI RGB 2: +--------+ +----------+ +------+ 3: | Video | | | | | 4: | quelle | ---> | ffmpeg | ---> | Perl | --> 5: +--------+ +----------+ +------+
1: +--------+ +--------+ +--------+ 2: | | | OpenGL | | Bild- | 3: --> | Textur | --> | Shader | ---> | schirm | 4: +--------+ +--------+ +--------+
1: ^^^ 2: Parameter steuerbar über Perl
(Ring)Puffer
Speichert die letzten 20 Einzelbilder zwischen
Aus ffmpeg
(schnell)
Von der Grafikkarte (langsam)
Delay (1 s)
1: +-------+ +--------------------+ +--------+ 2: | Video | --> | Puffer (20 Bilder) | --> | OpenGL | 3: +-------+ +--------------------+ +--------+
"Nervös"
1: +-------+ +--------------------+ 2: | Video | --> | Puffer (20 Bilder) | 3: +-------+ +--------------------+ 4: ^ +--------+ 5: +----rand(20)--------> | OpenGL | 6: +--------+
Feedback
1: +---------------+ +-------------+ 2: | Puffer | -> | OpenGL | -+-> Ausgabe 3: | (1-...Bilder) | | Verfremdung | | 4: +---------------+ +-------------+ | 5: ^ | 6: +--------<--Kopie----<------+
OpenGL
- OpenGL Anbindung
OpenGL::Shader
- OpenGL Shader/Filter
Source::FFmpeg
- der Decoder für die Videostreams
Filter::Buffer
- Ringpuffer für Feedback/Delay
1. Filme öffnen (Source::FFmpeg)
2. Mainloop
1: my $curr_texture; 2: for my $movie (@movies) { 3: # Bild in Grafikkarte laden 4: $curr_texture = $movie->tick(); 5: # Bild in Ringpuffer laden 6: ... 7: 8: # Textur-Ping-Pong 9: for my $filter (@active_filters) { 10: $curr_texture = $filter->render($texture); 11: }; 12: };
Startet ffmpeg
mit ein paar Kommandozeilenparametern
ffmpeg
gibt den Film nach STDOUT aus
Liefert ein Filehandle zurück, aus dem die einzelnen Bilder gelesen werden
1: sub stream_info { 2: my ($self,$filename) = @_; 3: my ($child_in, $stream, $info); 4: my $cmd = sprintf 5: qq{bin\\ffmpeg-old.exe -t 0 -i "%s" -}, 6: $filename; 7: my $pid = open3 $child_in, $stream, $stream, $cmd 8: or die "Couldn't spawn '$cmd': $!/$?";
1: while (my $line = <$stream>) { 2: #print ">>$line"; 3: if ($line =~ /Video: .*/) { 4: chomp $line; 5: print ">$line<\n"; 6: my ($width,$height) = $line =~ /(\d+)x(\d+)/; 7: return ($width,$height); 8: }; 9: }; 10: };
Parameterüberprüfung
1: sub new { 2: my ($class,%args) = @_; 3: my $file = delete $args{filename}; 4: die "No file: '$file'" 5: unless -f $file; 6: warn "$file seems valid.";
Die Informationen über den Film werden gleich beim Start eingelesen
1: my ($width,$height) = $class->stream_info($file); 2: 3: my $cmd = sprintf 4: qq{bin\\ffmpeg-old.exe -i "%s" } 5: . qq{-f rawvideo -pix_fmt rgb24 - |}, 6: $file; 7: my $pid = open my $stream, "$cmd" 8: or die "Couldn't spawn '$cmd': $!/$?"; 9: binmode $stream;
...
tick()
wird aufgerufen, wenn ein neues Bild eingelesen werden soll
1: sub tick { 2: my ($self) = @_;
1: # Textur anlegen 2: my $texture_id = $self->texture_id; 3: if (! $texture_id) { 4: ($texture_id) = glGenTextures_p(1); 5: $self->texture_id($texture_id); 6: };
1: # Daten lesen 2: my $frame; 3: read $self->stream, $frame, $self->width * $self->height * $self->depth 4: or die "Read failure"; 5: 6: # Übergeben an OpenGL 7: OpenGL::Tools::set_texture_pixels( 8: texture => $texture_id, 9: pixels => $frame, 10: ... 11: );
Wenn das Objekt weggeworfen wird, und
ffmpeg
noch läuft, auch mit ffmpeg
aufräumen.
1: sub DESTROY { 2: if (my $pid = $_[0]->pid) { 3: kill 9 => $pid 4: }; 5: };
Fällt aus, da mein Notebook nur OpenGL 1.3 kann :(
Kein Sound
Keine Pause/Vorwärts/Zurückspulen
Kein (bequemes) Speichern in neuem Format
Benötigt OpenGL, also nicht Server-fähig
Kein Sound
Keine Pause/Vorwärts/Zurückspulen
Einfache Installation
Fragen?