aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLyubomir Marinov <lyubomir.marinov@jitsi.org>2010-05-18 20:34:09 +0000
committerLyubomir Marinov <lyubomir.marinov@jitsi.org>2010-05-18 20:34:09 +0000
commite5dca5d66b0641a1a1bde355f86f53d41e3b7ae5 (patch)
tree4a2a2d30e8d64c20aab0102059409dba266b2122 /src
parent537a176b286163a6536cd7dd6c8344c28e3c040e (diff)
downloadjitsi-e5dca5d66b0641a1a1bde355f86f53d41e3b7ae5.zip
jitsi-e5dca5d66b0641a1a1bde355f86f53d41e3b7ae5.tar.gz
jitsi-e5dca5d66b0641a1a1bde355f86f53d41e3b7ae5.tar.bz2
Commits an initial version of a native VideoRenderer i.e. JAWTRenderer on Linux using the XVideo extension of the X Window System.
Diffstat (limited to 'src')
-rw-r--r--src/native/jawtrenderer/JAWTRenderer.h4
-rw-r--r--src/native/jawtrenderer/JAWTRenderer_Linux.c409
-rw-r--r--src/native/jawtrenderer/Makefile.linux15
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/codec/video/SwScaler.java54
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java8
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/video/JAWTRenderer.java27
6 files changed, 503 insertions, 14 deletions
diff --git a/src/native/jawtrenderer/JAWTRenderer.h b/src/native/jawtrenderer/JAWTRenderer.h
index 6d6ee9d..5505867 100644
--- a/src/native/jawtrenderer/JAWTRenderer.h
+++ b/src/native/jawtrenderer/JAWTRenderer.h
@@ -12,7 +12,7 @@
#include <jawt.h>
#ifdef __cplusplus
-extern "C" { /* } */
+extern "C" {
#endif
void JAWTRenderer_close
@@ -27,7 +27,7 @@ jboolean JAWTRenderer_process
jint width, jint height);
#ifdef __cplusplus
-}
+} /* extern "C" { */
#endif
#endif /* _JAWTRENDERER_H_ */
diff --git a/src/native/jawtrenderer/JAWTRenderer_Linux.c b/src/native/jawtrenderer/JAWTRenderer_Linux.c
new file mode 100644
index 0000000..903551b
--- /dev/null
+++ b/src/native/jawtrenderer/JAWTRenderer_Linux.c
@@ -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;
+}
diff --git a/src/native/jawtrenderer/Makefile.linux b/src/native/jawtrenderer/Makefile.linux
new file mode 100644
index 0000000..4c77f4b
--- /dev/null
+++ b/src/native/jawtrenderer/Makefile.linux
@@ -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)
diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/video/SwScaler.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/SwScaler.java
index 2d48190..c4e1602 100644
--- a/src/net/java/sip/communicator/impl/neomedia/codec/video/SwScaler.java
+++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/SwScaler.java
@@ -33,6 +33,12 @@ public class SwScaler
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 class SwScaler
*/
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 @@ public class SwScaler
}
else
{
-//System.err.println(
-// "SwScaler.process: srcPicture= 0x" + Long.toHexString(srcPicture));
FFmpeg.sws_scale(
swsContext,
srcPicture, 0, inputHeight,
@@ -437,6 +455,38 @@ public class SwScaler
@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() && (outputFormat != null))
diff --git a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java
index 2b76534..fc38745 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java
@@ -706,9 +706,9 @@ public class VideoMediaDeviceSession
*/
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 class VideoMediaDeviceSession
*/
public PlayerScaler(Player player)
{
+ super(true);
+
this.player = player;
}
diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/video/JAWTRenderer.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/video/JAWTRenderer.java
index 4552209..47cd769 100644
--- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/video/JAWTRenderer.java
+++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/renderer/video/JAWTRenderer.java
@@ -13,6 +13,7 @@ import javax.media.format.*;
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 @@ public class JAWTRenderer
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