# technologie für video, webcam & co



## michaelhh (4. Mai 2011)

hallo,

ich habe bereits einige jahre java erfahrung und möchte mich jetzt in den multimediabereich einarbeiten. ich möchte ein programm schreiben welches über den browser aufgerufen werden kann und dem user die möglichkeit bietet sich selbst über seine webcam aufzunehmen. das gemachte video soll entweder direkt auf den server geladen werden.
2. plane ich eine desktop anwendung welche es ermöglicht das video lokal zu speichern und dann per klick auf den webserver überträgt.

da ich mich bei den ganzen "lets" (servlets, applets,..) noch nicht so ganz durchblicke würde ich bisschen rat brauchen. ich habe einen server wo tomcat läuft. dh für die direkte übertragung werde ich wohl red5 brauchen? für das lokale speichern würde ich dann bei der übertragung eigentlich nur ein php file brauchen welches den dann die daten entgegennimmt und abspeichert.

nur was verwende ich für die webversion? jmf, fmj, applets, jsp,...

herzlichen dank im voraus!


----------



## Kr0e (5. Mai 2011)

jmf + fmj sind tot. Setze nicht auf veraltete und längst überholte Technologien.

Es gibt eigentlich nur eine Möglichkeit mit Java vernünftig mit Medien umzugehen, erfordert aber viel
Einarbeitung und ein umfangreiches Wissen über die GStreamer-Library -> Lohnt sich aber!

Ich rede von GStreamer-Java, welches ein Wrapperprojekt ist, um die GStreamer-Library zu benutzen.
Das Problem an Medien ist, dass die effiziente Wiedergabe höchstgradig nativ ist und überall anders funktioniert.

GStreamer schafft ein Abstraktionslayer über den ganzen langweiligen Kram  und bietet dir ein flexibles und
ausgereiftes API. 

Ich habe es damit gescahfft, auch HD Videos 1980*1080 mit knapp 5 % CPU Last darzustellen... Java hat da nämlich zum
Glück nicht mehr die Finger drin, sonst wären es 100% CPU Last 

Das Problem ist, dass Java eben in einer virtuellen Maschine läuft, die vom Arbeitsspeicher abgekapselt ist... Sicher es gibt die ByteBuffer , die schon der Schritt in die richtige Richtung sind... Aber bei HD z.B. treten irrwitzige Datenraten / sekunde auf, wo Java einfach schlapp machen würde, denn das Umkopieren, vom Arbeitsspeicher in die VM rein frisst dann endlos CPU-Zeit.

Mit Gstreamer kann man native Speicherbereiche direkt an OpenGL z.B. übergeben... 

Schaus dir mal an


----------



## Friedhelm (5. Mai 2011)

Also ich habe mir GStreamer vor Monaten angesehen, bin damit aber garnicht klar gekommen.

Hast Du ein Implementierungsbeispiel wie Du es geschafft hast HD Videos 1980*1080 mit 5% CPU Last darzustellen?

Ich meine, wenn Du es geschafft hast, sind bestimmt nicht wenig an einer Lösung interessiert. Ich zum Beispiel, wie schon angesprochen, habe es nicht hinbekommen.


----------



## Kr0e (5. Mai 2011)

Hallo,

selbstverständlich habe ich den. Ich arbeite z.T. an einem Multimedia-Online-Platform Programm, wo User 3D durch Räume laufen können und Medien mit anderen teilen können. Diese Software ist aber noch nicht fertig... Ich will mir auch noch offen halten, zu welcher Lizenz ich dieses Programm veröffentlichen werde... Aber ich werde hier den Teil posten, der das performante Rendern mit Java ermöglicht:

Das hier ist der Basiscode einer kleinen Testanwendung (OpenGL Video Renderer mit LWJGL)

