[4/4] rtpenc: Support muxing data in mpegts before packetizing

Message ID 1418765355-84643-4-git-send-email-martin@martin.st
State Deferred
Headers show

Commit Message

Martin Storsjö Dec. 16, 2014, 9:29 p.m.
This is an alternative way of achieving the same. This does
require a bit of hacks in both the SDP layer and in the rtp
muxer itself though. It is more straightforward in the sense
that it all is contained within the RTP muxer, but other than
that, it feels a bit hacky.

Opinions on which one is preffered are welcome.
---
 libavformat/rtpenc.c | 128 ++++++++++++++++++++++++++++++++++++++++++++-------
 libavformat/rtpenc.h |   3 ++
 libavformat/sdp.c    |  13 +++++-
 3 files changed, 125 insertions(+), 19 deletions(-)

Patch

diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c
index dafe3a0..be9eabc 100644
--- a/libavformat/rtpenc.c
+++ b/libavformat/rtpenc.c
@@ -30,6 +30,7 @@ 
 
 static const AVOption options[] = {
     FF_RTP_FLAG_OPTS(RTPMuxContext, flags),
+    { "mpegts", "Mux an mpegts stream inside RTP", 0, AV_OPT_TYPE_CONST, {.i64 = FF_RTP_FLAG_MPEGTS}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "rtpflags" },
     { "payload_type", "Specify RTP payload type", offsetof(RTPMuxContext, payload_type), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 127, AV_OPT_FLAG_ENCODING_PARAM },
     { "ssrc", "Stream identifier", offsetof(RTPMuxContext, ssrc), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
     { "cname", "CNAME to include in RTCP SR packets", offsetof(RTPMuxContext, cname), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
@@ -85,21 +86,81 @@  static int is_supported(enum AVCodecID id)
     }
 }
 
+static void rtp_free(RTPMuxContext *s)
+{
+    if (s->mpegts_ctx) {
+        if (!s->mpegts_ctx->pb)
+            avio_open_dyn_buf(&s->mpegts_ctx->pb);
+        if (s->mpegts_ctx->pb) {
+            uint8_t *buf;
+            av_write_trailer(s->mpegts_ctx);
+            avio_close_dyn_buf(s->mpegts_ctx->pb, &buf);
+            av_free(buf);
+        }
+        avformat_free_context(s->mpegts_ctx);
+    }
+
+    av_freep(&s->buf);
+}
+
+static void init_mpegts_packetization(RTPMuxContext *s)
+{
+    int n = s->max_payload_size / TS_PACKET_SIZE;
+    if (n < 1)
+        n = 1;
+    s->max_payload_size = n * TS_PACKET_SIZE;
+    s->buf_ptr = s->buf;
+}
+
 static int rtp_write_header(AVFormatContext *s1)
 {
     RTPMuxContext *s = s1->priv_data;
-    int n;
+    int n, ret = AVERROR(EINVAL);
     AVStream *st;
 
-    if (s1->nb_streams != 1) {
-        av_log(s1, AV_LOG_ERROR, "Only one stream supported in the RTP muxer\n");
-        return AVERROR(EINVAL);
-    }
-    st = s1->streams[0];
-    if (!is_supported(st->codec->codec_id)) {
-        av_log(s1, AV_LOG_ERROR, "Unsupported codec %x\n", st->codec->codec_id);
+    if (s->flags & FF_RTP_FLAG_MPEGTS) {
+        int i;
+        AVOutputFormat *mpegts_format = av_guess_format("mpegts", NULL, NULL);
+        if (s1->nb_streams < 1) {
+            av_log(s1, AV_LOG_ERROR, "At least one stream required\n");
+            return AVERROR(EINVAL);
+        }
+        if (!mpegts_format)
+            return AVERROR(ENOSYS);
+        s->mpegts_ctx = avformat_alloc_context();
+        if (!s->mpegts_ctx)
+            return AVERROR(ENOMEM);
+        s->mpegts_ctx->oformat = mpegts_format;
+        s->mpegts_ctx->max_delay = s1->max_delay;
+
+        for (i = 0; i < s1->nb_streams; i++) {
+            AVStream* st = avformat_new_stream(s->mpegts_ctx, NULL);
+            if (!st)
+                goto fail;
+            st->time_base = s1->streams[i]->time_base;
+            st->sample_aspect_ratio = s1->streams[i]->sample_aspect_ratio;
+            avcodec_copy_context(st->codec, s1->streams[i]->codec);
+        }
+        if ((ret = avio_open_dyn_buf(&s->mpegts_ctx->pb)) < 0)
+            goto fail;
+        if ((ret = avformat_write_header(s->mpegts_ctx, NULL)) < 0)
+            goto fail;
+        for (i = 0; i < s1->nb_streams; i++)
+            s1->streams[i]->time_base = s->mpegts_ctx->streams[i]->time_base;
+
+        s->payload_type = 33;
+        st = s1->streams[0];
+    } else {
+        if (s1->nb_streams != 1) {
+            av_log(s1, AV_LOG_ERROR, "Only one stream supported in the RTP muxer\n");
+            return AVERROR(EINVAL);
+        }
+        st = s1->streams[0];
+        if (!is_supported(st->codec->codec_id)) {
+            av_log(s1, AV_LOG_ERROR, "Unsupported codec %x\n", st->codec->codec_id);
 
-        return -1;
+            return -1;
+        }
     }
 
     if (s->payload_type < 0) {
@@ -148,6 +209,11 @@  static int rtp_write_header(AVFormatContext *s1)
     }
     s->max_payload_size = s1->packet_size - 12;
 
+    if (s->flags & FF_RTP_FLAG_MPEGTS) {
+        init_mpegts_packetization(s);
+        return 0;
+    }
+
     s->max_frames_per_packet = 0;
     if (s1->max_delay > 0) {
         if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
@@ -185,11 +251,7 @@  static int rtp_write_header(AVFormatContext *s1)
     case AV_CODEC_ID_MPEG2VIDEO:
         break;
     case AV_CODEC_ID_MPEG2TS:
-        n = s->max_payload_size / TS_PACKET_SIZE;
-        if (n < 1)
-            n = 1;
-        s->max_payload_size = n * TS_PACKET_SIZE;
-        s->buf_ptr = s->buf;
+        init_mpegts_packetization(s);
         break;
     case AV_CODEC_ID_H264:
         /* check for H.264 MP4 syntax */
@@ -269,8 +331,8 @@  defaultcase:
     return 0;
 
 fail:
-    av_freep(&s->buf);
-    return AVERROR(EINVAL);
+    rtp_free(s);
+    return ret;
 }
 
 /* send an rtcp sender report packet */
@@ -506,6 +568,31 @@  static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt)
     AVStream *st = s1->streams[0];
     int rtcp_bytes;
     int size= pkt->size;
+    AVPacket local_pkt;
+
+    if (s->flags & FF_RTP_FLAG_MPEGTS) {
+        int ret;
+        if (!s->mpegts_ctx->pb) {
+            if ((ret = avio_open_dyn_buf(&s->mpegts_ctx->pb)) < 0)
+                return ret;
+        }
+        if ((ret = av_write_frame(s->mpegts_ctx, pkt)) < 0)
+            return ret;
+        av_init_packet(&local_pkt);
+        size = avio_close_dyn_buf(s->mpegts_ctx->pb, &local_pkt.data);
+        s->mpegts_ctx->pb = NULL;
+        if (size == 0) {
+            av_free(local_pkt.data);
+            return 0;
+        }
+
+        local_pkt.size = size;
+        local_pkt.stream_index = 0;
+        local_pkt.pts  = pkt->pts;
+        local_pkt.dts  = pkt->dts;
+
+        pkt = &local_pkt;
+    }
 
     av_dlog(s1, "%d: write len=%d\n", pkt->stream_index, size);
 
@@ -520,6 +607,12 @@  static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt)
     }
     s->cur_timestamp = s->base_timestamp + pkt->pts;
 
