aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip
diff options
context:
space:
mode:
authorLyubomir Marinov <lyubomir.marinov@jitsi.org>2011-06-17 15:50:03 +0000
committerLyubomir Marinov <lyubomir.marinov@jitsi.org>2011-06-17 15:50:03 +0000
commit5744ceede0c7cf4ac9f8992b9643976e148f74b8 (patch)
treed45d14fbcdc530e4ead825aed0d76ed1c0fb7147 /src/net/java/sip
parent88bab9eb924413f995f4f4a0d55fa45b92929d65 (diff)
downloadjitsi-5744ceede0c7cf4ac9f8992b9643976e148f74b8.zip
jitsi-5744ceede0c7cf4ac9f8992b9643976e148f74b8.tar.gz
jitsi-5744ceede0c7cf4ac9f8992b9643976e148f74b8.tar.bz2
Responds to INFO requests for key frames.
Diffstat (limited to 'src/net/java/sip')
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/codec/video/h264/JNIEncoder.java87
-rw-r--r--src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java2
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java85
-rw-r--r--src/net/java/sip/communicator/impl/protocol/sip/OperationSetVideoTelephonySipImpl.java127
-rw-r--r--src/net/java/sip/communicator/service/neomedia/control/KeyFrameControl.java66
-rw-r--r--src/net/java/sip/communicator/service/neomedia/control/KeyFrameControlAdapter.java129
-rw-r--r--src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java17
7 files changed, 472 insertions, 41 deletions
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 bb894f4..48fcf99 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
@@ -16,6 +16,7 @@ import net.java.sip.communicator.impl.neomedia.*;
import net.java.sip.communicator.impl.neomedia.codec.*;
import net.java.sip.communicator.impl.neomedia.format.*;
import net.java.sip.communicator.service.configuration.*;
+import net.java.sip.communicator.service.neomedia.control.*;
import net.java.sip.communicator.service.neomedia.event.*;
import net.java.sip.communicator.util.*;
import net.sf.fmj.media.*;
@@ -138,9 +139,18 @@ public class JNIEncoder
private int framesSinceLastIFrame = IFRAME_INTERVAL + 1;
/**
- * Last keyframe request time.
+ * The <tt>KeyFrameControl</tt> used by this <tt>JNIEncoder</tt> to
+ * control its key frame-related logic.
*/
- private long lastKeyframeRequestTime = System.currentTimeMillis();
+ private KeyFrameControl keyFrameControl;
+
+ private KeyFrameControl.KeyFrameRequestee keyFrameRequestee;
+
+ /**
+ * The time in milliseconds of the last request for a key frame from the
+ * remote peer to this local peer.
+ */
+ private long lastKeyFrameRequestTime = System.currentTimeMillis();
/**
* The packetization mode to be used for the H.264 RTP payload output by
@@ -210,6 +220,13 @@ public class JNIEncoder
rawFrameBuffer = 0;
encFrameBuffer = null;
+
+ if (keyFrameRequestee != null)
+ {
+ if (keyFrameControl != null)
+ keyFrameControl.removeKeyFrameRequestee(keyFrameRequestee);
+ keyFrameRequestee = null;
+ }
}
}
@@ -231,13 +248,7 @@ public class JNIEncoder
{
case RTCPFeedbackEvent.FMT_PLI:
case RTCPFeedbackEvent.FMT_FIR:
- if (System.currentTimeMillis()
- > (lastKeyframeRequestTime + PLI_INTERVAL))
- {
- lastKeyframeRequestTime = System.currentTimeMillis();
- // Disable PLI.
- //forceKeyFrame = true;
- }
+ keyFrameRequest();
break;
default:
break;
@@ -303,6 +314,24 @@ public class JNIEncoder
}
/**
+ * Notifies this <tt>JNIEncoder</tt> that the remote peer has requested a
+ * key frame from this local peer.
+ *
+ * @return <tt>true</tt> if this <tt>JNIEncoder</tt> has honored the request
+ * for a key frame; otherwise, <tt>false</tt>
+ */
+ private boolean keyFrameRequest()
+ {
+ if (System.currentTimeMillis()
+ > (lastKeyFrameRequestTime + PLI_INTERVAL))
+ {
+ lastKeyFrameRequestTime = System.currentTimeMillis();
+ forceKeyFrame = true;
+ }
+ return true;
+ }
+
+ /**
* Opens this <tt>Codec</tt>.
*/
@Override
@@ -439,6 +468,23 @@ public class JNIEncoder
encFrameBuffer = new byte[encFrameLen];
+ /*
+ * Implement the ability to have the remote peer request key frames from
+ * this local peer.
+ */
+ if (keyFrameRequestee == null)
+ {
+ keyFrameRequestee = new KeyFrameControl.KeyFrameRequestee()
+ {
+ public boolean keyFrameRequest()
+ {
+ return JNIEncoder.this.keyFrameRequest();
+ }
+ };
+ }
+ if (keyFrameControl != null)
+ keyFrameControl.addKeyFrameRequestee(-1, keyFrameRequestee);
+
opened = true;
super.open();
}
@@ -586,6 +632,29 @@ public class JNIEncoder
}
/**
+ * Sets the <tt>KeyFrameControl</tt> to be used by this
+ * <tt>JNIEncoder</tt> as a means of control over its key frame-related
+ * logic.
+ *
+ * @param keyFrameControl the <tt>KeyFrameControl</tt> to be used by this
+ * <tt>JNIEncoder</tt> as a means of control over its key frame-related
+ * logic
+ */
+ public void setKeyFrameControl(KeyFrameControl keyFrameControl)
+ {
+ if (this.keyFrameControl != keyFrameControl)
+ {
+ if ((this.keyFrameControl != null) && (keyFrameRequestee != null))
+ this.keyFrameControl.removeKeyFrameRequestee(keyFrameRequestee);
+
+ this.keyFrameControl = keyFrameControl;
+
+ if ((this.keyFrameControl != null) && (keyFrameRequestee != null))
+ this.keyFrameControl.addKeyFrameRequestee(-1, keyFrameRequestee);
+ }
+ }
+
+ /**
* Sets the <tt>Format</tt> in which this <tt>Codec</tt> is to output media
* data.
*
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 2b78d93..8d459a1 100644
--- a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java
+++ b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java
@@ -1369,6 +1369,8 @@ public class VideoMediaDeviceSession
logger.error("Error cannot get RTCP input stream", ioe);
}
}
+ if (keyFrameControl != null)
+ encoder.setKeyFrameControl(keyFrameControl);
codecCount++;
}
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 e55b9cf..8cf3754 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerSipImpl.java
@@ -338,24 +338,89 @@ public class CallPeerSipImpl
return getMediaHandler().getCallInfoURL();
}
- /**
- * Processes the status code of a <tt>Response<tt> to a
- * <tt>picture_fast_update</tt> SIP INFO <tt>Request</tt> sent by the local
- * peer to this remote peer.
- *
- * @param statusCode the status code of the <tt>Response</tt> to be
- * processed
- */
- void processPictureFastUpdateResponseStatusCode(int statusCode)
+ void processPictureFastUpdate(
+ ClientTransaction clientTransaction,
+ Response response)
{
/*
* Disable the sending of picture_fast_update because it seems to be
* unsupported by this remote peer.
*/
- if ((statusCode != 200) && sendPictureFastUpdate)
+ if ((response.getStatusCode() != 200) && sendPictureFastUpdate)
sendPictureFastUpdate = false;
}
+ boolean processPictureFastUpdate(
+ ServerTransaction serverTransaction,
+ Request request)
+ throws OperationFailedException
+ {
+ System.err.println("processPictureFastUpdate Request");
+ CallPeerMediaHandlerSipImpl mediaHandler = getMediaHandler();
+ boolean requested
+ = (mediaHandler == null)
+ ? false
+ : mediaHandler.processKeyFrameRequest();
+
+ Response response;
+
+ try
+ {
+ response
+ = getProtocolProvider().getMessageFactory().createResponse(
+ Response.OK,
+ request);
+ }
+ catch (ParseException pe)
+ {
+ throw new OperationFailedException(
+ "Failed to create OK Response.",
+ OperationFailedException.INTERNAL_ERROR,
+ pe);
+ }
+
+ if (!requested)
+ {
+ ContentType ct
+ = new ContentType(
+ "application",
+ PICTURE_FAST_UPDATE_CONTENT_SUB_TYPE);
+ String content
+ = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n"
+ + "<media_control>\r\n"
+ + "<general_error>\r\n"
+ + "Failed to process picture_fast_update request.\r\n"
+ + "</general_error>\r\n"
+ + "</media_control>";
+
+ try
+ {
+ response.setContent(content, ct);
+ }
+ catch (ParseException pe)
+ {
+ throw new OperationFailedException(
+ "Failed to set content of OK Response.",
+ OperationFailedException.INTERNAL_ERROR,
+ pe);
+ }
+ }
+
+ try
+ {
+ serverTransaction.sendResponse(response);
+ }
+ catch (Exception e)
+ {
+ throw new OperationFailedException(
+ "Failed to send OK Response.",
+ OperationFailedException.INTERNAL_ERROR,
+ e);
+ }
+
+ return true;
+ }
+
/**
* Reinitializes the media session of the <tt>CallPeer</tt> that this
* INVITE request is destined to.
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetVideoTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetVideoTelephonySipImpl.java
index 967a88d..f188849 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetVideoTelephonySipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetVideoTelephonySipImpl.java
@@ -6,6 +6,7 @@
*/
package net.java.sip.communicator.impl.protocol.sip;
+import java.io.*;
import java.text.*;
import javax.sip.*;
@@ -240,6 +241,106 @@ public class OperationSetVideoTelephonySipImpl
extends MethodProcessorAdapter
{
/**
+ * Determines whether a specific <tt>Request</tt> is a
+ * <tt>picture_fast_update</tt> one.
+ *
+ * @param request the <tt>Request</tt> to check
+ * @return <tt>true</tt> if the specified <tt>request</tt> is a
+ * <tt>picture_fast_update</tt> one; otherwise, <tt>false</tt>
+ */
+ private boolean isPictureFastUpdate(Request request)
+ {
+ ContentTypeHeader contentTypeHeader
+ = (ContentTypeHeader) request.getHeader(ContentTypeHeader.NAME);
+ if (contentTypeHeader == null)
+ return false;
+ if (!"application".equalsIgnoreCase(
+ contentTypeHeader.getContentType()))
+ return false;
+ if (!CallPeerSipImpl.PICTURE_FAST_UPDATE_CONTENT_SUB_TYPE
+ .equalsIgnoreCase(
+ contentTypeHeader.getContentSubType()))
+ return false;
+
+ Object content = request.getContent();
+ if (content == null)
+ return false;
+
+ String xml;
+ if (content instanceof String)
+ xml = content.toString();
+ else if (content instanceof byte[])
+ {
+ byte[] bytes = (byte[]) content;
+ try
+ {
+ xml = new String(bytes, "UTF-8");
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ xml = new String(bytes);
+ }
+ }
+ else
+ return false;
+ if (xml == null)
+ return false;
+ return xml.contains("picture_fast_update");
+ }
+
+ /**
+ * Delivers <tt>picture_fast_update</tt> <tt>Request</tt>s to the
+ * targeted <tt>CallPeerSipImpl</tt>.
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean processRequest(RequestEvent requestEvent)
+ {
+ if (requestEvent == null)
+ return false;
+
+ Request request = requestEvent.getRequest();
+ if (request == null)
+ return false;
+ if (!isPictureFastUpdate(request))
+ return false;
+
+ ServerTransaction serverTransaction;
+ try
+ {
+ serverTransaction
+ = SipStackSharing.getOrCreateServerTransaction(
+ requestEvent);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace(System.err);
+ return false;
+ }
+ if (serverTransaction == null)
+ return false;
+
+ CallPeerSipImpl callPeer
+ = basicTelephony.getActiveCallsRepository().findCallPeer(
+ serverTransaction.getDialog());
+ if (callPeer == null)
+ return false;
+
+ try
+ {
+ return
+ callPeer.processPictureFastUpdate(
+ serverTransaction,
+ request);
+ }
+ catch (OperationFailedException ofe)
+ {
+ return false;
+ }
+ }
+
+ /**
* Delivers <tt>Response</tt>s to <tt>picture_fast_update</tt>
* <tt>Request</tt>s to the originating <tt>CallPeerSipImpl</tt>.
*
@@ -252,46 +353,28 @@ public class OperationSetVideoTelephonySipImpl
return false;
Response response = responseEvent.getResponse();
-
if (response == null)
return false;
ClientTransaction clientTransaction
= responseEvent.getClientTransaction();
-
if (clientTransaction == null)
return false;
Request request = clientTransaction.getRequest();
-
if (request == null)
return false;
-
- ContentTypeHeader contentTypeHeader
- = (ContentTypeHeader) request.getHeader(ContentTypeHeader.NAME);
-
- if (contentTypeHeader == null)
- return false;
- if (!"application".equalsIgnoreCase(
- contentTypeHeader.getContentType()))
- return false;
- if (!CallPeerSipImpl.PICTURE_FAST_UPDATE_CONTENT_SUB_TYPE
- .equalsIgnoreCase(
- contentTypeHeader.getContentSubType()))
+ if (!isPictureFastUpdate(request))
return false;
CallPeerSipImpl callPeer
= basicTelephony.getActiveCallsRepository().findCallPeer(
clientTransaction.getDialog());
-
if (callPeer == null)
return false;
- else
- {
- callPeer.processPictureFastUpdateResponseStatusCode(
- response.getStatusCode());
- return true;
- }
+
+ callPeer.processPictureFastUpdate(clientTransaction, response);
+ return true;
}
}
}
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 7da13ba..65554e8 100644
--- a/src/net/java/sip/communicator/service/neomedia/control/KeyFrameControl.java
+++ b/src/net/java/sip/communicator/service/neomedia/control/KeyFrameControl.java
@@ -17,6 +17,22 @@ import java.util.*;
public interface KeyFrameControl
{
/**
+ * Adds a <tt>KeyFrameRequestee</tt> to be made available through this
+ * <tt>KeyFrameControl</tt>.
+ *
+ * @param index the zero-based index at which <tt>keyFrameRequestee</tt> is
+ * to be added to the list of <tt>KeyFrameRequestee</tt>s made available or
+ * <tt>-1</tt> to have this <tt>KeyFrameControl</tt> choose at which index
+ * it is to be added in accord with its internal logic
+ * through this <tt>KeyFrameControl</tt>
+ * @param keyFrameRequestee the <tt>KeyFrameRequestee</tt> to be added to
+ * this <tt>KeyFrameControl</tt> so that it is made available through it
+ */
+ public void addKeyFrameRequestee(
+ int index,
+ KeyFrameRequestee keyFrameRequestee);
+
+ /**
* Adds a <tt>KeyFrameRequester</tt> to be made available through this
* <tt>KeyFrameControl</tt>.
*
@@ -33,6 +49,15 @@ public interface KeyFrameControl
KeyFrameRequester keyFrameRequester);
/**
+ * Gets the <tt>KeyFrameRequestee</tt>s made available through this
+ * <tt>KeyFrameControl</tt>.
+ *
+ * @return an unmodifiable list of <tt>KeyFrameRequestee</tt>s made
+ * available through this <tt>KeyFrameControl</tt>
+ */
+ public List<KeyFrameRequestee> getKeyFrameRequestees();
+
+ /**
* Gets the <tt>KeyFrameRequester</tt>s made available through this
* <tt>KeyFrameControl</tt>.
*
@@ -42,6 +67,28 @@ public interface KeyFrameControl
public List<KeyFrameRequester> getKeyFrameRequesters();
/**
+ * Notifies this <tt>KeyFrameControl</tt> that the remote peer of the
+ * associated <tt>VideoMediaStream</tt> has requested a key frame from the
+ * local peer.
+ *
+ * @return <tt>true</tt> if the local peer has honored the request from the
+ * remote peer for a key frame; otherwise, <tt>false</tt>
+ */
+ public boolean keyFrameRequest();
+
+ /**
+ * Removes a <tt>KeyFrameRequestee</tt> to no longer be made available
+ * through this <tt>KeyFrameControl</tt>.
+ *
+ * @param keyFrameRequestee the <tt>KeyFrameRequestee</tt> to be removed
+ * from this <tt>KeyFrameControl</tt> so that it is no longer made available
+ * through it
+ * @return <tt>true</tt> if <tt>keyFrameRequestee</tt> was found in this
+ * <tt>KeyFrameControl</tt>; otherwise, <tt>false</tt>
+ */
+ public boolean removeKeyFrameRequestee(KeyFrameRequestee keyFrameRequestee);
+
+ /**
* Removes a <tt>KeyFrameRequester</tt> to no longer be made available
* through this <tt>KeyFrameControl</tt>.
*
@@ -54,6 +101,25 @@ public interface KeyFrameControl
public boolean removeKeyFrameRequester(KeyFrameRequester keyFrameRequester);
/**
+ * Represents a way for the remote peer of a <tt>VideoMediaStream</tt> to
+ * request a key frame from its local peer.
+ *
+ * @author Lyubomir Marinov
+ */
+ public interface KeyFrameRequestee
+ {
+ /**
+ * Notifies this <tt>KeyFrameRequestee</tt> that the remote peer of the
+ * associated <tt>VideoMediaStream</tt> requests a key frame from the
+ * local peer.
+ *
+ * @return <tt>true</tt> if this <tt>KeyFrameRequestee</tt> has honored
+ * the request for a key frame; otherwise, <tt>false</tt>
+ */
+ public boolean keyFrameRequest();
+ }
+
+ /**
* Represents a way for a <tt>VideoMediaStream</tt> to request a key frame
* from its remote peer.
*
diff --git a/src/net/java/sip/communicator/service/neomedia/control/KeyFrameControlAdapter.java b/src/net/java/sip/communicator/service/neomedia/control/KeyFrameControlAdapter.java
index aa595a0..50449aa 100644
--- a/src/net/java/sip/communicator/service/neomedia/control/KeyFrameControlAdapter.java
+++ b/src/net/java/sip/communicator/service/neomedia/control/KeyFrameControlAdapter.java
@@ -17,6 +17,13 @@ public class KeyFrameControlAdapter
implements KeyFrameControl
{
/**
+ * The <tt>KeyFrameRequestee</tt>s made available by this
+ * <tt>KeyFrameControl</tt>.
+ */
+ private List<KeyFrameRequestee> keyFrameRequestees
+ = new ArrayList<KeyFrameRequestee>(0);
+
+ /**
* The <tt>KeyFrameRequester</tt>s made available by this
* <tt>KeyFrameControl</tt>.
*/
@@ -24,6 +31,12 @@ public class KeyFrameControlAdapter
= new ArrayList<KeyFrameRequester>(0);
/**
+ * An unmodifiable view of {@link #keyFrameRequestees} appropriate to be
+ * returned by {@link #getKeyFrameRequestees()}.
+ */
+ private List<KeyFrameRequestee> unmodifiableKeyFrameRequestees;
+
+ /**
* An unmodifiable view of {@link #keyFrameRequesters} appropriate to be
* returned by {@link #getKeyFrameRequesters()}.
*/
@@ -31,6 +44,50 @@ public class KeyFrameControlAdapter
/**
* Implements
+ * {@link KeyFrameControl#addKeyFrameRequestee(int, KeyFrameRequestee)}.
+ *
+ * {@inheritDoc}
+ */
+ public void addKeyFrameRequestee(
+ int index,
+ KeyFrameRequestee keyFrameRequestee)
+ {
+ if (keyFrameRequestee == null)
+ throw new NullPointerException("keyFrameRequestee");
+ synchronized (this)
+ {
+ if (!keyFrameRequestees.contains(keyFrameRequestee))
+ {
+ List<KeyFrameRequestee> newKeyFrameRequestees
+ = new ArrayList<KeyFrameRequestee>(
+ keyFrameRequestees.size() + 1);
+
+ newKeyFrameRequestees.addAll(keyFrameRequestees);
+ /*
+ * If this KeyFrameControl is to determine the index at which
+ * keyFrameRequestee is to be added according to its own
+ * internal logic, then it will prefer KeyFrameRequestee
+ * implementations from outside of neomedia rather than from its
+ * inside.
+ */
+ if (-1 == index)
+ {
+ if (keyFrameRequestee.getClass().getName().contains(
+ ".neomedia."))
+ index = newKeyFrameRequestees.size();
+ else
+ index = 0;
+ }
+ newKeyFrameRequestees.add(index, keyFrameRequestee);
+
+ keyFrameRequestees = newKeyFrameRequestees;
+ unmodifiableKeyFrameRequestees = null;
+ }
+ }
+ }
+
+ /**
+ * Implements
* {@link KeyFrameControl#addKeyFrameRequester(int, KeyFrameRequester)}.
*
* {@inheritDoc}
@@ -74,6 +131,24 @@ public class KeyFrameControlAdapter
}
/**
+ * Implements {@link KeyFrameControl#getKeyFrameRequestees()}.
+ *
+ * {@inheritDoc}
+ */
+ public List<KeyFrameRequestee> getKeyFrameRequestees()
+ {
+ synchronized (this)
+ {
+ if (unmodifiableKeyFrameRequestees == null)
+ {
+ unmodifiableKeyFrameRequestees
+ = Collections.unmodifiableList(keyFrameRequestees);
+ }
+ return unmodifiableKeyFrameRequestees;
+ }
+ }
+
+ /**
* Implements {@link KeyFrameControl#getKeyFrameRequesters()}.
*
* {@inheritDoc}
@@ -92,6 +167,60 @@ public class KeyFrameControlAdapter
}
/**
+ * Implements {@link KeyFrameControl#keyFrameRequest()}.
+ *
+ * {@inheritDoc}
+ */
+ public boolean keyFrameRequest()
+ {
+ for (KeyFrameRequestee keyFrameRequestee : getKeyFrameRequestees())
+ {
+ try
+ {
+ if (keyFrameRequestee.keyFrameRequest())
+ return true;
+ }
+ catch (Exception e)
+ {
+ /*
+ * A KeyFrameRequestee has malfunctioned, do not let it
+ * interfere with the others.
+ */
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Implements
+ * {@link KeyFrameControl#removeKeyFrameRequestee(KeyFrameRequestee)}.
+ *
+ * {@inheritDoc}
+ */
+ public boolean removeKeyFrameRequestee(KeyFrameRequestee keyFrameRequestee)
+ {
+ synchronized (this)
+ {
+ int index = keyFrameRequestees.indexOf(keyFrameRequestee);
+
+ if (-1 != index)
+ {
+ List<KeyFrameRequestee> newKeyFrameRequestees
+ = new ArrayList<KeyFrameRequestee>(keyFrameRequestees);
+
+ newKeyFrameRequestees.remove(index);
+
+ keyFrameRequestees = newKeyFrameRequestees;
+ unmodifiableKeyFrameRequestees = null;
+
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+
+ /**
* Implements
* {@link KeyFrameControl#removeKeyFrameRequester(KeyFrameRequester)}.
*
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 f7a881d..98cc3d6 100644
--- a/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java
+++ b/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java
@@ -1313,6 +1313,23 @@ public abstract class CallPeerMediaHandler<
}
/**
+ * Processes a request for a (video) key frame from the remote peer to the
+ * local peer.
+ *
+ * @return <tt>true</tt> if the request for a (video) key frame has been
+ * honored by the local peer; otherwise, <tt>false</tt>
+ */
+ public boolean processKeyFrameRequest()
+ {
+ KeyFrameControl keyFrameControl = this.keyFrameControl;
+
+ return
+ (keyFrameControl == null)
+ ? null
+ : keyFrameControl.keyFrameRequest();
+ }
+
+ /**
* Registers all audio level listeners currently known to this media handler
* with the specified <tt>audioStream</tt>.
*