diff --git a/lib/native/freebsd-64/libffmpeg.so b/lib/native/freebsd-64/libffmpeg.so index 579209272..077ddb5cb 100755 Binary files a/lib/native/freebsd-64/libffmpeg.so and b/lib/native/freebsd-64/libffmpeg.so differ diff --git a/lib/native/freebsd/libffmpeg.so b/lib/native/freebsd/libffmpeg.so index c080267be..8610d8059 100755 Binary files a/lib/native/freebsd/libffmpeg.so and b/lib/native/freebsd/libffmpeg.so differ diff --git a/lib/native/linux-64/libffmpeg.so b/lib/native/linux-64/libffmpeg.so index 42096a668..1b9b5c049 100755 Binary files a/lib/native/linux-64/libffmpeg.so and b/lib/native/linux-64/libffmpeg.so differ diff --git a/lib/native/linux/libffmpeg.so b/lib/native/linux/libffmpeg.so index 0622ab4d0..90b7cd8aa 100755 Binary files a/lib/native/linux/libffmpeg.so and b/lib/native/linux/libffmpeg.so differ diff --git a/lib/native/mac/libffmpeg.jnilib b/lib/native/mac/libffmpeg.jnilib index 17d8d444a..569a55c3f 100755 Binary files a/lib/native/mac/libffmpeg.jnilib and b/lib/native/mac/libffmpeg.jnilib differ diff --git a/lib/native/windows-64/ffmpeg.dll b/lib/native/windows-64/ffmpeg.dll index fa3f2a23f..8ffac89e8 100644 Binary files a/lib/native/windows-64/ffmpeg.dll and b/lib/native/windows-64/ffmpeg.dll differ diff --git a/lib/native/windows/ffmpeg.dll b/lib/native/windows/ffmpeg.dll index 9fddfca4b..276aa59c9 100644 Binary files a/lib/native/windows/ffmpeg.dll and b/lib/native/windows/ffmpeg.dll differ diff --git a/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG.c b/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG.c index ec434a565..515a7a036 100644 --- a/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG.c +++ b/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG.c @@ -438,15 +438,15 @@ Java_net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG_avpicture_1get_1 static int image_convert( AVPicture* dst, int dst_pix_fmt, -const AVPicture* src, int pix_fmt, int width, int height) +const AVPicture* src, int pix_fmt, int width, int height, jint newWidth, jint newHeight) { struct SwsContext *img_convert_ctx = sws_getContext( width, height, pix_fmt, - width, height, dst_pix_fmt, SWS_BICUBIC, NULL, NULL, NULL); + newWidth, newHeight, dst_pix_fmt, SWS_BICUBIC, NULL, NULL, NULL); int result = sws_scale(img_convert_ctx, - src->data, src->linesize, 0, height, + (uint8_t**)src->data, (int*)src->linesize, 0, height, dst->data, dst->linesize); sws_freeContext(img_convert_ctx); @@ -454,7 +454,7 @@ const AVPicture* src, int pix_fmt, int width, int height) } JNIEXPORT jint JNICALL -Java_net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG_img_1convert ( +Java_net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG_img_1convert__JIJIII ( JNIEnv *jniEnv, jclass clazz, jlong dst, jint dst_pix_fmt, jlong src, jint pix_fmt, jint width, jint height) { @@ -465,9 +465,50 @@ Java_net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG_img_1convert ( (const AVPicture *) src, (int) pix_fmt, (int) width, + (int) height, + (int) width, (int) height); } +JNIEXPORT jint JNICALL +Java_net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG_img_1convert___3BI_3BIIIII + (JNIEnv *jniEnv, jclass clazz, jbyteArray dst, jint dst_pix_fmt, + jbyteArray src, jint pix_fmt, jint width, jint height, jint newWidth, jint newHeight) +{ + AVPicture dst_dummy; + AVPicture src_dummy; + int size = width * height; + jint ret = 0; +/* + uint8_t* dst_buf = (*jniEnv)->GetPrimitiveArrayCritical(jniEnv, dst, 0); + uint8_t* src_buf = (*jniEnv)->GetPrimitiveArrayCritical(jniEnv, src, 0); +*/ + uint8_t* dst_buf = (*jniEnv)->GetByteArrayElements(jniEnv, dst, 0); + uint8_t* src_buf = (*jniEnv)->GetByteArrayElements(jniEnv, src, 0); + + if(!src || !dst) + { + return -1; + } + + /* assign AVPicture with buffer */ + avpicture_fill(&dst_dummy, dst_buf, (int)dst_pix_fmt, newWidth, newHeight); + avpicture_fill(&src_dummy, src_buf, (int)pix_fmt, width, height); + + ret = image_convert(&dst_dummy, (int)dst_pix_fmt, &src_dummy, (int)pix_fmt, + (int)width, (int)height, (int)newWidth, (int)newHeight); + + /* release pointers */ +/* + (*jniEnv)->ReleasePrimitiveArrayCritical(jniEnv, dst, dst_buf, 0); + (*jniEnv)->ReleasePrimitiveArrayCritical(jniEnv, src, src_buf, 0); +*/ + (*jniEnv)->ReleaseByteArrayElements(jniEnv, dst, dst_buf, 0); + (*jniEnv)->ReleaseByteArrayElements(jniEnv, src, src_buf, 0); + return ret; +} + + JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG_memcpy___3IIIJ ( JNIEnv *jniEnv, jclass clazz, jintArray dst, jint dst_offset, @@ -491,6 +532,13 @@ Java_net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG_getRGB32Format return PIX_FMT_RGB32; } +JNIEXPORT jint JNICALL +Java_net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG_getRGBAFormat + (JNIEnv *env, jclass clazz) +{ + return PIX_FMT_RGBA; +} + JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG_getYUV420PFormat (JNIEnv *env, jclass clazz) diff --git a/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG.h b/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG.h index 0e61df22e..d5825f176 100644 --- a/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG.h +++ b/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG.h @@ -29,6 +29,14 @@ extern "C" { JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG_getRGB32Format (JNIEnv *, jclass); +/* + * Class: net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG + * Method: getRGBAFormat + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG_getRGBAFormat + (JNIEnv *, jclass); + /* * Class: net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG * Method: getYUV420PFormat @@ -458,9 +466,17 @@ JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_codec_video_ * Method: img_convert * Signature: (JIJIII)I */ -JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG_img_1convert +JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG_img_1convert__JIJIII (JNIEnv *, jclass, jlong, jint, jlong, jint, jint, jint); +/* + * Class: net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG + * Method: img_convert + * Signature: ([BI[BIIIII)I + */ +JNIEXPORT jint JNICALL Java_net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG_img_1convert___3BI_3BIIIII + (JNIEnv *, jclass, jbyteArray, jint, jbyteArray, jint, jint, jint, jint, jint); + /* * Class: net_java_sip_communicator_impl_neomedia_codec_video_FFMPEG * Method: memcpy diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/EncodingConfiguration.java b/src/net/java/sip/communicator/impl/neomedia/codec/EncodingConfiguration.java index 0f2641359..ef87ec6c4 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/EncodingConfiguration.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/EncodingConfiguration.java @@ -62,7 +62,8 @@ public class EncodingConfiguration "net.java.sip.communicator.impl.neomedia.codec.video.h264.JNIEncoder", "net.java.sip.communicator.impl.neomedia.codec.video.h264.Packetizer", "net.java.sip.communicator.impl.neomedia.codec.video.h264.JNIDecoder", - "net.java.sip.communicator.impl.neomedia.codec.video.ImageScaler", + //"net.java.sip.communicator.impl.neomedia.codec.video.ImageScaler", + "net.java.sip.communicator.impl.neomedia.codec.video.SwScaler", "net.java.sip.communicator.impl.neomedia.codec.audio.speex.JavaEncoder", "net.java.sip.communicator.impl.neomedia.codec.audio.speex.JavaDecoder", "net.java.sip.communicator.impl.neomedia.codec.audio.ilbc.JavaEncoder", @@ -328,6 +329,9 @@ public void registerCustomCodecs() .getPlugInList(null, null, PlugInManager.CODEC)); boolean commit = false; + /* remove JavaRGBToYUV */ + PlugInManager.removePlugIn("com.sun.media.codec.video.colorspace.JavaRGBToYUV", PlugInManager.CODEC); + for (String className : CUSTOM_CODECS) { diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/video/FFMPEG.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/FFMPEG.java index 2551cc10b..27c702fbe 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/video/FFMPEG.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/FFMPEG.java @@ -24,12 +24,16 @@ public class FFMPEG public static final int FF_MB_DECISION_SIMPLE = 0; public static final int PIX_FMT_RGB32; + + public static final int PIX_FMT_RGBA; public static final int PIX_FMT_YUV420P; public static final int X264_RC_ABR = 2; public static native int getRGB32Format(); + + public static native int getRGBAFormat(); public static native int getYUV420PFormat(); @@ -172,6 +176,10 @@ public static native int avpicture_get_size(int pix_fmt, int width, public static native int img_convert(long dst, int dst_pix_fmt, long src, int pix_fmt, int width, int height); + + public static native int img_convert(byte dst[], int dst_pix_fmt, + byte src[], int pix_fmt, int width, int height, int newWidth, + int newHeight); public static native void memcpy(int[] dst, int dst_offset, int dst_length, long src); @@ -185,7 +193,8 @@ public static native void memcpy(long dst, byte[] src, int src_offset, av_register_all(); avcodec_init(); - PIX_FMT_RGB32 = getRGB32Format(); + PIX_FMT_RGB32 = getRGB32Format(); /* for decoding */ + PIX_FMT_RGBA = getRGBAFormat(); /* for encoding */ PIX_FMT_YUV420P = getYUV420PFormat(); } } 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 new file mode 100644 index 000000000..a8131df63 --- /dev/null +++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/SwScaler.java @@ -0,0 +1,196 @@ +/* + * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.impl.neomedia.codec.video; + +import java.awt.*; + +import javax.media.*; +import javax.media.format.*; + +import net.sf.fmj.media.*; + +/** + * Codec that use libswccale to scale images from one size to + * another and change format (typically RGB to YUV). + * + * @author Sebastien Vincent + */ +public class SwScaler + extends AbstractCodec + implements Codec +{ + + /** + * Supported input formats. + */ + private final Format[] supportedInputFormats = new Format[] { + new RGBFormat(null, -1, Format.byteArray, -1.0f, 32, -1, -1, -1) + }; + + /** + * Supported output formats. + */ + private Format[] supportedOutputFormats = new Format[] { + new YUVFormat(null, -1, Format.byteArray, -1.0f, YUVFormat.YUV_420, -1, -1, 0, -1, -1) + }; + + /** + * Set output size. + * + * @param size size to set + */ + public void setOutputSize(Dimension size) + { + if(size == null) + { + size = new Dimension(640, 480); + } + + Format newFormat = new YUVFormat(size, -1, Format.byteArray, -1.0f, YUVFormat.YUV_420, -1, -1, 0, -1, -1); + supportedOutputFormats[0] = newFormat; + } + + /** + * Get the supported input formats. + * + * @return array of supported input format + */ + @Override + public Format[] getSupportedInputFormats() + { + return supportedInputFormats; + } + + /** + * Get the supported output formats for an input ones. + * + * @param input input format to convert + * @return array of supported output format + */ + @Override + public Format[] getSupportedOutputFormats(Format input) + { + if(input == null) + { + return supportedOutputFormats; + } + + //System.out.println("input: " + ((VideoFormat)input).getSize()); + if(((VideoFormat)supportedOutputFormats[0]).getSize() != null) + { + return supportedOutputFormats; + } + + return new Format[] { new YUVFormat(((VideoFormat)input).getSize(), -1, Format.byteArray, -1.0f, YUVFormat.YUV_420, -1, -1, 0, -1, -1)}; + } + + /** + * Set the input format. + * + * @param format format to set + * @return format + */ + @Override + public Format setInputFormat(Format format) + { + //System.out.println("setInput: " + ((VideoFormat)format).getSize()); + final VideoFormat videoFormat = (VideoFormat) format; + + if (videoFormat.getSize() == null) + return null; // must set a size. + + format = super.setInputFormat(format); + return format; + } + + /** + * Set the output format. + * + * @param format format to set + * @return format + */ + @Override + public Format setOutputFormat(Format format) + { + //System.out.println("setOutput: " + ((VideoFormat)format).getSize()); + format = super.setOutputFormat(format); + return format; + } + + /** + * Process (format convertion, rescale) a buffer. + * + * @param input input buffer + * @param output output buffer + * @return BUFFER_PROCESSED_OK if buffer successfully processed + */ + @Override + public int process(Buffer input, Buffer output) + { + VideoFormat vinput = (VideoFormat)input.getFormat(); + VideoFormat voutput = (VideoFormat)output.getFormat(); + int inputWidth = (int)vinput.getSize().getWidth(); + int inputHeight = (int)vinput.getSize().getHeight(); + int outputWidth = (int)voutput.getSize().getWidth(); + int outputHeight = (int)voutput.getSize().getHeight(); + byte src[] = (byte[])input.getData(); + byte dst[] = (byte[])output.getData(); + int outputSize = 0; + int infmt = 0; + int outfmt = 0; + + if (!checkInputBuffer(input)) + { + return BUFFER_PROCESSED_FAILED; + } + + if (isEOM(input)) + { + propagateEOM(output); // TODO: what about data? can there be any? + return BUFFER_PROCESSED_OK; + } + + /* determine output format and output size needed */ + if(voutput instanceof YUVFormat) + { + /* YUV420P is 12 bpp (bit per pixel) => 1,5 bytes */ + outputSize = (int)(outputWidth * outputHeight * 1.5); + outfmt = FFMPEG.PIX_FMT_YUV420P; + } + else /* RGB format */ + { + outputSize = (outputWidth * outputHeight * 4); + outfmt = FFMPEG.PIX_FMT_RGBA; + } + + /* determine input format */ + if(vinput instanceof YUVFormat) + { + infmt = FFMPEG.PIX_FMT_YUV420P; + } + else /* RGBFormat */ + { + infmt = FFMPEG.PIX_FMT_RGBA; + } + + if(dst == null || dst.length < outputSize) + { + dst = new byte[outputSize]; + } + + /* convertion! */ + //System.out.println("Convert: " + inputWidth + "x" + inputHeight + " to " + outputWidth + "x" + outputHeight); + FFMPEG.img_convert(dst, outfmt, src, infmt, inputWidth, inputHeight, outputWidth, outputHeight); + + output.setData(dst); + output.setLength(dst.length); + output.setOffset(0); + + return BUFFER_PROCESSED_OK; + } +} + diff --git a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java index 78a19d0dd..decfcb025 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java @@ -19,6 +19,7 @@ import net.java.sip.communicator.impl.neomedia.*; import net.java.sip.communicator.impl.neomedia.format.*; import net.java.sip.communicator.impl.neomedia.protocol.*; +import net.java.sip.communicator.impl.neomedia.codec.video.*; import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.neomedia.device.*; import net.java.sip.communicator.service.neomedia.format.*; @@ -1226,7 +1227,7 @@ else if (processorIsPrematurelyClosed * of * @param format the JMF Format to set to processor */ - private void setFormat(Processor processor, Format format) + protected void setFormat(Processor processor, Format format) { TrackControl[] trackControls = processor.getTrackControls(); MediaType mediaType = getMediaType(); @@ -1276,7 +1277,36 @@ private void setFormat(Processor processor, Format format) case VIDEO: if (supportedFormats[0] instanceof VideoFormat) { - supportedFormat + VideoFormat tmp = (VideoFormat)format; + Dimension size = tmp.getSize(); + + if(size != null) + { + /* We have been explictely told to use + * a specified output size so create a + * custom SwScaler that will rescale and + * change format in one call + */ + Codec ar[] = new Codec[1]; + SwScaler scaler = new SwScaler(); + + scaler.setOutputSize(size); + ar[0] = scaler; + + /* add our custom SwScaler to the codec chain so that + * it will be used instead of default SwScaler + */ + try + { + trackControl.setCodecChain(ar); + } + catch(Exception e) + { + System.out.println("Error setCodecChain: " + e); + } + } + + supportedFormat = findFirstMatchingFormat(supportedFormats, format); /* 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 de1341faf..9e735589a 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java @@ -9,6 +9,7 @@ import java.awt.*; import javax.media.*; +import javax.media.format.*; import javax.media.control.*; import javax.media.protocol.*; @@ -47,6 +48,16 @@ public class VideoMediaDeviceSession */ private Player localPlayer = null; + /** + * Output size of the stream. + * + * It is used to specify a different size (generally lesser ones) + * than the capture device provides. Typically one usage can be + * in desktop streaming/sharing session when sender desktop is bigger + * than remote ones. + */ + private Dimension outputSize = null; + /** * Initializes a new VideoMediaDeviceSession instance which is to * represent the work of a MediaStream with a specific video @@ -147,6 +158,31 @@ protected DataSource createCaptureDevice() return captureDevice; } + /** + * Sets the JMF Format in which a specific Processor is to + * output media data. + * + * @param processor the Processor to set the output Format + * of + * @param format the JMF Format to set to processor + */ + @Override + protected void setFormat(Processor processor, Format format) + { + Format newFormat = null; + VideoFormat tmp = (VideoFormat)format; + + /* add a size in the output format, as VideoFormat has no + * set accessors, we recreate the object + */ + if(outputSize != null) + { + newFormat = new VideoFormat(tmp.getEncoding(), outputSize, tmp.getMaxDataLength(), tmp.getDataType(), tmp.getFrameRate()); + } + + super.setFormat(processor, newFormat != null ? newFormat : format); + } + /** * Asserts that a specific MediaDevice is acceptable to be set as * the MediaDevice of this instance. Makes sure that its diff --git a/src/net/java/sip/communicator/impl/neomedia/imgstreaming/NativeScreenCapture.java b/src/net/java/sip/communicator/impl/neomedia/imgstreaming/NativeScreenCapture.java index 66f170ff9..82568e26b 100644 --- a/src/net/java/sip/communicator/impl/neomedia/imgstreaming/NativeScreenCapture.java +++ b/src/net/java/sip/communicator/impl/neomedia/imgstreaming/NativeScreenCapture.java @@ -9,9 +9,10 @@ import java.awt.image.*; /** - * This class uses native code to capture - * desktop screen. It should work for Windows and - * X11-based Unix such as Linux and FreeBSD. + * This class uses native code to capture desktop screen. + * + * It should work for Windows, Mac OS X and X11-based Unix such as Linux + * and FreeBSD. * * @author Sebastien Vincent */