```
package lwjglvideorenderer.opengl.lwjgl.media.test;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.concurrent.TimeUnit;
import javax.swing.JFileChooser;
import lwjglvideorenderer.opengl.lwjgl.media.OpenGLMediaPlayer;
import org.gstreamer.ClockTime;
import org.gstreamer.io.ReadableByteChannelSrc;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

/**
 *
 * @author Christopher Probst
 */
public class BasicLWJGLTest2 {

    final OpenGLMediaPlayer player = new OpenGLMediaPlayer();
    int k = 0;

    public BasicLWJGLTest2() throws Exception {
        JFileChooser f = new JFileChooser();
        f.showOpenDialog(null);
        File ff = f.getSelectedFile();


        int targetWidth = 800;
        int targetHeight = 600;

        DisplayMode chosenMode = null;

        try {
            DisplayMode[] modes = Display.getAvailableDisplayModes();

            for (int i = 0; i < modes.length; i++) {
                if ((modes[i].getWidth() == targetWidth) && (modes[i].getHeight() == targetHeight)) {
                    chosenMode = modes[i];
                    break;
                }
            }
        } catch (LWJGLException e) {
            Sys.alert("Error", "Unable to determine display modes.");
            System.exit(0);
        }

        if (chosenMode == null) {
            Sys.alert("Error", "Unable to find appropriate display mode.");
            System.exit(0);
        }

        try {
            Display.setDisplayMode(chosenMode);
            Display.setTitle("An example title...");
            Display.setVSyncEnabled(true);
            Display.create();


        } catch (LWJGLException e) {
            System.exit(0);
        }


        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();

        float ratio = (float) Display.getDisplayMode().getWidth() / Display.getDisplayMode().getHeight();

        //Perspective etc...
        GL11.glFrustum(-ratio, ratio, -1, 1, 1, 100);
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glLoadIdentity();
        GL11.glViewport(0, 0, Display.getDisplayMode().getWidth(), Display.getDisplayMode().getHeight());

        GL11.glClearColor(1, 1, 1, 1);

        boolean gameRunning = true;

        //Enable textures
        GL11.glEnable(GL11.GL_TEXTURE_2D);



        System.out.println(player.getGStreamerPlayer().getPipeline().getClock().getTime());


        ReadableByteChannelSrc srcChannel = new ReadableByteChannelSrc(new ReadableByteChannel() {

            public int read(ByteBuffer dst) throws IOException {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            public boolean isOpen() {
                throw new UnsupportedOperationException("Not supported yet.");
            }

            public void close() throws IOException {
                throw new UnsupportedOperationException("Not supported yet.");
            }
        }, "bla");

        player.getGStreamerPlayer().setURI(null);

        player.getGStreamerPlayer().play();



        float scale = 10;
        float rotate = 0;


        GL11.glClearColor(0, 0, 0, 0);

        while (gameRunning) {



            GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

            if (Mouse.isButtonDown(0)) {
                int x = Mouse.getX();
                float percent = ((float) x) / Display.getDisplayMode().getWidth();


                player.getGStreamerPlayer().pause();

                long dur = player.getGStreamerPlayer().getPipeline().queryDuration(TimeUnit.SECONDS);

                long secs = Math.round(dur * percent);


                player.getGStreamerPlayer().getPipeline().seek(ClockTime.fromSeconds(secs));

                player.getGStreamerPlayer().play();
            }

            //keyinput
            if (Keyboard.isKeyDown(Keyboard.KEY_LEFT)) {
                rotate += 0.3f;
            } else if (Keyboard.isKeyDown(Keyboard.KEY_RIGHT)) {
                rotate -= 0.3f;
            }

            //keyinput
            if (Keyboard.isKeyDown(Keyboard.KEY_ADD)) {
                scale += 0.07f;
            } else if (Keyboard.isKeyDown(Keyboard.KEY_MINUS)) {
                scale -= 0.07f;
            }

            try {
                Thread.sleep(10);
            } catch (Exception e) {
            }


            player.getPBO().render();


            float ratio2 = player.getPBO().getRatio();
            GL11.glPushMatrix();
            GL11.glTranslatef(0, 0, -15);
            GL11.glRotatef(rotate, 0, 1, 0);
            GL11.glScalef(scale, scale, 0);

            if (player.getPBO().getPBOTexture() != null) {
                player.getPBO().getPBOTexture().bind();


                GL11.glBegin(GL11.GL_QUADS);
                GL11.glTexCoord2f(0, 0);

                GL11.glVertex3f(-1, ratio2, -2);

                GL11.glTexCoord2f(1, 0);

                GL11.glVertex3f(1, ratio2, -2);

                GL11.glTexCoord2f(1, 1);

                GL11.glVertex3f(1, -ratio2, -2);

                GL11.glTexCoord2f(0, 1);

                GL11.glVertex3f(-1, -ratio2, -2);
                GL11.glEnd();

                try {
                    Thread.sleep(10);
                } catch (Exception e) {
                }
            }
            // now tell the screen to update
            GL11.glPopMatrix();

            Display.update();

            // finally check if the user has requested that the display be
            // shutdown
            if (Display.isCloseRequested()) {
                gameRunning = false;
                player.getPBO().dispose();
                player.getGStreamerPlayer().stop();

                Display.destroy();
                System.exit(0);
            }
        }
    }
}
```

Das ist wie gesagt nur eine Testdatei, um das eigentlich wichtige zu testen:
Um mit Java extremst schnell und vorallem hardware beschleunigt Videos darzustellen,
braucht man OpenGL und damit verbunden das PixelBuffeRObject. Damit ist es möglich,
Texturinhalt zu streamen und zwar asynchron, sprich du musst nicht warten, opengl macht das im Hintergrund...
Aber es gibt noch einen weiteren Vorteil: DMA - Direct Memory Access wodruch native Buffer benutzt können!

Hier der OpenGLMediaPLayer zum Abspielen mit GSTreamer

```
package lwjglvideorenderer.opengl.lwjgl.media;

import java.nio.IntBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.gstreamer.Gst;
import org.gstreamer.elements.RGBDataSink;
import org.gstreamer.elements.RGBDataSink.Listener;
import org.gstreamer.media.PlayBinMediaPlayer;

/**
 *
 * @author Christopher Probst
 */
public final class OpenGLMediaPlayer implements Listener {

    private final PBO pbo = new PBO();
    private final PlayBinMediaPlayer gstreamerPlayer;

    public void rgbFrame(int width, int height, IntBuffer rgb) {
        pbo.update(rgb, width, height);
    }

    static {
        //Init GStreamer!!!
        Gst.init();
    }

    public OpenGLMediaPlayer() {


        //Use RGB data sink
        RGBDataSink videosink = new RGBDataSink("OpenGLMediaPlayer", this);

        //Very important! Use direct buffers, so the slow JVM is not involved!! Jiha :=)
        videosink.setPassDirectBuffer(true);

        //Default lateness
        videosink.getSinkElement().setMaximumLateness(20, TimeUnit.MILLISECONDS);
        videosink.getSinkElement().setQOSEnabled(true);

        //Create default media player
        gstreamerPlayer = new PlayBinMediaPlayer("OpenGLMediaPlayer",
                Executors.newCachedThreadPool());
        gstreamerPlayer.setVideoSink(videosink);
    }

    public PlayBinMediaPlayer getGStreamerPlayer() {
        return gstreamerPlayer;
    }

    public PBO getPBO() {
        return pbo;
    }
}
```


Hier ist das Herzstück: Das PBO Object


