[FFmpeg-devel] Multiprogram mode for mpeg TS

ffmpeg at a.legko.ru ffmpeg at a.legko.ru
Fri Jun 23 17:42:36 EEST 2017


Hi!
(corrected version of the patch.)

1. adding missed but required options for libavformat/mpegts encoder in
    ffmpeg_opt.c. also, it makes it possible to add pcr_pid for separate
    program inside TS.
    options are:
      service_provider: any provider name  (only default FFMPEG is present in original code)
      service_name: alias for title, used in mpegtsenc.c. it is present there, but never defined.
      pcr_pid: if required, it is possible to set the PCR pid for separate program inside TS

2. ffmpeg.c, when encoding multiple streams, stops encoding threads even
    when loop counter is set for separate input stream. adding corresponding
    function to correct the situation.

3. libavformat/mpegtsenc.c: adding multiprogram TS mode; thus, streams
    can be organized into progs as it is done in DVB streams. for that, PCR
    selection (auto) algo was changed (according to TS standart, each prog
    must have PCR pid inside). old PCR selection (auto) mode remains for 1-prog case.

4. possible to set default title|service_name/service_provider/pcr_pid
    via -metadata option (as in example below for the last prog). metadata
    options are used in 1-prog stream.


following example is for multi-prog TS streaming (open VLC with 
udp://@1234 and look at Playback/Program menu). here we have 4 progs with 
the loop(3 times) for auu.wav (when it stops, streaming remains with 1
silent prog):

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
-------------- next part --------------
From b043d9f5c894f4b9c9964e43392ee42adce2ef33 Mon Sep 17 00:00:00 2001
From: root <ffmpeg at scil.sinp.msu.ru>
Date: Fri, 23 Jun 2017 17:01:07 +0300
Subject: [PATCH] ffmpeg.c - add thread restart (when required by looping) for
 multi-stream encoding ffmpeg_opt.c - add required but missing options for
 mpegtsenc.c mpegtsenc.c - add support for multi-programm mpeg TS, add PCR
 selection algo for it

---
 ffmpeg.c                |  27 ++++++++
 ffmpeg_opt.c            |   7 ++-
 libavformat/mpegtsenc.c | 162 ++++++++++++++++++++++++++++++++++++++++--------
 3 files changed, 169 insertions(+), 27 deletions(-)

diff --git a/ffmpeg.c b/ffmpeg.c
index a783e6e..2866754 100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -4013,6 +4013,29 @@ static void free_input_threads(void)
     }
 }
 
+static int init_input_thread(int i)
+{
+    int ret;
+  
+    if (nb_input_files == 1)
+       return 0;
+
+    InputFile *f = input_files[i];
+    if (f->ctx->pb ? !f->ctx->pb->seekable :
+       strcmp(f->ctx->iformat->name, "lavfi"))
+           f->non_blocking = 1;
+    ret = av_thread_message_queue_alloc(&f->in_thread_queue,
+           f->thread_queue_size, sizeof(AVPacket));
+    if (ret < 0)
+       return ret;
+    if ((ret = pthread_create(&f->thread, NULL, input_thread, f))) {
+       av_log(NULL, AV_LOG_ERROR, "pthread_create failed: %s. Try to increase `ulimit -v` or decrease `ulimit -s`.\n", strerror(ret));
+       av_thread_message_queue_free(&f->in_thread_queue);
+       return AVERROR(ret);
+    }
+return 0;
+}
+ 
 static int init_input_threads(void)
 {
     int i, ret;
@@ -4191,9 +4214,13 @@ static int process_input(int file_index)
         ifile->eagain = 1;
         return ret;
     }
+
     if (ret < 0 && ifile->loop) {
         if ((ret = seek_to_start(ifile, is)) < 0)
             return ret;
+#if HAVE_PTHREADS
+        init_input_thread(file_index);
+#endif
         ret = get_input_packet(ifile, &pkt);
         if (ret == AVERROR(EAGAIN)) {
             ifile->eagain = 1;
diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c
index bb6001f..aa4ffb5 100644
--- a/ffmpeg_opt.c
+++ b/ffmpeg_opt.c
@@ -2653,13 +2653,18 @@ loop_end:
             if (!*p2)
                 exit_program(1);
             p2++;
-
             if (!strcmp(key, "title")) {
                 av_dict_set(&program->metadata, "title", p2, 0);
             } else if (!strcmp(key, "program_num")) {
             } else if (!strcmp(key, "st")) {
                 int st_num = strtol(p2, NULL, 0);
                 av_program_add_stream_index(oc, progid, st_num);
+            } else if (!strcmp(key, "service_provider")) {
+                av_dict_set(&program->metadata, "service_provider", p2, 0);
+            } else if (!strcmp(key, "service_name")) {
+                av_dict_set(&program->metadata, "service_name", p2, 0);
+            } else if (!strcmp(key, "pcr_pid")) {
+                av_dict_set(&program->metadata, "pcr_pid", p2, 0);
             } else {
                 av_log(NULL, AV_LOG_FATAL, "Unknown program key %s.\n", key);
                 exit_program(1);
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;
 
-- 
2.7.4



More information about the ffmpeg-devel mailing list