[3/5] rtpenc: MP4A-LATM payload support

Message ID 1305724910-73938-4-git-send-email-martin@martin.st
State Superseded
Headers show

Commit Message

Martin Storsjö May 18, 2011, 1:21 p.m.
From: Juan Carlos Rodriguez <ing.juancarlosrodriguez@hotmail.com>

---
 libavformat/Makefile      |    1 +
 libavformat/avformat.h    |    1 +
 libavformat/options.c     |    1 +
 libavformat/rtpenc.c      |    5 ++-
 libavformat/rtpenc.h      |    1 +
 libavformat/rtpenc_latm.c |   60 ++++++++++++++++++++++++++++++++++++
 libavformat/sdp.c         |   74 +++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 142 insertions(+), 1 deletions(-)
 create mode 100644 libavformat/rtpenc_latm.c

Comments

Diego Biurrun May 18, 2011, 2:16 p.m. | #1
On Wed, May 18, 2011 at 04:21:48PM +0300, Martin Storsjö wrote:
> From: Juan Carlos Rodriguez <ing.juancarlosrodriguez@hotmail.com>
> 
> --- a/libavformat/rtpenc.h
> +++ b/libavformat/rtpenc.h
> @@ -65,6 +65,7 @@ void ff_rtp_send_data(AVFormatContext *s1, const uint8_t *buf1, int len, int m);
>  void ff_rtp_send_h264(AVFormatContext *s1, const uint8_t *buf1, int size);
>  void ff_rtp_send_h263(AVFormatContext *s1, const uint8_t *buf1, int size);
>  void ff_rtp_send_aac(AVFormatContext *s1, const uint8_t *buff, int size);
> +void ff_rtp_send_latm(AVFormatContext *s1, const uint8_t *buff, int size);
>  void ff_rtp_send_amr(AVFormatContext *s1, const uint8_t *buff, int size);
>  void ff_rtp_send_mpegvideo(AVFormatContext *s1, const uint8_t *buf1, int size);
>  void ff_rtp_send_xiph(AVFormatContext *s1, const uint8_t *buff, int size);

This appears to have previously been in alphabetical order.