```
package lwjglvideorenderer.opengl.lwjgl.media;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL21;
import sun.nio.ch.DirectBuffer;

/**
 *
 * @author Christopher Probst
 */
public final class PBO {

    //PBO id
    private int pboId = 0;
    //PBO texture
    private volatile PBOTexture pboTexture = null;
    //Mapped byte buffer
    private ByteBuffer mappedBuffer = null;
    //Do we need an upate ?
    private boolean update = false;
    //Do we need a resize ?
    private volatile boolean resize = false;
    //Size vars
    private int size = 0;
    private int width = 0;
    private int height = 0;
    //Indicates that this PBO was used at least once
    private boolean init = false;

    public synchronized boolean update(IntBuffer pixelBuffer, int width, int height) {
        //Calc pixel count
        int pixelCount = width * height;

        //Calc frame size
        int frameSize = pixelCount * 4;

        //Wrong size...
        if (pixelBuffer.remaining() != pixelCount
                || pixelBuffer.remaining() == 0) {
            return false;
        }

        //Adjust size if necessary
        if (size != frameSize) {
            //Get size
            size = frameSize;

            //Get sizes
            this.width = width;
            this.height = height;

            //Set update to false
            update = false;

            //Set resize to true
            //VOLATILE - FLUSH!
            resize = true;
        } else if (!resize) {
            //Adjust order to speed up pixel transfer
            mappedBuffer.order(pixelBuffer.order());

            //At first clear the buffer
            mappedBuffer.clear();

            //Copy buffer
            mappedBuffer.asIntBuffer().put(pixelBuffer);

            //Rewind the buffer
            mappedBuffer.rewind();

            //Simply set update to true
            update = true;
        }

        return true;
    }

    public int getHeight() {
        return height;
    }

    public int getSize() {
        return size;
    }

    public int getWidth() {
        return width;
    }

    public float getRatio() {
        if (getWidth() == 0) {
            return 0;
        }

        return getHeight() / (float) getWidth();
    }

    public boolean isInit() {
        return init;
    }

    public PBOTexture getPBOTexture() {
        return pboTexture;
    }

    public synchronized void dispose() {
        //Remove buffers
        IntBuffer ids = BufferUtils.createIntBuffer(1);
        ids.put(pboId);
        ids.flip();
        GL15.glDeleteBuffers(ids);
        pboId = 0;

        //Destroy buffers
        pboTexture.destroy();
        pboTexture = null;

        //Reset vars
        mappedBuffer = null;
        update = false;
        resize = false;
        size = 0;
        width = 0;
        height = 0;
        init = false;
    }

    public synchronized void render() {
        //At first check buffer id!
        if (pboId == 0) {
            //Generate buffer
            IntBuffer ids = BufferUtils.createIntBuffer(1);
            GL15.glGenBuffers(ids);
            pboId = ids.get();
        }

        //Create texture
        if (pboTexture == null) {
            pboTexture = new PBOTexture();
        }

        //Initialize buffer
        if (resize) {

            //Create texture
            pboTexture.bind();

            //Bind buffer
            GL15.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, pboId);

            //Initialize buffer size
            GL15.glBufferData(GL21.GL_PIXEL_UNPACK_BUFFER, size, GL15.GL_STREAM_DRAW);

            //Load image data
            pboTexture.loadTexture(width, height, 0);

            //Map new buffer!
            mappedBuffer = GL15.glMapBuffer(GL21.GL_PIXEL_UNPACK_BUFFER,
                    GL15.GL_WRITE_ONLY,
                    mappedBuffer);

            //Unbind buffer
            GL15.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);

            //Unbind texture
            pboTexture.unbind();

            //First use!
            init = true;

            //Now we are initialized
            resize = false;
        } else if (update) {
            //Bind the texture
            pboTexture.bind();

            //Bind buffer
            GL15.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, pboId);

            //Then unmap the active buffer
            GL15.glUnmapBuffer(GL21.GL_PIXEL_UNPACK_BUFFER);

            //Upate texture with DMA!
            pboTexture.updateTexture(0, 0, width, height, 0);

            GL15.glBufferData(GL21.GL_PIXEL_UNPACK_BUFFER, size, GL15.GL_STREAM_DRAW);

            long d = System.nanoTime();

            if (mappedBuffer != null) {
                System.out.println("Old address: " + ((DirectBuffer) mappedBuffer).address());
            }


            //Map new buffer directly
            mappedBuffer = GL15.glMapBuffer(GL21.GL_PIXEL_UNPACK_BUFFER,
                    GL15.GL_WRITE_ONLY,
                    null);

            if (mappedBuffer != null) {
                System.out.println("New address: " + ((DirectBuffer) mappedBuffer).address());
            }


            d = System.nanoTime() - d;
            System.out.println("Update time : " + d / 1000000);

            //Unbind buffer
            GL15.glBindBuffer(GL21.GL_PIXEL_UNPACK_BUFFER, 0);

            //Unbind texture
            pboTexture.unbind();

            //Update is finished
            update = false;
        }
    }
}
```


Und natürlich ein wichtiges Kernstück ... Die PBO Texture


