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
From b043d9f5c894f4b9c9964e43392ee42adce2ef33 Mon Sep 17 00:00:00 2001 From: root <ffm...@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
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel