[FFmpeg-devel] [PATCH] adding support for writing m2ts bluray container
Herr Sven Alisch BSc.
svenali
Sun Feb 13 21:30:52 CET 2011
Hello,
here my complete working patch for m2ts. I added the line ts->last_arrival_time_stamp = ts->first_pcr. It is better, because the arrival_time_stamp needs to be <= pcr ticks. The streams are playable with the ps3 from sony.
regards,
Sven
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 1d4e75e..f408ad9 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -145,6 +145,7 @@ OBJS-$(CONFIG_MPEG2VIDEO_MUXER) += rawenc.o
OBJS-$(CONFIG_MPEGPS_DEMUXER) += mpeg.o
OBJS-$(CONFIG_MPEGTS_DEMUXER) += mpegts.o isom.o
OBJS-$(CONFIG_MPEGTS_MUXER) += mpegtsenc.o adtsenc.o
+OBJS-$(CONFIG_MPEG2TS_MUXER) += mpegtsenc.o adtsenc.o
OBJS-$(CONFIG_MPEGVIDEO_DEMUXER) += mpegvideodec.o rawdec.o
OBJS-$(CONFIG_MPJPEG_MUXER) += mpjpeg.o
OBJS-$(CONFIG_MSNWC_TCP_DEMUXER) += msnwc_tcp.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index 0ff4b5a..55045f0 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -135,6 +135,7 @@ void av_register_all(void)
REGISTER_MUXER (MPEG2VOB, mpeg2vob);
REGISTER_DEMUXER (MPEGPS, mpegps);
REGISTER_MUXDEMUX (MPEGTS, mpegts);
+ REGISTER_MUXER (MPEG2TS, mpeg2ts);
REGISTER_DEMUXER (MPEGTSRAW, mpegtsraw);
REGISTER_DEMUXER (MPEGVIDEO, mpegvideo);
REGISTER_MUXER (MPJPEG, mpjpeg);
diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
index b1bccd1..25a7f0f 100644
--- a/libavformat/mpegtsenc.c
+++ b/libavformat/mpegtsenc.c
@@ -1,6 +1,7 @@
/*
* MPEG2 transport stream (aka DVB) muxer
* Copyright (c) 2003 Fabrice Bellard
+ * M2TS patches Copyright (c) 2011 Sven Alisch
*
* This file is part of FFmpeg.
*
@@ -66,6 +67,11 @@ typedef struct MpegTSWrite {
int64_t first_pcr;
int mux_rate; ///< set to 1 when VBR
+ int64_t last_arrival_time_stamp; /* contains the last written arrival timestamp */
+ int counted_null_packets; /* Null Packets were not written into m2ts streams. But the pcr has to calculate with the bytes not written. */
+ int last_null_packets; /* To calculate the exact arrival_time_stamp, we have to get the number of last counted null packets. */
+ int m2ts_mode; /* 0, if not, 1 if yes */
+
int transport_stream_id;
int original_network_id;
int service_id;
@@ -95,6 +101,29 @@ static const AVClass mpegts_muxer_class = {
LIBAVUTIL_VERSION_INT,
};
+/* generates a arrival time stamp for the m2ts header */
+static int64_t get_arrival_time_stamp(MpegTSWrite *ts, ByteIOContext *pb)
+{
+ int64_t pcr_diff = (ts->last_null_packets + 1) * av_rescale(188 * 8, PCR_TIME_BASE, ts->mux_rate);
+
+ ts->last_arrival_time_stamp += pcr_diff;
+ return ts->last_arrival_time_stamp;
+}
+
+/* The arrival_time_stamp consists of 4 byte. The first two bits are reserved for a copyright and should set to 11 (binary) */
+static void write_starttime_code(uint8_t* q, int64_t arrival_time_stamp)
+{
+ *q = 3 << 6;
+ int val = (arrival_time_stamp >> 24) & 0xFF;
+ *q++ |= val;
+ val = (arrival_time_stamp >> 16) & 0xFF;
+ *q++ = val;
+ val = (arrival_time_stamp >> 8) & 0xFF;
+ *q++ = val;
+ val = arrival_time_stamp;
+ *q++ = val;
+}
+
/* NOTE: 4 bytes must be left at the end for the crc32 */
static void mpegts_write_section(MpegTSSection *s, uint8_t *buf, int len)
{
@@ -411,6 +440,20 @@ static void section_write_packet(MpegTSSection *s, const uint8_t *packet)
put_buffer(ctx->pb, packet, TS_PACKET_SIZE);
}
+/* a version for m2ts streams */
+static void section_write_packet2(MpegTSSection *s, const uint8_t *packet)
+{
+ AVFormatContext *ctx = s->opaque;
+ MpegTSWrite *ts = ctx->priv_data;
+ uint8_t buf[TS_PACKET_SIZE + 4];
+ int64_t arrival_time_stamp = get_arrival_time_stamp(ctx->priv_data, ctx->pb);
+
+ write_starttime_code(buf, arrival_time_stamp);
+ memcpy(&buf[4], packet, TS_PACKET_SIZE);
+
+ put_buffer(ctx->pb, buf, TS_PACKET_SIZE + 4);
+}
+
static int mpegts_write_header(AVFormatContext *s)
{
MpegTSWrite *ts = s->priv_data;
@@ -423,8 +466,13 @@ static int mpegts_write_header(AVFormatContext *s)
const char *provider_name;
int *pids;
+ if (ts->m2ts_mode != 1) /* if mpeg2ts_write_header is not called first */
+ ts->m2ts_mode = 0;
ts->tsid = ts->transport_stream_id;
ts->onid = ts->original_network_id;
+ /* Initializing the m2ts parameters. Not important for normal ts streams! */
+ ts->counted_null_packets = 0;
+ ts->last_null_packets = 0;
/* allocate a single DVB service */
title = av_metadata_get(s->metadata, "service_name", NULL, 0);
if (!title)
@@ -433,18 +481,27 @@ static int mpegts_write_header(AVFormatContext *s)
provider = av_metadata_get(s->metadata, "service_provider", NULL, 0);
provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME;
service = mpegts_add_service(ts, ts->service_id, provider_name, service_name);
- service->pmt.write_packet = section_write_packet;
+ if (ts->m2ts_mode == 1)
+ service->pmt.write_packet = section_write_packet2;
+ else
+ service->pmt.write_packet = section_write_packet;
service->pmt.opaque = s;
service->pmt.cc = 15;
ts->pat.pid = PAT_PID;
ts->pat.cc = 15; // Initialize at 15 so that it wraps and be equal to 0 for the first packet we write
- ts->pat.write_packet = section_write_packet;
+ if (ts->m2ts_mode == 1)
+ ts->pat.write_packet = section_write_packet2;
+ else
+ ts->pat.write_packet = section_write_packet;
ts->pat.opaque = s;
ts->sdt.pid = SDT_PID;
ts->sdt.cc = 15;
- ts->sdt.write_packet = section_write_packet;
+ if (ts->m2ts_mode == 1)
+ ts->sdt.write_packet = section_write_packet2;
+ else
+ ts->sdt.write_packet = section_write_packet;
ts->sdt.opaque = s;
pids = av_malloc(s->nb_streams * sizeof(*pids));
@@ -520,6 +577,7 @@ static int mpegts_write_header(AVFormatContext *s)
(TS_PACKET_SIZE * 8 * 1000);
ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE);
+ ts->last_arrival_time_stamp = ts->first_pcr;
} else {
/* Arbitrary values, PAT/PMT could be written on key frames */
ts->sdt_packet_period = 200;
@@ -588,7 +646,12 @@ static void retransmit_si_info(AVFormatContext *s)
static int64_t get_pcr(const MpegTSWrite *ts, ByteIOContext *pb)
{
- return av_rescale(url_ftell(pb) + 11, 8 * PCR_TIME_BASE, ts->mux_rate) +
+ /* if a m2ts is build, an offset is possible (depends on the muxrate), because
+ the null packets are only counted and NOT written! So url_ftell gives not the correct
+ written bytes. We have to add an offset of 192 multiplied by counted null packets. */
+ int byteoffset = (TS_PACKET_SIZE + 4) * ts->counted_null_packets;
+
+ return av_rescale(url_ftell(pb) + byteoffset + 11, 8 * PCR_TIME_BASE, ts->mux_rate) +
ts->first_pcr;
}
@@ -628,6 +691,7 @@ static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st)
MpegTSWriteStream *ts_st = st->priv_data;
uint8_t *q;
uint8_t buf[TS_PACKET_SIZE];
+ uint8_t source_packet[TS_PACKET_SIZE + 4];
q = buf;
*q++ = 0x47;
@@ -643,7 +707,16 @@ static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st)
/* stuffing bytes */
memset(q, 0xFF, TS_PACKET_SIZE - (q - buf));
- put_buffer(s->pb, buf, TS_PACKET_SIZE);
+
+ if (ts->m2ts_mode == 1) {
+ memcpy(&source_packet[4], buf, TS_PACKET_SIZE);
+ int64_t arrival_time_stamp = get_arrival_time_stamp(ts, s->pb);
+ write_starttime_code(source_packet, arrival_time_stamp);
+
+ put_buffer(s->pb, source_packet, TS_PACKET_SIZE + 4);
+ }
+ else
+ put_buffer(s->pb, buf, TS_PACKET_SIZE);
}
static void write_pts(uint8_t *q, int fourbits, int64_t pts)
@@ -672,6 +745,7 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
MpegTSWriteStream *ts_st = st->priv_data;
MpegTSWrite *ts = s->priv_data;
uint8_t buf[TS_PACKET_SIZE];
+ uint8_t source_packet[TS_PACKET_SIZE + 4]; /* we copy the content from buf into source_packet if m2ts mode :-) */
uint8_t *q;
int val, is_start, len, header_len, write_pcr, private_code, flags;
int afc_len, stuffing_len;
@@ -698,8 +772,14 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
/* pcr insert gets priority over null packet insert */
if (write_pcr)
mpegts_insert_pcr_only(s, st);
- else
- mpegts_insert_null_packet(s);
+ else {
+ if (ts->m2ts_mode == 1) {
+ ts->counted_null_packets++;
+ ts->last_null_packets++;
+ }
+ else
+ mpegts_insert_null_packet(s);
+ }
continue; /* recalculate write_pcr and possibly retransmit si_info */
}
@@ -837,7 +917,19 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
memcpy(buf + TS_PACKET_SIZE - len, payload, len);
payload += len;
payload_size -= len;
- put_buffer(s->pb, buf, TS_PACKET_SIZE);
+ if (ts->m2ts_mode == 1) {
+ memcpy(&source_packet[4], buf, TS_PACKET_SIZE);
+ int64_t arrival_time_stamp = get_arrival_time_stamp(s->priv_data, s->pb);
+
+ if (ts->last_null_packets > 0) {
+ ts->last_null_packets = 0;
+ }
+
+ write_starttime_code(source_packet, arrival_time_stamp);
+ put_buffer(s->pb, source_packet, TS_PACKET_SIZE + 4);
+ }
+ else
+ put_buffer(s->pb, buf, TS_PACKET_SIZE);
}
put_flush_packet(s->pb);
}
@@ -975,6 +1067,14 @@ static int mpegts_write_end(AVFormatContext *s)
return 0;
}
+/* resetting the function pointer */
+static int mpeg2ts_write_header(AVFormatContext *s)
+{
+ MpegTSWrite *ts = s->priv_data;
+ ts->m2ts_mode = 1;
+ return mpegts_write_header(s);
+}
+
AVOutputFormat ff_mpegts_muxer = {
"mpegts",
NULL_IF_CONFIG_SMALL("MPEG-2 transport stream format"),
@@ -988,3 +1088,17 @@ AVOutputFormat ff_mpegts_muxer = {
mpegts_write_end,
.priv_class = &mpegts_muxer_class,
};
+
+AVOutputFormat ff_mpeg2ts_muxer = {
+ "mpeg2ts",
+ NULL_IF_CONFIG_SMALL("BDAV MPEG-2 transport stream format"),
+ "video/x-mpeg2ts",
+ "m2ts",
+ sizeof(MpegTSWrite),
+ CODEC_ID_MP2,
+ CODEC_ID_MPEG2VIDEO,
+ mpeg2ts_write_header,
+ mpegts_write_packet,
+ mpegts_write_end,
+ .priv_class = &mpegts_muxer_class,
+};
More information about the ffmpeg-devel
mailing list