```
package lwjglvideorenderer.opengl.lwjgl.media;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;

/**
 *
 * @author Christopher Probst
 */
public class PBOTexture {

    public static final int LITTLE_PIXEL_FORMAT = GL12.GL_BGRA;
    public static final int BIG_PIXEL_FORMAT = GL11.GL_RGBA;
        public static final int NATIVE_PIXEL_FORMAT = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN
            ? LITTLE_PIXEL_FORMAT : BIG_PIXEL_FORMAT;
    private final int textureID;
    private volatile int pixelFormet = NATIVE_PIXEL_FORMAT;
    private int width = -1, height = -1;

    public PBOTexture() {
        IntBuffer buf = BufferUtils.createIntBuffer(1);
        GL11.glGenTextures(buf);
        buf.rewind();
        textureID = buf.get();
    }

    public float getRatio() {
        if (getWidth() == 0) {
            return 0;
        }

        return getHeight() / (float) getWidth();
    }

    public int getHeight() {
        return height;
    }

    public int getWidth() {
        return width;
    }

    public int getPixelFormet() {
        return pixelFormet;
    }

    public void setPixelFormet(int pixelFormet) {
        this.pixelFormet = pixelFormet;
    }

    public void updateTexture(int x, int y, int w, int h, IntBuffer data) {
        GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, x, y, w, h,
                getPixelFormet(), GL11.GL_UNSIGNED_BYTE, data);
    }

    public void updateTexture(int x, int y, int w, int h, ByteBuffer data) {
        GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, x, y, w, h,
                getPixelFormet(), GL11.GL_UNSIGNED_BYTE, data);
    }

    public void updateTexture(int x, int y, int w, int h, long pixelBufferOffset) {
        GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, x, y, w, h,
                getPixelFormet(), GL11.GL_UNSIGNED_BYTE, pixelBufferOffset);
    }

    public void loadImage(BufferedImage image) {
        DataBufferInt ints = (DataBufferInt)image.getData().getDataBuffer();
        loadTexture(image.getWidth(), image.getHeight(), IntBuffer.wrap(ints.getData()));
    }

    public void loadTexture(int width, int height, IntBuffer data) {
        this.width = width;
        this.height = height;

        //Use specific filters
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);

        //Set image data
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height,
                0, getPixelFormet(), GL11.GL_UNSIGNED_BYTE, data);
    }

    public void loadTexture(int width, int height, ByteBuffer data) {
        this.width = width;
        this.height = height;

        //Use specific filters
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);

        //Set image data
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height,
                0, getPixelFormet(), GL11.GL_UNSIGNED_BYTE, data);
    }

    public void loadTexture(int width, int height, long pixelBufferOffset) {
        this.width = width;
        this.height = height;

        //Use specific filters
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);

        //Set image data
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width, height,
                0, getPixelFormet(), GL11.GL_UNSIGNED_BYTE, pixelBufferOffset);
    }

    /**
     * Destroys this Texture, reclaiming all resources
     */
    public void destroy() {
        IntBuffer scratch = BufferUtils.createIntBuffer(1);
        scratch.put(0, textureID);
        GL11.glDeleteTextures(scratch);
    }

    /**
     * @return Texture ID for this image
     */
    public int getTextureID() {
        return textureID;
    }

    /**
     * Binds this image
     */
    public void bind() {
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
    }

    public void unbind() {
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
    }
}
```


Damit sind alle Formate abspielbar ohne nennenswerte CPU-Auslastung... Wobei das natürlich auf den Rechner ankommt!
Mit einem Singlecore 1,6 GHZ wirste trotzdem 100% Auslatung haben  Das würde dann aber uach mit nativen Playern passieren...


Aber wie ich schon sagte... Das ist ne Menge Arbeit und setzt eben etwas an Wissen voraus... GStreamer ist dafür da, um die Bilder zu dekodieren.. darstellen musst du shcon selbst 

Gruß,

Chris

PS:

Benutzt den oben geposteten Code nach freiem Belieben...

Als Beispiel habe ich das BigBuckBunny Video mit 1080p genommen, welches auf meinem Q8300 mit einer gts250 kaum auslastung verursachte...


----------



## michaelhh (5. Mai 2011)

danke für die ausführliche erklärung.
dh ich benötige entweder eine flash anwendung mit red5 und tomcat
oder ich arbeite mich in die von dir genannte library ein.

das finde ich echt erschreckend, da dies eigentlich eine simple "grundfunktion" sein sollte nicht nur bilder aus der webcam zu bekommen sondern ganze filme...


----------



## Kr0e (5. Mai 2011)

Du darfst nicht vergessen, dass Java eben plattformunabhängig ist... Wir können froh sein, dass es überhaupt möglich ist,
Code schreiben zu können, der zwar bei weitem komplexer und aufwendiger ist, aber dafür überall lauffähig ist... und das dann auch noch hardwarebeschleunigt...

Achja, und Java hat auch viele Stärken, die du dafür bei anderen Sprachen vergeblich suchst! Threading wäre da ein Thema... C# hat keine Volatile-Variablen im ursprünglichen Sinn z.B. und nur sehr primitive Locking Funktionen. C++ kann überhaupt kein Multithreading von haus aus und hat auch kein Memorymodell wie Java.. Du musst halt als Programmierer abwägen was du machen musst und machen kannst.. Dementsprechend wählst du heutzutage die Sprache ...

Aber du hast generell schon Recht, Sun hat damals einfach verpasst, eine schöne Media-Unterstützung auf die Bein zu stellen...
Ich meine, man kann von einem Programmierer echt nicht erwarten, eine derart schlecht konzipierte Library für Medienwiedergabe zu benutzen, wie JMF z.B.... Das ist einfach Krampf... Bei JavaFX wurde ein erster Versuch unternommen... Nennt sich JMC - Java Media Components. Es war erst geplant, dass das Teil von JDK7 sein soll... Wurde aber... wie so ziemlich ALLES (Swing Application Framework, JWebBrowser, Closures, und viele weitere wirklich coole Ideen...) gestrichen... Somit ist JDK7 ziemlich langweilig 

Zurück zum thema: Wenn du nur auf Windows programmierst, rate ich zu einem DirectShow Wrapper... DAs wäre um einiges einfacher, allerdings bist du dann auf Windowsplatformen beschränkt...

