#include "libavutil/bswap.h"
#include "libavutil/crc.h"
#include "libavutil/dict.h"
+#include "libavutil/intreadwrite.h"
#include "libavutil/mathematics.h"
#include "libavutil/opt.h"
-#include "libavcodec/mpegvideo.h"
+#include "libavcodec/internal.h"
#include "avformat.h"
#include "internal.h"
#include "mpegts.h"
-#include "adts.h"
#define PCR_TIME_BASE 27000000
int pmt_start_pid;
int start_pid;
- int reemit_pat_pmt;
+ int reemit_pat_pmt; // backward compatibility
+
+ int pcr_period;
+#define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01
+#define MPEGTS_FLAG_AAC_LATM 0x02
+ int flags;
} MpegTSWrite;
/* a PES packet header is generated every DEFAULT_PES_HEADER_FREQ packets */
#define DEFAULT_PES_HEADER_FREQ 16
#define DEFAULT_PES_PAYLOAD_SIZE ((DEFAULT_PES_HEADER_FREQ - 1) * 184 + 170)
-static const AVOption options[] = {
- { "mpegts_transport_stream_id", "Set transport_stream_id field.",
- offsetof(MpegTSWrite, transport_stream_id), AV_OPT_TYPE_INT, {.dbl = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM},
- { "mpegts_original_network_id", "Set original_network_id field.",
- offsetof(MpegTSWrite, original_network_id), AV_OPT_TYPE_INT, {.dbl = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM},
- { "mpegts_service_id", "Set service_id field.",
- offsetof(MpegTSWrite, service_id), AV_OPT_TYPE_INT, {.dbl = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM},
- { "mpegts_pmt_start_pid", "Set the first pid of the PMT.",
- offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT, {.dbl = 0x1000 }, 0x1000, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM},
- { "mpegts_start_pid", "Set the first pid.",
- offsetof(MpegTSWrite, start_pid), AV_OPT_TYPE_INT, {.dbl = 0x0100 }, 0x0100, 0x0f00, AV_OPT_FLAG_ENCODING_PARAM},
- { "muxrate", NULL, offsetof(MpegTSWrite, mux_rate), AV_OPT_TYPE_INT, {1}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
- { "pes_payload_size", "Minimum PES packet payload in bytes",
- offsetof(MpegTSWrite, pes_payload_size), AV_OPT_TYPE_INT, {DEFAULT_PES_PAYLOAD_SIZE}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
- { "resend_headers", "Reemit PAT/PMT before writing the next packet",
- offsetof(MpegTSWrite, reemit_pat_pmt), AV_OPT_TYPE_INT, {0}, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM},
- { NULL },
-};
-
-static const AVClass mpegts_muxer_class = {
- .class_name = "MPEGTS muxer",
- .item_name = av_default_item_name,
- .option = options,
- .version = LIBAVUTIL_VERSION_INT,
-};
-
/* NOTE: 4 bytes must be left at the end for the crc32 */
static void mpegts_write_section(MpegTSSection *s, uint8_t *buf, int len)
{
int64_t payload_dts;
int payload_flags;
uint8_t *payload;
- ADTSContext *adts;
+ AVFormatContext *amux;
+ AVRational user_tb;
} MpegTSWriteStream;
static void mpegts_write_pat(AVFormatContext *s)
static void mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
{
- // MpegTSWrite *ts = s->priv_data;
+ MpegTSWrite *ts = s->priv_data;
uint8_t data[1012], *q, *desc_length_ptr, *program_info_length_ptr;
int val, stream_type, i;
MpegTSWriteStream *ts_st = st->priv_data;
AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL,0);
switch(st->codec->codec_id) {
- case CODEC_ID_MPEG1VIDEO:
- case CODEC_ID_MPEG2VIDEO:
+ case AV_CODEC_ID_MPEG1VIDEO:
+ case AV_CODEC_ID_MPEG2VIDEO:
stream_type = STREAM_TYPE_VIDEO_MPEG2;
break;
- case CODEC_ID_MPEG4:
+ case AV_CODEC_ID_MPEG4:
stream_type = STREAM_TYPE_VIDEO_MPEG4;
break;
- case CODEC_ID_H264:
+ case AV_CODEC_ID_H264:
stream_type = STREAM_TYPE_VIDEO_H264;
break;
- case CODEC_ID_DIRAC:
+ 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 CODEC_ID_MP2:
- case CODEC_ID_MP3:
+ case AV_CODEC_ID_MP2:
+ case AV_CODEC_ID_MP3:
stream_type = STREAM_TYPE_AUDIO_MPEG1;
break;
- case CODEC_ID_AAC:
- stream_type = STREAM_TYPE_AUDIO_AAC;
+ case AV_CODEC_ID_AAC:
+ stream_type = (ts->flags & MPEGTS_FLAG_AAC_LATM) ? STREAM_TYPE_AUDIO_AAC_LATM : STREAM_TYPE_AUDIO_AAC;
break;
- case CODEC_ID_AAC_LATM:
+ case AV_CODEC_ID_AAC_LATM:
stream_type = STREAM_TYPE_AUDIO_AAC_LATM;
break;
- case CODEC_ID_AC3:
+ case AV_CODEC_ID_AC3:
stream_type = STREAM_TYPE_AUDIO_AC3;
break;
default:
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;
/* assign pids to each stream */
for(i = 0;i < s->nb_streams; i++) {
st = s->streams[i];
- avpriv_set_pts_info(st, 33, 1, 90000);
+
ts_st = av_mallocz(sizeof(MpegTSWriteStream));
- if (!ts_st)
+ if (!ts_st) {
+ 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)
+ if (!ts_st->payload) {
+ ret = AVERROR(ENOMEM);
goto fail;
+ }
ts_st->service = service;
/* MPEG pid values < 16 are reserved. Applications which set st->id in
* this range are assigned a calculated pid. */
ts_st->pid = st->id;
} else {
av_log(s, AV_LOG_ERROR, "Invalid stream id %d, must be less than 8191\n", st->id);
+ ret = AVERROR(EINVAL);
goto fail;
}
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++)
if (pids[j] == ts_st->pid) {
av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", ts_st->pid);
+ ret = AVERROR(EINVAL);
goto fail;
}
pids[i] = ts_st->pid;
service->pcr_pid = ts_st->pid;
pcr_st = st;
}
- if (st->codec->codec_id == CODEC_ID_AAC &&
- st->codec->extradata_size > 0) {
- ts_st->adts = av_mallocz(sizeof(*ts_st->adts));
- if (!ts_st->adts)
+ if (st->codec->codec_id == AV_CODEC_ID_AAC &&
+ st->codec->extradata_size > 0)
+ {
+ AVStream *ast;
+ ts_st->amux = avformat_alloc_context();
+ if (!ts_st->amux) {
+ 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) {
+ ret = AVERROR(EINVAL);
goto fail;
- if (ff_adts_decode_extradata(s, ts_st->adts, st->codec->extradata,
- st->codec->extradata_size) < 0)
+ }
+ ast = avformat_new_stream(ts_st->amux, NULL);
+ ret = avcodec_copy_context(ast->codec, st->codec);
+ if (ret != 0)
+ goto fail;
+ ret = avformat_write_header(ts_st->amux, NULL);
+ if (ret < 0)
goto fail;
}
}
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 = (ts->mux_rate * PCR_RETRANS_TIME) /
+ service->pcr_packet_period = (ts->mux_rate * ts->pcr_period) /
(TS_PACKET_SIZE * 8 * 1000);
ts->sdt_packet_period = (ts->mux_rate * SDT_RETRANS_TIME) /
(TS_PACKET_SIZE * 8 * 1000);
}
} else {
// max delta PCR 0.1s
+ // TODO: should be avg_frame_rate
service->pcr_packet_period =
- pcr_st->codec->time_base.den/(10*pcr_st->codec->time_base.num);
+ ts_st->user_tb.den / (10 * ts_st->user_tb.num);
}
}
ts->sdt_packet_count = ts->sdt_packet_period-1;
if (ts->mux_rate == 1)
- av_log(s, AV_LOG_INFO, "muxrate VBR, ");
+ av_log(s, AV_LOG_VERBOSE, "muxrate VBR, ");
else
- av_log(s, AV_LOG_INFO, "muxrate %d, ", ts->mux_rate);
- av_log(s, AV_LOG_INFO, "pcr every %d pkts, "
+ 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);
ts_st = st->priv_data;
if (ts_st) {
av_freep(&ts_st->payload);
- av_freep(&ts_st->adts);
+ if (ts_st->amux) {
+ avformat_free_context(ts_st->amux);
+ ts_st->amux = NULL;
+ }
}
av_freep(&st->priv_data);
}
- return -1;
+ return ret;
}
/* send SDT, PAT and PMT tables regulary */
*q++ = 0x01;
private_code = 0;
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
- if (st->codec->codec_id == CODEC_ID_DIRAC) {
+ if (st->codec->codec_id == AV_CODEC_ID_DIRAC) {
*q++ = 0xfd;
} else
*q++ = 0xe0;
} else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO &&
- (st->codec->codec_id == CODEC_ID_MP2 ||
- st->codec->codec_id == CODEC_ID_MP3 ||
- st->codec->codec_id == CODEC_ID_AAC)) {
+ (st->codec->codec_id == AV_CODEC_ID_MP2 ||
+ st->codec->codec_id == AV_CODEC_ID_MP3 ||
+ st->codec->codec_id == AV_CODEC_ID_AAC)) {
*q++ = 0xc0;
} else {
*q++ = 0xbd;
flags |= 0x40;
}
if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO &&
- st->codec->codec_id == CODEC_ID_DIRAC) {
+ st->codec->codec_id == AV_CODEC_ID_DIRAC) {
/* set PES_extension_flag */
pes_extension = 1;
flags |= 0x01;
write_pts(q, 1, dts);
q += 5;
}
- if (pes_extension && st->codec->codec_id == CODEC_ID_DIRAC) {
+ if (pes_extension && st->codec->codec_id == AV_CODEC_ID_DIRAC) {
flags = 0x01; /* set PES_extension_flag_2 */
*q++ = flags;
*q++ = 0x80 | 0x01; /* marker bit + extension length */
int64_t dts = AV_NOPTS_VALUE, pts = AV_NOPTS_VALUE;
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->reemit_pat_pmt = 0;
+ ts->flags &= ~MPEGTS_FLAG_REEMIT_PAT_PMT;
}
if (pkt->pts != AV_NOPTS_VALUE)
if (ts_st->first_pts_check && pts == AV_NOPTS_VALUE) {
av_log(s, AV_LOG_ERROR, "first pts value must set\n");
- return -1;
+ return AVERROR(EINVAL);
}
ts_st->first_pts_check = 0;
- if (st->codec->codec_id == CODEC_ID_H264) {
+ if (st->codec->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 -1;
+ return AVERROR(EINVAL);
}
do {
- p = avpriv_mpv_find_start_code(p, buf_end, &state);
- //av_log(s, AV_LOG_INFO, "nal %d\n", state & 0x1f);
+ p = avpriv_find_start_code(p, buf_end, &state);
+ av_dlog(s, "nal %d\n", state & 0x1f);
} while (p < buf_end && (state & 0x1f) != 9 &&
(state & 0x1f) != 5 && (state & 0x1f) != 1);
if ((state & 0x1f) != 9) { // AUD NAL
data = av_malloc(pkt->size+6);
if (!data)
- return -1;
+ return AVERROR(ENOMEM);
memcpy(data+6, pkt->data, pkt->size);
AV_WB32(data, 0x00000001);
data[4] = 0x09;
buf = data;
size = pkt->size+6;
}
- } else if (st->codec->codec_id == CODEC_ID_AAC) {
- if (pkt->size < 2)
- return -1;
+ } else if (st->codec->codec_id == AV_CODEC_ID_AAC) {
+ if (pkt->size < 2) {
+ av_log(s, AV_LOG_ERROR, "AAC packet too short\n");
+ return AVERROR(EINVAL);
+ }
if ((AV_RB16(pkt->data) & 0xfff0) != 0xfff0) {
- ADTSContext *adts = ts_st->adts;
- int new_size, err;
- if (!adts) {
- av_log(s, AV_LOG_ERROR, "aac bitstream not in adts format "
+ int ret;
+ AVPacket pkt2;
+
+ if (!ts_st->amux) {
+ av_log(s, AV_LOG_ERROR, "AAC bitstream not in ADTS format "
"and extradata missing\n");
- return -1;
+ return AVERROR(EINVAL);
}
- new_size = ADTS_HEADER_SIZE+adts->pce_size+pkt->size;
- if ((unsigned)new_size >= INT_MAX)
- return -1;
- data = av_malloc(new_size);
- if (!data)
+
+ av_init_packet(&pkt2);
+ pkt2.data = pkt->data;
+ pkt2.size = pkt->size;
+ ret = avio_open_dyn_buf(&ts_st->amux->pb);
+ if (ret < 0)
return AVERROR(ENOMEM);
- err = ff_adts_write_frame_header(adts, data, pkt->size,
- adts->pce_size);
- if (err < 0) {
+
+ 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);
- return err;
- }
- if (adts->pce_size) {
- memcpy(data+ADTS_HEADER_SIZE, adts->pce_data, adts->pce_size);
- adts->pce_size = 0;
+ return ret;
}
- memcpy(data+ADTS_HEADER_SIZE+adts->pce_size, pkt->data, pkt->size);
+ size = avio_close_dyn_buf(ts_st->amux->pb, &data);
+ ts_st->amux->pb = NULL;
buf = data;
- size = new_size;
}
}
AVStream *st = s->streams[i];
MpegTSWriteStream *ts_st = st->priv_data;
av_freep(&ts_st->payload);
- av_freep(&ts_st->adts);
+ if (ts_st->amux) {
+ avformat_free_context(ts_st->amux);
+ ts_st->amux = NULL;
+ }
}
for(i = 0; i < ts->nb_services; i++) {
return 0;
}
+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},
+ { "mpegts_original_network_id", "Set original_network_id field.",
+ offsetof(MpegTSWrite, original_network_id), AV_OPT_TYPE_INT, {.i64 = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM},
+ { "mpegts_service_id", "Set service_id field.",
+ offsetof(MpegTSWrite, service_id), AV_OPT_TYPE_INT, {.i64 = 0x0001 }, 0x0001, 0xffff, AV_OPT_FLAG_ENCODING_PARAM},
+ { "mpegts_pmt_start_pid", "Set the first pid of the PMT.",
+ offsetof(MpegTSWrite, pmt_start_pid), AV_OPT_TYPE_INT, {.i64 = 0x1000 }, 0x1000, 0x1f00, AV_OPT_FLAG_ENCODING_PARAM},
+ { "mpegts_start_pid", "Set the first pid.",
+ offsetof(MpegTSWrite, start_pid), AV_OPT_TYPE_INT, {.i64 = 0x0100 }, 0x0100, 0x0f00, 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",
+ 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,
+ 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,
+ 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},
+ { "pcr_period", "PCR retransmission time",
+ offsetof(MpegTSWrite, pcr_period), AV_OPT_TYPE_INT, { .i64 = PCR_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { NULL },
+};
+
+static const AVClass mpegts_muxer_class = {
+ .class_name = "MPEGTS muxer",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVOutputFormat ff_mpegts_muxer = {
.name = "mpegts",
- .long_name = NULL_IF_CONFIG_SMALL("MPEG-2 transport stream format"),
+ .long_name = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"),
.mime_type = "video/x-mpegts",
.extensions = "ts,m2t",
.priv_data_size = sizeof(MpegTSWrite),
- .audio_codec = CODEC_ID_MP2,
- .video_codec = CODEC_ID_MPEG2VIDEO,
+ .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,
- .priv_class = &mpegts_muxer_class,
+ .priv_class = &mpegts_muxer_class,
};