From: Sebastian Dröge <[email protected]>

Signed-off-by: Sebastian Dröge <[email protected]>
Previous version reviewed-by: Kieran Kunhya <[email protected]>
Signed-off-by: Michael Niedermayer <[email protected]>
---
 libavformat/mpegtsenc.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 179 insertions(+), 1 deletion(-)

diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
index 2522b72..42d903d 100644
--- a/libavformat/mpegtsenc.c
+++ b/libavformat/mpegtsenc.c
@@ -205,6 +205,9 @@ typedef struct MpegTSWriteStream {
     uint8_t *payload;
     AVFormatContext *amux;
     AVRational user_tb;
+
+    /* For Opus */
+    int opus_queued_samples;
 } MpegTSWriteStream;
 
 static void mpegts_write_pat(AVFormatContext *s)
@@ -286,6 +289,9 @@ static void mpegts_write_pmt(AVFormatContext *s, 
MpegTSService *service)
         case AV_CODEC_ID_AC3:
             stream_type = STREAM_TYPE_AUDIO_AC3;
             break;
+        case AV_CODEC_ID_OPUS:
+            stream_type = STREAM_TYPE_PRIVATE_DATA;
+            break;
         default:
             stream_type = STREAM_TYPE_PRIVATE_DATA;
             break;
@@ -351,6 +357,82 @@ static void mpegts_write_pmt(AVFormatContext *s, 
MpegTSService *service)
                 err = 1;
                 break;
             }