Ansonsten noch ein Tipp: Es gibt für die Bearbeitung von Mediendateien noch Xuggler, welches FFMPEG umschließt und auch sehr gut ist, allerdings kann man damit keinen Player schreiben.. bzw es wurde dafür nicht gemacht und daher wäre es sehr kompliziert...

Mir ist es aber mal gelungen, mit Xuggler einen Videostreamserver zu erstellen (Auch als Part von meinem Mediaprogramm), allerdings gab es hinterher Probleme beim Vorspulen... Nun mache ich das Streaming über GSTreamer welches dafür sehr gut ist... Denn mit GSTreamer kann man auch Streams erstellen und an eine Adresse senden... Vlt wäre das ja was für dich 

ALS allerletzte Lösung könntest du ja auch noch jVLC benutzen... Ist aber leider GPL - Lizenz, also ist das etwas doof, wenn du dein Programm kommerziell vermarkten willst ... 

Gruß,

Chris


----------



## Friedhelm (5. Mai 2011)

Das ist doch schon mal ein Anfang, danke!... aber Du arbeitest unter Windows oder? Ich muss mal sehen ob ich das auch mit MacOS (Java 1.6) hinbekomme...


----------



## Kr0e (5. Mai 2011)

Bekommst du, das einzige wo es harrig werden könnte, ist, das GStreamer-JAVA leider zur Zeit nur mit 32 Bit JVM läuft...
Sprich ne 64bit JVM erkennt die nativen Bibs nicht und sagt, dass keine nativen GStreamer sachen gefunden werden konnten...


Wichtig:

Beachte unbedingt die synchronized + volatile Statements... OpenGL läuft in einem anderen Thread als GStraemer dekodiert.. Es können schwere Fehler auftreten ohne Sync...


----------



## Friedhelm (5. Mai 2011)

Ich bekomme die Fehlermeldung: "Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'gstreamer-0.10': dlopen(libgstreamer-0.10.dylib, 9): image not found"

Ich habe es schon unter Eclips mit dem Parameter -d32 versucht, aber das bringt auch nichts.

Sonst irgendwelche Tipps?


*Anmerkung:*

Ich habe mir hier GSVideo Snow Leopard 64Bit gedownloaded:
Download GSVideo from SourceForge.net

Da ist wohl schon GStreamer drin. Ist eigentlich für Processing 1.5 unter Mac. Aber irgendwie scheint da noch ein Problem zu sein.


----------



## Kr0e (5. Mai 2011)

Ja habe ich:

bei den Java start options vlt mal das angeben:

-Djna.library.path="path/to/native/gstreamer/files"


GStreamer-Java benutzt JNA - Java Native Access.... Damit sollte es hinhauen, aber wie gesagt.. du braucsht eine 32bit JVM°! Und natürlich die richtigen GSTreamer nativen Bibliotheken! Ka wo man die für Mac herbekommt.. schlimmstenfalls selbst kompelieren

EDIT:

Oh.. dann bezog sich das 32bit only vermutlich auf Windows... dann vergiss alles bzg 32bit


----------



## Kr0e (5. Mai 2011)

Ansonsten mal bei google suchen, dieses Problem haben wohl sehr viele am Anfang.. also das GStreamer erstmal richtig erkannt wird... alles was dancah kommt ist ja dann durch die Doku erklärt...


----------



## Friedhelm (5. Mai 2011)

Also ich bin weiter... habe die Libs vom GStreamer einfach nach /usr/lib kopiert. Damit bekomme ich den Image not Found fehler nicht mehr, aber das hier:

java.lang.IllegalArgumentException: No such Gstreamer factory: fakesink
	at org.gstreamer.ElementFactory.makeRawElement(ElementFactory.java:219)
	at org.gstreamer.ElementFactory.make(ElementFactory.java:211)
	at org.gstreamer.elements.RGBDataSink.<init>(RGBDataSink.java:54)
	at OpenGLMediaPlayer.<init>(OpenGLMediaPlayer.java:32)
	at VideoGL.<init>(VideoGL.java:90)
	at Test.main(Test.java:13)


----------



## Friedhelm (5. Mai 2011)

Also das mit dem 64 Bit klappt schon, ... doch kann es sein das er die notwendigen GStreamer Plugins in dem Verzeichnis nicht finden kann?


----------



## Kr0e (5. Mai 2011)

Hallo,

dieser Fehler ist mit leider vollkommen unbekannt. Nach einer Suche bei Google, bin ich öfters auf den Satz "probably a 32 lib issue" gestoßen... Ich hatte es unter Ubuntu damals auch wirklich nur mit 32bit ans laufen bekommen.... Das ist das Manko, wenn man den defacto Standard für den Umgang mit Medien(FFMPEG, darauf basiert im Endeffekt GStreamer...) unter Java nutzen will.

Ich würde empfehlen (um wirklich dieses Problem mit 64bit auszuschließen), eifnach mal ne 32bit drauf und testen... Wenns dann immer noch nicht geht, kann man weiter forschen..


----------



## Friedhelm (6. Mai 2011)

So, also es lag wirklich an den Plugins.

ich habe das so geloest:


```
public static void main(String[] args) {
		// TODO Auto-generated method stub

		if (com.sun.jna.Platform.isMac()) {
			final String jnaLibraryPath = System.getProperty("jna.library.path");
			final StringBuilder newJnaLibraryPath = new StringBuilder(jnaLibraryPath != null ? (jnaLibraryPath + ":") : "");
			newJnaLibraryPath.append("/gstreamer/macosx");
			System.setProperty("jna.library.path", newJnaLibraryPath.toString());
					
			
		    Gst.setUseDefaultContext(false);
		    Gst.init("GSVideo", args);

		    Registry reg = Registry.getDefault();		
			boolean res;					
			res = reg.scanPath("/gstreamer/macosx/plugins");
			if (!res) {
				System.err.println("Cannot load GStreamer plugins from " + "/gstreamer/macosx/plugins");
			}
		} 

		
		
		try {
			VideoGL v = new VideoGL();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
```