> --- /dev/null
> +++ b/libavformat/rtpenc_latm.c
> @@ -0,0 +1,60 @@
> +/*
> + * RTP Packetization of MPEG-4 Audio (RFC 3016)
> + * Copyright (c) 2011 Juan Carlos Rodriguez <ing.juancarlosrodriguez@hotmail.com>
> + *
> + * This file is part of FFmpeg.

Ummm...

> +void ff_rtp_send_latm(AVFormatContext *s1, const uint8_t *buff, int size) {
> +}

New files should be in K&R style, so please move the '{' to the
next line.

> --- a/libavformat/sdp.c
> +++ b/libavformat/sdp.c
> @@ -299,6 +300,69 @@ xiph_fail:
>  
> +static int latm_context2profilelevel(AVCodecContext *c) {
> +}
> +
> +static char *latm_context2config(AVCodecContext *c) {
> +}

ditto

Diego
Martin Storsjö May 18, 2011, 2:22 p.m. | #2
On Wed, 18 May 2011, Diego Biurrun wrote:

> On Wed, May 18, 2011 at 04:21:48PM +0300, Martin Storsjö wrote:
> > From: Juan Carlos Rodriguez <ing.juancarlosrodriguez@hotmail.com>
> > 
> > --- a/libavformat/rtpenc.h
> > +++ b/libavformat/rtpenc.h
> > @@ -65,6 +65,7 @@ void ff_rtp_send_data(AVFormatContext *s1, const uint8_t *buf1, int len, int m);
> >  void ff_rtp_send_h264(AVFormatContext *s1, const uint8_t *buf1, int size);
> >  void ff_rtp_send_h263(AVFormatContext *s1, const uint8_t *buf1, int size);
> >  void ff_rtp_send_aac(AVFormatContext *s1, const uint8_t *buff, int size);
> > +void ff_rtp_send_latm(AVFormatContext *s1, const uint8_t *buff, int size);
> >  void ff_rtp_send_amr(AVFormatContext *s1, const uint8_t *buff, int size);
> >  void ff_rtp_send_mpegvideo(AVFormatContext *s1, const uint8_t *buf1, int size);
> >  void ff_rtp_send_xiph(AVFormatContext *s1, const uint8_t *buff, int size);
> 
> This appears to have previously been in alphabetical order.

Nope, they're just piled up in kinda chronological order. This one 
probably was placed here since it's related to the aac one above it.

> > --- /dev/null
> > +++ b/libavformat/rtpenc_latm.c
> > @@ -0,0 +1,60 @@
> > +/*
> > + * RTP Packetization of MPEG-4 Audio (RFC 3016)
> > + * Copyright (c) 2011 Juan Carlos Rodriguez <ing.juancarlosrodriguez@hotmail.com>
> > + *
> > + * This file is part of FFmpeg.
> 
> Ummm...

Fixed locally

> > +void ff_rtp_send_latm(AVFormatContext *s1, const uint8_t *buff, int size) {
> > +}
> 
> New files should be in K&R style, so please move the '{' to the
> next line.

Fixed

> > --- a/libavformat/sdp.c
> > +++ b/libavformat/sdp.c
> > @@ -299,6 +300,69 @@ xiph_fail:
> >  
> > +static int latm_context2profilelevel(AVCodecContext *c) {
> > +}
> > +
> > +static char *latm_context2config(AVCodecContext *c) {
> > +}
> 
> ditto

Fixed

// Martin

Patch

diff --git a/libavformat/Makefile b/libavformat/Makefile
index ba978af..c2fa8af 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -228,6 +228,7 @@  OBJS-$(CONFIG_RSO_MUXER)                 += rsoenc.o rso.o
 OBJS-$(CONFIG_RPL_DEMUXER)               += rpl.o
 OBJS-$(CONFIG_RTP_MUXER)                 += rtp.o         \
                                             rtpenc_aac.o     \
+                                            rtpenc_latm.o    \
                                             rtpenc_amr.o     \
                                             rtpenc_h263.o    \
                                             rtpenc_mpv.o     \
diff --git a/libavformat/avformat.h b/libavformat/avformat.h
index aca246d..1ca4f4f 100644
--- a/libavformat/avformat.h
+++ b/libavformat/avformat.h
@@ -720,6 +720,7 @@  typedef struct AVFormatContext {
 #define AVFMT_FLAG_NOFILLIN     0x0010 ///< Do not infer any values from other values, just return what is stored in the container
 #define AVFMT_FLAG_NOPARSE      0x0020 ///< Do not use AVParsers, you also must set AVFMT_FLAG_NOFILLIN as the fillin code works on frames and no parsing -> no frames. Also seeking to frames can not work if parsing to find frame boundaries has been disabled
 #define AVFMT_FLAG_RTP_HINT     0x0040 ///< Add RTP hinting to the output file
+#define AVFMT_FLAG_MP4A_LATM    0x0080 ///< Enable RTP MP4A-LATM payload
 
     int loop_input;
 
diff --git a/libavformat/options.c b/libavformat/options.c
index 22807c3..134d447 100644
--- a/libavformat/options.c
+++ b/libavformat/options.c
@@ -50,6 +50,7 @@  static const AVOption options[]={
 {"noparse", "disable AVParsers, this needs nofillin too", 0, FF_OPT_TYPE_CONST, {.dbl = AVFMT_FLAG_NOPARSE }, INT_MIN, INT_MAX, D, "fflags"},
 {"igndts", "ignore dts", 0, FF_OPT_TYPE_CONST, {.dbl = AVFMT_FLAG_IGNDTS }, INT_MIN, INT_MAX, D, "fflags"},
 {"rtphint", "add rtp hinting", 0, FF_OPT_TYPE_CONST, {.dbl = AVFMT_FLAG_RTP_HINT }, INT_MIN, INT_MAX, E, "fflags"},
+{"latm", "enable RTP MP4A-LATM payload", 0, FF_OPT_TYPE_CONST, {.dbl = AVFMT_FLAG_MP4A_LATM }, INT_MIN, INT_MAX, E, "fflags"},
 {"analyzeduration", "how many microseconds are analyzed to estimate duration", OFFSET(max_analyze_duration), FF_OPT_TYPE_INT, {.dbl = 5*AV_TIME_BASE }, 0, INT_MAX, D},
 {"cryptokey", "decryption key", OFFSET(key), FF_OPT_TYPE_BINARY, {.dbl = 0}, 0, 0, D},
 {"indexmem", "max memory used for timestamp index (per stream)", OFFSET(max_index_size), FF_OPT_TYPE_INT, {.dbl = 1<<20 }, 0, INT_MAX, D},
diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c
index 7cedff3..6be1159 100644
--- a/libavformat/rtpenc.c
+++ b/libavformat/rtpenc.c
@@ -404,7 +404,10 @@  static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt)
         ff_rtp_send_mpegvideo(s1, pkt->data, size);
         break;
     case CODEC_ID_AAC:
-        ff_rtp_send_aac(s1, pkt->data, size);
+        if (s1->flags & AVFMT_FLAG_MP4A_LATM)
+            ff_rtp_send_latm(s1, pkt->data, size);
+        else
+            ff_rtp_send_aac(s1, pkt->data, size);
         break;
     case CODEC_ID_AMR_NB:
     case CODEC_ID_AMR_WB:
diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h
index 21c5c31..cd2a221 100644
--- a/libavformat/rtpenc.h
+++ b/libavformat/rtpenc.h
@@ -65,6 +65,7 @@  void ff_rtp_send_data(AVFormatContext *s1, const uint8_t *buf1, int len, int m);
 void ff_rtp_send_h264(AVFormatContext *s1, const uint8_t *buf1, int size);
 void ff_rtp_send_h263(AVFormatContext *s1, const uint8_t *buf1, int size);
 void ff_rtp_send_aac(AVFormatContext *s1, const uint8_t *buff, int size);
+void ff_rtp_send_latm(AVFormatContext *s1, const uint8_t *buff, int size);
 void ff_rtp_send_amr(AVFormatContext *s1, const uint8_t *buff, int size);
 void ff_rtp_send_mpegvideo(AVFormatContext *s1, const uint8_t *buf1, int size);
 void ff_rtp_send_xiph(AVFormatContext *s1, const uint8_t *buff, int size);
diff --git a/libavformat/rtpenc_latm.c b/libavformat/rtpenc_latm.c
new file mode 100644
index 0000000..501fa5d
--- /dev/null
+++ b/libavformat/rtpenc_latm.c
@@ -0,0 +1,60 @@ 
+/*
+ * RTP Packetization of MPEG-4 Audio (RFC 3016)
+ * Copyright (c) 2011 Juan Carlos Rodriguez <ing.juancarlosrodriguez@hotmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "rtpenc.h"
+
+void ff_rtp_send_latm(AVFormatContext *s1, const uint8_t *buff, int size) {
+    /* MP4A-LATM
+     * The RTP payload format specification is described in RFC 3016
+     * The encoding specifications are provided in ISO/IEC 14496-3 */
+
+    RTPMuxContext *s = s1->priv_data;
+    int header_size;
+    int offset = 0;
+    int len    = 0;
+
+    /* skip ADTS header, if present */
+    if ((s1->streams[0]->codec->extradata_size) == 0) {
+        size -= 7;
+        buff += 7;
+    }
+
+    /* PayloadLengthInfo() */
+    header_size = size/0xFF + 1;
+    memset(s->buf, 0xFF, header_size - 1);
+    s->buf[header_size - 1] = size % 0xFF;
+
+    s->timestamp = s->cur_timestamp;
+
+    /* PayloadMux() */
+    while (size > 0) {
+        len   = FFMIN(size, s->max_payload_size - (!offset ? header_size : 0));
+        size -= len;
+        if (!offset) {
+            memcpy(s->buf + header_size, buff, len);
+            ff_rtp_send_data(s1, s->buf, header_size + len, !size);
+        } else {
+            ff_rtp_send_data(s1, buff + offset, len, !size);
+        }
+        offset += len;
+    }
+}
diff --git a/libavformat/sdp.c b/libavformat/sdp.c
index b7a2c37..6961aa1 100644
--- a/libavformat/sdp.c
+++ b/libavformat/sdp.c
@@ -23,6 +23,7 @@ 
 #include "libavutil/base64.h"
 #include "libavutil/parseutils.h"
 #include "libavcodec/xiph.h"
