#include "libavcodec/internal.h"
#include "avformat.h"
+#include "avio_internal.h"
#include "internal.h"
#include "mpegts.h"
int pmt_start_pid;
int start_pid;
+ int m2ts_mode;
int reemit_pat_pmt; // backward compatibility
int pcr_period;
#define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01
#define MPEGTS_FLAG_AAC_LATM 0x02
+#define MPEGTS_FLAG_SYSTEM_B 0x04
int flags;
} MpegTSWrite;
tot_len = 3 + 5 + len + 4;
/* check if not too big */
if (tot_len > 1024)
- return -1;
+ return AVERROR_INVALIDDATA;
q = section;
*q++ = tid;
{
MpegTSWrite *ts = s->priv_data;
uint8_t data[SECTION_LENGTH], *q, *desc_length_ptr, *program_info_length_ptr;
- int val, stream_type, i;
+ int val, stream_type, i, err = 0;
q = data;
put16(&q, 0xe000 | service->pcr_pid);
AVStream *st = s->streams[i];
MpegTSWriteStream *ts_st = st->priv_data;
AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0);
- switch (st->codec->codec_id) {
+
+ if (q - data > SECTION_LENGTH - 3 - 2 - 6) {
+ err = 1;
+ break;
+ }
+ switch (st->codecpar->codec_id) {
case AV_CODEC_ID_MPEG1VIDEO:
case AV_CODEC_ID_MPEG2VIDEO:
stream_type = STREAM_TYPE_VIDEO_MPEG2;
stream_type = STREAM_TYPE_AUDIO_AAC_LATM;
break;
case AV_CODEC_ID_AC3:
- stream_type = STREAM_TYPE_AUDIO_AC3;
+ stream_type = (ts->flags & MPEGTS_FLAG_SYSTEM_B)
+ ? STREAM_TYPE_PRIVATE_DATA
+ : STREAM_TYPE_AUDIO_AC3;
break;
default:
stream_type = STREAM_TYPE_PRIVATE_DATA;
q += 2; /* patched after */
/* write optional descriptors here */
- switch (st->codec->codec_type) {
+ 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; /* ETSI EN 300 468 AC-3 descriptor */
+ *q++ = 1;
+ *q++ = 0x00;
+ }
+
if (lang) {
char *p;
char *next = lang->value;
*len_ptr = 0;
for (p = lang->value; next && *len_ptr < 255 / 4 * 4; p = next + 1) {
+ if (q - data > SECTION_LENGTH - 4) {
+ err = 1;
+ break;
+ }
next = strchr(p, ',');
if (strlen(p) != 3 && (!next || next != p + 3))
continue; /* not a 3-letter code */
*q++ = language[1];
*q++ = language[2];
*q++ = 0x10; /* normal subtitles (0x20 = if hearing pb) */
- if (st->codec->extradata_size == 4) {
- memcpy(q, st->codec->extradata, 4);
+
+ if (q - data > SECTION_LENGTH - 4) {
+ err = 1;
+ break;
+ }
+
+ if (st->codecpar->extradata_size == 4) {
+ memcpy(q, st->codecpar->extradata, 4);
q += 4;
} else {
put16(&q, 1); /* page id */
desc_length_ptr[0] = val >> 8;
desc_length_ptr[1] = val;
}
+
+ if (err)
+ av_log(s, AV_LOG_ERROR,
+ "The PMT section cannot fit stream %d and all following streams.\n"
+ "Try reducing the number of languages in the audio streams "
+ "or the total number of streams.\n", i);
+
mpegts_write_section1(&service->pmt, PMT_TID, service->sid, 0, 0, 0,
data, q - data);
}
return NULL;
service->pmt.pid = ts->pmt_start_pid + ts->nb_services;
service->sid = sid;
+ service->pcr_pid = 0x1fff;
service->provider_name = av_strdup(provider_name);
service->name = av_strdup(name);
- service->pcr_pid = 0x1fff;
+ if (!service->provider_name || !service->name) {
+ av_free(service->provider_name);
+ av_free(service->name);
+ av_free(service);
+ return NULL;
+ }
dynarray_add(&ts->services, &ts->nb_services, service);
return service;
}
+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)
+{
+ 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));
+ }
+}
+
static void section_write_packet(MpegTSSection *s, const uint8_t *packet)
{
AVFormatContext *ctx = s->opaque;
+ mpegts_prefix_m2ts_header(ctx);
avio_write(ctx->pb, packet, TS_PACKET_SIZE);
}
service = mpegts_add_service(ts, 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;
ts->sdt.opaque = s;
pids = av_malloc(s->nb_streams * sizeof(*pids));
- if (!pids)
+ if (!pids) {
+ av_free(service);
return AVERROR(ENOMEM);
+ }
/* assign pids to each stream */
for (i = 0; i < s->nb_streams; i++) {
ts_st->first_pts_check = 1;
ts_st->cc = 15;
/* update PCR pid by using the first video stream */
- if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
service->pcr_pid == 0x1fff) {
service->pcr_pid = ts_st->pid;
pcr_st = st;
}
- if (st->codec->codec_id == AV_CODEC_ID_AAC &&
- st->codec->extradata_size > 0) {
+ 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(EINVAL);
goto fail;
}
- ast = avformat_new_stream(ts_st->amux, NULL);
- ret = avcodec_copy_context(ast->codec, st->codec);
+ if (!(ast = avformat_new_stream(ts_st->amux, NULL))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+ ret = avcodec_parameters_copy(ast->codecpar, st->codecpar);
if (ret != 0)
goto fail;
+ ast->time_base = st->time_base;
ret = avformat_write_header(ts_st->amux, NULL);
if (ret < 0)
goto fail;
/* Arbitrary values, PAT/PMT could be written on key frames */
ts->sdt_packet_period = 200;
ts->pat_packet_period = 40;
- if (pcr_st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
- if (!pcr_st->codec->frame_size) {
+ 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->codec->sample_rate / (10 * 512);
+ pcr_st->codecpar->sample_rate / (10 * 512);
} else {
service->pcr_packet_period =
- pcr_st->codec->sample_rate / (10 * pcr_st->codec->frame_size);
+ pcr_st->codecpar->sample_rate / (10 * frame_size);
}
} else {
// max delta PCR 0.1s
service->pcr_packet_period,
ts->sdt_packet_period, ts->pat_packet_period);
+ if (ts->m2ts_mode == -1) {
+ if (av_match_ext(s->filename, "m2ts")) {
+ ts->m2ts_mode = 1;
+ } else {
+ ts->m2ts_mode = 0;
+ }
+ }
+
avio_flush(s->pb);
return 0;
fail:
+ av_free(service);
av_free(pids);
for (i = 0; i < s->nb_streams; i++) {
st = s->streams[i];
}
}
-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 int write_pcr_bits(uint8_t *buf, int64_t pcr)
{
int64_t pcr_low = pcr % 300, pcr_high = pcr / 300;
*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);
}
/* stuffing bytes */
memset(q, 0xFF, TS_PACKET_SIZE - (q - buf));
+ mpegts_prefix_m2ts_header(s);
avio_write(s->pb, buf, TS_PACKET_SIZE);
}
*q++ = 0x00;
*q++ = 0x01;
private_code = 0;
- if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
- if (st->codec->codec_id == AV_CODEC_ID_DIRAC)
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+ if (st->codecpar->codec_id == AV_CODEC_ID_DIRAC)
*q++ = 0xfd;
else
*q++ = 0xe0;
- } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
- (st->codec->codec_id == AV_CODEC_ID_MP2 ||
- st->codec->codec_id == AV_CODEC_ID_MP3 ||
- st->codec->codec_id == AV_CODEC_ID_AAC)) {
+ } 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;
} else {
*q++ = 0xbd;
- if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE)
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
private_code = 0x20;
}
header_len = 0;
header_len += 5;
flags |= 0x40;
}
- if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
- st->codec->codec_id == AV_CODEC_ID_DIRAC) {
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
+ st->codecpar->codec_id == AV_CODEC_ID_DIRAC) {
/* set PES_extension_flag */
pes_extension = 1;
flags |= 0x01;
*q++ = len;
val = 0x80;
/* data alignment indicator is required for subtitle data */
- if (st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE)
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
val |= 0x04;
*q++ = val;
*q++ = flags;
write_pts(q, 1, dts);
q += 5;
}
- if (pes_extension && st->codec->codec_id == AV_CODEC_ID_DIRAC) {
+ if (pes_extension && st->codecpar->codec_id == AV_CODEC_ID_DIRAC) {
flags = 0x01; /* set PES_extension_flag_2 */
*q++ = flags;
*q++ = 0x80 | 0x01; /* marker bit + extension length */
memcpy(buf + TS_PACKET_SIZE - len, payload, len);
payload += len;
payload_size -= len;
+ mpegts_prefix_m2ts_header(s);
avio_write(s->pb, buf, TS_PACKET_SIZE);
}
avio_flush(s->pb);
if (ts_st->first_pts_check && pts == AV_NOPTS_VALUE) {
av_log(s, AV_LOG_ERROR, "first pts value must set\n");
- return AVERROR(EINVAL);
+ return AVERROR_INVALIDDATA;
}
ts_st->first_pts_check = 0;
- if (st->codec->codec_id == AV_CODEC_ID_H264) {
+ if (st->codecpar->codec_id == AV_CODEC_ID_H264) {
const uint8_t *p = buf, *buf_end = p + size;
uint32_t state = -1;
if (pkt->size < 5 || AV_RB32(pkt->data) != 0x0000001) {
av_log(s, AV_LOG_ERROR, "H.264 bitstream malformed, "
"no startcode found, use -bsf h264_mp4toannexb\n");
- return AVERROR(EINVAL);
+ return AVERROR_INVALIDDATA;
}
do {
p = avpriv_find_start_code(p, buf_end, &state);
- av_dlog(s, "nal %d\n", state & 0x1f);
+ av_log(s, AV_LOG_TRACE, "nal %d\n", state & 0x1f);
} while (p < buf_end && (state & 0x1f) != 9 &&
(state & 0x1f) != 5 && (state & 0x1f) != 1);
buf = data;
size = pkt->size + 6;
}
- } else if (st->codec->codec_id == AV_CODEC_ID_AAC) {
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_AAC) {
if (pkt->size < 2) {
av_log(s, AV_LOG_ERROR, "AAC packet too short\n");
- return AVERROR(EINVAL);
+ return AVERROR_INVALIDDATA;
}
if ((AV_RB16(pkt->data) & 0xfff0) != 0xfff0) {
int ret;
if (!ts_st->amux) {
av_log(s, AV_LOG_ERROR, "AAC bitstream not in ADTS format "
"and extradata missing\n");
- return AVERROR(EINVAL);
+ return AVERROR_INVALIDDATA;
}
av_init_packet(&pkt2);
ret = av_write_frame(ts_st->amux, &pkt2);
if (ret < 0) {
- avio_close_dyn_buf(ts_st->amux->pb, &data);
- ts_st->amux->pb = NULL;
- av_free(data);
+ ffio_free_dyn_buf(&ts_st->amux->pb);
return ret;
}
size = avio_close_dyn_buf(ts_st->amux->pb, &data);
}
}
- if (st->codec->codec_type != AVMEDIA_TYPE_AUDIO) {
+ if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) {
// for video and subtitle, write a single pes packet
mpegts_write_pes(s, st, buf, size, pts, dts,
pkt->flags & AV_PKT_FLAG_KEY);
return 0;
}
- if (ts_st->payload_size + size > ts->pes_payload_size) {
+ if (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)) {
if (ts_st->payload_size) {
mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_size,
ts_st->payload_pts, ts_st->payload_dts,
MpegTSService *service;
int i;
- mpegts_write_flush(s);
+ if (s->pb)
+ mpegts_write_flush(s);
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
{ "mpegts_start_pid", "Set the first pid.",
offsetof(MpegTSWrite, start_pid), AV_OPT_TYPE_INT,
{ .i64 = 0x0100 }, 0x0100, 0x0f00, AV_OPT_FLAG_ENCODING_PARAM },
+ {"mpegts_m2ts_mode", "Enable m2ts mode.",
+ offsetof(MpegTSWrite, m2ts_mode), AV_OPT_TYPE_INT,
+ { .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 },
{ "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" },
+ { "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" },
// backward compatibility
{ "resend_headers", "Reemit PAT/PMT before writing the next packet",
offsetof(MpegTSWrite, reemit_pat_pmt), AV_OPT_TYPE_INT,
AVOutputFormat ff_mpegts_muxer = {
.name = "mpegts",
.long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"),
- .mime_type = "video/x-mpegts",
- .extensions = "ts,m2t",
+ .mime_type = "video/MP2T",
+ .extensions = "ts,m2t,m2ts,mts",
.priv_data_size = sizeof(MpegTSWrite),
.audio_codec = AV_CODEC_ID_MP2,
.video_codec = AV_CODEC_ID_MPEG2VIDEO,
.write_header = mpegts_write_header,
.write_packet = mpegts_write_packet,
.write_trailer = mpegts_write_end,
- .flags = AVFMT_ALLOW_FLUSH,
+ .flags = AVFMT_ALLOW_FLUSH | AVFMT_VARIABLE_FPS,
.priv_class = &mpegts_muxer_class,
};