Bis dahin kommt auch keine Fehlermeldung mehr. Der Movie-Player startet, ich kann ein Film aussuchen, aber dann kommt diese Fehlermeldung (mp4 oder MPeg2 File):

2011-05-06 00:18:27.079 java[9393:11f0f] Error loading /Library/Audio/Plug-Ins/HAL/DVCPROHDAudio.plugin/Contents/MacOS/DVCPROHDAudio:  dlopen(/Library/Audio/Plug-Ins/HAL/DVCPROHDAudio.plugin/Contents/MacOS/DVCPROHDAudio, 262): no suitable image found.  Did find:
	/Library/Audio/Plug-Ins/HAL/DVCPROHDAudio.plugin/Contents/MacOS/DVCPROHDAudio: no matching architecture in universal wrapper
2011-05-06 00:18:27.081 java[9393:11f0f] Cannot find function pointer NewPlugIn for factory C5A4CE5B-0BB8-11D8-9D75-0003939615B6 in CFBundle/CFPlugIn 0x126232d00 </Library/Audio/Plug-Ins/HAL/DVCPROHDAudio.plugin> (bundle, not loaded)


Kann damit jemand was anfangen?


*Achso*, ich habe die Zeilen rausgenommen:
 player.getGStreamerPlayer().setURI(null); 
        player.getGStreamerPlayer().play();

Weil ich eine Null Pointer exception dadurch erhalten habe. Ich frage mich auch, wie das ausgewählte File (ff) durch den Player gestartet wird. Denn wenn ich die beiden oben genannten Zeilen rausnehme, dann startet der Film nicht. Aber ich bekomme auch keine Fehlermeldeung


----------



## Kr0e (6. Mai 2011)

Bei so speziellen würde ich bei der Google GStreamer-Java mal vorbeischauen, die sind recht fix mit dem Beantworten von Fragen, ich habe nur Erfahrung mit Linux und Windows, bei beiden Platformen jeweils 32 Bit. Von mehr kann ich nicht berichten...

Nur vlt. soviel: Die GStreamer-Java Library ist eigentlich für ein 32 Bit System geschrieben... Ich habe deinen letzten post nicht so richtig verstanden... Du meintest "Es lag wirklich an den Plugins". Hast du jetzt ne 32 JVM genommen ? Weil wenn nicht, dann würde ich dir das wirklich empfehlen:

Also lad einfach 32bit GStreamer bibliotheken herunter für deinen Mac und dann eine 32 Bit JVM und dann alles nochmal probieren.
Vom Gefühl her, würde ich sowas nicht Mischen... Das kann zu undurchschaubaren Fehlern führen... 

Mehr Rat kann ich dir leider nicht geben, bei mir klappte es sowohl auf Windows und Linux (32Bit).

Gruß,
Chris

PS: Schreib aber bitte dennoch, ob du es, und falls ja wie, hinbekommen hast. Danke =)


EDIT:

Tut mir Leid, wie gesagt, die erste Datei war eine Test datei!!
setURI(null) hab ich aus Test benutzt! Tut mir leid, ich wollte damals über eine HTTP Adresse eine Datei angeben. Einfach durch eine gültige URI ersetzen und das wars. Dieses ReadableChannelSink kannste uach wegmachen!!


Konkret:


```
//DAS hier kann weg!
        ReadableByteChannelSrc srcChannel = new ReadableByteChannelSrc(new ReadableByteChannel() {
 
            public int read(ByteBuffer dst) throws IOException {
                throw new UnsupportedOperationException("Not supported yet.");
            }
 
            public boolean isOpen() {
                throw new UnsupportedOperationException("Not supported yet.");
            }
 
            public void close() throws IOException {
                throw new UnsupportedOperationException("Not supported yet.");
            }
        }, "bla");
 
        player.getGStreamerPlayer().setURI(new File("C:/movie.avi").toURI());
        player.getGStreamerPlayer().play();
```

Für mein Programm benutze ich ja nicht pures LWJGL, ich benutze die jMonkeyEngine aber ich wollte halt pures OpenGL nehmen, damit ich das optimal tunen kann. jME unterstützt nur HighLevel-Abstraktion....


----------



## Friedhelm (6. Mai 2011)

Aha, ok. Habe alle Probleme geloest, habe einfach das Audio-Plugin geloescht (auf Empfehlung von Apple, das war noch ein altes Final Cut Plugin). 

Die URI habe ich durch ein lokales File ersetzt. Der Player startet... läd, dann stürtz er ohne eine Fehlermeldung ab. Ok, ich denke ich habe alles gegeben.

Die Files die ich ausprobiert habe waren ein mp4 h264 File, und ein MPEG2 DVB-T Stream, die beide mit dem VLC Player problemlos abgespielt werden können.
Ich denke das wars. Nach wieder mal Stunden rumprobieren (wie vor ein paar Monaten), wieder kein Erfolg.

Wie ich das programmieren hasse, seit ca. 27 jahren tuhe ich mir das jetzt an, diesen Codezeilen Mist 
Naja, das hat ja bald ein Ende.


----------



## Friedhelm (6. Mai 2011)

Jetzt habe ich es doch noch hinbekommen... habe den Rechner neu gestartet und vorher noch ein paar Verzeichniss und Plugins gelöscht. 

Ok, die Videofiles hat er trotzdem nicht abegespielt, aber dafür spielt er jetzt VOB Videofiles ab.

Kann man die Anzeige:

