From 9142c3dbcdb14a893f97a6c31e612e87c5a47acd Mon Sep 17 00:00:00 2001 From: Lyubomir Marinov Date: Thu, 9 Jun 2011 10:57:14 +0000 Subject: [PATCH] Implements packetization-mode=0 from "RTP Payload Format for H.264 Video". --- ...ibavcodec_libx264.c-rtp_payload_size.patch | 11 ++++ .../impl/neomedia/MediaUtils.java | 19 ++++++ .../neomedia/codec/video/h264/JNIEncoder.java | 43 ++++++++++++- .../device/VideoMediaDeviceSession.java | 14 +++++ .../neomedia/format/VideoMediaFormatImpl.java | 60 +++++++++++++++++++ 5 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 src/native/ffmpeg/ffmpeg-02-libavcodec_libx264.c-rtp_payload_size.patch diff --git a/src/native/ffmpeg/ffmpeg-02-libavcodec_libx264.c-rtp_payload_size.patch b/src/native/ffmpeg/ffmpeg-02-libavcodec_libx264.c-rtp_payload_size.patch new file mode 100644 index 000000000..0c249eebd --- /dev/null +++ b/src/native/ffmpeg/ffmpeg-02-libavcodec_libx264.c-rtp_payload_size.patch @@ -0,0 +1,11 @@ +--- a/libavcodec/libx264.c 2011-04-30 17:06:44.304599700 +0300 ++++ b/libavcodec/libx264.c 2011-04-16 05:01:34.391924200 +0300 +@@ -312,6 +312,8 @@ + x4->params.b_sliced_threads = 1; + x4->params.b_vfr_input = 0; + ++ x4->params.i_slice_max_size = avctx->rtp_payload_size; ++ + x4->enc = x264_encoder_open(&x4->params); + if (!x4->enc) + return -1; diff --git a/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java b/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java index 444504d29..83f27cb46 100644 --- a/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java +++ b/src/net/java/sip/communicator/impl/neomedia/MediaUtils.java @@ -193,6 +193,25 @@ public class MediaUtils h264AdvancedAttributes.put("imageattr", createImageAttr(null, res)); // packetization-mode=1 + addMediaFormats( + MediaFormat.RTP_PAYLOAD_TYPE_UNKNOWN, + "H264", + MediaType.VIDEO, + Constants.H264_RTP, + h264FormatParams, + h264AdvancedAttributes); + // packetization-mode=0 + /* + * XXX At the time of this writing, + * EncodingConfiguration#compareEncodingPreferences(MediaFormat, + * MediaFormat) is incomplete and considers two MediaFormats to be equal + * if they have an equal number of format parameters (given that the + * encodings and clock rates are equal, of course). Either fix the + * method in question or don't add a format parameter for packetization + * mode 0 (which is equivalent to having packetization-mode explicitly + * defined as 0 anyway, according to the respective RFC). + */ + h264FormatParams.remove(packetizationMode); addMediaFormats( MediaFormat.RTP_PAYLOAD_TYPE_UNKNOWN, "H264", 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 b45f0bca8..e1ea4d8ce 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 @@ -106,6 +106,16 @@ public class JNIEncoder */ private long lastKeyframeRequestTime = System.currentTimeMillis(); + /** + * The packetization mode to be used for the H.264 RTP payload output by + * this JNIEncoder and the associated packetizer. RFC 3984 "RTP + * Payload Format for H.264 Video" says that "[w]hen the value of + * packetization-mode is equal to 0 or packetization-mode is not present, + * the single NAL mode, as defined in section 6.2 of RFC 3984, MUST be + * used." + */ + private byte packetizationMode = 0; + /** * The raw frame buffer. */ @@ -220,7 +230,7 @@ private Format[] getMatchingOutputFormats(Format in) videoIn.getFrameRate(), ParameterizedVideoFormat.toMap( PACKETIZATION_MODE_FMTP, - "1")) + Integer.toString(packetizationMode))) }; } @@ -337,6 +347,11 @@ public synchronized void open() FFmpeg.avcodeccontext_set_keyint_min(avctx, 0); + if (packetizationMode == 0) + FFmpeg.avcodeccontext_set_rtp_payload_size( + avctx, + Packetizer.MAX_PAYLOAD_SIZE); + if (FFmpeg.avcodec_open(avctx, avcodec) < 0) { throw @@ -550,7 +565,7 @@ public Format setOutputFormat(Format out) fmtps = ((ParameterizedVideoFormat) out).getFormatParameters(); if (fmtps == null) fmtps = new HashMap(); - fmtps.put(PACKETIZATION_MODE_FMTP, "1"); + fmtps.put(PACKETIZATION_MODE_FMTP, Integer.toString(packetizationMode)); outputFormat = new ParameterizedVideoFormat( @@ -564,4 +579,28 @@ public Format setOutputFormat(Format out) // Return the selected outputFormat return outputFormat; } + + /** + * Sets the packetization mode to be used for the H.264 RTP payload output + * by this JNIEncoder and the associated packetizer. + * + * @param packetizationMode the packetization mode to be used for the H.264 + * RTP payload output by this JNIEncoder and the associated + * packetizer + */ + public void setPacketizationMode(String packetizationMode) + { + /* + * RFC 3984 "RTP Payload Format for H.264 Video" says that "[w]hen the + * value of packetization-mode is equal to 0 or packetization-mode is + * not present, the single NAL mode, as defined in section 6.2 of RFC + * 3984, MUST be used." + */ + if ((packetizationMode == null) || "0".equals(packetizationMode)) + this.packetizationMode = 0; + else if ("1".equals(packetizationMode)) + this.packetizationMode = 1; + else + throw new IllegalArgumentException("packetizationMode"); + } } 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 0e6f55cbc..c28f5a364 100644 --- a/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java +++ b/src/net/java/sip/communicator/impl/neomedia/device/VideoMediaDeviceSession.java @@ -8,6 +8,7 @@ import java.awt.*; import java.awt.event.*; +import java.util.*; import javax.media.*; import javax.media.control.*; @@ -1259,6 +1260,19 @@ protected Format setProcessorFormat( { encoder = new JNIEncoder(); + // packetization-mode + { + Map formatParameters + = mediaFormat.getFormatParameters(); + String packetizationMode + = (formatParameters == null) + ? null + : formatParameters.get( + JNIEncoder.PACKETIZATION_MODE_FMTP); + + encoder.setPacketizationMode(packetizationMode); + } + // The H.264 encoder needs to be notified of RTCP feedback message. try { 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 cfbc2bb56..29d2408b1 100644 --- a/src/net/java/sip/communicator/impl/neomedia/format/VideoMediaFormatImpl.java +++ b/src/net/java/sip/communicator/impl/neomedia/format/VideoMediaFormatImpl.java @@ -161,6 +161,12 @@ public boolean equals(Object mediaFormat) /** * {@inheritDoc} + *

