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

ffmpeg at scil.sinp.msu.ru ffmpeg at scil.sinp.msu.ru
Tue Mar 28 18:30:30 EEST 2017


Hi!

Attached patch changes the PCR pid dedication number algo:
pcr_pid belongs to the program, not to entire TS (it was the mistake in 
TS code). So, the patch makes 
it possible to broadcast TS with several programs inside.
It is possible to set the pcr pid manually by adding in the prog conf 
like this (pcr_pid=0x301):
-program title="Xren2":service_provider="provider5":program_num=0x5579:pcr_pid=0x301:st=0
Not tested: multiple TS streaming.

Also, the title, service name and provider name were corrected in the 
ffmpeg_opt.c: options required in mpegtsenc.c were not present in 
ffmpeg_opt from the genesis, and the only provider was ffmpeg. Look at 
example above (service_provider="provider5").

Behaviour of "-stream_loop" and "-re" options changes with this patch:
ffmpeg -re \
         -i auu.wav \
         -re \
         -stream_loop 4 \
         -re \
         -i auu1.wav \

will loop 4 times auu1.wav, while auu.wav forever. So, the streaming 
continues while one prog is finished. Touches all containers.

Example 1 (streaming 4 progs in one mpeg TS):
ffmpeg -re \
         -i bbbbb_800x450_25fps.avi \
         -i xxxxx.wav \
         -i yyyyy.wav -stream_loop 10\
         -i zzzz.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 800k \
         -x264-params level=30:bframes=5:weightp=0:\
cabac=0:ref=1:vbv-maxrate=560:vbv-bufsize=2000:analyse=all:me=umh:\
no-fast-pskip=1:subq=6:8x8dct=0:trellis=0 \
         -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 \
         -metadata service_provider="Some provider1" \
         -metadata service_name="Some Channel1" \
         -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 title="Xren3":service_provider="provider6":program_num=0x5581:st=4 \
         -metadata service_provider="Some provider3" \
         -metadata service_name="Some Channel3" \
         -f mpegts  udp://172.16.1.10:1234\&pkt_size=1316

Example 2 (streaming 2 audio files with looping/repeat; note, to repeat, 
use -re for required input stream; 1 mpeg TS out):
ffmpeg -re \
         -i auu.wav \
         -re \
         -stream_loop 4 \
         -re \
         -i auu.wav \
         -map 0:a \
         -mpegts_original_network_id 0x1127 \
         -mpegts_transport_stream_id 0x3348 \
         -mpegts_service_id 0x55CE \
         -map_channel 0.0.0:0.0 \
         -map_channel 0.0.1:0.1 \
         -c:a:0 libfdk_aac -profile:a aac_he_v2  -ac 2 -b:a 32k \
         -streamid 0:0x182 \
         -map 1:a \
         -mpegts_original_network_id 0x1129 \
         -mpegts_transport_stream_id 0x3349 \
         -mpegts_service_id 0x55CF \
         -map_channel 1.0.0:1.0 \
         -map_channel 1.0.1:1.1 \
         -c:a:1 libfdk_aac -profile:a aac_he_v2  -ac 2 -b:a 32k \
         -streamid 1:0x184 \
         -program title="Xren2":service_provider="provider5":program_num=0x5579:st=0 \
         -program title="Xren3":service_provider="provider6":program_num=0x5581:st=1 \
         -metadata service_provider="Some provider3" \
         -metadata service_name="Some Channel3" \
         -f mpegts  udp://172.16.1.10:1234\&pkt_size=1316
-------------- next part --------------
diff -c -r ffmpeg_git_old/ffmpeg.c ffmpeg_git_new/ffmpeg.c
*** ffmpeg_git_old/ffmpeg.c	2017-03-27 19:20:02.000000000 +0300
--- ffmpeg_git_new/ffmpeg.c	2017-03-28 17:25:10.000000000 +0300
***************
*** 4028,4033 ****
--- 4028,4058 ----
      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,4187 ****
--- 4207,4213 ----
      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 -c -r ffmpeg_git_old/ffmpeg_opt.c ffmpeg_git_new/ffmpeg_opt.c
*** ffmpeg_git_old/ffmpeg_opt.c	2017-03-27 19:20:02.000000000 +0300
--- ffmpeg_git_new/ffmpeg_opt.c	2017-03-28 17:26:43.000000000 +0300
***************
*** 2609,2615 ****
              } else if (!strcmp(key, "st")) {
                  int st_num = strtol(p2, NULL, 0);
                  av_program_add_stream_index(oc, progid, st_num);
!             } else {
                  av_log(NULL, AV_LOG_FATAL, "Unknown program key %s.\n", key);
                  exit_program(1);
              }