Update time : 0
Old address: 4958814208
New address: 4958814208
Update time : 0
Old address: 4958814208
New address: 4958814208
Update time : 0
Old address: 4958814208
New address: 4958814208
Update time : 0
Old address: 4958814208
New address: 4958814208


irgendwie unterdrücken?

Und warum spielt er keine MPG's und mp4 Movies ab?


Was ich mich auch frage, warum das hier:
System.out.println(player.getGStreamerPlayer().getPipeline().getClock().getTime());

... immer das: 23:17:47
anzeigt, also nicht bei 0:0:0 losgeht, sonder immer bei 23:17:xx


Übrigens habe ich bei mir eine CPU Auslastung von 50% anstatt 10%  (Dual Core Duo 2, 2 Ghz, 8GB Ram, NVIDIA GeForce 9400 256 MB). Bei VLC habe ich eine Auslastung von ca. 13%


----------



## Kr0e (6. Mai 2011)

Die System-Out-Befehle in der PBO Datei kannst du entfernen, die sind als Test gedacht gewesen, um zu zeigen, dass es jeweils der selbe Puffer nativ gesehen ist, damit es performanter wirst.

Dass du trotzdem 50% Auslastung bekommst ist strange! Vlt mal die visualisuerung weglassen also das reine Dekodieren. WEn ndas auch 50 % verursacht, liegt es an den nativen GStreamer sachen... Mac is ja oftmals so, dass wirkich nichts, außer den Macinternen Sachen gut läuft... Der OpenGL-Teil ist so performant, wie technisch nur iwie mglich ist... Das siehst du daran, dass das Updaten 0 ms dauert wie durch die Textausgabe bestätigt ("update time 0"). Muss definitiv an GStreamer liegen oder iwie an der Konfiguration auf deinem Rechner.

Vlt ansosnten einfach mal die reine GSTreamer Bilbiothek selbst herunter laden und selbst kompilieren (Erfordert C Kenntnisse).
Der Witz ist, dass man beim Kompilieren ne ganze Menge Attriubte angeben kann, die das Verhalten hinterher grundlegend verändern.... So ist z.B. Multithreading-Decoding erst möglich, wenn es wirklich auch damit kompiliert wurde. Oder hardware Decoding ist auch erst möglich (WEnn überhaupt unterstrützt von der Graka), wenn man es aktiviert.
Es ist shcon etwas komplizierter als der reine Javacode, der ja auch schohn nciht wenig ist  