+    if (s->flags & FF_RTP_FLAG_MPEGTS) {
+        rtp_send_mpegts_raw(s1, local_pkt.data, local_pkt.size);
+        av_free(local_pkt.data);
+        return 0;
+    }
+
     switch(st->codec->codec_id) {
     case AV_CODEC_ID_PCM_MULAW:
     case AV_CODEC_ID_PCM_ALAW:
@@ -617,7 +710,8 @@  static int rtp_write_trailer(AVFormatContext *s1)
      * be NULL here even if it was successfully allocated at the start. */
     if (s1->pb && (s->flags & FF_RTP_FLAG_SEND_BYE))
         rtcp_send_sr(s1, ff_ntp_time(), 1);
-    av_freep(&s->buf);
+
+    rtp_free(s);
 
     return 0;
 }
diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h
index 4a72a49..88d75fc 100644
--- a/libavformat/rtpenc.h
+++ b/libavformat/rtpenc.h
@@ -60,6 +60,8 @@  struct RTPMuxContext {
     int flags;
 
     unsigned int frame_count;
+
+    AVFormatContext *mpegts_ctx;
 };
 
 typedef struct RTPMuxContext RTPMuxContext;
@@ -69,6 +71,7 @@  typedef struct RTPMuxContext RTPMuxContext;
 #define FF_RTP_FLAG_SKIP_RTCP 4
 #define FF_RTP_FLAG_H264_MODE0 8
 #define FF_RTP_FLAG_SEND_BYE  16
