[FFmpeg-devel] patch 3. Adding support for the multiprog TS

Submitted by ffmpeg@a.legko.ru on June 28, 2017, 12:13 p.m.

Details

Message ID alpine.LNX.2.20.1706281506590.26976@scil.sinp.msu.ru
State New
Headers show

Commit Message

ffmpeg@a.legko.ru June 28, 2017, 12:13 p.m.
subject: program inside TS MUST have it's own PCR pid. so, the new algo 
solves the problem, selecting (auto mode) PCR pid inside the program. 
first, video stream, if no video - audio, if no audio - any first stream 
in the program. old behaviour remains for 1-prog TS. it is possible to set 
pcr_pid via program options.

full example, using the new code (4 progs inside TS):

ffmpeg -re \
         -i ZZ.avi \
         -i test.wav \
         -stream_loop 3 -i auu.wav \
         -i existone.mp3 \
         -map 0:v \
         -map 0:a \
         -map_channel 0.1.0:1.0 \
         -map_channel 0.1.1:1.1 \
         -vcodec libx264 -b:v 400k \
         -mpegts_original_network_id 0x1122 \
         -mpegts_transport_stream_id 0x3344 \
         -mpegts_service_id 0x5566 \
         -streamid 0:0x159 \
         -metadata service_provider="Some provider" \
         -metadata service_name="Some Channel" \
         -c:a:0 libfdk_aac -profile:a aac_he  -ac 2 -b:a 32k \
         -streamid 1:0x160 \
         -f mpegts \
         -map 1:a \
         -mpegts_original_network_id 0x1123 \
         -mpegts_transport_stream_id 0x3345 \
         -mpegts_service_id 0x55CA \
         -map_channel 1.0.0:2.0 \
         -map_channel 1.0.1:2.1 \
         -c:a:1 libfdk_aac -profile:a aac_he_v2  -ac 2 -b:a 32k \
         -streamid 2:0x180 \
         -f mpegts \
         -map 2:a \
         -mpegts_original_network_id 0x1127 \
         -mpegts_transport_stream_id 0x3348 \
         -mpegts_service_id 0x55CE \
         -map_channel 2.0.0:3.0 \
         -map_channel 2.0.1:3.1 \
         -c:a:2 libfdk_aac -profile:a aac_he_v2  -ac 2 -b:a 32k \
         -streamid 3:0x182 \
         -map 3:a \
         -mpegts_original_network_id 0x1129 \
         -mpegts_transport_stream_id 0x3349 \
         -mpegts_service_id 0x55CF \
         -map_channel 3.0.0:4.0 \
         -map_channel 3.0.1:4.1 \
         -c:a:3 libfdk_aac -profile:a aac_he_v2  -ac 2 -b:a 32k \
         -streamid 4:0x184 \
         -program title="Xren0":service_name="Zanunda":service_provider="provider4":program_num=0x5576:st=0:st=1 \
         -program title="Xren1":service_provider="provider4":program_num=0x5578:st=2 \
         -program title="Xren2":service_provider="provider5":program_num=0x5579:st=3 \
         -program program_num=0x5581:st=4 \
         -f mpegts  udp://192.11.1.12:1234\&pkt_size=1316
From d36f6f2a291c842654d3836e650b9f9fb2b16774 Mon Sep 17 00:00:00 2001
From: root <ffmpeg@scil.sinp.msu.ru>

Date: Wed, 28 Jun 2017 14:56:34 +0300
Subject: [PATCH 3/3] add multiprog mode PCR (automatic) selection algo, thus
 making ffmpeg to produce multiprog TS. corrected behaviour with some metadata
 (prog name, provider name).

---
 libavformat/mpegtsenc.c | 162 ++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 136 insertions(+), 26 deletions(-)

-- 
2.7.4

Patch hide | download patch | download mbox

diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
index acea2e9..15260a9 100644
--- a/libavformat/mpegtsenc.c
+++ b/libavformat/mpegtsenc.c
@@ -59,6 +59,7 @@  typedef struct MpegTSService {
     int pcr_pid;
     int pcr_packet_count;
     int pcr_packet_period;
+    int pcr_type;      /* if the service has a/v pid: AVMEDIA_TYPE_UNKNOWN/AUDIO/VIDEO...*/
     AVProgram *program;
 } MpegTSService;
 
