]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/mpegtsenc.c
Add support for mp3 over RTP in rtpdec.c
[ffmpeg] / libavformat / mpegtsenc.c
index 6c31f6df5125507c56781236f5fa6ceaabaa9f53..a8d5d38129a7be399453bd784d1e013b791aabab 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * MPEG2 transport stream (aka DVB) muxer
- * Copyright (c) 2003 Fabrice Bellard.
+ * Copyright (c) 2003 Fabrice Bellard
  *
  * This file is part of FFmpeg.
  *
@@ -44,7 +44,7 @@ typedef struct MpegTSService {
     char *provider_name;
     int pcr_pid;
     int pcr_packet_count;
-    int pcr_packet_freq;
+    int pcr_packet_period;
 } MpegTSService;
 
 typedef struct MpegTSWrite {
@@ -52,9 +52,9 @@ typedef struct MpegTSWrite {
     MpegTSSection sdt; /* MPEG2 sdt table context */
     MpegTSService **services;
     int sdt_packet_count;
-    int sdt_packet_freq;
+    int sdt_packet_period;
     int pat_packet_count;
-    int pat_packet_freq;
+    int pat_packet_period;
     int nb_services;
     int onid;
     int tsid;
@@ -173,6 +173,7 @@ typedef struct MpegTSWriteStream {
     int pid; /* stream associated pid */
     int cc;
     int payload_index;
+    int first_pts_check; ///< first pts check needed
     int64_t payload_pts;
     int64_t payload_dts;
     uint8_t payload[DEFAULT_PES_PAYLOAD_SIZE];
@@ -216,6 +217,7 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
     for(i = 0; i < s->nb_streams; i++) {
         AVStream *st = s->streams[i];
         MpegTSWriteStream *ts_st = st->priv_data;
+        AVMetadataTag *lang = av_metadata_get(st->metadata, "language", NULL,0);
         switch(st->codec->codec_id) {
         case CODEC_ID_MPEG1VIDEO:
         case CODEC_ID_MPEG2VIDEO:
@@ -252,21 +254,19 @@ static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
         /* write optional descriptors here */
         switch(st->codec->codec_type) {
         case CODEC_TYPE_AUDIO:
-            if (strlen(st->language) == 3) {
+            if (lang && strlen(lang->value) == 3) {
                 *q++ = 0x0a; /* ISO 639 language descriptor */
                 *q++ = 4;
-                *q++ = st->language[0];
-                *q++ = st->language[1];
-                *q++ = st->language[2];
+                *q++ = lang->value[0];
+                *q++ = lang->value[1];
+                *q++ = lang->value[2];
                 *q++ = 0; /* undefined type */
             }
             break;
         case CODEC_TYPE_SUBTITLE:
             {
                 const char *language;
-                language = st->language;
-                if (strlen(language) != 3)
-                    language = "eng";
+                language = lang && strlen(lang->value)==3 ? lang->value : "eng";
                 *q++ = 0x59;
                 *q++ = 8;
                 *q++ = language[0];
@@ -383,15 +383,16 @@ static int mpegts_write_header(AVFormatContext *s)
     MpegTSWriteStream *ts_st;
     MpegTSService *service;
     AVStream *st;
+    AVMetadataTag *title;
     int i, total_bit_rate;
     const char *service_name;
+    uint64_t sdt_size, pat_pmt_size, pos;
 
     ts->tsid = DEFAULT_TSID;
     ts->onid = DEFAULT_ONID;
     /* allocate a single DVB service */
-    service_name = s->title;
-    if (service_name[0] == '\0')
-        service_name = DEFAULT_SERVICE_NAME;
+    title = av_metadata_get(s->metadata, "title", NULL, 0);
+    service_name = title ? title->value : DEFAULT_SERVICE_NAME;
     service = mpegts_add_service(ts, DEFAULT_SID,
                                  DEFAULT_PROVIDER_NAME, service_name);
     service->pmt.write_packet = section_write_packet;
@@ -419,11 +420,26 @@ static int mpegts_write_header(AVFormatContext *s)
         ts_st->pid = DEFAULT_START_PID + i;
         ts_st->payload_pts = AV_NOPTS_VALUE;
         ts_st->payload_dts = AV_NOPTS_VALUE;
+        ts_st->first_pts_check = 1;
         /* update PCR pid by using the first video stream */
         if (st->codec->codec_type == CODEC_TYPE_VIDEO &&
             service->pcr_pid == 0x1fff)
             service->pcr_pid = ts_st->pid;
         total_bit_rate += st->codec->bit_rate;
+        /* PES header size */
+        if (st->codec->codec_type == CODEC_TYPE_VIDEO ||
+            st->codec->codec_type == CODEC_TYPE_SUBTITLE) {
+            /* 1 PES per frame
+             * 19 bytes of PES header
+             * on average a half TS-packet (184/2) of padding-overhead every PES */
+            total_bit_rate += (19 + 184/2)*8 / av_q2d(st->codec->time_base);
+        } else {
+            /* 1 PES per DEFAULT_PES_PAYLOAD_SIZE bytes of audio data
+             * 14 bytes of PES header
+             * on average a half TS-packet (184/2) of padding-overhead every PES */
+            total_bit_rate += (14 + 184/2) *
+                st->codec->bit_rate / DEFAULT_PES_PAYLOAD_SIZE;
+        }
     }
 
     /* if no video stream, use the first stream as PCR */
@@ -434,26 +450,44 @@ static int mpegts_write_header(AVFormatContext *s)
 
     if (total_bit_rate <= 8 * 1024)
         total_bit_rate = 8 * 1024;
-    service->pcr_packet_freq = (total_bit_rate * PCR_RETRANS_TIME) /
+    service->pcr_packet_period = (total_bit_rate * PCR_RETRANS_TIME) /
         (TS_PACKET_SIZE * 8 * 1000);
-    ts->sdt_packet_freq = (total_bit_rate * SDT_RETRANS_TIME) /
+    ts->sdt_packet_period = (total_bit_rate * SDT_RETRANS_TIME) /
         (TS_PACKET_SIZE * 8 * 1000);
-    ts->pat_packet_freq = (total_bit_rate * PAT_RETRANS_TIME) /
+    ts->pat_packet_period = (total_bit_rate * PAT_RETRANS_TIME) /
         (TS_PACKET_SIZE * 8 * 1000);
-#if 0
-    printf("%d %d %d\n",
-           total_bit_rate, ts->sdt_packet_freq, ts->pat_packet_freq);
-#endif
 
-    ts->mux_rate = total_bit_rate;
+    ts->mux_rate = 1; // avoid div by 0
 
     /* write info at the start of the file, so that it will be fast to
        find them */
+    pos = url_ftell(s->pb);
     mpegts_write_sdt(s);
+    sdt_size = url_ftell(s->pb) - pos;
+    pos = url_ftell(s->pb);
     mpegts_write_pat(s);
     for(i = 0; i < ts->nb_services; i++) {
         mpegts_write_pmt(s, ts->services[i]);
     }
+    pat_pmt_size = url_ftell(s->pb) - pos;
+
+    total_bit_rate +=
+        total_bit_rate *  4 / TS_PACKET_SIZE           + /* TS  header size */
+        SDT_RETRANS_TIME * 8 * sdt_size     / 1000     + /* SDT size */
+        PAT_RETRANS_TIME * 8 * pat_pmt_size / 1000     + /* PAT+PMT size */
+        PCR_RETRANS_TIME * 8 * 8            / 1000;      /* PCR size */
+
+    av_log(s, AV_LOG_DEBUG, "muxrate %d freq sdt %d pat %d\n",
+           total_bit_rate, ts->sdt_packet_period, ts->pat_packet_period);
+
+    if (s->mux_rate)
+        ts->mux_rate = s->mux_rate;
+    else
+        ts->mux_rate = total_bit_rate;
+
+    // adjust pcr
+    ts->cur_pcr /= ts->mux_rate;
+
     put_flush_packet(s->pb);
 
     return 0;
@@ -472,11 +506,11 @@ static void retransmit_si_info(AVFormatContext *s)
     MpegTSWrite *ts = s->priv_data;
     int i;
 
-    if (++ts->sdt_packet_count == ts->sdt_packet_freq) {
+    if (++ts->sdt_packet_count == ts->sdt_packet_period) {
         ts->sdt_packet_count = 0;
         mpegts_write_sdt(s);
     }
-    if (++ts->pat_packet_count == ts->pat_packet_freq) {
+    if (++ts->pat_packet_count == ts->pat_packet_period) {
         ts->pat_packet_count = 0;
         mpegts_write_pat(s);
         for(i = 0; i < ts->nb_services; i++) {
@@ -520,7 +554,7 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
         if (ts_st->pid == ts_st->service->pcr_pid) {
             ts_st->service->pcr_packet_count++;
             if (ts_st->service->pcr_packet_count >=
-                ts_st->service->pcr_packet_freq) {
+                ts_st->service->pcr_packet_period) {
                 ts_st->service->pcr_packet_count = 0;
                 write_pcr = 1;
             }
@@ -578,7 +612,7 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
                 header_len += 5;
                 flags |= 0x80;
             }
-            if (dts != AV_NOPTS_VALUE && dts != pts) {
+            if (dts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE && dts != pts) {
                 header_len += 5;
                 flags |= 0x40;
             }
@@ -598,6 +632,8 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
             len = payload_size + header_len + 3;
             if (private_code != 0)
                 len++;
+            if (len > 0xffff)
+                len = 0;
             *q++ = len >> 8;
             *q++ = len;
             val = 0x80;
@@ -611,7 +647,7 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
                 write_pts(q, flags >> 6, pts);
                 q += 5;
             }
-            if (dts != AV_NOPTS_VALUE && dts != pts) {
+            if (dts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE && dts != pts) {
                 write_pts(q, 1, dts);
                 q += 5;
             }
@@ -669,69 +705,72 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
 static int mpegts_write_packet(AVFormatContext *s, AVPacket *pkt)
 {
     AVStream *st = s->streams[pkt->stream_index];
-    int size= pkt->size;
+    int len, size = pkt->size;
     uint8_t *buf= pkt->data;
+    uint8_t *data= NULL;
     MpegTSWriteStream *ts_st = st->priv_data;
-    int len, max_payload_size;
-    const uint8_t *access_unit_index = NULL;
     const uint64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE);
+    int64_t dts = AV_NOPTS_VALUE, pts = AV_NOPTS_VALUE;
 
-    if (st->codec->codec_type == CODEC_TYPE_SUBTITLE) {
-        /* for subtitle, a single PES packet must be generated */
-        mpegts_write_pes(s, st, buf, size, pkt->pts, AV_NOPTS_VALUE);
-        return 0;
-    }
+    if (pkt->pts != AV_NOPTS_VALUE)
+        pts = pkt->pts + delay;
+    if (pkt->dts != AV_NOPTS_VALUE)
+        dts = pkt->dts + delay;
 
-    if (st->codec->codec_id == CODEC_ID_DIRAC) {
-        /* for Dirac, a single PES packet must be generated */
-        mpegts_write_pes(s, st, buf, size, pkt->pts, pkt->dts);
-        return 0;
+    if (ts_st->first_pts_check && pts == AV_NOPTS_VALUE) {
+        av_log(s, AV_LOG_ERROR, "first pts value must set\n");
+        return -1;
     }
-    max_payload_size = DEFAULT_PES_PAYLOAD_SIZE;
-    if (st->codec->codec_id == CODEC_ID_MPEG2VIDEO ||
-        st->codec->codec_id == CODEC_ID_MPEG1VIDEO) {
-        const uint8_t *p = pkt->data;
-        const uint8_t *end = pkt->data+pkt->size;
-        uint32_t state = -1;
-        while (p < end) {
-            p = ff_find_start_code(p, end, &state);
-            if (state == PICTURE_START_CODE) {
-                access_unit_index = p - 4;
-                break;
-            }
+    ts_st->first_pts_check = 0;
+
+    if (st->codec->codec_id == CODEC_ID_H264) {
+        if (pkt->size < 5 || AV_RB32(pkt->data) != 0x0000001) {
+            av_log(s, AV_LOG_ERROR, "h264 bitstream malformated\n");
+            return -1;
+        }
+        if (pkt->data[4] != 0x09) { // AUD NAL
+            data = av_malloc(pkt->size+6);
+            if (!data)
+                return -1;
+            memcpy(data+6, pkt->data, pkt->size);
+            AV_WB32(data, 0x00000001);
+            data[4] = 0x09;
+            data[5] = 0xe0; // any slice type
+            buf  = data;
+            size = pkt->size+6;
         }
-    } else if (st->codec->codec_type == CODEC_TYPE_AUDIO) {
-        access_unit_index = pkt->data;
     }
 
-    if (!access_unit_index) {
-        av_log(s, AV_LOG_ERROR, "error, could not find access unit start\n");
-        return -1;
+    if (st->codec->codec_type != CODEC_TYPE_AUDIO) {
+        // for video and subtitle, write a single pes packet
+        mpegts_write_pes(s, st, buf, size, pts, dts);
+        av_free(data);
+        return 0;
+    }
+
+    if (ts_st->payload_pts == AV_NOPTS_VALUE) {
+        ts_st->payload_dts = dts;
+        ts_st->payload_pts = pts;
     }
 
+    // audio
     while (size > 0) {
-        len = max_payload_size - ts_st->payload_index;
+        len = DEFAULT_PES_PAYLOAD_SIZE - ts_st->payload_index;
         if (len > size)
             len = size;
         memcpy(ts_st->payload + ts_st->payload_index, buf, len);
         buf += len;
         size -= len;
         ts_st->payload_index += len;
-        if (access_unit_index && access_unit_index < buf &&
-            ts_st->payload_pts == AV_NOPTS_VALUE &&
-            ts_st->payload_dts == AV_NOPTS_VALUE) {
-            ts_st->payload_dts = pkt->dts + delay;
-            ts_st->payload_pts = pkt->pts + delay;
-        }
-        if (ts_st->payload_index >= max_payload_size) {
+        if (ts_st->payload_index >= DEFAULT_PES_PAYLOAD_SIZE) {
             mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_index,
                              ts_st->payload_pts, ts_st->payload_dts);
             ts_st->payload_pts = AV_NOPTS_VALUE;
             ts_st->payload_dts = AV_NOPTS_VALUE;
             ts_st->payload_index = 0;
-            access_unit_index = NULL; // unset access unit to avoid setting pts/dts again
         }
     }
+
     return 0;
 }