+#include "libavcodec/mpeg4audio.h"
 #include "avformat.h"
 #include "internal.h"
 #include "avc.h"
@@ -299,6 +300,69 @@  xiph_fail:
     return NULL;
 }
 
+static int latm_context2profilelevel(AVCodecContext *c) {
+    /* MP4A-LATM
+     * The RTP payload format specification is described in RFC 3016
+     * The encoding specifications are provided in ISO/IEC 14496-3 */
+
+    int profile_level = 0x2B;
+
+    /* TODO: AAC Profile only supports AAC LC Object Type.
+     * Different Object Types should implement different Profile Levels */
+
+    if (c->sample_rate <= 24000) {
+        if (c->channels <= 2)
+            profile_level = 0x28; // AAC Profile, Level 1
+    } else if (c->sample_rate <= 48000) {
+        if (c->channels <= 2) {
+            profile_level = 0x29; // AAC Profile, Level 2
+        } else if (c->channels <= 5) {
+            profile_level = 0x2A; // AAC Profile, Level 4
+        }
+    } else if (c->sample_rate <= 96000) {
+        if (c->channels <= 5) {
+            profile_level = 0x2B; // AAC Profile, Level 5
+        }
+    }
+
+    return profile_level;
+}
+
+static char *latm_context2config(AVCodecContext *c) {
+    /* MP4A-LATM
+     * The RTP payload format specification is described in RFC 3016
+     * The encoding specifications are provided in ISO/IEC 14496-3 */
+
+    uint8_t config_byte[6];
+    int rate_index;
+    char *config;
+
+    for (rate_index = 0; rate_index < 16; rate_index++)
+        if (ff_mpeg4audio_sample_rates[rate_index] == c->sample_rate)
+            break;
+    if (rate_index == 16) {
+        av_log(c, AV_LOG_ERROR, "Unsupported sample rate\n");
+        return NULL;
+    }
+
+    config_byte[0] = 0x40;
+    config_byte[1] = 0;
+    config_byte[2] = 0x20 | rate_index;
+    config_byte[3] = c->channels << 4;
+    config_byte[4] = 0x3f;
+    config_byte[5] = 0xc0;
+
+    config = av_malloc(6*2+1);
+    if (!config) {
+        av_log(c, AV_LOG_ERROR, "Cannot allocate memory for the config info.\n");
+        return NULL;
+    }
+    ff_data_to_hex(config, config_byte, 6, 1);
+    config[12] = 0;
+
+    return config;
+}
+
 static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, int payload_type, int flags)
 {
     char *config = NULL;
@@ -334,6 +398,15 @@  static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c,
                                      payload_type, config ? config : "");
             break;
         case CODEC_ID_AAC:
+            if (flags & AVFMT_FLAG_MP4A_LATM) {
+                config = latm_context2config(c);
+                if (!config)
+                    return NULL;
+                av_strlcatf(buff, size, "a=rtpmap:%d MP4A-LATM/%d/%d\r\n"
+                                        "a=fmtp:%d profile-level-id=%d;cpresent=0;config=%s\r\n",
+                                         payload_type, c->sample_rate, c->channels,
+                                         payload_type, latm_context2profilelevel(c), config);
+            } else {
             if (c->extradata_size) {
                 config = extradata2config(c);
             } else {
@@ -352,6 +425,7 @@  static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c,
                                     "indexdeltalength=3%s\r\n",
                                      payload_type, c->sample_rate, c->channels,
                                      payload_type, config);
+            }
             break;
         case CODEC_ID_PCM_S16BE:
             if (payload_type >= RTP_PT_PRIVATE)