@@ -707,7 +708,8 @@  static void mpegts_write_sdt(AVFormatContext *s)
 
 static MpegTSService *mpegts_add_service(MpegTSWrite *ts, int sid,
                                          const char *provider_name,
-                                         const char *name)
+                                         const char *name,
+                                         int pcr_pid)
 {
     MpegTSService *service;
 
@@ -716,7 +718,7 @@  static MpegTSService *mpegts_add_service(MpegTSWrite *ts, int sid,
         return NULL;
     service->pmt.pid       = ts->pmt_start_pid + ts->nb_services;
     service->sid           = sid;
-    service->pcr_pid       = 0x1fff;
+    service->pcr_pid       = pcr_pid; /* was 0x1fff */
     service->provider_name = av_strdup(provider_name);
     service->name          = av_strdup(name);
     if (!service->provider_name || !service->name)
@@ -763,11 +765,12 @@  static int mpegts_init(AVFormatContext *s)
     MpegTSWriteStream *ts_st;
     MpegTSService *service;
     AVStream *st, *pcr_st = NULL;
-    AVDictionaryEntry *title, *provider;
+    AVDictionaryEntry *title, *provider, *p_pid;
+    char *endz;
     int i, j;
-    const char *service_name;
-    const char *provider_name;
-    int *pids;
+    const char *service_name, *dflt_service_name;
+    const char *provider_name, *dflt_provider_name;
+    int *pids, pcr_pid = 0x1fff, dflt_pcr_pid = 0x1fff;
     int ret;
 
     if (s->max_delay < 0) /* Not set by the caller */
@@ -778,17 +781,34 @@  static int mpegts_init(AVFormatContext *s)
 
     ts->tsid = ts->transport_stream_id;
     ts->onid = ts->original_network_id;
+
+    dflt_service_name = DEFAULT_SERVICE_NAME;
+    title = av_dict_get(s->metadata, "title", NULL, 0);
+    if(title != NULL)
+          dflt_service_name = title->value;
+    else {
+      title = av_dict_get(s->metadata, "service_name", NULL, 0);
+      if(title != NULL)
+        dflt_service_name = title->value;
+      }
+
+    dflt_provider_name = DEFAULT_PROVIDER_NAME;
+    provider = av_dict_get(s->metadata, "service_provider", NULL, 0);
+    if(provider != NULL)
+      dflt_provider_name = provider->value;
+
+    p_pid  = av_dict_get(s->metadata, "pcr_pid", NULL, 0);
+    if (p_pid)  {
+      endz = NULL;
+      dflt_pcr_pid = strtol(p_pid->value, &endz, 0);
+      }
+
     if (!s->nb_programs) {
-        /* allocate a single DVB service */
-        title = av_dict_get(s->metadata, "service_name", NULL, 0);
-        if (!title)
-            title = av_dict_get(s->metadata, "title", NULL, 0);
-        service_name  = title ? title->value : DEFAULT_SERVICE_NAME;
-        provider      = av_dict_get(s->metadata, "service_provider", NULL, 0);
-        provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME;
+        /* allocate a single DVB service/no prog */
+        service_name  = dflt_service_name;
+        provider_name = dflt_provider_name;
         service       = mpegts_add_service(ts, ts->service_id,
-                                           provider_name, service_name);
-
+                                      provider_name, service_name, dflt_pcr_pid);
         if (!service)
             return AVERROR(ENOMEM);
 
@@ -802,11 +822,18 @@  static int mpegts_init(AVFormatContext *s)
             title = av_dict_get(program->metadata, "service_name", NULL, 0);
             if (!title)
                 title = av_dict_get(program->metadata, "title", NULL, 0);
-            service_name  = title ? title->value : DEFAULT_SERVICE_NAME;
+            service_name  = title ? title->value : dflt_service_name;
             provider      = av_dict_get(program->metadata, "service_provider", NULL, 0);
-            provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME;
+            provider_name = provider ? provider->value : dflt_provider_name;
+            p_pid         = av_dict_get(s->metadata, "pcr_pid", NULL, 0);
+            if (p_pid) {
+                endz = NULL;
+                pcr_pid = strtol(p_pid->value, &endz, 0);
+                }
+                else
+                pcr_pid = dflt_pcr_pid;
             service       = mpegts_add_service(ts, program->id,
-                                               provider_name, service_name);
+                                               provider_name, service_name, pcr_pid);
 
             if (!service)
                 return AVERROR(ENOMEM);
@@ -901,12 +928,7 @@  static int mpegts_init(AVFormatContext *s)
         ts_st->first_pts_check = 1;
         ts_st->cc              = 15;
         ts_st->discontinuity   = ts->flags & MPEGTS_FLAG_DISCONT;
-        /* update PCR pid by using the first video stream */
-        if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
-            service->pcr_pid == 0x1fff) {
-            service->pcr_pid = ts_st->pid;
-            pcr_st           = st;
-        }
+
         if (st->codecpar->codec_id == AV_CODEC_ID_AAC &&
             st->codecpar->extradata_size > 0) {
             AVStream *ast;
@@ -940,8 +962,94 @@  static int mpegts_init(AVFormatContext *s)
     }
 
     av_freep(&pids);
