#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
+#include "libavcodec/ac3_parser_internal.h"
#include "libavcodec/internal.h"
#include "avformat.h"
uint8_t name[256];
uint8_t provider_name[256];
int pcr_pid;
- int pcr_packet_count;
- int pcr_packet_period;
AVProgram *program;
} MpegTSService;
MpegTSSection pat; /* MPEG-2 PAT table */
MpegTSSection sdt; /* MPEG-2 SDT table context */
MpegTSService **services;
- int sdt_packet_count;
- int sdt_packet_period;
- int pat_packet_count;
- int pat_packet_period;
+ AVPacket *pkt;
+ int64_t sdt_period; /* SDT period in PCR time base */
+ int64_t pat_period; /* PAT/PMT period in PCR time base */
int nb_services;
- int onid;
- int tsid;
int64_t first_pcr;
+ int first_dts_checked;
+ int64_t next_pcr;
int mux_rate; ///< set to 1 when VBR
int pes_payload_size;
+ int64_t total_size;
int transport_stream_id;
int original_network_id;
int pmt_start_pid;
int start_pid;
int m2ts_mode;
+ int m2ts_video_pid;
+ int m2ts_audio_pid;
+ int m2ts_pgssub_pid;
+ int m2ts_textsub_pid;
- int reemit_pat_pmt; // backward compatibility
-
- int pcr_period;
+ int pcr_period_ms;
#define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01
#define MPEGTS_FLAG_AAC_LATM 0x02
#define MPEGTS_FLAG_PAT_PMT_AT_FRAMES 0x04
int flags;
int copyts;
int tables_version;
- double pat_period;
- double sdt_period;
+ int64_t pat_period_us;
+ int64_t sdt_period_us;
int64_t last_pat_ts;
int64_t last_sdt_ts;
/* mpegts writer */
#define DEFAULT_PROVIDER_NAME "FFmpeg"
-#define DEFAULT_SERVICE_NAME "Service01"
+#define DEFAULT_SERVICE_NAME "Service"
/* we retransmit the SI info at this rate */
#define SDT_RETRANS_TIME 500
#define PCR_RETRANS_TIME 20
typedef struct MpegTSWriteStream {
- struct MpegTSService *service;
int pid; /* stream associated pid */
int cc;
int discontinuity;
int payload_size;
- int first_pts_check; ///< first pts check needed
+ int first_timestamp_checked; ///< first pts/dts check needed
int prev_payload_key;
int64_t payload_pts;
int64_t payload_dts;
int payload_flags;
uint8_t *payload;
AVFormatContext *amux;
- AVRational user_tb;
+ int data_st_warning;
+
+ int64_t pcr_period; /* PCR period in PCR time base */
+ int64_t last_pcr;
/* For Opus */
int opus_queued_samples;
int opus_pending_trim_start;
+
+ DVBAC3Descriptor *dvb_ac3_desc;
} MpegTSWriteStream;
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->tsid, ts->tables_version, 0, 0,
+ mpegts_write_section1(&ts->pat, PAT_TID, ts->transport_stream_id, ts->tables_version, 0, 0,
data, q - data);
}
static void put_registration_descriptor(uint8_t **q_ptr, uint32_t tag)
{
uint8_t *q = *q_ptr;
- *q++ = 0x05; /* MPEG-2 registration descriptor*/
+ *q++ = REGISTRATION_DESCRIPTOR;
*q++ = 4;
*q++ = tag;
*q++ = tag >> 8;
*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;
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;
AVStream *st = s->streams[i];
MpegTSWriteStream *ts_st = st->priv_data;
AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0);
+ const char default_language[] = "und";
+ const char *language = lang && strlen(lang->value) >= 3 ? lang->value : default_language;
+ enum AVCodecID codec_id = st->codecpar->codec_id;
if (s->nb_programs) {
int k, found = 0;
err = 1;
break;
}
- 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;
- }
+
+ stream_type = ts->m2ts_mode ? get_m2ts_stream_type(s, st) : get_dvb_stream_type(s, st);
*q++ = stream_type;
put16(&q, 0xe000 | ts_st->pid);
/* write optional descriptors here */
switch (st->codecpar->codec_type) {
case AVMEDIA_TYPE_AUDIO:
- if (st->codecpar->codec_id==AV_CODEC_ID_AC3 && (ts->flags & MPEGTS_FLAG_SYSTEM_B)) {
- *q++=0x6a; // AC3 descriptor see A038 DVB SI
- *q++=1; // 1 byte, all flags sets to 0
- *q++=0; // omit all fields...
- }
- if (st->codecpar->codec_id==AV_CODEC_ID_EAC3 && (ts->flags & MPEGTS_FLAG_SYSTEM_B)) {
- *q++=0x7a; // EAC3 descriptor see A038 DVB SI
- *q++=1; // 1 byte, all flags sets to 0
- *q++=0; // omit all fields...
+ if (codec_id == AV_CODEC_ID_AC3)
+ put_registration_descriptor(&q, MKTAG('A', 'C', '-', '3'));
+ if (codec_id == AV_CODEC_ID_EAC3)
+ put_registration_descriptor(&q, MKTAG('E', 'A', 'C', '3'));
+ if (ts->flags & MPEGTS_FLAG_SYSTEM_B) {
+ if (codec_id == AV_CODEC_ID_AC3) {
+ DVBAC3Descriptor *dvb_ac3_desc = ts_st->dvb_ac3_desc;
+
+ *q++=0x6a; // AC3 descriptor see A038 DVB SI
+ if (dvb_ac3_desc) {
+ int len = 1 +
+ !!(dvb_ac3_desc->component_type_flag) +
+ !!(dvb_ac3_desc->bsid_flag) +
+ !!(dvb_ac3_desc->mainid_flag) +
+ !!(dvb_ac3_desc->asvc_flag);
+
+ *q++ = len;
+ *q++ = dvb_ac3_desc->component_type_flag << 7 | dvb_ac3_desc->bsid_flag << 6 |
+ dvb_ac3_desc->mainid_flag << 5 | dvb_ac3_desc->asvc_flag << 4;
+
+ if (dvb_ac3_desc->component_type_flag) *q++ = dvb_ac3_desc->component_type;
+ if (dvb_ac3_desc->bsid_flag) *q++ = dvb_ac3_desc->bsid;
+ if (dvb_ac3_desc->mainid_flag) *q++ = dvb_ac3_desc->mainid;
+ if (dvb_ac3_desc->asvc_flag) *q++ = dvb_ac3_desc->asvc;
+ } else {
+ *q++=1; // 1 byte, all flags sets to 0
+ *q++=0; // omit all fields...
+ }
+ } else if (codec_id == AV_CODEC_ID_EAC3) {
+ *q++=0x7a; // EAC3 descriptor see A038 DVB SI
+ *q++=1; // 1 byte, all flags sets to 0
+ *q++=0; // omit all fields...
+ }
}
- if (st->codecpar->codec_id==AV_CODEC_ID_S302M)
+ if (codec_id == AV_CODEC_ID_S302M)
put_registration_descriptor(&q, MKTAG('B', 'S', 'S', 'D'));
- if (st->codecpar->codec_id==AV_CODEC_ID_OPUS) {
+ if (codec_id == AV_CODEC_ID_OPUS) {
/* 6 bytes registration descriptor, 4 bytes Opus audio descriptor */
if (q - data > SECTION_LENGTH - 6 - 4) {
err = 1;
}
}
- if (lang) {
- char *p;
- char *next = lang->value;
+ if (language != default_language ||
+ st->disposition & (AV_DISPOSITION_CLEAN_EFFECTS |
+ AV_DISPOSITION_HEARING_IMPAIRED |
+ AV_DISPOSITION_VISUAL_IMPAIRED)) {
+ const char *p, *next;
uint8_t *len_ptr;
- *q++ = 0x0a; /* ISO 639 language descriptor */
+ *q++ = ISO_639_LANGUAGE_DESCRIPTOR;
len_ptr = q++;
*len_ptr = 0;
- for (p = lang->value; next && *len_ptr < 255 / 4 * 4; p = next + 1) {
+ for (p = next = language; next && *len_ptr < 255 / 4 * 4; p = next + 1) {
if (q - data > SECTION_LENGTH - 4) {
err = 1;
break;
}
break;
case AVMEDIA_TYPE_SUBTITLE:
- {
- const char default_language[] = "und";
- const char *language = lang && strlen(lang->value) >= 3 ? lang->value : default_language;
-
- if (st->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
+ if (codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
uint8_t *len_ptr;
int extradata_copied = 0;
}
*len_ptr = q - len_ptr - 1;
- } else if (st->codecpar->codec_id == AV_CODEC_ID_DVB_TELETEXT) {
+ } else if (codec_id == AV_CODEC_ID_DVB_TELETEXT) {
uint8_t *len_ptr = NULL;
int extradata_copied = 0;
*len_ptr = q - len_ptr - 1;
}
- }
break;
case AVMEDIA_TYPE_VIDEO:
if (stream_type == STREAM_TYPE_VIDEO_DIRAC) {
}
break;
case AVMEDIA_TYPE_DATA:
- if (st->codecpar->codec_id == AV_CODEC_ID_SMPTE_KLV) {
+ if (codec_id == AV_CODEC_ID_SMPTE_KLV) {
put_registration_descriptor(&q, MKTAG('K', 'L', 'V', 'A'));
- } else if (st->codecpar->codec_id == AV_CODEC_ID_TIMED_ID3) {
+ } else if (codec_id == AV_CODEC_ID_TIMED_ID3) {
const char *tag = "ID3 ";
- *q++ = 0x26; /* metadata descriptor */
+ *q++ = METADATA_DESCRIPTOR;
*q++ = 13;
put16(&q, 0xffff); /* metadata application format */
putbuf(&q, tag, strlen(tag));
int i, running_status, free_ca_mode, val;
q = data;
- put16(&q, ts->onid);
+ put16(&q, ts->original_network_id);
*q++ = 0xff;
for (i = 0; i < ts->nb_services; i++) {
service = ts->services[i];
desc_list_len_ptr[0] = val >> 8;
desc_list_len_ptr[1] = val;
}
- mpegts_write_section1(&ts->sdt, SDT_TID, ts->tsid, ts->tables_version, 0, 0,
+ mpegts_write_section1(&ts->sdt, SDT_TID, ts->transport_stream_id, ts->tables_version, 0, 0,
data, q - data);
}
return 0;
}
+static int64_t get_pcr(const MpegTSWrite *ts)
+{
+ return av_rescale(ts->total_size + 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);
+ 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);
+ ts->total_size += 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 char *provider_name,
- const char *name)
+ const AVDictionary *metadata,
+ AVProgram *program)
{
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)
service->sid = sid;
service->pcr_pid = 0x1fff;
if (encode_str8(service->provider_name, provider_name) < 0 ||
- encode_str8(service->name, 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 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 mpegts_prefix_m2ts_header(AVFormatContext *s)
+static void enable_pcr_generation_for_stream(AVFormatContext *s, AVStream *pcr_st)
{
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));
+ 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 section_write_packet(MpegTSSection *s, const uint8_t *packet)
+static void select_pcr_streams(AVFormatContext *s)
{
- AVFormatContext *ctx = s->opaque;
- mpegts_prefix_m2ts_header(ctx);
- avio_write(ctx->pb, packet, TS_PACKET_SIZE);
+ 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;
- 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 (ts->m2ts_mode == -1) {
+ if (av_match_ext(s->url, "m2ts")) {
+ ts->m2ts_mode = 1;
+ } else {
+ ts->m2ts_mode = 0;
+ }
+ }
+
+ 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 */
- 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)
+ if (!mpegts_add_service(s, ts->service_id, s->metadata, NULL))
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];
- 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)
+ if (!mpegts_add_service(s, program->id, program->metadata, program))
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;
}
}
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;
- }
+ ts->pkt = av_packet_alloc();
+ if (!ts->pkt)
+ return AVERROR(ENOMEM);
/* assign pids to each stream */
for (i = 0; i < s->nb_streams; i++) {
- AVProgram *program;
- st = s->streams[i];
+ AVStream *st = s->streams[i];
+ MpegTSWriteStream *ts_st;
ts_st = av_mallocz(sizeof(MpegTSWriteStream));
if (!ts_st) {
- ret = AVERROR(ENOMEM);
- goto fail;
+ return AVERROR(ENOMEM);
}
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) {
- ret = AVERROR(ENOMEM);
- goto fail;
- }
-
- 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;
- }
- }
+ return AVERROR(ENOMEM);
}
- 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;
+ 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;
+ }
+ 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) {
av_log(s, AV_LOG_ERROR,
"Invalid stream id %d, must be less than 8191\n", st->id);
- ret = AVERROR(EINVAL);
- goto fail;
+ 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 < 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);
+ }
}
for (j = 0; j < i; j++) {
- if (pids[j] == ts_st->pid) {
+ MpegTSWriteStream *ts_st_prev = s->streams[j]->priv_data;
+ if (ts_st_prev->pid == ts_st->pid) {
av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", ts_st->pid);
- ret = AVERROR(EINVAL);
- goto fail;
+ return AVERROR(EINVAL);
}
}
- 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_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;
ts_st->amux = avformat_alloc_context();
if (!ts_st->amux) {
- ret = AVERROR(ENOMEM);
- goto fail;
+ return AVERROR(ENOMEM);
}
ts_st->amux->oformat =
av_guess_format((ts->flags & MPEGTS_FLAG_AAC_LATM) ? "latm" : "adts",
NULL, NULL);
if (!ts_st->amux->oformat) {
- ret = AVERROR(EINVAL);
- goto fail;
+ return AVERROR(EINVAL);
}
if (!(ast = avformat_new_stream(ts_st->amux, NULL))) {
- ret = AVERROR(ENOMEM);
- goto fail;
+ return AVERROR(ENOMEM);
}
ret = avcodec_parameters_copy(ast->codecpar, st->codecpar);
if (ret != 0)
- goto fail;
+ return ret;
ast->time_base = st->time_base;
ret = avformat_write_header(ts_st->amux, NULL);
if (ret < 0)
- goto fail;
+ return ret;
}
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 (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 /
- (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;
- 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;
- }
+ 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;
- // The user specified a period, use only it
- if (ts->pat_period < INT_MAX/2) {
- ts->pat_packet_period = INT_MAX;
- }
- if (ts->sdt_period < INT_MAX/2) {
- ts->sdt_packet_period = INT_MAX;
- }
-
- // 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;
+ 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);
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,
- "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->url, "m2ts")) {
- ts->m2ts_mode = 1;
- } else {
- ts->m2ts_mode = 0;
- }
- }
+ "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, int64_t dts)
+static void retransmit_si_info(AVFormatContext *s, int force_pat, int force_sdt, int64_t pcr)
{
MpegTSWrite *ts = s->priv_data;
int i;
- if (++ts->sdt_packet_count == ts->sdt_packet_period ||
- (dts != AV_NOPTS_VALUE && ts->last_sdt_ts == AV_NOPTS_VALUE) ||
- (dts != AV_NOPTS_VALUE && dts - ts->last_sdt_ts >= ts->sdt_period*90000.0)
+ 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
) {
- ts->sdt_packet_count = 0;
- if (dts != AV_NOPTS_VALUE)
- ts->last_sdt_ts = FFMAX(dts, ts->last_sdt_ts);
+ if (pcr != AV_NOPTS_VALUE)
+ ts->last_sdt_ts = FFMAX(pcr, ts->last_sdt_ts);
mpegts_write_sdt(s);
}
- if (++ts->pat_packet_count == ts->pat_packet_period ||
- (dts != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE) ||
- (dts != AV_NOPTS_VALUE && dts - ts->last_pat_ts >= ts->pat_period*90000.0) ||
+ 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) {
- ts->pat_packet_count = 0;
- if (dts != AV_NOPTS_VALUE)
- ts->last_pat_ts = FFMAX(dts, ts->last_pat_ts);
+ if (pcr != AV_NOPTS_VALUE)
+ ts->last_pat_ts = FFMAX(pcr, ts->last_pat_ts);
mpegts_write_pat(s);
for (i = 0; i < ts->nb_services; i++)
mpegts_write_pmt(s, ts->services[i]);
*q++ = 0xff;
*q++ = 0x10;
memset(q, 0x0FF, TS_PACKET_SIZE - (q - buf));
- mpegts_prefix_m2ts_header(s);
- avio_write(s->pb, buf, TS_PACKET_SIZE);
+ write_packet(s, buf);
}
/* Write a single transport stream packet with a PCR and no payload */
}
/* PCR coded into 6 bytes */
- q += write_pcr_bits(q, get_pcr(ts, s->pb));
+ q += write_pcr_bits(q, get_pcr(ts));
/* stuffing bytes */
memset(q, 0xFF, TS_PACKET_SIZE - (q - buf));
- mpegts_prefix_m2ts_header(s);
- avio_write(s->pb, buf, TS_PACKET_SIZE);
+ write_packet(s, buf);
}
static void write_pts(uint8_t *q, int fourbits, int64_t pts)
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 pcr = -1; /* avoid warning */
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) {
- retransmit_si_info(s, force_pat, dts);
+ int64_t pcr = AV_NOPTS_VALUE;
+ if (ts->mux_rate > 1)
+ pcr = get_pcr(ts);
+ else if (dts != AV_NOPTS_VALUE)
+ pcr = (dts - delay) * 300;
+
+ retransmit_si_info(s, force_pat, force_sdt, pcr);
force_pat = 0;
+ force_sdt = 0;
write_pcr = 0;
- if (ts_st->pid == ts_st->service->pcr_pid) {
- if (ts->mux_rate > 1 || is_start) // VBR pcr period is based on frames
- ts_st->service->pcr_packet_count++;
- if (ts_st->service->pcr_packet_count >=
- ts_st->service->pcr_packet_period) {
- ts_st->service->pcr_packet_count = 0;
+ if (ts->mux_rate > 1) {
+ /* Send PCR packets for all PCR streams if needed */
+ pcr = get_pcr(ts);
+ 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);
+ } 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);
write_pcr = 1;
}
}
- if (ts->mux_rate > 1 && dts != AV_NOPTS_VALUE &&
- (dts - get_pcr(ts, s->pb) / 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;
- }
-
/* 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;
*q++ = val;
}
if (key && is_start && pts != AV_NOPTS_VALUE) {
// set Random Access for key frames
- if (ts_st->pid == ts_st->service->pcr_pid)
+ if (ts_st->pcr_period)
write_pcr = 1;
set_af_flag(buf, 0x40);
q = get_ts_payload_start(buf);
set_af_flag(buf, 0x10);
q = get_ts_payload_start(buf);
// add 11, pcr references the last byte of program clock reference base
- if (ts->mux_rate > 1)
- pcr = get_pcr(ts, s->pb);
- else
- pcr = (dts - delay) * 300;
if (dts != AV_NOPTS_VALUE && dts < pcr / 300)
av_log(s, AV_LOG_WARNING, "dts < pcr, TS is invalid\n");
extend_af(buf, write_pcr_bits(q, pcr));
is_dvb_teletext = 0;
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
if (st->codecpar->codec_id == AV_CODEC_ID_DIRAC)
- *q++ = 0xfd;
+ *q++ = STREAM_ID_EXTENDED_STREAM_ID;
else
- *q++ = 0xe0;
+ *q++ = STREAM_ID_VIDEO_STREAM_0;
} else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
(st->codecpar->codec_id == AV_CODEC_ID_MP2 ||
st->codecpar->codec_id == AV_CODEC_ID_MP3 ||
st->codecpar->codec_id == AV_CODEC_ID_AAC)) {
- *q++ = 0xc0;
+ *q++ = STREAM_ID_AUDIO_STREAM_0;
} else if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
st->codecpar->codec_id == AV_CODEC_ID_AC3 &&
ts->m2ts_mode) {
- *q++ = 0xfd;
+ *q++ = STREAM_ID_EXTENDED_STREAM_ID;
} else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA &&
st->codecpar->codec_id == AV_CODEC_ID_TIMED_ID3) {
- *q++ = 0xbd;
+ *q++ = STREAM_ID_PRIVATE_STREAM_1;
} else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) {
- *q++ = stream_id != -1 ? stream_id : 0xfc;
+ *q++ = stream_id != -1 ? stream_id : STREAM_ID_METADATA_STREAM;
- if (stream_id == 0xbd) /* asynchronous KLV */
+ if (stream_id == STREAM_ID_PRIVATE_STREAM_1) /* asynchronous KLV */
pts = dts = AV_NOPTS_VALUE;
} else {
- *q++ = 0xbd;
+ *q++ = STREAM_ID_PRIVATE_STREAM_1;
if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
if (st->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE) {
is_dvb_subtitle = 1;
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;
*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) {
payload += len;
payload_size -= len;
- mpegts_prefix_m2ts_header(s);
- avio_write(s->pb, buf, TS_PACKET_SIZE);
+ write_packet(s, buf);
}
ts_st->prev_payload_key = key;
}
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;
+ size_t side_data_size;
uint8_t *side_data = NULL;
int stream_id = -1;
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_count = ts->pat_packet_period - 1;
- ts->sdt_packet_count = ts->sdt_packet_period - 1;
- ts->flags &= ~MPEGTS_FLAG_REEMIT_PAT_PMT;
- }
-
if (ts->copyts < 1) {
+ if (!ts->first_dts_checked && dts != AV_NOPTS_VALUE) {
+ ts->first_pcr += dts * 300;
+ ts->first_dts_checked = 1;
+ }
+
if (pts != AV_NOPTS_VALUE)
pts += delay;
if (dts != AV_NOPTS_VALUE)
dts += delay;
}
- if (ts_st->first_pts_check && pts == AV_NOPTS_VALUE) {
- av_log(s, AV_LOG_ERROR, "first pts value must be set\n");
+ if (!ts_st->first_timestamp_checked && (pts == AV_NOPTS_VALUE || dts == AV_NOPTS_VALUE)) {
+ av_log(s, AV_LOG_ERROR, "first pts and dts value must be set\n");
return AVERROR_INVALIDDATA;
}
- ts_st->first_pts_check = 0;
+ ts_st->first_timestamp_checked = 1;
if (st->codecpar->codec_id == AV_CODEC_ID_H264) {
const uint8_t *p = buf, *buf_end = p + size;
}
if ((AV_RB16(pkt->data) & 0xfff0) != 0xfff0) {
int ret;
- AVPacket pkt2;
+ AVPacket *pkt2 = ts->pkt;
if (!ts_st->amux) {
av_log(s, AV_LOG_ERROR, "AAC bitstream not in ADTS format "
"and extradata missing\n");
} else {
- av_init_packet(&pkt2);
- pkt2.data = pkt->data;
- pkt2.size = pkt->size;
- av_assert0(pkt->dts != AV_NOPTS_VALUE);
- pkt2.dts = av_rescale_q(pkt->dts, st->time_base, ts_st->amux->streams[0]->time_base);
-
- ret = avio_open_dyn_buf(&ts_st->amux->pb);
- if (ret < 0)
- return AVERROR(ENOMEM);
-
- ret = av_write_frame(ts_st->amux, &pkt2);
- if (ret < 0) {
- ffio_free_dyn_buf(&ts_st->amux->pb);
- return ret;
- }
- size = avio_close_dyn_buf(ts_st->amux->pb, &data);
- ts_st->amux->pb = NULL;
- buf = data;
+ av_packet_unref(pkt2);
+ pkt2->data = pkt->data;
+ pkt2->size = pkt->size;
+ av_assert0(pkt->dts != AV_NOPTS_VALUE);
+ pkt2->dts = av_rescale_q(pkt->dts, st->time_base, ts_st->amux->streams[0]->time_base);
+
+ ret = avio_open_dyn_buf(&ts_st->amux->pb);
+ if (ret < 0)
+ return ret;
+
+ ret = av_write_frame(ts_st->amux, pkt2);
+ if (ret < 0) {
+ ffio_free_dyn_buf(&ts_st->amux->pb);
+ return ret;
+ }
+ size = avio_close_dyn_buf(ts_st->amux->pb, &data);
+ ts_st->amux->pb = NULL;
+ buf = data;
}
}
} else if (st->codecpar->codec_id == AV_CODEC_ID_HEVC) {
} 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);
/* Add Opus control header */
if ((AV_RB16(pkt->data) >> 5) != 0x3ff) {
uint8_t *side_data;
- int side_data_size;
+ size_t side_data_size;
int i, n;
int ctrl_header_size;
int trim_start = 0, trim_end = 0;
* need to count the samples of that too! */
av_log(s, AV_LOG_WARNING, "Got MPEG-TS formatted Opus data, unhandled");
}
- }
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_AC3 && !ts_st->dvb_ac3_desc) {
+ AC3HeaderInfo *hdr = NULL;
+
+ if (avpriv_ac3_parse_header(&hdr, pkt->data, pkt->size) >= 0) {
+ uint8_t number_of_channels_flag;
+ uint8_t service_type_flag;
+ uint8_t full_service_flag = 1;
+ DVBAC3Descriptor *dvb_ac3_desc;
+
+ dvb_ac3_desc = av_mallocz(sizeof(*dvb_ac3_desc));
+ if (!dvb_ac3_desc) {
+ av_free(hdr);
+ return AVERROR(ENOMEM);
+ }
- if (pkt->dts != AV_NOPTS_VALUE) {
- int i;
- for(i=0; i<s->nb_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 > delay/2)) {
- 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;
+ service_type_flag = hdr->bitstream_mode;
+ switch (hdr->channel_mode) {
+ case AC3_CHMODE_DUALMONO:
+ number_of_channels_flag = 1;
+ break;
+ case AC3_CHMODE_MONO:
+ number_of_channels_flag = 0;
+ break;
+ case AC3_CHMODE_STEREO:
+ if (hdr->dolby_surround_mode == AC3_DSURMOD_ON)
+ number_of_channels_flag = 3;
+ else
+ number_of_channels_flag = 2;
+ break;
+ case AC3_CHMODE_3F:
+ case AC3_CHMODE_2F1R:
+ case AC3_CHMODE_3F1R:
+ case AC3_CHMODE_2F2R:
+ case AC3_CHMODE_3F2R:
+ number_of_channels_flag = 4;
+ break;
+ default: /* reserved */
+ number_of_channels_flag = 7;
+ break;
}
+
+ if (service_type_flag == 1 || service_type_flag == 4 ||
+ (service_type_flag == 7 && !number_of_channels_flag))
+ full_service_flag = 0;
+
+ dvb_ac3_desc->component_type_flag = 1;
+ dvb_ac3_desc->component_type = (full_service_flag << 6) |
+ ((service_type_flag & 0x7) << 3) |
+ (number_of_channels_flag & 0x7);
+ dvb_ac3_desc->bsid_flag = 1;
+ dvb_ac3_desc->bsid = hdr->bitstream_id;
+ dvb_ac3_desc->mainid_flag = 0;
+ dvb_ac3_desc->asvc_flag = 0;
+
+ ts_st->dvb_ac3_desc = dvb_ac3_desc;
}
+ av_free(hdr);
}
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 &&
- av_compare_ts(dts - ts_st->payload_dts, st->time_base,
- s->max_delay, AV_TIME_BASE_Q) >= 0) ||
+ dts - ts_st->payload_dts >= max_audio_delay) ||
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,
static void mpegts_write_flush(AVFormatContext *s)
{
+ MpegTSWrite *ts = s->priv_data;
int i;
/* flush current packets */
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)
MpegTSService *service;
int i;
+ av_packet_free(&ts->pkt);
+
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MpegTSWriteStream *ts_st = st->priv_data;
if (ts_st) {
+ av_freep(&ts_st->dvb_ac3_desc);
av_freep(&ts_st->payload);
if (ts_st->amux) {
avformat_free_context(ts_st->amux);
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.",
- offsetof(MpegTSWrite, transport_stream_id), AV_OPT_TYPE_INT,
- { .i64 = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM },
+ OFFSET(transport_stream_id), AV_OPT_TYPE_INT, { .i64 = 0x0001 }, 0x0001, 0xffff, ENC },
{ "mpegts_original_network_id", "Set original_network_id field.",
- offsetof(MpegTSWrite, original_network_id), AV_OPT_TYPE_INT,
- { .i64 = DVB_PRIVATE_NETWORK_START }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM },
+ OFFSET(original_network_id), AV_OPT_TYPE_INT, { .i64 = DVB_PRIVATE_NETWORK_START }, 0x0001, 0xffff, ENC },
{ "mpegts_service_id", "Set service_id field.",
- offsetof(MpegTSWrite, service_id), AV_OPT_TYPE_INT,
- { .i64 = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM },
+ OFFSET(service_id), AV_OPT_TYPE_INT, { .i64 = 0x0001 }, 0x0001, 0xffff, ENC },
{ "mpegts_service_type", "Set service_type field.",
- offsetof(MpegTSWrite, service_type), AV_OPT_TYPE_INT,
- { .i64 = 0x01 }, 0x01, 0xff, AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ OFFSET(service_type), AV_OPT_TYPE_INT, { .i64 = 0x01 }, 0x01, 0xff, ENC, "mpegts_service_type" },
{ "digital_tv", "Digital Television.",
- 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_TV }, 0x01, 0xff,
- AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_TV }, 0x01, 0xff, ENC, "mpegts_service_type" },
{ "digital_radio", "Digital Radio.",
- 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_RADIO }, 0x01, 0xff,
- AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_DIGITAL_RADIO }, 0x01, 0xff, ENC, "mpegts_service_type" },
{ "teletext", "Teletext.",
- 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_TELETEXT }, 0x01, 0xff,
- AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_TELETEXT }, 0x01, 0xff, ENC, "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,
- AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_RADIO }, 0x01, 0xff, ENC, "mpegts_service_type" },
{ "mpeg2_digital_hdtv", "MPEG2 Digital HDTV.",
- 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_MPEG2_DIGITAL_HDTV }, 0x01, 0xff,
- AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_MPEG2_DIGITAL_HDTV }, 0x01, 0xff, ENC, "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,
- AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_SDTV }, 0x01, 0xff, ENC, "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,
- AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_ADVANCED_CODEC_DIGITAL_HDTV }, 0x01, 0xff, ENC, "mpegts_service_type" },
{ "hevc_digital_hdtv", "HEVC Digital Television Service.",
- 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_HEVC_DIGITAL_HDTV }, 0x01, 0xff,
- AV_OPT_FLAG_ENCODING_PARAM, "mpegts_service_type" },
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_HEVC_DIGITAL_HDTV }, 0x01, 0xff, ENC, "mpegts_service_type" },
{ "mpegts_pmt_start_pid", "Set the first pid of the PMT.",
- offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT,
- { .i64 = 0x1000 }, 0x0010, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM },
+ OFFSET(pmt_start_pid), AV_OPT_TYPE_INT, { .i64 = 0x1000 }, FIRST_OTHER_PID, LAST_OTHER_PID, ENC },
{ "mpegts_start_pid", "Set the first pid.",
- 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, 1, 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 },
+ 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 },
{ "pes_payload_size", "Minimum PES packet payload in bytes",
- 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" },
+ 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" },
{ "resend_headers", "Reemit PAT/PMT before writing the next packet",
- 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_REEMIT_PAT_PMT }, 0, INT_MAX,
- AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" },
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_REEMIT_PAT_PMT }, 0, INT_MAX, ENC, "mpegts_flags" },
{ "latm", "Use LATM packetization for AAC",
- 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_AAC_LATM }, 0, INT_MAX,
- AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" },
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_AAC_LATM }, 0, INT_MAX, ENC, "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,
- AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" },
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_PAT_PMT_AT_FRAMES}, 0, INT_MAX, ENC, "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,
- AV_OPT_FLAG_ENCODING_PARAM, "mpegts_flags" },
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_SYSTEM_B }, 0, INT_MAX, ENC, "mpegts_flags" },
{ "initial_discontinuity", "Mark initial packets as discontinuous",
- 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 },
+ 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 },
{ "omit_video_pes_length", "Omit the PES packet length for video packets",
- offsetof(MpegTSWrite, omit_video_pes_length), AV_OPT_TYPE_BOOL,
- { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
+ OFFSET(omit_video_pes_length), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, ENC },
{ "pcr_period", "PCR retransmission time in milliseconds",
- offsetof(MpegTSWrite, pcr_period), AV_OPT_TYPE_INT,
- { .i64 = PCR_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ OFFSET(pcr_period_ms), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, ENC },
{ "pat_period", "PAT/PMT retransmission time limit in seconds",
- offsetof(MpegTSWrite, pat_period), AV_OPT_TYPE_DOUBLE,
- { .dbl = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ 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",
- offsetof(MpegTSWrite, sdt_period), AV_OPT_TYPE_DOUBLE,
- { .dbl = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ OFFSET(sdt_period_us), AV_OPT_TYPE_DURATION, { .i64 = SDT_RETRANS_TIME * 1000LL }, 0, INT64_MAX, ENC },
{ NULL },
};
.version = LIBAVUTIL_VERSION_INT,
};
-AVOutputFormat ff_mpegts_muxer = {
+const AVOutputFormat ff_mpegts_muxer = {
.name = "mpegts",
.long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"),
.mime_type = "video/MP2T",