@@ -30,6 +30,7 @@
static const AVOption options[] = {
FF_RTP_FLAG_OPTS(RTPMuxContext, flags),
+ { "mpegts", "Mux an mpegts stream inside RTP", 0, AV_OPT_TYPE_CONST, {.i64 = FF_RTP_FLAG_MPEGTS}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "rtpflags" },
{ "payload_type", "Specify RTP payload type", offsetof(RTPMuxContext, payload_type), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 127, AV_OPT_FLAG_ENCODING_PARAM },
{ "ssrc", "Stream identifier", offsetof(RTPMuxContext, ssrc), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
{ "cname", "CNAME to include in RTCP SR packets", offsetof(RTPMuxContext, cname), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
@@ -85,21 +86,81 @@ static int is_supported(enum AVCodecID id)
}
}
+static void rtp_free(RTPMuxContext *s)
+{
+ if (s->mpegts_ctx) {
+ if (!s->mpegts_ctx->pb)
+ avio_open_dyn_buf(&s->mpegts_ctx->pb);
+ if (s->mpegts_ctx->pb) {
+ uint8_t *buf;
+ av_write_trailer(s->mpegts_ctx);
+ avio_close_dyn_buf(s->mpegts_ctx->pb, &buf);
+ av_free(buf);
+ }
+ avformat_free_context(s->mpegts_ctx);
+ }
+
+ av_freep(&s->buf);
+}
+
+static void init_mpegts_packetization(RTPMuxContext *s)
+{
+ int n = s->max_payload_size / TS_PACKET_SIZE;
+ if (n < 1)
+ n = 1;
+ s->max_payload_size = n * TS_PACKET_SIZE;
+ s->buf_ptr = s->buf;
+}
+
static int rtp_write_header(AVFormatContext *s1)
{
RTPMuxContext *s = s1->priv_data;
- int n;
+ int n, ret = AVERROR(EINVAL);
AVStream *st;
- if (s1->nb_streams != 1) {
- av_log(s1, AV_LOG_ERROR, "Only one stream supported in the RTP muxer\n");
- return AVERROR(EINVAL);
- }
- st = s1->streams[0];
- if (!is_supported(st->codec->codec_id)) {
- av_log(s1, AV_LOG_ERROR, "Unsupported codec %x\n", st->codec->codec_id);
+ if (s->flags & FF_RTP_FLAG_MPEGTS) {
+ int i;
+ AVOutputFormat *mpegts_format = av_guess_format("mpegts", NULL, NULL);
+ if (s1->nb_streams < 1) {
+ av_log(s1, AV_LOG_ERROR, "At least one stream required\n");
+ return AVERROR(EINVAL);
+ }
+ if (!mpegts_format)
+ return AVERROR(ENOSYS);
+ s->mpegts_ctx = avformat_alloc_context();
+ if (!s->mpegts_ctx)
+ return AVERROR(ENOMEM);
+ s->mpegts_ctx->oformat = mpegts_format;
+ s->mpegts_ctx->max_delay = s1->max_delay;
+
+ for (i = 0; i < s1->nb_streams; i++) {
+ AVStream* st = avformat_new_stream(s->mpegts_ctx, NULL);
+ if (!st)
+ goto fail;
+ st->time_base = s1->streams[i]->time_base;
+ st->sample_aspect_ratio = s1->streams[i]->sample_aspect_ratio;
+ avcodec_copy_context(st->codec, s1->streams[i]->codec);
+ }
+ if ((ret = avio_open_dyn_buf(&s->mpegts_ctx->pb)) < 0)
+ goto fail;
+ if ((ret = avformat_write_header(s->mpegts_ctx, NULL)) < 0)
+ goto fail;
+ for (i = 0; i < s1->nb_streams; i++)
+ s1->streams[i]->time_base = s->mpegts_ctx->streams[i]->time_base;
+
+ s->payload_type = 33;
+ st = s1->streams[0];
+ } else {
+ if (s1->nb_streams != 1) {
+ av_log(s1, AV_LOG_ERROR, "Only one stream supported in the RTP muxer\n");
+ return AVERROR(EINVAL);
+ }
+ st = s1->streams[0];
+ if (!is_supported(st->codec->codec_id)) {
+ av_log(s1, AV_LOG_ERROR, "Unsupported codec %x\n", st->codec->codec_id);
- return -1;
+ return -1;
+ }
}
if (s->payload_type < 0) {
@@ -148,6 +209,11 @@ static int rtp_write_header(AVFormatContext *s1)
}
s->max_payload_size = s1->packet_size - 12;
+ if (s->flags & FF_RTP_FLAG_MPEGTS) {
+ init_mpegts_packetization(s);
+ return 0;
+ }
+
s->max_frames_per_packet = 0;
if (s1->max_delay > 0) {
if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
@@ -185,11 +251,7 @@ static int rtp_write_header(AVFormatContext *s1)
case AV_CODEC_ID_MPEG2VIDEO:
break;
case AV_CODEC_ID_MPEG2TS:
- n = s->max_payload_size / TS_PACKET_SIZE;
- if (n < 1)
- n = 1;
- s->max_payload_size = n * TS_PACKET_SIZE;
- s->buf_ptr = s->buf;
+ init_mpegts_packetization(s);
break;
case AV_CODEC_ID_H264:
/* check for H.264 MP4 syntax */
@@ -269,8 +331,8 @@ defaultcase:
return 0;
fail:
- av_freep(&s->buf);
- return AVERROR(EINVAL);
+ rtp_free(s);
+ return ret;
}
/* send an rtcp sender report packet */
@@ -506,6 +568,31 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt)
AVStream *st = s1->streams[0];
int rtcp_bytes;
int size= pkt->size;
+ AVPacket local_pkt;
+
+ if (s->flags & FF_RTP_FLAG_MPEGTS) {
+ int ret;
+ if (!s->mpegts_ctx->pb) {
+ if ((ret = avio_open_dyn_buf(&s->mpegts_ctx->pb)) < 0)
+ return ret;
+ }
+ if ((ret = av_write_frame(s->mpegts_ctx, pkt)) < 0)
+ return ret;
+ av_init_packet(&local_pkt);
+ size = avio_close_dyn_buf(s->mpegts_ctx->pb, &local_pkt.data);
+ s->mpegts_ctx->pb = NULL;
+ if (size == 0) {
+ av_free(local_pkt.data);
+ return 0;
+ }
+
+ local_pkt.size = size;
+ local_pkt.stream_index = 0;
+ local_pkt.pts = pkt->pts;
+ local_pkt.dts = pkt->dts;
+
+ pkt = &local_pkt;
+ }
av_dlog(s1, "%d: write len=%d\n", pkt->stream_index, size);
@@ -520,6 +607,12 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt)
}
s->cur_timestamp = s->base_timestamp + pkt->pts;
+ if (s->flags & FF_RTP_FLAG_MPEGTS) {
+ rtp_send_mpegts_raw(s1, local_pkt.data, local_pkt.size);
+ av_free(local_pkt.data);
+ return 0;
+ }
+
switch(st->codec->codec_id) {
case AV_CODEC_ID_PCM_MULAW:
case AV_CODEC_ID_PCM_ALAW:
@@ -617,7 +710,8 @@ static int rtp_write_trailer(AVFormatContext *s1)
* be NULL here even if it was successfully allocated at the start. */
if (s1->pb && (s->flags & FF_RTP_FLAG_SEND_BYE))
rtcp_send_sr(s1, ff_ntp_time(), 1);
- av_freep(&s->buf);
+
+ rtp_free(s);
return 0;
}
@@ -60,6 +60,8 @@ struct RTPMuxContext {
int flags;
unsigned int frame_count;
+
+ AVFormatContext *mpegts_ctx;
};
typedef struct RTPMuxContext RTPMuxContext;
@@ -69,6 +71,7 @@ typedef struct RTPMuxContext RTPMuxContext;
#define FF_RTP_FLAG_SKIP_RTCP 4
#define FF_RTP_FLAG_H264_MODE0 8
#define FF_RTP_FLAG_SEND_BYE 16
+#define FF_RTP_FLAG_MPEGTS 32
#define FF_RTP_FLAG_OPTS(ctx, fieldname) \
{ "rtpflags", "RTP muxer flags", offsetof(ctx, fieldname), AV_OPT_TYPE_FLAGS, {.i64 = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "rtpflags" }, \
@@ -698,7 +698,7 @@ void ff_sdp_write_media(char *buff, int size, AVStream *st, int idx,
{
AVCodecContext *c = st->codec;
const char *type;
- int payload_type;
+ int payload_type, mpegts = 0;
payload_type = ff_rtp_get_payload_type(fmt, c, idx);
@@ -708,6 +708,11 @@ void ff_sdp_write_media(char *buff, int size, AVStream *st, int idx,
case AVMEDIA_TYPE_SUBTITLE: type = "text" ; break;
default : type = "application"; break;
}
+ if (fmt && fmt->oformat && fmt->oformat->priv_class &&
+ av_opt_flag_is_set(fmt->priv_data, "rtpflags", "mpegts")) {
+ type = "application";
+ mpegts = 1;
+ }
av_strlcatf(buff, size, "m=%s %d RTP/AVP %d\r\n", type, port, payload_type);
sdp_write_address(buff, size, dest_addr, dest_type, ttl);
@@ -715,7 +720,8 @@ void ff_sdp_write_media(char *buff, int size, AVStream *st, int idx,
av_strlcatf(buff, size, "b=AS:%d\r\n", c->bit_rate / 1000);
}
- sdp_write_media_attributes(buff, size, c, payload_type, fmt);
+ if (!mpegts)
+ sdp_write_media_attributes(buff, size, c, payload_type, fmt);
}
int av_sdp_create(AVFormatContext *ac[], int n_files, char *buf, int size)
@@ -782,6 +788,9 @@ int av_sdp_create(AVFormatContext *ac[], int n_files, char *buf, int size)
av_free(crypto_suite);
av_free(crypto_params);
}
+ if (ac[i] && ac[i]->oformat && ac[i]->oformat->priv_class &&
+ av_opt_flag_is_set(ac[i]->priv_data, "rtpflags", "mpegts"))
+ break;
}
}