+    
+    /* automatic PCR pid selection in multiprog mode */
+    if(s->nb_programs > 0) {
+    MpegTSService *serv;
+    int k;
+    /* find a/v pid for PCR or any pid if no a/v found */
+    for (j = 0; j < ts->nb_services; j++) {
+        serv = ts->services[j];
+        serv->pcr_type = AVMEDIA_TYPE_UNKNOWN;
+        AVProgram *prog = serv->program;
+        if (serv->pcr_pid == 0x1fff) {
+           for (k = 0; k < prog->nb_stream_indexes; k++) {
+               st = s->streams[prog->stream_index[k]];
+               if(serv->pcr_type == AVMEDIA_TYPE_UNKNOWN &&
+                  (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
+                   st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))
+                         serv->pcr_type = st->codecpar->codec_type;
+               else /* video stream preference */
+                  if(serv->pcr_type == AVMEDIA_TYPE_AUDIO &&
+                     st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+                         serv->pcr_type = st->codecpar->codec_type;
+            }
+        }
+    }
+
+    for (j = 0; j < ts->nb_services; j++) {
+        serv = ts->services[j];
+        AVProgram *prog = serv->program;
+        if(serv->pcr_pid == 0x1fff) {
+          /* find first a/v media PID to hold PCR; calculate PCR period */
+          for (k = 0; k < prog->nb_stream_indexes; k++) {
+              st = s->streams[prog->stream_index[k]];
+              if(serv->pcr_type == AVMEDIA_TYPE_UNKNOWN ||
+                 (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
+                  serv->pcr_type == AVMEDIA_TYPE_VIDEO) ||
+                 (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
+                  serv->pcr_type == AVMEDIA_TYPE_AUDIO)) {
+                         serv->pcr_pid = st->id;
+              if (ts->mux_rate > 1) {
+                         serv->pcr_packet_period = (int64_t)ts->mux_rate * \
+                                                  ts->pcr_period /
+                                                  (TS_PACKET_SIZE * 8 * 1000);
+                         } else {
+                         if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+                             int frame_size =
+                                av_get_audio_frame_duration2(st->codecpar, 0);
+                              if (!frame_size) {
+                                  av_log(s, AV_LOG_WARNING,
+                                    "pcr_packet_period: frame size not set\n");
+                                  serv->pcr_packet_period =
+                                    st->codecpar->sample_rate / (10 * 512);
+                                  } else
+                                  serv->pcr_packet_period =
+                                  st->codecpar->sample_rate / (10 * frame_size);
+                         } else {
+                        /* max delta PCR 0.1s */
+                        /* TODO: should be avg_frame_rate */
+                            ts_st = st->priv_data;
+                            serv->pcr_packet_period =
+                                 ts_st->user_tb.den / (10 * ts_st->user_tb.num);
+                        }
+              }
+              break;
+              }
+          } /* for k */
+        }
+        if (!serv->pcr_packet_period)
+            serv->pcr_packet_period = 1;
+        /* send PCR as soon as possible */
+        serv->pcr_packet_count = serv->pcr_packet_period;
+        }
 
-    /* if no video stream, use the first stream as PCR */
+    if (ts->mux_rate > 1) {
+        ts->sdt_packet_period      = (int64_t)ts->mux_rate * SDT_RETRANS_TIME /
+                                     (TS_PACKET_SIZE * 8 * 1000);
+        ts->pat_packet_period      = (int64_t)ts->mux_rate * PAT_RETRANS_TIME /
+                                     (TS_PACKET_SIZE * 8 * 1000);
+
+        if (ts->copyts < 1)
+            ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE);
+    } else {
+        /* Arbitrary values, PAT/PMT will also be written on video key frames */
+        ts->sdt_packet_period = 200;
+        ts->pat_packet_period = 40;
+    }
+
+    }  else { /* default PCR pid selection in singleprog mode */
+        /* if no video stream, use the first stream as PCR */
     if (service->pcr_pid == 0x1fff && s->nb_streams > 0) {
         pcr_st           = s->streams[0];
         ts_st            = pcr_st->priv_data;
@@ -983,6 +1091,8 @@  static int mpegts_init(AVFormatContext *s)
             service->pcr_packet_period = 1;
     }
 
+    }
+
     ts->last_pat_ts = AV_NOPTS_VALUE;
     ts->last_sdt_ts = AV_NOPTS_VALUE;
     // The user specified a period, use only it
@@ -994,7 +1104,7 @@  static int mpegts_init(AVFormatContext *s)
     }
 
     // output a PCR as soon as possible
-    service->pcr_packet_count = service->pcr_packet_period;
+    // service->pcr_packet_count = service->pcr_packet_period;
     ts->pat_packet_count      = ts->pat_packet_period - 1;
     ts->sdt_packet_count      = ts->sdt_packet_period - 1;