+#define FF_RTP_FLAG_MPEGTS    32
 
 #define FF_RTP_FLAG_OPTS(ctx, fieldname) \
     { "rtpflags", "RTP muxer flags", offsetof(ctx, fieldname), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "rtpflags" }, \
diff --git a/libavformat/sdp.c b/libavformat/sdp.c
index a14a239..0d4cf89 100644
--- a/libavformat/sdp.c
+++ b/libavformat/sdp.c
@@ -698,7 +698,7 @@  void ff_sdp_write_media(char *buff, int size, AVStream *st, int idx,
 {
     AVCodecContext *c = st->codec;
     const char *type;
-    int payload_type;
+    int payload_type, mpegts = 0;
 
     payload_type = ff_rtp_get_payload_type(fmt, c, idx);
 
@@ -708,6 +708,11 @@  void ff_sdp_write_media(char *buff, int size, AVStream *st, int idx,
         case AVMEDIA_TYPE_SUBTITLE: type = "text"       ; break;
         default                 : type = "application"; break;
     }
+    if (fmt && fmt->oformat && fmt->oformat->priv_class &&
+        av_opt_flag_is_set(fmt->priv_data, "rtpflags", "mpegts")) {
+        type = "application";
+        mpegts = 1;
+    }
 
     av_strlcatf(buff, size, "m=%s %d RTP/AVP %d\r\n", type, port, payload_type);
     sdp_write_address(buff, size, dest_addr, dest_type, ttl);
@@ -715,7 +720,8 @@  void ff_sdp_write_media(char *buff, int size, AVStream *st, int idx,
         av_strlcatf(buff, size, "b=AS:%d\r\n", c->bit_rate / 1000);
     }
 
-    sdp_write_media_attributes(buff, size, c, payload_type, fmt);
+    if (!mpegts)
+        sdp_write_media_attributes(buff, size, c, payload_type, fmt);
 }
 
 int av_sdp_create(AVFormatContext *ac[], int n_files, char *buf, int size)
@@ -782,6 +788,9 @@  int av_sdp_create(AVFormatContext *ac[], int n_files, char *buf, int size)
                 av_free(crypto_suite);
                 av_free(crypto_params);
             }
+            if (ac[i] && ac[i]->oformat && ac[i]->oformat->priv_class &&
+                av_opt_flag_is_set(ac[i]->priv_data, "rtpflags", "mpegts"))
+                break;
         }
     }