[sip-comm-dev] Re: svn commit: r7133 - trunk/src: native/jawtrenderer net/java/sip/communicator/impl/neomedia/codec/video net/java/sip/communicator/impl/neomedia/devi...


#1

Hi devs, Werner,

As Werner has already helpfully noticed :wink: the source code of a native
VideoRenderer for Linux has been committed into trunk. While any
testing and bug reporting is wholeheartedly welcome, please keep in
mind that the implementation still needs love - I don't expect it to
eat kittens... but it's known, for example, to produce an incorrect
image at times, especially with desktop streaming, which appears as if
long lines of pixels are crammed into a smaller width and wrapped
around.

Best regards,
Lubo

···

On Tue, May 18, 2010 at 11:34 PM, <lubomir_m@dev.java.net> wrote:

Author: lubomir_m
Date: 2010-05-18 20:34:09+0000
New Revision: 7133

Added:
trunk/src/native/jawtrenderer/JAWTRenderer_Linux.c
trunk/src/native/jawtrenderer/Makefile.linux
Modified:
trunk/src/native/jawtrenderer/JAWTRenderer.h
trunk/src/net/java/sip/communicator/impl/neomedia/codec/video/SwScaler.java
trunk/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java
trunk/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/video/JAWTRenderer.java

Log:
Commits an initial version of a native VideoRenderer i.e. JAWTRenderer on Linux using the XVideo extension of the X Window System.

Modified: trunk/src/native/jawtrenderer/JAWTRenderer.h
Url: https://sip-communicator.dev.java.net/source/browse/sip-communicator/trunk/src/native/jawtrenderer/JAWTRenderer.h?view=diff&rev=7133&p1=trunk/src/native/jawtrenderer/JAWTRenderer.h&p2=trunk/src/native/jawtrenderer/JAWTRenderer.h&r1=7132&r2=7133

--- trunk/src/native/jawtrenderer/JAWTRenderer.h (original)
+++ trunk/src/native/jawtrenderer/JAWTRenderer.h 2010-05-18 20:34:09+0000
@@ -12,7 +12,7 @@
#include <jawt.h>

#ifdef __cplusplus
-extern "C" { /* } */
+extern "C" {
#endif

void JAWTRenderer_close
@@ -27,7 +27,7 @@
jint width, jint height);

#ifdef __cplusplus
-}
+} /* extern "C" { */
#endif

#endif /* _JAWTRENDERER_H_ */

Added: trunk/src/native/jawtrenderer/JAWTRenderer_Linux.c
Url: https://sip-communicator.dev.java.net/source/browse/sip-communicator/trunk/src/native/jawtrenderer/JAWTRenderer_Linux.c?view=auto&rev=7133

