diff --git a/lib/installer-exclude/jmf.jar b/lib/installer-exclude/jmf.jar index b1caad008..b7e3637d5 100644 Binary files a/lib/installer-exclude/jmf.jar and b/lib/installer-exclude/jmf.jar differ diff --git a/lib/native/freebsd-64/libffmpeg.so b/lib/native/freebsd-64/libffmpeg.so index 657083f88..2c7cb62e8 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 4c81af102..e513c9c89 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 db854a4e2..471023c7d 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 0c369fc76..3f81e0b0e 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 a6defa570..c2bcd57ef 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 2bcfbbde0..f0ac03a39 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 4cc94c57f..d6a1f54b1 100644 Binary files a/lib/native/windows/ffmpeg.dll and b/lib/native/windows/ffmpeg.dll differ diff --git a/lib/os-specific/linux/installer-exclude/jmf.jar b/lib/os-specific/linux/installer-exclude/jmf.jar index 010d17304..8984c22ea 100644 Binary files a/lib/os-specific/linux/installer-exclude/jmf.jar and b/lib/os-specific/linux/installer-exclude/jmf.jar differ diff --git a/lib/os-specific/mac/installer-exclude/jmf.jar b/lib/os-specific/mac/installer-exclude/jmf.jar index a9f9804ab..18dee93a0 100644 Binary files a/lib/os-specific/mac/installer-exclude/jmf.jar and b/lib/os-specific/mac/installer-exclude/jmf.jar differ diff --git a/lib/os-specific/windows/installer-exclude/jmf.jar b/lib/os-specific/windows/installer-exclude/jmf.jar index 83b9a8d1d..28081c04e 100644 Binary files a/lib/os-specific/windows/installer-exclude/jmf.jar and b/lib/os-specific/windows/installer-exclude/jmf.jar differ diff --git a/src/native/ffmpeg/README b/src/native/ffmpeg/README index 43f0d8ce1..14406bc81 100644 --- a/src/native/ffmpeg/README +++ b/src/native/ffmpeg/README @@ -51,6 +51,8 @@ patch -p0 < ffmpeg-23181.diff --disable-network \ --enable-libx264 --enable-gpl --enable-parser=h264 --enable-encoder=libx264 \ --enable-decoder=h264 --enable-muxer=h264 --enable-demuxer=h264 \ + --enable-parser=h263 --enable-encoder=h263p \ + --enable-decoder=h263 --enable-muxer=h263 --enable-demuxer=h263 \ --enable-libmp3lame --enable-encoder=libmp3lame \ --extra-ldflags="-L$MP3LAME_HOME/libmp3lame/.libs -L$X264_HOME" --extra-cflags="-I$MP3LAME_HOME/include -I$X264_HOME" \ --disable-debug \ @@ -72,6 +74,8 @@ Then run: --disable-network \ --enable-libx264 --enable-gpl --enable-parser=h264 --enable-encoder=libx264 \ --enable-decoder=h264 --enable-muxer=h264 --enable-demuxer=h264 \ + --enable-parser=h263 --enable-encoder=h263p \ + --enable-decoder=h263 --enable-muxer=h263 --enable-demuxer=h263 \ --enable-libmp3lame --enable-encoder=libmp3lame \ --extra-ldflags="-L$MP3LAME_HOME/libmp3lame/.libs -L$X264_HOME" --extra-cflags="-I$MP3LAME_HOME/include -I$X264_HOME" \ --disable-debug \ @@ -93,6 +97,8 @@ Note: mingw64-make failed to work (segmentation fault) that's why we fallback to --disable-network \ --enable-libx264 --enable-gpl --enable-parser=h264 --enable-encoder=libx264 \ --enable-decoder=h264 --enable-muxer=h264 --enable-demuxer=h264 \ + --enable-parser=h263 --enable-encoder=h263p \ + --enable-decoder=h263 --enable-muxer=h263 --enable-demuxer=h263 \ --enable-decoder=mjpeg --enable-muxer=mjpeg --enable-demuxer=mjpeg --enable-parser=mjpeg \ --enable-libmp3lame --enable-encoder=libmp3lame \ --extra-ldflags="-L$MP3LAME_HOME/libmp3lame/.libs -L$X264_HOME" --extra-cflags="-I$MP3LAME_HOME/include -I$X264_HOME" \ @@ -103,7 +109,8 @@ Note: mingw64-make failed to work (segmentation fault) that's why we fallback to - Mac OS X Note: removed from configure script the option to add -mdynamic-no-pic unless you compile - for i386 architecture (intel 32-bit). + for i386 architecture (intel 32-bit). For i386, remove "--enable-pic" from the following + command. ./configure \ --enable-pic \ @@ -113,6 +120,8 @@ Note: mingw64-make failed to work (segmentation fault) that's why we fallback to --disable-network \ --enable-libx264 --enable-gpl --enable-parser=h264 --enable-encoder=libx264 \ --enable-decoder=h264 --enable-muxer=h264 --enable-demuxer=h264 \ + --enable-parser=h263 --enable-encoder=h263p \ + --enable-decoder=h263 --enable-muxer=h263 --enable-demuxer=h263 \ --enable-libmp3lame --enable-encoder=libmp3lame \ --extra-ldflags="-fPIC -L$MP3LAME_HOME/libmp3lame/.libs -L$X264_HOME" --extra-cflags="-fPIC -I$MP3LAME_HOME/include -I$X264_HOME" \ --disable-debug \ diff --git a/src/native/ffmpeg/ffmpeg-23181.diff b/src/native/ffmpeg/ffmpeg-23181.diff index 664c093ef..5f9eceaa7 100644 --- a/src/native/ffmpeg/ffmpeg-23181.diff +++ b/src/native/ffmpeg/ffmpeg-23181.diff @@ -70,4 +70,26 @@ Index: libavcodec/libx264.c + return 0; } + +Index: libavcodec/ituh263enc.c +=================================================================== +--- libavcodec/ituh263enc.c (révision 23181) ++++ libavcodec/ituh263enc.c (copie de travail) +@@ -162,7 +162,7 @@ + + put_bits(&s->pb, 3, 7); + put_bits(&s->pb,3,ufep); /* Update Full Extended PTYPE */ +- if (format == 7) ++ if (format == 8) + put_bits(&s->pb,3,6); /* Custom Source Format */ + else + put_bits(&s->pb, 3, format); +@@ -192,7 +192,7 @@ + /* This should be here if PLUSPTYPE */ + put_bits(&s->pb, 1, 0); /* Continuous Presence Multipoint mode: off */ + +- if (format == 7) { ++ if (format == 8) { + /* Custom Picture Format (CPFMT) */ + s->aspect_ratio_info= ff_h263_aspect_to_info(s->avctx->sample_aspect_ratio); diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java b/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java index f848766a4..49c9aba0c 100644 --- a/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java +++ b/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java @@ -173,6 +173,7 @@ public class MediaUtils VideoFormat.H261_RTP); */ + /* H264 */ Map h264FormatParams = new HashMap(); Map h264AdvancedAttributes @@ -198,6 +199,26 @@ public class MediaUtils Constants.H264_RTP, h264FormatParams, h264AdvancedAttributes); + + /* H263+ */ + Map h263FormatParams + = new HashMap(); + Map h263AdvancedAttributes + = new LinkedHashMap(); + + // maximum resolution we can receive is the size of our screen device + h263FormatParams.put("CUSTOM", res.width + "," + res.height + ",2"); + h263FormatParams.put("VGA", "2"); + h263FormatParams.put("CIF", "1"); + h263FormatParams.put("QCIF", "1"); + + addMediaFormats( + MediaFormat.RTP_PAYLOAD_TYPE_UNKNOWN, + "H263-1998", + MediaType.VIDEO, + Constants.H263P_RTP, + h263FormatParams, + h263AdvancedAttributes); } /** diff --git a/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java index f5a232f40..e7daf7404 100644 --- a/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java @@ -16,7 +16,6 @@ import javax.media.protocol.*; import javax.media.rtp.*; -import net.java.sip.communicator.impl.neomedia.codec.*; import net.java.sip.communicator.impl.neomedia.device.*; import net.java.sip.communicator.impl.neomedia.transform.*; import net.java.sip.communicator.service.neomedia.*; @@ -567,7 +566,72 @@ else if(key.equals("imageattr")) { outputSize = res[1]; ((VideoMediaDeviceSession)getDeviceSession()). + setOutputSize(outputSize); + } + } + else if(key.equals("CIF")) + { + Dimension dim = new Dimension(352, 288); + + if(outputSize == null || (outputSize.width < dim.width && + outputSize.height < dim.height)) + { + outputSize = dim; + ((VideoMediaDeviceSession)getDeviceSession()). + setOutputSize(outputSize); + } + } + else if(key.equals("QCIF")) + { + Dimension dim = new Dimension(176, 144); + + if(outputSize == null || (outputSize.width < dim.width && + outputSize.height < dim.height)) + { + outputSize = dim; + ((VideoMediaDeviceSession)getDeviceSession()). + setOutputSize(outputSize); + } + } + else if(key.equals("VGA")) // X-lite send it + { + Dimension dim = new Dimension(640, 480); + + if(outputSize == null || (outputSize.width < dim.width && + outputSize.height < dim.height)) + { + /* X-lite does not display anything if we send 640x480 + * video + */ + outputSize = dim; + ((VideoMediaDeviceSession)getDeviceSession()). + setOutputSize(outputSize); + } + } + else if(key.equals("CUSTOM")) + { + String args[] = value.split(","); + + if(args.length < 3) + { + continue; + } + + try + { + Dimension dim = new Dimension(Integer.parseInt(args[0]), + Integer.parseInt(args[1])); + + if(outputSize == null || (outputSize.width < dim.width + && outputSize.height < dim.height)) + { + outputSize = dim; + ((VideoMediaDeviceSession)getDeviceSession()). setOutputSize(outputSize); + } + } + catch(Exception e) + { } } } diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/Constants.java b/src/net/java/sip/communicator/impl/neomedia/codec/Constants.java index bb15bfcbc..178e32fc4 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/Constants.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/Constants.java @@ -28,6 +28,8 @@ public class Constants public static final String H264 = "h264"; public static final String H264_RTP = "h264/rtp"; + public static final String H263P = "H263-1998"; + public static final String H263P_RTP = "h263-1998/rtp"; /** * Pseudo format representing DTMF tones sent over RTP. 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 85d465793..3a105b587 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/EncodingConfiguration.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/EncodingConfiguration.java @@ -88,6 +88,10 @@ public class EncodingConfiguration : null, "net.java.sip.communicator.impl.neomedia.codec.audio.g722.JNIDecoder", "net.java.sip.communicator.impl.neomedia.codec.audio.g722.JNIEncoder", + "net.java.sip.communicator.impl.neomedia.codec.video.h263p.DePacketizer", + "net.java.sip.communicator.impl.neomedia.codec.video.h263p.JNIDecoder", + "net.java.sip.communicator.impl.neomedia.codec.video.h263p.JNIEncoder", + "net.java.sip.communicator.impl.neomedia.codec.video.h263p.Packetizer", "net.java.sip.communicator.impl.neomedia.codec.video.h264.DePacketizer", "net.java.sip.communicator.impl.neomedia.codec.video.h264.JNIDecoder", "net.java.sip.communicator.impl.neomedia.codec.video.h264.JNIEncoder", @@ -161,10 +165,17 @@ public void initializeFormatPreferences() "H264", VideoMediaFormatImpl.DEFAULT_CLOCK_RATE, 1100); + + setEncodingPreference( + "H263-1998", + VideoMediaFormatImpl.DEFAULT_CLOCK_RATE, + 0); + /* setEncodingPreference( "H263", VideoMediaFormatImpl.DEFAULT_CLOCK_RATE, 1000); + */ setEncodingPreference( "JPEG", VideoMediaFormatImpl.DEFAULT_CLOCK_RATE, @@ -382,6 +393,17 @@ public void registerCustomCodecs() PlugInManager.removePlugIn( "com.sun.media.codec.video.colorspace.JavaRGBConverter", PlugInManager.CODEC); + PlugInManager.removePlugIn( + "com.sun.media.codec.video.colorspace.RGBScaler", + PlugInManager.CODEC); + + /* remove JMF's H263 codec */ + PlugInManager.removePlugIn( + "com.sun.media.codec.video.vh263.NativeDecoder", + PlugInManager.CODEC); + PlugInManager.removePlugIn( + "com.ibm.media.codec.video.h263.NativeEncoder", + PlugInManager.CODEC); for (String className : CUSTOM_CODECS) { diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/FFmpeg.java b/src/net/java/sip/communicator/impl/neomedia/codec/FFmpeg.java index 9a6109d7a..3f7b62fc3 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/FFmpeg.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/FFmpeg.java @@ -18,8 +18,18 @@ public class FFmpeg public static final int CODEC_FLAG_LOOP_FILTER = 0x00000800; + public static final int CODEC_FLAG_H263P_UMV = 0x01000000 ; + + public static final int CODEC_FLAG_AC_PRED = 0x02000000; + + public static final int CODEC_FLAG_H263P_SLICE_STRUCT = 0x10000000; + public static final int CODEC_ID_H264 = 28; + public static final int CODEC_ID_H263 = 5; + + public static final int CODEC_ID_H263P = 20; + public static final int CODEC_ID_MJPEG = 8; public static final int CODEC_ID_MP3 = 0x15000 + 1; diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/video/h263p/DePacketizer.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/h263p/DePacketizer.java new file mode 100644 index 000000000..9b8d26a16 --- /dev/null +++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/h263p/DePacketizer.java @@ -0,0 +1,232 @@ +/* + * 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.h263p; + +import java.util.Arrays; + +import javax.media.*; +import javax.media.format.*; + +import net.java.sip.communicator.impl.neomedia.codec.*; +import net.java.sip.communicator.util.Logger; +import net.sf.fmj.media.*; + +/** + * Depacketizes H.263+ RTP packets in in accord with RFC 4529 "RTP Payload + * Format for ITU-T Rec. H.263 Video". + * + * @author Sebastien Vincent + * @author Lubomir Marinov + */ +public class DePacketizer + extends AbstractCodecExt +{ + /** + * The Logger used by the DePacketizer class and its + * instances for logging output. + */ + private static final Logger logger = Logger.getLogger(DePacketizer.class); + + /** + * The size of the padding at the end of the output data of this + * DePacketizer expected by the H.263+ decoder. + */ + private final int outputPaddingSize = FFmpeg.FF_INPUT_BUFFER_PADDING_SIZE; + + /** + * Keeps track of last (input) sequence number in order to avoid + * inconsistent data. + */ + private long lastSequenceNumber = -1; + + /** + * The indicator which determines whether incomplete buffer packets are + * output from the H.263+ DePacketizer to the decoder. + */ + private static final boolean OUTPUT_INCOMPLETE_BUFFER = true; + + /** + * Initializes a new DePacketizer instance which is to depacketize + * H.263+ RTP packet. + */ + public DePacketizer() + { + super( + "H263+ DePacketizer", + VideoFormat.class, + new VideoFormat[] { new VideoFormat(Constants.H263P) }); + + inputFormats + = new VideoFormat[] { new VideoFormat(Constants.H263P_RTP) }; + } + + /** + * Close the Codec. + */ + protected void doClose() + { + } + + /** + * Opens this Codec and acquires the resources that it needs to + * operate. A call to {@link PlugIn#open()} on this instance will result in + * a call to doOpen only if {@link AbstractCodec#opened} is + * false. All required input and/or output formats are assumed to + * have been set on this Codec before doOpen is called. + * + * @throws ResourceUnavailableException if any of the resources that this + * Codec needs to operate cannot be acquired + * @see AbstractCodecExt#doOpen() + */ + protected void doOpen() + throws ResourceUnavailableException + { + } + + /** + * Processes (depacketizes) a buffer. + * + * @param inBuffer input buffer + * @param outBuffer output buffer + * @return BUFFER_PROCESSED_OK if buffer has been successfully + * processed + */ + protected int doProcess(Buffer inBuffer, Buffer outBuffer) + { + long sequenceNumber = inBuffer.getSequenceNumber(); + + if ((lastSequenceNumber != -1) + && ((sequenceNumber - lastSequenceNumber) != 1)) + { + int ret = 0; + + /* maybe we lost a frame somewhere or the sequence number reach + * its maximum number + */ + if (logger.isTraceEnabled()) + logger.trace( + "Dropped RTP packets upto sequenceNumber " + + lastSequenceNumber + + " and continuing with sequenceNumber " + + sequenceNumber); + + ret = reset(outBuffer); + + if ((ret & OUTPUT_BUFFER_NOT_FILLED) == 0) + { + lastSequenceNumber = -1; + return ret; + } + } + + lastSequenceNumber = sequenceNumber; + + byte[] in = (byte[]) inBuffer.getData(); + int inLength = inBuffer.getLength(); + int inOffset = inBuffer.getOffset(); + int outOffset = outBuffer.getOffset(); + + if(inLength < 3) + { + return BUFFER_PROCESSED_FAILED; + } + + boolean pBit = ((in[inOffset] & 0x04) > 0); + boolean vBit = ((in[inOffset] & 0x02) > 0);; + int plen = ((in[inOffset] & 0x01) << 5) + + ((in[inOffset + 1] & 0xF8) >> 3); + int dataLength = inLength - plen - (vBit ? 1 : 0) - (pBit ? 0 : 2); + + byte out[] = validateByteArraySize(outBuffer, outOffset + dataLength + + outputPaddingSize); + + if(pBit) + { + out[0] = 0x00; + out[1] = 0x00; + } + + if(vBit) + { + /* ignore VRC */ + } + + if(plen > 0) + { + if(logger.isInfoEnabled()) + { + logger.info("Extra picture header present PLEN=" + plen); + } + } + + System.arraycopy(in, inOffset + 2 + (vBit ? 1 : 0) + plen, + out, outOffset + (pBit ? 2 : 0), dataLength - (pBit ? 2 : 0)); + + padOutput(out, outOffset + dataLength); + + outBuffer.setLength(outOffset + dataLength); + outBuffer.setSequenceNumber(sequenceNumber); + + /* + * The RTP marker bit is set for the very last packet of the access unit + * indicated by the RTP time stamp to allow an efficient playout buffer + * handling. Consequently, we have to output it as well. + */ + if ((inBuffer.getFlags() & Buffer.FLAG_RTP_MARKER) != 0) + { + outBuffer.setFlags(outBuffer.getFlags() | Buffer.FLAG_RTP_MARKER); + outBuffer.setOffset(0); + return BUFFER_PROCESSED_OK; + } + else + { + outBuffer.setOffset(outOffset + dataLength); + return OUTPUT_BUFFER_NOT_FILLED; + } + } + + /** + * Appends {@link #outputPaddingSize} number of bytes to out + * beginning at index outOffset. The specified out is + * expected to be large enough to accommodate the mentioned number of bytes. + * + * @param out the buffer in which outputPaddingSize number of bytes + * are to be written + * @param outOffset the index in outOffset at which the writing of + * outputPaddingSize number of bytes is to begin + */ + private void padOutput(byte[] out, int outOffset) + { + Arrays.fill(out, outOffset, outOffset + outputPaddingSize, (byte) 0); + } + + /** + * Resets the states of this DePacketizer and a specific output + * Buffer so that they are ready to have this DePacketizer + * process input RTP payloads. + * + * @param outBuffer the output Buffer to be reset + * @return the flags such as BUFFER_PROCESSED_OK and + * OUTPUT_BUFFER_NOT_FILLED to be returned by + * {@link #process(Buffer, Buffer)} + */ + private int reset(Buffer outBuffer) + { + if (OUTPUT_INCOMPLETE_BUFFER && outBuffer.getLength() > 0) + { + Object outData = outBuffer.getData(); + + if (outData instanceof byte[]) + { + return (BUFFER_PROCESSED_OK | INPUT_BUFFER_NOT_CONSUMED); + } + } + + outBuffer.setLength(0); + return OUTPUT_BUFFER_NOT_FILLED; + } +} diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/video/h263p/JNIDecoder.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/h263p/JNIDecoder.java new file mode 100644 index 000000000..48801eb2c --- /dev/null +++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/h263p/JNIDecoder.java @@ -0,0 +1,349 @@ +/* + * 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.h263p; + +import java.awt.*; + +import javax.media.*; +import javax.media.format.*; + +import net.java.sip.communicator.impl.neomedia.codec.*; +import net.java.sip.communicator.impl.neomedia.codec.video.*; +import net.sf.fmj.media.*; + +/** + * Implements a H.263+ decoder. + * + * @author Sebastien Vincent + * @author Lubomir Marinov + */ +public class JNIDecoder + extends AbstractCodec +{ + /** + * Plugin name. + */ + private static final String PLUGIN_NAME = "H.263+ Decoder"; + + /** + * The default output VideoFormat. + */ + private static final VideoFormat[] DEFAULT_OUTPUT_FORMATS + = new VideoFormat[] { new AVFrameFormat() }; + + /** + * Array of output VideoFormats. + */ + private final VideoFormat[] outputFormats; + + /** + * If decoder has got a picture. + */ + private final boolean[] got_picture = new boolean[1]; + + /** + * The codec context native pointer we will use. + */ + private long avcontext = 0; + + /** + * The decoded data is stored in avpicture in native ffmpeg format (YUV). + */ + private long avframe = 0; + + /** + * The last known width of {@link #avcontext} i.e. the video output by this + * JNIDecoder. Used to detect changes in the output size. + */ + private int width = 0; + + /** + * The last known height of {@link #avcontext} i.e. the video output by this + * JNIDecoder. Used to detect changes in the output size. + */ + private int height = 0; + + /** + * Initializes a new JNIDecoder instance which is to decode H.263+ + * encoded data into frames in YUV format. + */ + public JNIDecoder() + { + inputFormats + = new VideoFormat[] { new VideoFormat(Constants.H263P) }; + outputFormats + = new VideoFormat[] + { + new AVFrameFormat( + new Dimension( + Constants.VIDEO_WIDTH, + Constants.VIDEO_HEIGHT), + ensureFrameRate(Format.NOT_SPECIFIED), + FFmpeg.PIX_FMT_YUV420P, + Format.NOT_SPECIFIED) + }; + + Dimension outputSize = outputFormats[0].getSize(); + + width = outputSize.width; + height = outputSize.height; + } + + /** + * Check Format. + * + * @param format Format to check + * @return true if Format is H263P_RTP + */ + public boolean checkFormat(Format format) + { + return format.getEncoding().equals(Constants.H263P_RTP); + } + + /** + * Close Codec. + */ + @Override + public synchronized void close() + { + if (opened) + { + opened = false; + super.close(); + + FFmpeg.avcodec_close(avcontext); + FFmpeg.av_free(avcontext); + avcontext = 0; + + FFmpeg.av_free(avframe); + avframe = 0; + } + } + + /** + * Ensure frame rate. + * + * @param frameRate frame rate + * @return frame rate + */ + private float ensureFrameRate(float frameRate) + { + return frameRate; + } + + /** + * Get matching outputs for a specified input Format. + * + * @param in input Format + * @return array of matching outputs or null if there are no matching + * outputs. + */ + protected Format[] getMatchingOutputFormats(Format in) + { + VideoFormat ivf = (VideoFormat) in; + Dimension inSize = ivf.getSize(); + Dimension outSize; + + // return the default size/currently decoder and encoder + // set to transmit/receive at this size + if (inSize == null) + { + VideoFormat ovf = outputFormats[0]; + + if (ovf == null) + return null; + else + outSize = ovf.getSize(); + } + else + outSize = inSize; // Output in same size as input. + + return + new Format[] + { + new AVFrameFormat( + outSize, + ensureFrameRate(ivf.getFrameRate()), + FFmpeg.PIX_FMT_YUV420P, + Format.NOT_SPECIFIED) + }; + } + + /** + * Get plugin name. + * + * @return "H.263+ Decoder" + */ + @Override + public String getName() + { + return PLUGIN_NAME; + } + + /** + * Get all supported output Formats. + * + * @param in input Format to determine corresponding output + * Format/tt>s + * @return array of supported Format + */ + public Format[] getSupportedOutputFormats(Format in) + { + if (in == null) + return DEFAULT_OUTPUT_FORMATS; + + // mismatch input format + if (!(in instanceof VideoFormat) + || (AbstractCodecExt.matches(in, inputFormats) == null)) + return new Format[0]; + + // match input format + return getMatchingOutputFormats(in); + } + + /** + * Inits the codec instances. + * + * @throws ResourceUnavailableException if codec initialization failed + */ + @Override + public synchronized void open() + throws ResourceUnavailableException + { + if (opened) + return; + + /* from ffmpeg -formats output: "For example, the h263 decoder + * corresponds to the h263 and h263p encoders". That's why we use + * CODEC_ID_H263 for the decoder side (instead of CODEC_ID_H263P). + */ + long avcodec = FFmpeg.avcodec_find_decoder(FFmpeg.CODEC_ID_H263); + + avcontext = FFmpeg.avcodec_alloc_context(); + FFmpeg.avcodeccontext_set_workaround_bugs(avcontext, + FFmpeg.FF_BUG_AUTODETECT); + + if (FFmpeg.avcodec_open(avcontext, avcodec) < 0) + throw new RuntimeException("Could not open codec CODEC_ID_H263"); + + avframe = FFmpeg.avcodec_alloc_frame(); + + opened = true; + super.open(); + } + + /** + * Decodes H.263+ media data read from a specific input Buffer into + * a specific output Buffer. + * + * @param inBuffer input Buffer + * @param outBuffer output Buffer + * @return BUFFER_PROCESSED_OK if inBuffer has been + * successfully processed + */ + public synchronized int process(Buffer inBuffer, Buffer outBuffer) + { + if (!checkInputBuffer(inBuffer)) + return BUFFER_PROCESSED_FAILED; + + if (isEOM(inBuffer) || !opened) + { + propagateEOM(outBuffer); + return BUFFER_PROCESSED_OK; + } + + if (inBuffer.isDiscard()) + { + outBuffer.setDiscard(true); + return BUFFER_PROCESSED_OK; + } + + // Ask FFmpeg to decode. + got_picture[0] = false; + // TODO Take into account the offset of inputBuffer. + FFmpeg.avcodec_decode_video( + avcontext, + avframe, + got_picture, + (byte[]) inBuffer.getData(), inBuffer.getLength()); + + if (!got_picture[0]) + { + outBuffer.setDiscard(true); + return BUFFER_PROCESSED_OK; + } + + // format + int width = FFmpeg.avcodeccontext_get_width(avcontext); + int height = FFmpeg.avcodeccontext_get_height(avcontext); + + if ((width > 0) + && (height > 0) + && ((this.width != width) || (this.height != height))) + { + this.width = width; + this.height = height; + + // Output in same size and frame rate as input. + Dimension outSize = new Dimension(this.width, this.height); + VideoFormat inFormat = (VideoFormat) inBuffer.getFormat(); + float outFrameRate = ensureFrameRate(inFormat.getFrameRate()); + + outputFormat + = new AVFrameFormat( + outSize, + outFrameRate, + FFmpeg.PIX_FMT_YUV420P, + Format.NOT_SPECIFIED); + } + outBuffer.setFormat(outputFormat); + + // data + Object out = outBuffer.getData(); + + if (!(out instanceof AVFrame) || (((AVFrame) out).getPtr() != avframe)) + outBuffer.setData(new AVFrame(avframe)); + + // timeStamp + long pts = FFmpeg.AV_NOPTS_VALUE; // TODO avframe_get_pts(avframe); + + if (pts == FFmpeg.AV_NOPTS_VALUE) + outBuffer.setTimeStamp(Buffer.TIME_UNKNOWN); + else + { + outBuffer.setTimeStamp(pts); + + int outFlags = outBuffer.getFlags(); + + outFlags |= Buffer.FLAG_RELATIVE_TIME; + outFlags &= ~(Buffer.FLAG_RTP_TIME | Buffer.FLAG_SYSTEM_TIME); + outBuffer.setFlags(outFlags); + } + + return BUFFER_PROCESSED_OK; + } + + /** + * Sets the Format of the media data to be input for processing in + * this Codec. + * + * @param format the Format of the media data to be input for + * processing in this Codec + * @return the Format of the media data to be input for processing + * in this Codec if format is compatible with this + * Codec; otherwise, null + */ + @Override + public Format setInputFormat(Format format) + { + Format setFormat = super.setInputFormat(format); + + if (setFormat != null) + reset(); + return setFormat; + } +} diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/video/h263p/JNIEncoder.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/h263p/JNIEncoder.java new file mode 100644 index 000000000..c57481482 --- /dev/null +++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/h263p/JNIEncoder.java @@ -0,0 +1,463 @@ +/* + * 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.h263p; + +import java.awt.*; + +import javax.media.*; +import javax.media.format.*; + +import net.java.sip.communicator.impl.neomedia.codec.*; +import net.sf.fmj.media.*; + +/** + * Implements a H.263+ encoder. + * + * @author Sebastien Vincent + * @author Lubomir Marinov + */ +public class JNIEncoder + extends AbstractCodec +{ + /** + * The frame rate to be assumed by JNIEncoder instance in the + * absence of any other frame rate indication. + */ + private static final int DEFAULT_FRAME_RATE = 30; + + /** + * Default output formats. + */ + private static final Format[] DEFAULT_OUTPUT_FORMATS + = { new VideoFormat(Constants.H263P) }; + + /** + * Key frame every 300 frames. + */ + private static final int IFRAME_INTERVAL = 300; + + /** + * Name of the code. + */ + private static final String PLUGIN_NAME = "H.263+ Encoder"; + + /** + * The codec we will use. + */ + private long avcontext = 0; + + /** + * The encoded data is stored in avpicture. + */ + private long avframe = 0; + + /** + * We use this buffer to supply data to encoder. + */ + private byte[] encFrameBuffer = null; + + /** + * The supplied data length. + */ + private int encFrameLen = 0; + + /** + * The raw frame buffer. + */ + private long rawFrameBuffer = 0; + + /** + * Next interval for an automatic keyframe. + */ + private int framesSinceLastIFrame = IFRAME_INTERVAL + 1; + + /** + * Initializes a new JNIEncoder instance. + */ + public JNIEncoder() + { + inputFormats + = new Format[] + { + new YUVFormat( + null, + Format.NOT_SPECIFIED, + Format.byteArray, + DEFAULT_FRAME_RATE, + YUVFormat.YUV_420, + Format.NOT_SPECIFIED, Format.NOT_SPECIFIED, + 0, Format.NOT_SPECIFIED, Format.NOT_SPECIFIED) + }; + + inputFormat = null; + outputFormat = null; + } + + /** + * Closes this Codec. + */ + @Override + public synchronized void close() + { + if (opened) + { + opened = false; + super.close(); + + FFmpeg.avcodec_close(avcontext); + FFmpeg.av_free(avcontext); + avcontext = 0; + + FFmpeg.av_free(avframe); + avframe = 0; + FFmpeg.av_free(rawFrameBuffer); + rawFrameBuffer = 0; + + encFrameBuffer = null; + } + } + + /** + * Gets the matching output formats for a specific format. + * + * @param in input format + * @return array for formats matching input format + */ + private Format[] getMatchingOutputFormats(Format in) + { + VideoFormat videoIn = (VideoFormat) in; + + return + new VideoFormat[] + { + new VideoFormat( + Constants.H263P, + videoIn.getSize(), + Format.NOT_SPECIFIED, + Format.byteArray, + videoIn.getFrameRate()) + }; + } + + /** + * Gets the name of this Codec. + * + * @return codec name + */ + @Override + public String getName() + { + return PLUGIN_NAME; + } + + /** + * Returns the list of formats supported at the output. + * + * @param in input Format to determine corresponding output + * Format/tt>s + * @return array of formats supported at output + */ + public Format[] getSupportedOutputFormats(Format in) + { + // null input format + if (in == null) + return DEFAULT_OUTPUT_FORMATS; + + // mismatch input format + if (!(in instanceof VideoFormat) + || (null == AbstractCodecExt.matches(in, inputFormats))) + return new Format[0]; + + return getMatchingOutputFormats(in); + } + + /** + * Opens this Codec. + */ + @Override + public synchronized void open() + throws ResourceUnavailableException + { + if (opened) + return; + + if (inputFormat == null) + throw new ResourceUnavailableException("No input format selected"); + if (outputFormat == null) + throw new ResourceUnavailableException("No output format selected"); + + VideoFormat outputVideoFormat = (VideoFormat) outputFormat; + Dimension size = outputVideoFormat.getSize(); + int width = size.width; + int height = size.height; + + long avcodec = FFmpeg.avcodec_find_encoder(FFmpeg.CODEC_ID_H263P); + + avcontext = FFmpeg.avcodec_alloc_context(); + + System.out.println("transmit " + width + "x" + height); + FFmpeg.avcodeccontext_set_pix_fmt(avcontext, FFmpeg.PIX_FMT_YUV420P); + FFmpeg.avcodeccontext_set_size(avcontext, width, height); + FFmpeg.avcodeccontext_set_qcompress(avcontext, 0.6f); + + int bitRate = 256000; + int frameRate = (int) outputVideoFormat.getFrameRate(); + + if (frameRate == Format.NOT_SPECIFIED) + frameRate = DEFAULT_FRAME_RATE; + + // average bit rate + FFmpeg.avcodeccontext_set_bit_rate(avcontext, bitRate); + FFmpeg.avcodeccontext_set_bit_rate_tolerance(avcontext, + bitRate / (frameRate - 1)); + //FFmpeg.avcodeccontext_set_rc_max_rate(avcontext, bitRate); + //FFmpeg.avcodeccontext_set_sample_aspect_ratio(avcontext, 0, 0); + + // time_base should be 1 / frame rate + FFmpeg.avcodeccontext_set_time_base(avcontext, 1, frameRate); + //FFmpeg.avcodeccontext_set_quantizer(avcontext, 10, 51, 4); + + FFmpeg.avcodeccontext_set_mb_decision(avcontext, + FFmpeg.FF_MB_DECISION_SIMPLE); + + //FFmpeg.avcodeccontext_set_rc_eq(avcontext, "blurCplx^(1-qComp)"); + + FFmpeg.avcodeccontext_add_flags(avcontext, + FFmpeg.CODEC_FLAG_LOOP_FILTER); + FFmpeg.avcodeccontext_add_flags(avcontext, + FFmpeg.CODEC_FLAG_AC_PRED); + FFmpeg.avcodeccontext_add_flags(avcontext, + FFmpeg.CODEC_FLAG_H263P_UMV); + FFmpeg.avcodeccontext_add_flags(avcontext, + FFmpeg.CODEC_FLAG_H263P_SLICE_STRUCT); + + FFmpeg.avcodeccontext_set_me_method(avcontext, 6); + FFmpeg.avcodeccontext_set_me_subpel_quality(avcontext, 2); + FFmpeg.avcodeccontext_set_me_range(avcontext, 18); + FFmpeg.avcodeccontext_set_me_cmp(avcontext, FFmpeg.FF_CMP_CHROMA); + FFmpeg.avcodeccontext_set_scenechange_threshold(avcontext, 40); + + // Constant quality mode (also known as constant ratefactor) + //FFmpeg.avcodeccontext_set_crf(avcontext, 0); + //FFmpeg.avcodeccontext_set_rc_buffer_size(avcontext, 0); + FFmpeg.avcodeccontext_set_gop_size(avcontext, IFRAME_INTERVAL); + //FFmpeg.avcodeccontext_set_i_quant_factor(avcontext, 1f / 1.4f); + + //FFmpeg.avcodeccontext_set_refs(avcontext, 2); + //FFmpeg.avcodeccontext_set_trellis(avcontext, 2); + + if (FFmpeg.avcodec_open(avcontext, avcodec) < 0) + { + throw + new ResourceUnavailableException( + "Could not open codec. (size= " + + width + "x" + height + + ")"); + } + + encFrameLen = (width * height * 3) / 2; + + rawFrameBuffer = FFmpeg.av_malloc(encFrameLen); + + avframe = FFmpeg.avcodec_alloc_frame(); + + int sizeInBytes = width * height; + + FFmpeg.avframe_set_data( + avframe, + rawFrameBuffer, + sizeInBytes, + sizeInBytes / 4); + FFmpeg.avframe_set_linesize(avframe, width, width / 2, width / 2); + + encFrameBuffer = new byte[encFrameLen]; + + opened = true; + + super.open(); + } + + /** + * Processes/encodes a buffer. + * + * @param inBuffer input buffer + * @param outBuffer output buffer + * @return BUFFER_PROCESSED_OK if buffer has been successfully + * processed + */ + public synchronized int process(Buffer inBuffer, Buffer outBuffer) + { + if (isEOM(inBuffer)) + { + propagateEOM(outBuffer); + reset(); + return BUFFER_PROCESSED_OK; + } + if (inBuffer.isDiscard()) + { + outBuffer.setDiscard(true); + reset(); + return BUFFER_PROCESSED_OK; + } + + Format inFormat = inBuffer.getFormat(); + + if ((inFormat != inputFormat) && !inFormat.matches(inputFormat)) + setInputFormat(inFormat); + + if (inBuffer.getLength() < 3) + { + outBuffer.setDiscard(true); + reset(); + return BUFFER_PROCESSED_OK; + } + + // copy data to avframe + FFmpeg.memcpy( + rawFrameBuffer, + (byte[]) inBuffer.getData(), inBuffer.getOffset(), + encFrameLen); + + if (framesSinceLastIFrame >= IFRAME_INTERVAL) + { + FFmpeg.avframe_set_key_frame(avframe, true); + framesSinceLastIFrame = 0; + } + else + { + framesSinceLastIFrame++; + FFmpeg.avframe_set_key_frame(avframe, false); + } + + // encode data + int encLen + = FFmpeg.avcodec_encode_video( + avcontext, + encFrameBuffer, encFrameLen, + avframe); + + /* + * Do not always allocate a new data array for outBuffer, try to reuse + * the existing one if it is suitable. + */ + Object outData = outBuffer.getData(); + byte[] out; + + if (outData instanceof byte[]) + { + out = (byte[]) outData; + if (out.length < encLen) + out = null; + } + else + out = null; + if (out == null) + out = new byte[encLen]; + + System.arraycopy(encFrameBuffer, 0, out, 0, encLen); + + outBuffer.setData(out); + outBuffer.setLength(encLen); + outBuffer.setOffset(0); + outBuffer.setTimeStamp(inBuffer.getTimeStamp()); + + return BUFFER_PROCESSED_OK; + } + + /** + * Sets the input format. + * + * @param in format to set + * @return format + */ + @Override + public Format setInputFormat(Format in) + { + // mismatch input format + if (!(in instanceof VideoFormat) + || (null == AbstractCodecExt.matches(in, inputFormats))) + return null; + + YUVFormat yuv = (YUVFormat) in; + + if (yuv.getOffsetU() > yuv.getOffsetV()) + return null; + + Dimension size = yuv.getSize(); + + if (size == null) + size = new Dimension(Constants.VIDEO_WIDTH, Constants.VIDEO_HEIGHT); + + int strideY = size.width; + int strideUV = strideY / 2; + int offsetU = strideY * size.height; + int offsetV = offsetU + strideUV * size.height / 2; + + int yuvMaxDataLength = (strideY + strideUV) * size.height; + + inputFormat + = new YUVFormat( + size, + yuvMaxDataLength + FFmpeg.FF_INPUT_BUFFER_PADDING_SIZE, + Format.byteArray, + yuv.getFrameRate(), + YUVFormat.YUV_420, + strideY, strideUV, + 0, offsetU, offsetV); + + // Return the selected inputFormat + return inputFormat; + } + + /** + * Sets the Format in which this Codec is to output media + * data. + * + * @param out the Format in which this Codec is to + * output media data + * @return the Format in which this Codec is currently + * configured to output media data or null if format was + * found to be incompatible with this Codec + */ + @Override + public Format setOutputFormat(Format out) + { + // mismatch output format + if (!(out instanceof VideoFormat) + || (null + == AbstractCodecExt.matches( + out, + getMatchingOutputFormats(inputFormat)))) + return null; + + VideoFormat videoOut = (VideoFormat) out; + Dimension outSize = videoOut.getSize(); + + if (outSize == null) + { + Dimension inSize = ((VideoFormat) inputFormat).getSize(); + + outSize + = (inSize == null) + ? new Dimension( + Constants.VIDEO_WIDTH, + Constants.VIDEO_HEIGHT) + : inSize; + } + + outputFormat + = new VideoFormat( + videoOut.getEncoding(), + outSize, + Format.NOT_SPECIFIED, + Format.byteArray, + videoOut.getFrameRate()); + + // Return the selected outputFormat + return outputFormat; + } +} diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/video/h263p/Packetizer.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/h263p/Packetizer.java new file mode 100644 index 000000000..e50638df5 --- /dev/null +++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/h263p/Packetizer.java @@ -0,0 +1,419 @@ +/* + * 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.h263p; + +import java.awt.*; +import java.util.*; +import java.util.List; // disambiguation + +import javax.media.*; +import javax.media.format.*; + +import net.java.sip.communicator.impl.neomedia.codec.*; +import net.sf.fmj.media.*; + +/** + * Packetizes H.263+ encoded data into RTP packets in accord with RFC 4529 + * "RTP Payload Format for ITU-T Rec. H.263 Video". + * + * @author Sebastien Vincent + * @author Lubomir Marinov + */ +public class Packetizer + extends AbstractPacketizer +{ + /** + * Array of default output formats. + */ + private static final Format[] DEFAULT_OUTPUT_FORMATS + = { new VideoFormat(Constants.H263P_RTP) }; + + /** + * Maximum payload size without the headers. + */ + public static final int MAX_PAYLOAD_SIZE = 1024; + + /** + * Name of the plugin. + */ + private static final String PLUGIN_NAME = "H263+ Packetizer"; + + /** + * The sequence number of the next RTP packet to be output by this + * Packetizer. + */ + private int sequenceNumber = 0; + + /** + * The list of H263+ "Start code" video packets to be sent as payload in RTP + * packets. + */ + private final List videoPkts = new LinkedList(); + + /** + * The timeStamp of the RTP packets in which H263+ packets are to be sent. + */ + private long timeStamp = 0; + + /** + * Initializes a new Packetizer instance which is to packetize + * H.263+ encoded data into RTP packets in accord with + * RFC 4529 "RTP Payload Format for ITU-T Rec. H.263 Video". + */ + public Packetizer() + { + inputFormats = new Format[] { new VideoFormat(Constants.H263P) }; + + inputFormat = null; + outputFormat = null; + } + + /** + * Get the matching output formats for a specific format. + * + * @param in input format + * @return array for formats matching input format + */ + private Format[] getMatchingOutputFormats(Format in) + { + VideoFormat videoIn = (VideoFormat) in; + Dimension inSize = videoIn.getSize(); + + return + new VideoFormat[] + { + new VideoFormat( + Constants.H263P_RTP, + inSize, + Format.NOT_SPECIFIED, + Format.byteArray, + videoIn.getFrameRate()) + }; + } + + /** + * Get codec name. + * + * @return codec name + */ + @Override + public String getName() + { + return PLUGIN_NAME; + } + + /** + * Return the list of formats supported at the output. + * @param in input Format to determine corresponding output + * Format/tt>s + * @return array of formats supported at output + */ + public Format[] getSupportedOutputFormats(Format in) + { + // null input format + if (in == null) + return DEFAULT_OUTPUT_FORMATS; + + // mismatch input format + if (!(in instanceof VideoFormat) + || (null == AbstractCodecExt.matches(in, inputFormats))) + { + return new Format[0]; + } + + return getMatchingOutputFormats(in); + } + + /** + * Open this Packetizer. + * + * @throws ResourceUnavailableException if something goes wrong during + * initialization of the Packetizer. + */ + @Override + public synchronized void open() + throws ResourceUnavailableException + { + if (!opened) + { + videoPkts.clear(); + sequenceNumber = 0; + + super.open(); + opened = true; + } + } + + /** + * Close this Packetizer. + */ + @Override + public synchronized void close() + { + if (opened) + { + videoPkts.clear(); + opened = false; + super.close(); + } + } + + /** + * Sets the input format. + * + * @param in format to set + * @return format + */ + @Override + public Format setInputFormat(Format in) + { + /* + * Return null if the specified input Format is incompatible with this + * Packetizer. + */ + if (!(in instanceof VideoFormat) + || null == AbstractCodecExt.matches(in, inputFormats)) + return null; + + inputFormat = in; + return in; + } + + /** + * Sets the Format in which this Codec is to output media + * data. + * + * @param out the Format in which this Codec is to + * output media data + * @return the Format in which this Codec is currently + * configured to output media data or null if format was + * found to be incompatible with this Codec + */ + @Override + public Format setOutputFormat(Format out) + { + /* + * Return null if the specified output Format is incompatible with this + * Packetizer. + */ + if (!(out instanceof VideoFormat) + || (null + == AbstractCodecExt.matches( + out, + getMatchingOutputFormats(inputFormat)))) + return null; + + VideoFormat videoOut = (VideoFormat) out; + Dimension outSize = videoOut.getSize(); + + if (outSize == null) + { + Dimension inSize = ((VideoFormat) inputFormat).getSize(); + + outSize + = (inSize == null) + ? new Dimension( + Constants.VIDEO_WIDTH, + Constants.VIDEO_HEIGHT) + : inSize; + } + + outputFormat + = new VideoFormat( + videoOut.getEncoding(), + outSize, + outSize.width * outSize.height, + Format.byteArray, + videoOut.getFrameRate()); + + // Return the outputFormat which is actually set. + return outputFormat; + } + + /** + * Processes (packetize) a buffer. + * + * @param inBuffer input buffer + * @param outBuffer output buffer + * @return BUFFER_PROCESSED_OK if buffer has been successfully + * processed + */ + @Override + public int process(Buffer inBuffer, Buffer outBuffer) + { + int inLength = inBuffer.getLength(); + byte inData[] = (byte[])inBuffer.getData(); + int inOffset = inBuffer.getOffset(); + boolean pktAdded = false; + + if (videoPkts.size() > 0) + { + byte[] pktData = videoPkts.remove(0); + + // Send the packet. + outBuffer.setData(pktData); + outBuffer.setLength(pktData.length); + outBuffer.setOffset(0); + outBuffer.setTimeStamp(timeStamp); + outBuffer.setSequenceNumber(sequenceNumber++); + + // If there are other packets, send them as well. + if(videoPkts.size() > 0) + { + return (BUFFER_PROCESSED_OK | INPUT_BUFFER_NOT_CONSUMED); + } + else + { + // It's the last packet of the current frame so mark it. + outBuffer.setFlags( + outBuffer.getFlags() | Buffer.FLAG_RTP_MARKER); + + return BUFFER_PROCESSED_OK; + } + } + + if (isEOM(inBuffer)) + { + propagateEOM(outBuffer); + reset(); + return BUFFER_PROCESSED_OK; + } + + if (inBuffer.isDiscard()) + { + outBuffer.setDiscard(true); + reset(); + return BUFFER_PROCESSED_OK; + } + + Format inFormat = inBuffer.getFormat(); + + if ((inFormat != inputFormat) && !inFormat.matches(inputFormat)) + setInputFormat(inFormat); + + int endIndex = inOffset + inLength; + int beginIndex = findStartcode(inData, inOffset, endIndex); + + if (beginIndex < endIndex) + { + for (int nextBeginIndex; + beginIndex < endIndex; + beginIndex = nextBeginIndex + 3) + { + nextBeginIndex = findStartcode(inData, beginIndex + 3, + endIndex); + int length = nextBeginIndex - beginIndex; + + if (length > 0) + { + pktAdded + = packetize(inData, beginIndex, length) + || pktAdded; + beginIndex += length; + } + } + } + + timeStamp = inBuffer.getTimeStamp(); + + if(pktAdded) + { + return process(inBuffer, outBuffer); + } + else + { + /* first frame is not a synchronization point, discard ?*/ + return BUFFER_PROCESSED_FAILED; + } + } + + /** + * Packetizes H.263+ encoded data so that it becomes ready to be sent as the + * payload of RTP packets. + * + * @param data the bytes which contain the H.263+ encoded data to be + * packetized + * @param offset the offset of H.263+ encoded data to be packetized begins + * @param length the length of the H.263+ encoded data starting at offset + * @return true if at least one RTP packet payload has been + * packetized i.e. prepared for sending; otherwise, false + */ + private boolean packetize(byte[] data, int offset, int length) + { + boolean pktAdded = false; + + while(length > 0) + { + boolean isPsc = false; + int pos = 0; + int maxPayloadLength = MAX_PAYLOAD_SIZE; + byte pkt[] = null; + int payloadLength = 0; + + /* is we are at synchronization point (PSC, GSBC, EOS, EOSBS) */ + if(data.length > 3 && data[offset] == 0x00 && + data[offset + 1] == 0x00) + { + isPsc = true; + pos = 2; + } + else + { + maxPayloadLength -= 2; + } + + if(length > maxPayloadLength) + { + payloadLength = maxPayloadLength; + } + else + { + payloadLength = length; + } + + pkt = new byte[payloadLength + (isPsc ? 0 : 2)]; + + /* add H263+ payload header */ + /* no VRC and no extra picture header */ + pkt[0] = (byte)(isPsc ? 0x04 : 0x00); + pkt[1] = 0x00; + + System.arraycopy(data, offset + pos, pkt, 2, payloadLength - pos); + pktAdded = videoPkts.add(pkt) || pktAdded; + + offset += payloadLength; + length -= payloadLength; + } + + return pktAdded; + } + + /** + * Finds the index in byteStream at which a Picture Start code + * begins. + * + * @param byteStream the H.263+ encoded byte stream + * @param beginIndex the inclusive index in byteStream at which the + * search is to begin + * @param endIndex the exclusive index in byteStream at which the + * search is to end + * @return the index in byteStream at which the Picture Start code + * begins, otherwise, endIndex + */ + private static int findStartcode(byte[] byteStream, int beginIndex, + int endIndex) + { + for (; beginIndex < (endIndex - 3); beginIndex++) + if((byteStream[beginIndex] == 0) + && (byteStream[beginIndex + 1] == 0) + && ((byteStream[beginIndex + 2] & (byte)0x80) == -128)) + { + return beginIndex; + } + return endIndex; + } +} diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/DePacketizer.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/DePacketizer.java index e0be1a817..042648a0e 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/DePacketizer.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/DePacketizer.java @@ -13,7 +13,6 @@ import net.java.sip.communicator.impl.neomedia.*; import net.java.sip.communicator.impl.neomedia.codec.*; -import net.java.sip.communicator.impl.neomedia.codec.video.*; import net.java.sip.communicator.util.*; import net.sf.fmj.media.*; @@ -345,7 +344,7 @@ protected void doOpen() lastTimeStamp = -1; } /** - * Processes (depacketize) a buffer. + * Processes (depacketizes) a buffer. * * @param inBuffer input buffer * @param outBuffer output buffer diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/JNIEncoder.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/JNIEncoder.java index dfb472018..9fc81aad3 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/JNIEncoder.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/JNIEncoder.java @@ -14,7 +14,6 @@ import net.java.sip.communicator.service.neomedia.event.*; import net.java.sip.communicator.impl.neomedia.codec.*; -import net.java.sip.communicator.impl.neomedia.codec.video.*; import net.sf.fmj.media.*; /** diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/Packetizer.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/Packetizer.java index 73f37c06c..a56f0f5a1 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/Packetizer.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/Packetizer.java @@ -166,6 +166,9 @@ public Format[] getSupportedOutputFormats(Format in) /** * Open this Packetizer. + * + * @throws ResourceUnavailableException if something goes wrong during + * initialization of the Packetizer. */ @Override public synchronized void open() 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 9f678362b..b31042da5 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java @@ -1055,6 +1055,19 @@ public void setRemoteSSRC(long remoteSSRC) @Override protected void setProcessorFormat(Processor processor, Format format) { + if(format.getEncoding().equals("h263-1998/rtp")) + { + /* if no output size has been defined, it means that no SDP's fmtp + * has been found with QCIF, CIF, VGA or CUSTOM elements + * + * Let's choose QCIF size (176x144) + */ + if(outputSize == null) + { + outputSize = new Dimension(176, 144); + } + } + /* * Add a size in the output format. As VideoFormat has no setter, we * recreate the object. Also check whether capture device can output diff --git a/src/net/java/sip/communicator/impl/neomedia/format/MediaFormatFactoryImpl.java b/src/net/java/sip/communicator/impl/neomedia/format/MediaFormatFactoryImpl.java index 2feecf47f..94e57d959 100644 --- a/src/net/java/sip/communicator/impl/neomedia/format/MediaFormatFactoryImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/format/MediaFormatFactoryImpl.java @@ -219,7 +219,7 @@ public MediaFormat createMediaFormat(String encoding, Map advancedParameters = new HashMap(); - formatParameters.putAll(mediaFormat.getFormatParameters()); + //formatParameters.putAll(mediaFormat.getFormatParameters()); formatParameters.putAll(formatParams); if(advancedParams != null)