+
+ title = av_dict_get(metadata, "service_name", NULL, 0);
+ if (!title)
+ title = av_dict_get(metadata, "title", NULL, 0);
+ snprintf(default_service_name, sizeof(default_service_name), "%s%02d", DEFAULT_SERVICE_NAME, ts->nb_services + 1);
+ service_name = title ? title->value : default_service_name;
+ provider = av_dict_get(metadata, "service_provider", NULL, 0);
+ provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME;
+
+ service = av_mallocz(sizeof(MpegTSService));
+ if (!service)
+ return NULL;
+ service->pmt.pid = ts->pmt_start_pid + ts->nb_services;
+ service->sid = sid;
+ service->pcr_pid = 0x1fff;
+ if (encode_str8(service->provider_name, provider_name) < 0 ||
+ encode_str8(service->name, service_name) < 0) {
+ av_log(s, AV_LOG_ERROR, "Too long service or provider name\n");
+ goto fail;
+ }
+ if (av_dynarray_add_nofree(&ts->services, &ts->nb_services, service) < 0)
+ goto fail;
+
+ service->pmt.write_packet = section_write_packet;
+ service->pmt.opaque = s;
+ service->pmt.cc = 15;
+ service->pmt.discontinuity= ts->flags & MPEGTS_FLAG_DISCONT;
+ service->program = program;
+
+ return service;
+fail:
+ av_free(service);
+ return NULL;
+}
+
+static void enable_pcr_generation_for_stream(AVFormatContext *s, AVStream *pcr_st)
+{
+ MpegTSWrite *ts = s->priv_data;
+ MpegTSWriteStream *ts_st = pcr_st->priv_data;
+
+ if (ts->mux_rate > 1 || ts->pcr_period_ms >= 0) {
+ int pcr_period_ms = ts->pcr_period_ms == -1 ? PCR_RETRANS_TIME : ts->pcr_period_ms;
+ ts_st->pcr_period = av_rescale(pcr_period_ms, PCR_TIME_BASE, 1000);
+ } else {
+ /* By default, for VBR we select the highest multiple of frame duration which is less than 100 ms. */
+ int64_t frame_period = 0;
+ 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");
+ frame_size = 512;
+ }
+ frame_period = av_rescale_rnd(frame_size, PCR_TIME_BASE, pcr_st->codecpar->sample_rate, AV_ROUND_UP);
+ } else if (pcr_st->avg_frame_rate.num) {
+ frame_period = av_rescale_rnd(pcr_st->avg_frame_rate.den, PCR_TIME_BASE, pcr_st->avg_frame_rate.num, AV_ROUND_UP);
+ }
+ if (frame_period > 0 && frame_period <= PCR_TIME_BASE / 10)
+ ts_st->pcr_period = frame_period * (PCR_TIME_BASE / 10 / frame_period);
+ else
+ ts_st->pcr_period = 1;
+ }
+
+ // output a PCR as soon as possible
+ ts_st->last_pcr = ts->first_pcr - ts_st->pcr_period;
+}
+
+static void select_pcr_streams(AVFormatContext *s)
+{
+ MpegTSWrite *ts = s->priv_data;
+
+ for (int i = 0; i < ts->nb_services; i++) {
+ MpegTSService *service = ts->services[i];
+ AVStream *pcr_st = NULL;
+ AVProgram *program = service->program;
+ int nb_streams = program ? program->nb_stream_indexes : s->nb_streams;
+
+ for (int j = 0; j < nb_streams; j++) {
+ AVStream *st = s->streams[program ? program->stream_index[j] : j];
+ if (!pcr_st ||
+ pcr_st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+ {
+ pcr_st = st;
+ }
+ }
+
+ if (pcr_st) {
+ MpegTSWriteStream *ts_st = pcr_st->priv_data;
+ service->pcr_pid = ts_st->pid;
+ enable_pcr_generation_for_stream(s, pcr_st);
+ av_log(s, AV_LOG_VERBOSE, "service %i using PCR in pid=%i, pcr_period=%"PRId64"ms\n",
+ service->sid, service->pcr_pid, av_rescale(ts_st->pcr_period, 1000, PCR_TIME_BASE));
+ }
+ }
+}
+
+static int mpegts_init(AVFormatContext *s)
+{
+ MpegTSWrite *ts = s->priv_data;
+ int i, j;