/** * Create a new video surface for "direct" rendering via callbacks. * * @param bufferFormatCallback buffer format callback * @param renderCallback render callback * @param lockBuffers <code>true</code> if the video buffer should be locked; <code>false</code> if it should not * @return video surface */ public CallbackVideoSurface newVideoSurface(BufferFormatCallback bufferFormatCallback, RenderCallback renderCallback, boolean lockBuffers) { return new CallbackVideoSurface(bufferFormatCallback, renderCallback, lockBuffers, VideoSurfaceAdapters.getVideoSurfaceAdapter()); }
/** * Create a new video surface for a Component. * <p> * The optimal component in a {@link Canvas}, {@link Window} can be used, as can any other AWT {@link Component}, at * least in principle. * </p> * * @param component component * @return video surface */ public ComponentVideoSurface newVideoSurface(Component component) { return new ComponentVideoSurface(component, VideoSurfaceAdapters.getVideoSurfaceAdapter()); }
/** * Create a new video surface for a native component id. * * @param componentId native component id * @return video surface */ public ComponentIdVideoSurface newVideoSurface(long componentId) { return new ComponentIdVideoSurface(componentId, VideoSurfaceAdapters.getVideoSurfaceAdapter()); }
/** * Get a video surface adapter for the current run-time operating system. * * @return video surface adapter */ public static VideoSurfaceAdapter getVideoSurfaceAdapter() { if (RuntimeUtil.isNix()) { return new LinuxVideoSurfaceAdapter(); } else if (RuntimeUtil.isWindows()) { return new WindowsVideoSurfaceAdapter(); } else if (RuntimeUtil.isMac()) { return new OsxVideoSurfaceAdapter(); } else { throw new RuntimeException("Unable to create a video surface - failed to detect a supported operating system"); } }
@Override public void attach(LibVlc libvlc, MediaPlayer mediaPlayer) { if(component.isDisplayable()) { long componentId = getComponentId(component); videoSurfaceAdapter.attach(libvlc, mediaPlayer, componentId); } else { throw new IllegalStateException("The video surface component must be displayable"); } }
@Override public int format(PointerByReference opaque, PointerByReference chroma, IntByReference width, IntByReference height, PointerByReference pitches, PointerByReference lines) { bufferFormat = bufferFormatCallback.getBufferFormat(width.getValue(), height.getValue()); applyBufferFormat(bufferFormat, chroma, width, height, pitches, lines); return nativeBuffers.allocate(bufferFormat); }
/** * * Memory must be aligned correctly (on a 32-byte boundary) for the libvlc API functions, this is all taken care of * by the {@link ByteBufferFactory}. * * @param bufferFormat * @return */ int allocate(BufferFormat bufferFormat) { int planeCount = bufferFormat.getPlaneCount(); int[] pitchValues = bufferFormat.getPitches(); int[] lineValues = bufferFormat.getLines(); nativeBuffers = new ByteBuffer[planeCount]; pointers = new Pointer[planeCount]; for (int i = 0; i < planeCount; i ++ ) { ByteBuffer buffer = ByteBufferFactory.allocateAlignedBuffer(pitchValues[i] * lineValues[i]); nativeBuffers[i] = buffer; pointers[i] = Pointer.createConstant(ByteBufferFactory.getAddress(buffer)); if (lockBuffers) { if (!RuntimeUtil.isWindows()) { LibC.INSTANCE.mlock(pointers[i], new NativeLong(buffer.capacity())); } else { Kernel32.INSTANCE.VirtualLock(pointers[i], new size_t(buffer.capacity())); } } } return nativeBuffers.length; }
/** * Allocate a properly aligned native byte buffer, suitable for use by the LibVLC video callbacks. * * @param capacity required size for the buffer * @return aligned byte buffer */ static ByteBuffer allocateAlignedBuffer(int capacity) { return allocateAlignedBuffer(capacity, LIBVLC_ALIGNMENT); }
@Override public void attach(LibVlc libvlc, MediaPlayer mediaPlayer) { videoSurfaceAdapter.attach(libvlc, mediaPlayer, componentId); }
@Override public void cleanup(Pointer opaque) { nativeBuffers.free(); }
/** * Create a video surface. * * @param bufferFormatCallback callback providing the video buffer format * @param renderCallback callback used to render the video frame buffer * @param lockBuffers <code>true</code> if the video buffer should be locked; <code>false</code> if not * @param videoSurfaceAdapter adapter to attach a video surface to a native media player */ public CallbackVideoSurface(BufferFormatCallback bufferFormatCallback, RenderCallback renderCallback, boolean lockBuffers, VideoSurfaceAdapter videoSurfaceAdapter) { super(videoSurfaceAdapter); this.bufferFormatCallback = bufferFormatCallback; this.renderCallback = renderCallback; this.nativeBuffers = new NativeBuffers(lockBuffers); }
/** * Ensure that the video surface has been associated with the native media player. * <p> * Ordinarily when setting the video surface the actual association of the video surface with * the native media player is deferred until the first time media is played. * <p> * This deferring behaviour is usually a good thing because when setting a video surface * component on the native media player the video surface component must be a displayable * component and this is often not the case during the construction and initialisation of the * application. * <p> * Most applications will not need to call this method. * <p> * However, in special circumstances such as associating an embedded media player with a media * list player, media is played through the media list rather than the media player itself so * the deferred association of the video surface would never happen. * <p> * Calling this method ensures that the video surface is properly associated with the native * media player and consequently the video surface component must be visible when this method is * called. */ public void attachVideoSurface() { if (videoSurface != null) { videoSurface.attach(libvlc, mediaPlayer); } else { // This is not necessarily an error } }
@Override public void display(Pointer opaque, Pointer picture) { CallbackVideoSurface.this.renderCallback.display(mediaPlayer, nativeBuffers.buffers(), bufferFormat); }
/** * Allocate a property aligned native byte buffer. * * @param capacity required size for the buffer * @param alignment alignment alignment * @return aligned byte buffer */ private static ByteBuffer allocateAlignedBuffer(int capacity, int alignment) { ByteBuffer result; ByteBuffer buffer = ByteBuffer.allocateDirect(capacity + alignment); long address = getAddress(buffer); 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; }
/** * Get the video surface component. * <p> * This method will only be used if the video surface is a {@link ComponentVideoSurface} so we can forgo some * checks. * * @return */ private Component getComponent() { return ((ComponentVideoSurface) mediaPlayer.videoSurface().getVideoSurface()).component(); }
private long getComponentId(Component component) { if (!RuntimeUtil.isMac()) { return Native.getComponentID(component); } else { return OsxComponentId.getOsxComponentId(component); } }
@Override public Pointer lock(Pointer opaque, PointerByReference planes) { Pointer[] pointers = nativeBuffers.pointers(); planes.getPointer().write(0, pointers, 0, pointers.length); return null; }