+            if (st->codec->codec_id==AV_CODEC_ID_OPUS) {
+                /* 6 bytes registration descriptor, 4 bytes Opus audio 
descriptor */
+                if (q - data > SECTION_LENGTH - 6 - 4) {
+                    err = 1;
+                    break;
+                }
+
+                *q++ = 0x05; /* MPEG-2 registration descriptor*/
+                *q++ = 4;
+                *q++ = 'O';
+                *q++ = 'p';
+                *q++ = 'u';
+                *q++ = 's';
+
+                *q++ = 0x7f; /* DVB extension descriptor */
+                *q++ = 2;
+                *q++ = 0x80;
+
+                if (st->codec->extradata && st->codec->extradata_size >= 19) {
+                    if (st->codec->extradata[18] == 0 && st->codec->channels 
<= 2) {
+                        /* RTP mapping family */
+                        *q++ = st->codec->channels;
+                    } else if (st->codec->extradata[18] == 1 && 
st->codec->channels <= 8 &&
+                               st->codec->extradata_size >= 21 + 
st->codec->channels) {
+                        static const uint8_t coupled_stream_counts[9] = {
+                            1, 0, 1, 1, 2, 2, 2, 3, 3
+                        };
+                        static const uint8_t channel_map_a[8][8] = {
+                            {0},
+                            {0, 1},
+                            {0, 2, 1},
+                            {0, 1, 2, 3},
+                            {0, 4, 1, 2, 3},
+                            {0, 4, 1, 2, 3, 5},
+                            {0, 4, 1, 2, 3, 5, 6},
+                            {0, 6, 1, 2, 3, 4, 5, 7},
+                        };
+                        static const uint8_t channel_map_b[8][8] = {
+                            {0},
+                            {0, 1},
+                            {0, 1, 2},
+                            {0, 1, 2, 3},
+                            {0, 1, 2, 3, 4},
+                            {0, 1, 2, 3, 4, 5},
+                            {0, 1, 2, 3, 4, 5, 6},
+                            {0, 1, 2, 3, 4, 5, 6, 7},
+                        };
+                        /* Vorbis mapping family */
+
+                        if (st->codec->extradata[19] == st->codec->channels - 
coupled_stream_counts[st->codec->channels] &&
+                            st->codec->extradata[20] == 
coupled_stream_counts[st->codec->channels] &&
+                            memcmp(&st->codec->extradata[21], 
channel_map_a[st->codec->channels], st->codec->channels) == 0) {
+                            *q++ = st->codec->channels;
+                        } else if (st->codec->channels >= 2 && 
st->codec->extradata[19] == st->codec->channels &&
+                                   st->codec->extradata[20] == 0 &&
+                                   memcmp(&st->codec->extradata[21], 
channel_map_b[st->codec->channels], st->codec->channels) == 0) {
+                            *q++ = st->codec->channels | 0x80;
+                        } else {
+                            /* Unsupported, could write an extended descriptor 
here */
+                            av_log(s, AV_LOG_ERROR, "Unsupported Opus 
Vorbis-style channel mapping");
+                            *q++ = 0xff;
+                        }
+                    } else {
+                        /* Unsupported */
+                        av_log(s, AV_LOG_ERROR, "Unsupported Opus channel 
mapping for family %d", st->codec->extradata[18]);
+                        *q++ = 0xff;
+                    }
+                } else if (st->codec->channels <= 2) {
+                    /* Assume RTP mapping family */
+                    *q++ = st->codec->channels;
+                } else {
+                    /* Unsupported */
+                    av_log(s, AV_LOG_ERROR, "Unsupported Opus channel 
mapping");
+                    *q++ = 0xff;
+                }
+            }
 
             if (st->codec->extradata_size == 4) {
                 memcpy(q, st->codec->extradata, 4);
@@ -1024,6 +1106,58 @@ static void mpegts_write_pes(AVFormatContext *s, 
AVStream *st,
     avio_flush(s->pb);
 }
 
+/* Based on GStreamer's gst-plugins-base/ext/ogg/gstoggstream.c
+ * Released under the LGPL v2.1+, written by
+ * Vincent Penquerc'h <[email protected]>
+ */
+static int opus_get_packet_samples(AVFormatContext *s, AVPacket *pkt)
+{
+    static const int durations[32] = {
+      480, 960, 1920, 2880,       /* Silk NB */
+      480, 960, 1920, 2880,       /* Silk MB */
+      480, 960, 1920, 2880,       /* Silk WB */
+      480, 960,                   /* Hybrid SWB */
+      480, 960,                   /* Hybrid FB */
+      120, 240, 480, 960,         /* CELT NB */
+      120, 240, 480, 960,         /* CELT NB */
+      120, 240, 480, 960,         /* CELT NB */
+      120, 240, 480, 960,         /* CELT NB */
+    };
+    int toc, frame_duration, nframes, duration;
+
+    if (pkt->size < 1)
+        return 0;
+
+    toc = pkt->data[0];
+
+    frame_duration = durations[toc >> 3];
+    switch (toc & 3) {
+    case 0:
+        nframes = 1;
+        break;
+    case 1:
+        nframes = 2;
+        break;
+    case 2:
+        nframes = 2;
+        break;
+    case 3:
+        if (pkt->size < 2)
+            return 0;
+        nframes = pkt->data[1] & 63;
+        break;
+    }
+
+    duration = nframes * frame_duration;
+    if (duration > 5760) {
+        av_log(s, AV_LOG_WARNING,
+               "Opus packet duration > 120 ms, invalid");
+        return 0;
+    }
+
+    return duration;
+}
+
 static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
 {
     AVStream *st = s->streams[pkt->stream_index];
@@ -1034,6 +1168,7 @@ static int mpegts_write_packet_internal(AVFormatContext 
*s, AVPacket *pkt)
     MpegTSWriteStream *ts_st = st->priv_data;
     const uint64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE) * 2;
     int64_t dts = AV_NOPTS_VALUE, pts = AV_NOPTS_VALUE;
+    int opus_samples = 0;
 
     if (ts->reemit_pat_pmt) {
         av_log(s, AV_LOG_WARNING,
@@ -1118,12 +1253,50 @@ static int mpegts_write_packet_internal(AVFormatContext 
*s, AVPacket *pkt)
             ts_st->amux->pb = NULL;
             buf             = data;
         }
+     } else if (st->codec->codec_id == AV_CODEC_ID_OPUS) {
+         if (pkt->size < 2) {
+             av_log(s, AV_LOG_ERROR, "Opus packet too short\n");
+             return AVERROR_INVALIDDATA;
+         }
+
+         /* Add Opus control header */
+         if ((AV_RB16(pkt->data) >> 5) != 0x3ff) {
+             int i, n;
+
+             opus_samples = opus_get_packet_samples(s, pkt);
+
+             data = av_malloc(pkt->size + 2 + pkt->size / 255 + 1);
+             if (!data)
+                 return AVERROR(ENOMEM);
+
+             /* TODO: Write trim if needed */
+             data[0] = 0x7f;
+             data[1] = 0xe0;
+
+             n = pkt->size;
+             i = 2;
+             do {
+                 data[i] = FFMIN(n, 255);
+                 n -= 255;
+                 i++;
+             } while (n >= 0);
+
+             memcpy(data + i, pkt->data, pkt->size);
+             buf     = data;
+             size    = pkt->size + 2 + pkt->size / 255 + 1;
+         } else {
+             /* TODO: Can we get TS formatted data here? If so we will
+              * need to count the samples of that too! */
+             av_log(s, AV_LOG_WARNING,
+                    "Got MPEG-TS formatted Opus data, unhandled");
+         }
     }
 
     if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO) {
         // for video and subtitle, write a single pes packet
         mpegts_write_pes(s, st, buf, size, pts, dts,
                          pkt->flags & AV_PKT_FLAG_KEY);
+        ts_st->opus_queued_samples = 0;
         av_free(data);
         return 0;
     }
@@ -1131,12 +1304,15 @@ static int mpegts_write_packet_internal(AVFormatContext 
*s, AVPacket *pkt)
     if (ts_st->payload_size + size > ts->pes_payload_size ||
         (dts != AV_NOPTS_VALUE && ts_st->payload_dts != AV_NOPTS_VALUE &&
          av_compare_ts(dts - ts_st->payload_dts, st->time_base,
-                       s->max_delay, AV_TIME_BASE_Q) >= 0)) {
+                       s->max_delay, AV_TIME_BASE_Q) >= 0) ||
+         ts_st->opus_queued_samples + opus_samples >= 5760 /* 120ms */) {
+
         if (ts_st->payload_size) {
             mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_size,
                              ts_st->payload_pts, ts_st->payload_dts,
                              ts_st->payload_flags & AV_PKT_FLAG_KEY);
             ts_st->payload_size = 0;
+            ts_st->opus_queued_samples = 0;
         }
         if (size > ts->pes_payload_size) {
             mpegts_write_pes(s, st, buf, size, pts, dts,
@@ -1154,6 +1330,7 @@ static int mpegts_write_packet_internal(AVFormatContext 
*s, AVPacket *pkt)
 
     memcpy(ts_st->payload + ts_st->payload_size, buf, size);
     ts_st->payload_size += size;
+    ts_st->opus_queued_samples += opus_samples;
 
     av_free(data);
 
@@ -1173,6 +1350,7 @@ static void mpegts_write_flush(AVFormatContext *s)
                              ts_st->payload_pts, ts_st->payload_dts,
                              ts_st->payload_flags & AV_PKT_FLAG_KEY);
             ts_st->payload_size = 0;
+            ts_st->opus_queued_samples = 0;
         }
     }
     avio_flush(s->pb);
-- 
2.6.3

_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to