Current Page: | Greybox » Authoring » Course ID: medieninformatik » Modules » Module ID: m06 » Learning Units » Unit ID: 3_03 |
---|---|
Last Modified: | Tuesday, 2015-05-05 - 08:09:01 |
Tools: | Validate — Preview XML Preview HTML Preview PDF |
Alternative: | Printable HTML |
Title: | JMF Plug-Ins - Codecs und Effekte | ||
---|---|---|---|
Abstract: | In dieser LU wird die Funktionsweise von Codecs und Effekten in JMF erläutert. Neben der Vorstellung in JMF vorhandener Codecs und Effekte, wird besonders auf die Gestaltung eigener Codecs und Effekte eingegangen. Es werden Beispiele und lauffähige Applets für Bild-, Video und Audioeffekte präsentiert. | ||
Status: |
|
Version: | 7.1 |
History: |
Acronyme done. Rechtschreibung gecheckt. Unbekannte Character ausgebessert. Sourcecode repariert. Sourcecode formatiert. |
Author 1: | Bernhard Tatzmann | E-Mail: | bernhard@isys.uni-klu.ac.at |
---|---|---|---|
Author 2: | (empty) | E-Mail: | (empty) |
Author 3: | (empty) | E-Mail: | (empty) |
Author 4: | (empty) | E-Mail: | (empty) |
Author 5: | (empty) | E-Mail: | (empty) |
Organization: | Universität Klagenfurt - Institut für Informatik-Systeme |
Einleitung1Auto
Installierten Codecs und Filter werden in der JMF464 Registry angezeigt (Karteikarte "Plug-Ins"): Auto PCAuto PDA_Phone2AutoWie in LU Das Java Media Framework erläutert wird, können diverse Codecs und Effekte in das JMF464 Datenverarbeitungsmodell eingebaut werden. Diese werden ebenso wie das Multiplexen und Demultiplexen durch Plug-Ins realisiert, die in der untersten Ebene der JMF464-Architektur angesiedelt sind. JMF464 stellt bereits einige Codecs und Konverter zur Verfügung und erlaubt die Implementierung neuer Plug-Ins durch das Bereitstellen von Interfaces. Die installierten Codecs und Filter werden in der JMF464 Registry in der Karteikarte "Plug-Ins" angezeigt (siehe folgende Abbildung). Auto PCAuto PDA_PhoneVideo Codecs1Auto
JMF464 stellt nicht für jedes Videoformat, für das ein Decoder bereitgestellt wird, auch einen Encoder zur Verfügung: Video Codecs: Liste aller von JMF 2.1.1 Windows Performance Pack zur Verfügung gestellten Video Codecs (Caption)
JavaRGBConverter und RGBScaler
Beispiel VideoskalierungseffektAnwendungsbeispiel der Klasse RGBScaler//Erzeuge eine Instanz des RGBScaler Codecs RGBScaler scaler = new RGBScaler(new Dimension(20,20)); Codec codecs[] = new Codec[1]; codecs[0] = scaler; //Füge den Codec der Liste der TrackControls hinzu TrackControl tracks[] = processor.getTrackControls(); tracks[0].setCodecChain(codecs); Videoskalierungseffekt
Verwendung des Applets
VCM-Konverter
2AutoSollen Daten verarbeitet werden, die in Form eines komprimierten Datenformats zur Verfügung stehen, so muss zu Beginn des Verarbeitungsprozesses der Decoder stehen. Im Falle von Videodaten wandelt dieser die komprimierten Daten in das RGB302 bzw. YUF304 Format um ED04. Sollen die Daten in einem bestimmten Videoformat in eine Datensenke geschrieben werden oder "gestreamt" werden, so ist es nötig, den erforderlichen Encoder zur Verfügung zu haben. Wie aus folgender Tabelle entnommen werden kann, stellt JMF464 nicht für jedes Videoformat, für das ein Decoder bereitgestellt wird, auch einen Encoder zur Verfügung. JMF464 stellt bereits folgende Video-Codecs bereit: Video Codecs: Liste aller von JMF 2.1.1 Windows Performance Pack zur Verfügung gestellten Video Codecs (Caption)
JavaRGBConverter und RGBScalerAbgesehen von den oben erwähnten Codecs, die sich mit komprimierten Videoformaten beschäftigen, stehen auch zwei Codecs zur Verfügung, mit Hilfe derer es möglich ist, die im RGB Format vorliegenden Daten hinsichtlich ihrer Formateigenschaften (Größe, Farbtiefe, usw.) zu bearbeiten. Der erste dieser beiden Codecs nennt sich JavaRGBConverter und ist im Paket com.sun.media.codec.video.colorspace zu finden. Er funktioniert jedoch nur, wenn er vom Prozessor selbst eingesetzt wird. Dies geschieht z.B. dann, wenn sich Ein- und Ausgabeformat leicht voneinander unterscheiden. Wird der Codec vom Programmierer selbst in Verarbeitungsprozess eingebunden hat dies keine Auswirkungen, da die gesetzten Werte vom Codec-Aufruf des Prozessors wieder überschrieben werden. Im Gegensatz zum Vorigen lässt sich der Codec RGBScaler vom Programmierer gezielt einsetzen. Er ist ebenfalls im colorspace Package enthalten und dient zum Verändern der PAL167-Auflösung. Das Hinzufügen eines Codecs zum Verarbeitungsprozess durch den Programmierer wird folgendermaßen durchgeführt: Hinzufügen eines CodecsTrackControl tracks[] = processor.getTrackControls(); tracks[0].setCodecChain(Codec[]); AutoÜber die Methode getTrackControls erhält man Zugriff auf die entsprechende Spur, die mit einem Codec versehen werden soll. Das Array, das der Methode setCodecChain übergeben wird, enthält alle Codecs, die im Verarbeitungsprozess in vorgegebener Reihenfolge zur Anwendung kommen sollen. Die Verwendung des RGBScaler Codec wird im folgenden Beispielapplet unter Anwendung dieser Methodik gezeigt. Beispiel VideoskalierungseffektAuto//Erzeuge eine Instanz des RGBScaler Codecs RGBScaler scaler = new RGBScaler(new Dimension(20,20)); Codec codecs[] = new Codec[1]; codecs[0] = scaler; //Füge den Codec der Liste der TrackControls hinzu TrackControl tracks[] = processor.getTrackControls(); tracks[0].setCodecChain(codecs); Videoskalierungseffekt
Verwendung des AppletsDas Applet bietet die Möglichkeit der Betrachtung einer über die Appletparameter definierten Videodatei, wobei die Auflösung durch den angewendeten Effekt drastisch reduziert wird. Zum Betrachten des Applets ist eine lokale Installation des Java Media Frameworks(http://java.sun.com/products/java-media/jmf/2.1.1/download.html), sowie eine Webcam nötig. VCM-KonverterEin weiterer bereits in JMF464 enthaltener Codec ist der VCM468 (Video Compression Manager)-Konverter. Dieser Wrapper-Codec ist nur in Windowsversionen von JMF464 enthalten und erlaubt es Codecs zu verwenden, die im Betriebssystem installiert sind (siehe Systemsteuerung - > Geräte Manager), auch wenn gar keine JMF464 Implementierung für diesen Codec zur Verfügung steht. Soll z.B. eine mit DivX323 (Digital Video Express) komprimierte Videodatei gelesen werden, und ist der Decoder im Betriebssystem installiert, so kann mit Hilfe des VCM468-Konverters auf diese Datei zugegriffen werden ED04. Audio Codecs1Auto
Audio Codecs: Liste aller von JMF 2.1.1 Windows Performance Pack zur Verfügung gestellten Audio Codecs (Caption)
ACM-Codec
PCMtoPCM-Codec
Rate-Cvrt-Codec und RCModule-Codec
2AutoJMF464 stellt auch bereits einige Audio Codecs zur Verfügung. Die Kodierung und Dekodierung werden dabei wieder von separaten Klassen durchgeführt, wobei im Gegensatz zum Decoder nicht für jedes Format ein Encoder zur Verfügung steht. Audio Codecs: Liste aller von JMF 2.1.1 Windows Performance Pack zur Verfügung gestellten Audio Codecs (Caption)
AutoZusätzlich zu den hier genannten Codecs enthielten frühere Versionen von JMF464 auch den Codec für die Unterstützung des populären MP395 (MPEG Audio Layer III) Formats. Aus lizenzrechtlichen Gründen musste dieser Codec aber von SUN wieder aus JMF464 entfernt werden ED04. ACM-CodecAnalog zum VCM468-Konverter bei den Video Codecs gibt es auch eine Audio Codec Wrapperklasse. Der Wrapper nennt sich ACM469-Codec (Audio Compression Manager) und ist im Windows Performance Pack der JMF464 Version 2.1.1 in der Klasse com.ibm.media.codec.audio.ACMCodec implementiert. Mit dieser Klasse lassen sich wiederum im Windows Betriebsystem installierte Codecs ohne die Existenz einer JMF464 Implementierung verwenden ED04. PCMtoPCM-CodecDas Pendant zu den Video Codecs JavaRGBConverter und RGBScaler ist bei den Audio Codecs der PCMtoPCM-Codec. Mit Hilfe dieses Codecs lassen sich Ein- und Ausgabeformate innerhalb einer Verarbeitungskette (abgesehen von den Abtastraten) auf das gleiche Format bringen. Ebenso wie der JavaRGBConverter wird auch der PCMtoPCM-Codec von JMF464 automatisch eingesetzt, wenn es notwendig ist ED04. Rate-Cvrt-Codec und RCModule-CodecDa der PCMtoPCM Codec keine Konvertierung der Abtastrate durchführen kann, sind weitere Codecs nötig. Der Rate-Cvrt Codec wandelt verschiedenste Samplingraten in einander um, wogegen der RCMModule Codec vorerst nur Konvertierungen auf 8 kHz, 16 Bit Stereo durchführen kann. Implementierung eigener Codecs und Effekte1Einleitung
Die wichtigsten Methoden zur Implementierung eines Codec oder Effect Interfaces:
2EinleitungIn den folgenden Abschnitten wird auf die Erstellung eigener Codecs und Effekte eingegangen. JMF464 unterstützt dies durch das Bereitstellen zweier Interfaces: javax.media.Codec und javax.media.Effect. Das Codec Interface wird verwendet, wenn sich das Format der Frames ändern soll, das Effect Interface hingegen, wenn sich der Inhalt ändert. Prinzipiell sind die beiden Interfaces aber identisch. Die wichtigsten Methoden zur Implementierung eines Codec oder Effect Interfaces werden im Folgenden beschrieben ED04:
Bildeffekte1Einleitung
Beispiel DrawEffectEffekt lässt den Benutzer bestimmte Bildbereiche eines laufenden Videos durch Interaktion mit der Maus markieren oder aufhellen Einbindung des Effekts im AppletInstantierung eines Prozessors mit dem ersten im System verfügbaren GerätVector device_list = CaptureDeviceManager.getDeviceList(new javax.media.format.RGBFormat()); CaptureDeviceInfo dev_info = (CaptureDeviceInfo) device_list.firstElement(); DataSource source = Manager.createDataSource(dev_info.getLocator()); Processor processor = Manager.createProcessor(source); ... Konfiguration des Prozessors und Zuordnung des eigenen Effekts... DrawEffect draw_effect = new DrawEffect(mask, mode); //Definition der Klasse DrawEffect folgt Codec codecs[] = new Codec[1]; odecs[0] = draw_effect; TrackControl tracks[] = processor.getTrackControls(); tracks[0].setCodecChain(codecs); Veranschaulichung der JMF Verarbeitungskette durch JMF PlugIn Viewer PCVeranschaulichung der JMF Verarbeitungskette durch JMF PlugIn Viewer PDA_PhoneAnbinden eines Players zur Darstellung und starten der beiden Komponenten... player = Manager.createRealizedPlayer(processor.getDataOutput()); player.start(); processor.start(); ... Implementierung des EffektsVariablendeklarationpublic class DrawEffect implements Effect { //Parameter des Effekts private Set mask; private BitSet mode; public final static int MODE_PAINT = 1; public final static int MODE_BRIGHTEN = 2; //Formate private Format input_format; private Format output_format; private Format input_formats[] = new Format[] { new RGBFormat(null, Format.NOT_SPECIFIED, Format.byteArray, Format.NOT_SPECIFIED, 24, 3, 2, 1, 3, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED) }; private Format output_formats[] = new Format[] { new RGBFormat(null, Format.NOT_SPECIFIED, Format.byteArray, Format.NOT_SPECIFIED, 24, 3, 2, 1, 3, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED) }; ... Implementierung des Konstuktors und Methoden zum Setzen und Abfragen der Formatepublic DrawEffect(Set mask, BitSet mode) { this.mask = mask; this.mode = mode; } public Format setInputFormat(Format input_format) { this.input_format = input_format; return this.input_format; } public Format setOutputFormat(Format output_format) { this.output_format = input_format; return this.output_format; } public Format[] getSupportedInputFormats() { return this.input_formats; } public Format[] getSupportedOutputFormats(Format in_format) { return new Format[] { output_formats[0].intersects(input_format)}; } Auto
Autopublic int process(Buffer in_buffer, Buffer out_buffer) { int data_lenght=((RGBFormat)this.output_format).getMaxDataLength(); out_buffer.setLength(in_buffer.getLength()); out_buffer.setFormat(this.output_format); out_buffer.setFlags(in_buffer.getFlags()); out_buffer.setData(new byte[data_lenght]); byte output_data[] = (byte[]) out_buffer.getData(); byte input_data[] = (byte[]) in_buffer.getData(); int width = ((RGBFormat)this.output_format).getSize().width; int height = ((RGBFormat)this.output_format).getSize().height; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int index = linearIndex(x, y); Point p = new Point(x * 50 / width, 50 - y * 50 / height); if (mask.contains(p)) { processPixel(input_data, output_data, index); } else { output_data[index] = input_data[index]; output_data[index + 1] = input_data[index + 1]; output_data[index + 2] = input_data[index + 2]; } } } return BUFFER_PROCESSED_OK; } Auto
Autoprivate void processPixel(byte[] input_data, byte[] output_data, int index) { if (mode.get(MODE_BRIGHTEN)) { output_data[index] = (byte)((((int) input_data[index] & 0xff) + 255) / 2); output_data[index + 1] = (byte)((((int) input_data[index + 1] & 0xff) + 255) / 2); output_data[index + 2] = (byte)((((int) input_data[index + 2] & 0xff) + 255) / 2); return; } if (mode.get(MODE_PAINT)) { output_data[index] = 0; output_data[index + 1] = 0; output_data[index + 2] = (byte) 255; return; } } Annotation von Videodaten
Verwendung des Applets
2EinleitungBildeffekte können entweder auf Bilder oder auf Einzelframes von Videos angewendet werden. Handelt es sich um Videos, so ist weder die Kenntnis der Lage des betroffenen Frames im Video, noch die Kenntnis vorangegangener oder nachfolgender Bilder notwendig, da jedes Bild separat mit dem Effekt belegt wird. Vertreter dieser Effekte sind z.B. Scharf-/Weichzeichner oder aber einfach auch Effekte, die die Bildhelligkeit oder den Kontrast ändern. Solche Effekte sind in fast jeder Bildbearbeitungssoftware enthalten. Im Folgenden soll anhand eines Beispieleffekts nochmals die Methoden eines Effect Interfaces erläutert werden. Die Implementierung eines Codecs würde fast analog erfolgen. Beispiel DrawEffectDer Effekt lässt den Benutzer bestimmte Bildbereiche eines laufenden Videos durch Interaktion mit der Maus markieren oder aufhellen. Initialisierung im AppletZuerst wird ein Processor mit dem ersten im System verfügbaren Video Eingabegerät instanziert. AutoVector device_list = CaptureDeviceManager.getDeviceList(new javax.media.format.RGBFormat()); CaptureDeviceInfo dev_info = (CaptureDeviceInfo) device_list.firstElement(); DataSource source = Manager.createDataSource(dev_info.getLocator()); Processor processor = Manager.createProcessor(source); ... AutoDanach muss der Prozessor konfiguriert werden (Methode configure ; siehe Echtzeiterfassung mit JMF - Capturing). Als nächstes wird der eigene Effekt erzeugt und dem Prozessor zugeordnet: Auto... DrawEffect draw_effect = new DrawEffect(mask, mode); //Definition der Klasse DrawEffect folgt Codec codecs[] = new Codec[1]; codecs[0] = draw_effect; TrackControl tracks[] = processor.getTrackControls(); tracks[0].setCodecChain(codecs); AutoDie folgende Abbildung zeigt das Datenverarbeitungsmodell, das das DrawEffect -Objekt enthält, im JMF464 PlugIn Viewer, welcher zur Veranschaulichung der JMF464 Verarbeitungskette dient. AutoPCAuto PDA_PhoneAutoAnschließend muss die Processor -Instanz realisiert werden (siehe Das Java Media Framework). Um den Ausgang des Prozessors mit dem erzielten Effekt darstellen zu können, wird noch ein Player hinter den Prozessor geschalten. Anschließend können der Player und der Prozessor gestartet werden. Auto... player = Manager.createRealizedPlayer(processor.getDataOutput()); player.start(); processor.start(); ... Implementierung des EffektsZuerst müssen die für die Effect Klasse nötigen Variable deklariert werden. Darunter sind auch die später benötigten, sehr allgemein deklarierten Ein-/Ausgabeformate. Autopublic class DrawEffect implements Effect { //Parameter des Effekts private Set mask; private BitSet mode; public final static int MODE_PAINT = 1; public final static int MODE_BRIGHTEN = 2; //Formate private Format input_format; private Format output_format; private Format input_formats[] = new Format[] { new RGBFormat(null, Format.NOT_SPECIFIED, Format.byteArray, Format.NOT_SPECIFIED, 24, 3, 2, 1, 3, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED) }; private Format output_formats[] = new Format[] { new RGBFormat(null, Format.NOT_SPECIFIED, Format.byteArray, Format.NOT_SPECIFIED, 24, 3, 2, 1, 3, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED) }; ... AutoWeiters müssen der Konstruktor und die für das Setzen bzw. Abfragen der Formate nötigen Methoden implementiert werden. Autopublic DrawEffect(Set mask, BitSet mode) { this.mask = mask; this.mode = mode; } public Format setInputFormat(Format input_format) { this.input_format = input_format; return this.input_format; } public Format setOutputFormat(Format output_format) { this.output_format = input_format; return this.output_format; } public Format[] getSupportedInputFormats() { return this.input_formats; } public Format[] getSupportedOutputFormats(Format in_format) { return new Format[] { output_formats[0].intersects(input_format)}; } AutoDa dieser Effekt das Eingangsformat beibehalten soll, kann man sehen, dass es in der Methode setOutputFormat auch als Ausgangsformat verwendet wird. Der übergebene Parameter in_format der Methode getSupportedOutputFormats wird also ignoriert. Wird die Methode getSupportedOutputFormats zur Ausgabe verfügbarer Ausgangsformate für ein bestimmtes Eingangsformat aufgerufen, wird das oben definierte Ausgangsformat mit dem übergebenen geschnitten retourniert. Dies bedeutet, dass Formatattribute, die in nur in einem der beiden Formate spezifiziert sind direkt ins Ausgabeformat übernommen werden. Ist ein Attribut in beiden Formaten spezifiziert so erhält jenes Formatobjekt auf das die Methode intersect angewendet wird den Vorzug. In der Methode process wird die eigentliche Filteroperation durchgeführt. Um in den Ausgabepuffer schreiben zu können, muss er zunächst initialisiert werden. Dabei wird er unter anderem auf die richtige Größe und das richtige Format gebracht. Nach Umwandlung der beiden Buffer Objekte in ein byte Array kann damit begonnen werden, die einzelnen Pixel vom Eingabearray in das Ausgabearray zu schreiben. Ist ein Pixel in der übergebenen Maske enthalten, so wird es mit es mit Hilfe der Methode processPixel bearbeitet. Ansonsten wird es unverändert übernommen. Dies wird für jede der drei Farbkomponenten durchgeführt. Autopublic int process(Buffer in_buffer, Buffer out_buffer) { int data_lenght=((RGBFormat)this.output_format).getMaxDataLength(); out_buffer.setLength(in_buffer.getLength()); out_buffer.setFormat(this.output_format); out_buffer.setFlags(in_buffer.getFlags()); out_buffer.setData(new byte[data_lenght]); byte output_data[] = (byte[]) out_buffer.getData(); byte input_data[] = (byte[]) in_buffer.getData(); int width = ((RGBFormat)this.output_format).getSize().width; int height = ((RGBFormat)this.output_format).getSize().height; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int index = linearIndex(x, y); Point p = new Point(x * 50 / width, 50 - y * 50 / height); if (mask.contains(p)) { processPixel(input_data, output_data, index); } else { output_data[index] = input_data[index]; output_data[index + 1] = input_data[index + 1]; output_data[index + 2] = input_data[index + 2]; } } } return BUFFER_PROCESSED_OK; } AutoDie Hilfsmethode Methode linearIndex rechnet die zweidimensionalen Pixelkoordinaten in die entsprechende Position im linearen Buffer um. Dabei wird die x-Koordinate mit der Anzahl der pro Pixel benötigten Bytes (pixel_stride) multipliziert und zum Produkt der Zeilennummer mit der Zeilenlänge (line_stride) addiert. Autoprivate int linearIndex(int x, int y) { int pixel_stride = ((RGBFormat)this.output_format).getPixelStride(); int line_stride = ((RGBFormat)this.input_format).getLineStride(); return (x * pixel_stride) + (y * line_stride); } AutoDie Methode processPixel weist einem zu bearbeitenden Pixel einen neuen Farbwert zu. Im Aufhellungsmodus geschieht dies durch Berechnung des Durchschnitts zwischen dem aktuellen Farbwert des Pixels und dem Maximalwert. Um mit den byte Werten rechnen zu können, müssen diese zuerst in den primitiven Typ int umgewandelt werden. Dabei ist zu beachten, dass nur Werte zwischen 0 und 255 Sinn machen. Daher wird eine Bitmaske, die nur die ersten 8 Bits durchlässt, mit dem byte Wert UND-verknüpft. Autoprivate void processPixel(byte[] input_data, byte[] output_data, int index) { if (mode.get(MODE_BRIGHTEN)) { output_data[index] = (byte)((((int) input_data[index] & 0xff) + 255) / 2); output_data[index + 1] = (byte)((((int) input_data[index + 1] & 0xff) + 255) / 2); output_data[index + 2] = (byte)((((int) input_data[index + 2] & 0xff) + 255) / 2); return; } if (mode.get(MODE_PAINT)) { output_data[index] = 0; output_data[index + 1] = 0; output_data[index + 2] = (byte) 255; return; } } AutoDer Zeichenmodus ist einfacher. Er weist dem aktuellen Pixel einfach die Farbe Rot zu. Annotation von Videodaten
Verwendung des AppletsDas Applet bietet die Möglichkeit das Videobild einer an das System angeschlossenen Webcam zu annotieren bzw. punktuell aufzuhellen. Zum Betrachten des Applets ist eine lokale Installation des Java Media Frameworks(http://java.sun.com/products/java-media/jmf/2.1.1/download.html), sowie eine Webcam nötig. Videoeffekte1Einleitung
Beispiel Video Overlay-Effekt
Autopublic int process(Buffer in_buffer, Buffer out_buffer) { out_buffer.copy(in_buffer); byte output_data[] = (byte[]) out_buffer.getData(); int width = ((RGBFormat)this.output_format).getSize().width; for (int y = 0; y < 5; y++) { for (int x = 0; x < (frame_nr - 1) * width / (frame_rate - 1); x++) { int index = linearIndex(x, y); output_data[index] = (byte) 255; output_data[index + 1] = 0; output_data[index + 2] = 0; } } frame_nr++; if (frame_nr > frame_rate) { frame_nr = 1; } return BUFFER_PROCESSED_OK; } Speichern der Bildrate des Videosignalspublic void open() throws javax.media.ResourceUnavailableException { if (input_format instanceof VideoFormat) { frame_rate = ((VideoFormat) input_format).getFrameRate(); } } Zurücksetzen des Bildzählerspublic void reset() { frame_nr = 1; } Video Overlay-Effekt
Verwendung des Applets
Beispiel Stroboskopeffekt
Methode processpublic int process(Buffer in_buffer, Buffer out_buffer) { if (frame_nr % 5 == 0) { key_frame.copy(in_buffer); } out_buffer.copy(key_frame); frame_nr++; return BUFFER_PROCESSED_OK; } Stroboskopeffekt
Verwendung des Applets
2EinleitungDie oben beschriebenen Bildeffekte berücksichtigen für die Berechnung nur den Inhalt des aktuellen Bildes. Videoeffekte hingegen nehmen auch Bezug auf vorangegangene Bilder oder zumindest auf die zeitliche Lage des aktuellen Bildes. Im Folgenden wird nun für jede Methode ein Beispiel gezeigt. Beispiel Video Overlay-EffektIn diesem Beispiel wird ein Effekt präsentiert, der am unteren Bildrand des Videos einen animierten Balken anzeigt. Der Balken repräsentiert die Anzahl der Frames die in der aktuellen Sekunde bereits angezeigt wurden. Dazu ist es nötig, einen Zähler in der process Methode des Effekts mitlaufen zu lassen, der die Frames zählt, die ja für jeden Frame einmal aufgerufen wird. Da sich nur ein kleiner Teil des Bildes verändert, kann zuerst mit Hilfe der copy Methode der Eingangspuffer in den Ausgangspuffer kopiert werden. Anschließend wird die aktuelle Länge des Balkens berechnet und der Balken gezeichnet. Autopublic int process(Buffer in_buffer, Buffer out_buffer) { out_buffer.copy(in_buffer); byte output_data[] = (byte[]) out_buffer.getData(); int width = ((RGBFormat)this.output_format).getSize().width; for (int y = 0; y < 5; y++) { for (int x = 0; x < (frame_nr - 1) * width / (frame_rate - 1); x++) { int index = linearIndex(x, y); output_data[index] = (byte) 255; output_data[index + 1] = 0; output_data[index + 2] = 0; } } frame_nr++; if (frame_nr > frame_rate) { frame_nr = 1; } return BUFFER_PROCESSED_OK; } AutoIn der open Methode muss die Bildrate des Videosignals berechnet werden. Autopublic void open() throws javax.media.ResourceUnavailableException { if (input_format instanceof VideoFormat) { frame_rate = ((VideoFormat) input_format).getFrameRate(); } } AutoDie reset Methode setzt den Bildzähler zurück. Autopublic void reset() { frame_nr = 1; } Video Overlay-Effekt
Verwendung des AppletsDas Applet bietet die Möglichkeit der Betrachtung einer über die Appletparameter definierten Videodatei, wobei am unteren Bildrand die Anzahl der in der aktuellen Sekunde bereits dargestellten Frames durch einen Balken repräsentiert wird. Zum Betrachten des Applets ist eine lokale Installation des Java Media Frameworks(http://java.sun.com/products/java-media/jmf/2.1.1/download.html), sowie eine Webcam nötig. Beispiel StroboskopeffektIn diesem Beispiel werden im Gegensatz zu obigem auch vorhergehende Frames berücksichtigt. Im Detail werden spezielle Schlüsselbilder ("Keyframes") gespeichert und dann für eine bestimmte Zeit statt dem aktuellen Frame dargestellt. Dies führt zu einem strobuskopartigen Effekt. Die Methode process kopiert bei jedem fünften Frame den Eingangspuffer in den Keyframepuffer. Zur Darstellung wird dann jedes Mal der Keyframepuffer in den Ausgangspuffer kopiert. Autopublic int process(Buffer in_buffer, Buffer out_buffer) { if (frame_nr % 5 == 0) { key_frame.copy(in_buffer); } out_buffer.copy(key_frame); frame_nr++; return BUFFER_PROCESSED_OK; } Stroboskopeffekt
Verwendung des AppletsDas Applet bietet die Möglichkeit der Betrachtung einer über die Appletparameter definierten Videodatei, wobei die Bildrate reduziert wird. Zum Betrachten des Applets ist eine lokale Installation des Java Media Frameworks(http://java.sun.com/products/java-media/jmf/2.1.1/download.html), sowie eine Webcam nötig. Audioeffekte1Einleitung
Beispiel Sinuseffekt
Variable Definition der Eingangs- und Ausgangsformatepublic AudioEffect() { this(Format.NOT_SPECIFIED); } public AudioEffect(int num_of_chan) { this.input_formats = new Format[]{ new AudioFormat( AudioFormat.LINEAR, Format.NOT_SPECIFIED, 16, num_of_chan, AudioFormat.LITTLE_ENDIAN, AudioFormat.SIGNED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.byteArray) }; this.output_formats = new Format[]{ new AudioFormat( AudioFormat.LINEAR, Format.NOT_SPECIFIED, 16, num_of_chan, AudioFormat.LITTLE_ENDIAN, AudioFormat.SIGNED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.byteArray) }; } Auto
Methode processpublic int process(Buffer in_buffer, Buffer out_buffer) { byte input_data[] =(byte[]) in_buffer.getData(); int offset = in_buffer.getOffset(); int length = in_buffer.getLength(); out_buffer.setFormat(this.output_format); out_buffer.setFlags(in_buffer.getFlags()); byte data[]=new byte[length]; out_buffer.setData(data); byte output_data[] =(byte[]) out_buffer.getData(); int num_of_channels = this.output_format.getChannels(); int num_of_samples = length / (2*num_of_channels); int input_index = offset; int output_index = 0; for(int i=0;i<num_of_samples;i++) for(int j=0;j<num_of_channels;j++) { //Zusammensetzen des Samples. Oderverknüpfung //der niederwertigen Bits [i] //und der 8 höherwertigen Bits [i+1] (geshiftet <<8) int sample_value = (input_data[input_index] & 0xff)| (input_data[input_index+1]<<8); //Verändern der Lautstärke durch Multiplikation mit Sinus sample_value*=Math.sin(frame_nr*0.5); output_data[output_index] = (byte)sample_value; output_data[output_index+1] = (byte)(sample_value>>8); input_index+=2; output_index+=2; } out_buffer.setOffset(0); out_buffer.setLength(output_index); frame_nr++; return BUFFER_PROCESSED_OK; } Audio-Sinuseffekt
Verwendung des Applets
2EinleitungFür die Implementierung von Audioeffekten werden die gleichen Interfaces verwendet wie für Videoeffekte. Auch die Zuweisung eines Effekts zum Processor Objekt erfolgt über dieselben Methoden (setCodecChain), wie sie für Videoeffekte verwendet werden. Im Folgenden wird nun anhand eines Beispiels auf die Implementierung von Audioeffekten im Detail eingegangen. Beispiel SinuseffektDas Bespiel SinusEffect definiert einen Filter, der die Lautstärke eines Audiosignals nach einer Sinusfunktion verändert. Da die Anzahl der Kanäle variabel sein soll, werden die Eingangs- und Ausgangsformate im Gegensatz zu den obigen Videofiltern hier im Konstruktor unter Berücksichtigung dieser Parameter definiert. Autopublic AudioEffect() { this(Format.NOT_SPECIFIED); } public AudioEffect(int num_of_chan) { this.input_formats = new Format[]{ new AudioFormat( AudioFormat.LINEAR, Format.NOT_SPECIFIED, 16, num_of_chan, AudioFormat.LITTLE_ENDIAN, AudioFormat.SIGNED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.byteArray) }; this.output_formats = new Format[]{ new AudioFormat( AudioFormat.LINEAR, Format.NOT_SPECIFIED, 16, num_of_chan, AudioFormat.LITTLE_ENDIAN, AudioFormat.SIGNED, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, Format.byteArray) }; } AutoDie Format-get/set-Methoden werden wieder gleich wie bei den Videofiltern implementiert. Der Zugriff auf die Audiodaten in der process Methode erfolgt hingegen etwas anders. Autopublic int process(Buffer in_buffer, Buffer out_buffer) { byte input_data[] =(byte[]) in_buffer.getData(); int offset = in_buffer.getOffset(); int length = in_buffer.getLength(); out_buffer.setFormat(this.output_format); out_buffer.setFlags(in_buffer.getFlags()); byte data[]=new byte[length]; out_buffer.setData(data); byte output_data[] =(byte[]) out_buffer.getData(); int num_of_channels = this.output_format.getChannels(); int num_of_samples = length / (2*num_of_channels); int input_index = offset; int output_index = 0; for(int i=0;i<num_of_samples;i++) for(int j=0;j<num_of_channels;j++) { //Zusammensetzen des Samples. Oderverknüpfung //der niederwertigen Bits [i] //und der 8 höherwertigen Bits [i+1] (geshiftet <<8) int sample_value = (input_data[input_index] & 0xff)| (input_data[input_index+1]<<8); //Verändern der Lautstärke durch Multiplikation mit Sinus sample_value*=Math.sin(frame_nr*0.5); output_data[output_index] = (byte)sample_value; output_data[output_index+1] = (byte)(sample_value>>8); input_index+=2; output_index+=2; } out_buffer.setOffset(0); out_buffer.setLength(output_index); frame_nr++; return BUFFER_PROCESSED_OK; } AutoZunächst muss darauf geachtet werden, dass mit dem Lesen der Inputdaten erst beim Offset des Eingangspuffers begonnen wird. In zwei verschachtelten Schleifen über die Anzahl der Samples und die Anzahl der Kanäle wird dann auf die Daten aus dem Eingangspuffer zugegriffen. Ein Sample erstreckt sich dabei über zwei Indizes des byte Arrays, da mit einer Samplerate von 16 Bits(also 2 Bytes) gearbeitet wird. Die niederwertigen Bits kommen dabei immer vor den höherwertigen 8. Durch Shiften der zweiten 8 Bits lässt sich ein Integer erzeugen, dessen Wert dem Sample entspricht. Durch modifizieren dieses Wertes lässt sich dann auf das Signal Einfluss nehmen. In diesem Fall wird dieser Wert mit dem Sinus der halben Frame-Nummer multipliziert. Dadurch verändert sich die Lautstärke wellenförmig. Anschließend kann der verändertet Samplewert in den Ausgangsbuffer geschrieben werden. Audio-Sinuseffekt
Verwendung des AppletsDas Applet bietet die Möglichkeit des Abspielens einer über die Appletparameter definierten WAV Datei, wobei die Lautstärke sinusförmig verändert wird. Zum Betrachten des Applets ist eine lokale Installation des Java Media Frameworks(http://java.sun.com/products/java-media/jmf/2.1.1/download.html), sowie eine Webcam nötig. Bibliographie2AutoAG04 ED04 |
(empty) |