+ * Takes into account RFC 3984 "RTP Payload Format for H.264 Video" which + * says that "[w]hen the value of packetization-mode [format parameter] + * is equal to 0 or packetization-mode is not present, the single NAL mode, + * as defined in section 6.2 of RFC 3984, MUST be used." + *

* * @see MediaFormatImpl#formatParametersAreEqual(Map, Map) */ @@ -200,6 +206,32 @@ public static boolean formatParametersAreEqual( String encoding, Map fmtps1, Map fmtps2) { + /* + * RFC 3984 "RTP Payload Format for H.264 Video" says that "[w]hen the + * value of packetization-mode is equal to 0 or packetization-mode is + * not present, the single NAL mode, as defined in section 6.2 of RFC + * 3984, MUST be used." + */ + if ("H264".equalsIgnoreCase(encoding) + || "h264/rtp".equalsIgnoreCase(encoding)) + { + String packetizationMode = "packetization-mode"; + String pm1 = null; + String pm2 = null; + + if (fmtps1 != null) + pm1 = fmtps1.remove(packetizationMode); + if (fmtps2 != null) + pm2 = fmtps2.remove(packetizationMode); + + if (pm1 == null) + pm1 = "0"; + if (pm2 == null) + pm2 = "0"; + if (!pm1.equals(pm2)) + return false; + } + return MediaFormatImpl.formatParametersAreEqual(encoding, fmtps1, fmtps2); } @@ -207,6 +239,11 @@ public static boolean formatParametersAreEqual( /** * Determines whether the format parameters of this MediaFormat * match a specific set of format parameters. + *

+ * VideoMediaFormat reflects the fact that the + * packetization-mode format parameter distinguishes H.264 payload + * types. + *

* * @param fmtps the set of format parameters to match to the format * parameters of this MediaFormat @@ -238,6 +275,29 @@ public static boolean formatParametersMatch( String encoding, Map fmtps1 , Map fmtps2) { + /* + * RFC 3984 "RTP Payload Format for H.264 Video" says that "[w]hen the + * value of packetization-mode is equal to 0 or packetization-mode is + * not present, the single NAL mode, as defined in section 6.2 of RFC + * 3984, MUST be used." + */ + if ("H264".equalsIgnoreCase(encoding) + || "h264/rtp".equalsIgnoreCase(encoding)) + { + String packetizationMode = "packetization-mode"; + String pm1 + = (fmtps1 == null) ? null : fmtps1.get(packetizationMode); + String pm2 + = (fmtps2 == null) ? null : fmtps2.get(packetizationMode); + + if (pm1 == null) + pm1 = "0"; + if (pm2 == null) + pm2 = "0"; + if (!pm1.equals(pm2)) + return false; + } + return true; }