From patchwork Fri Jun 23 14:42:36 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: ffmpeg@a.legko.ru X-Patchwork-Id: 4089 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.22.4 with SMTP id 4csp155030vsw; Fri, 23 Jun 2017 07:42:51 -0700 (PDT) X-Received: by 10.223.133.67 with SMTP id 61mr6611536wrh.30.1498228971072; Fri, 23 Jun 2017 07:42:51 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1498228971; cv=none; d=google.com; s=arc-20160816; b=Zg4fl98DExYfZKeDKSVcof8OkV2Ks7cDU/R5cdCKIuXKRtLHnNL+tGZqrjh4+XcSY9 ZqxFLTd4WCSxd3mn0FXu1i5/O7L/2zf+/FULGgcAA5rI+7MrfhfLibGWoP1b2YtF9Qrq McKlO+i4zkzRw3IeWB/iYWvFq/yTfXcbv96YmATyJVimCWI0aMKw0pIS7ke9EBraHT5U FPoKQR5UZfFtOa+ABEPDJjvMIjPhIVNzTzRiJJN8JDn1mRf21Avjjog2GKnjHSKZilaR CfmEMuL7w+6e5kILBdHnAy9Vu+VOWYxpzHWr09siyXD55EKI+rga1gExZtCvXVHiNPhs Ia0w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence:subject :mime-version:user-agent:message-id:to:from:date:delivered-to :arc-authentication-results; bh=EjSMpxVNWX5/MKHVorw/5y7mdp9yiP2RepikBMAfg4Q=; b=UBAvviofUw7JeC6pKIIMtx93dzYx4XEav4z3ucPZF8PKw98sPOHJGSNsqGnQYe3RHX c8T/yFlqdbvOY5YPSaMZBDaLfzQhVtad37/HYGynGju28tmN/vgAj1RIqAkF0teKXx26 EUSXxRWuIJLLfUWhouXeVBvOpo2U47HlQqI/ZE9WsJvurRjOu2ky5WRxsbXBoWmyEc6f S27DoJ7bl6NEl4ZutWWhEdP0u6ojGuV8T3G+020cLrjAw1PwbqV7fjWOZDLFFwluiXaI UYZYk9G8YlY1wFB6aep5tf38W538BLOHIDmsnQx4vOh+RLaSeKFPrzm+K58WtUQ9nQxl iODA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id a77si4319908wmi.19.2017.06.23.07.42.50; Fri, 23 Jun 2017 07:42:51 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id C1A4B689E69; Fri, 23 Jun 2017 17:42:46 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from scil.sinp.msu.ru (scil.sinp.msu.ru [213.131.0.130]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id BDB3F689B62 for ; Fri, 23 Jun 2017 17:42:39 +0300 (EEST) Received: from ffmpeg (helo=localhost) by scil.sinp.msu.ru with local-esmtp (Exim 4.86) (envelope-from ) id 1dOPnA-0002Y7-9F for ffmpeg-devel@ffmpeg.org; Fri, 23 Jun 2017 17:42:37 +0300 Date: Fri, 23 Jun 2017 17:42:36 +0300 (MSK) From: ffmpeg@a.legko.ru X-X-Sender: ffmpeg@scil.sinp.msu.ru To: ffmpeg-devel@ffmpeg.org Message-ID: User-Agent: Alpine 2.20 (LNX 67 2015-01-07) MIME-Version: 1.0 X-Spam-Score: 0.0 (/) X-Spam-Report: Spam detection software, running on the system "scil.sinp.msu.ru", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see vvs for details. Content preview: 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 [...] Content analysis details: (0.0 points, 6.5 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 HEADER_FROM_DIFFERENT_DOMAINS From and EnvelopeFrom 2nd level mail domains are different -0.0 NO_RELAYS Informational: message was not relayed via SMTP Subject: [FFmpeg-devel] Multiprogram mode for mpeg TS X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" 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 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(-) -- 2.7.4 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;