diff --git a/lib/installer-exclude/jmf.jar b/lib/installer-exclude/jmf.jar index b10e9c36a..adbcbbd7b 100644 Binary files a/lib/installer-exclude/jmf.jar and b/lib/installer-exclude/jmf.jar differ diff --git a/lib/native/linux-64/libffmpeg.so b/lib/native/linux-64/libffmpeg.so deleted file mode 100755 index 128947b1c..000000000 Binary files a/lib/native/linux-64/libffmpeg.so and /dev/null differ diff --git a/lib/native/linux-64/libjnffmpeg.so b/lib/native/linux-64/libjnffmpeg.so new file mode 100755 index 000000000..6d677e101 Binary files /dev/null and b/lib/native/linux-64/libjnffmpeg.so differ diff --git a/lib/native/linux/libffmpeg.so b/lib/native/linux/libffmpeg.so deleted file mode 100755 index 49a4e5b32..000000000 Binary files a/lib/native/linux/libffmpeg.so and /dev/null differ diff --git a/lib/native/linux/libjnffmpeg.so b/lib/native/linux/libjnffmpeg.so new file mode 100755 index 000000000..b5b887683 Binary files /dev/null and b/lib/native/linux/libjnffmpeg.so differ diff --git a/lib/native/mac/libffmpeg.jnilib b/lib/native/mac/libffmpeg.jnilib deleted file mode 100755 index 6707a9833..000000000 Binary files a/lib/native/mac/libffmpeg.jnilib and /dev/null differ diff --git a/lib/native/mac/libjnffmpeg.jnilib b/lib/native/mac/libjnffmpeg.jnilib new file mode 100755 index 000000000..e2da99282 Binary files /dev/null and b/lib/native/mac/libjnffmpeg.jnilib differ diff --git a/lib/native/windows-64/ffmpeg.dll b/lib/native/windows-64/ffmpeg.dll deleted file mode 100644 index 8cc41feff..000000000 Binary files a/lib/native/windows-64/ffmpeg.dll and /dev/null differ diff --git a/lib/native/windows-64/jnffmpeg.dll b/lib/native/windows-64/jnffmpeg.dll new file mode 100644 index 000000000..84f426a72 Binary files /dev/null and b/lib/native/windows-64/jnffmpeg.dll differ diff --git a/lib/native/windows/ffmpeg.dll b/lib/native/windows/ffmpeg.dll deleted file mode 100644 index d1c578ac5..000000000 Binary files a/lib/native/windows/ffmpeg.dll and /dev/null differ diff --git a/lib/native/windows/jnffmpeg.dll b/lib/native/windows/jnffmpeg.dll new file mode 100644 index 000000000..69db9f796 Binary files /dev/null and b/lib/native/windows/jnffmpeg.dll differ diff --git a/lib/os-specific/linux/installer-exclude/jmf.jar b/lib/os-specific/linux/installer-exclude/jmf.jar index 2d9d8e2a4..929466979 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 ccadc10e8..7fb2646a0 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 59af29d2e..4bd27f9b0 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 a3d287e60..65f62fd1c 100644 --- a/src/native/ffmpeg/README +++ b/src/native/ffmpeg/README @@ -35,18 +35,15 @@ When building the x64 library on Windows x86, add --host=x86_64-w64-mingw32 to the configure line. ./configure --enable-win32thread -make - FreeBSD patch -Np1 -i x264-01-freebsd.patch ./configure --enable-pic -gmake - Linux, Mac OS X ./configure --enable-pic -make 3. ffmpeg @@ -75,35 +72,11 @@ Add --target-os=mingw32, --enable-memalign-hack, --disable-pthreads and x64: Add --arch=amd64 and --enable-cross-compile to the configure line used for x86. -mingw32-make - -- Linux, FreeBSD +- FreeBSD, Linux, Mac OS X Add --enable-pic and --enable-pthreads. -- Mac OS X - -Note: Remove from the configure script the option to add -mdynamic-no-pic unless -building for the i386 architecture. For i386, remove "--enable-pic" from the -following command. - -./configure \ - --enable-pic \ - --enable-static --disable-shared --shlibdir=. \ - --disable-encoders --disable-decoders --disable-muxers --disable-demuxers \ - --disable-parsers --disable-bsfs --disable-protocols --disable-devices \ - --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 \ - --disable-ffserver --disable-ffprobe --disable-ffplay --disable-ffmpeg \ - --enable-pthreads - -4. libffmpeg +4. jnffmpeg Define the environment variable JAVA_HOME so that the JNI headers can be found. Change the current directory to src\native and run "ant ffmpeg" passing it diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java b/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java index 7052dbb3c..3ce06628a 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java @@ -411,16 +411,12 @@ public static void enableDesktopSharing(Call call, boolean enable) else if (showDesktopSharingWarning()) { MediaService mediaService = GuiActivator.getMediaService(); - - List desktopDevices = mediaService.getDevices( - MediaType.VIDEO, MediaUseCase.DESKTOP); - + List desktopDevices + = mediaService.getDevices(MediaType.VIDEO, MediaUseCase.DESKTOP); int deviceNumber = desktopDevices.size(); if (deviceNumber == 1) - { enableDesktopSharing(call, null, enable); - } else if (deviceNumber > 1) { SelectScreenDialog selectDialog @@ -517,7 +513,6 @@ private static void enableDesktopSharing(Call call, OperationSetDesktopSharingServer desktopOpSet = call.getProtocolProvider().getOperationSet( OperationSetDesktopSharingServer.class); - boolean enableSucceeded = false; // This shouldn't happen at this stage, because we disable the button diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java b/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java index a08711e3d..67783b8c1 100644 --- a/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/MediaServiceImpl.java @@ -9,11 +9,11 @@ import java.awt.*; import java.awt.event.*; import java.util.*; -import java.util.List; +import java.util.List; // disambiguation import javax.media.*; import javax.media.control.*; -import javax.media.format.RGBFormat; +import javax.media.format.*; import javax.media.protocol.*; import javax.swing.*; @@ -22,7 +22,6 @@ import net.java.sip.communicator.impl.neomedia.device.*; import net.java.sip.communicator.impl.neomedia.format.*; import net.java.sip.communicator.impl.neomedia.protocol.*; -import net.java.sip.communicator.impl.neomedia.videoflip.*; import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.neomedia.device.*; import net.java.sip.communicator.service.neomedia.format.*; @@ -788,20 +787,12 @@ private static void controllerUpdateForPreview(ControllerEvent event, Codec codecs[] = null; SwScaler scaler = new SwScaler(); - if(locator.getProtocol().equals( - ImageStreamingAuto.LOCATOR_PROTOCOL)) - { - // do not flip desktop - codecs = new Codec[1]; - codecs[0] = scaler; - } + // do not flip desktop + if (ImageStreamingAuto.LOCATOR_PROTOCOL.equals( + locator.getProtocol())) + codecs = new Codec[] { scaler }; else - { - VideoFlipEffect flipEffect = new VideoFlipEffect(); - codecs = new Codec[2]; - codecs[0] = flipEffect; - codecs[1] = scaler; - } + codecs = new Codec[] { new HFlip(), scaler }; trackControl.setCodecChain(codecs); break; @@ -1136,26 +1127,23 @@ public Point getOriginForDesktopStreamingDevice(MediaDevice mediaDevice) CaptureDeviceInfo devInfo = dev.getCaptureDeviceInfo(); MediaLocator locator = devInfo.getLocator(); - if(!locator.getProtocol().equals(ImageStreamingAuto.LOCATOR_PROTOCOL)) + if(!ImageStreamingAuto.LOCATOR_PROTOCOL.equals(locator.getProtocol())) return null; String remainder = locator.getRemainder(); String split[] = remainder.split(","); - int index = 0; - - if(split != null && split.length > 1) - { - index = Integer.parseInt(split[0]); - } - else - { - index = Integer.parseInt(remainder); - } + int index + = Integer.parseInt( + ((split != null) && (split.length > 1)) + ? split[0] + : remainder); ScreenDevice devs[] = ScreenDeviceImpl.getAvailableScreenDevice(); - if(devs.length - 1 >= index) + + if (devs.length - 1 >= index) { Rectangle r = ((ScreenDeviceImpl)devs[index]).getBounds(); + return new Point(r.x, r.y); } diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java index 20d25498b..41cebde1e 100644 --- a/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/MediaStreamImpl.java @@ -402,8 +402,9 @@ public void addDynamicRTPPayloadType( dynamicRTPPayloadTypes.put(Byte.valueOf(rtpPayloadType), format); if (rtpManager != null) - rtpManager - .addFormat(mediaFormatImpl.getFormat(), rtpPayloadType); + rtpManager.addFormat( + mediaFormatImpl.getFormat(), + rtpPayloadType); } } @@ -1068,10 +1069,14 @@ private RTPManager getRTPManager() //sure you check that the line below still works. rtpManager.initialize(rtpConnector); - //JMF inits the local SSRC upon initialize(RTPConnector) so now's - //the time to ask: - //as JMF keep the ssrc in int value, we cast here from - //signed int to unsigned + /* + * JMF initializes the local SSRC upon #initialize(RTPConnector) so + * now's the time to ask. + */ + /* + * As JMF keeps the SSRC as a signed int value, convert it to + * unsigned. + */ setLocalSourceID( ((RTPSessionMgr) rtpManager).getLocalSSRC() & 0xFFFFFFFFL); } @@ -1205,8 +1210,9 @@ protected void registerCustomCodecFormats(RTPManager rtpManager) = (MediaFormatImpl) dynamicRTPPayloadType.getValue(); - rtpManager.addFormat( mediaFormatImpl.getFormat(), - dynamicRTPPayloadType.getKey()); + rtpManager.addFormat( + mediaFormatImpl.getFormat(), + dynamicRTPPayloadType.getKey()); } } } @@ -1430,25 +1436,27 @@ public void setDirection(MediaDirection direction) */ public void setFormat(MediaFormat format) { - if(logger.isTraceEnabled()) - logger.trace("Changing format of stream " + hashCode() - // getDeviceSession().getFormat() will activate - // device in order to get its format - //+ " from:" + getDeviceSession().getFormat() - + " to:" + format); + if (logger.isTraceEnabled()) + logger.trace( + "Changing format of stream " + hashCode() + + " from: " + getDeviceSession().getFormat() + + " to: " + format); setAdvancedAttributes(format.getAdvancedAttributes()); - handleAttributes(format.getAdvancedAttributes()); - handleAttributes(format.getFormatParameters()); + handleAttributes(format, format.getAdvancedAttributes()); + handleAttributes(format, format.getFormatParameters()); getDeviceSession().setFormat(format); } /** * Handles attributes contained in MediaFormat. * - * @param attrs the attributes list to handle + * @param format the MediaFormat to handle the attributes of + * @param attrs the attributes Map to handle */ - protected void handleAttributes(Map attrs) + protected void handleAttributes( + MediaFormat format, + Map attrs) { } @@ -1623,9 +1631,9 @@ private void startReceiveStreams() = receiveStream.getDataSource(); /* - * For an unknown reason, the stream DataSource can be - * null at the end of the Call after re-INVITEs have - * been handled. + * For an unknown reason, the stream DataSource can be null + * at the end of the Call after re-INVITEs have been + * handled. */ if (receiveStreamDataSource != null) receiveStreamDataSource.start(); @@ -1852,17 +1860,16 @@ private void stopReceiveStreams() = receiveStream.getDataSource(); /* - * For an unknown reason, the stream DataSource can be - * null at the end of the Call after re-INVITEs have - * been handled. + * For an unknown reason, the stream DataSource can be null + * at the end of the Call after re-INVITEs have been + * handled. */ if (receiveStreamDataSource != null) receiveStreamDataSource.stop(); } catch (IOException ioex) { - logger.warn("Failed to stop stream " + receiveStream, - ioex); + logger.warn("Failed to stop stream " + receiveStream, ioex); } } } @@ -1980,8 +1987,7 @@ public static String toString(DataSource dataSource) /** * Notifies this ReceiveStreamListener that the RTPManager - * it is registered with has generated an event related to a - * ReceiveStream. + * it is registered with has generated an event related to a ReceiveStream. * * @param event the ReceiveStreamEvent which specifies the * ReceiveStream that is the cause of the event and the very type @@ -2067,13 +2073,9 @@ else if (event instanceof TimeoutEvent) */ public void update(SendStreamEvent event) { - if(event instanceof NewSendStreamEvent) - { - if(event.getSendStream().getSSRC() != this.localSourceID) - { - setLocalSourceID(event.getSendStream().getSSRC()); - } - } + if ((event instanceof NewSendStreamEvent) + && (event.getSendStream().getSSRC() != this.localSourceID)) + setLocalSourceID(event.getSendStream().getSSRC()); } /** @@ -2129,12 +2131,15 @@ public void update(RemoteEvent remoteEvent) if(numberOfReceivedSenderReports%4 != 1) return; - StringBuilder buff = - new StringBuilder(StatisticsEngine.RTP_STAT_PREFIX); + StringBuilder buff + = new StringBuilder(StatisticsEngine.RTP_STAT_PREFIX); + MediaFormat format = getFormat(); buff.append("Received a report for ") - .append(getFormat() != null ? - getFormat().getMediaType().toString() : "") + .append( + (format == null) + ? "" + : format.getMediaType().toString()) .append(" stream SSRC:") .append(getLocalSourceID()) .append(" [packet count:") diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java b/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java index 6df5b5341..f16765d01 100644 --- a/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java +++ b/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java @@ -23,7 +23,7 @@ * Implements static utility methods used by media classes. * * @author Emil Ivov - * @author Lubomir Marinov + * @author Lyubomir Marinov */ public class MediaUtils { @@ -70,8 +70,9 @@ public class MediaUtils AudioFormat.ULAW_RTP, 8000); - /* some codecs depend on JMF native libraries which are only available - * on Windows 32-bit and Linux 32-bit + /* + * Some codecs depend on JMF native libraries which are only available + * on 32-bit Linux and 32-bit Windows. */ if(OSUtils.IS_LINUX32 || OSUtils.IS_WINDOWS32) { @@ -174,12 +175,13 @@ public class MediaUtils /* H264 */ Map h264FormatParams = new HashMap(); + String packetizationMode = "packetization-mode"; Map h264AdvancedAttributes = new HashMap(); - h264FormatParams.put("packetization-mode", "1"); + h264FormatParams.put(packetizationMode, "1"); /* - * Disable PLI since we use periodic intra-refresh feature of + * Disable PLI since we use the periodic intra-refresh feature of * FFmpeg/x264. */ //h264AdvancedAttributes.put("rtcp-fb", "nack pli"); @@ -190,6 +192,7 @@ public class MediaUtils h264AdvancedAttributes.put("imageattr", createImageAttr(null, res)); + // packetization-mode=1 addMediaFormats( MediaFormat.RTP_PAYLOAD_TYPE_UNKNOWN, "H264", @@ -206,10 +209,7 @@ public class MediaUtils // maximum resolution we can receive is the size of our screen device if(res != null) - { h263FormatParams.put("CUSTOM", res.width + "," + res.height + ",2"); - } - h263FormatParams.put("VGA", "2"); h263FormatParams.put("CIF", "1"); h263FormatParams.put("QCIF", "1"); @@ -293,36 +293,37 @@ private static void addMediaFormats( = new ArrayList(clockRateCount); if (clockRateCount > 0) + { for (double clockRate : clockRates) { Format format; switch (mediaType) { - case AUDIO: - format = new AudioFormat(jmfEncoding); - break; - case VIDEO: - format = new VideoFormat(jmfEncoding); - break; - default: - throw new IllegalArgumentException("mediaType"); + case AUDIO: + format = new AudioFormat(jmfEncoding); + break; + case VIDEO: + format + = new ParameterizedVideoFormat( + jmfEncoding, + formatParameters); + break; + default: + throw new IllegalArgumentException("mediaType"); } - if (format != null) - { - MediaFormat mediaFormat - = MediaFormatImpl - .createInstance( - format, - clockRate, - formatParameters, - advancedAttributes); - - if (mediaFormat != null) - mediaFormats.add(mediaFormat); - } + MediaFormat mediaFormat + = MediaFormatImpl.createInstance( + format, + clockRate, + formatParameters, + advancedAttributes); + + if (mediaFormat != null) + mediaFormats.add(mediaFormat); } + } else { Format format; @@ -330,30 +331,32 @@ private static void addMediaFormats( switch (mediaType) { - case AUDIO: - AudioFormat audioFormat = new AudioFormat(jmfEncoding); + case AUDIO: + AudioFormat audioFormat = new AudioFormat(jmfEncoding); - format = audioFormat; - clockRate = audioFormat.getSampleRate(); - break; - case VIDEO: - format = new VideoFormat(jmfEncoding); - clockRate = VideoMediaFormatImpl.DEFAULT_CLOCK_RATE; - break; - default: - throw new IllegalArgumentException("mediaType"); + format = audioFormat; + clockRate = audioFormat.getSampleRate(); + break; + case VIDEO: + format + = new ParameterizedVideoFormat( + jmfEncoding, + formatParameters); + clockRate = VideoMediaFormatImpl.DEFAULT_CLOCK_RATE; + break; + default: + throw new IllegalArgumentException("mediaType"); } - if (format != null) - { - MediaFormat mediaFormat - = MediaFormatImpl - .createInstance(format, clockRate, formatParameters, - advancedAttributes); + MediaFormat mediaFormat + = MediaFormatImpl.createInstance( + format, + clockRate, + formatParameters, + advancedAttributes); - if (mediaFormat != null) - mediaFormats.add(mediaFormat); - } + if (mediaFormat != null) + mediaFormats.add(mediaFormat); } if (mediaFormats.size() > 0) @@ -361,13 +364,11 @@ private static void addMediaFormats( if (MediaFormat.RTP_PAYLOAD_TYPE_UNKNOWN == rtpPayloadType) rtpPayloadTypelessMediaFormats.addAll(mediaFormats); else - rtpPayloadTypeStrToMediaFormats - .put( + rtpPayloadTypeStrToMediaFormats.put( Byte.toString(rtpPayloadType), mediaFormats.toArray(EMPTY_MEDIA_FORMATS)); - jmfEncodingToEncodings - .put( + jmfEncodingToEncodings.put( ((MediaFormatImpl) mediaFormats.get(0)) .getJMFEncoding(), encoding); diff --git a/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java index 35382dcc1..93d6f8824 100644 --- a/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java @@ -20,6 +20,7 @@ import net.java.sip.communicator.impl.neomedia.transform.*; import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.neomedia.device.*; +import net.java.sip.communicator.service.neomedia.format.*; import net.java.sip.communicator.service.neomedia.event.*; import net.java.sip.communicator.util.*; @@ -28,7 +29,7 @@ * VideoMediaStream. * * @author Lyubomir Marinov - * @author Sébastien Vincent + * @author Sebastien Vincent */ public class VideoMediaStreamImpl extends MediaStreamImpl @@ -279,12 +280,12 @@ protected void configureDataOutputStream( { super.configureDataOutputStream(dataOutputStream); - int maxBandwidth = - NeomediaActivator.getMediaServiceImpl().getDeviceConfiguration() - .getVideoMaxBandwidth(); + int maxBandwidth + = NeomediaActivator.getMediaServiceImpl().getDeviceConfiguration() + .getVideoMaxBandwidth(); //maximum one packet for X milliseconds(the settings are for one second) - dataOutputStream.setMaxPacketsPerMillis(1, 1000/maxBandwidth); + dataOutputStream.setMaxPacketsPerMillis(1, 1000 / maxBandwidth); } /** @@ -353,8 +354,8 @@ protected void deviceSessionChanged( if ((oldValue instanceof VideoMediaDeviceSession) && (deviceSessionVideoListener != null)) { - ((VideoMediaDeviceSession) oldValue) - .removeVideoListener(deviceSessionVideoListener); + ((VideoMediaDeviceSession) oldValue).removeVideoListener( + deviceSessionVideoListener); } if (newValue instanceof VideoMediaDeviceSession) { @@ -551,9 +552,13 @@ protected void rtpConnectorChanged( /** * Handles attributes contained in MediaFormat. * - * @param attrs the attributes list to handle + * @param format the MediaFormat to handle the attributes of + * @param attrs the attributes Map to handle */ - protected void handleAttributes(Map attrs) + @Override + protected void handleAttributes( + MediaFormat format, + Map attrs) { /* walk through attributes and see if we recognized something * we support @@ -565,9 +570,10 @@ protected void handleAttributes(Map attrs) String key = mapEntry.getKey(); String value = mapEntry.getValue(); - if(key.equals("rtcp-fb") && value.equals("nack pli")) + if(key.equals("rtcp-fb")) { - usePLI = true; + if (value.equals("nack pli")) + usePLI = true; } else if(key.equals("imageattr")) { @@ -624,9 +630,7 @@ else if(key.equals("CUSTOM")) String args[] = value.split(","); if(args.length < 3) - { continue; - } try { @@ -656,7 +660,7 @@ else if(key.equals("CUSTOM")) * @param imgattr send/recv resolution string * @return maximum resolution array (first element is send, second one is * recv). Elements could be null if image attribute is not present or if - * resoluion is a wildcard. + * resolution is a wildcard. */ public static java.awt.Dimension[] parseSendRecvResolution(String imgattr) { diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/AbstractCodecExt.java b/src/net/java/sip/communicator/impl/neomedia/codec/AbstractCodecExt.java index a1da7c342..a58f11817 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/AbstractCodecExt.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/AbstractCodecExt.java @@ -1,259 +1,259 @@ -/* - * 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; - -import javax.media.*; - -import net.sf.fmj.media.*; - -/** - * Extends FMJ's AbstractCodec to make it even easier to implement a - * Codec. - * - * @author Lyubomir Marinov - */ -public abstract class AbstractCodecExt - extends AbstractCodec -{ - private final Class formatClass; - - /** - * The name of this PlugIn. - */ - private final String name; - - private final Format[] supportedOutputFormats; - - /** - * Initializes a new AbstractCodecExt instance with a specific - * PlugIn name, a specific Class of input and output - * Formats and a specific list of Formats supported as - * output. - * - * @param name the PlugIn name of the new instance - * @param formatClass the Class of input and output - * Formats supported by the new instance - * @param supportedOutputFormats the list of Formats supported by - * the new instance as output - */ - protected AbstractCodecExt( - String name, - Class formatClass, - Format[] supportedOutputFormats) - { - this.formatClass = formatClass; - this.name = name; - this.supportedOutputFormats = supportedOutputFormats; - - /* - * An Effect is a Codec that does not modify the Format of the data, it - * modifies the contents. - */ - if (this instanceof Effect) - inputFormats = this.supportedOutputFormats; - } - - @Override - public void close() - { - if (!opened) - return; - - doClose(); - - opened = false; - super.close(); - } - - protected void discardOutputBuffer(Buffer outputBuffer) - { - outputBuffer.setDiscard(true); - } - - protected abstract 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 - */ - protected abstract void doOpen() - throws ResourceUnavailableException; - - protected abstract int doProcess(Buffer inputBuffer, Buffer outputBuffer); - - protected Format[] getMatchingOutputFormats(Format inputFormat) - { - /* - * An Effect is a Codec that does not modify the Format of the data, it - * modifies the contents. - */ - if (this instanceof Effect) - return new Format[] { inputFormat }; - - if (supportedOutputFormats != null) - return supportedOutputFormats.clone(); - return new Format[0]; - } - - @Override - public String getName() - { - return (name == null) ? super.getName() : name; - } - - /** - * Implements {AbstractCodec#getSupportedOutputFormats(Format)}. - * - * @param inputFormat - * @return - * @see AbstractCodec#getSupportedOutputFormats(Format) - */ - public Format[] getSupportedOutputFormats(Format inputFormat) - { - if (inputFormat == null) - return supportedOutputFormats; - - if (!formatClass.isInstance(inputFormat) - || (matches(inputFormat, inputFormats) == null)) - return new Format[0]; - - return getMatchingOutputFormats(inputFormat); - } - - /** - * Utility to perform format matching. - */ - public static Format matches(Format in, Format outs[]) - { - for (Format out : outs) - if (in.matches(out)) - return out; - return null; - } - - /** - * Opens this PlugIn software or hardware component and acquires - * the resources that it needs to operate. All required input and/or output - * formats have to be set on this PlugIn before open is - * called. Buffers should not be passed into this PlugIn without - * first calling open. - * - * @throws ResourceUnavailableException if any of the resources that this - * PlugIn needs to operate cannot be acquired - * @see AbstractPlugIn#open() - */ - @Override - public void open() - throws ResourceUnavailableException - { - if (opened) - return; - - doOpen(); - - opened = true; - super.open(); - } - - /** - * Implements AbstractCodec#process(Buffer, Buffer). - * - * @param inputBuffer - * @param outputBuffer - * @return - * @see AbstractCodec#process(Buffer, Buffer) - */ - public int process(Buffer inputBuffer, Buffer outputBuffer) - { - if (!checkInputBuffer(inputBuffer)) - return BUFFER_PROCESSED_FAILED; - if (isEOM(inputBuffer)) - { - propagateEOM(outputBuffer); - return BUFFER_PROCESSED_OK; - } - if (inputBuffer.isDiscard()) - { - discardOutputBuffer(outputBuffer); - return BUFFER_PROCESSED_OK; - } - - return doProcess(inputBuffer, outputBuffer); - } - - @Override - public Format setInputFormat(Format format) - { - if (!formatClass.isInstance(format) - || (matches(format, inputFormats) == null)) - return null; - - return super.setInputFormat(format); - } - - @Override - public Format setOutputFormat(Format format) - { - if (!formatClass.isInstance(format) - || (matches(format, getMatchingOutputFormats(inputFormat)) - == null)) - return null; - - return super.setOutputFormat(format); - } - - /** - * Updates the format, length and offset of a - * specific output Buffer to specific values. - * - * @param outputBuffer the output Buffer to update the properties - * of - * @param format the Format to set on outputBuffer - * @param length the length to set on outputBuffer - * @param offset the offset to set on outputBuffer - */ - protected void updateOutput( - Buffer outputBuffer, - Format format, int length, int offset) - { - outputBuffer.setFormat(format); - outputBuffer.setLength(length); - outputBuffer.setOffset(offset); - } - - protected byte[] validateByteArraySize(Buffer buffer, int newSize) - { - Object data = buffer.getData(); - byte[] newBytes; - - if (data instanceof byte[]) - { - byte[] bytes = (byte[]) data; - - if (bytes.length >= newSize) - return bytes; - - newBytes = new byte[newSize]; - System.arraycopy(bytes, 0, newBytes, 0, bytes.length); - } - else - { - newBytes = new byte[newSize]; - buffer.setLength(0); - buffer.setOffset(0); - } - - buffer.setData(newBytes); - return newBytes; - } -} +/* + * 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; + +import javax.media.*; + +import net.sf.fmj.media.*; + +/** + * Extends FMJ's AbstractCodec to make it even easier to implement a + * Codec. + * + * @author Lyubomir Marinov + */ +public abstract class AbstractCodecExt + extends AbstractCodec +{ + private final Class formatClass; + + /** + * The name of this PlugIn. + */ + private final String name; + + private final Format[] supportedOutputFormats; + + /** + * Initializes a new AbstractCodecExt instance with a specific + * PlugIn name, a specific Class of input and output + * Formats and a specific list of Formats supported as + * output. + * + * @param name the PlugIn name of the new instance + * @param formatClass the Class of input and output + * Formats supported by the new instance + * @param supportedOutputFormats the list of Formats supported by + * the new instance as output + */ + protected AbstractCodecExt( + String name, + Class formatClass, + Format[] supportedOutputFormats) + { + this.formatClass = formatClass; + this.name = name; + this.supportedOutputFormats = supportedOutputFormats; + + /* + * An Effect is a Codec that does not modify the Format of the data, it + * modifies the contents. + */ + if (this instanceof Effect) + inputFormats = this.supportedOutputFormats; + } + + @Override + public void close() + { + if (!opened) + return; + + doClose(); + + opened = false; + super.close(); + } + + protected void discardOutputBuffer(Buffer outputBuffer) + { + outputBuffer.setDiscard(true); + } + + protected abstract 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 + */ + protected abstract void doOpen() + throws ResourceUnavailableException; + + protected abstract int doProcess(Buffer inputBuffer, Buffer outputBuffer); + + protected Format[] getMatchingOutputFormats(Format inputFormat) + { + /* + * An Effect is a Codec that does not modify the Format of the data, it + * modifies the contents. + */ + if (this instanceof Effect) + return new Format[] { inputFormat }; + + if (supportedOutputFormats != null) + return supportedOutputFormats.clone(); + return new Format[0]; + } + + @Override + public String getName() + { + return (name == null) ? super.getName() : name; + } + + /** + * Implements {@link AbstractCodec#getSupportedOutputFormats(Format)}. + * + * @param inputFormat + * @return + * @see AbstractCodec#getSupportedOutputFormats(Format) + */ + public Format[] getSupportedOutputFormats(Format inputFormat) + { + if (inputFormat == null) + return supportedOutputFormats; + + if (!formatClass.isInstance(inputFormat) + || (matches(inputFormat, inputFormats) == null)) + return new Format[0]; + + return getMatchingOutputFormats(inputFormat); + } + + /** + * Utility to perform format matching. + */ + public static Format matches(Format in, Format outs[]) + { + for (Format out : outs) + if (in.matches(out)) + return out; + return null; + } + + /** + * Opens this PlugIn software or hardware component and acquires + * the resources that it needs to operate. All required input and/or output + * formats have to be set on this PlugIn before open is + * called. Buffers should not be passed into this PlugIn without + * first calling open. + * + * @throws ResourceUnavailableException if any of the resources that this + * PlugIn needs to operate cannot be acquired + * @see AbstractPlugIn#open() + */ + @Override + public void open() + throws ResourceUnavailableException + { + if (opened) + return; + + doOpen(); + + opened = true; + super.open(); + } + + /** + * Implements AbstractCodec#process(Buffer, Buffer). + * + * @param inputBuffer + * @param outputBuffer + * @return + * @see AbstractCodec#process(Buffer, Buffer) + */ + public int process(Buffer inputBuffer, Buffer outputBuffer) + { + if (!checkInputBuffer(inputBuffer)) + return BUFFER_PROCESSED_FAILED; + if (isEOM(inputBuffer)) + { + propagateEOM(outputBuffer); + return BUFFER_PROCESSED_OK; + } + if (inputBuffer.isDiscard()) + { + discardOutputBuffer(outputBuffer); + return BUFFER_PROCESSED_OK; + } + + return doProcess(inputBuffer, outputBuffer); + } + + @Override + public Format setInputFormat(Format format) + { + if (!formatClass.isInstance(format) + || (matches(format, inputFormats) == null)) + return null; + + return super.setInputFormat(format); + } + + @Override + public Format setOutputFormat(Format format) + { + if (!formatClass.isInstance(format) + || (matches(format, getMatchingOutputFormats(inputFormat)) + == null)) + return null; + + return super.setOutputFormat(format); + } + + /** + * Updates the format, length and offset of a + * specific output Buffer to specific values. + * + * @param outputBuffer the output Buffer to update the properties + * of + * @param format the Format to set on outputBuffer + * @param length the length to set on outputBuffer + * @param offset the offset to set on outputBuffer + */ + protected void updateOutput( + Buffer outputBuffer, + Format format, int length, int offset) + { + outputBuffer.setFormat(format); + outputBuffer.setLength(length); + outputBuffer.setOffset(offset); + } + + protected byte[] validateByteArraySize(Buffer buffer, int newSize) + { + Object data = buffer.getData(); + byte[] newBytes; + + if (data instanceof byte[]) + { + byte[] bytes = (byte[]) data; + + if (bytes.length >= newSize) + return bytes; + + newBytes = new byte[newSize]; + System.arraycopy(bytes, 0, newBytes, 0, bytes.length); + } + else + { + newBytes = new byte[newSize]; + buffer.setLength(0); + buffer.setOffset(0); + } + + buffer.setData(newBytes); + return newBytes; + } +} 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 808aad717..1fa8509e7 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/FFmpeg.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/FFmpeg.java @@ -1,758 +1,764 @@ -/* - * 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; - -/** - * Provides the interface to the native FFmpeg library. - * - * @author Lubomir Marinov - * @author Sebastien Vincent - */ -public class FFmpeg -{ - /** - * No pts value. - */ - public static final long AV_NOPTS_VALUE = 0x8000000000000000L; - - /** - * Loop filter flag. - */ - public static final int CODEC_FLAG_LOOP_FILTER = 0x00000800; - - /** - * H263+ UMV flag. - */ - public static final int CODEC_FLAG_H263P_UMV = 0x01000000 ; - - /** - * AC pred flag. - */ - public static final int CODEC_FLAG_AC_PRED = 0x02000000; - - /** - * H263+ slice struct flag. - */ - public static final int CODEC_FLAG_H263P_SLICE_STRUCT = 0x10000000; - - /** - * Intra refresh flag2. - */ - public static final int CODEC_FLAG2_INTRA_REFRESH = 0x00200000; - - /** - * Allow to pass incomplete frame to decoder. - */ - public static final int CODEC_FLAG2_CHUNKS = 0x00008000; - - /** - * H264 codec ID. - */ - public static final int CODEC_ID_H264 = 28; - - /** - * H263 codec ID. - */ - public static final int CODEC_ID_H263 = 5; - - /** - * H263+ codec ID. - */ - public static final int CODEC_ID_H263P = 20; - - /** - * MJPEG codec ID. - */ - public static final int CODEC_ID_MJPEG = 8; - - /** - * MP3 codec ID. - */ - public static final int CODEC_ID_MP3 = 0x15000 + 1; - - /** - * Work around bugs in encoders which sometimes cannot be detected - * automatically. - */ - public static final int FF_BUG_AUTODETECT = 1; - - public static final int FF_CMP_CHROMA = 256; - - /** - * Padding size for FFmpeg input buffer. - */ - public static final int FF_INPUT_BUFFER_PADDING_SIZE = 8; - - public static final int FF_MB_DECISION_SIMPLE = 0; - - /** - * The minimum encoding buffer size defined by libavcodec. - */ - public static final int FF_MIN_BUFFER_SIZE = 16384; - - /** - * ARGB format. - */ - public static final int PIX_FMT_ARGB = 27; - - /** - * BGR32 format handled in endian specific manner. - * It is stored as ABGR on big-endian and RGBA on little-endian. - */ - public static final int PIX_FMT_BGR32; - - /** - * BGR32_1 format handled in endian specific manner. - * It is stored as BGRA on big-endian and ARGB on little-endian. - */ - public static final int PIX_FMT_BGR32_1; - - /** - * "NONE" format. - */ - public static final int PIX_FMT_NONE = -1; - - /** - * NV12 format. - */ - public static final int PIX_FMT_NV12 = 25; - - /** - * RGB24 format handled in endian specific manner. - * It is stored as RGB on big-endian and BGR on little-endian. - */ - public static final int PIX_FMT_RGB24; - - /** - * RGB32 format handled in endian specific manner. - * It is stored as ARGB on big-endian and BGRA on little-endian. - */ - public static final int PIX_FMT_RGB32; - - /** - * RGB32_1 format handled in endian specific manner. - * It is stored as RGBA on big-endian and ABGR on little-endian. - */ - public static final int PIX_FMT_RGB32_1; - - /** - * UYVY422 format. - */ - public static final int PIX_FMT_UYVY422 = 17; - - /** - * UYYVYY411 format. - */ - public static final int PIX_FMT_UYYVYY411 = 18; - - /** - * YUV420P format. - */ - public static final int PIX_FMT_YUV420P = 0; - - /** - * YUYV422 format. - */ - public static final int PIX_FMT_YUYV422 = 1; - - /** - * YUVJ422P format. - */ - public static final int PIX_FMT_YUVJ422P = 13; - - /** - * BICUBIC type for libswscale conversion. - */ - public static final int SWS_BICUBIC = 4; - - //public static final int X264_RC_ABR = 2; - - /** - * Free a native pointer allocated by av_malloc. - * - * @param ptr native pointer to free - */ - public static native void av_free(long ptr); - - /** - * Allocate memory. - * - * @param size size to allocate - * @return native pointer or 0 if av_malloc failed - */ - public static native long av_malloc(int size); - - /** - * Initialize libavformat and register all the muxers, demuxers and - * protocols. - */ - public static native void av_register_all(); - - /** - * Allocate a AVContext. - * - * @return native pointer to AVContext - */ - public static native long avcodec_alloc_context(); - - /** - * Allocate a AVFrame. - * - * @return native pointer to AVFrame - */ - public static native long avcodec_alloc_frame(); - - /** - * Close an AVCodecContext - * - * @param avctx pointer to AVCodecContex - * @return 0 if success, -1 otherwise - */ - public static native int avcodec_close(long avctx); - - /** - * Decode a video frame. - * - * @param avctx codec context - * @param frame frame decoded - * @param got_picture if the decoding has produced a valid picture - * @param buf the input buffer - * @param buf_size input buffer size - * @return number of bytes written to buff if success - */ - public static native int avcodec_decode_video(long avctx, long frame, - boolean[] got_picture, byte[] buf, int buf_size); - - /** - * Decode a video frame. - * - * @param avcontext codec context - * @param avframe frame decoded - * @param src input buffer - * @param src_length input buffer size - * @return number of bytes written to buff if success - */ - public static native int avcodec_decode_video(long avcontext, - long avframe, long src, int src_length); - - /** - * Encodes an audio frame from samples into buf. - * - * @param avctx the codec context - * @param buf the output buffer - * @param buf_offset the output buffer offset - * @param buf_size the output buffer size - * @param samples the input buffer containing the samples. The number of - * samples read from this buffer is frame_size*channels, - * both of which are defined in avctx. For PCM audio the number of - * samples read from samples is equal to - * buf_size*input_sample_size/output_sample_size. - * @param samples_offset the offset in the input buffer containing the - * samples - * @return on error a negative value is returned, on success zero or the - * number of bytes used to encode the data read from the input buffer - */ - public static native int avcodec_encode_audio( - long avctx, - byte[] buf, int buf_offset, int buf_size, - byte[] samples, int samples_offset); - - /** - * Encode a video frame. - * - * @param avctx codec context - * @param buff the output buffer - * @param buf_size output buffer size - * @param frame frame to encode - * @return number of bytes written to buff if success - */ - public static native int avcodec_encode_video(long avctx, byte[] buff, - int buf_size, long frame); - - /** - * Find a registered decoder with a matching ID. - * - * @param id CodecID of the requested encoder - * @return an AVCodec encoder if one was found; 0, - * otherwise - */ - public static native long avcodec_find_decoder(int id); - - /** - * Finds a registered encoder with a matching codec ID. - * - * @param id CodecID of the requested encoder - * @return an AVCodec encoder if one was found; 0, - * otherwise - */ - public static native long avcodec_find_encoder(int id); - - /** - * Initializes FFmpeg's avcodec. - */ - public static native void avcodec_init(); - - /** - * Initializes the specified AVCodecContext to use the specified - * AVCodec. - * - * @param avctx the AVCodecContext which will be set up to use the - * specified AVCodec - * @param codec the AVCodec to use within the - * AVCodecContext - * @return zero on success, a negative value on error - */ - public static native int avcodec_open(long avctx, long codec); - - /** - * Add specific flags to AVCodecContext's flags member. - * - * @param avctx pointer to AVCodecContext - * @param flags flags to add - */ - public static native void avcodeccontext_add_flags(long avctx, int flags); - - /** - * Add specific flags to AVCodecContext's flags2 member. - * - * @param avctx pointer to AVCodecContext - * @param flags flags to add - */ - public static native void avcodeccontext_add_flags2(long avctx, int flags); - - public static native void avcodeccontext_add_partitions(long avctx, - int partitions); - - /** - * Gets the samples per packet of the specified AVCodecContext. The - * property is set by libavcodec upon {@link #avcodec_open(long, long)}. - * - * @param avctx the AVCodecContext to get the samples per packet of - * @return the samples per packet of the specified AVCodecContext - */ - public static native int avcodeccontext_get_frame_size(long avctx); - - /** - * Get height of the video. - * - * @param avctx pointer to AVCodecContext - * @return video height - */ - public static native int avcodeccontext_get_height(long avctx); - - /** - * Get pixel format. - * - * @param avctx pointer to AVCodecContext - * @return pixel format - */ - public static native int avcodeccontext_get_pix_fmt(long avctx); - - /** - * Get width of the video. - * - * @param avctx pointer to AVCodecContext - * @return video width - */ - public static native int avcodeccontext_get_width(long avctx); - - /** - * Set the B-Frame strategy. - * - * @param avctx AVCodecContext pointer - * @param b_frame_strategy strategy - */ - public static native void avcodeccontext_set_b_frame_strategy(long avctx, - int b_frame_strategy); - - /** - * Sets the average bit rate of the specified AVCodecContext. The - * property is to be set by the user when encoding and is unused for - * constant quantizer encoding. It is set by libavcodec when decoding and - * its value is 0 or some bitrate if this info is available in the - * stream. - * - * @param avctx the AVCodecContext to set the average bit rate of - * @param bit_rate the average bit rate to be set to the specified - * AVCodecContext - */ - public static native void avcodeccontext_set_bit_rate(long avctx, - int bit_rate); - - /** - * Set the bit rate tolerance - * - * @param avctx the AVCodecContext to set the bit rate of - * @param bit_rate_tolerance bit rate tolerance - */ - public static native void avcodeccontext_set_bit_rate_tolerance(long avctx, - int bit_rate_tolerance); - - /** - * Sets the number of channels of the specified AVCodecContext. The - * property is audio only. - * - * @param avctx the AVCodecContext to set the number of channels of - * @param channels the number of channels to set to the specified - * AVCodecContext - */ - public static native void avcodeccontext_set_channels( - long avctx, int channels); - - public static native void avcodeccontext_set_chromaoffset(long avctx, - int chromaoffset); - - public static native void avcodeccontext_set_crf(long avctx, float crf); - - public static native void avcodeccontext_set_deblockbeta(long avctx, - int deblockbeta); - - /** - * Set the gop size (key frame interval). - * - * @param avctx the AVCodecContext to set the gop size of - * @param gop_size key frame interval - */ - public static native void avcodeccontext_set_gop_size(long avctx, - int gop_size); - - public static native void avcodeccontext_set_i_quant_factor(long avctx, - float i_quant_factor); - - /** - * Set the maximum B frames. - * - * @param avctx the AVCodecContext to set the maximum B frames of - * @param max_b_frames maximum B frames - */ - public static native void avcodeccontext_set_max_b_frames(long avctx, - int max_b_frames); - - public static native void avcodeccontext_set_mb_decision(long avctx, - int mb_decision); - - public static native void avcodeccontext_set_me_cmp(long avctx, int me_cmp); - - public static native void avcodeccontext_set_me_method(long avctx, - int me_method); - - public static native void avcodeccontext_set_me_range(long avctx, - int me_range); - - public static native void avcodeccontext_set_me_subpel_quality(long avctx, - int me_subpel_quality); - - /** - * Set the pixel format. - * - * @param avctx the AVCodecContext to set the pixel format of - * @param pix_fmt pixel format - */ - public static native void avcodeccontext_set_pix_fmt(long avctx, - int pix_fmt); - - public static native void avcodeccontext_set_qcompress(long avctx, - float qcompress); - - public static native void avcodeccontext_set_quantizer(long avctx, - int qmin, int qmax, int max_qdiff); - - public static native void avcodeccontext_set_rc_buffer_size(long avctx, - int rc_buffer_size); - - public static native void avcodeccontext_set_rc_eq(long avctx, String rc_eq); - - public static native void avcodeccontext_set_rc_max_rate(long avctx, - int rc_max_rate); - - public static native void avcodeccontext_set_refs(long avctx, - int refs); - - /** - * Set the RTP payload size. - * - * @param avctx the AVCodecContext to set the RTP payload size of - * @param rtp_payload_size RTP payload size - */ - public static native void avcodeccontext_set_rtp_payload_size(long avctx, - int rtp_payload_size); - - public static native void avcodeccontext_set_sample_aspect_ratio( - long avctx, int num, int den); - - /** - * Sets the samples per second of the specified AVCodecContext. The - * property is audio only. - * - * @param avctx the AVCodecContext to set the samples per second of - * @param sample_rate the samples per second to set to the specified - * AVCodecContext - */ - public static native void avcodeccontext_set_sample_rate( - long avctx, int sample_rate); - - /** - * Set the scene change threshold (in percent). - * - * @param avctx AVCodecContext pointer - * @param scenechange_threshold value between 0 and 100 - */ - public static native void avcodeccontext_set_scenechange_threshold( - long avctx, int scenechange_threshold); - - /** - * Set the size of the video. - * - * @param avctx pointer to AVCodecContext - * @param width video width - * @param height video height - */ - public static native void avcodeccontext_set_size(long avctx, int width, - int height); - - /** - * Set the number of thread. - * - * @param avctx the AVCodecContext to set the number of thread of - * @param thread_count number of thread to set - */ - public static native void avcodeccontext_set_thread_count(long avctx, - int thread_count); - - public static native void avcodeccontext_set_ticks_per_frame(long avctx, - int ticks_per_frame); - - public static native void avcodeccontext_set_time_base(long avctx, int num, - int den); - - public static native void avcodeccontext_set_trellis(long avctx, - int trellis); - - public static native void avcodeccontext_set_workaround_bugs(long avctx, - int workaround_bugs); - - public static native long avframe_get_pts(long frame); - - public static native void avframe_set_data(long frame, long data0, - long offset1, long offset2); - - public static native void avframe_set_key_frame(long frame, - boolean key_frame); - - public static native void avframe_set_linesize(long frame, int linesize0, - int linesize1, int linesize2); - - public static native int avpicture_fill(long picture, long ptr, - int pix_fmt, int width, int height); - - public static native long avpicture_get_data0(long picture); - - public static native int avpicture_get_size(int pix_fmt, int width, - int height); - - public static native void memcpy(int[] dst, int dst_offset, int dst_length, - long src); - - public static native void memcpy(long dst, byte[] src, int src_offset, - int src_length); - - /** - * Get BGR32 pixel format. - * - * @return BGR32 pixel format - */ - private static native int PIX_FMT_BGR32(); - - /** - * Get BGR32_1 pixel format. - * - * @return BGR32_1 pixel format - */ - private static native int PIX_FMT_BGR32_1(); - - /** - * Get RGB24 pixel format. - * - * @return RGB24 pixel format - */ - private static native int PIX_FMT_RGB24(); - - /** - * Get RGB32 pixel format. - * - * @return RGB32 pixel format - */ - private static native int PIX_FMT_RGB32(); - - /** - * Get RGB32_1 pixel format. - * - * @return RGB32_1 pixel format - */ - private static native int PIX_FMT_RGB32_1(); - - /** - * Free an SwsContext. - * - * @param context SwsContext native pointer - */ - public static native void sws_freeContext(long context); - - /** - * Get a SwsContext pointer. - * - * @param context SwsContext - * @param srcW width of source image - * @param srcH height of source image - * @param srcFormat image format - * @param dstW width of destination image - * @param dstH height destination image - * @param dstFormat destination format - * @param flags flags - * @return cached SwsContext pointer - */ - public static native long sws_getCachedContext( - long context, - int srcW, int srcH, int srcFormat, - int dstW, int dstH, int dstFormat, - int flags); - - /** - * Scale an image. - * - * @param context SwsContext native pointer - * @param src source image (native pointer) - * @param srcSliceY slice Y of source image - * @param srcSliceH slice H of source image - * @param dst destination image (java type) - * @param dstFormat destination format - * @param dstW width of destination image - * @param dstH height destination image - * @return 0 if success, -1 otherwise - */ - public static native int sws_scale( - long context, - long src, int srcSliceY, int srcSliceH, - Object dst, int dstFormat, int dstW, int dstH); - - /** - * Scale image an image. - * - * @param context SwsContext native pointer - * @param src source image (java type) - * @param srcFormat image format - * @param srcW width of source image - * @param srcH height of source image - * @param srcSliceY slice Y of source image - * @param srcSliceH slice H of source image - * @param dst destination image (java type) - * @param dstFormat destination format - * @param dstW width of destination image - * @param dstH height destination image - * @return 0 if success, -1 otherwise - */ - public static native int sws_scale( - long context, - Object src, int srcFormat, int srcW, int srcH, - int srcSliceY, int srcSliceH, - Object dst, int dstFormat, int dstW, int dstH); - - /** - * Register all filter from libavfilter. - */ - public static native void avfilter_register_all(); - - /** - * Allocate a AVFilterGraph. - * - * @return pointer to graph - */ - public static native long avfilter_alloc_filtergraph(); - - /** - * Free a AVFilterGraph. - * - * @param ptr native pointer - */ - public static native void avfilter_free_filtergraph(long ptr); - - /** - * Allocate a AVInputStream. - * - * @return native pointer or 0 if failure - */ - public static native long avfilter_alloc_inputstream(); - - /** - * Free a AVInputStream. - * - * @param ptr native pointer - */ - public static native void avfilter_free_inputstream(long ptr); - - /** - * Allocate a AVOutputStream. - * - * @return native pointer or 0 if failure - */ - public static native long avfilter_alloc_outputstream(); - - /** - * Free a AVOutputStream. - * - * @param ptr native pointer - */ - public static native void avfilter_free_outputstream(long ptr); - - /** - * Configure the filters. - * - * @param filters filters list - * @param avinputstream AVInputStream pointer - * @param pix_fmt pixel format - * @param width width of the video - * @param height height of the video - * @param graph AVFilterGraph pointer - * @return 0 if success, -1 otherwise - */ - public static native int avfilter_configure_filters(String filters, - long avinputstream, int pix_fmt, int width, int height, long graph); - - /** - * Add the picture to the inputstream to be filtered. - * - * @param avinputstream AVInputStream pointer - * @param avframe AVFrame pointer - */ - public static native void av_vsrc_buffer_add_frame(long avinputstream, - long avframe); - - /** - * Get the filtered video frame. - * - * @param avinputstream AVInputStream pointer - * @param avframe AVFrame pointer - * @return 0 if success, -1 otherwise - */ - public static native int av_get_filtered_video_frame(long avinputstream, - long avframe); - - static - { - System.loadLibrary("ffmpeg"); - - av_register_all(); - avfilter_register_all(); - avcodec_init(); - - PIX_FMT_BGR32 = PIX_FMT_BGR32(); - PIX_FMT_BGR32_1 = PIX_FMT_BGR32_1(); - PIX_FMT_RGB24 = PIX_FMT_RGB24(); - PIX_FMT_RGB32 = PIX_FMT_RGB32(); - PIX_FMT_RGB32_1 = PIX_FMT_RGB32_1(); - } -} +/* + * 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; + +/** + * Provides the interface to the native FFmpeg library. + * + * @author Lyubomir Marinov + * @author Sebastien Vincent + */ +public class FFmpeg +{ + /** + * No pts value. + */ + public static final long AV_NOPTS_VALUE = 0x8000000000000000L; + + /** + * Loop filter flag. + */ + public static final int CODEC_FLAG_LOOP_FILTER = 0x00000800; + + /** + * H263+ UMV flag. + */ + public static final int CODEC_FLAG_H263P_UMV = 0x01000000 ; + + /** + * AC pred flag. + */ + public static final int CODEC_FLAG_AC_PRED = 0x02000000; + + /** + * H263+ slice struct flag. + */ + public static final int CODEC_FLAG_H263P_SLICE_STRUCT = 0x10000000; + + /** + * Intra refresh flag2. + */ + public static final int CODEC_FLAG2_INTRA_REFRESH = 0x00200000; + + /** + * Allow to pass incomplete frame to decoder. + */ + public static final int CODEC_FLAG2_CHUNKS = 0x00008000; + + /** + * H264 codec ID. + */ + public static final int CODEC_ID_H264 = 28; + + /** + * H263 codec ID. + */ + public static final int CODEC_ID_H263 = 5; + + /** + * H263+ codec ID. + */ + public static final int CODEC_ID_H263P = 20; + + /** + * MJPEG codec ID. + */ + public static final int CODEC_ID_MJPEG = 8; + + /** + * MP3 codec ID. + */ + public static final int CODEC_ID_MP3 = 0x15000 + 1; + + /** + * Work around bugs in encoders which sometimes cannot be detected + * automatically. + */ + public static final int FF_BUG_AUTODETECT = 1; + + public static final int FF_CMP_CHROMA = 256; + + /** + * Padding size for FFmpeg input buffer. + */ + public static final int FF_INPUT_BUFFER_PADDING_SIZE = 8; + + public static final int FF_MB_DECISION_SIMPLE = 0; + + /** + * The minimum encoding buffer size defined by libavcodec. + */ + public static final int FF_MIN_BUFFER_SIZE = 16384; + + /** + * ARGB format. + */ + public static final int PIX_FMT_ARGB = 27; + + /** + * BGR32 format handled in endian specific manner. + * It is stored as ABGR on big-endian and RGBA on little-endian. + */ + public static final int PIX_FMT_BGR32; + + /** + * BGR32_1 format handled in endian specific manner. + * It is stored as BGRA on big-endian and ARGB on little-endian. + */ + public static final int PIX_FMT_BGR32_1; + + /** + * "NONE" format. + */ + public static final int PIX_FMT_NONE = -1; + + /** + * NV12 format. + */ + public static final int PIX_FMT_NV12 = 25; + + /** + * RGB24 format handled in endian specific manner. + * It is stored as RGB on big-endian and BGR on little-endian. + */ + public static final int PIX_FMT_RGB24; + + /** + * RGB32 format handled in endian specific manner. + * It is stored as ARGB on big-endian and BGRA on little-endian. + */ + public static final int PIX_FMT_RGB32; + + /** + * RGB32_1 format handled in endian specific manner. + * It is stored as RGBA on big-endian and ABGR on little-endian. + */ + public static final int PIX_FMT_RGB32_1; + + /** + * UYVY422 format. + */ + public static final int PIX_FMT_UYVY422 = 17; + + /** + * UYYVYY411 format. + */ + public static final int PIX_FMT_UYYVYY411 = 18; + + /** + * YUV420P format. + */ + public static final int PIX_FMT_YUV420P = 0; + + /** + * YUYV422 format. + */ + public static final int PIX_FMT_YUYV422 = 1; + + /** + * YUVJ422P format. + */ + public static final int PIX_FMT_YUVJ422P = 13; + + /** + * BICUBIC type for libswscale conversion. + */ + public static final int SWS_BICUBIC = 4; + + //public static final int X264_RC_ABR = 2; + + /** + * Free a native pointer allocated by av_malloc. + * + * @param ptr native pointer to free + */ + public static native void av_free(long ptr); + + /** + * Allocate memory. + * + * @param size size to allocate + * @return native pointer or 0 if av_malloc failed + */ + public static native long av_malloc(int size); + + /** + * Initialize libavformat and register all the muxers, demuxers and + * protocols. + */ + public static native void av_register_all(); + + /** + * Allocate a AVContext. + * + * @return native pointer to AVContext + */ + public static native long avcodec_alloc_context(); + + /** + * Allocate a AVFrame. + * + * @return native pointer to AVFrame + */ + public static native long avcodec_alloc_frame(); + + /** + * Close an AVCodecContext + * + * @param avctx pointer to AVCodecContex + * @return 0 if success, -1 otherwise + */ + public static native int avcodec_close(long avctx); + + /** + * Decode a video frame. + * + * @param avctx codec context + * @param frame frame decoded + * @param got_picture if the decoding has produced a valid picture + * @param buf the input buffer + * @param buf_size input buffer size + * @return number of bytes written to buff if success + */ + public static native int avcodec_decode_video(long avctx, long frame, + boolean[] got_picture, byte[] buf, int buf_size); + + /** + * Decode a video frame. + * + * @param avctx codec context + * @param avframe frame decoded + * @param src input buffer + * @param src_length input buffer size + * @return number of bytes written to buff if success + */ + public static native int avcodec_decode_video(long avctx, + long avframe, long src, int src_length); + + /** + * Encodes an audio frame from samples into buf. + * + * @param avctx the codec context + * @param buf the output buffer + * @param buf_offset the output buffer offset + * @param buf_size the output buffer size + * @param samples the input buffer containing the samples. The number of + * samples read from this buffer is frame_size*channels, + * both of which are defined in avctx. For PCM audio the number of + * samples read from samples is equal to + * buf_size*input_sample_size/output_sample_size. + * @param samples_offset the offset in the input buffer containing the + * samples + * @return on error a negative value is returned, on success zero or the + * number of bytes used to encode the data read from the input buffer + */ + public static native int avcodec_encode_audio( + long avctx, + byte[] buf, int buf_offset, int buf_size, + byte[] samples, int samples_offset); + + /** + * Encode a video frame. + * + * @param avctx codec context + * @param buff the output buffer + * @param buf_size output buffer size + * @param frame frame to encode + * @return number of bytes written to buff if success + */ + public static native int avcodec_encode_video(long avctx, byte[] buff, + int buf_size, long frame); + + /** + * Find a registered decoder with a matching ID. + * + * @param id CodecID of the requested encoder + * @return an AVCodec encoder if one was found; 0, + * otherwise + */ + public static native long avcodec_find_decoder(int id); + + /** + * Finds a registered encoder with a matching codec ID. + * + * @param id CodecID of the requested encoder + * @return an AVCodec encoder if one was found; 0, + * otherwise + */ + public static native long avcodec_find_encoder(int id); + + /** + * Initializes FFmpeg's avcodec. + */ + public static native void avcodec_init(); + + /** + * Initializes the specified AVCodecContext to use the specified + * AVCodec. + * + * @param avctx the AVCodecContext which will be set up to use the + * specified AVCodec + * @param codec the AVCodec to use within the + * AVCodecContext + * @return zero on success, a negative value on error + */ + public static native int avcodec_open(long avctx, long codec); + + /** + * Add specific flags to AVCodecContext's flags member. + * + * @param avctx pointer to AVCodecContext + * @param flags flags to add + */ + public static native void avcodeccontext_add_flags(long avctx, int flags); + + /** + * Add specific flags to AVCodecContext's flags2 member. + * + * @param avctx pointer to AVCodecContext + * @param flags2 flags to add + */ + public static native void avcodeccontext_add_flags2(long avctx, int flags2); + + public static native void avcodeccontext_add_partitions(long avctx, + int partitions); + + /** + * Gets the samples per packet of the specified AVCodecContext. The + * property is set by libavcodec upon {@link #avcodec_open(long, long)}. + * + * @param avctx the AVCodecContext to get the samples per packet of + * @return the samples per packet of the specified AVCodecContext + */ + public static native int avcodeccontext_get_frame_size(long avctx); + + /** + * Get height of the video. + * + * @param avctx pointer to AVCodecContext + * @return video height + */ + public static native int avcodeccontext_get_height(long avctx); + + /** + * Get pixel format. + * + * @param avctx pointer to AVCodecContext + * @return pixel format + */ + public static native int avcodeccontext_get_pix_fmt(long avctx); + + /** + * Get width of the video. + * + * @param avctx pointer to AVCodecContext + * @return video width + */ + public static native int avcodeccontext_get_width(long avctx); + + /** + * Set the B-Frame strategy. + * + * @param avctx AVCodecContext pointer + * @param b_frame_strategy strategy + */ + public static native void avcodeccontext_set_b_frame_strategy(long avctx, + int b_frame_strategy); + + /** + * Sets the average bit rate of the specified AVCodecContext. The + * property is to be set by the user when encoding and is unused for + * constant quantizer encoding. It is set by libavcodec when decoding and + * its value is 0 or some bitrate if this info is available in the + * stream. + * + * @param avctx the AVCodecContext to set the average bit rate of + * @param bit_rate the average bit rate to be set to the specified + * AVCodecContext + */ + public static native void avcodeccontext_set_bit_rate(long avctx, + int bit_rate); + + /** + * Set the bit rate tolerance + * + * @param avctx the AVCodecContext to set the bit rate of + * @param bit_rate_tolerance bit rate tolerance + */ + public static native void avcodeccontext_set_bit_rate_tolerance(long avctx, + int bit_rate_tolerance); + + /** + * Sets the number of channels of the specified AVCodecContext. The + * property is audio only. + * + * @param avctx the AVCodecContext to set the number of channels of + * @param channels the number of channels to set to the specified + * AVCodecContext + */ + public static native void avcodeccontext_set_channels( + long avctx, int channels); + + public static native void avcodeccontext_set_chromaoffset(long avctx, + int chromaoffset); + + public static native void avcodeccontext_set_crf(long avctx, float crf); + + public static native void avcodeccontext_set_deblockbeta(long avctx, + int deblockbeta); + + /** + * Set the gop size (key frame interval). + * + * @param avctx the AVCodecContext to set the gop size of + * @param gop_size key frame interval + */ + public static native void avcodeccontext_set_gop_size(long avctx, + int gop_size); + + public static native void avcodeccontext_set_i_quant_factor(long avctx, + float i_quant_factor); + + /** + * Sets the minimum GOP size. + * + * @param avctx the AVCodecContext to set the minimum GOP size of + * @param keyint_min the minimum GOP size to set on avctx + */ + public static native void avcodeccontext_set_keyint_min(long avctx, + int keyint_min); + + /** + * Set the maximum B frames. + * + * @param avctx the AVCodecContext to set the maximum B frames of + * @param max_b_frames maximum B frames + */ + public static native void avcodeccontext_set_max_b_frames(long avctx, + int max_b_frames); + + public static native void avcodeccontext_set_mb_decision(long avctx, + int mb_decision); + + public static native void avcodeccontext_set_me_cmp(long avctx, int me_cmp); + + public static native void avcodeccontext_set_me_method(long avctx, + int me_method); + + public static native void avcodeccontext_set_me_range(long avctx, + int me_range); + + public static native void avcodeccontext_set_me_subpel_quality(long avctx, + int me_subpel_quality); + + /** + * Set the pixel format. + * + * @param avctx the AVCodecContext to set the pixel format of + * @param pix_fmt pixel format + */ + public static native void avcodeccontext_set_pix_fmt(long avctx, + int pix_fmt); + + public static native void avcodeccontext_set_qcompress(long avctx, + float qcompress); + + public static native void avcodeccontext_set_quantizer(long avctx, + int qmin, int qmax, int max_qdiff); + + public static native void avcodeccontext_set_rc_buffer_size(long avctx, + int rc_buffer_size); + + public static native void avcodeccontext_set_rc_eq(long avctx, String rc_eq); + + public static native void avcodeccontext_set_rc_max_rate(long avctx, + int rc_max_rate); + + public static native void avcodeccontext_set_refs(long avctx, + int refs); + + /** + * Set the RTP payload size. + * + * @param avctx the AVCodecContext to set the RTP payload size of + * @param rtp_payload_size RTP payload size + */ + public static native void avcodeccontext_set_rtp_payload_size(long avctx, + int rtp_payload_size); + + public static native void avcodeccontext_set_sample_aspect_ratio( + long avctx, int num, int den); + + /** + * Sets the samples per second of the specified AVCodecContext. The + * property is audio only. + * + * @param avctx the AVCodecContext to set the samples per second of + * @param sample_rate the samples per second to set to the specified + * AVCodecContext + */ + public static native void avcodeccontext_set_sample_rate( + long avctx, int sample_rate); + + /** + * Set the scene change threshold (in percent). + * + * @param avctx AVCodecContext pointer + * @param scenechange_threshold value between 0 and 100 + */ + public static native void avcodeccontext_set_scenechange_threshold( + long avctx, int scenechange_threshold); + + /** + * Set the size of the video. + * + * @param avctx pointer to AVCodecContext + * @param width video width + * @param height video height + */ + public static native void avcodeccontext_set_size(long avctx, int width, + int height); + + /** + * Set the number of thread. + * + * @param avctx the AVCodecContext to set the number of thread of + * @param thread_count number of thread to set + */ + public static native void avcodeccontext_set_thread_count(long avctx, + int thread_count); + + public static native void avcodeccontext_set_ticks_per_frame(long avctx, + int ticks_per_frame); + + public static native void avcodeccontext_set_time_base(long avctx, int num, + int den); + + public static native void avcodeccontext_set_trellis(long avctx, + int trellis); + + public static native void avcodeccontext_set_workaround_bugs(long avctx, + int workaround_bugs); + + public static native long avframe_get_pts(long frame); + + public static native void avframe_set_data(long frame, long data0, + long offset1, long offset2); + + public static native void avframe_set_key_frame(long frame, + boolean key_frame); + + public static native void avframe_set_linesize(long frame, int linesize0, + int linesize1, int linesize2); + + public static native int avpicture_fill(long picture, long ptr, + int pix_fmt, int width, int height); + + public static native long avpicture_get_data0(long picture); + + public static native int avpicture_get_size(int pix_fmt, int width, + int height); + + public static native void memcpy(int[] dst, int dst_offset, int dst_length, + long src); + + public static native void memcpy(long dst, byte[] src, int src_offset, + int src_length); + + /** + * Get BGR32 pixel format. + * + * @return BGR32 pixel format + */ + private static native int PIX_FMT_BGR32(); + + /** + * Get BGR32_1 pixel format. + * + * @return BGR32_1 pixel format + */ + private static native int PIX_FMT_BGR32_1(); + + /** + * Get RGB24 pixel format. + * + * @return RGB24 pixel format + */ + private static native int PIX_FMT_RGB24(); + + /** + * Get RGB32 pixel format. + * + * @return RGB32 pixel format + */ + private static native int PIX_FMT_RGB32(); + + /** + * Get RGB32_1 pixel format. + * + * @return RGB32_1 pixel format + */ + private static native int PIX_FMT_RGB32_1(); + + /** + * Free an SwsContext. + * + * @param context SwsContext native pointer + */ + public static native void sws_freeContext(long context); + + /** + * Get a SwsContext pointer. + * + * @param context SwsContext + * @param srcW width of source image + * @param srcH height of source image + * @param srcFormat image format + * @param dstW width of destination image + * @param dstH height destination image + * @param dstFormat destination format + * @param flags flags + * @return cached SwsContext pointer + */ + public static native long sws_getCachedContext( + long context, + int srcW, int srcH, int srcFormat, + int dstW, int dstH, int dstFormat, + int flags); + + /** + * Scale an image. + * + * @param context SwsContext native pointer + * @param src source image (native pointer) + * @param srcSliceY slice Y of source image + * @param srcSliceH slice H of source image + * @param dst destination image (java type) + * @param dstFormat destination format + * @param dstW width of destination image + * @param dstH height destination image + * @return 0 if success, -1 otherwise + */ + public static native int sws_scale( + long context, + long src, int srcSliceY, int srcSliceH, + Object dst, int dstFormat, int dstW, int dstH); + + /** + * Scale image an image. + * + * @param context SwsContext native pointer + * @param src source image (java type) + * @param srcFormat image format + * @param srcW width of source image + * @param srcH height of source image + * @param srcSliceY slice Y of source image + * @param srcSliceH slice H of source image + * @param dst destination image (java type) + * @param dstFormat destination format + * @param dstW width of destination image + * @param dstH height destination image + * @return 0 if success, -1 otherwise + */ + public static native int sws_scale( + long context, + Object src, int srcFormat, int srcW, int srcH, + int srcSliceY, int srcSliceH, + Object dst, int dstFormat, int dstW, int dstH); + + /** + * Allocates a new AVFilterGraph instance. + * + * @return a pointer to the newly-allocated AVFilterGraph instance + */ + public static native long avfilter_graph_alloc(); + + /** + * Checks the validity and configures all the links and formats in a + * specific AVFilterGraph instance. + * + * @param graph a pointer to the AVFilterGraph instance to check + * the validity of and configure + * @param log_ctx the AVClass context to be used for logging + * @return 0 on success; a negative AVERROR on error + */ + public static native int avfilter_graph_config(long graph, long log_ctx); + + /** + * Frees a specific AVFilterGraph instance and destroys its links. + * + * @param graph a pointer to the AVFilterGraph instance to free + */ + public static native void avfilter_graph_free(long graph); + + /** + * Gets a pointer to an AVFilterContext instance with a specific + * name in a specific AVFilterGraph instance. + * + * @param graph a pointer to the AVFilterGraph instance where the + * AVFilterContext instance with the specified name is to be found + * @param name the name of the AVFilterContext instance which is to + * be found in the specified graph + */ + public static native long avfilter_graph_get_filter( + long graph, + String name); + + /** + * Adds a filter graph described by a String to a specific + * AVFilterGraph instance. + * + * @param graph a pointer to the AVFilterGraph instance where to + * link the parsed graph context + * @param filters the String to be parsed + * @param inputs a pointer to a linked list to the inputs of the graph if + * any; otherwise, 0 + * @param outputs a pointer to a linked list to the outputs of the graph if + * any; otherwise, 0 + * @param log_ctx the AVClass context to be used for logging + * @return 0 on success; a negative AVERROR on error + */ + public static native int avfilter_graph_parse( + long graph, + String filters, long inputs, long outputs, long log_ctx); + + /** + * Initializes the libavfilter system and registers all built-in + * filters. + */ + public static native void avfilter_register_all(); + + /** + * Removes a reference to a buffer. If the specified + * AVFilterBufferRef is the last reference to the buffer, the + * buffer is also automatically freed. + * + * @param ref a pointer to the AVFilterBufferRef instance to remove + */ + public static native void avfilter_unref_buffer(long ref); + + public static native long get_filtered_video_frame( + long input, int width, int height, int pixFmt, + long buffer, + long ffsink, + long output); + + static + { + System.loadLibrary("jnffmpeg"); + + av_register_all(); + avfilter_register_all(); + avcodec_init(); + + PIX_FMT_BGR32 = PIX_FMT_BGR32(); + PIX_FMT_BGR32_1 = PIX_FMT_BGR32_1(); + PIX_FMT_RGB24 = PIX_FMT_RGB24(); + PIX_FMT_RGB32 = PIX_FMT_RGB32(); + PIX_FMT_RGB32_1 = PIX_FMT_RGB32_1(); + } +} diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/video/AVFrameFormat.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/AVFrameFormat.java index a58d6ee70..6d448aab2 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/video/AVFrameFormat.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/AVFrameFormat.java @@ -135,6 +135,7 @@ protected void copy(Format f) AVFrameFormat avFrameFormat = (AVFrameFormat) f; pixFmt = avFrameFormat.pixFmt; + devicePixFmt = avFrameFormat.devicePixFmt; } } @@ -181,6 +182,12 @@ public int getDevicePixFmt() return devicePixFmt; } + @Override + public int hashCode() + { + return super.hashCode() + pixFmt; + } + /** * Finds the attributes shared by two matching Formats. If the * specified Format does not match this one, the result is @@ -195,15 +202,17 @@ public Format intersects(Format format) { Format intersection = super.intersects(format); - if (intersection == null) - return null; - - AVFrameFormat avFrameFormatIntersection = (AVFrameFormat) intersection; + if (intersection != null) + { + AVFrameFormat avFrameFormatIntersection + = (AVFrameFormat) intersection; - avFrameFormatIntersection.pixFmt - = ((pixFmt == NOT_SPECIFIED) && (format instanceof AVFrameFormat)) - ? ((AVFrameFormat) format).pixFmt - : pixFmt; + avFrameFormatIntersection.pixFmt + = ((pixFmt == NOT_SPECIFIED) + && (format instanceof AVFrameFormat)) + ? ((AVFrameFormat) format).pixFmt + : pixFmt; + } return intersection; } @@ -219,16 +228,30 @@ public Format intersects(Format format) @Override public boolean matches(Format format) { - if (!super.matches(format)) - return false; - if (!(format instanceof AVFrameFormat)) - return true; + boolean matches; - AVFrameFormat avFrameFormat = (AVFrameFormat) format; + if (super.matches(format)) + { + if (format instanceof AVFrameFormat) + { + AVFrameFormat avFrameFormat = (AVFrameFormat) format; - return - (pixFmt == NOT_SPECIFIED - || (avFrameFormat.pixFmt == NOT_SPECIFIED) - || (pixFmt == avFrameFormat.pixFmt)); + matches + = ((pixFmt == NOT_SPECIFIED) + || (avFrameFormat.pixFmt == NOT_SPECIFIED) + || (pixFmt == avFrameFormat.pixFmt)); + } + else + matches = true; + } + else + matches = false; + return matches; + } + + @Override + public String toString() + { + return super.toString() + ", pixFmt= " + pixFmt; } } diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/video/HFlip.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/HFlip.java new file mode 100644 index 000000000..e22b75a4c --- /dev/null +++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/HFlip.java @@ -0,0 +1,347 @@ +/* + * 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 net.java.sip.communicator.impl.neomedia.codec.*; +import net.java.sip.communicator.util.*; + +/** + * Implements a video Effect which horizontally flips + * AVFrames. + * + * @author Sebastien Vincent + * @author Lyubomir Marinov + */ +public class HFlip + extends AbstractCodecExt + implements Effect +{ + /** + * The Logger used by the HFlip class and its instances + * for logging output. + */ + private static final Logger logger = Logger.getLogger(HFlip.class); + + /** + * The list of Formats supported by HFlip instances as + * input and output. + */ + private static final Format[] SUPPORTED_FORMATS + = new Format[] { new AVFrameFormat() }; + + /** + * The name of the FFmpeg ffsink video source AVFilter used by + * HFlip. + */ + private static final String VSINK_FFSINK_NAME = "nullsink"; + + /** + * The name of the FFmpeg buffer video source AVFilter used by + * HFlip. + */ + private static final String VSRC_BUFFER_NAME = "buffer"; + + /** + * The pointer to the AVFilterContext in {@link #graph} of the + * FFmpeg video source with the name {@link #VSRC_BUFFER_NAME}. + */ + private long buffer; + + /** + * The pointer to the AVFilterContext in {@link #graph} of the + * FFmpeg video sink with the name {@link #VSINK_FFSINK_NAME}. + */ + private long ffsink; + + /** + * The pointer to the AVFilterGraph instance which contains the + * FFmpeg hflip filter represented by this Effect. + */ + private long graph; + + /** + * The height of {@link #graph}. + */ + private int height; + + /** + * The pointer to the AVFilterBufferRef instance represented as an + * AVFrame by {@link #outputFrame}. + */ + private long outputFilterBufferRef; + + /** + * The pointer to the AVFrame instance which is the output (data) + * of this Effect. + */ + private long outputFrame; + + /** + * The FFmpeg pixel format of {@link #graph}. + */ + private int pixFmt = FFmpeg.PIX_FMT_NONE; + + /** + * The width of {@link #graph}. + */ + private int width; + + /** + * Initializes a new HFlip instance. + */ + public HFlip() + { + super("FFmpeg HFlip Filter", AVFrameFormat.class, SUPPORTED_FORMATS); + } + + /** + * Closes this Effect. + * + * @see AbstractCodecExt#doClose() + */ + protected synchronized void doClose() + { + try + { + if (outputFrame != 0) + { + FFmpeg.av_free(outputFrame); + outputFrame = 0; + } + } + finally + { + reset(); + } + } + + /** + * Opens this Effect. + * + * @throws ResourceUnavailableException if any of the required resource + * cannot be allocated + * @see AbstractCodecExt#doOpen() + */ + protected synchronized void doOpen() + throws ResourceUnavailableException + { + outputFrame = FFmpeg.avcodec_alloc_frame(); + if (outputFrame == 0) + { + String reason = "avcodec_alloc_frame: " + outputFrame; + + logger.error(reason); + throw new ResourceUnavailableException(reason); + } + } + + /** + * Performs the media processing defined by this Effect. + * + * @param inputBuffer the Buffer that contains the media data to be + * processed + * @param outputBuffer the Buffer in which to store the processed + * media data + * @return BUFFER_PROCESSED_OK if the processing is successful + * @see AbstractCodecExt#doProcess(Buffer, Buffer) + */ + protected synchronized int doProcess( + Buffer inputBuffer, + Buffer outputBuffer) + { + /* + * A new frame is about to be output so the old frame is no longer + * necessary. + */ + if (outputFilterBufferRef != 0) + { + FFmpeg.avfilter_unref_buffer(outputFilterBufferRef); + outputFilterBufferRef = 0; + } + + /* + * Make sure the graph is configured with the current Format i.e. size + * and pixFmt. + */ + AVFrameFormat format = (AVFrameFormat) inputBuffer.getFormat(); + Dimension size = format.getSize(); + int pixFmt = format.getPixFmt(); + + if ((this.width != size.width) + || (this.height != size.height) + || (this.pixFmt != pixFmt)) + reset(); + if (graph == 0) + { + String errorReason = null; + int error = 0; + long buffer = 0; + long ffsink = 0; + + graph = FFmpeg.avfilter_graph_alloc(); + if (graph == 0) + errorReason = "avfilter_graph_alloc"; + else + { + String filters + = VSRC_BUFFER_NAME + "=" + size.width + ":" + size.height + + ":" + pixFmt + ":1:1000000:1:1,hflip," + + VSINK_FFSINK_NAME; + long log_ctx = 0; + + error + = FFmpeg.avfilter_graph_parse( + graph, + filters, + 0, 0, + log_ctx); + if (error == 0) + { + /* + * Unfortunately, the name of an AVFilterContext created by + * avfilter_graph_parse is not the name of the AVFilter. + */ + String parsedFilterNameFormat = "Parsed filter %1$d %2$s"; + + buffer + = FFmpeg.avfilter_graph_get_filter( + graph, + String.format( + parsedFilterNameFormat, + 0, + VSRC_BUFFER_NAME)); + if (buffer == 0) + { + errorReason + = "avfilter_graph_get_filter: " + VSRC_BUFFER_NAME; + } + else + { + ffsink + = FFmpeg.avfilter_graph_get_filter( + graph, + String.format( + parsedFilterNameFormat, + 2, + VSINK_FFSINK_NAME)); + if (ffsink == 0) + { + errorReason + = "avfilter_graph_get_filter: " + + VSINK_FFSINK_NAME; + } + else + { + error + = FFmpeg.avfilter_graph_config(graph, log_ctx); + if (error != 0) + errorReason = "avfilter_graph_config"; + } + } + } + else + errorReason = "avfilter_graph_parse"; + if ((errorReason != null) || (error != 0)) + { + FFmpeg.avfilter_graph_free(graph); + graph = 0; + } + } + if (graph == 0) + { + if (errorReason != null) + { + if (error == 0) + logger.error(errorReason); + else + logger.error(errorReason + ": " + error); + } + return BUFFER_PROCESSED_FAILED; + } + else + { + this.width = size.width; + this.height = size.height; + this.pixFmt = pixFmt; + this.buffer = buffer; + this.ffsink = ffsink; + } + } + + /* + * The graph is configured for the current Format, apply its filters to + * the inputFrame. + */ + long inputFrame = ((AVFrame) inputBuffer.getData()).getPtr(); + + outputFilterBufferRef + = FFmpeg.get_filtered_video_frame( + inputFrame, this.width, this.height, this.pixFmt, + buffer, + ffsink, + outputFrame); + if(outputFilterBufferRef == 0) + { + /* + * If get_filtered_video_frame fails, it is likely to fail for any + * frame. Consequently, printing that it has failed will result in a + * lot of repeating logging output. Since the failure in question + * will be visible in the UI anyway, just debug it. + */ + if (logger.isDebugEnabled()) + logger.debug("get_filtered_video_frame"); + return BUFFER_PROCESSED_FAILED; + } + + Object out = outputBuffer.getData(); + + if (!(out instanceof AVFrame) + || (((AVFrame) out).getPtr() != outputFrame)) + { + outputBuffer.setData(new AVFrame(outputFrame)); + } + + outputBuffer.setDiscard(inputBuffer.isDiscard()); + outputBuffer.setDuration(inputBuffer.getDuration()); + outputBuffer.setEOM(inputBuffer.isEOM()); + outputBuffer.setFlags(inputBuffer.getFlags()); + outputBuffer.setFormat(format); + outputBuffer.setHeader(inputBuffer.getHeader()); + outputBuffer.setLength(inputBuffer.getLength()); + outputBuffer.setSequenceNumber(inputBuffer.getSequenceNumber()); + outputBuffer.setTimeStamp(inputBuffer.getTimeStamp()); + return BUFFER_PROCESSED_OK; + } + + /** + * Resets the state of this PlugIn. + */ + @Override + public synchronized void reset() + { + if (outputFilterBufferRef != 0) + { + FFmpeg.avfilter_unref_buffer(outputFilterBufferRef); + outputFilterBufferRef = 0; + } + if (graph != 0) + { + FFmpeg.avfilter_graph_free(graph); + graph = 0; + + width = 0; + height = 0; + pixFmt = FFmpeg.PIX_FMT_NONE; + buffer = 0; + ffsink = 0; + } + } +} 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 042648a0e..ea510f713 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 @@ -20,7 +20,7 @@ * Implements Codec to represent a depacketizer of H.264 RTP packets * into NAL units. * - * @author Lubomir Marinov + * @author Lyubomir Marinov * @author Damian Minkov */ public class DePacketizer @@ -83,7 +83,7 @@ public class DePacketizer private long lastSequenceNumber = -1; /** - * The timestamp of the last received RTP packet. + * The time stamp of the last received RTP packet. */ private long lastTimeStamp = -1; @@ -117,31 +117,31 @@ public class DePacketizer private boolean usePLI = false; /** - * Last timestamp of keyframe request. - * - * This is used to retransmit keyframe request in case previous get lost - * and codec doesn't receive keyframe. + * The time stamp of the last keyframe request. Used to retransmit a + * keyframe request in case a previous one gets lost and this + * DePacketizer does not receive a keyframe. */ private long lastPLIRequestTime = -1; /** - * Last timestamp of received keyframe. + * The time stamp of the last received keyframe. */ private long lastReceivedKeyframeTime = -1; /** - * If Codec has detected frames lost. + * The indicator which determines whether this DePacketizer has + * detected lost frames. */ - private boolean missFrame = false; + private boolean lostFrame = false; /** - * State of PLISendThread. + * The indicator which determines whether {@link #pliSendThread} is running. */ - private boolean isPLIThreadRunning = false; + private boolean pliSendThreadIsRunning = false; /** - * Thread that will monitor time between PLI request, keyframes received - * and will send or not a PLI. + * The Thread which monitors the time between PLI request, + * keyframes received and sends a PLI. */ private PLISendThread pliSendThread = new PLISendThread(); @@ -156,8 +156,18 @@ public DePacketizer() VideoFormat.class, new VideoFormat[] { new VideoFormat(Constants.H264) }); - inputFormats - = new VideoFormat[] { new VideoFormat(Constants.H264_RTP) }; + List inputFormats = new ArrayList(); + + inputFormats.add(new VideoFormat(Constants.H264_RTP)); + /* + * Apart from the generic Constants.H264_RTP VideoFormat, add the + * possible respective ParameterizedVideoFormats because + * ParameterizedVideoFormat will not match every VideoFormat due to the + * fact that a missing packetization-mode format parameter is + * interpreted as it having a value of 0. + */ + Collections.addAll(inputFormats, Packetizer.SUPPORTED_OUTPUT_FORMATS); + this.inputFormats = inputFormats.toArray(new Format[inputFormats.size()]); } /** @@ -201,7 +211,7 @@ private int dePacketizeFUA( if(keyframe) { /* keyframe received */ - missFrame = false; + lostFrame = false; lastReceivedKeyframeTime = System.currentTimeMillis(); } @@ -308,7 +318,7 @@ private int dePacketizeSingleNALUnitPacket( if(nal_unit_type == 5) { /* keyframe received */ - missFrame = false; + lostFrame = false; lastReceivedKeyframeTime = System.currentTimeMillis(); } @@ -321,7 +331,7 @@ private int dePacketizeSingleNALUnitPacket( protected void doClose() { /* will end PLISendThread loop */ - isPLIThreadRunning = false; + pliSendThreadIsRunning = false; pliSendThread = null; } @@ -343,6 +353,7 @@ protected void doOpen() lastSequenceNumber = -1; lastTimeStamp = -1; } + /** * Processes (depacketizes) a buffer. * @@ -382,12 +393,12 @@ protected int doProcess(Buffer inBuffer, Buffer outBuffer) /* detection of missed frames */ if(usePLI) { - missFrame = true; + lostFrame = true; /* if the PLI thread is not started, start it! */ - if(!isPLIThreadRunning) + if(!pliSendThreadIsRunning) { - isPLIThreadRunning = true; + pliSendThreadIsRunning = true; pliSendThread.start(); } } @@ -574,35 +585,39 @@ public void setRtcpFeedbackPLI(boolean use) } /** - * Thread that will handle Picture Loss Indication (PLI) transmission. - * Note that PLI will be sent with a delay if recently we received a + * Represents a Thread which is to handle Picture Loss Indication + * (PLI) transmission. + *

+ * Note: PLI will be sent with a delay if we have recently received a * keyframe or sent a PLI. + *

* * @author Sebastien Vincent */ - private class PLISendThread extends Thread + private class PLISendThread + extends Thread { /** - * Represents the entry point of PLISendThread. + * Implements the entry point of this PLISendThread. */ @Override public void run() { - while(isPLIThreadRunning) + while(pliSendThreadIsRunning) { long time = System.currentTimeMillis(); - if(missFrame) + if(lostFrame) { /* we keep some delay with the latest keyframe */ - if(time > (lastReceivedKeyframeTime + - PLI_KEYFRAME_INTERVAL)) + if(time > + (lastReceivedKeyframeTime + PLI_KEYFRAME_INTERVAL)) { /* we keep delay between latest PLI to not spam sender * with a lot of PLI request */ - if(lastPLIRequestTime == -1 || - time > lastPLIRequestTime + PLI_INTERVAL) + if((lastPLIRequestTime == -1) || + (time > lastPLIRequestTime + PLI_INTERVAL)) { lastPLIRequestTime = time; sendRTCPFeedbackPLI(); @@ -614,7 +629,7 @@ public void run() { Thread.sleep(100); } - catch(Exception e) + catch (InterruptedException ie) { } } diff --git a/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/JNIDecoder.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/JNIDecoder.java index 90ffd7c8a..2d37b2409 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/JNIDecoder.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/JNIDecoder.java @@ -1,349 +1,357 @@ -/* - * 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.h264; - -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.*; - -/** - * Decodes H.264 NAL units and returns the resulting frames as FFmpeg - * AVFrames (i.e. in YUV format). - * - * @author Damian Minkov - * @author Lubomir Marinov - * @author Sebastien Vincent - */ -public class JNIDecoder - extends AbstractCodec -{ - /** - * Plugin name. - */ - private static final String PLUGIN_NAME = "H.264 Decoder"; - - /** - * The default output VideoFormat. - */ - private static final VideoFormat[] DEFAULT_OUTPUT_FORMATS - = new VideoFormat[] { new AVFrameFormat() }; - - /** - * The codec context native pointer we will use. - */ - private long avcontext; - - /** - * The decoded data is stored in avpicture in native ffmpeg format (YUV). - */ - private long avframe; - - /** - * If decoder has got a picture. - */ - private final boolean[] got_picture = new boolean[1]; - - /** - * 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; - - /** - * Array of output VideoFormats. - */ - private final VideoFormat[] outputFormats; - - /** - * 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; - - /** - * Initializes a new JNIDecoder instance which is to decode H.264 - * NAL units into frames in YUV format. - */ - public JNIDecoder() - { - inputFormats - = new VideoFormat[] { new VideoFormat(Constants.H264) }; - 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 H264_RTP - */ - public boolean checkFormat(Format format) - { - return format.getEncoding().equals(Constants.H264_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.264 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; - - long avcodec = FFmpeg.avcodec_find_decoder(FFmpeg.CODEC_ID_H264); - - avcontext = FFmpeg.avcodec_alloc_context(); - FFmpeg.avcodeccontext_set_workaround_bugs(avcontext, - FFmpeg.FF_BUG_AUTODETECT); - - /* allow to pass incomplete frame to decoder */ - FFmpeg.avcodeccontext_add_flags2(avcontext, FFmpeg.CODEC_FLAG2_CHUNKS); - - if (FFmpeg.avcodec_open(avcontext, avcodec) < 0) - throw new RuntimeException("Could not open codec CODEC_ID_H264"); - - avframe = FFmpeg.avcodec_alloc_frame(); - - opened = true; - super.open(); - } - - /** - * Decodes H.264 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; - } -} +/* + * 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.h264; + +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.*; + +/** + * Decodes H.264 NAL units and returns the resulting frames as FFmpeg + * AVFrames (i.e. in YUV format). + * + * @author Damian Minkov + * @author Lyubomir Marinov + * @author Sebastien Vincent + */ +public class JNIDecoder + extends AbstractCodec +{ + /** + * Plugin name. + */ + private static final String PLUGIN_NAME = "H.264 Decoder"; + + /** + * The default output VideoFormat. + */ + private static final VideoFormat[] DEFAULT_OUTPUT_FORMATS + = new VideoFormat[] { new AVFrameFormat() }; + + /** + * The codec context native pointer we will use. + */ + private long avctx; + + /** + * The decoded data is stored in avpicture in native ffmpeg format (YUV). + */ + private long avframe; + + /** + * If decoder has got a picture. + */ + private final boolean[] got_picture = new boolean[1]; + + /** + * The last known height of {@link #avctx} i.e. the video output by this + * JNIDecoder. Used to detect changes in the output size. + */ + private int height; + + /** + * Array of output VideoFormats. + */ + private final VideoFormat[] outputFormats; + + /** + * The last known width of {@link #avctx} i.e. the video output by this + * JNIDecoder. Used to detect changes in the output size. + */ + private int width; + + /** + * Initializes a new JNIDecoder instance which is to decode H.264 + * NAL units into frames in YUV format. + */ + public JNIDecoder() + { + inputFormats + = new VideoFormat[] { new VideoFormat(Constants.H264) }; + 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 H264_RTP + */ + public boolean checkFormat(Format format) + { + return format.getEncoding().equals(Constants.H264_RTP); + } + + /** + * Close Codec. + */ + @Override + public synchronized void close() + { + if (opened) + { + opened = false; + super.close(); + + FFmpeg.avcodec_close(avctx); + FFmpeg.av_free(avctx); + avctx = 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.264 Decoder" + */ + @Override + public String getName() + { + return PLUGIN_NAME; + } + + /** + * Get all supported output Formats. + * + * @param inputFormat input Format to determine corresponding + * output Format/tt>s + * @return an array of supported output Formats + */ + public Format[] getSupportedOutputFormats(Format inputFormat) + { + Format[] supportedOutputFormats; + + if (inputFormat == null) + supportedOutputFormats = DEFAULT_OUTPUT_FORMATS; + else + { + // mismatch input format + if (!(inputFormat instanceof VideoFormat) + || (AbstractCodecExt.matches(inputFormat, inputFormats) + == null)) + supportedOutputFormats = new Format[0]; + else + { + // match input format + supportedOutputFormats = getMatchingOutputFormats(inputFormat); + } + } + return supportedOutputFormats; + } + + /** + * Inits the codec instances. + * + * @throws ResourceUnavailableException if codec initialization failed + */ + @Override + public synchronized void open() + throws ResourceUnavailableException + { + if (opened) + return; + + long avcodec = FFmpeg.avcodec_find_decoder(FFmpeg.CODEC_ID_H264); + + avctx = FFmpeg.avcodec_alloc_context(); + FFmpeg.avcodeccontext_set_workaround_bugs(avctx, + FFmpeg.FF_BUG_AUTODETECT); + + /* allow to pass incomplete frame to decoder */ + FFmpeg.avcodeccontext_add_flags2(avctx, + FFmpeg.CODEC_FLAG2_CHUNKS); + + if (FFmpeg.avcodec_open(avctx, avcodec) < 0) + throw new RuntimeException("Could not open codec CODEC_ID_H264"); + + avframe = FFmpeg.avcodec_alloc_frame(); + + opened = true; + super.open(); + } + + /** + * Decodes H.264 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( + avctx, + 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(avctx); + int height = FFmpeg.avcodeccontext_get_height(avctx); + + 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/h264/JNIEncoder.java b/src/net/java/sip/communicator/impl/neomedia/codec/video/h264/JNIEncoder.java index 9016f254d..b45f0bca8 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 @@ -7,20 +7,21 @@ package net.java.sip.communicator.impl.neomedia.codec.video.h264; import java.awt.*; +import java.util.*; import javax.media.*; import javax.media.format.*; -import net.java.sip.communicator.service.neomedia.event.*; - import net.java.sip.communicator.impl.neomedia.codec.*; +import net.java.sip.communicator.impl.neomedia.format.*; +import net.java.sip.communicator.service.neomedia.event.*; import net.sf.fmj.media.*; /** * Implements a H.264 encoder. * * @author Damian Minkov - * @author Lubomir Marinov + * @author Lyubomir Marinov * @author Sebastien Vincent */ public class JNIEncoder @@ -35,15 +36,15 @@ public class JNIEncoder private static final int DEFAULT_FRAME_RATE = 15; /** - * Default output formats. + * Key frame every 150 frames. */ - private static final Format[] DEFAULT_OUTPUT_FORMATS - = { new VideoFormat(Constants.H264) }; + private static final int IFRAME_INTERVAL = 150; /** - * Key frame every 150 frames. + * The name of the format parameter which specifies the packetization mode + * of H.264 RTP payload. */ - private static final int IFRAME_INTERVAL = 150; + public static final String PACKETIZATION_MODE_FMTP = "packetization-mode"; /** * Minimum interval between two PLI request processing (in milliseconds). @@ -55,10 +56,24 @@ public class JNIEncoder */ private static final String PLUGIN_NAME = "H.264 Encoder"; + /** + * The list of Formats supported by JNIEncoder instances + * as output. + */ + static final Format[] SUPPORTED_OUTPUT_FORMATS + = { + new ParameterizedVideoFormat( + Constants.H264, + PACKETIZATION_MODE_FMTP, "0"), + new ParameterizedVideoFormat( + Constants.H264, + PACKETIZATION_MODE_FMTP, "1") + }; + /** * The codec we will use. */ - private long avcontext; + private long avctx; /** * The encoded data is stored in avpicture. @@ -101,8 +116,8 @@ public class JNIEncoder * manage to decode the first keyframe and must wait for the next periodic * intra refresh to display the video to the user. * - * Temporary solution for this probolem: send the two first frames as - * keyframe to display video stream. + * Temporary solution for this problem: send the two first frames as + * keyframes to display video stream. */ private boolean secondKeyFrame = true; @@ -139,9 +154,9 @@ public synchronized void close() opened = false; super.close(); - FFmpeg.avcodec_close(avcontext); - FFmpeg.av_free(avcontext); - avcontext = 0; + FFmpeg.avcodec_close(avctx); + FFmpeg.av_free(avctx); + avctx = 0; FFmpeg.av_free(avframe); avframe = 0; @@ -152,6 +167,38 @@ public synchronized void close() } } + /** + * Event fired when RTCP feedback message is received. + * + * @param event RTCPFeedbackEvent + */ + public void feedbackReceived(RTCPFeedbackEvent event) + { + /* + * If RTCP message is a Picture Loss Indication (PLI) or a + * Full Intra-frame Request (FIR) the encoder will force the next frame + * to be a keyframe. + */ + if (event.getPayloadType() == RTCPFeedbackEvent.PT_PS) + { + switch (event.getFeedbackMessageType()) + { + case RTCPFeedbackEvent.FMT_PLI: + case RTCPFeedbackEvent.FMT_FIR: + if (System.currentTimeMillis() + > (lastKeyframeRequestTime + PLI_INTERVAL)) + { + lastKeyframeRequestTime = System.currentTimeMillis(); + // Disable PLI. + //forceKeyFrame = true; + } + break; + default: + break; + } + } + } + /** * Gets the matching output formats for a specific format. * @@ -163,14 +210,17 @@ private Format[] getMatchingOutputFormats(Format in) VideoFormat videoIn = (VideoFormat) in; return - new VideoFormat[] + new Format[] { - new VideoFormat( + new ParameterizedVideoFormat( Constants.H264, videoIn.getSize(), Format.NOT_SPECIFIED, Format.byteArray, - videoIn.getFrameRate()) + videoIn.getFrameRate(), + ParameterizedVideoFormat.toMap( + PACKETIZATION_MODE_FMTP, + "1")) }; } @@ -196,7 +246,7 @@ public Format[] getSupportedOutputFormats(Format in) { // null input format if (in == null) - return DEFAULT_OUTPUT_FORMATS; + return SUPPORTED_OUTPUT_FORMATS; // mismatch input format if (!(in instanceof VideoFormat) @@ -228,12 +278,12 @@ public synchronized void open() long avcodec = FFmpeg.avcodec_find_encoder(FFmpeg.CODEC_ID_H264); - avcontext = FFmpeg.avcodec_alloc_context(); + avctx = FFmpeg.avcodec_alloc_context(); - FFmpeg.avcodeccontext_set_pix_fmt(avcontext, FFmpeg.PIX_FMT_YUV420P); - FFmpeg.avcodeccontext_set_size(avcontext, width, height); + FFmpeg.avcodeccontext_set_pix_fmt(avctx, FFmpeg.PIX_FMT_YUV420P); + FFmpeg.avcodeccontext_set_size(avctx, width, height); - FFmpeg.avcodeccontext_set_qcompress(avcontext, 0.6f); + FFmpeg.avcodeccontext_set_qcompress(avctx, 0.6f); int bitRate = 128000; int frameRate = (int) outputVideoFormat.getFrameRate(); @@ -242,58 +292,52 @@ public synchronized void open() frameRate = DEFAULT_FRAME_RATE; // average bit rate - FFmpeg.avcodeccontext_set_bit_rate(avcontext, bitRate); + FFmpeg.avcodeccontext_set_bit_rate(avctx, bitRate); // so to be 1 in x264 - FFmpeg.avcodeccontext_set_bit_rate_tolerance(avcontext, (bitRate / + FFmpeg.avcodeccontext_set_bit_rate_tolerance(avctx, (bitRate / frameRate)); - FFmpeg.avcodeccontext_set_rc_max_rate(avcontext, bitRate); - FFmpeg.avcodeccontext_set_sample_aspect_ratio(avcontext, 0, 0); - FFmpeg.avcodeccontext_set_thread_count(avcontext, 1); + FFmpeg.avcodeccontext_set_rc_max_rate(avctx, bitRate); + FFmpeg.avcodeccontext_set_sample_aspect_ratio(avctx, 0, 0); + FFmpeg.avcodeccontext_set_thread_count(avctx, 1); // time_base should be 1 / frame rate - FFmpeg.avcodeccontext_set_time_base(avcontext, 1, frameRate); - FFmpeg.avcodeccontext_set_ticks_per_frame(avcontext, 2); - FFmpeg.avcodeccontext_set_quantizer(avcontext, 30, 31, 4); + FFmpeg.avcodeccontext_set_time_base(avctx, 1, frameRate); + FFmpeg.avcodeccontext_set_ticks_per_frame(avctx, 2); + FFmpeg.avcodeccontext_set_quantizer(avctx, 30, 31, 4); - // avcontext.chromaoffset = -2; + // avctx.chromaoffset = -2; - FFmpeg.avcodeccontext_add_partitions(avcontext, 0x111); + FFmpeg.avcodeccontext_add_partitions(avctx, 0x111); // X264_PART_I4X4 0x001 // X264_PART_P8X8 0x010 // X264_PART_B8X8 0x100 - FFmpeg.avcodeccontext_set_mb_decision(avcontext, + FFmpeg.avcodeccontext_set_mb_decision(avctx, 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_set_me_method(avcontext, 7); - FFmpeg.avcodeccontext_set_me_subpel_quality(avcontext, 2); - FFmpeg.avcodeccontext_set_me_range(avcontext, 16); - FFmpeg.avcodeccontext_set_me_cmp(avcontext, FFmpeg.FF_CMP_CHROMA); - FFmpeg.avcodeccontext_set_scenechange_threshold(avcontext, 40); + FFmpeg.avcodeccontext_set_rc_eq(avctx, "blurCplx^(1-qComp)"); + + FFmpeg.avcodeccontext_add_flags(avctx, + FFmpeg.CODEC_FLAG_LOOP_FILTER); + FFmpeg.avcodeccontext_add_flags2(avctx, + FFmpeg.CODEC_FLAG2_INTRA_REFRESH); + FFmpeg.avcodeccontext_set_me_method(avctx, 7); + FFmpeg.avcodeccontext_set_me_subpel_quality(avctx, 2); + FFmpeg.avcodeccontext_set_me_range(avctx, 16); + FFmpeg.avcodeccontext_set_me_cmp(avctx, FFmpeg.FF_CMP_CHROMA); + FFmpeg.avcodeccontext_set_scenechange_threshold(avctx, 40); // Constant quality mode (also known as constant ratefactor) - FFmpeg.avcodeccontext_set_crf(avcontext, 0); - FFmpeg.avcodeccontext_set_rc_buffer_size(avcontext, 10); - FFmpeg.avcodeccontext_set_gop_size(avcontext, IFRAME_INTERVAL); - FFmpeg.avcodeccontext_set_i_quant_factor(avcontext, 1f / 1.4f); + FFmpeg.avcodeccontext_set_crf(avctx, 0); + FFmpeg.avcodeccontext_set_rc_buffer_size(avctx, 10); + FFmpeg.avcodeccontext_set_gop_size(avctx, IFRAME_INTERVAL); + FFmpeg.avcodeccontext_set_i_quant_factor(avctx, 1f / 1.4f); - FFmpeg.avcodeccontext_set_refs(avcontext, 1); - //FFmpeg.avcodeccontext_set_trellis(avcontext, 2); + FFmpeg.avcodeccontext_set_refs(avctx, 1); + //FFmpeg.avcodeccontext_set_trellis(avctx, 2); - /* - * AVCodecContext's rtp_payload_size is supposed to try to provide an - * alternative to the "Fragmentation Units (FUs)" created by Packetizer - * but the former does not seem to be guaranteed and the video looks - * better at the time of this writing when only Packetizer takes care of - * ensuring MAX_PAYLOAD_SIZE. - */ - //FFmpeg.avcodeccontext_set_rtp_payload_size(avcontext, - // Packetizer.MAX_PAYLOAD_SIZE); + FFmpeg.avcodeccontext_set_keyint_min(avctx, 0); - if (FFmpeg.avcodec_open(avcontext, avcodec) < 0) + if (FFmpeg.avcodec_open(avctx, avcodec) < 0) { throw new ResourceUnavailableException( @@ -305,7 +349,6 @@ public synchronized void open() encFrameLen = (width * height * 3) / 2; rawFrameBuffer = FFmpeg.av_malloc(encFrameLen); - avframe = FFmpeg.avcodec_alloc_frame(); int sizeInBytes = width * height; @@ -389,7 +432,7 @@ public synchronized int process(Buffer inBuffer, Buffer outBuffer) // encode data int encLen = FFmpeg.avcodec_encode_video( - avcontext, + avctx, encFrameBuffer, encFrameLen, avframe); @@ -417,22 +460,6 @@ public synchronized int process(Buffer inBuffer, Buffer outBuffer) outBuffer.setLength(encLen); outBuffer.setOffset(0); outBuffer.setTimeStamp(inBuffer.getTimeStamp()); - -/* - // flags - int inFlags = inBuffer.getFlags(); - int outFlags = outBuffer.getFlags(); - - if ((inFlags & Buffer.FLAG_LIVE_DATA) != 0) - outFlags |= Buffer.FLAG_LIVE_DATA; - if ((inFlags & Buffer.FLAG_RELATIVE_TIME) != 0) - outFlags |= Buffer.FLAG_RELATIVE_TIME; - if ((inFlags & Buffer.FLAG_RTP_TIME) != 0) - outFlags |= Buffer.FLAG_RTP_TIME; - if ((inFlags & Buffer.FLAG_SYSTEM_TIME) != 0) - outFlags |= Buffer.FLAG_SYSTEM_TIME; - outBuffer.setFlags(outFlags); -*/ return BUFFER_PROCESSED_OK; } @@ -517,46 +544,24 @@ public Format setOutputFormat(Format out) : inSize; } + Map fmtps = null; + + if (out instanceof ParameterizedVideoFormat) + fmtps = ((ParameterizedVideoFormat) out).getFormatParameters(); + if (fmtps == null) + fmtps = new HashMap(); + fmtps.put(PACKETIZATION_MODE_FMTP, "1"); + outputFormat - = new VideoFormat( + = new ParameterizedVideoFormat( videoOut.getEncoding(), outSize, Format.NOT_SPECIFIED, Format.byteArray, - videoOut.getFrameRate()); + videoOut.getFrameRate(), + fmtps); // Return the selected outputFormat return outputFormat; } - - /** - * Event fired when RTCP feedback message is received. - * - * @param event RTCPFeedbackEvent - */ - public void feedbackReceived(RTCPFeedbackEvent event) - { - /* if RTCP message is a Picture Loss Indication (PLI) - * or a Full Intra-frame Request (FIR) the encoder will - * force the next frame to be a keyframe - */ - if(event.getPayloadType() == RTCPFeedbackEvent.PT_PS) - { - switch(event.getFeedbackMessageType()) - { - case RTCPFeedbackEvent.FMT_PLI: - case RTCPFeedbackEvent.FMT_FIR: - if(System.currentTimeMillis() > lastKeyframeRequestTime - + PLI_INTERVAL) - { - lastKeyframeRequestTime = System.currentTimeMillis(); - /* disable PLI */ - //forceKeyFrame = true; - } - break; - default: - break; - } - } - } } 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 a56f0f5a1..d1956a569 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 @@ -12,6 +12,7 @@ import javax.media.*; import javax.media.format.*; +import net.java.sip.communicator.impl.neomedia.format.*; import net.java.sip.communicator.impl.neomedia.codec.*; import net.sf.fmj.media.*; @@ -20,17 +21,11 @@ * 3984 "RTP Payload Format for H.264 Video". * * @author Damian Minkov - * @author Lubomir Marinov + * @author Lyubomir Marinov */ public class Packetizer extends AbstractPacketizer { - /** - * Array of default output formats. - */ - private static final Format[] DEFAULT_OUTPUT_FORMATS - = { new VideoFormat(Constants.H264_RTP) }; - /** * Maximum payload size without the headers. */ @@ -41,6 +36,20 @@ public class Packetizer */ private static final String PLUGIN_NAME = "H264 Packetizer"; + /** + * The Formats supported by Packetizer instances as + * output. + */ + static final Format[] SUPPORTED_OUTPUT_FORMATS + = { + new ParameterizedVideoFormat( + Constants.H264_RTP, + JNIEncoder.PACKETIZATION_MODE_FMTP, "0"), + new ParameterizedVideoFormat( + Constants.H264_RTP, + JNIEncoder.PACKETIZATION_MODE_FMTP, "1") + }; + /** * The list of NAL units to be sent as payload in RTP packets. */ @@ -64,7 +73,7 @@ public class Packetizer */ public Packetizer() { - inputFormats = new Format[] { new VideoFormat(Constants.H264) }; + inputFormats = JNIEncoder.SUPPORTED_OUTPUT_FORMATS; inputFormat = null; outputFormat = null; @@ -112,31 +121,37 @@ private static int ff_avc_find_startcode( } /** - * Get the matching output formats for a specific format. + * Gets the output formats matching a specific input format. * - * @param in input format - * @return array for formats matching input format + * @param input the input format to get the matching output formats for + * @return an array of output formats matching the specified input format */ - private Format[] getMatchingOutputFormats(Format in) + private Format[] getMatchingOutputFormats(Format input) { - VideoFormat videoIn = (VideoFormat) in; - Dimension inSize = videoIn.getSize(); + VideoFormat videoInput = (VideoFormat) input; + Dimension size = videoInput.getSize(); + float frameRate = videoInput.getFrameRate(); + String packetizationMode = getPacketizationMode(input); return - new VideoFormat[] + new Format[] { - new VideoFormat( + new ParameterizedVideoFormat( Constants.H264_RTP, - inSize, + size, Format.NOT_SPECIFIED, Format.byteArray, - videoIn.getFrameRate()) + frameRate, + ParameterizedVideoFormat.toMap( + JNIEncoder.PACKETIZATION_MODE_FMTP, + packetizationMode)) }; } + /** - * Get codec name. + * Gets the name of this PlugIn. * - * @return codec name + * @return the name of this PlugIn */ @Override public String getName() @@ -144,6 +159,28 @@ public String getName() return PLUGIN_NAME; } + /** + * Gets the value of the packetization-mode format parameter + * assigned by a specific Format. + * + * @param format the Format which assigns a value to the + * packetization-mode format parameter + * @return the value of the packetization-mode format parameter + * assigned by the specified format + */ + private String getPacketizationMode(Format format) + { + String packetizationMode = null; + + if (format instanceof ParameterizedVideoFormat) + packetizationMode + = ((ParameterizedVideoFormat) format).getFormatParameter( + JNIEncoder.PACKETIZATION_MODE_FMTP); + if (packetizationMode == null) + packetizationMode = "0"; + return packetizationMode; + } + /** * Return the list of formats supported at the output. * @param in input Format to determine corresponding output @@ -154,7 +191,7 @@ public Format[] getSupportedOutputFormats(Format in) { // null input format if (in == null) - return DEFAULT_OUTPUT_FORMATS; + return SUPPORTED_OUTPUT_FORMATS; // mismatch input format if (!(in instanceof VideoFormat) @@ -298,20 +335,6 @@ public int process(Buffer inBuffer, Buffer outBuffer) outBuffer.setTimeStamp(nalsTimeStamp); outBuffer.setSequenceNumber(sequenceNumber++); - // flags -// int inFlags = inBuffer.getFlags(); -// int outFlags = outBuffer.getFlags(); - -// if ((inFlags & Buffer.FLAG_LIVE_DATA) != 0) -// outFlags |= Buffer.FLAG_LIVE_DATA; -// if ((inFlags & Buffer.FLAG_RELATIVE_TIME) != 0) -// outFlags |= Buffer.FLAG_RELATIVE_TIME; -// if ((inFlags & Buffer.FLAG_RTP_TIME) != 0) -// outFlags |= Buffer.FLAG_RTP_TIME; -// if ((inFlags & Buffer.FLAG_SYSTEM_TIME) != 0) -// outFlags |= Buffer.FLAG_SYSTEM_TIME; -// outBuffer.setFlags(outFlags); - // If there are other NALs, send them as well. if(nals.size() > 0) return (BUFFER_PROCESSED_OK | INPUT_BUFFER_NOT_CONSUMED); @@ -319,7 +342,7 @@ public int process(Buffer inBuffer, Buffer outBuffer) { // It's the last NAL of the current frame so mark it. outBuffer.setFlags( - outBuffer.getFlags() | Buffer.FLAG_RTP_MARKER); + outBuffer.getFlags() | Buffer.FLAG_RTP_MARKER); return BUFFER_PROCESSED_OK; } } @@ -377,8 +400,7 @@ public int process(Buffer inBuffer, Buffer outBuffer) && ((nextBeginIndex = ff_avc_find_startcode( inData, - beginIndex, - endIndex)) + beginIndex, endIndex)) <= endIndex); beginIndex = nextBeginIndex + 3) { @@ -427,48 +449,60 @@ public Format setInputFormat(Format in) * Sets the Format in which this Codec is to output media * data. * - * @param out the Format in which this Codec is to + * @param output 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) + public Format setOutputFormat(Format output) { /* * Return null if the specified output Format is incompatible with this * Packetizer. */ - if (!(out instanceof VideoFormat) + if (!(output instanceof VideoFormat) || (null == AbstractCodecExt.matches( - out, + output, getMatchingOutputFormats(inputFormat)))) return null; - VideoFormat videoOut = (VideoFormat) out; - Dimension outSize = videoOut.getSize(); + VideoFormat videoOutput = (VideoFormat) output; + Dimension outputSize = videoOutput.getSize(); - if (outSize == null) + if (outputSize == null) { - Dimension inSize = ((VideoFormat) inputFormat).getSize(); + Dimension inputSize = ((VideoFormat) inputFormat).getSize(); - outSize - = (inSize == null) + outputSize + = (inputSize == null) ? new Dimension( Constants.VIDEO_WIDTH, Constants.VIDEO_HEIGHT) - : inSize; + : inputSize; } + Map fmtps = null; + + if (output instanceof ParameterizedVideoFormat) + fmtps = ((ParameterizedVideoFormat) output).getFormatParameters(); + if (fmtps == null) + fmtps = new HashMap(); + if (fmtps.get(JNIEncoder.PACKETIZATION_MODE_FMTP) == null) + fmtps.put( + JNIEncoder.PACKETIZATION_MODE_FMTP, + getPacketizationMode(inputFormat)); + outputFormat - = new VideoFormat( - videoOut.getEncoding(), - outSize, - outSize.width * outSize.height, + = new ParameterizedVideoFormat( + videoOutput.getEncoding(), + outputSize, + outputSize.width * outputSize.height, Format.byteArray, - videoOut.getFrameRate()); + videoOutput.getFrameRate(), + fmtps); // Return the outputFormat which is actually set. return outputFormat; diff --git a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceImpl.java b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceImpl.java index 0ac243866..1908ac076 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceImpl.java @@ -134,8 +134,8 @@ CaptureDevice createCaptureDevice() { if(captureDevice instanceof AbstractPullBufferCaptureDevice) { - ((AbstractPullBufferCaptureDevice)captureDevice). - setCaptureDeviceInfo(captureDeviceInfo); + ((AbstractPullBufferCaptureDevice)captureDevice) + .setCaptureDeviceInfo(captureDeviceInfo); } // Try to enable tracing on captureDevice. 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 d466ddd50..d1e6adfb5 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/MediaDeviceSession.java @@ -28,7 +28,7 @@ * Represents the use of a specific MediaDevice by a * MediaStream. * - * @author Lubomir Marinov + * @author Lyubomir Marinov * @author Damian Minkov * @author Emil Ivov */ @@ -88,7 +88,7 @@ public class MediaDeviceSession * {@link #setFormat(MediaFormat)} and to be set as the output format of * {@link #processor}. */ - protected Format format; + private MediaFormatImpl format; /** * The indicator which determines whether this MediaDeviceSession @@ -738,7 +738,7 @@ public Format getProcessorFormat() ? MediaType.VIDEO : MediaType.AUDIO; - if(mediaType.equals(type)) + if (mediaType.equals(type)) return jmfFormat; } } @@ -752,20 +752,28 @@ public Format getProcessorFormat() * @return the MediaFormat in which this instance captures media * from its associated MediaDevice */ - public MediaFormat getFormat() + public MediaFormatImpl getFormat() { - // if processor is not present we will not query for format - // and return the locally stored one. - Format jmfFormat = (processor == null) ? - this.format : getProcessorFormat(); - - if(jmfFormat != null) + /* + * If the Format of the processor is different than the format of this + * MediaDeviceSession, we'll likely run into unexpected issues so debug + * whether there are such cases. + */ + if (logger.isDebugEnabled() && (processor != null)) { - MediaFormat format - = MediaFormatImpl.createInstance(jmfFormat); - return format; + Format processorFormat = getProcessorFormat(); + Format format + = (this.format == null) ? null : this.format.getFormat(); + boolean processorFormatEqualsFormat + = (processorFormat == null) + ? (format == null) + : processorFormat.equals(format); + + if (!processorFormatEqualsFormat) + logger.debug("processorFormat != format"); } - return null; + + return format; } /** @@ -1277,9 +1285,7 @@ public void setContentDescriptor(ContentDescriptor contentDescriptor) */ public void setFormat(MediaFormat format) { - MediaType mediaType = getMediaType(); - - if (!mediaType.equals(format.getMediaType())) + if (!getMediaType().equals(format.getMediaType())) throw new IllegalArgumentException("format"); /* @@ -1290,7 +1296,14 @@ public void setFormat(MediaFormat format) MediaFormatImpl mediaFormatImpl = (MediaFormatImpl) format; - this.format = mediaFormatImpl.getFormat(); + this.format = mediaFormatImpl; + if (logger.isTraceEnabled()) + { + logger.trace( + "Set format " + this.format + + " on " + + getClass().getSimpleName() + " " + hashCode()); + } /* * If the processor is after Configured, setting a different format will @@ -1305,7 +1318,8 @@ public void setFormat(MediaFormat format) setProcessorFormat(processor, this.format); else if (processorIsPrematurelyClosed || ((processorState > Processor.Configured) - && !this.format.equals(getProcessorFormat()))) + && !this.format.getFormat().equals( + getProcessorFormat()))) { setProcessor(null); } @@ -1313,17 +1327,21 @@ else if (processorIsPrematurelyClosed } /** - * Sets the JMF Format in which a specific Processor + * Sets the MediaFormatImpl in which a specific Processor * producing media to be streamed to the remote peer is to output. * - * @param processor the Processor to set the output Format - * of - * @param format the JMF Format to set to processor - */ - protected void setProcessorFormat(Processor processor, Format format) + * @param processor the Processor to set the output + * MediaFormatImpl of + * @param mediaFormat the MediaFormatImpl to set on + * processor + */ + protected void setProcessorFormat( + Processor processor, + MediaFormatImpl mediaFormat) { TrackControl[] trackControls = processor.getTrackControls(); MediaType mediaType = getMediaType(); + Format format = mediaFormat.getFormat(); for (int trackIndex = 0; trackIndex < trackControls.length; @@ -1393,7 +1411,9 @@ protected void setProcessorFormat(Processor processor, Format format) else if (!supportedFormat.equals(trackControl.getFormat())) { Format setFormat - = setProcessorFormat(trackControl, supportedFormat); + = setProcessorFormat( + trackControl, + mediaFormat, supportedFormat); if (setFormat == null) logger @@ -1427,16 +1447,26 @@ else if (logger.isTraceEnabled()) } /** - * Sets the JMF Format of a specific TrackControl of the - * Processor which produces the media to be streamed by this + * Sets the MediaFormatImpl of a specific TrackControl of + * the Processor which produces the media to be streamed by this * MediaDeviceSession to the remote peer. Allows extenders to * override the set procedure and to detect when the JMF Format of * the specified TrackControl changes. * * @param trackControl the TrackControl to set the JMF * Format of + * @param mediaFormat the MediaFormatImpl to be set on the + * specified TrackControl. Though mediaFormat encapsulates + * a JMF Format, format is to be set on the specified + * trackControl because it may be more specific. In any case, the + * two JMF Formats match. The MediaFormatImpl is provided + * anyway because it carries additional information such as format + * parameters. * @param format the JMF Format to be set on the specified - * TrackControl + * TrackControl. Though mediaFormat encapsulates a JMF + * Format, the specified format is to be set on the + * specified trackControl because it may be more specific than the + * JMF Format of the mediaFormat * @return the JMF Format set on TrackControl after the * attempt to set the specified format or null if the * specified format was found to be incompatible with @@ -1444,6 +1474,7 @@ else if (logger.isTraceEnabled()) */ protected Format setProcessorFormat( TrackControl trackControl, + MediaFormatImpl mediaFormat, Format format) { return trackControl.setFormat(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 7d21f045a..b182e4187 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java @@ -18,8 +18,8 @@ import net.java.sip.communicator.impl.neomedia.*; import net.java.sip.communicator.impl.neomedia.codec.video.*; import net.java.sip.communicator.impl.neomedia.codec.video.h264.*; +import net.java.sip.communicator.impl.neomedia.format.*; import net.java.sip.communicator.impl.neomedia.transform.*; -import net.java.sip.communicator.impl.neomedia.videoflip.*; import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.neomedia.event.*; import net.java.sip.communicator.service.resources.*; @@ -34,6 +34,7 @@ public class VideoMediaDeviceSession extends MediaDeviceSession { + /** * The Logger used by the VideoMediaDeviceSession class * and its instances for logging output. @@ -188,7 +189,6 @@ protected DataSource createCaptureDevice() if ((maxSupportedFrameRate > 0) && (frameRate > maxSupportedFrameRate)) frameRate = maxSupportedFrameRate; - if(frameRate > 0) frameRateControl.setFrameRate(frameRate); } @@ -421,11 +421,8 @@ private void controllerUpdateForCreateLocalVisualComponent( { for (TrackControl trackControl : trackControls) { - VideoFlipEffect flipEffect = new VideoFlipEffect(); - SwScaler scaler = new SwScaler(); - trackControl.setCodecChain( - new Codec[] {flipEffect, scaler}); + new Codec[] { new HFlip(), new SwScaler() }); break; } } @@ -788,31 +785,33 @@ protected void playerConfigureComplete(final Processor player) * 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) + if (usePLI + && "h264/rtp".equalsIgnoreCase( + getFormat().getJMFEncoding())) { - DePacketizer depack = new DePacketizer(); - - depack.setRtcpFeedbackPLI(usePLI); + DePacketizer depacketizer = new DePacketizer(); + depacketizer.setRtcpFeedbackPLI(usePLI); try { - depack.setConnector(rtpConnector. - getControlOutputStream()); + depacketizer.setConnector( + rtpConnector.getControlOutputStream()); } catch(Exception e) { - logger.error("Error cannot get RTCP output stream", + logger.error( + "Error cannot get RTCP output stream", e); } + depacketizer.setSSRC(localSSRC, remoteSSRC); - depack.setSSRC(localSSRC, remoteSSRC); - - trackControl.setCodecChain(new Codec[] { - depack, playerScaler}); + trackControl.setCodecChain( + new Codec[] { depacketizer, playerScaler }); } else { - trackControl.setCodecChain(new Codec[] {playerScaler}); + trackControl.setCodecChain( + new Codec[] { playerScaler }); } break; } @@ -1123,28 +1122,31 @@ public void setRemoteSSRC(long remoteSSRC) } /** - * Sets the JMF Format in which a specific Processor + * Sets the MediaFormatImpl in which a specific Processor * producing media to be streamed to the remote peer is to output. * - * @param processor the Processor to set the output Format - * of - * @param format the JMF Format to set to processor - * @see MediaDeviceSession#setProcessorFormat(Processor, Format) + * @param processor the Processor to set the output + * MediaFormatImpl of + * @param mediaFormat the MediaFormatImpl to set on + * processor + * @see MediaDeviceSession#setProcessorFormat(Processor, MediaFormatImpl) */ @Override - protected void setProcessorFormat(Processor processor, Format format) + protected void setProcessorFormat( + Processor processor, + MediaFormatImpl mediaFormat) { - if(format.getEncoding().equals("h263-1998/rtp")) + Format format = mediaFormat.getFormat(); + + if ("h263-1998/rtp".equalsIgnoreCase(format.getEncoding())) { - /* 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 no output size has been defined, then no SDP fmtp has been + * found with QCIF, CIF, VGA or CUSTOM parameters. Let's default to + * QCIF (176x144). */ - if(outputSize == null) - { + if (outputSize == null) outputSize = new Dimension(176, 144); - } } /* @@ -1152,7 +1154,7 @@ protected void setProcessorFormat(Processor processor, Format format) * recreate the object. Also check whether capture device can output * such a size. */ - if((outputSize != null) + if ((outputSize != null) && (outputSize.width > 0) && (outputSize.height > 0)) { @@ -1179,39 +1181,52 @@ protected void setProcessorFormat(Processor processor, Format format) else outputSize = null; - super.setProcessorFormat(processor, format); + super.setProcessorFormat(processor, mediaFormat); } /** - * Sets the JMF Format of a specific TrackControl of the - * Processor which produces the media to be streamed by this + * Sets the MediaFormatImpl of a specific TrackControl of + * the Processor which produces the media to be streamed by this * MediaDeviceSession to the remote peer. Allows extenders to * override the set procedure and to detect when the JMF Format of * the specified TrackControl changes. * * @param trackControl the TrackControl to set the JMF * Format of + * @param mediaFormat the MediaFormatImpl to be set on the + * specified TrackControl. Though mediaFormat encapsulates + * a JMF Format, format is to be set on the specified + * trackControl because it may be more specific. In any case, the + * two JMF Formats match. The MediaFormatImpl is provided + * anyway because it carries additional information such as format + * parameters. * @param format the JMF Format to be set on the specified - * TrackControl + * TrackControl. Though mediaFormat encapsulates a JMF + * Format, the specified format is to be set on the + * specified trackControl because it may be more specific than the + * JMF Format of the mediaFormat * @return the JMF Format set on TrackControl after the - * attempt to set the specified format or null if the + * attempt to set the specified mediaFormat or null if the * specified format was found to be incompatible with * trackControl - * @see MediaDeviceSession#setProcessorFormat(TrackControl, Format) + * @see MediaDeviceSession#setProcessorFormat(TrackControl, MediaFormatImpl, + * Format) */ @Override protected Format setProcessorFormat( TrackControl trackControl, + MediaFormatImpl mediaFormat, Format format) { JNIEncoder encoder = null; SwScaler scaler = null; int codecCount = 0; - /* For H.264 we will monitor RTCP feedback. For example, if we receive a + /* + * For H.264 we will monitor RTCP feedback. For example, if we receive a * PLI/FIR message, we will send a keyframe. */ - if(format.getEncoding().equals("h264/rtp") && usePLI) + if (usePLI && "h264/rtp".equalsIgnoreCase(format.getEncoding())) { encoder = new JNIEncoder(); @@ -1222,14 +1237,15 @@ protected Format setProcessorFormat( rtpConnector.getControlInputStream()) .addRTCPFeedbackListener(encoder); } - catch(Exception e) + catch (Exception e) { logger.error("Error cannot get RTCP input stream", e); } + codecCount++; } - if(outputSize != null) + if (outputSize != null) { /* We have been explicitly told to use a specified output size so * create a custom SwScaler that will scale and convert color spaces @@ -1265,7 +1281,7 @@ protected Format setProcessorFormat( } } - return super.setProcessorFormat(trackControl, format); + return super.setProcessorFormat(trackControl, mediaFormat, format); } /** 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 845eb16d9..c267bc879 100644 --- a/src/net/java/sip/communicator/impl/neomedia/format/MediaFormatFactoryImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/format/MediaFormatFactoryImpl.java @@ -12,23 +12,25 @@ import net.java.sip.communicator.impl.neomedia.*; import net.java.sip.communicator.impl.neomedia.codec.*; -import net.java.sip.communicator.service.neomedia.MediaType; +import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.neomedia.format.*; import net.java.sip.communicator.util.*; /** * Implements MediaFormatFactory for the JMF Format types. * - * @author Lubomir Marinov + * @author Lyubomir Marinov */ public class MediaFormatFactoryImpl implements MediaFormatFactory { /** - * Our class logger. + * The Logger used by the MediaFormatFactoryImpl class and + * its instances for logging output. */ private static final Logger logger = Logger.getLogger(MediaFormatFactoryImpl.class); + /** * Creates an unknown MediaFormat. * @@ -39,14 +41,14 @@ public MediaFormat createUnknownMediaFormat(MediaType type) { Format unknown = null; + /* + * FIXME Why is a VideoFormat instance created for MediaType.AUDIO and + * an AudioFormat instance for MediaType.VIDEO? + */ if(type.equals(MediaType.AUDIO)) - { unknown = new VideoFormat("unknown"); - } else if(type.equals(MediaType.VIDEO)) - { unknown = new AudioFormat("unknown"); - } return MediaFormatImpl.createInstance(unknown); } @@ -88,11 +90,10 @@ public MediaFormat createMediaFormat(String encoding) */ public MediaFormat createMediaFormat(byte rtpPayloadType) { - /* - * We know which are the MediaFormat instance with the specified + * We know which are the MediaFormat instances with the specified * rtpPayloadType but we cannot directly return them because they do not - * reflect the user configuration with respect to being enabled and + * reflect the user's configuration with respect to being enabled and * disabled. */ for (MediaFormat rtpPayloadTypeMediaFormat @@ -126,9 +127,7 @@ public MediaFormat createMediaFormat(byte rtpPayloadType) * MediaFormatFactory; otherwise, null * @see MediaFormatFactory#createMediaFormat(String, double) */ - public MediaFormat createMediaFormat( - String encoding, - double clockRate) + public MediaFormat createMediaFormat(String encoding, double clockRate) { return createMediaFormat(encoding, clockRate, 1); } @@ -145,7 +144,7 @@ public MediaFormat createMediaFormat( * MediaFormat for * @param clockRate the clock rate in Hz to create a MediaFormat * for - * @param channels the number of availabe channels (1 for mono, 2 for + * @param channels the number of available channels (1 for mono, 2 for * stereo) if it makes sense for the MediaFormat with the specified * encoding; otherwise, ignored * @return a MediaFormat with the specified encoding, @@ -156,22 +155,30 @@ public MediaFormat createMediaFormat( * @see MediaFormatFactory#createMediaFormat(String, double, int) */ public MediaFormat createMediaFormat( - String encoding, - double clockRate, - int channels) + String encoding, double clockRate, int channels) { - for (MediaFormat format : getSupportedMediaFormats(encoding, clockRate)) - if ((CHANNELS_NOT_SPECIFIED != channels) - && (format instanceof AudioMediaFormat)) - { - AudioMediaFormat audioFormat = (AudioMediaFormat) format; - int audioFormatChannels = audioFormat.getChannels(); + return createMediaFormat(encoding, clockRate, channels, null); + } - if ((CHANNELS_NOT_SPECIFIED == audioFormatChannels) - || (audioFormatChannels == channels)) - return audioFormat; - } else + private MediaFormat createMediaFormat( + String encoding, double clockRate, int channels, + Map fmtps) + { + for (MediaFormat format : getSupportedMediaFormats(encoding, clockRate)) + { + /* + * The mediaType, encoding and clockRate properties are sure to + * match because format is the result of the search for encoding and + * clockRate. We just want to make sure that the channels and the + * format parameters match. + */ + if (AbstractMediaStream.matches( + format, + format.getMediaType(), + format.getEncoding(), format.getClockRate(), channels, + fmtps)) return format; + } return null; } @@ -195,13 +202,16 @@ public MediaFormat createMediaFormat( * otherwise, null * @see MediaFormatFactory#createMediaFormat(String, double, Map, Map) */ - public MediaFormat createMediaFormat( String encoding, - double clockRate, - Map formatParams, - Map advancedParams) + public MediaFormat createMediaFormat( + String encoding, double clockRate, + Map formatParams, + Map advancedParams) { - return createMediaFormat( encoding, clockRate, 1, formatParams, - advancedParams); + return + createMediaFormat( + encoding, clockRate, 1, + formatParams, + advancedParams); } /** @@ -215,7 +225,7 @@ public MediaFormat createMediaFormat( String encoding, * MediaFormat for * @param clockRate the clock rate in Hz to create a MediaFormat * for - * @param channels the number of availabe channels (1 for mono, 2 for + * @param channels the number of available channels (1 for mono, 2 for * stereo) if it makes sense for the MediaFormat with the specified * encoding; otherwise, ignored * @param formatParams any codec specific parameters which have been @@ -229,42 +239,31 @@ public MediaFormat createMediaFormat( String encoding, * MediaFormatFactory; otherwise, null * @see MediaFormatFactory#createMediaFormat(String, double, int, Map, Map) */ - public MediaFormat createMediaFormat(String encoding, - double clockRate, - int channels, - Map formatParams, - Map advancedParams) + public MediaFormat createMediaFormat( + String encoding, double clockRate, int channels, + Map formatParams, + Map advancedParams) { MediaFormat mediaFormat - = createMediaFormat(encoding, clockRate, channels); - Map formatParameters - = new HashMap(); - Map advancedParameters - = new HashMap(); - boolean recreate = false; + = createMediaFormat(encoding, clockRate, channels, formatParams); - if ((mediaFormat != null) - && (formatParams != null) - && !formatParams.isEmpty()) - { - recreate = true; - - //formatParameters.putAll(mediaFormat.getFormatParameters()); - formatParameters.putAll(formatParams); - } + if (mediaFormat == null) + return null; - if(mediaFormat != null && (advancedParams != null ) && - !advancedParams.isEmpty()) - { - recreate = true; + /* + * MediaFormatImpl is immutable so if the caller wants to change the + * format parameters and/or the advanced attributes, we'll have to + * create a new MediaFormatImpl. + */ + Map formatParameters = null; + Map advancedParameters = null; - if(advancedParams != null) - { - advancedParameters.putAll(advancedParams); - } - } + if ((formatParams != null) && !formatParams.isEmpty()) + formatParameters = formatParams; + if ((advancedParams != null ) && !advancedParams.isEmpty()) + advancedParameters = advancedParams; - if(recreate) + if ((formatParameters != null) || (advancedParameters != null)) { switch (mediaFormat.getMediaType()) { @@ -278,12 +277,23 @@ public MediaFormat createMediaFormat(String encoding, VideoMediaFormatImpl videoMediaFormatImpl = (VideoMediaFormatImpl) mediaFormat; + /* + * If the format of VideoMediaFormatImpl is + * a ParameterizedVideoFormat, it's possible for the format + * parameters of that ParameterizedVideoFormat and of the new + * VideoMediaFormatImpl (to be created) to be out of sync. While + * it's not technically perfect, it should be practically safe + * for the format parameters which distinguish VideoFormats with + * the same encoding and clock rate because mediaFormat has + * already been created in sync with formatParams (with respect + * to the format parameters which distinguish VideoFormats with + * the same encoding and clock rate). + */ mediaFormat = new VideoMediaFormatImpl( videoMediaFormatImpl.getFormat(), videoMediaFormatImpl.getClockRate(), - formatParameters, - advancedParameters); + formatParameters, advancedParameters); break; default: mediaFormat = null; @@ -312,7 +322,7 @@ public MediaFormat createMediaFormat(String encoding, * MediaFormat for; null * @param clockRate the clock rate in Hz to create a MediaFormat * for - * @param channels the number of availabe channels (1 for mono, 2 for + * @param channels the number of available channels (1 for mono, 2 for * stereo) if it makes sense for the MediaFormat with the specified * encoding; otherwise, ignored * @param formatParams any codec specific parameters which have been @@ -327,9 +337,7 @@ public MediaFormat createMediaFormat(String encoding, */ public MediaFormat createMediaFormat( byte rtpPayloadType, - String encoding, - double clockRate, - int channels, + String encoding, double clockRate, int channels, Map formatParams, Map advancedParams) { @@ -374,8 +382,11 @@ public MediaFormat createMediaFormat( } } - return createMediaFormat(encoding, clockRate, channels, formatParams, - advancedParams); + return + createMediaFormat( + encoding, clockRate, channels, + formatParams, + advancedParams); } /** @@ -397,28 +408,37 @@ public MediaFormat createMediaFormat( */ private List getMatchingMediaFormats( MediaFormat[] mediaFormats, - String encoding, - double clockRate) + String encoding, double clockRate) { - // As per RFC 3551.4.5.2, because of a mistake in RFC 1890 and for - // b-ward compatibility, G.722 should always be announced as 8000 even - // though it is wideband. So, if someone is looking for G722/16000 - // then: Forgive them, for they know not what they do! - if("G722".equals(encoding) && clockRate == 16000) + /* + * XXX Use String#equalsIgnoreCase(String) because some clients transmit + * some of the codecs starting with capital letters. + */ + + /* + * As per RFC 3551.4.5.2, because of a mistake in RFC 1890 and for + * backward compatibility, G.722 should always be announced as 8000 even + * though it is wideband. So, if someone is looking for G722/16000, + * then: Forgive them, for they know not what they do! + */ + if("G722".equalsIgnoreCase(encoding) && (16000 == clockRate)) { - logger.info("Suppressing erroneous 16000 announcement for G.722"); clockRate = 8000; + if (logger.isInfoEnabled()) + logger.info("Suppressing erroneous 16000 announcement for G.722"); } List supportedMediaFormats = new ArrayList(); - // uses equalsIgnoreCase, as some clients transmit some of the codecs - // starting with capital letters for (MediaFormat mediaFormat : mediaFormats) + { if (mediaFormat.getEncoding().equalsIgnoreCase(encoding) && ((CLOCK_RATE_NOT_SPECIFIED == clockRate) || (mediaFormat.getClockRate() == clockRate))) + { supportedMediaFormats.add(mediaFormat); + } + } return supportedMediaFormats; } @@ -438,24 +458,23 @@ private List getMatchingMediaFormats( * and having the specified encoding and, optionally, clock rate */ private List getSupportedMediaFormats( - String encoding, - double clockRate) + String encoding, double clockRate) { EncodingConfiguration encodingConfiguration - = NeomediaActivator - .getMediaServiceImpl().getEncodingConfiguration(); + = NeomediaActivator.getMediaServiceImpl() + .getEncodingConfiguration(); List supportedMediaFormats = getMatchingMediaFormats( - encodingConfiguration - .getSupportedEncodings(MediaType.AUDIO), + encodingConfiguration.getSupportedEncodings( + MediaType.AUDIO), encoding, clockRate); if (supportedMediaFormats.isEmpty()) supportedMediaFormats = getMatchingMediaFormats( - encodingConfiguration - .getSupportedEncodings(MediaType.VIDEO), + encodingConfiguration.getSupportedEncodings( + MediaType.VIDEO), encoding, clockRate); return supportedMediaFormats; diff --git a/src/net/java/sip/communicator/impl/neomedia/format/MediaFormatImpl.java b/src/net/java/sip/communicator/impl/neomedia/format/MediaFormatImpl.java index 790e747b9..460d449be 100644 --- a/src/net/java/sip/communicator/impl/neomedia/format/MediaFormatImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/format/MediaFormatImpl.java @@ -20,7 +20,7 @@ * * @param the type of the wrapped Format * - * @author Lubomir Marinov + * @author Lyubomir Marinov */ public abstract class MediaFormatImpl implements MediaFormat @@ -32,7 +32,7 @@ public abstract class MediaFormatImpl * received via SIP/SDP or XMPP/Jingle. Explicitly defined in order to * reduce unnecessary allocations. */ - private static final Map EMPTY_FORMAT_PARAMETERS + static final Map EMPTY_FORMAT_PARAMETERS = Collections.emptyMap(); /** @@ -112,38 +112,72 @@ public static MediaFormatImpl createInstance( * number of parameters and assign them equal values. Since the values are * Strings, presumes that a value of null is equal to the * empty String. + *

+ * The two Map instances of format parameters to be checked for + * equality are presumed to be modifiable in the sense that if the lack of a + * format parameter in a given Map is equivalent to it having a + * specific value, an association of the format parameter to the value in + * question may be added to or removed from the respective Map + * instance for the purposes of determining equality. + *

* - * @param formatParameters1 the first set of format parameters to be tested - * for equality - * @param formatParameters2 the second set of format parameters to be tested - * for equality + * @param fmtps1 the first set of format parameters to be tested for + * equality + * @param fmtps2 the second set of format parameters to be tested for + * equality * @return true if the specified sets of format parameters are * equal; false, otherwise */ - static boolean formatParametersAreEqual( - Map formatParameters1, - Map formatParameters2) + protected boolean formatParametersAreEqual( + Map fmtps1, + Map fmtps2) { - if (formatParameters1 == null) - return - (formatParameters2 == null) - || formatParameters2.isEmpty(); - if (formatParameters2 == null) - return - (formatParameters1 == null) - || formatParameters1.isEmpty(); - if (formatParameters1.size() == formatParameters2.size()) + return formatParametersAreEqual(getEncoding(), fmtps1, fmtps2); + } + + /** + * Determines whether a specific set of format parameters is equal to + * another set of format parameters in the sense that they define an equal + * number of parameters and assign them equal values. Since the values are + * Strings, presumes that a value of null is equal to the + * empty String. + *

+ * The two Map instances of format parameters to be checked for + * equality are presumed to be modifiable in the sense that if the lack of a + * format parameter in a given Map is equivalent to it having a + * specific value, an association of the format parameter to the value in + * question may be added to or removed from the respective Map + * instance for the purposes of determining equality. + *

+ * + * @param encoding the encoding (name) related to the two sets of format + * parameters to be tested for equality + * @param fmtps1 the first set of format parameters to be tested for + * equality + * @param fmtps2 the second set of format parameters to be tested for + * equality + * @return true if the specified sets of format parameters are + * equal; false, otherwise + */ + public static boolean formatParametersAreEqual( + String encoding, + Map fmtps1, Map fmtps2) + { + if (fmtps1 == null) + return (fmtps2 == null) || fmtps2.isEmpty(); + if (fmtps2 == null) + return (fmtps1 == null) || fmtps1.isEmpty(); + if (fmtps1.size() == fmtps2.size()) { - for (Map.Entry formatParameter1 - : formatParameters1.entrySet()) + for (Map.Entry fmtp1 : fmtps1.entrySet()) { - String key1 = formatParameter1.getKey(); + String key1 = fmtp1.getKey(); - if (!formatParameters2.containsKey(key1)) + if (!fmtps2.containsKey(key1)) return false; - String value1 = formatParameter1.getValue(); - String value2 = formatParameters2.get(key1); + String value1 = fmtp1.getValue(); + String value2 = fmtps2.get(key1); /* * Since the values are strings, allow null to be equal to the @@ -163,6 +197,19 @@ else if (!value1.equals(value2)) return false; } + /** + * {@inheritDoc} + *

+ * The default implementation of MediaFormatImpl always returns + * true because format parameters in general do not cause the + * distinction of payload types. + *

+ */ + public boolean formatParametersMatch(Map fmtps) + { + return true; + } + /** * The JMF Format this instance wraps and provides an * implementation of MediaFormat for. @@ -205,7 +252,9 @@ protected MediaFormatImpl(T format) * @param advancedParameters any parameters that have been * received via SIP/SDP or XMPP/Jingle */ - protected MediaFormatImpl(T format, Map formatParameters, + protected MediaFormatImpl( + T format, + Map formatParameters, Map advancedParameters) { if (format == null) @@ -372,7 +421,7 @@ public int hashCode() public String getClockRateString() { double clockRate = getClockRate(); - long clockRateL = (long)clockRate; + long clockRateL = (long) clockRate; if (clockRateL == clockRate) return Long.toString(clockRateL); diff --git a/src/net/java/sip/communicator/impl/neomedia/format/ParameterizedVideoFormat.java b/src/net/java/sip/communicator/impl/neomedia/format/ParameterizedVideoFormat.java new file mode 100644 index 000000000..be4e04766 --- /dev/null +++ b/src/net/java/sip/communicator/impl/neomedia/format/ParameterizedVideoFormat.java @@ -0,0 +1,240 @@ +/* + * 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.format; + +import java.awt.*; +import java.util.*; + +import javax.media.*; +import javax.media.format.*; + +/** + * Implements a VideoFormat with format parameters (like + * {@link VideoMediaFormatImpl}) (some of) which (could) distinguish payload + * types. + * + * @author Lyubomir Marinov + */ +public class ParameterizedVideoFormat + extends VideoFormat +{ + /** + * The format parameters of this ParameterizedVideoFormat instance. + */ + private Map fmtps; + + public ParameterizedVideoFormat( + String encoding, + Dimension size, + int maxDataLength, + Class dataType, + float frameRate, + Map fmtps) + { + super(encoding, size, maxDataLength, dataType, frameRate); + + this.fmtps + = ((fmtps == null) || fmtps.isEmpty()) + ? MediaFormatImpl.EMPTY_FORMAT_PARAMETERS + : new HashMap(fmtps); + } + + /** + * Initializes a new ParameterizedVideoFormat with a specific + * encoding and a specific set of format parameters. + * + * @param encoding the encoding of the new instance + * @param fmtps the format parameters of the new instance + */ + public ParameterizedVideoFormat(String encoding, Map fmtps) + { + super(encoding); + + this.fmtps + = ((fmtps == null) || fmtps.isEmpty()) + ? MediaFormatImpl.EMPTY_FORMAT_PARAMETERS + : new HashMap(fmtps); + } + + /** + * Initializes a new ParameterizedVideoFormat with a specific + * encoding and a specific set of format parameters. + * + * @param encoding the encoding of the new instance + * @param fmtps the format parameters of the new instance in the form of an + * array of Strings in which the key and the value of an + * association are expressed as consecutive elements. + */ + public ParameterizedVideoFormat(String encoding, String... fmtps) + { + this(encoding, toMap(fmtps)); + } + + /** + * Initializes a new ParameterizedVideoFormat instance which has + * the same properties as this instance. + * + * @return a new ParameterizedVideoFormat instance which has the + * same properties as this instance + */ + @Override + public Object clone() + { + ParameterizedVideoFormat f + = new ParameterizedVideoFormat( + getEncoding(), + getSize(), + getMaxDataLength(), + getDataType(), + getFrameRate(), + /* + * The formatParameters will be copied by + * ParameterizedVideoFormat#copy(Format) bellow. + */ + null); + + f.copy(this); + return f; + } + + /** + * Copies the properties of the specified Format into this + * instance. + * + * @param f the Format the properties of which are to be copied + * into this instance + */ + @Override + protected void copy(Format f) + { + super.copy(f); + + if (f instanceof ParameterizedVideoFormat) + { + ParameterizedVideoFormat pvf = (ParameterizedVideoFormat) f; + Map pvfFmtps = pvf.getFormatParameters(); + + fmtps + = ((pvfFmtps == null) || pvfFmtps.isEmpty()) + ? MediaFormatImpl.EMPTY_FORMAT_PARAMETERS + : new HashMap(pvfFmtps); + } + } + + /** + * Determines whether a specific Object represents a value that is + * equal to the value represented by this instance. + * + * @param obj the Object to be determined whether it represents a + * value that is equal to the value represented by this instance + * @return true if the specified obj represents a value + * that is equal to the value represented by this instance; otherwise, + * false + */ + @Override + public boolean equals(Object obj) + { + if (!super.equals(obj)) + return false; + + Map objFmtps = null; + + if (obj instanceof ParameterizedVideoFormat) + objFmtps = ((ParameterizedVideoFormat) obj).getFormatParameters(); + return + VideoMediaFormatImpl.formatParametersAreEqual( + getEncoding(), + getFormatParameters(), objFmtps); + } + + public String getFormatParameter(String name) + { + return fmtps.get(name); + } + + public Map getFormatParameters() + { + return new HashMap(fmtps); + } + + /** + * Finds the attributes shared by two matching Formats. If the + * specified Format does not match this one, the result is + * undefined. + * + * @param format the matching Format to intersect with this one + * @return a Format with its attributes set to the attributes + * common to this instance and the specified format + */ + @Override + public Format intersects(Format format) + { + Format intersection = super.intersects(format); + + if (intersection == null) + return null; + + ((ParameterizedVideoFormat) intersection).fmtps + = fmtps.isEmpty() + ? MediaFormatImpl.EMPTY_FORMAT_PARAMETERS + : getFormatParameters(); + return intersection; + } + + /** + * Determines whether a specific format matches this instance i.e. whether + * their attributes match according to the definition of "match" given by + * {@link Format#matches(Format)}. + * + * @param format the Format to compare to this instance + * @return true if the specified format matches this one; + * otherwise, false + */ + @Override + public boolean matches(Format format) + { + if (!super.matches(format)) + return false; + + Map formatFmtps = null; + + if (format instanceof ParameterizedVideoFormat) + formatFmtps + = ((ParameterizedVideoFormat) format).getFormatParameters(); + return + VideoMediaFormatImpl.formatParametersMatch( + getEncoding(), + getFormatParameters(), formatFmtps); + } + + /** + * Initializes a new Map from an array in which the key and the + * value of an association are expressed as consecutive elements. + * + * @param the very type of the keys and the values to be associated in + * the new Map + * @param entries the associations to be created in the new Map + * where the key and value of an association are expressed as consecutive + * elements + * @return a new Map with the associations specified by + * entries + */ + public static Map toMap(T... entries) + { + Map map; + + if ((entries == null) || (entries.length == 0)) + map = null; + else + { + map = new HashMap(); + for (int i = 0; i < entries.length; i++) + map.put(entries[i++], entries[i]); + } + return map; + } +} diff --git a/src/net/java/sip/communicator/impl/neomedia/format/VideoMediaFormatImpl.java b/src/net/java/sip/communicator/impl/neomedia/format/VideoMediaFormatImpl.java index e04d11635..fbc025eb1 100644 --- a/src/net/java/sip/communicator/impl/neomedia/format/VideoMediaFormatImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/format/VideoMediaFormatImpl.java @@ -17,7 +17,7 @@ /** * Implements VideoMediaFormat for the JMF VideoFormat. * - * @author Lubomir Marinov + * @author Lyubomir Marinov */ public class VideoMediaFormatImpl extends MediaFormatImpl @@ -150,6 +150,88 @@ public boolean equals(Object mediaFormat) return (clockRate == videoMediaFormatImplClockRate); } + /** + * {@inheritDoc} + * + * @see MediaFormatImpl#formatParametersAreEqual(Map, Map) + */ + @Override + protected boolean formatParametersAreEqual( + Map fmtps1, + Map fmtps2) + { + return formatParametersAreEqual(getEncoding(), fmtps1, fmtps2); + } + + /** + * Determines whether a specific set of format parameters is equal to + * another set of format parameters in the sense that they define an equal + * number of parameters and assign them equal values. Since the values are + * Strings, presumes that a value of null is equal to the + * empty String. + *

+ * The two Map instances of format parameters to be checked for + * equality are presumed to be modifiable in the sense that if the lack of a + * format parameter in a given Map is equivalent to it having a + * specific value, an association of the format parameter to the value in + * question may be added to or removed from the respective Map + * instance for the purposes of determining equality. + *

+ * + * @param encoding the encoding (name) related to the two sets of format + * parameters to be tested for equality + * @param fmtps1 the first set of format parameters to be tested for + * equality + * @param fmtps2 the second set of format parameters to be tested for + * equality + * @return true if the specified sets of format parameters are + * equal; false, otherwise + */ + public static boolean formatParametersAreEqual( + String encoding, + Map fmtps1, Map fmtps2) + { + return + MediaFormatImpl.formatParametersAreEqual(encoding, fmtps1, fmtps2); + } + + /** + * Determines whether the format parameters of this MediaFormat + * match a specific set of format parameters. + * + * @param fmtps the set of format parameters to match to the format + * parameters of this MediaFormat + * @return true if this MediaFormat considers + * fmtps matching its format parameters; otherwise, false + */ + @Override + public boolean formatParametersMatch(Map fmtps) + { + return + formatParametersMatch(getEncoding(), getFormatParameters(), fmtps) + && super.formatParametersMatch(fmtps); + } + + /** + * Determines whether two sets of format parameters match in the context of + * a specific encoding. + * + * @param encoding the encoding (name) related to the two sets of format + * parameters to be matched. + * @param fmtps1 the first set of format parameters which is to be matched + * against fmtps2 + * @param fmtps2 the second set of format parameters which is to be matched + * against fmtps1 + * @return true if the two sets of format parameters match in the + * context of the specified encoding; otherwise, false + */ + public static boolean formatParametersMatch( + String encoding, + Map fmtps1 , Map fmtps2) + { + return true; + } + /** * Gets the clock rate associated with this MediaFormat. * diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractBufferCaptureDevice.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractBufferCaptureDevice.java index 509b1af26..a60083d09 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractBufferCaptureDevice.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractBufferCaptureDevice.java @@ -65,6 +65,12 @@ public abstract class AbstractBufferCaptureDevice */ private FormatControl[] formatControls; + /** + * The FrameRateControls of this + * AbstractBufferCaptureDevice. + */ + private FrameRateControl[] frameRateControls; + /** * The indicator which determines whether the transfer of media data from * this DataSource has been started. @@ -205,6 +211,20 @@ protected FormatControl[] createFormatControls() : new FormatControl[] { formatControl }; } + /** + * Creates a new FrameRateControl instance which is to allow the + * getting and setting of the frame rate of this + * AbstractBufferCaptureDevice. + * + * @return a new FrameRateControl instance which is to allow the + * getting and setting of the frame rate of this + * AbstractBufferCaptureDevice + */ + protected FrameRateControl createFrameRateControl() + { + return null; + } + /** * Create a new AbstractBufferStream which is to be at a specific * zero-based index in the list of streams of this @@ -269,19 +289,27 @@ final void defaultDoStop() final Object[] defaultGetControls() { FormatControl[] formatControls = internalGetFormatControls(); + FrameRateControl[] frameRateControls = internalGetFrameRateControls(); - if ((formatControls == null) || (formatControls.length == 0)) + if (((formatControls == null) || (formatControls.length == 0)) + && ((frameRateControls == null) + || (frameRateControls.length == 0))) return ControlsAdapter.EMPTY_CONTROLS; else { - Object[] controls = new Object[formatControls.length]; + Object[] controls + = new Object[formatControls.length + frameRateControls.length]; + int offset = 0; System.arraycopy( - formatControls, - 0, - controls, - 0, + formatControls, 0, + controls, offset, formatControls.length); + offset += formatControls.length; + System.arraycopy( + frameRateControls, 0, + controls, offset, + frameRateControls.length); return controls; } } @@ -636,6 +664,30 @@ private synchronized FormatControl[] internalGetFormatControls() return formatControls; } + /** + * Gets an array of FrameRateControl instances which can be used to + * get and/or set the output frame rate of this + * AbstractBufferCaptureDevice. + * + * @return an array of FrameRateControl instances which can be used + * to get and/or set the output frame rate of this + * AbstractBufferCaptureDevice. + */ + private synchronized FrameRateControl[] internalGetFrameRateControls() + { + if (frameRateControls == null) + { + FrameRateControl frameRateControl = createFrameRateControl(); + + // Don't try to create the FrameRateControl more than once. + frameRateControls + = (frameRateControl == null) + ? new FrameRateControl[0] + : new FrameRateControl[] { frameRateControl }; + } + return frameRateControls; + } + /** * Attempts to set the Format to be reported by the * FormatControl of a PushBufferStream at a specific diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferCaptureDevice.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferCaptureDevice.java index 0bc408e05..242c5ca35 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferCaptureDevice.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferCaptureDevice.java @@ -35,72 +35,80 @@ public abstract class AbstractPullBufferCaptureDevice */ private final AbstractBufferCaptureDevice impl = new AbstractBufferCaptureDevice() - { - protected AbstractPullBufferStream createStream( - int streamIndex, - FormatControl formatControl) - { - return - AbstractPullBufferCaptureDevice.this.createStream( - streamIndex, - formatControl); - } - - protected void doConnect() - throws IOException - { - AbstractPullBufferCaptureDevice.this.doConnect(); - } - - protected void doDisconnect() - { - AbstractPullBufferCaptureDevice.this.doDisconnect(); - } - - protected void doStart() - throws IOException - { - AbstractPullBufferCaptureDevice.this.doStart(); - } - - protected void doStop() - throws IOException - { - AbstractPullBufferCaptureDevice.this.doStop(); - } - - public CaptureDeviceInfo getCaptureDeviceInfo() - { - return - AbstractPullBufferCaptureDevice.this - .getCaptureDeviceInfo(); - } - - protected Format getFormat(int streamIndex, Format oldValue) - { - return - AbstractPullBufferCaptureDevice.this.getFormat( - streamIndex, - oldValue); - } - - protected Format[] getSupportedFormats(int streamIndex) - { - return - AbstractPullBufferCaptureDevice.this - .getSupportedFormats(streamIndex); - } - - protected Format setFormat( - int streamIndex, - Format oldValue, Format newValue) - { - return - AbstractPullBufferCaptureDevice.this.setFormat( - streamIndex, - oldValue, newValue); - } - }; + { + @Override + protected FrameRateControl createFrameRateControl() + { + return + AbstractPullBufferCaptureDevice.this + .createFrameRateControl(); + } + + protected AbstractPullBufferStream createStream( + int streamIndex, + FormatControl formatControl) + { + return + AbstractPullBufferCaptureDevice.this.createStream( + streamIndex, + formatControl); + } + + protected void doConnect() + throws IOException + { + AbstractPullBufferCaptureDevice.this.doConnect(); + } + + protected void doDisconnect() + { + AbstractPullBufferCaptureDevice.this.doDisconnect(); + } + + protected void doStart() + throws IOException + { + AbstractPullBufferCaptureDevice.this.doStart(); + } + + protected void doStop() + throws IOException + { + AbstractPullBufferCaptureDevice.this.doStop(); + } + + public CaptureDeviceInfo getCaptureDeviceInfo() + { + return + AbstractPullBufferCaptureDevice.this + .getCaptureDeviceInfo(); + } + + protected Format getFormat(int streamIndex, Format oldValue) + { + return + AbstractPullBufferCaptureDevice.this.getFormat( + streamIndex, + oldValue); + } + + protected Format[] getSupportedFormats(int streamIndex) + { + return + AbstractPullBufferCaptureDevice.this + .getSupportedFormats(streamIndex); + } + + protected Format setFormat( + int streamIndex, + Format oldValue, Format newValue) + { + return + AbstractPullBufferCaptureDevice.this.setFormat( + streamIndex, + oldValue, newValue); + } + }; /** * Initializes a new AbstractPullBufferCaptureDevice instance. @@ -134,6 +142,20 @@ public void connect() impl.connect(); } + /** + * Creates a new FrameRateControl instance which is to allow the + * getting and setting of the frame rate of this + * AbstractPullBufferCaptureDevice. + * + * @return a new FrameRateControl instance which is to allow the + * getting and setting of the frame rate of this + * AbstractPullBufferCaptureDevice + */ + protected FrameRateControl createFrameRateControl() + { + return null; + } + /** * Creates a new PullBufferStream which is to be at a specific * zero-based index in the list of streams of this diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferStream.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferStream.java index b52be8b8b..ec8ebc632 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferStream.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPullBufferStream.java @@ -6,6 +6,7 @@ */ package net.java.sip.communicator.impl.neomedia.jmfext.media.protocol; +import javax.media.*; import javax.media.control.*; import javax.media.protocol.*; @@ -39,7 +40,7 @@ protected AbstractPullBufferStream( } /** - * Determines if read will block. + * Determines if {@link #read(Buffer)} will block. * * @return true if read block, false otherwise */ diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPushBufferCaptureDevice.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPushBufferCaptureDevice.java index 8535afbe4..976ed6c65 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPushBufferCaptureDevice.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractPushBufferCaptureDevice.java @@ -29,87 +29,93 @@ public abstract class AbstractPushBufferCaptureDevice */ private final AbstractBufferCaptureDevice impl = new AbstractBufferCaptureDevice() - { - protected AbstractPushBufferStream createStream( - int streamIndex, - FormatControl formatControl) - { - return - AbstractPushBufferCaptureDevice.this.createStream( - streamIndex, - formatControl); - } - - protected void doConnect() - throws IOException - { - AbstractPushBufferCaptureDevice.this.doConnect(); - } - - protected void doDisconnect() - { - AbstractPushBufferCaptureDevice.this.doDisconnect(); - } - - protected void doStart() - throws IOException - { - AbstractPushBufferCaptureDevice.this.doStart(); - } - - protected void doStop() - throws IOException - { - AbstractPushBufferCaptureDevice.this.doStop(); - } - - public CaptureDeviceInfo getCaptureDeviceInfo() - { - return - AbstractPushBufferCaptureDevice.this - .getCaptureDeviceInfo(); - } - - /** - * Overrides - * {@link AbstractBufferCaptureDevice#getControls()} to add - * controls specific to this - * AbstractPushBufferCaptureDevice. - * - * {@inheritDoc} - */ - @Override - public Object[] getControls() - { - return - AbstractPushBufferCaptureDevice.this.getControls(); - } - - protected Format getFormat(int streamIndex, Format oldValue) - { - return - AbstractPushBufferCaptureDevice.this.getFormat( - streamIndex, - oldValue); - } - - protected Format[] getSupportedFormats(int streamIndex) - { - return - AbstractPushBufferCaptureDevice.this - .getSupportedFormats(streamIndex); - } - - protected Format setFormat( - int streamIndex, - Format oldValue, Format newValue) - { - return - AbstractPushBufferCaptureDevice.this.setFormat( - streamIndex, - oldValue, newValue); - } - }; + { + @Override + protected FrameRateControl createFrameRateControl() + { + return + AbstractPushBufferCaptureDevice.this + .createFrameRateControl(); + } + + protected AbstractPushBufferStream createStream( + int streamIndex, + FormatControl formatControl) + { + return + AbstractPushBufferCaptureDevice.this.createStream( + streamIndex, + formatControl); + } + + protected void doConnect() + throws IOException + { + AbstractPushBufferCaptureDevice.this.doConnect(); + } + + protected void doDisconnect() + { + AbstractPushBufferCaptureDevice.this.doDisconnect(); + } + + protected void doStart() + throws IOException + { + AbstractPushBufferCaptureDevice.this.doStart(); + } + + protected void doStop() + throws IOException + { + AbstractPushBufferCaptureDevice.this.doStop(); + } + + public CaptureDeviceInfo getCaptureDeviceInfo() + { + return + AbstractPushBufferCaptureDevice.this + .getCaptureDeviceInfo(); + } + + /** + * Overrides {@link AbstractBufferCaptureDevice#getControls()} to + * add controls specific to this + * AbstractPushBufferCaptureDevice. + * + * {@inheritDoc} + */ + @Override + public Object[] getControls() + { + return AbstractPushBufferCaptureDevice.this.getControls(); + } + + protected Format getFormat(int streamIndex, Format oldValue) + { + return + AbstractPushBufferCaptureDevice.this.getFormat( + streamIndex, + oldValue); + } + + protected Format[] getSupportedFormats(int streamIndex) + { + return + AbstractPushBufferCaptureDevice.this + .getSupportedFormats(streamIndex); + } + + protected Format setFormat( + int streamIndex, + Format oldValue, Format newValue) + { + return + AbstractPushBufferCaptureDevice.this.setFormat( + streamIndex, + oldValue, newValue); + } + }; /** * Initializes a new AbstractPushBufferCaptureDevice instance. @@ -143,6 +149,20 @@ public void connect() impl.connect(); } + /** + * Creates a new FrameRateControl instance which is to allow the + * getting and setting of the frame rate of this + * AbstractPushBufferCaptureDevice. + * + * @return a new FrameRateControl instance which is to allow the + * getting and setting of the frame rate of this + * AbstractPushBufferCaptureDevice + */ + protected FrameRateControl createFrameRateControl() + { + return null; + } + /** * Create a new PushBufferStream which is to be at a specific * zero-based index in the list of streams of this diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractVideoPullBufferCaptureDevice.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractVideoPullBufferCaptureDevice.java new file mode 100644 index 000000000..46b0a1f23 --- /dev/null +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractVideoPullBufferCaptureDevice.java @@ -0,0 +1,79 @@ +/* + * Jitsi, 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.jmfext.media.protocol; + +import javax.media.*; +import javax.media.control.*; + +import net.java.sip.communicator.impl.neomedia.control.*; + +/** + * Provides a base implementation of PullBufferDataSource and + * CaptureDevice for the purposes of video in order to facilitate + * implementers by taking care of boilerplate in the most common cases. + * + * @author Lyubomir Marinov + */ +public abstract class AbstractVideoPullBufferCaptureDevice + extends AbstractPullBufferCaptureDevice +{ + + /** + * Initializes a new AbstractVideoPullBufferCaptureDevice instance. + */ + protected AbstractVideoPullBufferCaptureDevice() + { + } + + /** + * Initializes a new AbstractVideoPullBufferCaptureDevice instance + * from a specific MediaLocator. + * + * @param locator the MediaLocator to create the new instance from + */ + protected AbstractVideoPullBufferCaptureDevice(MediaLocator locator) + { + super(locator); + } + + /** + * Creates a new FrameRateControl instance which is to allow the + * getting and setting of the frame rate of this + * AbstractVideoPullBufferCaptureDevice. + * + * @return a new FrameRateControl instance which is to allow the + * getting and setting of the frame rate of this + * AbstractVideoPullBufferCaptureDevice + * @see AbstractPullBufferCaptureDevice#createFrameRateControl() + */ + @Override + protected FrameRateControl createFrameRateControl() + { + return + new FrameRateControlAdapter() + { + /** + * The output frame rate of this + * AbstractVideoPullBufferCaptureDevice. + */ + private float frameRate = -1; + + @Override + public float getFrameRate() + { + return frameRate; + } + + @Override + public float setFrameRate(float frameRate) + { + this.frameRate = frameRate; + return this.frameRate; + } + }; + } +} diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractVideoPullBufferStream.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractVideoPullBufferStream.java new file mode 100644 index 000000000..62a7f8be1 --- /dev/null +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractVideoPullBufferStream.java @@ -0,0 +1,182 @@ +/* + * Jitsi, 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.jmfext.media.protocol; + +import java.io.*; + +import javax.media.*; +import javax.media.control.*; +import javax.media.protocol.*; + +/** + * Provides a base implementation of PullBufferStream for video in + * order to facilitate implementers by taking care of boilerplate in the most + * common cases. + * + * @author Lyubomir Marinov + */ +public abstract class AbstractVideoPullBufferStream + extends AbstractPullBufferStream +{ + + /** + * The output frame rate of this AbstractVideoPullBufferStream + * which has been specified by {@link #frameRateControl} and depending on + * which {@link #minimumVideoFrameInterval} has been calculated. + */ + private float frameRate; + + /** + * The FrameRateControl which gets and sets the output frame rate + * of this AbstractVideoPullBufferStream. + */ + private FrameRateControl frameRateControl; + + /** + * The minimum interval in milliseconds between consecutive video frames + * i.e. the reverse of {@link #frameRate}. + */ + private long minimumVideoFrameInterval; + + /** + * Initializes a new AbstractVideoPullBufferStream instance which + * is to have its Format-related information abstracted by a + * specific FormatControl. + * + * @param dataSource the PullBufferDataSource which is creating the + * new instance so that it becomes one of its streams + * @param formatControl the FormatControl which is to abstract the + * Format-related information of the new instance + */ + protected AbstractVideoPullBufferStream( + PullBufferDataSource dataSource, + FormatControl formatControl) + { + super(dataSource, formatControl); + } + + /** + * Blocks and reads into a Buffer from this + * PullBufferStream. + * + * @param buffer the Buffer this PullBufferStream is to + * read into + * @throws IOException if an I/O error occurs while this + * PullBufferStream reads into the specified Buffer + */ + protected abstract void doRead(Buffer buffer) + throws IOException; + + /** + * Blocks and reads into a Buffer from this + * PullBufferStream. + * + * @param buffer the Buffer this PullBufferStream is to + * read into + * @throws IOException if an I/O error occurs while this + * PullBufferStream reads into the specified Buffer + */ + public void read(Buffer buffer) + throws IOException + { + FrameRateControl frameRateControl = this.frameRateControl; + + if (frameRateControl != null) + { + float frameRate = frameRateControl.getFrameRate(); + + if (frameRate > 0) + { + if (this.frameRate != frameRate) + { + minimumVideoFrameInterval = (long) (1000 / frameRate); + this.frameRate = frameRate; + } + if (minimumVideoFrameInterval > 0) + { + long startTime = System.currentTimeMillis(); + + doRead(buffer); + + if (!buffer.isDiscard()) + { + boolean interrupted = false; + + while (true) + { + // Sleep to respect the frame rate as much as possible. + long sleep + = minimumVideoFrameInterval + - (System.currentTimeMillis() - startTime); + + if (sleep > 0) + { + try + { + Thread.sleep(sleep); + } + catch (InterruptedException ie) + { + interrupted = true; + } + } + else + { + // Yield a little bit to not use all the whole CPU. + Thread.yield(); + break; + } + } + if (interrupted) + Thread.currentThread().interrupt(); + } + + // We've executed #doRead(Buffer). + return; + } + } + } + + // If there is no frame rate to be respected, just #doRead(Buffer). + doRead(buffer); + } + + /** + * Starts the transfer of media data from this + * AbstractBufferStream. + * + * @throws IOException if anything goes wrong while starting the transfer of + * media data from this AbstractBufferStream + * @see AbstractBufferStream#start() + */ + @Override + public void start() + throws IOException + { + super.start(); + + frameRateControl + = (FrameRateControl) + dataSource.getControl(FrameRateControl.class.getName()); + } + + /** + * Stops the transfer of media data from this AbstractBufferStream. + * + * @throws IOException if anything goes wrong while stopping the transfer of + * media data from this AbstractBufferStream + * @see AbstractBufferStream#stop() + */ + @Override + public void stop() + throws IOException + { + super.stop(); + + frameRateControl = null; + } +} diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractVideoPushBufferCaptureDevice.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractVideoPushBufferCaptureDevice.java index 40036eab9..f831e28db 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractVideoPushBufferCaptureDevice.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/AbstractVideoPushBufferCaptureDevice.java @@ -6,8 +6,6 @@ */ package net.java.sip.communicator.impl.neomedia.jmfext.media.protocol; -import java.util.*; - import javax.media.*; import javax.media.control.*; @@ -21,11 +19,6 @@ public abstract class AbstractVideoPushBufferCaptureDevice extends AbstractPushBufferCaptureDevice { - /** - * The FrameRateControls of this - * AbstractVideoPushBufferCaptureDevice. - */ - private FrameRateControl[] frameRateControls; /** * Initializes a new AbstractVideoPushBufferCaptureDevice instance. @@ -53,47 +46,11 @@ protected AbstractVideoPushBufferCaptureDevice(MediaLocator locator) * @return a new FrameRateControl instance which is to allow the * getting and setting of the frame rate of this * AbstractVideoPushBufferCaptureDevice + * @see AbstractPushBufferCaptureDevice#createFrameRateControl() */ + @Override protected FrameRateControl createFrameRateControl() { return null; } - - /** - * Implements {@link javax.media.protocol.DataSource#getControls()}. Gets - * the controls available for this instance. - * - * @return an array of Objects which represent the controls - * available for this instance - */ - @Override - public Object[] getControls() - { - List controls = new ArrayList(); - - Collections.addAll(controls, super.getControls()); - - /* - * Add any FrameRateControl that this AbstractPushBufferCaptureDevice - * may want to have. - */ - synchronized (this) - { - if (frameRateControls == null) - { - FrameRateControl frameRateControl = createFrameRateControl(); - - // Don't try to create the FrameRateControl more than once. - if (frameRateControl == null) - frameRateControls = new FrameRateControl[0]; - else - frameRateControls - = new FrameRateControl[] { frameRateControl }; - } - if (frameRateControls != null) - Collections.addAll(controls, frameRateControls); - } - - return controls.toArray(); - } } diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/alsa/DataSource.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/alsa/DataSource.java index a0aed8f17..2c9ce1d66 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/alsa/DataSource.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/alsa/DataSource.java @@ -23,7 +23,6 @@ public class DataSource extends PushBufferDataSource { protected boolean started = false; - protected String contentType = "raw"; protected boolean connected = false; protected Time duration = DURATION_UNKNOWN; protected AlsaStream [] streams = null; @@ -37,18 +36,15 @@ public DataSource() } /** - * Tell we are a raw datasource + * Gets the type of the content made available by this DataSource + * (i.e. raw). * - * @return "raw" + * @return the type of the content made available by this + * DataSource (i.e. raw) */ public String getContentType() { - if (!connected) - { - System.err.println("Error: DataSource not connected"); - return null; - } - return contentType; + return ContentDescriptor.RAW; } /** diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/DataSource.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/DataSource.java index 3f3453fb9..d42a2ad32 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/DataSource.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/DataSource.java @@ -20,7 +20,7 @@ * @author Damian Minkov */ public class DataSource - extends AbstractPullBufferCaptureDevice + extends AbstractVideoPullBufferCaptureDevice { /** * Stream created. diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/ImageStream.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/ImageStream.java index c9919c9b8..eb423d1c1 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/ImageStream.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/imgstreaming/ImageStream.java @@ -28,7 +28,7 @@ * @author Damian Minkov */ public class ImageStream - extends AbstractPullBufferStream + extends AbstractVideoPullBufferStream { /** * The Logger @@ -85,20 +85,18 @@ public class ImageStream } /** - * Block and read a buffer from the stream. + * Blocks and reads into a Buffer from this + * PullBufferStream. * - * @param buffer the Buffer to read captured media into - * @throws IOException if an error occurs while reading. + * @param buffer the Buffer this PullBufferStream is to + * read into + * @throws IOException if an I/O error occurs while this + * PullBufferStream reads into the specified Buffer + * @see AbstractVideoPullBufferStream#doRead(Buffer) */ - public void read(Buffer buffer) + protected void doRead(Buffer buffer) throws IOException { - //System.out.println(System.currentTimeMillis()); - long begin = System.currentTimeMillis(); - /* maximum time allowed for a capture to respect frame rate */ - long maxTime = 1000 / 10; - int wait = 0; - /* * Determine the Format in which we're expected to output. We cannot * rely on the Format always being specified in the Buffer because it is @@ -116,8 +114,9 @@ public void read(Buffer buffer) if(bufferFormat instanceof AVFrameFormat) { - /* native transfert: we keep data in native memory rather - * than Java Heap until we reach SwScaler + /* + * Native transfer: we keep data in native memory rather than the + * Java heap until we reach SwScaler. */ Object dataAv = buffer.getData(); AVFrame bufferFrame = null; @@ -184,29 +183,6 @@ public void read(Buffer buffer) buffer.setSequenceNumber(seqNo); buffer.setFlags(Buffer.FLAG_SYSTEM_TIME | Buffer.FLAG_LIVE_DATA); seqNo++; - - wait = (int)(maxTime - (System.currentTimeMillis() - begin)); - - try - { - /* sleep to respect as much as possible the - * frame rate - */ - if(wait > 0) - { - Thread.sleep(wait); - } - else - { - /* yield a little bit to not use all the - * CPU - */ - Thread.yield(); - } - } - catch(Exception e) - { - } } /** diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/portaudio/PortAudioStream.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/portaudio/PortAudioStream.java index a95643740..fede7d277 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/portaudio/PortAudioStream.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/portaudio/PortAudioStream.java @@ -144,6 +144,7 @@ protected Format doGetFormat() * @throws IOException if anything goes wrong while reading media data from * this PullBufferStream into the specified buffer */ + @Override public void read(Buffer buffer) throws IOException { diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/video4linux2/DataSource.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/video4linux2/DataSource.java index f7d6f9755..5a105b8d9 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/video4linux2/DataSource.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/video4linux2/DataSource.java @@ -22,7 +22,7 @@ * @author Lyubomir Marinov */ public class DataSource - extends AbstractPullBufferCaptureDevice + extends AbstractVideoPullBufferCaptureDevice { /** * The map of Video for Linux Two API Specification pixel formats to FFmpeg diff --git a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/video4linux2/Video4Linux2Stream.java b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/video4linux2/Video4Linux2Stream.java index 47efd5e2e..314f0537d 100644 --- a/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/video4linux2/Video4Linux2Stream.java +++ b/src/net/java/sip/communicator/impl/neomedia/jmfext/media/protocol/video4linux2/Video4Linux2Stream.java @@ -25,7 +25,7 @@ * @author Lyubomir Marinov */ public class Video4Linux2Stream - extends AbstractPullBufferStream + extends AbstractVideoPullBufferStream { /** * The pool of ByteBuffers this instances is using to transfer the @@ -188,6 +188,124 @@ protected Format doGetFormat() return format; } + /** + * Reads media data from this PullBufferStream into a specific + * Buffer with blocking. + * + * @param buffer the Buffer in which media data is to be read from + * this PullBufferStream + * @throws IOException if anything goes wrong while reading media data from + * this PullBufferStream into the specified buffer + * @see AbstractVideoPullBufferStream#doRead(Buffer) + */ + protected void doRead(Buffer buffer) + throws IOException + { + Format format = buffer.getFormat(); + + if (!(format instanceof AVFrameFormat)) + format = null; + if (format == null) + { + format = getFormat(); + if (format != null) + buffer.setFormat(format); + } + + if(startInRead) + { + startInRead = false; + + long v4l2_buf_type + = Video4Linux2.v4l2_buf_type_alloc( + Video4Linux2.V4L2_BUF_TYPE_VIDEO_CAPTURE); + + if (0 == v4l2_buf_type) + throw new OutOfMemoryError("v4l2_buf_type_alloc"); + try + { + if (Video4Linux2.ioctl(fd, Video4Linux2.VIDIOC_STREAMON, + v4l2_buf_type) == -1) + { + throw new IOException("ioctl: request= VIDIOC_STREAMON"); + } + } + finally + { + Video4Linux2.free(v4l2_buf_type); + } + } + + if (Video4Linux2.ioctl(fd, Video4Linux2.VIDIOC_DQBUF, v4l2_buffer) + == -1) + throw new IOException("ioctl: request= VIDIOC_DQBUF"); + + long timeStamp = System.nanoTime(); + + try + { + ByteBuffer data = null; + int index = Video4Linux2.v4l2_buffer_getIndex(v4l2_buffer); + long mmap = mmaps[index]; + int bytesused = Video4Linux2.v4l2_buffer_getBytesused(v4l2_buffer); + + if(nativePixelFormat == Video4Linux2.V4L2_PIX_FMT_MJPEG || + nativePixelFormat == Video4Linux2.V4L2_PIX_FMT_JPEG) + { + /* initialize FFmpeg's MJPEG decoder if not already done */ + if(mjpeg_context == 0) + { + long avcodec = FFmpeg.avcodec_find_decoder( + FFmpeg.CODEC_ID_MJPEG); + + mjpeg_context = FFmpeg.avcodec_alloc_context(); + FFmpeg.avcodeccontext_set_workaround_bugs(mjpeg_context, + FFmpeg.FF_BUG_AUTODETECT); + + if (FFmpeg.avcodec_open(mjpeg_context, avcodec) < 0) + { + throw new RuntimeException("" + + "Could not open codec CODEC_ID_MJPEG"); + } + + avframe = FFmpeg.avcodec_alloc_frame(); + } + + if(FFmpeg.avcodec_decode_video(mjpeg_context, avframe, mmap, + bytesused) != -1) + { + Object out = buffer.getData(); + + if (!(out instanceof AVFrame) || + (((AVFrame) out).getPtr() != avframe)) + { + buffer.setData(new AVFrame(avframe)); + } + } + } + else + { + data = byteBufferPool.getFreeBuffer(bytesused); + + if (data != null) + { + Video4Linux2.memcpy(data.ptr, mmap, bytesused); + } + data.setLength(bytesused); + FinalizableAVFrame.read(buffer, format, data, byteBufferPool); + } + } + finally + { + if (Video4Linux2.ioctl(fd, Video4Linux2.VIDIOC_QBUF, v4l2_buffer) + == -1) + throw new IOException("ioctl: request= VIDIOC_QBUF"); + } + + buffer.setFlags(Buffer.FLAG_LIVE_DATA | Buffer.FLAG_SYSTEM_TIME); + buffer.setTimeStamp(timeStamp); + } + /** * Gets the Format of the media data captured by the Video for * Linux Two API Specification device represented by the fd of this @@ -422,124 +540,6 @@ private void negotiateFdInputMethod() } } - /** - * Reads media data from this PullBufferStream into a specific - * Buffer with blocking. - * - * @param buffer the Buffer in which media data is to be read from - * this PullBufferStream - * @throws IOException if anything goes wrong while reading media data from - * this PullBufferStream into the specified buffer - */ - public void read(Buffer buffer) - throws IOException - { - Format format = buffer.getFormat(); - - if (!(format instanceof AVFrameFormat)) - format = null; - - if (format == null) - { - format = getFormat(); - if (format != null) - buffer.setFormat(format); - } - - if(startInRead) - { - startInRead = false; - - long v4l2_buf_type - = Video4Linux2.v4l2_buf_type_alloc( - Video4Linux2.V4L2_BUF_TYPE_VIDEO_CAPTURE); - - if (0 == v4l2_buf_type) - throw new OutOfMemoryError("v4l2_buf_type_alloc"); - try - { - if (Video4Linux2.ioctl(fd, Video4Linux2.VIDIOC_STREAMON, - v4l2_buf_type) == -1) - { - throw new IOException("ioctl: request= VIDIOC_STREAMON"); - } - } - finally - { - Video4Linux2.free(v4l2_buf_type); - } - } - - if (Video4Linux2.ioctl(fd, Video4Linux2.VIDIOC_DQBUF, v4l2_buffer) - == -1) - throw new IOException("ioctl: request= VIDIOC_DQBUF"); - - long timeStamp = System.nanoTime(); - - try - { - ByteBuffer data = null; - int index = Video4Linux2.v4l2_buffer_getIndex(v4l2_buffer); - long mmap = mmaps[index]; - int bytesused = Video4Linux2.v4l2_buffer_getBytesused(v4l2_buffer); - - if(nativePixelFormat == Video4Linux2.V4L2_PIX_FMT_MJPEG || - nativePixelFormat == Video4Linux2.V4L2_PIX_FMT_JPEG) - { - /* initialize FFmpeg's MJPEG decoder if not already done */ - if(mjpeg_context == 0) - { - long avcodec = FFmpeg.avcodec_find_decoder( - FFmpeg.CODEC_ID_MJPEG); - - mjpeg_context = FFmpeg.avcodec_alloc_context(); - FFmpeg.avcodeccontext_set_workaround_bugs(mjpeg_context, - FFmpeg.FF_BUG_AUTODETECT); - - if (FFmpeg.avcodec_open(mjpeg_context, avcodec) < 0) - { - throw new RuntimeException("" + - "Could not open codec CODEC_ID_MJPEG"); - } - - avframe = FFmpeg.avcodec_alloc_frame(); - } - - if(FFmpeg.avcodec_decode_video(mjpeg_context, avframe, mmap, - bytesused) != -1) - { - Object out = buffer.getData(); - - if (!(out instanceof AVFrame) || - (((AVFrame) out).getPtr() != avframe)) - { - buffer.setData(new AVFrame(avframe)); - } - } - } - else - { - data = byteBufferPool.getFreeBuffer(bytesused); - - if (data != null) - { - Video4Linux2.memcpy(data.ptr, mmap, bytesused); - } - data.setLength(bytesused); - FinalizableAVFrame.read(buffer, format, data, byteBufferPool); - } - } - finally - { - if (Video4Linux2.ioctl(fd, Video4Linux2.VIDIOC_QBUF, v4l2_buffer) - == -1) - throw new IOException("ioctl: request= VIDIOC_QBUF"); - } - - buffer.setFlags(Buffer.FLAG_LIVE_DATA | Buffer.FLAG_SYSTEM_TIME); - buffer.setTimeStamp(timeStamp); - } - /** * Sets the file descriptor of the Video for Linux Two API Specification * device which is to be read through this PullBufferStream. diff --git a/src/net/java/sip/communicator/impl/neomedia/videoflip/VideoFlipEffect.java b/src/net/java/sip/communicator/impl/neomedia/videoflip/VideoFlipEffect.java deleted file mode 100644 index 48934c20f..000000000 --- a/src/net/java/sip/communicator/impl/neomedia/videoflip/VideoFlipEffect.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * 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.videoflip; - -import javax.media.*; -import javax.media.format.*; - -import net.java.sip.communicator.impl.neomedia.control.*; -import net.java.sip.communicator.impl.neomedia.codec.*; -import net.java.sip.communicator.impl.neomedia.codec.video.*; -import net.java.sip.communicator.util.*; - -/** - * JMF Effect to flip local video horizontally. - * - * @author Sebastien Vincent - */ -public class VideoFlipEffect - extends ControlsAdapter - implements Effect -{ - /** - * The Logger used by the SwScaler class and its instances - * for logging output. - */ - private static final Logger logger = Logger - .getLogger(VideoFlipEffect.class); - - /** - * List of supported input formats. - */ - private final static Format DEFAULT_INPUT_FORMATS[] - = new Format[] - { - new AVFrameFormat(), - }; - - /** - * List of supported output formats. - */ - private final static Format DEFAULT_OUTPUT_FORMATS[] - = new Format[] - { - new AVFrameFormat(), - }; - - /** - * Name of the effect. - */ - private static final String PLUGIN_NAME = "Local video flip Effect"; - - /** - * Input Format - */ - private VideoFormat inputFormat = null; - - /** - * Output Format - */ - private VideoFormat outputFormat = null; - - /** - * AVFilterGraph reference. - */ - private long graph = 0; - - /** - * AVInputStream reference. - */ - private long inputstream = 0; - - /** - * AVFrame reference. - */ - private long avframe = 0; - - /** - * If the filters are configured. - */ - private boolean configured = false; - - /** - * Constructor. - */ - public VideoFlipEffect() - { - } - - /** - * Lists all of the input formats that this codec accepts. - * - * @return An array that contains the supported input Formats. - */ - public Format[] getSupportedInputFormats() - { - return DEFAULT_INPUT_FORMATS; - } - - /** - * Lists the output formats that this codec can generate. - * - * @param input The Format of the data to be used as input to the - * plug-in. - * @return An array that contains the supported output Formats. - */ - public Format[] getSupportedOutputFormats(Format input) - { - if(input == null) - { - return DEFAULT_OUTPUT_FORMATS; - } - - return new Format[] - { - (Format)input.clone(), - }; - } - - /** - * Sets the format of the data to be input to this codec. - * - * @param format The Format to be set. - * @return The Format that was set. - */ - public Format setInputFormat(Format format) - { - inputFormat = (VideoFormat)format; - return inputFormat; - } - - /** - * Sets the format for the data this codec outputs. - * - * @param format The Format to be set. - * @return The Format that was set. - */ - public Format setOutputFormat(Format format) - { - outputFormat = (VideoFormat)format; - return outputFormat; - } - - /** - * Opens this effect. - * - * @throws ResourceUnavailableException If all of the required resources - * cannot be acquired. - */ - public synchronized void open() throws ResourceUnavailableException - { - graph = FFmpeg.avfilter_alloc_filtergraph(); - inputstream = FFmpeg.avfilter_alloc_inputstream(); - avframe = FFmpeg.avcodec_alloc_frame(); - } - - /** - * Closes this effect. - */ - public synchronized void close() - { - if(graph != 0) - { - FFmpeg.avfilter_free_filtergraph(graph); - } - - if(inputstream != 0) - { - FFmpeg.avfilter_free_inputstream(inputstream); - } - - if(avframe != 0) - { - FFmpeg.av_free(avframe); - avframe = 0; - } - - configured = false; - } - - /** - * Gets the name of this plug-in as a human-readable string. - * - * @return A String that contains the descriptive name of the - * plug-in. - */ - public String getName() - { - return PLUGIN_NAME; - } - - /** - * Performs the media processing defined by this codec. - * - * @param inputBuffer The Buffer that contains the media data to be - * processed. - * @param outputBuffer The Buffer in which to store the processed - * media data. - * @return BUFFER_PROCESSED_OK if the processing is successful. - * @see PlugIn - */ - public synchronized int process(Buffer inputBuffer, Buffer outputBuffer) - { - int ret = 0; - - if(inputBuffer.getFormat() instanceof AVFrameFormat) - { - if(!configured) - { - AVFrameFormat format = (AVFrameFormat)inputBuffer.getFormat(); - if(FFmpeg.avfilter_configure_filters("hflip", inputstream, - format.getPixFmt(), format.getSize().width, - format.getSize().height, graph) != 0) - { - if(logger.isDebugEnabled()) - { - logger.debug( - "Failed to configure libavfilter's filters"); - } - return BUFFER_PROCESSED_FAILED; - } - configured = true; - } - - AVFrame frame = (AVFrame)inputBuffer.getData(); - FFmpeg.av_vsrc_buffer_add_frame(inputstream, frame.getPtr()); - - if(FFmpeg.av_get_filtered_video_frame(inputstream, avframe) != -1) - { - Object out = outputBuffer.getData(); - - if (!(out instanceof AVFrame) || - (((AVFrame) out).getPtr() != avframe)) - { - outputBuffer.setData(new AVFrame(avframe)); - } - } - } - - if(ret == BUFFER_PROCESSED_OK) - { - outputBuffer.setLength(inputBuffer.getLength()); - outputBuffer.setFormat(inputBuffer.getFormat()); - outputBuffer.setHeader(inputBuffer.getHeader()); - outputBuffer.setSequenceNumber(inputBuffer.getSequenceNumber()); - outputBuffer.setTimeStamp(inputBuffer.getTimeStamp()); - outputBuffer.setFlags(inputBuffer.getFlags()); - outputBuffer.setDiscard(inputBuffer.isDiscard()); - outputBuffer.setEOM(inputBuffer.isEOM()); - outputBuffer.setDuration(inputBuffer.getDuration()); - } - - return ret; - } - - /** - * Resets effect state. - */ - public void reset() - { - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java index 73954c2ef..273264163 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java @@ -1127,8 +1127,11 @@ private void sendReInvite(String sdpOffer) try { // Content-Type - invite.setContent(sdpOffer, getProtocolProvider().getHeaderFactory() - .createContentTypeHeader("application", "sdp")); + invite.setContent( + sdpOffer, + getProtocolProvider() + .getHeaderFactory() + .createContentTypeHeader("application", "sdp")); /* * If the local peer represented by the Call of this CallPeer is @@ -1140,12 +1143,14 @@ private void sendReInvite(String sdpOffer) catch (ParseException ex) { ProtocolProviderServiceSipImpl.throwOperationFailedException( - "Failed to parse SDP offer for the new invite.", - OperationFailedException.INTERNAL_ERROR, ex, logger); + "Failed to parse SDP offer for the new invite.", + OperationFailedException.INTERNAL_ERROR, + ex, + logger); } getProtocolProvider().sendInDialogRequest( - getJainSipProvider(), invite, dialog); + getJainSipProvider(), invite, dialog); } /** @@ -1173,8 +1178,9 @@ public void invite() .getHeaderFactory() .createContentTypeHeader("application", "sdp"); - invite - .setContent(getMediaHandler().createOffer(), contentTypeHeader); + invite.setContent( + getMediaHandler().createOffer(), + contentTypeHeader); /* * If the local peer represented by the Call of this CallPeer is @@ -1190,8 +1196,10 @@ public void invite() catch (Exception ex) { ProtocolProviderServiceSipImpl.throwOperationFailedException( - "An error occurred while sending invite request", - OperationFailedException.NETWORK_FAILURE, ex, logger); + "An error occurred while sending invite request", + OperationFailedException.NETWORK_FAILURE, + ex, + logger); } } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java index 2c76bcdce..632b622a7 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java @@ -199,9 +199,11 @@ public CallPeerSipImpl invite(Address calleeAddress, public void reInvite() throws OperationFailedException { Iterator peers = getCallPeers(); + while (peers.hasNext()) { CallPeerSipImpl peer = peers.next(); + peer.sendReInvite(); } } diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetDesktopStreamingSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetDesktopStreamingSipImpl.java index 89fdd6895..8947bacf0 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetDesktopStreamingSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetDesktopStreamingSipImpl.java @@ -241,15 +241,18 @@ public void setLocalVideoAllowed(Call call, boolean allowed) throws OperationFailedException { - ((CallSipImpl)call).setVideoDevice(mediaDevice); - ((CallSipImpl)call).setLocalVideoAllowed(allowed, MediaUseCase.DESKTOP); - size = (((VideoMediaFormat)((CallSipImpl)call). - getDefaultDevice(MediaType.VIDEO). - getFormat()).getSize()); + CallSipImpl sipCall = (CallSipImpl) call; + + sipCall.setVideoDevice(mediaDevice); + sipCall.setLocalVideoAllowed(allowed, MediaUseCase.DESKTOP); + size + = (((VideoMediaFormat) + sipCall.getDefaultDevice(MediaType.VIDEO).getFormat()) + .getSize()); origin = getOriginForMediaDevice(mediaDevice); /* reinvite all peers */ - ((CallSipImpl)call).reInvite(); + sipCall.reInvite(); } /** diff --git a/src/net/java/sip/communicator/service/neomedia/AbstractMediaStream.java b/src/net/java/sip/communicator/service/neomedia/AbstractMediaStream.java index e8853dff5..c0489d50f 100644 --- a/src/net/java/sip/communicator/service/neomedia/AbstractMediaStream.java +++ b/src/net/java/sip/communicator/service/neomedia/AbstractMediaStream.java @@ -7,6 +7,7 @@ package net.java.sip.communicator.service.neomedia; import java.beans.*; +import java.util.*; import net.java.sip.communicator.service.neomedia.format.*; @@ -14,7 +15,7 @@ * Abstract base implementation of MediaStream to ease the * implementation of the interface. * - * @author Lubomir Marinov + * @author Lyubomir Marinov */ public abstract class AbstractMediaStream implements MediaStream @@ -80,6 +81,8 @@ protected void firePropertyChange( * @param encoding the encoding we are looking for. * @param clockRate the clock rate that we'd like the format to have. * @param channels the number of channels that expect to find in this format + * @param formatParameters the format parameters expected to match these of + * the specified format * @return true if the specified format has specific * values for its properties mediaType, encoding, * clockRate and channels; otherwise, false @@ -87,9 +90,8 @@ protected void firePropertyChange( public static boolean matches( MediaFormat format, MediaType mediaType, - String encoding, - double clockRate, - int channels) + String encoding, double clockRate, int channels, + Map formatParameters) { // mediaType // encoding @@ -121,7 +123,8 @@ public static boolean matches( return false; } - return true; + // formatParameters + return format.formatParametersMatch(formatParameters); } /** diff --git a/src/net/java/sip/communicator/service/neomedia/format/MediaFormat.java b/src/net/java/sip/communicator/service/neomedia/format/MediaFormat.java index c566bab89..d8a793fcb 100644 --- a/src/net/java/sip/communicator/service/neomedia/format/MediaFormat.java +++ b/src/net/java/sip/communicator/service/neomedia/format/MediaFormat.java @@ -20,6 +20,7 @@ * or video formats are likely to add other methods. * * @author Emil Ivov + * @author Lyubomir Marinov */ public interface MediaFormat { @@ -80,17 +81,27 @@ public interface MediaFormat /** * Determines whether this MediaFormat is equal to - * mediaFormat, or in other words that they have the same encoding - * and attributes (e.g. refreshRate, channels, etc.). + * mediaFormat i.e. they have the same encoding, clock rate, format + * parameters, advanced attributes, etc. * - * @param mediaFormat The MediaFormat that we would like to compare - * with the current one. + * @param mediaFormat the MediaFormat to compare to this instance * @return true if mediaFormat is equal to this format and * false otherwise. */ @Override public boolean equals(Object mediaFormat); + /** + * Determines whether the format parameters of this MediaFormat + * match a specific set of format parameters. + * + * @param fmtps the set of format parameters to match to the format + * parameters of this MediaFormat + * @return true if this MediaFormat considers + * fmtps matching its format parameters; otherwise, false + */ + public boolean formatParametersMatch(Map fmtps); + /** * Returns a Map containing advanced parameters specific to this * particular MediaFormat. The parameters returned here are meant diff --git a/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java b/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java index 3c15ffa1f..65287a830 100644 --- a/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java +++ b/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java @@ -1280,8 +1280,8 @@ private void registerAudioLevelListeners(AudioMediaStream audioStream) */ private void registerDynamicPTsWithStream(MediaStream stream) { - for ( Map.Entry mapEntry - : dynamicPayloadTypes.getMappings().entrySet()) + for (Map.Entry mapEntry + : dynamicPayloadTypes.getMappings().entrySet()) { byte pt = mapEntry.getValue(); MediaFormat fmt = mapEntry.getKey(); @@ -1402,12 +1402,14 @@ protected MediaFormat findMediaFormat( = MediaType.AUDIO.equals(mediaType) ? ((AudioMediaFormat) format).getChannels() : MediaFormatFactory.CHANNELS_NOT_SPECIFIED; + Map formatParameters = format.getFormatParameters(); for(MediaFormat match : formats) { if (AbstractMediaStream.matches( match, - mediaType, encoding, clockRate, channels)) + mediaType, + encoding, clockRate, channels, formatParameters)) return match; } return null; diff --git a/src/net/java/sip/communicator/service/protocol/media/DynamicPayloadTypeRegistry.java b/src/net/java/sip/communicator/service/protocol/media/DynamicPayloadTypeRegistry.java index 1ddf42deb..6a219f44a 100644 --- a/src/net/java/sip/communicator/service/protocol/media/DynamicPayloadTypeRegistry.java +++ b/src/net/java/sip/communicator/service/protocol/media/DynamicPayloadTypeRegistry.java @@ -7,7 +7,6 @@ package net.java.sip.communicator.service.protocol.media; import java.util.*; -import java.util.Map.*; import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.neomedia.format.*; @@ -37,11 +36,11 @@ public class DynamicPayloadTypeRegistry private byte nextDynamicPayloadType = MediaFormat.MIN_DYNAMIC_PAYLOAD_TYPE; /** - * A table mapping MediaFormat instances to the dynamic payload - * type number they have obtained for the lifetime of this registry. + * The mappings of MediaFormat instances to the dynamic payload + * type numbers they have obtained for the lifetime of this registry. */ - private Map payloadTypeMappings - = new Hashtable(); + private final Map payloadTypeMappings + = new HashMap(); /** * Returns the dynamic payload type that has been allocated for @@ -50,7 +49,7 @@ public class DynamicPayloadTypeRegistry * primarily during generation of SDP descriptions. * * @param format the MediaFormat instance that we'd like to obtain - * a payload type number for.. + * a payload type number for. * * @return the (possibly newly allocated) payload type number corresponding * to the specified format instance for the lifetime of the media @@ -70,13 +69,15 @@ public byte obtainPayloadTypeNumber(MediaFormat format) ? ((AudioMediaFormat) format).getChannels() : MediaFormatFactory.CHANNELS_NOT_SPECIFIED; Byte payloadType = null; + Map formatParameters + = format.getFormatParameters(); for (Map.Entry payloadTypeMapping : payloadTypeMappings.entrySet()) { if (AbstractMediaStream.matches( payloadTypeMapping.getKey(), - mediaType, encoding, clockRate, channels)) + mediaType, encoding, clockRate, channels, formatParameters)) { payloadType = payloadTypeMapping.getValue(); break; @@ -121,8 +122,9 @@ public byte obtainPayloadTypeNumber(MediaFormat format) */ private Byte getPreferredDynamicPayloadType(MediaFormat format) { - return ProtocolMediaActivator.getMediaService() - .getDynamicPayloadTypePreferences().get(format); + return + ProtocolMediaActivator.getMediaService() + .getDynamicPayloadTypePreferences().get(format); } /** @@ -147,16 +149,18 @@ public void addMapping(MediaFormat format, byte payloadType) if(alreadyMappedFmt != null) { - throw new IllegalArgumentException(payloadType - + " has already been allocated to " + alreadyMappedFmt); + throw new IllegalArgumentException( + payloadType + " has already been allocated to " + + alreadyMappedFmt); } if( payloadType < MediaFormat.MIN_DYNAMIC_PAYLOAD_TYPE) { - throw new IllegalArgumentException(payloadType - + " is not a valid dynamic payload type number." - + " (must be between " + MediaFormat.MIN_DYNAMIC_PAYLOAD_TYPE - + " and " + MediaFormat.MAX_DYNAMIC_PAYLOAD_TYPE); + throw new IllegalArgumentException( + payloadType + " is not a valid dynamic payload type number." + + " (must be between " + + MediaFormat.MIN_DYNAMIC_PAYLOAD_TYPE + " and " + + MediaFormat.MAX_DYNAMIC_PAYLOAD_TYPE); } payloadTypeMappings.put(format, Byte.valueOf(payloadType)); @@ -203,19 +207,21 @@ private byte nextPayloadTypeNumber() if (nextDynamicPayloadType < 0) { throw new IllegalStateException( - "Impossible to allocate more than the already 32 mapped " - +"dynamic payload type numbers"); + "Impossible to allocate more than the already 32 " + + "mapped dynamic payload type numbers"); } byte payloadType = nextDynamicPayloadType++; - if(findFormat(payloadType) == null - && findFormatWithPreference(payloadType) == null) + if ((findFormat(payloadType) == null) + && (findFormatWithPreference(payloadType) == null)) return payloadType; - //if we get here then that means that the number we obtained by - //incrementing our PT counter was already occupied (probably by an - //incoming SDP). continue bravely and get the next free one. + /* + * If we get here, then that means that the number we obtained by + * incrementing our PT counter was already occupied (probably by an + * incoming SDP). Continue bravely and get the next free one. + */ } } @@ -234,14 +240,13 @@ && findFormatWithPreference(payloadType) == null) */ private MediaFormat findFormatWithPreference(Byte payloadTypePreference) { - for(Entry entry - : ProtocolMediaActivator.getMediaService() - .getDynamicPayloadTypePreferences().entrySet()) + for(Map.Entry entry + : ProtocolMediaActivator.getMediaService() + .getDynamicPayloadTypePreferences().entrySet()) { if(entry.getValue() == payloadTypePreference) return entry.getKey(); } - return null; } @@ -252,6 +257,6 @@ private MediaFormat findFormatWithPreference(Byte payloadTypePreference) */ public Map getMappings() { - return new Hashtable(payloadTypeMappings); + return new HashMap(payloadTypeMappings); } } diff --git a/src/net/java/sip/communicator/util/launchutils/AEGetURLEventHandler.java b/src/net/java/sip/communicator/util/launchutils/AEGetURLEventHandler.java index 144f2028f..f20b45b04 100644 --- a/src/net/java/sip/communicator/util/launchutils/AEGetURLEventHandler.java +++ b/src/net/java/sip/communicator/util/launchutils/AEGetURLEventHandler.java @@ -49,8 +49,10 @@ public void run() catch(Throwable err) { //we don't have logging here so dump to stderr - System.err.println("Warning: Failed to register our ARG handler."); - System.err.println("We won't be able to handle command line arguments."); + System.err.println( + "Warning: Failed to register our command line argument" + + " handler. We won't be able to handle command line" + + " arguments."); err.printStackTrace(); }