From c2ff63e3ac82d5ee501e480d4714e982fc45cf8b Mon Sep 17 00:00:00 2001 From: Martin Storsjö Date: Thu, 23 Feb 2012 11:38:24 +0200 Subject: rtpenc: Move the trailing comma into FF_RTP_FLAG_OPTS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This simplifies adding more flags to the macro. Signed-off-by: Martin Storsjö --- libavformat/rtpenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libavformat/rtpenc.c') diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c index a4a6987032..e19541798b 100644 --- a/libavformat/rtpenc.c +++ b/libavformat/rtpenc.c @@ -31,7 +31,7 @@ //#define DEBUG static const AVOption options[] = { - FF_RTP_FLAG_OPTS(RTPMuxContext, flags), + FF_RTP_FLAG_OPTS(RTPMuxContext, flags) { "payload_type", "Specify RTP payload type", offsetof(RTPMuxContext, payload_type), AV_OPT_TYPE_INT, {.dbl = -1 }, -1, 127, AV_OPT_FLAG_ENCODING_PARAM }, { NULL }, }; -- cgit v1.2.3 From c4584f3c1ff3997fd98c3cc992fe82cb99f6b248 Mon Sep 17 00:00:00 2001 From: Martin Storsjö Date: Tue, 7 Feb 2012 16:39:14 +0200 Subject: rtpenc: Allow packetizing H263 according to the old RFC 2190 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to newer RFCs, this packetization scheme should only be used for interfacing with legacy systems. Implementing this packetization mode properly requires parsing the full H263 bitstream to find macroblock boundaries (and knowing their macroblock and gob numbers and motion vector predictors). This implementation tries to look for GOB headers (which can be inserted by using -ps ), but if the GOBs aren't small enough to fit into the MTU, the packetizer blindly splits packets at any offset and claims it to be a GOB boundary (by using Mode A from the RFC). While not correct, this seems to work with some receivers. Signed-off-by: Martin Storsjö --- libavformat/Makefile | 1 + libavformat/rtp.c | 4 +- libavformat/rtpenc.c | 5 ++ libavformat/rtpenc.h | 6 +++ libavformat/rtpenc_h263.c | 7 +-- libavformat/rtpenc_h263_rfc2190.c | 104 ++++++++++++++++++++++++++++++++++++++ libavformat/sdp.c | 3 ++ 7 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 libavformat/rtpenc_h263_rfc2190.c (limited to 'libavformat/rtpenc.c') diff --git a/libavformat/Makefile b/libavformat/Makefile index 5c0e34242e..9682ece804 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -242,6 +242,7 @@ OBJS-$(CONFIG_RTP_MUXER) += rtp.o \ rtpenc_latm.o \ rtpenc_amr.o \ rtpenc_h263.o \ + rtpenc_h263_rfc2190.o \ rtpenc_mpv.o \ rtpenc.o \ rtpenc_h264.o \ diff --git a/libavformat/rtp.c b/libavformat/rtp.c index b6b4b72aa3..6516779feb 100644 --- a/libavformat/rtp.c +++ b/libavformat/rtp.c @@ -106,7 +106,9 @@ int ff_rtp_get_payload_type(AVFormatContext *fmt, AVCodecContext *codec) /* static payload type */ for (i = 0; AVRtpPayloadTypes[i].pt >= 0; ++i) if (AVRtpPayloadTypes[i].codec_id == codec->codec_id) { - if (codec->codec_id == CODEC_ID_H263) + if (codec->codec_id == CODEC_ID_H263 && (!fmt || + !fmt->oformat->priv_class || + !av_opt_flag_is_set(fmt->priv_data, "rtpflags", "rfc2190"))) continue; if (codec->codec_id == CODEC_ID_PCM_S16BE) if (codec->channels != AVRtpPayloadTypes[i].audio_channels) diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c index e19541798b..4d4e168c97 100644 --- a/libavformat/rtpenc.c +++ b/libavformat/rtpenc.c @@ -443,6 +443,11 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt) ff_rtp_send_h264(s1, pkt->data, size); break; case CODEC_ID_H263: + if (s->flags & FF_RTP_FLAG_RFC2190) { + ff_rtp_send_h263_rfc2190(s1, pkt->data, size); + break; + } + /* Fallthrough */ case CODEC_ID_H263P: ff_rtp_send_h263(s1, pkt->data, size); break; diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h index fdad24947b..ff423a55d1 100644 --- a/libavformat/rtpenc.h +++ b/libavformat/rtpenc.h @@ -64,15 +64,18 @@ struct RTPMuxContext { typedef struct RTPMuxContext RTPMuxContext; #define FF_RTP_FLAG_MP4A_LATM 1 +#define FF_RTP_FLAG_RFC2190 2 #define FF_RTP_FLAG_OPTS(ctx, fieldname) \ { "rtpflags", "RTP muxer flags", offsetof(ctx, fieldname), AV_OPT_TYPE_FLAGS, {.dbl = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "rtpflags" }, \ { "latm", "Use MP4A-LATM packetization instead of MPEG4-GENERIC for AAC", 0, AV_OPT_TYPE_CONST, {.dbl = FF_RTP_FLAG_MP4A_LATM}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "rtpflags" }, \ + { "rfc2190", "Use RFC 2190 packetization instead of RFC 4629 for H.263", 0, AV_OPT_TYPE_CONST, {.dbl = FF_RTP_FLAG_RFC2190}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "rtpflags" }, \ void ff_rtp_send_data(AVFormatContext *s1, const uint8_t *buf1, int len, int m); void ff_rtp_send_h264(AVFormatContext *s1, const uint8_t *buf1, int size); void ff_rtp_send_h263(AVFormatContext *s1, const uint8_t *buf1, int size); +void ff_rtp_send_h263_rfc2190(AVFormatContext *s1, const uint8_t *buf1, int size); void ff_rtp_send_aac(AVFormatContext *s1, const uint8_t *buff, int size); void ff_rtp_send_latm(AVFormatContext *s1, const uint8_t *buff, int size); void ff_rtp_send_amr(AVFormatContext *s1, const uint8_t *buff, int size); @@ -80,4 +83,7 @@ void ff_rtp_send_mpegvideo(AVFormatContext *s1, const uint8_t *buf1, int size); void ff_rtp_send_xiph(AVFormatContext *s1, const uint8_t *buff, int size); void ff_rtp_send_vp8(AVFormatContext *s1, const uint8_t *buff, int size); +const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *restrict start, + const uint8_t *restrict end); + #endif /* AVFORMAT_RTPENC_H */ diff --git a/libavformat/rtpenc_h263.c b/libavformat/rtpenc_h263.c index fbc696e1b4..87f0bd7981 100644 --- a/libavformat/rtpenc_h263.c +++ b/libavformat/rtpenc_h263.c @@ -23,8 +23,8 @@ #include "avformat.h" #include "rtpenc.h" -static const uint8_t *find_resync_marker_reverse(const uint8_t *restrict start, - const uint8_t *restrict end) +const uint8_t *ff_h263_find_resync_marker_reverse(const uint8_t *restrict start, + const uint8_t *restrict end) { const uint8_t *p = end - 1; start += 1; /* Make sure we never return the original start. */ @@ -63,7 +63,8 @@ void ff_rtp_send_h263(AVFormatContext *s1, const uint8_t *buf1, int size) /* Look for a better place to split the frame into packets. */ if (len < size) { - const uint8_t *end = find_resync_marker_reverse(buf1, buf1 + len); + const uint8_t *end = ff_h263_find_resync_marker_reverse(buf1, + buf1 + len); len = end - buf1; } diff --git a/libavformat/rtpenc_h263_rfc2190.c b/libavformat/rtpenc_h263_rfc2190.c new file mode 100644 index 0000000000..305c1a27c8 --- /dev/null +++ b/libavformat/rtpenc_h263_rfc2190.c @@ -0,0 +1,104 @@ +/* + * RTP packetization for H.263 video + * Copyright (c) 2012 Martin Storsjo + * + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "rtpenc.h" +#include "libavcodec/put_bits.h" +#include "libavcodec/get_bits.h" + +struct H263Info { + int src; + int i; + int u; + int s; + int a; + int pb; + int tr; +}; + +static void send_mode_a(AVFormatContext *s1, const struct H263Info *info, + const uint8_t *buf, int len, int m) +{ + RTPMuxContext *s = s1->priv_data; + PutBitContext pb; + + init_put_bits(&pb, s->buf, 32); + put_bits(&pb, 1, 0); /* F - 0, mode A */ + put_bits(&pb, 1, 0); /* P - 0, normal I/P */ + put_bits(&pb, 3, 0); /* SBIT - 0 bits */ + put_bits(&pb, 3, 0); /* EBIT - 0 bits */ + put_bits(&pb, 3, info->src); /* SRC - source format */ + put_bits(&pb, 1, info->i); /* I - inter/intra */ + put_bits(&pb, 1, info->u); /* U - unrestricted motion vector */ + put_bits(&pb, 1, info->s); /* S - syntax-baesd arithmetic coding */ + put_bits(&pb, 1, info->a); /* A - advanced prediction */ + put_bits(&pb, 4, 0); /* R - reserved */ + put_bits(&pb, 2, 0); /* DBQ - 0 */ + put_bits(&pb, 3, 0); /* TRB - 0 */ + put_bits(&pb, 8, info->tr); /* TR */ + flush_put_bits(&pb); + memcpy(s->buf + 4, buf, len); + + ff_rtp_send_data(s1, s->buf, len + 4, m); +} + +void ff_rtp_send_h263_rfc2190(AVFormatContext *s1, const uint8_t *buf, int size) +{ + RTPMuxContext *s = s1->priv_data; + int len; + GetBitContext gb; + struct H263Info info = { 0 }; + + s->timestamp = s->cur_timestamp; + + init_get_bits(&gb, buf, size*8); + if (get_bits(&gb, 22) == 0x20) { /* Picture Start Code */ + info.tr = get_bits(&gb, 8); + skip_bits(&gb, 2); /* PTYPE start, H261 disambiguation */ + skip_bits(&gb, 3); /* Split screen, document camera, freeze picture release */ + info.src = get_bits(&gb, 3); + info.i = get_bits(&gb, 1); + info.u = get_bits(&gb, 1); + info.s = get_bits(&gb, 1); + info.a = get_bits(&gb, 1); + info.pb = get_bits(&gb, 1); + } + + while (size > 0) { + len = FFMIN(s->max_payload_size - 4, size); + + /* Look for a better place to split the frame into packets. */ + if (len < size) { + const uint8_t *end = ff_h263_find_resync_marker_reverse(buf, + buf + len); + len = end - buf; + if (len == s->max_payload_size - 4) + av_log(s1, AV_LOG_WARNING, + "No GOB boundary found within MTU size, splitting at " + "a random boundary\n"); + } + + send_mode_a(s1, &info, buf, len, len == size); + + buf += len; + size -= len; + } +} diff --git a/libavformat/sdp.c b/libavformat/sdp.c index 5e0bf72050..b2c4f7bcd8 100644 --- a/libavformat/sdp.c +++ b/libavformat/sdp.c @@ -404,6 +404,9 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, * actually specifies the maximum video size, but we only know * the current size. This is required for playback on Android * stagefright and on Samsung bada. */ + if (!fmt || !fmt->oformat->priv_class || + !av_opt_flag_is_set(fmt->priv_data, "rtpflags", "rfc2190") || + c->codec_id == CODEC_ID_H263P) av_strlcatf(buff, size, "a=rtpmap:%d H263-2000/90000\r\n" "a=framesize:%d %d-%d\r\n", payload_type, -- cgit v1.2.3 From ada4e362b9fa8cef033afb040f99b07069a7a5ee Mon Sep 17 00:00:00 2001 From: Martin Storsjö Date: Sun, 5 Sep 2010 01:10:54 +0300 Subject: rtpenc: Add an error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also return a proper error code. Signed-off-by: Martin Storsjö --- libavformat/rtpenc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'libavformat/rtpenc.c') diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c index 4d4e168c97..604c4a0552 100644 --- a/libavformat/rtpenc.c +++ b/libavformat/rtpenc.c @@ -85,8 +85,10 @@ static int rtp_write_header(AVFormatContext *s1) int max_packet_size, n; AVStream *st; - if (s1->nb_streams != 1) - return -1; + 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); -- cgit v1.2.3 From 7337484ed21d068184eff74fcdb14b886553159f Mon Sep 17 00:00:00 2001 From: Martin Storsjö Date: Thu, 9 Feb 2012 23:28:15 +0200 Subject: rtpenc: Add an option for not sending RTCP packets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Martin Storsjö --- libavformat/rtpenc.c | 5 +++-- libavformat/rtpenc.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'libavformat/rtpenc.c') diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c index 604c4a0552..66ea4ced92 100644 --- a/libavformat/rtpenc.c +++ b/libavformat/rtpenc.c @@ -388,8 +388,9 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt) rtcp_bytes = ((s->octet_count - s->last_octet_count) * RTCP_TX_RATIO_NUM) / RTCP_TX_RATIO_DEN; - if (s->first_packet || ((rtcp_bytes >= RTCP_SR_SIZE) && - (ff_ntp_time() - s->last_rtcp_ntp_time > 5000000))) { + if ((s->first_packet || ((rtcp_bytes >= RTCP_SR_SIZE) && + (ff_ntp_time() - s->last_rtcp_ntp_time > 5000000))) && + !(s->flags & FF_RTP_FLAG_SKIP_RTCP)) { rtcp_send_sr(s1, ff_ntp_time()); s->last_octet_count = s->octet_count; s->first_packet = 0; diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h index ff423a55d1..9ccc8e71fa 100644 --- a/libavformat/rtpenc.h +++ b/libavformat/rtpenc.h @@ -65,11 +65,13 @@ typedef struct RTPMuxContext RTPMuxContext; #define FF_RTP_FLAG_MP4A_LATM 1 #define FF_RTP_FLAG_RFC2190 2 +#define FF_RTP_FLAG_SKIP_RTCP 4 #define FF_RTP_FLAG_OPTS(ctx, fieldname) \ { "rtpflags", "RTP muxer flags", offsetof(ctx, fieldname), AV_OPT_TYPE_FLAGS, {.dbl = 0}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "rtpflags" }, \ { "latm", "Use MP4A-LATM packetization instead of MPEG4-GENERIC for AAC", 0, AV_OPT_TYPE_CONST, {.dbl = FF_RTP_FLAG_MP4A_LATM}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "rtpflags" }, \ { "rfc2190", "Use RFC 2190 packetization instead of RFC 4629 for H.263", 0, AV_OPT_TYPE_CONST, {.dbl = FF_RTP_FLAG_RFC2190}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "rtpflags" }, \ + { "skip_rtcp", "Don't send RTCP sender reports", 0, AV_OPT_TYPE_CONST, {.dbl = FF_RTP_FLAG_SKIP_RTCP}, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM, "rtpflags" }, \ void ff_rtp_send_data(AVFormatContext *s1, const uint8_t *buf1, int len, int m); -- cgit v1.2.3 From f553462041096d5d2afd9a8841a7af50df5c2540 Mon Sep 17 00:00:00 2001 From: Martin Storsjö Date: Thu, 23 Feb 2012 11:54:13 +0200 Subject: rtpenc: Move max_packet_size to a context variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is in preparation for exposing this via an avoption. Signed-off-by: Martin Storsjö --- libavformat/rtpenc.c | 12 ++++++------ libavformat/rtpenc.h | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'libavformat/rtpenc.c') diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c index 66ea4ced92..bdbe411192 100644 --- a/libavformat/rtpenc.c +++ b/libavformat/rtpenc.c @@ -82,7 +82,7 @@ static int is_supported(enum CodecID id) static int rtp_write_header(AVFormatContext *s1) { RTPMuxContext *s = s1->priv_data; - int max_packet_size, n; + int n; AVStream *st; if (s1->nb_streams != 1) { @@ -109,16 +109,16 @@ static int rtp_write_header(AVFormatContext *s1) s->first_rtcp_ntp_time = (s1->start_time_realtime / 1000) * 1000 + NTP_OFFSET_US; - max_packet_size = s1->pb->max_packet_size; - if (max_packet_size <= 12) { - av_log(s1, AV_LOG_ERROR, "Max packet size %d too low\n", max_packet_size); + s->max_packet_size = s1->pb->max_packet_size; + if (s->max_packet_size <= 12) { + av_log(s1, AV_LOG_ERROR, "Max packet size %d too low\n", s->max_packet_size); return AVERROR(EIO); } - s->buf = av_malloc(max_packet_size); + s->buf = av_malloc(s->max_packet_size); if (s->buf == NULL) { return AVERROR(ENOMEM); } - s->max_payload_size = max_packet_size - 12; + s->max_payload_size = s->max_packet_size - 12; s->max_frames_per_packet = 0; if (s1->max_delay) { diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h index 9ccc8e71fa..2bb2b815c3 100644 --- a/libavformat/rtpenc.h +++ b/libavformat/rtpenc.h @@ -34,6 +34,7 @@ struct RTPMuxContext { uint32_t timestamp; uint32_t base_timestamp; uint32_t cur_timestamp; + int max_packet_size; int max_payload_size; int num_frames; -- cgit v1.2.3 From ba605cef7961ee699c893d1a3b5c9730f0a37b6c Mon Sep 17 00:00:00 2001 From: Martin Storsjö Date: Thu, 23 Feb 2012 11:56:15 +0200 Subject: rtpenc: Expose the max packet size via an avoption MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows opting for a lower MTU than what the AVIOContext indicated, and allows writing into outputs that don't indicate an MTU at all (such as plain files, which is useful for testing). This also allows querying for the MTU via the avoption. Signed-off-by: Martin Storsjö --- libavformat/rtpenc.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'libavformat/rtpenc.c') diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c index bdbe411192..e4ef0fc92b 100644 --- a/libavformat/rtpenc.c +++ b/libavformat/rtpenc.c @@ -33,6 +33,7 @@ static const AVOption options[] = { FF_RTP_FLAG_OPTS(RTPMuxContext, flags) { "payload_type", "Specify RTP payload type", offsetof(RTPMuxContext, payload_type), AV_OPT_TYPE_INT, {.dbl = -1 }, -1, 127, AV_OPT_FLAG_ENCODING_PARAM }, + { "max_packet_size", "Max packet size", offsetof(RTPMuxContext, max_packet_size), AV_OPT_TYPE_INT, {.dbl = 0 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, { NULL }, }; @@ -109,7 +110,12 @@ static int rtp_write_header(AVFormatContext *s1) s->first_rtcp_ntp_time = (s1->start_time_realtime / 1000) * 1000 + NTP_OFFSET_US; - s->max_packet_size = s1->pb->max_packet_size; + if (s->max_packet_size) { + if (s1->pb->max_packet_size) + s->max_packet_size = FFMIN(s->max_payload_size, + s1->pb->max_packet_size); + } else + s->max_packet_size = s1->pb->max_packet_size; if (s->max_packet_size <= 12) { av_log(s1, AV_LOG_ERROR, "Max packet size %d too low\n", s->max_packet_size); return AVERROR(EIO); -- cgit v1.2.3