Skip to content

Commit

Permalink
Use native direct ByteBuffer instead of Java heap buffer.
Browse files Browse the repository at this point in the history
This removes one full-frame memory copy.
  • Loading branch information
caprica committed Feb 3, 2016
1 parent 79cd2ca commit ac6817c
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 110 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
![vlcj](https://github.com/caprica/vlcj/raw/master/etc/vlcj-logo.png "vlcj")

vlcj experimental branch
========================

This is an experimental branch of vlcj, used to test improvements to the direct
rendering media players.

Currently this is limited to using direct ByteBuffers to provide access to the
video data. LibVLC can render directly into the ByteBuffer, thereby removing at
least one full frame copy. An application can render the contents of the
ByteBuffer in whatever way it sees fit - e.g. it could copy the data to a
BufferedImage or a PixelWriter.

There is still one 'extra' copy here, ideally LibVLC would render directly into
the BufferedImage or PixelWriter.

Nevertheless, initial performance testing shows considerable improvement over
that in vlcj 3.x.

vlcj
====

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

<groupId>uk.co.caprica</groupId>
<artifactId>vlcj</artifactId>
<version>3.11.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>

<name>vlcj</name>
<description>Java Framework for the vlc Media Player.</description>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package uk.co.caprica.vlcj.component;

import java.nio.ByteBuffer;
import java.util.Arrays;

import org.slf4j.Logger;
Expand All @@ -32,7 +33,6 @@
import uk.co.caprica.vlcj.player.direct.BufferFormatCallback;
import uk.co.caprica.vlcj.player.direct.DirectMediaPlayer;
import uk.co.caprica.vlcj.player.direct.RenderCallback;
import uk.co.caprica.vlcj.player.direct.RenderCallbackAdapter;

import com.sun.jna.Memory;

Expand Down Expand Up @@ -214,10 +214,9 @@ protected String[] onGetMediaPlayerFactoryExtraArgs() {
* Template method to obtain a render callback implementation.
* <p>
* The default behaviour is simply to return this component instance itself so that sub-classes
* may override {@link #display(Memory)}.
* may override {@link #display(DirectMediaPlayer, ByteBuffer[], BufferFormat)}.
* <p>
* A sub-class may provide any implementation of {@link RenderCallback} - including
* {@link RenderCallbackAdapter}.
* A sub-class may provide any implementation of {@link RenderCallback}.
*
* @return render callback implementation
*/
Expand Down Expand Up @@ -402,7 +401,7 @@ public void endOfSubItems(MediaPlayer mediaPlayer) {
// === RenderCallback =======================================================

@Override
public void display(DirectMediaPlayer mediaPlayer, Memory[] nativeBuffers, BufferFormat bufferFormat) {
public void display(DirectMediaPlayer mediaPlayer, ByteBuffer[] nativeBuffers, BufferFormat bufferFormat) {
// Default implementation does nothing, sub-classes should override this or
// provide their own implementation of a RenderCallback
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package uk.co.caprica.vlcj.player.direct;

import sun.nio.ch.DirectBuffer;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
* Factory for creating property aligned native byte buffers.
*/
public class ByteBufferFactory {

/**
* Alignment suitable for use by LibVLC video callbacks.
*/
private static final int LIBVLC_ALIGNMENT = 32;

/**
* Allocate a properly aligned native byte buffer, suitable for use by the LibVLC video
* callbacks.
*
* @param capacity size of the buffer
* @return aligned byte buffer
*/
public static ByteBuffer allocateAlignedBuffer(int capacity) {
return allocateAlignedBuffer(capacity, LIBVLC_ALIGNMENT);
}

/**
* Allocate a property aligned native byte buffer.
* <p></p>
* Original credit: http://psy-lob-saw.blogspot.co.uk/2013/01/direct-memory-alignment-in-java.html
*
* @param capacity size of the buffer
* @param alignment alignment
* @return aligned byte buffer
*/
public static ByteBuffer allocateAlignedBuffer(int capacity, int alignment) {
ByteBuffer result;
ByteBuffer buffer = ByteBuffer.allocateDirect(capacity + alignment);
long address = ((DirectBuffer) buffer).address();
if ((address & (alignment - 1)) == 0) {
buffer.limit(capacity);
result = buffer.slice().order(ByteOrder.nativeOrder());
}
else {
int newPosition = (int) (alignment - (address & (alignment - 1)));
buffer.position(newPosition);
buffer.limit(newPosition + capacity);
result = buffer.slice().order(ByteOrder.nativeOrder());
}
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@

package uk.co.caprica.vlcj.player.direct;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.Semaphore;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sun.nio.ch.DirectBuffer;
import uk.co.caprica.vlcj.binding.LibVlc;
import uk.co.caprica.vlcj.binding.internal.libvlc_display_callback_t;
import uk.co.caprica.vlcj.binding.internal.libvlc_instance_t;
Expand Down Expand Up @@ -127,7 +130,12 @@ public class DefaultDirectMediaPlayer extends DefaultMediaPlayer implements Dire
/**
* Native memory buffers, one for each plane.
*/
private Memory[] nativeBuffers;
private ByteBuffer[] nativeBuffers;

/**
* Native memory pointers to each byte buffer.
*/
private Pointer[] pointers;

/**
* Create a new media player.
Expand Down Expand Up @@ -179,7 +187,7 @@ public final BufferFormat getBufferFormat() {
}

@Override
public final Memory[] lock() {
public final ByteBuffer[] lock() {
semaphore.acquireUninterruptibly();
return nativeBuffers;
}
Expand Down Expand Up @@ -218,9 +226,12 @@ public int format(PointerByReference opaque, PointerByReference chroma, IntByRef
// Memory must be aligned correctly (on a 32-byte boundary) for the libvlc
// API functions (extra bytes are allocated to allow for enough memory if
// the alignment needs to be changed)
nativeBuffers = new Memory[bufferFormat.getPlaneCount()];
nativeBuffers = new ByteBuffer[bufferFormat.getPlaneCount()];
pointers = new Pointer[bufferFormat.getPlaneCount()];
for(int i = 0; i < bufferFormat.getPlaneCount(); i ++ ) {
nativeBuffers[i] = new Memory(pitchValues[i] * lineValues[i] + 32).align(32);
ByteBuffer buffer = ByteBufferFactory.allocateAlignedBuffer(pitchValues[i] * lineValues[i]);
nativeBuffers[i] = buffer;
pointers[i] = Pointer.createConstant(((DirectBuffer) buffer).address());
}
logger.trace("format finished");
return pitchValues.length;
Expand Down Expand Up @@ -260,7 +271,7 @@ public Pointer lock(Pointer opaque, PointerByReference planes) {
semaphore.acquireUninterruptibly();
logger.trace("acquired");
// Set the pre-allocated buffers to use for each plane
planes.getPointer().write(0, nativeBuffers, 0, nativeBuffers.length);
planes.getPointer().write(0, pointers, 0, pointers.length);
logger.trace("lock finished");
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,13 @@

package uk.co.caprica.vlcj.player.direct;

import com.sun.jna.Pointer;
import uk.co.caprica.vlcj.player.MediaPlayer;

import com.sun.jna.Memory;

import java.nio.ByteBuffer;

/**
* Specification for a media player that provides direct access to the video frame data.
* <p>
Expand Down Expand Up @@ -52,7 +55,7 @@ public interface DirectMediaPlayer extends MediaPlayer {
*
* @return native memory buffers, may be <code>null</code>
*/
Memory[] lock();
ByteBuffer[] lock();

/**
* Unlock the native memory buffers.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
package uk.co.caprica.vlcj.player.direct;

import com.sun.jna.Memory;
import com.sun.jna.Pointer;

import java.nio.ByteBuffer;

/**
* Specification for a component that wishes to be called back to process video frames.
Expand All @@ -40,5 +43,5 @@ public interface RenderCallback {
* @param nativeBuffers video data for one frame
* @param bufferFormat information about the format of the buffer used
*/
void display(DirectMediaPlayer mediaPlayer, Memory[] nativeBuffers, BufferFormat bufferFormat);
void display(DirectMediaPlayer mediaPlayer, ByteBuffer[] nativeBuffers, BufferFormat bufferFormat);
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;

import javax.swing.JComponent;
import javax.swing.JFrame;
Expand All @@ -38,7 +39,6 @@
import uk.co.caprica.vlcj.player.direct.BufferFormatCallback;
import uk.co.caprica.vlcj.player.direct.DirectMediaPlayer;
import uk.co.caprica.vlcj.player.direct.RenderCallback;
import uk.co.caprica.vlcj.player.direct.RenderCallbackAdapter;
import uk.co.caprica.vlcj.player.direct.format.RV32BufferFormat;
import uk.co.caprica.vlcj.test.VlcjTest;

Expand Down Expand Up @@ -144,7 +144,15 @@ public BufferFormat getBufferFormat(int sourceWidth, int sourceHeight) {
mediaPlayerComponent = new DirectMediaPlayerComponent(bufferFormatCallback) {
@Override
protected RenderCallback onGetRenderCallback() {
return new TestRenderCallbackAdapter();
return new RenderCallback() {
@Override
public void display(DirectMediaPlayer mediaPlayer, ByteBuffer[] nativeBuffers, BufferFormat bufferFormat) {
// FIXME test!
int[] rgbBuffer = nativeBuffers[0].asIntBuffer().array();
image.setRGB(0, 0, width, height, rgbBuffer, 0, width);
panel.repaint();
}
};
}
};

Expand All @@ -165,18 +173,4 @@ private void start(String mrl) {
// One line of vlcj code to play the media...
mediaPlayerComponent.getMediaPlayer().playMedia(mrl);
}

private class TestRenderCallbackAdapter extends RenderCallbackAdapter {

private TestRenderCallbackAdapter() {
super(new int[width * height]);
}

@Override
protected void onDisplay(DirectMediaPlayer mediaPlayer, int[] rgbBuffer) {
// Simply copy buffer to the image and repaint
image.setRGB(0, 0, width, height, rgbBuffer, 0, width);
panel.repaint();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import java.util.Arrays;

import javax.swing.ImageIcon;
Expand All @@ -44,7 +45,7 @@
import uk.co.caprica.vlcj.player.direct.BufferFormat;
import uk.co.caprica.vlcj.player.direct.BufferFormatCallback;
import uk.co.caprica.vlcj.player.direct.DirectMediaPlayer;
import uk.co.caprica.vlcj.player.direct.RenderCallbackAdapter;
import uk.co.caprica.vlcj.player.direct.RenderCallback;
import uk.co.caprica.vlcj.player.direct.format.RV32BufferFormat;
import uk.co.caprica.vlcj.test.VlcjTest;

Expand Down Expand Up @@ -163,14 +164,11 @@ public void paint(Graphics g) {
}
}

private final class TestRenderCallback extends RenderCallbackAdapter {

public TestRenderCallback() {
super(((DataBufferInt) image.getRaster().getDataBuffer()).getData());
}
private final class TestRenderCallback implements RenderCallback {

@Override
public void onDisplay(DirectMediaPlayer mediaPlayer, int[] data) {
public void display(DirectMediaPlayer mediaPlayer, ByteBuffer[] nativeBuffers, BufferFormat bufferFormat) {
// FIXME make this work again
// The image data could be manipulated here...

/* RGB to GRAYScale conversion example */
Expand Down

0 comments on commit ac6817c

Please sign in to comment.