--- 2609,2620 ----
              } 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 {
                  av_log(NULL, AV_LOG_FATAL, "Unknown program key %s.\n", key);
                  exit_program(1);
              }
Only in ffmpeg_git_new: ffmpeg_orig.c
Only in ffmpeg_git_new: ffmpeg_vvs.c
diff -c -r ffmpeg_git_old/libavformat/mpegtsenc.c ffmpeg_git_new/libavformat/mpegtsenc.c
*** ffmpeg_git_old/libavformat/mpegtsenc.c	2017-03-18 20:20:05.000000000 +0300
--- ffmpeg_git_new/libavformat/mpegtsenc.c	2017-03-28 13:30:59.000000000 +0300
***************
*** 59,64 ****
--- 59,65 ----
      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,713 ****
  
  static MpegTSService *mpegts_add_service(MpegTSWrite *ts, int sid,
                                           const char *provider_name,
!                                          const char *name)
  {
      MpegTSService *service;
  
--- 708,714 ----
  
  static MpegTSService *mpegts_add_service(MpegTSWrite *ts, int sid,
                                           const char *provider_name,
!                                          const char *name, int pcr_pid)
  {
      MpegTSService *service;
  
***************
*** 716,722 ****
          return NULL;
      service->pmt.pid       = ts->pmt_start_pid + ts->nb_services;
      service->sid           = sid;
!     service->pcr_pid       = 0x1fff;
      service->provider_name = av_strdup(provider_name);
      service->name          = av_strdup(name);
      if (!service->provider_name || !service->name)
--- 717,723 ----
          return NULL;
      service->pmt.pid       = ts->pmt_start_pid + ts->nb_services;
      service->sid           = sid;
!     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,773 ****
      MpegTSWriteStream *ts_st;
      MpegTSService *service;
      AVStream *st, *pcr_st = NULL;
!     AVDictionaryEntry *title, *provider;
      int i, j;
      const char *service_name;
      const char *provider_name;
!     int *pids;
      int ret;
  
      if (s->max_delay < 0) /* Not set by the caller */
--- 764,774 ----
      MpegTSWriteStream *ts_st;
      MpegTSService *service;
      AVStream *st, *pcr_st = NULL;
!     AVDictionaryEntry *title, *provider, *p_pid;
      int i, j;
      const char *service_name;
      const char *provider_name;
!     int *pids, pcr_pid = 0x1fff;
      int ret;
  
      if (s->max_delay < 0) /* Not set by the caller */
***************
*** 786,793 ****
          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;
          service       = mpegts_add_service(ts, ts->service_id,
!                                            provider_name, service_name);
  
          if (!service)
              return AVERROR(ENOMEM);
--- 787,797 ----
          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, pcr_pid);
  
          if (!service)
              return AVERROR(ENOMEM);
***************
*** 805,812 ****
              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;
              service       = mpegts_add_service(ts, program->id,
!                                                provider_name, service_name);
  
              if (!service)
                  return AVERROR(ENOMEM);
--- 809,819 ----
              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);
              service       = mpegts_add_service(ts, program->id,
!                                                provider_name, service_name, pcr_pid);
  
              if (!service)
                  return AVERROR(ENOMEM);
***************
*** 901,912 ****
          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;
--- 908,921 ----
          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; old behaviour */
! /*        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,957 ****
  
      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;
  
      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 /
--- 950,1033 ----
  
      av_freep(&pids);
  
+  	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;
+  		/* 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 (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;
+ */
  
      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 /
***************
*** 963,986 ****
          /* 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;
--- 1039,1044 ----
***************
*** 994,1000 ****
      }
  
      // output a PCR as soon as possible
!     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;
  
--- 1052,1058 ----
      }
  
      // output a PCR as soon as possible
! //    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;
  
***************
*** 1002,1011 ****
          av_log(s, AV_LOG_VERBOSE, "muxrate VBR, ");
      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);
  
      if (ts->m2ts_mode == -1) {
          if (av_match_ext(s->filename, "m2ts")) {
--- 1060,1069 ----
          av_log(s, AV_LOG_VERBOSE, "muxrate VBR, ");
      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);
  
      if (ts->m2ts_mode == -1) {
          if (av_match_ext(s->filename, "m2ts")) {


More information about the ffmpeg-devel mailing list