diff options
author | Lyubomir Marinov <lyubomir.marinov@jitsi.org> | 2010-05-18 20:34:09 +0000 |
---|---|---|
committer | Lyubomir Marinov <lyubomir.marinov@jitsi.org> | 2010-05-18 20:34:09 +0000 |
commit | e5dca5d66b0641a1a1bde355f86f53d41e3b7ae5 (patch) | |
tree | 4a2a2d30e8d64c20aab0102059409dba266b2122 /src | |
parent | 537a176b286163a6536cd7dd6c8344c28e3c040e (diff) | |
download | jitsi-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')
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 |