--- (empty file)
+++ trunk/src/native/jawtrenderer/JAWTRenderer_Linux.c 2010-05-18 20:34:09+0000
@@ -0,0 +1,409 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+#include "JAWTRenderer.h"
+
+#include <jawt_md.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <X11/extensions/Xvlib.h>
+
+typedef struct _JAWTRenderer
+{
+ Display *display;
+ Drawable drawable;
+
+ XvPortID port;
+ int imageFormatID;
+ XvImage *image;
+
+ char *data;
+ size_t dataCapacity;
+ jint dataHeight;
+ jint dataLength;
+ jint dataWidth;
+}
+JAWTRenderer;
+
+static XvImage *_JAWTRenderer_createImage(JAWTRenderer *renderer);
+static int _JAWTRenderer_freeImage(JAWTRenderer *renderer);
+static XvPortID _JAWTRenderer_grabPort
+ (JAWTRenderer *renderer, JAWT_X11DrawingSurfaceInfo *x11dsi);
+static int _JAWTRenderer_ungrabPort(JAWTRenderer *renderer);
+
+void
+JAWTRenderer_close
+ (JNIEnv *jniEnv, jclass clazz, jlong handle, jobject component)
+{
+ JAWTRenderer *renderer;
+
+ renderer = (JAWTRenderer *) handle;
+ if (-1 != renderer->port)
+ _JAWTRenderer_ungrabPort(renderer);
+ if (renderer->data)
+ free(renderer->data);
+ free(renderer);
+}
+
+jlong
+JAWTRenderer_open(JNIEnv *jniEnv, jclass clazz, jobject component)
+{
+ Display *display;
+ JAWTRenderer *renderer;
+
+ display = XOpenDisplay(NULL);
+ if (display)
+ {
+ unsigned int ver, rev, req, ev, err;
+
+ if (Success == XvQueryExtension(display, &ver, &rev, &req, &ev, &err))
+ {
+ renderer = malloc(sizeof(JAWTRenderer));
+ if (renderer)
+ {
+ renderer->display = NULL;
+ renderer->drawable = 0;
+
+ renderer->port = -1;
+ renderer->image = NULL;
+
+ renderer->data = NULL;
+ renderer->dataLength = 0;
+ }
+ }
+ else
+ renderer = NULL;
+ XCloseDisplay(display);
+ }
+ else
+ renderer = NULL;
+ return (jlong) renderer;
+}
+
+jboolean
+JAWTRenderer_paint
+ (JAWT_DrawingSurfaceInfo *dsi, jclass clazz, jlong handle, jobject g)
+{
+ JAWT_X11DrawingSurfaceInfo *x11dsi;
+ JAWTRenderer *renderer;
+ Display *display;
+ Drawable drawable;
+ XvPortID port;
+
+ x11dsi = (JAWT_X11DrawingSurfaceInfo *) (dsi->platformInfo);
+ renderer = (JAWTRenderer *) handle;
+
+ display = x11dsi->display;
+ drawable = x11dsi->drawable;
+ if ((renderer->display != display) || (renderer->drawable != drawable))
+ {
+ if (-1 != renderer->port)
+ _JAWTRenderer_ungrabPort(renderer);
+
+ renderer->display = display;
+ renderer->drawable = drawable;
+
+ port = _JAWTRenderer_grabPort(renderer, x11dsi);
+ }
+ else
+ port = renderer->port;
+ if (-1 != port)
+ {
+ XvImage *image;
+
+ if (renderer->data && renderer->dataLength)
+ image = _JAWTRenderer_createImage(renderer);
+ else
+ image = renderer->image;
+ if (image)
+ {
+ Window root;
+ int x, y;
+ unsigned int width, height;
+ unsigned int borderWidth;
+ unsigned int depth;
+
+ if (XGetGeometry(
+ display,
+ drawable,
+ &root,
+ &x, &y,
+ &width, &height,
+ &borderWidth,
+ &depth))
+ {
+ GC gc;
+
+ gc = XCreateGC(display, drawable, 0, NULL);
+ /* XXX How does one check that XCreateGC has succeeded? */
+ XvPutImage(
+ display,
+ port,
+ drawable,
+ gc,
+ image,
+ 0, 0, image->width, image->height,
+ 0, 0, width, height);
+ XFreeGC(display, gc);
+ }
+ }
+ }
+ return JNI_TRUE;
+}
+
+jboolean
+JAWTRenderer_process
+ (JNIEnv *jniEnv, jclass clazz,
+ jlong handle, jobject component,
+ jint *data, jint length,
+ jint width, jint height)
+{
+ if (data && length)
+ {
+ JAWTRenderer *renderer;
+ char *rendererData;
+ jint dataLength;
+
+ renderer = (JAWTRenderer *) handle;
+ rendererData = renderer->data;
+ dataLength = sizeof(jint) * length;
+ if (!rendererData || (renderer->dataCapacity < dataLength))
+ {
+ char *newData;
+
+ newData = realloc(rendererData, dataLength);
+ if (newData)
+ {
+ renderer->data = rendererData = newData;
+ renderer->dataCapacity = dataLength;
+ }
+ else
+ rendererData = NULL;
+ }
+ if (rendererData)
+ {
+ memcpy(rendererData, data, dataLength);
+ renderer->dataLength = dataLength;
+ renderer->dataWidth = width;
+ renderer->dataHeight = height;
+ }
+ else
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+static XvImage *
+_JAWTRenderer_createImage(JAWTRenderer *renderer)
+{
+ XvImage *image;
+ jint width;
+ jint height;
+
+ image = renderer->image;
+ width = renderer->dataWidth;
+ height = renderer->dataHeight;
+ if (image && ((image->width != width) || (image->height != height)))
+ {
+ XFree(image);
+ image = NULL;
+ }
+ if (!image)
+ {
+ image
+ = XvCreateImage(
+ renderer->display,
+ renderer->port,
+ renderer->imageFormatID,
+ NULL,
+ width, height);
+ if (image && ((image->width != width) || (image->height != height)))
+ {
+ XFree(image);
+ image = NULL;
+ }
+ }
+ if (image)
+ {
+ size_t imageDataSize;
+
+ imageDataSize = image->data_size;
+ if (imageDataSize > renderer->dataCapacity)
+ {
+ size_t newDataCapacity;
+ char *newData;
+
+ newDataCapacity = imageDataSize;
+ newData = realloc(renderer->data, newDataCapacity);
+ if (newData)
+ {
+ renderer->data = newData;
+ renderer->dataCapacity = newDataCapacity;
+ }
+ else
+ {
+ XFree(image);
+ image = NULL;
+ }
+ }
+ if (image)
+ {
+ image->data = renderer->data;
+ /*
+ * We've just turned data into image and we don't want to do it
+ * again.
+ */
+ renderer->dataLength = 0;
+ }
+ }
+ renderer->image = image;
+ return image;
+}
+
+static int
+_JAWTRenderer_freeImage(JAWTRenderer *renderer)
+{
+ int ret;
+
+ ret = XFree(renderer->image);
+ renderer->image = NULL;
+ return ret;
+}
+
+static XvPortID
+_JAWTRenderer_grabPort
+ (JAWTRenderer *renderer, JAWT_X11DrawingSurfaceInfo *x11dsi)
+{
+ Display *display;
+ unsigned int ver, rev, req, ev, err;
+ Drawable drawable;
+ unsigned int adaptorInfoCount;
+ XvAdaptorInfo *adaptorInfos;
+ XvPortID grabbedPort;
+
+ display = renderer->display;
+ drawable = renderer->drawable;
+ grabbedPort = -1;
+ if ((Success == XvQueryExtension(display, &ver, &rev, &req, &ev, &err))
+ && (Success
+ == XvQueryAdaptors(
+ display,
+ (Window) drawable,
+ &adaptorInfoCount, &adaptorInfos))
+ && adaptorInfoCount)
+ {
+ int depth;
+ VisualID visualID;
+ unsigned int adaptorInfoIndex;
+
+ depth = x11dsi->depth;
+ visualID = x11dsi->visualID;
+ for (adaptorInfoIndex = 0;
+ adaptorInfoIndex < adaptorInfoCount;
+ adaptorInfoIndex++)
+ {
+ XvAdaptorInfo *adaptorInfo;
+ char type;
+
+ unsigned long formatCount;
+ XvFormat *formats;
+ unsigned long formatIndex;
+ Bool formatIsFound;
+
+ unsigned long portCount;
+ XvPortID basePortID;
+ unsigned long portIndex;
+
+ adaptorInfo = adaptorInfos + adaptorInfoIndex;
+ type = adaptorInfo->type;
+ if (!(type & XvInputMask) || !(type & XvImageMask))
+ continue;
+
+ formatCount = adaptorInfo->num_formats;
+ formats = adaptorInfo->formats;
+ formatIsFound = False;
+ for (formatIndex = 0; formatIndex < formatCount; formatIndex++)
+ {
+ XvFormat *format;
+
+ format = formats + formatIndex;
+ if ((depth == format->depth) && (visualID == format->visual_id))
+ {
+ formatIsFound = True;
+ break;
+ }
+ }
+ if (!formatIsFound)
+ continue;
+
+ portCount = adaptorInfo->num_ports;
+ basePortID = adaptorInfo->base_id;
+ for (portIndex = 0; portIndex < portCount; portIndex++)
+ {
+ XvPortID port;
+ XvImageFormatValues *imageFormats;
+ int imageFormatCount;
+
+ port = basePortID + portIndex;
+ imageFormats
+ = XvListImageFormats(display, port, &imageFormatCount);
+ if (imageFormats && imageFormatCount)
+ {
+ int imageFormatIndex;
+
+ for (imageFormatIndex = 0;
+ imageFormatIndex < imageFormatCount;
+ imageFormatIndex++)
+ {
+ XvImageFormatValues *imageFormat;
+ const char *guid;
+
+ imageFormat = imageFormats + imageFormatIndex;
+ guid = imageFormat->guid;
+ /* I420 */
+ if (('I' == guid[0])
+ && ('4' == guid[1])
+ && ('2' == guid[2])
+ && ('0' == guid[3]))
+ {
+ if (Success
+ == XvGrabPort(display, port, CurrentTime))
+ {
+ grabbedPort = port;
+ renderer->imageFormatID = imageFormat->id;
+ }
+ break;
+ }
+ }
+ XFree(imageFormats);
+ if (-1 != grabbedPort)
+ break;
+ }
+ }
+ if (-1 != grabbedPort)
+ break;
+ }
+ XvFreeAdaptorInfo(adaptorInfos);
+ }
+ renderer->port = grabbedPort;
+ return grabbedPort;
+}
+
+static int
+_JAWTRenderer_ungrabPort(JAWTRenderer *renderer)
+{
+ int ret;
+
+ /* The XvImage is created on the XvPortID. */
+ if (renderer->image)
+ _JAWTRenderer_freeImage(renderer);
+
+ ret = XvUngrabPort(renderer->display, renderer->port, CurrentTime);
+ renderer->port = -1;
+ return ret;
+}

Added: trunk/src/native/jawtrenderer/Makefile.linux
Url: https://sip-communicator.dev.java.net/source/browse/sip-communicator/trunk/src/native/jawtrenderer/Makefile.linux?view=auto&rev=7133

--- (empty file)
+++ trunk/src/native/jawtrenderer/Makefile.linux 2010-05-18 20:34:09+0000
@@ -0,0 +1,15 @@
+JAVA_HOME?=/usr/lib/jvm/java-6-sun
+
+ARCH=\(shell uname \-m | sed \-e s/x86\_64/\-64/ \-e s/i\.86//\) \+TARGET=\.\./\.\./\.\./lib/native/linux(ARCH)/libjawtrenderer.so
+
+CC=gcc -g -std=c99
+CPPFLAGS=-DJNI_IMPLEMENTATION \
+ -fPIC \
+ -Wall -Wreturn-type \
+ -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux
+LDFLAGS=-shared -Wl,--no-undefined
+LIBS=-L$(JAVA_HOME)/jre/lib/amd64 -ljawt -lXv -lX11
+
+\(TARGET\): net\_java\_sip\_communicator\_impl\_neomedia\_jmfext\_media\_renderer\_video\_JAWTRenderer\.c JAWTRenderer\_Linux\.c net\_java\_sip\_communicator\_impl\_neomedia\_jmfext\_media\_renderer\_video\_JAWTRenderer\.h JAWTRenderer\.h \+ (CC) \(CPPFLAGS\) ^ \(LDFLAGS\) \-o @ $(LIBS)

Modified: trunk/src/net/java/sip/communicator/impl/neomedia/codec/video/SwScaler.java
Url: https://sip-communicator.dev.java.net/source/browse/sip-communicator/trunk/src/net/java/sip/communicator/impl/neomedia/codec/video/SwScaler.java?view=diff&rev=7133&p1=trunk/src/net/java/sip/communicator/impl/neomedia/codec/video/SwScaler.java&p2=trunk/src/net/java/sip/communicator/impl/neomedia/codec/video/SwScaler.java&r1=7132&r2=7133

--- trunk/src/net/java/sip/communicator/impl/neomedia/codec/video/SwScaler.java (original)
+++ trunk/src/net/java/sip/communicator/impl/neomedia/codec/video/SwScaler.java 2010-05-18 20:34:09+0000
@@ -33,6 +33,12 @@
private static final Logger logger = Logger.getLogger(SwScaler.class);

/\*\*

+ * The indicator which determines whether this scaler will attempt to keep
+ * the width and height of YUV 420 output even.
+ */
+ private final boolean fixOddYuv420Size;
+
+ /**
* The <tt>FrameProcessingControl</tt> of this <tt>Codec</tt> which allows
* JMF to instruct it to drop frames because it's behind schedule.
*/
@@ -70,6 +76,20 @@
*/
public SwScaler()
{
+ this(false);
+ }
+
+ /**
+ * Initializes a new <tt>SwScaler</tt> instance which can optionally attempt
+ * to keep the width and height of YUV 420 output even.
+ *
+ * @param fixOddYuv420Size <tt>true</tt> to keep the width and height of YUV
+ * 420 output even; otherwise, <tt>false</tt>
+ */
+ protected SwScaler(boolean fixOddYuv420Size)
+ {
+ this.fixOddYuv420Size = fixOddYuv420Size;
+
inputFormats = new Format[]
{
new AVFrameFormat(),
@@ -365,8 +385,6 @@
}
else
{
-//System.err.println(
-// "SwScaler.process: srcPicture= 0x" + Long.toHexString(srcPicture));
FFmpeg.sws_scale(
swsContext,
srcPicture, 0, inputHeight,
@@ -437,6 +455,38 @@
@Override
public Format setOutputFormat(Format format)
{
+ if (fixOddYuv420Size && (format instanceof YUVFormat))
+ {
+ YUVFormat yuvFormat = (YUVFormat) format;
+
+ if (YUVFormat.YUV_420 == yuvFormat.getYuvType())
+ {
+ Dimension size = yuvFormat.getSize();
+
+ if ((size != null) && (size.width > 2) && (size.height > 2))
+ {
+ int width = (size.width >> 1) << 1;
+ int height = (size.height >> 1) << 1;
+
+ if ((width != size.width) || (height != size.height))
+ {
+ format
+ = new YUVFormat(
+ new Dimension(width, height),
+ Format.NOT_SPECIFIED,
+ yuvFormat.getDataType(),
+ yuvFormat.getFrameRate(),
+ yuvFormat.getYuvType(),
+ Format.NOT_SPECIFIED,
+ Format.NOT_SPECIFIED,
+ 0,
+ Format.NOT_SPECIFIED,
+ Format.NOT_SPECIFIED);
+ }
+ }
+ }
+ }
+
Format outputFormat = super.setOutputFormat(format);

    if \(logger\.isDebugEnabled\(\) &amp;&amp; \(outputFormat \!= null\)\)

Modified: trunk/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java
Url: https://sip-communicator.dev.java.net/source/browse/sip-communicator/trunk/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java?view=diff&rev=7133&p1=trunk/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java&p2=trunk/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java&r1=7132&r2=7133

--- trunk/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java (original)
+++ trunk/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java 2010-05-18 20:34:09+0000
@@ -706,9 +706,9 @@
*/
playerScaler = new PlayerScaler(player);

- /* for H264 codec, we will use RTCP feedback
- * for example to advertise sender that we miss
- * a frame
+ /*
+ * For H.264, we will use RTCP feedback. For example, to
+ * tell the sender that we've missed a frame.
*/
if(format.getEncoding().equals("h264/rtp") && usePLI)
{
@@ -1269,6 +1269,8 @@
*/
public PlayerScaler(Player player)
{
+ super(true);
+
this.player = player;
}

Modified: trunk/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/video/JAWTRenderer.java
Url: https://sip-communicator.dev.java.net/source/browse/sip-communicator/trunk/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/video/JAWTRenderer.java?view=diff&rev=7133&p1=trunk/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/video/JAWTRenderer.java&p2=trunk/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/video/JAWTRenderer.java&r1=7132&r2=7133

--- trunk/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/video/JAWTRenderer.java (original)
+++ trunk/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/video/JAWTRenderer.java 2010-05-18 20:34:09+0000
@@ -13,6 +13,7 @@
import javax.media.renderer.*;

import net.java.sip.communicator.impl.neomedia.control.*;
+import net.java.sip.communicator.util.*;

/**
* Implements a <tt>VideoRenderer</tt> which uses JAWT to perform native
@@ -34,13 +35,25 @@
private static final Format[] SUPPORTED_INPUT_FORMATS
= new Format[]
{
- new RGBFormat(
- null,
- Format.NOT_SPECIFIED,
- Format.intArray,
- Format.NOT_SPECIFIED,
- 32,
- 0x00FF0000, 0x0000FF00, 0x000000FF)
+ OSUtils.IS_LINUX
+ ? new YUVFormat(
+ null /* size */,
+ Format.NOT_SPECIFIED /* maxDataLength */,
+ Format.intArray,
+ Format.NOT_SPECIFIED /* frameRate */,
+ YUVFormat.YUV_420,
+ Format.NOT_SPECIFIED /* strideY */,
+ Format.NOT_SPECIFIED /* strideUV */,
+ Format.NOT_SPECIFIED /* offsetY */,
+ Format.NOT_SPECIFIED /* offsetU */,
+ Format.NOT_SPECIFIED /* offsetV */)
+ : new RGBFormat(
+ null,
+ Format.NOT_SPECIFIED,
+ Format.intArray,
+ Format.NOT_SPECIFIED,
+ 32,
+ 0x00FF0000, 0x0000FF00, 0x000000FF)
};

static

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@sip-communicator.dev.java.net
For additional commands, e-mail: dev-help@sip-communicator.dev.java.net