diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c index d827ba3e28..c8c6db979b 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -56,7 +56,8 @@ typedef struct MpegTSService { int sid; /* service ID */ uint8_t name[256]; uint8_t provider_name[256]; - int pcr_pid; + int64_t pcr, pcr_packet_timer, pcr_packet_period; + int pcr_sid, pcr_pid; AVProgram *program; } MpegTSService; @@ -76,11 +77,12 @@ typedef struct MpegTSWrite { MpegTSSection pat; /* MPEG-2 PAT table */ MpegTSSection sdt; /* MPEG-2 SDT table context */ MpegTSService **services; - int64_t sdt_period; /* SDT period in PCR time base */ - int64_t pat_period; /* PAT/PMT period in PCR time base */ + int64_t sdt_packet_timer, sdt_packet_period; + int64_t pat_packet_timer, pat_packet_period; int nb_services; - int64_t first_pcr; - int64_t next_pcr; + int onid; + int tsid; + int64_t pcr, first_pcr, delay; int mux_rate; ///< set to 1 when VBR int pes_payload_size; @@ -90,14 +92,14 @@ typedef struct MpegTSWrite { int service_type; int pmt_start_pid; + int pcr_start_pid; int start_pid; int m2ts_mode; - int m2ts_video_pid; - int m2ts_audio_pid; - int m2ts_pgssub_pid; - int m2ts_textsub_pid; + int64_t ts_offset; - int pcr_period_ms; + int reemit_pat_pmt; // backward compatibility + + double pcr_period; #define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01 #define MPEGTS_FLAG_AAC_LATM 0x02 #define MPEGTS_FLAG_PAT_PMT_AT_FRAMES 0x04 @@ -106,10 +108,8 @@ typedef struct MpegTSWrite { int flags; int copyts; int tables_version; - int64_t pat_period_us; - int64_t sdt_period_us; - int64_t last_pat_ts; - int64_t last_sdt_ts; + double pat_period; + double sdt_period; int omit_video_pes_length; } MpegTSWrite; @@ -217,14 +217,15 @@ static int mpegts_write_section1(MpegTSSection *s, int tid, int id, /* mpegts writer */ #define DEFAULT_PROVIDER_NAME "FFmpeg" -#define DEFAULT_SERVICE_NAME "Service" +#define DEFAULT_SERVICE_NAME "Service01" -/* we retransmit the SI info at this rate */ +/* we retransmit the SI info at this rate (ms) */ #define SDT_RETRANS_TIME 500 #define PAT_RETRANS_TIME 100 -#define PCR_RETRANS_TIME 20 +#define PCR_RETRANS_TIME 50 typedef struct MpegTSWriteStream { + struct MpegTSService *service; int pid; /* stream associated pid */ int cc; int discontinuity; @@ -236,10 +237,7 @@ typedef struct MpegTSWriteStream { int payload_flags; uint8_t *payload; AVFormatContext *amux; - int data_st_warning; - - int64_t pcr_period; /* PCR period in PCR time base */ - int64_t last_pcr; + AVRational user_tb; /* For Opus */ int opus_queued_samples; @@ -259,7 +257,7 @@ static void mpegts_write_pat(AVFormatContext *s) put16(&q, service->sid); put16(&q, 0xe000 | service->pmt.pid); } - mpegts_write_section1(&ts->pat, PAT_TID, ts->transport_stream_id, ts->tables_version, 0, 0, + mpegts_write_section1(&ts->pat, PAT_TID, ts->tsid, ts->tables_version, 0, 0, data, q - data); } @@ -281,148 +279,6 @@ static void put_registration_descriptor(uint8_t **q_ptr, uint32_t tag) *q_ptr = q; } -static int get_dvb_stream_type(AVFormatContext *s, AVStream *st) -{ - MpegTSWrite *ts = s->priv_data; - MpegTSWriteStream *ts_st = st->priv_data; - int stream_type; - - switch (st->codecpar->codec_id) { - case AV_CODEC_ID_MPEG1VIDEO: - case AV_CODEC_ID_MPEG2VIDEO: - stream_type = STREAM_TYPE_VIDEO_MPEG2; - break; - case AV_CODEC_ID_MPEG4: - stream_type = STREAM_TYPE_VIDEO_MPEG4; - break; - case AV_CODEC_ID_H264: - stream_type = STREAM_TYPE_VIDEO_H264; - break; - case AV_CODEC_ID_HEVC: - stream_type = STREAM_TYPE_VIDEO_HEVC; - break; - case AV_CODEC_ID_CAVS: - stream_type = STREAM_TYPE_VIDEO_CAVS; - break; - case AV_CODEC_ID_DIRAC: - stream_type = STREAM_TYPE_VIDEO_DIRAC; - break; - case AV_CODEC_ID_VC1: - stream_type = STREAM_TYPE_VIDEO_VC1; - break; - case AV_CODEC_ID_MP2: - case AV_CODEC_ID_MP3: - if ( st->codecpar->sample_rate > 0 - && st->codecpar->sample_rate < 32000) { - stream_type = STREAM_TYPE_AUDIO_MPEG2; - } else { - stream_type = STREAM_TYPE_AUDIO_MPEG1; - } - break; - case AV_CODEC_ID_AAC: - stream_type = (ts->flags & MPEGTS_FLAG_AAC_LATM) - ? STREAM_TYPE_AUDIO_AAC_LATM - : STREAM_TYPE_AUDIO_AAC; - break; - case AV_CODEC_ID_AAC_LATM: - stream_type = STREAM_TYPE_AUDIO_AAC_LATM; - break; - case AV_CODEC_ID_AC3: - stream_type = (ts->flags & MPEGTS_FLAG_SYSTEM_B) - ? STREAM_TYPE_PRIVATE_DATA - : STREAM_TYPE_AUDIO_AC3; - break; - case AV_CODEC_ID_EAC3: - stream_type = (ts->flags & MPEGTS_FLAG_SYSTEM_B) - ? STREAM_TYPE_PRIVATE_DATA - : STREAM_TYPE_AUDIO_EAC3; - break; - case AV_CODEC_ID_DTS: - stream_type = STREAM_TYPE_AUDIO_DTS; - break; - case AV_CODEC_ID_TRUEHD: - stream_type = STREAM_TYPE_AUDIO_TRUEHD; - break; - case AV_CODEC_ID_OPUS: - stream_type = STREAM_TYPE_PRIVATE_DATA; - break; - case AV_CODEC_ID_TIMED_ID3: - stream_type = STREAM_TYPE_METADATA; - break; - case AV_CODEC_ID_DVB_SUBTITLE: - case AV_CODEC_ID_DVB_TELETEXT: - stream_type = STREAM_TYPE_PRIVATE_DATA; - break; - case AV_CODEC_ID_SMPTE_KLV: - if (st->codecpar->profile == FF_PROFILE_KLVA_SYNC) { - stream_type = STREAM_TYPE_METADATA; - } else { - stream_type = STREAM_TYPE_PRIVATE_DATA; - } - break; - default: - av_log_once(s, AV_LOG_WARNING, AV_LOG_DEBUG, &ts_st->data_st_warning, - "Stream %d, codec %s, is muxed as a private data stream " - "and may not be recognized upon reading.\n", st->index, - avcodec_get_name(st->codecpar->codec_id)); - stream_type = STREAM_TYPE_PRIVATE_DATA; - break; - } - - return stream_type; -} - -static int get_m2ts_stream_type(AVFormatContext *s, AVStream *st) -{ - int stream_type; - MpegTSWriteStream *ts_st = st->priv_data; - - switch (st->codecpar->codec_id) { - case AV_CODEC_ID_MPEG2VIDEO: - stream_type = STREAM_TYPE_VIDEO_MPEG2; - break; - case AV_CODEC_ID_H264: - stream_type = STREAM_TYPE_VIDEO_H264; - break; - case AV_CODEC_ID_VC1: - stream_type = STREAM_TYPE_VIDEO_VC1; - break; - case AV_CODEC_ID_HEVC: - stream_type = STREAM_TYPE_VIDEO_HEVC; - break; - case AV_CODEC_ID_PCM_BLURAY: - stream_type = 0x80; - break; - case AV_CODEC_ID_AC3: - stream_type = 0x81; - break; - case AV_CODEC_ID_DTS: - stream_type = (st->codecpar->channels > 6) ? 0x85 : 0x82; - break; - case AV_CODEC_ID_TRUEHD: - stream_type = 0x83; - break; - case AV_CODEC_ID_EAC3: - stream_type = 0x84; - break; - case AV_CODEC_ID_HDMV_PGS_SUBTITLE: - stream_type = 0x90; - break; - case AV_CODEC_ID_HDMV_TEXT_SUBTITLE: - stream_type = 0x92; - break; - default: - av_log_once(s, AV_LOG_WARNING, AV_LOG_DEBUG, &ts_st->data_st_warning, - "Stream %d, codec %s, is muxed as a private data stream " - "and may not be recognized upon reading.\n", st->index, - avcodec_get_name(st->codecpar->codec_id)); - stream_type = STREAM_TYPE_PRIVATE_DATA; - break; - } - - return stream_type; -} - static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) { MpegTSWrite *ts = s->priv_data; @@ -436,14 +292,6 @@ static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) q += 2; /* patched after */ /* put program info here */ - if (ts->m2ts_mode) { - put_registration_descriptor(&q, MKTAG('H', 'D', 'M', 'V')); - *q++ = 0x88; // descriptor_tag - hdmv_copy_control_descriptor - *q++ = 0x04; // descriptor_length - put16(&q, 0x0fff); // CA_System_ID - *q++ = 0xfc; // private_data_byte - *q++ = 0xfc; // private_data_byte - } val = 0xf000 | (q - program_info_length_ptr - 2); program_info_length_ptr[0] = val >> 8; @@ -472,8 +320,72 @@ static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service) err = 1; break; } - - stream_type = ts->m2ts_mode ? get_m2ts_stream_type(s, st) : get_dvb_stream_type(s, st); + switch (st->codecpar->codec_id) { + case AV_CODEC_ID_MPEG1VIDEO: + case AV_CODEC_ID_MPEG2VIDEO: + stream_type = STREAM_TYPE_VIDEO_MPEG2; + break; + case AV_CODEC_ID_MPEG4: + stream_type = STREAM_TYPE_VIDEO_MPEG4; + break; + case AV_CODEC_ID_H264: + stream_type = STREAM_TYPE_VIDEO_H264; + break; + case AV_CODEC_ID_HEVC: + stream_type = STREAM_TYPE_VIDEO_HEVC; + break; + case AV_CODEC_ID_CAVS: + stream_type = STREAM_TYPE_VIDEO_CAVS; + break; + case AV_CODEC_ID_DIRAC: + stream_type = STREAM_TYPE_VIDEO_DIRAC; + break; + case AV_CODEC_ID_VC1: + stream_type = STREAM_TYPE_VIDEO_VC1; + break; + case AV_CODEC_ID_MP2: + case AV_CODEC_ID_MP3: + if ( st->codecpar->sample_rate > 0 + && st->codecpar->sample_rate < 32000) { + stream_type = STREAM_TYPE_AUDIO_MPEG2; + } else { + stream_type = STREAM_TYPE_AUDIO_MPEG1; + } + break; + case AV_CODEC_ID_AAC: + stream_type = (ts->flags & MPEGTS_FLAG_AAC_LATM) + ? STREAM_TYPE_AUDIO_AAC_LATM + : STREAM_TYPE_AUDIO_AAC; + break; + case AV_CODEC_ID_AAC_LATM: + stream_type = STREAM_TYPE_AUDIO_AAC_LATM; + break; + case AV_CODEC_ID_AC3: + stream_type = (ts->flags & MPEGTS_FLAG_SYSTEM_B) + ? STREAM_TYPE_PRIVATE_DATA + : STREAM_TYPE_AUDIO_AC3; + break; + case AV_CODEC_ID_EAC3: + stream_type = (ts->flags & MPEGTS_FLAG_SYSTEM_B) + ? STREAM_TYPE_PRIVATE_DATA + : STREAM_TYPE_AUDIO_EAC3; + break; + case AV_CODEC_ID_DTS: + stream_type = STREAM_TYPE_AUDIO_DTS; + break; + case AV_CODEC_ID_TRUEHD: + stream_type = STREAM_TYPE_AUDIO_TRUEHD; + break; + case AV_CODEC_ID_OPUS: + stream_type = STREAM_TYPE_PRIVATE_DATA; + break; + case AV_CODEC_ID_TIMED_ID3: + stream_type = STREAM_TYPE_METADATA; + break; + default: + stream_type = STREAM_TYPE_PRIVATE_DATA; + break; + } *q++ = stream_type; put16(&q, 0xe000 | ts_st->pid); @@ -736,7 +648,7 @@ static void mpegts_write_sdt(AVFormatContext *s) int i, running_status, free_ca_mode, val; q = data; - put16(&q, ts->original_network_id); + put16(&q, ts->onid); *q++ = 0xff; for (i = 0; i < ts->nb_services; i++) { service = ts->services[i]; @@ -762,7 +674,7 @@ static void mpegts_write_sdt(AVFormatContext *s) desc_list_len_ptr[0] = val >> 8; desc_list_len_ptr[1] = val; } - mpegts_write_section1(&ts->sdt, SDT_TID, ts->transport_stream_id, ts->tables_version, 0, 0, + mpegts_write_section1(&ts->sdt, SDT_TID, ts->tsid, ts->tables_version, 0, 0, data, q - data); } @@ -802,49 +714,12 @@ invalid: return 0; } -static int64_t get_pcr(const MpegTSWrite *ts, AVIOContext *pb) -{ - return av_rescale(avio_tell(pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate) + - ts->first_pcr; -} - -static void write_packet(AVFormatContext *s, const uint8_t *packet) -{ - MpegTSWrite *ts = s->priv_data; - if (ts->m2ts_mode) { - int64_t pcr = get_pcr(s->priv_data, s->pb); - uint32_t tp_extra_header = pcr % 0x3fffffff; - tp_extra_header = AV_RB32(&tp_extra_header); - avio_write(s->pb, (unsigned char *) &tp_extra_header, - sizeof(tp_extra_header)); - } - avio_write(s->pb, packet, TS_PACKET_SIZE); -} - -static void section_write_packet(MpegTSSection *s, const uint8_t *packet) -{ - AVFormatContext *ctx = s->opaque; - write_packet(ctx, packet); -} - static MpegTSService *mpegts_add_service(AVFormatContext *s, int sid, - const AVDictionary *metadata, - AVProgram *program) + const char *provider_name, + const char *name) { MpegTSWrite *ts = s->priv_data; MpegTSService *service; - AVDictionaryEntry *title, *provider; - char default_service_name[32]; - const char *service_name; - const char *provider_name; - - 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) @@ -852,92 +727,57 @@ static MpegTSService *mpegts_add_service(AVFormatContext *s, int sid, service->pmt.pid = ts->pmt_start_pid + ts->nb_services; service->sid = sid; service->pcr_pid = 0x1fff; + service->pcr_sid = 0x1fff; if (encode_str8(service->provider_name, provider_name) < 0 || - encode_str8(service->name, service_name) < 0) { + encode_str8(service->name, 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) +static void mpegts_prefix_m2ts_header(AVFormatContext *s) { 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; + if (ts->m2ts_mode) { + uint32_t tp_extra_header = ts->pcr % 0x3fffffff; + tp_extra_header = AV_RB32(&tp_extra_header); + avio_write(s->pb, (unsigned char *) &tp_extra_header, + sizeof(tp_extra_header)); } - - // 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) +static void section_write_packet(MpegTSSection *s, const uint8_t *packet) { - 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)); - } - } + AVFormatContext *ctx = s->opaque; + mpegts_prefix_m2ts_header(ctx); + avio_write(ctx->pb, packet, TS_PACKET_SIZE); } static int mpegts_init(AVFormatContext *s) { MpegTSWrite *ts = s->priv_data; + MpegTSWriteStream *ts_st; + MpegTSService *service; + AVStream *st, *pcr_st = NULL; + AVDictionaryEntry *title, *provider; + double clk_rate; 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 */ + s->max_delay = 0; + ts->delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE); + if (ts->m2ts_mode == -1) { if (av_match_ext(s->url, "m2ts")) { ts->m2ts_mode = 1; @@ -946,36 +786,53 @@ static int mpegts_init(AVFormatContext *s) } } - ts->m2ts_video_pid = M2TS_VIDEO_PID; - ts->m2ts_audio_pid = M2TS_AUDIO_START_PID; - ts->m2ts_pgssub_pid = M2TS_PGSSUB_START_PID; - ts->m2ts_textsub_pid = M2TS_TEXTSUB_PID; - - if (ts->m2ts_mode) { - ts->pmt_start_pid = M2TS_PMT_PID; - if (s->nb_programs > 1) { - av_log(s, AV_LOG_ERROR, "Only one program is allowed in m2ts mode!\n"); - return AVERROR(EINVAL); - } - } - - if (s->max_delay < 0) /* Not set by the caller */ - s->max_delay = 0; - // round up to a whole number of TS packets ts->pes_payload_size = (ts->pes_payload_size + 14 + 183) / 184 * 184 - 14; + ts->tsid = ts->transport_stream_id; + ts->onid = ts->original_network_id; if (!s->nb_programs) { /* allocate a single DVB service */ - if (!mpegts_add_service(s, ts->service_id, s->metadata, NULL)) + 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; + service = mpegts_add_service(s, ts->service_id, + provider_name, service_name); + + if (!service) return AVERROR(ENOMEM); + + service->pmt.write_packet = section_write_packet; + service->pmt.opaque = s; + service->pmt.cc = 15; + service->pmt.discontinuity= ts->flags & MPEGTS_FLAG_DISCONT; } else { for (i = 0; i < s->nb_programs; i++) { AVProgram *program = s->programs[i]; - if (!mpegts_add_service(s, program->id, program->metadata, program)) + 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; + provider = av_dict_get(program->metadata, "service_provider", NULL, 0); + provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME; + service = mpegts_add_service(s, program->id, + provider_name, service_name); + + if (!service) return AVERROR(ENOMEM); + + 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; } } + if (ts->m2ts_mode > 1) + service->pmt.pid = 0x00ff + ts->service_id; ts->pat.pid = PAT_PID; /* Initialize at 15 so that it wraps and is equal to 0 for the @@ -991,158 +848,175 @@ static int mpegts_init(AVFormatContext *s) ts->sdt.write_packet = section_write_packet; ts->sdt.opaque = s; + pids = av_malloc_array(s->nb_streams, sizeof(*pids)); + if (!pids) { + ret = AVERROR(ENOMEM); + goto fail; + } + /* assign pids to each stream */ for (i = 0; i < s->nb_streams; i++) { - AVStream *st = s->streams[i]; - MpegTSWriteStream *ts_st; + AVProgram *program; + st = s->streams[i]; ts_st = av_mallocz(sizeof(MpegTSWriteStream)); if (!ts_st) { - return AVERROR(ENOMEM); + ret = AVERROR(ENOMEM); + goto fail; } st->priv_data = ts_st; + ts_st->user_tb = st->time_base; avpriv_set_pts_info(st, 33, 1, 90000); ts_st->payload = av_mallocz(ts->pes_payload_size); if (!ts_st->payload) { - return AVERROR(ENOMEM); + ret = AVERROR(ENOMEM); + goto fail; } - /* MPEG pid values < 16 are reserved. Applications which set st->id in - * this range are assigned a calculated pid. */ - if (st->id < 16) { - if (ts->m2ts_mode) { - switch (st->codecpar->codec_type) { - case AVMEDIA_TYPE_VIDEO: - ts_st->pid = ts->m2ts_video_pid++; - break; - case AVMEDIA_TYPE_AUDIO: - ts_st->pid = ts->m2ts_audio_pid++; - break; - case AVMEDIA_TYPE_SUBTITLE: - switch (st->codecpar->codec_id) { - case AV_CODEC_ID_HDMV_PGS_SUBTITLE: - ts_st->pid = ts->m2ts_pgssub_pid++; - break; - case AV_CODEC_ID_HDMV_TEXT_SUBTITLE: - ts_st->pid = ts->m2ts_textsub_pid++; - break; - } + program = av_find_program_from_stream(s, NULL, i); + if (program) { + for (j = 0; j < ts->nb_services; j++) { + if (ts->services[j]->program == program) { + service = ts->services[j]; break; } - if (ts->m2ts_video_pid > M2TS_VIDEO_PID + 1 || - ts->m2ts_audio_pid > M2TS_AUDIO_START_PID + 32 || - ts->m2ts_pgssub_pid > M2TS_PGSSUB_START_PID + 32 || - ts->m2ts_textsub_pid > M2TS_TEXTSUB_PID + 1 || - ts_st->pid < 16) { - av_log(s, AV_LOG_ERROR, "Cannot automatically assign PID for stream %d\n", st->index); - return AVERROR(EINVAL); - } - } else { - ts_st->pid = ts->start_pid + i; } - } else { - ts_st->pid = st->id; } - if (ts_st->pid >= 0x1FFF) { + + ts_st->service = service; + /* MPEG pid values < 16 are reserved. Applications which set st->id in + * this range are assigned a calculated pid. */ + if (st->id < 16) { + ts_st->pid = ts->start_pid + i; + } else if (st->id < 0x1FFF) { + ts_st->pid = st->id; + } else { av_log(s, AV_LOG_ERROR, "Invalid stream id %d, must be less than 8191\n", st->id); - return AVERROR(EINVAL); + ret = AVERROR(EINVAL); + goto fail; } - for (j = 0; j < ts->nb_services; j++) { - if (ts->services[j]->pmt.pid > LAST_OTHER_PID) { - av_log(s, AV_LOG_ERROR, - "Invalid PMT PID %d, must be less than %d\n", ts->services[j]->pmt.pid, LAST_OTHER_PID + 1); - return AVERROR(EINVAL); - } - if (ts_st->pid == ts->services[j]->pmt.pid) { - av_log(s, AV_LOG_ERROR, "PID %d cannot be both elementary and PMT PID\n", ts_st->pid); - return AVERROR(EINVAL); - } + if (ts_st->pid == service->pmt.pid) { + av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", ts_st->pid); + ret = AVERROR(EINVAL); + goto fail; } for (j = 0; j < i; j++) { - MpegTSWriteStream *ts_st_prev = s->streams[j]->priv_data; - if (ts_st_prev->pid == ts_st->pid) { + if (pids[j] == ts_st->pid) { av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", ts_st->pid); - return AVERROR(EINVAL); + ret = AVERROR(EINVAL); + goto fail; } } + pids[i] = ts_st->pid; ts_st->payload_pts = AV_NOPTS_VALUE; ts_st->payload_dts = AV_NOPTS_VALUE; 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_sid == 0x1fff) + pcr_st = st; + if (st->codecpar->codec_id == AV_CODEC_ID_AAC && st->codecpar->extradata_size > 0) { AVStream *ast; ts_st->amux = avformat_alloc_context(); if (!ts_st->amux) { - return AVERROR(ENOMEM); + ret = AVERROR(ENOMEM); + goto fail; } ts_st->amux->oformat = av_guess_format((ts->flags & MPEGTS_FLAG_AAC_LATM) ? "latm" : "adts", NULL, NULL); if (!ts_st->amux->oformat) { - return AVERROR(EINVAL); + ret = AVERROR(EINVAL); + goto fail; } if (!(ast = avformat_new_stream(ts_st->amux, NULL))) { - return AVERROR(ENOMEM); + ret = AVERROR(ENOMEM); + goto fail; } ret = avcodec_parameters_copy(ast->codecpar, st->codecpar); if (ret != 0) - return ret; + goto fail; ast->time_base = st->time_base; ret = avformat_write_header(ts_st->amux, NULL); if (ret < 0) - return ret; + goto fail; } if (st->codecpar->codec_id == AV_CODEC_ID_OPUS) { ts_st->opus_pending_trim_start = st->codecpar->initial_padding * 48000 / st->codecpar->sample_rate; } } + av_freep(&pids); + + /* if no video stream, use the first stream as PCR */ + if (!pcr_st && s->nb_streams > 0) + pcr_st = s->streams[0]; + if (!pcr_st) { + av_log(s, AV_LOG_ERROR, "no streams\n"); + ret = AVERROR(EINVAL); + goto fail; + } + ts_st = pcr_st->priv_data; + if (service->pcr_sid == 0x1fff) + service->pcr_sid = ts_st->pid; + if (service->pcr_pid == 0x1fff) + service->pcr_pid = ts->m2ts_mode > 1 ? + 0x1000 + ts->service_id : service->pcr_sid ; + if (service->pmt.pid == service->pcr_pid) { + av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", service->pcr_pid); + ret = AVERROR(EINVAL); + goto fail; + } + + clk_rate = ts->mux_rate > 1 ? ts->mux_rate : PCR_TIME_BASE; + ts->sdt_packet_period = ts->sdt_period < 0 ? -1 : ts->sdt_period/1000 * clk_rate; + ts->pat_packet_period = ts->pat_period/1000 * clk_rate; + service->pcr_packet_period = ts->pcr_period/1000 * clk_rate; + if (service->pcr_packet_period < (TS_PACKET_SIZE*8*10)) + service->pcr_packet_period = (TS_PACKET_SIZE*8*10); + av_log(s, AV_LOG_VERBOSE, "clk_rate %f: ticks/pkt %d pcr, %d sdt, %d pmt\n", clk_rate, + (int)service->pcr_packet_period, (int)ts->sdt_packet_period, (int)ts->pat_packet_period); + if (ts->copyts < 1) ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE); - select_pcr_streams(s); - - ts->last_pat_ts = AV_NOPTS_VALUE; - ts->last_sdt_ts = AV_NOPTS_VALUE; - ts->pat_period = av_rescale(ts->pat_period_us, PCR_TIME_BASE, AV_TIME_BASE); - ts->sdt_period = av_rescale(ts->sdt_period_us, PCR_TIME_BASE, AV_TIME_BASE); + // output a PCR as soon as possible + ts->pcr = 0; + service->pcr_packet_timer = 0; + ts->pat_packet_timer = 0; + ts->sdt_packet_timer = 0; if (ts->mux_rate == 1) 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, - "sdt every %"PRId64" ms, pat/pmt every %"PRId64" ms\n", - av_rescale(ts->sdt_period, 1000, PCR_TIME_BASE), - av_rescale(ts->pat_period, 1000, PCR_TIME_BASE)); return 0; + +fail: + av_freep(&pids); + return ret; } /* send SDT, PAT and PMT tables regularly */ -static void retransmit_si_info(AVFormatContext *s, int force_pat, int force_sdt, int64_t pcr) +static void retransmit_si_info(AVFormatContext *s, int force_pat, int64_t dts) { MpegTSWrite *ts = s->priv_data; int i; - if ((pcr != AV_NOPTS_VALUE && ts->last_sdt_ts == AV_NOPTS_VALUE) || - (pcr != AV_NOPTS_VALUE && pcr - ts->last_sdt_ts >= ts->sdt_period) || - force_sdt - ) { - if (pcr != AV_NOPTS_VALUE) - ts->last_sdt_ts = FFMAX(pcr, ts->last_sdt_ts); + if ( ts->sdt_packet_period >= 0 && ts->pcr >= ts->sdt_packet_timer ) { + ts->sdt_packet_timer = ts->pcr + ts->sdt_packet_period; mpegts_write_sdt(s); } - if ((pcr != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE) || - (pcr != AV_NOPTS_VALUE && pcr - ts->last_pat_ts >= ts->pat_period) || - force_pat) { - if (pcr != AV_NOPTS_VALUE) - ts->last_pat_ts = FFMAX(pcr, ts->last_pat_ts); + if (ts->pcr >= ts->pat_packet_timer || force_pat) { + ts->pat_packet_timer = ts->pcr + ts->pat_packet_period; mpegts_write_pat(s); for (i = 0; i < ts->nb_services; i++) mpegts_write_pmt(s, ts->services[i]); @@ -1175,7 +1049,8 @@ static void mpegts_insert_null_packet(AVFormatContext *s) *q++ = 0xff; *q++ = 0x10; memset(q, 0x0FF, TS_PACKET_SIZE - (q - buf)); - write_packet(s, buf); + mpegts_prefix_m2ts_header(s); + avio_write(s->pb, buf, TS_PACKET_SIZE); } /* Write a single transport stream packet with a PCR and no payload */ @@ -1183,13 +1058,14 @@ static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st) { MpegTSWrite *ts = s->priv_data; MpegTSWriteStream *ts_st = st->priv_data; + uint32_t pcr_pid = ts_st->service->pcr_pid; uint8_t *q; uint8_t buf[TS_PACKET_SIZE]; q = buf; *q++ = 0x47; - *q++ = ts_st->pid >> 8; - *q++ = ts_st->pid; + *q++ = pcr_pid >> 8; + *q++ = pcr_pid; *q++ = 0x20 | ts_st->cc; /* Adaptation only */ /* Continuity Count field does not increment (see 13818-1 section 2.4.3.3) */ *q++ = TS_PACKET_SIZE - 5; /* Adaptation Field Length */ @@ -1200,11 +1076,12 @@ static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st) } /* PCR coded into 6 bytes */ - q += write_pcr_bits(q, get_pcr(ts, s->pb)); + q += write_pcr_bits(q, ts->pcr); /* stuffing bytes */ memset(q, 0xFF, TS_PACKET_SIZE - (q - buf)); - write_packet(s, buf); + mpegts_prefix_m2ts_header(s); + avio_write(s->pb, buf, TS_PACKET_SIZE); } static void write_pts(uint8_t *q, int fourbits, int64_t pts) @@ -1268,84 +1145,55 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, uint8_t *q; int val, is_start, len, header_len, write_pcr, is_dvb_subtitle, is_dvb_teletext, flags; int afc_len, stuffing_len; - int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE); int force_pat = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key; - int force_sdt = 0; av_assert0(ts_st->payload != buf || st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO); if (ts->flags & MPEGTS_FLAG_PAT_PMT_AT_FRAMES && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { force_pat = 1; } - if (ts->flags & MPEGTS_FLAG_REEMIT_PAT_PMT) { - force_pat = 1; - force_sdt = 1; - ts->flags &= ~MPEGTS_FLAG_REEMIT_PAT_PMT; - } - is_start = 1; while (payload_size > 0) { - int64_t pcr = AV_NOPTS_VALUE; - if (ts->mux_rate > 1) - pcr = get_pcr(ts, s->pb); - else if (dts != AV_NOPTS_VALUE) - pcr = (dts - delay) * 300; + ts->pcr = ts->first_pcr + (ts->mux_rate == 1 ? + (dts == AV_NOPTS_VALUE ? 0 : (dts - ts->delay) * 300) : + // add 11, pcr references the last byte of program clock reference base + av_rescale(avio_tell(s->pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate)); - retransmit_si_info(s, force_pat, force_sdt, pcr); + retransmit_si_info(s, force_pat, dts); force_pat = 0; - force_sdt = 0; write_pcr = 0; - if (ts->mux_rate > 1) { - /* Send PCR packets for all PCR streams if needed */ - pcr = get_pcr(ts, s->pb); - if (pcr >= ts->next_pcr) { - int64_t next_pcr = INT64_MAX; - for (int i = 0; i < s->nb_streams; i++) { - /* Make the current stream the last, because for that we - * can insert the pcr into the payload later */ - int st2_index = i < st->index ? i : (i + 1 == s->nb_streams ? st->index : i + 1); - AVStream *st2 = s->streams[st2_index]; - MpegTSWriteStream *ts_st2 = st2->priv_data; - if (ts_st2->pcr_period) { - if (pcr - ts_st2->last_pcr >= ts_st2->pcr_period) { - ts_st2->last_pcr = FFMAX(pcr - ts_st2->pcr_period, ts_st2->last_pcr + ts_st2->pcr_period); - if (st2 != st) { - mpegts_insert_pcr_only(s, st2); - pcr = get_pcr(ts, s->pb); - } else { - write_pcr = 1; - } - } - next_pcr = FFMIN(next_pcr, ts_st2->last_pcr + ts_st2->pcr_period); - } - } - ts->next_pcr = next_pcr; - } - if (dts != AV_NOPTS_VALUE && (dts - pcr / 300) > delay) { - /* pcr insert gets priority over null packet insert */ - if (write_pcr) - mpegts_insert_pcr_only(s, st); - else - mpegts_insert_null_packet(s); - /* recalculate write_pcr and possibly retransmit si_info */ - continue; - } - } else if (ts_st->pcr_period && pcr != AV_NOPTS_VALUE) { - if (pcr - ts_st->last_pcr >= ts_st->pcr_period && is_start) { - ts_st->last_pcr = FFMAX(pcr - ts_st->pcr_period, ts_st->last_pcr + ts_st->pcr_period); + if (ts_st->pid == ts_st->service->pcr_sid) { + if( ts->pcr >= ts_st->service->pcr_packet_timer ) { + ts_st->service->pcr_packet_timer = ts->pcr + ts_st->service->pcr_packet_period; write_pcr = 1; } } + if (write_pcr && ts_st->service->pcr_sid != ts_st->service->pcr_pid) { + mpegts_insert_pcr_only(s, st); + continue; + } + if (ts->mux_rate > 1 && dts != AV_NOPTS_VALUE && + (dts - ts->pcr / 300) > ts->delay) { + /* pcr insert gets priority over null packet insert */ + if (write_pcr) + mpegts_insert_pcr_only(s, st); + else + mpegts_insert_null_packet(s); + /* recalculate write_pcr and possibly retransimit si_info */ + continue; + } /* prepare packet header */ q = buf; *q++ = 0x47; val = ts_st->pid >> 8; - if (ts->m2ts_mode && st->codecpar->codec_id == AV_CODEC_ID_AC3) - val |= 0x20; if (is_start) val |= 0x40; + if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && + st->codecpar->codec_id == AV_CODEC_ID_AC3 && + ts->m2ts_mode > 1) + val |= 0x20; *q++ = val; *q++ = ts_st->pid; ts_st->cc = ts_st->cc + 1 & 0xf; @@ -1357,7 +1205,7 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, } if (key && is_start && pts != AV_NOPTS_VALUE) { // set Random Access for key frames - if (ts_st->pcr_period) + if (ts_st->pid == ts_st->service->pcr_sid) write_pcr = 1; set_af_flag(buf, 0x40); q = get_ts_payload_start(buf); @@ -1365,10 +1213,10 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, if (write_pcr) { set_af_flag(buf, 0x10); q = get_ts_payload_start(buf); - // add 11, pcr references the last byte of program clock reference base - if (dts != AV_NOPTS_VALUE && dts < pcr / 300) + if (ts->mux_rate > 1) + if (dts != AV_NOPTS_VALUE && dts < ts->pcr / 300) av_log(s, AV_LOG_WARNING, "dts < pcr, TS is invalid\n"); - extend_af(buf, write_pcr_bits(q, pcr)); + extend_af(buf, write_pcr_bits(q, ts->pcr)); q = get_ts_payload_start(buf); } if (is_start) { @@ -1439,10 +1287,10 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, if (ts->m2ts_mode && st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->codec_id == AV_CODEC_ID_AC3) { - /* set PES_extension_flag */ - pes_extension = 1; - flags |= 0x01; - header_len += 3; + /* set PES_extension_flag */ + pes_extension = 1; + flags |= 0x01; + header_len += 3; } if (is_dvb_teletext) { pes_header_stuffing_bytes = 0x24 - header_len; @@ -1469,11 +1317,13 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, *q++ = flags; *q++ = header_len; if (pts != AV_NOPTS_VALUE) { - write_pts(q, flags >> 6, pts); + int64_t ts_pts = pts + ts->ts_offset; + write_pts(q, flags >> 6, ts_pts); q += 5; } if (dts != AV_NOPTS_VALUE && pts != AV_NOPTS_VALUE && dts != pts) { - write_pts(q, 1, dts); + int64_t ts_dts = dts + ts->ts_offset; + write_pts(q, 1, ts_dts); q += 5; } if (pes_extension && st->codecpar->codec_id == AV_CODEC_ID_DIRAC) { @@ -1485,14 +1335,14 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, *q++ = 0x00 | 0x60; } /* For Blu-ray AC3 Audio Setting extended flags */ - if (ts->m2ts_mode && - pes_extension && - st->codecpar->codec_id == AV_CODEC_ID_AC3) { - flags = 0x01; /* set PES_extension_flag_2 */ - *q++ = flags; - *q++ = 0x80 | 0x01; /* marker bit + extension length */ - *q++ = 0x00 | 0x71; /* for AC3 Audio (specifically on blue-rays) */ - } + if (ts->m2ts_mode && + pes_extension && + st->codecpar->codec_id == AV_CODEC_ID_AC3) { + flags = 0x01; /* set PES_extension_flag_2 */ + *q++ = flags; + *q++ = 0x80 | 0x01; /* marker bit + extension length */ + *q++ = 0x00 | 0x71; /* for AC3 Audio (specifically on blue-rays) */ + } if (is_dvb_subtitle) { @@ -1546,7 +1396,8 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, payload += len; payload_size -= len; - write_packet(s, buf); + mpegts_prefix_m2ts_header(s); + avio_write(s->pb, buf, TS_PACKET_SIZE); } ts_st->prev_payload_key = key; } @@ -1643,8 +1494,6 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) uint8_t *data = NULL; MpegTSWrite *ts = s->priv_data; MpegTSWriteStream *ts_st = st->priv_data; - const int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE) * 2; - const int64_t max_audio_delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE) / 2; int64_t dts = pkt->dts, pts = pkt->pts; int opus_samples = 0; int side_data_size; @@ -1657,11 +1506,23 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) if (side_data) stream_id = side_data[0]; + if (ts->reemit_pat_pmt) { + av_log(s, AV_LOG_WARNING, + "resend_headers option is deprecated, use -mpegts_flags resend_headers\n"); + ts->reemit_pat_pmt = 0; + ts->flags |= MPEGTS_FLAG_REEMIT_PAT_PMT; + } + + if (ts->flags & MPEGTS_FLAG_REEMIT_PAT_PMT) { + ts->pat_packet_timer = ts->sdt_packet_timer = 0; + ts->flags &= ~MPEGTS_FLAG_REEMIT_PAT_PMT; + } + if (ts->copyts < 1) { if (pts != AV_NOPTS_VALUE) - pts += delay; + pts += 2*ts->delay; if (dts != AV_NOPTS_VALUE) - dts += delay; + dts += 2*ts->delay; } if (ts_st->first_pts_check && pts == AV_NOPTS_VALUE) { @@ -1724,7 +1585,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) ret = avio_open_dyn_buf(&ts_st->amux->pb); if (ret < 0) - return ret; + return AVERROR(ENOMEM); ret = av_write_frame(ts_st->amux, &pkt2); if (ret < 0) { @@ -1755,7 +1616,7 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) } while (p < buf_end && (state & 0x7e) != 2*35 && (state & 0x7e) >= 2*32); - if ((state & 0x7e) < 2*16 || (state & 0x7e) >= 2*24) + if ((state & 0x7e) < 2*16 && (state & 0x7e) >= 2*24) extradd = 0; if ((state & 0x7e) != 2*35) { // AUD NAL data = av_malloc(pkt->size + 7 + extradd); @@ -1843,9 +1704,25 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) } } + if (pkt->dts != AV_NOPTS_VALUE) { + int i; + for(i=0; inb_streams; i++) { + AVStream *st2 = s->streams[i]; + MpegTSWriteStream *ts_st2 = st2->priv_data; + if ( ts_st2->payload_size + && (ts_st2->payload_dts == AV_NOPTS_VALUE || dts - ts_st2->payload_dts > ts->delay)) { + mpegts_write_pes(s, st2, ts_st2->payload, ts_st2->payload_size, + ts_st2->payload_pts, ts_st2->payload_dts, + ts_st2->payload_flags & AV_PKT_FLAG_KEY, stream_id); + ts_st2->payload_size = 0; + } + } + } + if (ts_st->payload_size && (ts_st->payload_size + size > ts->pes_payload_size || (dts != AV_NOPTS_VALUE && ts_st->payload_dts != AV_NOPTS_VALUE && - dts - ts_st->payload_dts >= max_audio_delay) || + av_compare_ts(dts - ts_st->payload_dts, st->time_base, + s->max_delay, AV_TIME_BASE_Q) >= 0) || ts_st->opus_queued_samples + opus_samples >= 5760 /* 120ms */)) { mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_size, ts_st->payload_pts, ts_st->payload_dts, @@ -1881,7 +1758,6 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) static void mpegts_write_flush(AVFormatContext *s) { - MpegTSWrite *ts = s->priv_data; int i; /* flush current packets */ @@ -1896,12 +1772,6 @@ static void mpegts_write_flush(AVFormatContext *s) ts_st->opus_queued_samples = 0; } } - - if (ts->m2ts_mode) { - int packets = (avio_tell(s->pb) / (TS_PACKET_SIZE + 4)) % 32; - while (packets++ < 32) - mpegts_insert_null_packet(s); - } } static int mpegts_write_packet(AVFormatContext *s, AVPacket *pkt) @@ -1969,62 +1839,104 @@ static int mpegts_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt return ret; } -#define OFFSET(x) offsetof(MpegTSWrite, x) -#define ENC AV_OPT_FLAG_ENCODING_PARAM static const AVOption options[] = { { "mpegts_transport_stream_id", "Set transport_stream_id field.", - OFFSET(transport_stream_id), AV_OPT_TYPE_INT, { .i64 = 0x0001 }, 0x0001, 0xffff, ENC }, + offsetof(MpegTSWrite, transport_stream_id), AV_OPT_TYPE_INT, + { .i64 = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM }, { "mpegts_original_network_id", "Set original_network_id field.", - OFFSET(original_network_id), AV_OPT_TYPE_INT, { .i64 = DVB_PRIVATE_NETWORK_START }, 0x0001, 0xffff, ENC }, + offsetof(MpegTSWrite, original_network_id), AV_OPT_TYPE_INT, + { .i64 = DVB_PRIVATE_NETWORK_START }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM }, { "mpegts_service_id", "Set service_id field.", - OFFSET(service_id), AV_OPT_TYPE_INT, { .i64 = 0x0001 }, 0x0001, 0xffff, ENC }, + offsetof(MpegTSWrite, service_id), AV_OPT_TYPE_INT, + { .i64 = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM }, { "mpegts_service_type", "Set service_type field.", - OFFSET(service_type), AV_OPT_TYPE_INT, { .i64 = 0x01 }, 0x01, 0xff, ENC, "mpegts_service_type" }, + offsetof(MpegTSWrite, service_type), AV_OPT_TYPE_INT, + { .i64 = 0x01 }, 0x01, 0xff, AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, { "digital_tv", "Digital Television.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_TV }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_TV }, 0x01, 0xff, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, { "digital_radio", "Digital Radio.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_RADIO }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_RADIO }, 0x01, 0xff, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, { "teletext", "Teletext.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_TELETEXT }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_TELETEXT }, 0x01, 0xff, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, { "advanced_codec_digital_radio", "Advanced Codec Digital Radio.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_RADIO }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_RADIO }, 0x01, 0xff, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, { "mpeg2_digital_hdtv", "MPEG2 Digital HDTV.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_MPEG2_DIGITAL_HDTV }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_MPEG2_DIGITAL_HDTV }, 0x01, 0xff, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, { "advanced_codec_digital_sdtv", "Advanced Codec Digital SDTV.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_SDTV }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_SDTV }, 0x01, 0xff, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, { "advanced_codec_digital_hdtv", "Advanced Codec Digital HDTV.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_HDTV }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_HDTV }, 0x01, 0xff, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, { "hevc_digital_hdtv", "HEVC Digital Television Service.", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_HEVC_DIGITAL_HDTV }, 0x01, 0xff, ENC, "mpegts_service_type" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_HEVC_DIGITAL_HDTV }, 0x01, 0xff, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" }, { "mpegts_pmt_start_pid", "Set the first pid of the PMT.", - OFFSET(pmt_start_pid), AV_OPT_TYPE_INT, { .i64 = 0x1000 }, FIRST_OTHER_PID, LAST_OTHER_PID, ENC }, + offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT, + { .i64 = 0x1000 }, 0x0010, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM }, + { "mpegts_pcr_start_pid", "Set the first pid of the PCR.", + offsetof(MpegTSWrite, pcr_start_pid), AV_OPT_TYPE_INT, + { .i64 = 0x1000 }, 0x0010, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM }, { "mpegts_start_pid", "Set the first pid.", - OFFSET(start_pid), AV_OPT_TYPE_INT, { .i64 = 0x0100 }, FIRST_OTHER_PID, LAST_OTHER_PID, ENC }, - { "mpegts_m2ts_mode", "Enable m2ts mode.", OFFSET(m2ts_mode), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, ENC }, - { "muxrate", NULL, OFFSET(mux_rate), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, ENC }, + offsetof(MpegTSWrite, start_pid), AV_OPT_TYPE_INT, + { .i64 = 0x0100 }, 0x0010, 0x0f00, AV_OPT_FLAG_ENCODING_PARAM }, + { "mpegts_m2ts_mode", "Enable m2ts mode.", + offsetof(MpegTSWrite, m2ts_mode), AV_OPT_TYPE_BOOL, + { .i64 = -1 }, -1, 2, AV_OPT_FLAG_ENCODING_PARAM }, + { "mpegts_pcr_offset", "clock offset.", + offsetof(MpegTSWrite, ts_offset), AV_OPT_TYPE_BOOL, + { .i64 = 0 }, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { "muxrate", NULL, + offsetof(MpegTSWrite, mux_rate), AV_OPT_TYPE_INT, + { .i64 = 1 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, { "pes_payload_size", "Minimum PES packet payload in bytes", - OFFSET(pes_payload_size), AV_OPT_TYPE_INT, { .i64 = DEFAULT_PES_PAYLOAD_SIZE }, 0, INT_MAX, ENC }, - { "mpegts_flags", "MPEG-TS muxing flags", OFFSET(flags), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, 0, INT_MAX, ENC, "mpegts_flags" }, + offsetof(MpegTSWrite, pes_payload_size), AV_OPT_TYPE_INT, + { .i64 = DEFAULT_PES_PAYLOAD_SIZE }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { "mpegts_flags", "MPEG-TS muxing flags", + offsetof(MpegTSWrite, flags), AV_OPT_TYPE_FLAGS, { .i64 = 0 }, 0, INT_MAX, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" }, { "resend_headers", "Reemit PAT/PMT before writing the next packet", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_REEMIT_PAT_PMT }, 0, INT_MAX, ENC, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_REEMIT_PAT_PMT }, 0, INT_MAX, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" }, { "latm", "Use LATM packetization for AAC", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_AAC_LATM }, 0, INT_MAX, ENC, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_AAC_LATM }, 0, INT_MAX, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" }, { "pat_pmt_at_frames", "Reemit PAT and PMT at each video frame", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_PAT_PMT_AT_FRAMES}, 0, INT_MAX, ENC, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_PAT_PMT_AT_FRAMES}, 0, INT_MAX, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" }, { "system_b", "Conform to System B (DVB) instead of System A (ATSC)", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_SYSTEM_B }, 0, INT_MAX, ENC, "mpegts_flags" }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_SYSTEM_B }, 0, INT_MAX, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" }, { "initial_discontinuity", "Mark initial packets as discontinuous", - 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_DISCONT }, 0, INT_MAX, ENC, "mpegts_flags" }, - { "mpegts_copyts", "don't offset dts/pts", OFFSET(copyts), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, ENC }, - { "tables_version", "set PAT, PMT and SDT version", OFFSET(tables_version), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 31, ENC }, + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_DISCONT }, 0, INT_MAX, + AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" }, + // backward compatibility + { "resend_headers", "Reemit PAT/PMT before writing the next packet", + offsetof(MpegTSWrite, reemit_pat_pmt), AV_OPT_TYPE_INT, + { .i64 = 0 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { "mpegts_copyts", "don't offset dts/pts", + offsetof(MpegTSWrite, copyts), AV_OPT_TYPE_BOOL, + { .i64 = -1 }, -1, 1, AV_OPT_FLAG_ENCODING_PARAM }, + { "tables_version", "set PAT, PMT and SDT version", + offsetof(MpegTSWrite, tables_version), AV_OPT_TYPE_INT, + { .i64 = 0 }, 0, 31, AV_OPT_FLAG_ENCODING_PARAM }, { "omit_video_pes_length", "Omit the PES packet length for video packets", - OFFSET(omit_video_pes_length), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, ENC }, - { "pcr_period", "PCR retransmission time in milliseconds", - OFFSET(pcr_period_ms), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, ENC }, - { "pat_period", "PAT/PMT retransmission time limit in seconds", - OFFSET(pat_period_us), AV_OPT_TYPE_DURATION, { .i64 = PAT_RETRANS_TIME * 1000LL }, 0, INT64_MAX, ENC }, - { "sdt_period", "SDT retransmission time limit in seconds", - OFFSET(sdt_period_us), AV_OPT_TYPE_DURATION, { .i64 = SDT_RETRANS_TIME * 1000LL }, 0, INT64_MAX, ENC }, + offsetof(MpegTSWrite, omit_video_pes_length), AV_OPT_TYPE_BOOL, + { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, + { "pcr_period", "PCR retransmission time limit in msecs", + offsetof(MpegTSWrite, pcr_period), AV_OPT_TYPE_DOUBLE, + { .dbl = PCR_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { "pat_period", "PAT/PMT retransmission time limit in msecs", + offsetof(MpegTSWrite, pat_period), AV_OPT_TYPE_DOUBLE, + { .dbl = PAT_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, + { "sdt_period", "SDT retransmission time limit in msecs", + offsetof(MpegTSWrite, sdt_period), AV_OPT_TYPE_DOUBLE, + { .dbl = SDT_RETRANS_TIME }, -1, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, { NULL }, };