[FFmpeg-devel] mpeg TS encoder PCR algo + looping

Submitted by ffmpeg@a.legko.ru on March 29, 2017, 12:12 p.m.

Details

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

Commit Message

ffmpeg@a.legko.ru March 29, 2017, 12:12 p.m.
Hi!
Thanx for help with git!
Reposting the same patch in git format now.
From 248d67b353cd76f201b7174a6bdf160e6c6f64b1 Mon Sep 17 00:00:00 2001
From: Vadka aka VVS <ffmpeg@a.legko.ru>

Date: Wed, 29 Mar 2017 15:09:32 +0300
Subject: [PATCH] ffmpeg.c - bug with streams loop/repeat fixed; ffmpeg_opt.c -
 adding missed options for libavformat/mpegtsenc.c; libavformat/mpegtsenc.c -
 PCR pids selection procedure changed, making it possible to broadcast
 standard mpeg TS/DVB streams with multiple programs (it is possible to define
 pcr_pid manually now).

---
 ffmpeg.c                |  26 ++++++++++
 ffmpeg_opt.c            |   7 ++-
 libavformat/mpegtsenc.c | 131 ++++++++++++++++++++++++++++++++----------------
 3 files changed, 120 insertions(+), 44 deletions(-)

-- 
2.9.0

Patch hide | download patch | download mbox

diff --git a/ffmpeg.c b/ffmpeg.c
index 3aa1e78..254960f 100644
--- a/ffmpeg.c
+++ b/ffmpeg.c
@@ -4028,6 +4028,31 @@  static int init_input_threads(void)
     return 0;
 }
 
+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 get_input_packet_mt(InputFile *f, AVPacket *pkt)
 {
     return av_thread_message_queue_recv(f->in_thread_queue, pkt,
@@ -4182,6 +4207,7 @@  static int process_input(int file_index)
     if (ret < 0 && ifile->loop) {
         if ((ret = seek_to_start(ifile, is)) < 0)
             return ret;
+        init_input_thread(file_index);
         ret = get_input_packet(ifile, &pkt);
         if (ret == AVERROR(EAGAIN)) {
             ifile->eagain = 1;
diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c
index ffe1abd..a5eb821 100644
--- a/ffmpeg_opt.c
+++ b/ffmpeg_opt.c
@@ -2609,7 +2609,12 @@  loop_end:
             } else if (!strcmp(key, "st")) {
                 int st_num = strtol(p2, NULL, 0);
                 av_program_add_stream_index(oc, progid, st_num);
-            } else {
+             } 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 {
                 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 3250dde..e9f0ab8 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,7 @@  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 +717,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 +764,11 @@  static int mpegts_init(AVFormatContext *s)
     MpegTSWriteStream *ts_st;
     MpegTSService *service;
     AVStream *st, *pcr_st = NULL;
-    AVDictionaryEntry *title, *provider;
+    AVDictionaryEntry *title, *provider, *p_pid;
     int i, j;
     const char *service_name;
     const char *provider_name;
-    int *pids;
+    int *pids, pcr_pid = 0x1fff;
     int ret;
 
     if (s->max_delay < 0) /* Not set by the caller */
@@ -786,8 +787,11 @@  static int mpegts_init(AVFormatContext *s)
         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;
+        p_pid      = av_dict_get(s->metadata, "pcr_pid", NULL, 0);
+        if(p_pid)
+                pcr_pid = atoi(p_pid->value);
         service       = mpegts_add_service(ts, ts->service_id,
-                                           provider_name, service_name);
+                                           provider_name, service_name, pcr_pid);
 
         if (!service)
             return AVERROR(ENOMEM);
@@ -805,8 +809,13 @@  static int mpegts_init(AVFormatContext *s)
             service_name  = title ? title->value : DEFAULT_SERVICE_NAME;
             provider      = av_dict_get(program->metadata, "service_provider", NULL, 0);
             provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME;
+            p_pid      = av_dict_get(s->metadata, "pcr_pid", NULL, 0);
+            if(p_pid)
+                pcr_pid = atoi(p_pid->value);
+                else
+                pcr_pid = 0x1fff;
             service       = mpegts_add_service(ts, program->id,
-                                               provider_name, service_name);
+                                               provider_name, service_name, pcr_pid);
 
             if (!service)
                 return AVERROR(ENOMEM);
@@ -901,12 +910,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;
@@ -941,17 +945,76 @@  static int mpegts_init(AVFormatContext *s)
 
     av_freep(&pids);
 
-    /* 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;
-        service->pcr_pid = ts_st->pid;
-    } else
-        ts_st = pcr_st->priv_data;
+        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;
+                                }
+                }
+                }
+                if (!serv->pcr_packet_period)
+                          serv->pcr_packet_period = 1;
+                av_log(s, AV_LOG_VERBOSE,
+                    "service 0x%x: pcr every %d pkts\n", serv->sid, serv->pcr_packet_period);
+                /* send PCR as soon as possible */
+                serv->pcr_packet_count = serv->pcr_packet_period;
+      }
+
+
 
     if (ts->mux_rate > 1) {
-        service->pcr_packet_period = (int64_t)ts->mux_rate * ts->pcr_period /
-                                     (TS_PACKET_SIZE * 8 * 1000);
         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 /
@@ -963,24 +1026,6 @@  static int mpegts_init(AVFormatContext *s)
         /* Arbitrary values, PAT/PMT will also be written on video key frames */
         ts->sdt_packet_period = 200;
         ts->pat_packet_period = 40;
-        if (pcr_st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
-            int frame_size = av_get_audio_frame_duration2(pcr_st->codecpar, 0);
-            if (!frame_size) {
-                av_log(s, AV_LOG_WARNING, "frame size not set\n");
-                service->pcr_packet_period =
-                    pcr_st->codecpar->sample_rate / (10 * 512);
-            } else {
-                service->pcr_packet_period =
-                    pcr_st->codecpar->sample_rate / (10 * frame_size);
-            }
-        } else {
-            // max delta PCR 0.1s
-            // TODO: should be avg_frame_rate
-            service->pcr_packet_period =
-                ts_st->user_tb.den / (10 * ts_st->user_tb.num);
-        }
-        if (!service->pcr_packet_period)
-            service->pcr_packet_period = 1;
     }
 
     ts->last_pat_ts = AV_NOPTS_VALUE;
@@ -994,7 +1039,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;
 
@@ -1003,9 +1048,9 @@  static int mpegts_init(AVFormatContext *s)
     else
         av_log(s, AV_LOG_VERBOSE, "muxrate %d, ", ts->mux_rate);
     av_log(s, AV_LOG_VERBOSE,
-           "pcr every %d pkts, sdt every %d, pat/pmt every %d pkts\n",
-           service->pcr_packet_period,
-           ts->sdt_packet_period, ts->pat_packet_period);
+            "sdt every %d, pat/pmt every %d pkts\n",
+              service->pcr_packet_period,
+              ts->sdt_packet_period, ts->pat_packet_period);
 
     if (ts->m2ts_mode == -1) {
         if (av_match_ext(s->filename, "m2ts")) {