diff --git a/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_FFmpeg.c b/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_FFmpeg.c index c05d3231e..d033b6b39 100644 --- a/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_FFmpeg.c +++ b/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_FFmpeg.c @@ -302,6 +302,7 @@ DEFINE_AVCODECCONTEXT_I_PROPERTY_SETTER(me_1method, me_method) DEFINE_AVCODECCONTEXT_I_PROPERTY_SETTER(me_1range, me_range) DEFINE_AVCODECCONTEXT_I_PROPERTY_SETTER(me_1subpel_1quality, me_subpel_quality) DEFINE_AVCODECCONTEXT_I_PROPERTY_SETTER(pix_1fmt, pix_fmt) +DEFINE_AVCODECCONTEXT_I_PROPERTY_SETTER(profile, profile) DEFINE_AVCODECCONTEXT_F_PROPERTY_SETTER(qcompress, qcompress) diff --git a/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_FFmpeg.h b/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_FFmpeg.h index c95f85c4a..1823d73ff 100644 --- a/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_FFmpeg.h +++ b/src/native/ffmpeg/net_java_sip_communicator_impl_neomedia_codec_FFmpeg.h @@ -311,6 +311,14 @@ JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_codec_FFmpeg JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_codec_FFmpeg_avcodeccontext_1set_1pix_1fmt (JNIEnv *, jclass, jlong, jint); +/* + * Class: net_java_sip_communicator_impl_neomedia_codec_FFmpeg + * Method: avcodeccontext_set_profile + * Signature: (JI)V + */ +JNIEXPORT void JNICALL Java_net_java_sip_communicator_impl_neomedia_codec_FFmpeg_avcodeccontext_1set_1profile + (JNIEnv *, jclass, jlong, jint); + /* * Class: net_java_sip_communicator_impl_neomedia_codec_FFmpeg * Method: avcodeccontext_set_qcompress diff --git a/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java b/src/net/java/sip/communicator/impl/neomedia/VideoMediaStreamImpl.java index fe0a0a0f8..e2af1636a 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.QualityControl; // disambiguation +import net.java.sip.communicator.service.neomedia.control.*; import net.java.sip.communicator.service.neomedia.control.KeyFrameControl; // disambiguation import net.java.sip.communicator.service.neomedia.device.*; import net.java.sip.communicator.service.neomedia.format.*; @@ -870,7 +871,7 @@ public QualityControl getQualityControl() * @author Lyubomir Marinov */ private class KeyFrameControlImpl - implements KeyFrameControl + extends KeyFrameControlAdapter { } 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 02eab6c72..daca4e1a3 100644 --- a/src/net/java/sip/communicator/impl/neomedia/codec/FFmpeg.java +++ b/src/net/java/sip/communicator/impl/neomedia/codec/FFmpeg.java @@ -96,6 +96,10 @@ public class FFmpeg */ public static final int FF_MIN_BUFFER_SIZE = 16384; + public static final int FF_PROFILE_H264_BASELINE = 66; + + public static final int FF_PROFILE_H264_MAIN = 77; + /** * ARGB format. */ @@ -466,6 +470,9 @@ public static native void avcodeccontext_set_me_subpel_quality(long avctx, public static native void avcodeccontext_set_pix_fmt(long avctx, int pix_fmt); + public static native void avcodeccontext_set_profile(long avctx, + int profile); + public static native void avcodeccontext_set_qcompress(long avctx, float qcompress); 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 e1ea4d8ce..39846ee76 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 @@ -15,6 +15,7 @@ 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.java.sip.communicator.util.*; import net.sf.fmj.media.*; /** @@ -28,6 +29,11 @@ public class JNIEncoder extends AbstractCodec implements RTCPFeedbackListener { + /** + * The logger used by the JNIEncoder class and its instances for + * logging output. + */ + private static final Logger logger = Logger.getLogger(JNIEncoder.class); /** * The frame rate to be assumed by JNIEncoder instance in the @@ -348,9 +354,25 @@ public synchronized void open() FFmpeg.avcodeccontext_set_keyint_min(avctx, 0); if (packetizationMode == 0) - FFmpeg.avcodeccontext_set_rtp_payload_size( - avctx, + { + FFmpeg.avcodeccontext_set_rtp_payload_size(avctx, Packetizer.MAX_PAYLOAD_SIZE); + } + + /* + * XXX We do not currently negotiate the profile so, regardless of the + * many AVCodecContext properties we have set above, force the baseline + * profile which is the default in the absence of negotiation. + */ + try + { + FFmpeg.avcodeccontext_set_profile(avctx, + FFmpeg.FF_PROFILE_H264_BASELINE); + } + catch (UnsatisfiedLinkError ule) + { + logger.warn("The FFmpeg JNI library is out-of-date."); + } if (FFmpeg.avcodec_open(avctx, avcodec) < 0) { diff --git a/src/net/java/sip/communicator/impl/protocol/sip/dtmf/DTMFInfo.java b/src/net/java/sip/communicator/impl/protocol/sip/dtmf/DTMFInfo.java index ace7e6fd6..d071bc5c1 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/dtmf/DTMFInfo.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/dtmf/DTMFInfo.java @@ -12,6 +12,7 @@ import java.util.*; import javax.sip.*; +import javax.sip.header.*; import javax.sip.message.*; import net.java.sip.communicator.impl.protocol.sip.*; @@ -28,11 +29,24 @@ public class DTMFInfo extends MethodProcessorAdapter { /** - * logger for the class + * The Logger used by the DTMFInfo class and its instances + * for logging output. */ private static final Logger logger = Logger.getLogger(DTMFInfo.class); + /** + * The sub-type of the content of the Requests being sent by + * DTMFInfo. + */ + private static final String CONTENT_SUB_TYPE = "dtmf-relay"; + + /** + * The type of the content of the Requests being sent by + * DTMFInfo. + */ + private static final String CONTENT_TYPE = "application"; + /** * Maps call peers and tone and its start time, so we can compute duration. */ @@ -120,9 +134,10 @@ private void sayInfo(CallPeerSipImpl callPeer, callPeer.getDialog(), Request.INFO); //here we add the body - ContentType ct = new ContentType("application", "dtmf-relay"); - String content = "Signal=" + dtmftone.getValue() - + "\r\nDuration=" + duration + "\r\n"; + ContentType ct = new ContentType(CONTENT_TYPE, CONTENT_SUB_TYPE); + String content + = "Signal=" + dtmftone.getValue() + + "\r\nDuration=" + duration + "\r\n"; ContentLength cl = new ContentLength(content.length()); info.setContentLength(cl); @@ -182,24 +197,7 @@ private void sayInfo(CallPeerSipImpl callPeer, , ex); } } -/* - @Override - public boolean processRequest(RequestEvent requestEvent) - { - try - { - requestEvent.getServerTransaction().sendResponse( - pps.getMessageFactory().createResponse( - Response.OK, requestEvent.getRequest())); - } - catch (Exception e) - { - e.printStackTrace(System.err); - return false; - } - return true; - } -*/ + /** * Just look if the DTMF signal was well received, and log it * @@ -211,31 +209,72 @@ public boolean processRequest(RequestEvent requestEvent) @Override public boolean processResponse(ResponseEvent responseEvent) { + boolean processed = false; + if (responseEvent == null) { if (logger.isDebugEnabled()) logger.debug("null responseEvent"); - return false; - } - Response response = responseEvent.getResponse(); - if (response == null) - { - if (logger.isDebugEnabled()) - logger.debug("null response"); - return false; - } - int code = response.getStatusCode(); - if (logger.isDebugEnabled()) - logger.debug("DTMF status code=" + code); - if (code != 200) - { - logger.error("DTMF Send failed :" + code); } else { - if (logger.isDebugEnabled()) - logger.debug("DTMF succeeded"); + Response response = responseEvent.getResponse(); + + if (response == null) + { + if (logger.isDebugEnabled()) + logger.debug("null response"); + } + else + { + // Is it even for us? + ClientTransaction clientTransaction + = responseEvent.getClientTransaction(); + + if (clientTransaction == null) + { + if (logger.isDebugEnabled()) + logger.debug("null clientTransaction"); + } + else + { + Request request = clientTransaction.getRequest(); + + if (request == null) + { + if (logger.isDebugEnabled()) + logger.debug("null request"); + } + else + { + ContentTypeHeader contentTypeHeader + = (ContentTypeHeader) + request.getHeader(ContentTypeHeader.NAME); + + if ((contentTypeHeader != null) + && CONTENT_TYPE.equalsIgnoreCase( + contentTypeHeader.getContentType()) + && CONTENT_SUB_TYPE.equalsIgnoreCase( + contentTypeHeader.getContentSubType())) + { + processed = true; + + int statusCode = response.getStatusCode(); + + if (statusCode == 200) + { + if (logger.isDebugEnabled()) + logger.debug( + "DTMF send succeeded: " + + statusCode); + } + else + logger.error("DTMF send failed: " + statusCode); + } + } + } + } } - return true; + return processed; } } diff --git a/src/net/java/sip/communicator/service/neomedia/control/KeyFrameControl.java b/src/net/java/sip/communicator/service/neomedia/control/KeyFrameControl.java index 9fda6b737..9cd523b87 100644 --- a/src/net/java/sip/communicator/service/neomedia/control/KeyFrameControl.java +++ b/src/net/java/sip/communicator/service/neomedia/control/KeyFrameControl.java @@ -6,6 +6,8 @@ */ package net.java.sip.communicator.service.neomedia.control; +import java.util.*; + /** * Represents a control over the key frame-related logic of a * VideoMediaStream. @@ -14,4 +16,48 @@ */ public interface KeyFrameControl { + /** + * Adds a KeyFrameRequester to be made available through this + * KeyFrameControl. + * + * @param index the zero-based index at which keyFrameRequester is + * to be added to the list of KeyFrameRequesters made available + * through this KeyFrameControl + * @param keyFrameRequester the KeyFrameRequester to be added to + * this KeyFrameControl so that it is made available through it + */ + public void addKeyFrameRequester( + int index, + KeyFrameRequester keyFrameRequester); + + /** + * Gets the KeyFrameRequesters made available through this + * KeyFrameControl. + * + * @return an unmodifiable list of KeyFrameRequesters made + * available through this KeyFrameControl + */ + public List getKeyFrameRequesters(); + + /** + * Removes a KeyFrameRequester to no longer be made available + * through this KeyFrameControl. + * + * @param keyFrameRequester the KeyFrameRequester to be removed + * from this KeyFrameControl so that it is no longer made available + * through it + * @return true if keyFrameRequester was found in this + * KeyFrameControl; otherwise, false + */ + public boolean removeKeyFrameRequester(KeyFrameRequester keyFrameRequester); + + /** + * Represents a way for a VideoMediaStream to request a key frame + * from its remote peer. + * + * @author Lyubomir Marinov + */ + public interface KeyFrameRequester + { + } } diff --git a/src/net/java/sip/communicator/service/neomedia/control/KeyFrameControlAdapter.java b/src/net/java/sip/communicator/service/neomedia/control/KeyFrameControlAdapter.java new file mode 100644 index 000000000..ab48257ff --- /dev/null +++ b/src/net/java/sip/communicator/service/neomedia/control/KeyFrameControlAdapter.java @@ -0,0 +1,107 @@ +/* + * 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.service.neomedia.control; + +import java.util.*; + +/** + * Provides a default implementation of {@link KeyFrameControl}. + * + * @author Lyubomir Marinov + */ +public class KeyFrameControlAdapter + implements KeyFrameControl +{ + /** + * The KeyFrameRequesters made available by this + * KeyFrameControl. + */ + private List keyFrameRequesters + = new ArrayList(0); + + /** + * An unmodifiable view of {@link #keyFrameRequesters} appropriate to be + * returned by {@link #getKeyFrameRequesters()}. + */ + private List unmodifiableKeyFrameRequesters; + + /** + * Implements + * {@link KeyFrameControl#addKeyFrameRequester(int, KeyFrameRequester)}. + * + * {@inheritDoc} + */ + public void addKeyFrameRequester( + int index, + KeyFrameRequester keyFrameRequester) + { + if (keyFrameRequester == null) + throw new NullPointerException("keyFrameRequester"); + synchronized (this) + { + if (!keyFrameRequesters.contains(keyFrameRequester)) + { + List newKeyFrameRequesters + = new ArrayList( + keyFrameRequesters.size() + 1); + + newKeyFrameRequesters.addAll(keyFrameRequesters); + newKeyFrameRequesters.add(index, keyFrameRequester); + + keyFrameRequesters = newKeyFrameRequesters; + unmodifiableKeyFrameRequesters = null; + } + } + } + + /** + * Implements {@link KeyFrameControl#getKeyFrameRequesters()}. + * + * {@inheritDoc} + */ + public List getKeyFrameRequesters() + { + synchronized (this) + { + if (unmodifiableKeyFrameRequesters == null) + { + unmodifiableKeyFrameRequesters + = Collections.unmodifiableList(keyFrameRequesters); + } + return unmodifiableKeyFrameRequesters; + } + } + + /** + * Implements + * {@link KeyFrameControl#removeKeyFrameRequester(KeyFrameRequester)}. + * + * {@inheritDoc} + */ + public boolean removeKeyFrameRequester(KeyFrameRequester keyFrameRequester) + { + synchronized (this) + { + int index = keyFrameRequesters.indexOf(keyFrameRequester); + + if (-1 != index) + { + List newKeyFrameRequesters + = new ArrayList(keyFrameRequesters); + + newKeyFrameRequesters.remove(index); + + keyFrameRequesters = newKeyFrameRequesters; + unmodifiableKeyFrameRequesters = null; + + return true; + } + else + return false; + } + } +}