tut mir Leid, wenn es bei dir nicht läuft =( Ich werde es demnächst mal selbst auf einem Mac testen...

PS:

Wenn die Clock Unsinn anzeigt, ist die Datei doof codiert wurden, sprich iein Programm, dass die Datei erstellt hat, hat Murks gemacht... Die Clock zeigt nur den Zeitstempel eines jeden Frames an


----------



## Kr0e (6. Mai 2011)

Hab den Post oben von dir garnicht gesehen  Seit 27 Jahren ? Bist ja lange dabei 
Ich kann verstehen, dass man nach Jahren iwann die Schnauze voll hat..  Aber ich bleibe da, bei meinem anfänglichen Post...
Wenn man sich selbst den Stress auferlegt, mit Java professionell Medien wiederzugeben, muss man eben wissen, worauf man sich einlässt.. Technisch ist es möglich und das auch verdammt performant. Leider können kleinste Änderungen an der Konfiguration, alles zum Kippen bringen... Was bei dir offensichtlich grad der Fall ist =( ... DAs tut mir Leid!

Ich würde sowieso eigentlich lieber C# + Dx nehmen für mein Multimediaprogramm... Java ist in der Hinsicht einkrampf... Aber ich will, dass es eben überall läuft... 

Ist ja nich so, als ob ich den Code, den ich gepostet habe, innerhalb einer Woche gemacht hab... Bis ich mcih in das Thema eingearbeitet hatte und vorallem auf die Idee kam, das PBO-Objekt von OGL zu missbrauchen... da vergingen viele Testphasen.. Ich hab es auf Win/Linux getesten und ich habe nun eine Konfiguration, die bei mir klappt. ich liefere eben auch die von mir selbst kompilierten gstreamer biblitoheken aus, damit der user hinterher nich gstreamer selbst installieren muss...

Vlt nich ALLES an einem Abend probieren... gib dem Problem etwas Zeit um sich zu setzen... Dann tötest du es dann iwann 


ANSONSTEN:

Ich habe gehört, dass Apple sowieso nicht gut auf Java zu sprechen ist (****** Apple!) und lieber eigene JVMs macht, die natürlich theoretisch alles laufen lassen können, aber teils nciht grad optimiert sind. Ich weiß nicht wie es heute ist , aber bis JVM 5 einschließlich war es glaub ich so...


----------



## Kr0e (6. Mai 2011)

Ich separariere meine Posts damit meine neuen GEdankengänge klarwerden und nich ein Post tausend mal editiert wurde:



Bei mir ist komischerweise Firefox z.B. ein FAktor, ob GSTreamer langsam läuft oder nicht!! Ich vermute, dass Firefox, genauso wie jedes andere Medienwiedergabeprogramm die Codecs, die installiert sind, in irgendeiner Art und Weise blockiert für kurze Zeiträume iwie... Ich kanns mir auch nicht erklären... Aber wenn Firefox an ist, läuft mein Programm mit einer höheren Auslastung.. Das liegt nicht an zu wenig RAM oder so..  Vlt. noch als interesanter Aspekt...


So und noch eine andere Sache (ist eben sehr weitläufig ;D):

Ich habe grad auf der Seite von GStreamer gelesen, dass GStreamer zwar eigene, lauffähige Codecs hat aber auch die installierten Codecs, sofern vorhanden, benutzt. Sprich unter Windows die DirectShow-Sachen, was natürlich sehr performant ist..
Vlt. sind die GStreamer eigenen Libs nciht gaanz so performant...

Hmm.. weitere Ursachen könnten das Pixelformat der OpenGL-Texture sein... Ich habe ein Format genommen, dass abhängig ist, von ByteBuffer.nativeOrder() Also bei BIG_ENDIAN nehme ich RGBA und LITTLE_ENDIAN ABGR .. sofern ich das grad nicht vertauscht hab im Kopf... ANSONSTEN muss OGL das alles wieder umdrehen .. also JEDES Frame was natürlich ne Menge an Resourcen verschwendet, vorallem weil solche Konvertiertungen leider über die CPU per default laufen und somit diese massiv auslasten!

Vlt. hat die JVM für Mac an dieser Stelle eine kleine Ungereimtheit bzg. der Annahme, dass ByteBuffer.nativeOrder() etwas über die wirklich ative Bytereihenfolge bei OpenGL aussagt..

Also: Einfach mal das Format bei PBOTexture verdrehen.. vlt änderts ja was...

Jetzt bin ich überfragt... =(


----------



## Friedhelm (6. Mai 2011)

Danke für die vielen Hinweise. Ich habe jetzt mal die ganzen System.out.println's rausgenommen, jetzt ist die CPU-Last bei ca. 20% Das ist in Ordnung. 

Warum aber die anderen Filme nicht laufen... wer weiss. Ist auch erstmal nicht so wichtig. Hauptsache es läuft überhaupt, darüber ist man ja schon erstmal froh 

Den Rest kann man immer noch optimieren, danke.


Was ich jetzt noch angezeigt bekomme ist das hier (bei einem VOB Film):

No accelerated IMDCT transform found
No accelerated IMDCT transform found
No accelerated IMDCT transform found
No accelerated IMDCT transform found


----------



## Kr0e (6. Mai 2011)

Hallo, das sind Ausgaben von GStreamer, welche auf Fehler hinweisen... Darauf hat man glaub ich keinen Einfluss... Aber solang es läuft.. Bei mir kamen mal Warnmeldungen, dass die Datei inkorrekt ist, ließ sich aber abspielen ohne Probleme...  Keine Sorge...
Manchmal stimmen z.B. die Zetistempel nicht bei den Frames (Verursacht durch schlechte Encoder) und dann werden manch gedroppt sowas wird auch vermerkt.. im Film merkt mans nicht .. also meistens

Gruß,

Chris


----------



## Friedhelm (6. Mai 2011)

Jo, ist auch nicht so schlimm.

Was ich mich gerade frage ist, warum JNA und Dylib Dateien von GStreamer gebraucht werden, wenn:

- GStreamer die VideoFrames nur dekodiert
- Java die VideoFrames über den ByteBuffer ins OpenGL schreibt

Normalerweise müsste das doch auch ohne den ganzen Hardwarenahen Quatsch gehen. Also den gStreamer Decoder nehmen und selbst integrieren, und die Frames dann nach OpenGL schieben.

Also, wozu die dann Hardwareunterstützung per GStreamer? OpenGL als Videoanzeige ist ja schon Hardwarenah, oder reicht das nicht?


----------



## Kr0e (6. Mai 2011)

Langsam! JNA wird von GStreamer-Java benötigt! Sonst kannst du von Java aus garnicht erst auf die nativen , in C geschriebenen Bibliotheken zugreifen! Für die Anzeige OpenGL zu benutzen leuchtet, wie du ja auch schreibst, ein. 

Du darfst nicht vergessen, dass GStreamer und GStreamer-JAva 2 vollkommen verschiedene Dinge sind... GStreamer ist eine Bilbiothek zum Dekodieren, GStreamer-Java ist ein Wrapper, der es ermöglicht, die C-Methoden aufzurufen.

Also nochmal konkret:

OpenGL wird benutzt um generell Daten aus dem Arbeitsspeicher sehr shcnell an die Grafikkarte zu übertragen. Vergiss nich, dass das mit der normalen Vorgehensweise bei OpenGL eigentlich nciht geht. Sprich wenn du diese enormen Datenraten manuell über glSubTexImage2D (oder wie die heißen) überträgst, dauert das sehr lange... Durch meinen Ansatz mit dem PBO wird das auf nahe zu "instant"-runtergeschraubt...

Und GStreamer dekodiert Medien und nutzt ggf. hardwarebeschleunigung, falls vorhanden... Es gibt soweit ich weiß Prozessorbefehlssätze (gerade von Intel), die das Dekodieren von Filmen bereits hardware-mäßig beschleunigen, wodurch die CPU quasi nciht mehr machen muss...

Also als Faustregel:

GStreamer fürs Dekodieren, GSTreamer-Java fürs Wrappen nach Java und OpenGL um die nativem Buffer am Ende wirklich auf dem Monitor auszugeben!

Gruß,

Chris


----------



## Friedhelm (6. Mai 2011)

Aha, ok, für die Plugins wahrscheinlich, stimmt.

Aber es wurde ja schon oft mit Java gezeigt, dass C++ nicht unbedingt schneller als Java-Code ist. Sogar manchmal langsamer. Naja, ich stecke ja bei GStreamer nicht drin, das kann nur ein Gstreamer-Coder beantworten denke ich, der den Code in und auswendig kennt.

Es wäre schon von Vorteil, wenn man sich hier einen universellen Dekodierer schnappen könnte und dann die Frames einfach in OpenGL schaufelt. Ich denke vom Speed her wäre das kein Problem (mein Gefühl sagt mir das, denn mit MJPEG Video-Streams klappt das sehr gut und belastet die CPU kaum).


----------

