On 27 July 2010 15:28, Josh Allmann <joshua.allm...@gmail.com> wrote: > Hi, > > On 27 July 2010 03:03, Martin Storsjö <mar...@martin.st> wrote: >> On Mon, 26 Jul 2010, Josh Allmann wrote: >> >>> This version works better, but is not yet complete. >>> >>> -The scaffolding for Vorbis is mostly up, but I have to set its SDP >>> properly. > > Done. > >>> -Theora video has problems with I-frames (or whatever the Theora >>> equivalent is), and exhibits serious blocking in areas of motion. > > Fixed, although on occassion it is a bit wonky. The depacketizer will > complain about a missing start fragment at a particular position in > big buck bunny, disrupting the video briefly, but otherwise it is > fine. > >>> I hope this is due to some invalid reads that Valgrind complains about. > > Valgrind fixed. > >>> -Packing multiple frames in a single packet is another TODO. >> > > Not yet complete, but is not critical for proper operation, either. > >> A few comments: >> >> + /* set xiph data type */ >> + switch (*buff) { >> + case 0x01: // vorbis id >> + case 0x05: // vorbis setup >> + case 0x80: // theora header >> + case 0x82: // theora tables >> + xdt = 1; // packed config payload >> + case 0x03: // vorbis comments >> + case 0x81: // theora comments >> + xdt = 2; // comment payload >> + default: >> + xdt = 0; // raw data payload >> + } >> >> I guess you want break statements in the switch, too... >> > > Fixed. > >> + /* set ident >> + * Probably need a non-fixed way of generating >> + * this, but it has to be done in SDP and passed in from there. */ >> + q = s->buf; >> + *q++ = 0xfe; >> + *q++ = 0xcd; >> + *q++ = 0xba; >> >> I haven't read the specs, but what's the role of this ident code? Is there >> any harm in having it hardcoded to a specific value? Is it set in the >> original stream data somewhere, so that you'd have to parse out the >> correct value from there? Or is it only used to distinguish streams if you >> have more than one vorbis/theora stream in the same presentation? In that >> case, you could use e.g. one hardcoded value for vorbis and another for >> theora - that would probably be enough for some time at lesat. > > As Luca said, it is only used to make sure the extradata doesn't > change mid-stream. Different streams can have the same ident, eg a > Vorbis and a Theora can share 0xfecdba. Our depacketizer doesn't > handle changing the ident anyway. > > Revised patch attached. I also had to enlarge the outgoing RTSP buffer > to handle the SDP extradata. >
Re-sending, due to this hunk: @@ -154,6 +156,11 @@ static int rtp_write_header(AVFormatContext *s1) } case CODEC_ID_AAC: s->num_frames = 0; + case CODEC_ID_VORBIS: + case CODEC_ID_THEORA: + if(!s->max_frames_per_packet) s->max_frames_per_packet = 15; + s->max_frames_per_packet = av_clip(s->max_frames_per_packet, 1, 15); + s->max_payload_size -= 6; // ident+frag+tdt/vdt+pkt_num+pkt_length default: if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) { av_set_pts_info(st, 32, 1, st->codec->sample_rate); Apparently AAC and AMR both fall through to the default case. Whether that's intentional, I don't know, so I moved my stuff to minimize behavioral changes to existing code. Josh
diff --git a/libavformat/Makefile b/libavformat/Makefile index c32a254..dbb326c 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -219,6 +219,7 @@ OBJS-$(CONFIG_RTP_MUXER) += rtp.o \ rtpenc_mpv.o \ rtpenc.o \ rtpenc_h264.o \ + rtpenc_xiph.o \ avc.o OBJS-$(CONFIG_RTSP_DEMUXER) += rtsp.o httpauth.o OBJS-$(CONFIG_RTSP_MUXER) += rtsp.o rtspenc.o httpauth.o diff --git a/libavformat/rtpenc.c b/libavformat/rtpenc.c index 4453f65..b4b4053 100644 --- a/libavformat/rtpenc.c +++ b/libavformat/rtpenc.c @@ -53,6 +53,8 @@ static int is_supported(enum CodecID id) case CODEC_ID_MPEG2TS: case CODEC_ID_AMR_NB: case CODEC_ID_AMR_WB: + case CODEC_ID_VORBIS: + case CODEC_ID_THEORA: return 1; default: return 0; @@ -135,6 +137,12 @@ static int rtp_write_header(AVFormatContext *s1) s->nal_length_size = (st->codec->extradata[4] & 0x03) + 1; } break; + case CODEC_ID_VORBIS: + case CODEC_ID_THEORA: + if(!s->max_frames_per_packet) s->max_frames_per_packet = 15; + s->max_frames_per_packet = av_clip(s->max_frames_per_packet, 1, 15); + s->max_payload_size -= 6; // ident+frag+tdt/vdt+pkt_num+pkt_length + break; case CODEC_ID_AMR_NB: case CODEC_ID_AMR_WB: if (!s->max_frames_per_packet) @@ -393,6 +401,10 @@ static int rtp_write_packet(AVFormatContext *s1, AVPacket *pkt) case CODEC_ID_H263P: ff_rtp_send_h263(s1, pkt->data, size); break; + case CODEC_ID_VORBIS: + case CODEC_ID_THEORA: + ff_rtp_send_xiph(s1, pkt->data, size); + break; default: /* better than nothing : send the codec raw data */ rtp_send_raw(s1, pkt->data, size); diff --git a/libavformat/rtpenc.h b/libavformat/rtpenc.h index 95e70c1..d5d8b99 100644 --- a/libavformat/rtpenc.h +++ b/libavformat/rtpenc.h @@ -67,5 +67,6 @@ void ff_rtp_send_h263(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_amr(AVFormatContext *s1, const uint8_t *buff, int size); 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); #endif /* AVFORMAT_RTPENC_H */ diff --git a/libavformat/rtpenc_xiph.c b/libavformat/rtpenc_xiph.c new file mode 100644 index 0000000..d01ecdd --- /dev/null +++ b/libavformat/rtpenc_xiph.c @@ -0,0 +1,91 @@ +/* + * RTP packetization for Xiph audio and video + * Copyright (c) 2010 Josh Allmann + * + * This file is part of FFmpeg. + * + * FFmpeg 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. + * + * FFmpeg 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 FFmpeg; 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" + +/** + * Packetize Xiph frames into RTP according to + * RFC 5215 (Vorbis) and the Theora RFC draft. + * (http://svn.xiph.org/trunk/theora/doc/draft-ietf-avt-rtp-theora-00.txt) + */ +void ff_rtp_send_xiph(AVFormatContext *s1, const uint8_t *buff, int size) +{ + RTPMuxContext *s = s1->priv_data; + int max_pkt_size, xdt, frag; + uint8_t *q; + + max_pkt_size = s->max_payload_size; + + /* set xiph data type */ + switch (*buff) { + case 0x01: // vorbis id + case 0x05: // vorbis setup + case 0x80: // theora header + case 0x82: // theora tables + xdt = 1; // packed config payload + break; + case 0x03: // vorbis comments + case 0x81: // theora comments + xdt = 2; // comment payload + break; + default: + xdt = 0; // raw data payload + } + + /* set ident + * Probably need a non-fixed way of generating + * this, but it has to be done in SDP and passed in from there. */ + q = s->buf; + *q++ = 0xfe; + *q++ = 0xcd; + *q++ = 0xba; + s->buf_ptr = q; + + /* set fragment + * 0 - whole frame (possibly multiple frames) + * 1 - first fragment + * 2 - fragment continuation + * 3 - last fragmement */ + frag = size <= max_pkt_size ? 0 : 1; + + /* TODO use s->buf_ptr, mark position for later in order to + * transmit multiple frames in one RTP packet. To do this, + * need to avoid adding in ident, frag, xdt twice */ + while (size > 0) { + int len = (!frag || frag == 3) ? size : max_pkt_size; + int num_pkts = frag ? 0 : 1; // XXX set properly for >1 frame/pkt + q = s->buf_ptr; + + /* set packet headers */ + *q++ = (frag << 6) | (xdt << 4) | num_pkts; + *q++ = (len >> 8) & 0xff; + *q++ = len & 0xff; + /* set packet body */ + memmove(q, buff, len); + q += len; + buff += len; + + ff_rtp_send_data(s1, s->buf, q - s->buf, 0); // marker bit unused + size -= len; + frag = size <= max_pkt_size ? 3 : 2; + } +} diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c index 7dede4c..5596ae5 100644 --- a/libavformat/rtsp.c +++ b/libavformat/rtsp.c @@ -872,7 +872,7 @@ int ff_rtsp_send_cmd_with_content_async(AVFormatContext *s, int send_content_length) { RTSPState *rt = s->priv_data; - char buf[4096], *out_buf; + char buf[16384], *out_buf; // large buffer to accommodate xiph sdp char base64buf[AV_BASE64_SIZE(sizeof(buf))]; /* Add in RTSP headers */ @@ -1295,7 +1295,7 @@ static int rtsp_setup_output_streams(AVFormatContext *s, const char *addr) rt->start_time = av_gettime(); /* Announce the stream */ - sdp = av_mallocz(8192); + sdp = av_mallocz(16384); // massive SDP buffer due to Xiph extradata if (sdp == NULL) return AVERROR(ENOMEM); /* We create the SDP based on the RTSP AVFormatContext where we @@ -1314,7 +1314,7 @@ static int rtsp_setup_output_streams(AVFormatContext *s, const char *addr) ff_url_join(sdp_ctx.filename, sizeof(sdp_ctx.filename), "rtsp", NULL, addr, -1, NULL); ctx_array[0] = &sdp_ctx; - if (avf_sdp_create(ctx_array, 1, sdp, 8192)) { + if (avf_sdp_create(ctx_array, 1, sdp, 16384)) { av_free(sdp); return AVERROR_INVALIDDATA; } diff --git a/libavformat/sdp.c b/libavformat/sdp.c index b34b944..870cd90 100644 --- a/libavformat/sdp.c +++ b/libavformat/sdp.c @@ -21,6 +21,7 @@ #include <string.h> #include "libavutil/avstring.h" #include "libavutil/base64.h" +#include "libavcodec/xiph.h" #include "avformat.h" #include "internal.h" #include "avc.h" @@ -220,6 +221,68 @@ static char *extradata2config(AVCodecContext *c) return config; } +static char *xiph_extradata2config(AVCodecContext *c) +{ + char *config, *encoded_config; + uint8_t *header_start[3]; + int headers_len, header_len[3], config_len; + int first_header_size; + + switch (c->codec_id) { + case CODEC_ID_THEORA: + first_header_size = 42; + break; + case CODEC_ID_VORBIS: + first_header_size = 30; + break; + default: + av_log(c, AV_LOG_ERROR, "Unsupported Xiph codec ID\n"); + return NULL; + } + + if (ff_split_xiph_headers(c->extradata, c->extradata_size, + first_header_size, header_start, + header_len) < 0) { + av_log(c, AV_LOG_ERROR, "Extradata corrupt."); + return NULL; + } + + headers_len = header_len[0]+header_len[2]; + config_len = 4 + // count + 3 + // ident + 2 + // packet size + 1 + // header count + 2 + // header size + headers_len; // and the rest + config = av_malloc(config_len); + encoded_config = av_malloc(AV_BASE64_SIZE(config_len)); + + if (!config || !encoded_config) { + av_log(c, AV_LOG_ERROR, + "Not enough memory for configuration string\n"); + return NULL; + } + + config[0] = config[1] = config[2] = 0; + config[3] = 1; + config[4] = 0xfe; + config[5] = 0xcd; + config[6] = 0xba; + config[7] = (headers_len >> 8) & 0xff; + config[8] = headers_len & 0xff; + config[9] = 2; + config[10] = header_len[0]; + config[11] = 0; // size of comment header; nonexistent + memcpy(config + 12, header_start[0], header_len[0]); + memcpy(config + 12 + header_len[0], header_start[2], header_len[2]); + + av_base64_encode(encoded_config, AV_BASE64_SIZE(config_len), + config, config_len); + av_free(config); + + return encoded_config; +} + static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, int payload_type) { char *config = NULL; @@ -297,6 +360,51 @@ static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, payload_type, c->sample_rate, c->channels, payload_type); break; + case CODEC_ID_VORBIS: + if (c->extradata_size) + config = xiph_extradata2config(c); + else + av_log(c, AV_LOG_ERROR, "Vorbis configuration info missing\n"); + if (!config) + return NULL; + + av_strlcatf(buff, size, "a=rtpmap:%d vorbis/%d/%d\r\n" + "a=fmtp:%d configuration=%s\r\n", + payload_type, c->sample_rate, c->channels, + payload_type, config); + break; + case CODEC_ID_THEORA: { + const char *pix_fmt; + if (c->extradata_size) + config = xiph_extradata2config(c); + else + av_log(c, AV_LOG_ERROR, "Theora configuation info missing\n"); + if (!config) + return NULL; + + switch (c->pix_fmt) { + case PIX_FMT_YUV420P: + pix_fmt = "YCbCr-4:2:0"; + break; + case PIX_FMT_YUV422P: + pix_fmt = "YCbCr-4:2:2"; + break; + case PIX_FMT_YUV444P: + pix_fmt = "YCbCr-4:4:4"; + break; + default: + av_log(c, AV_LOG_ERROR, "Unsupported pixel format.\n"); + return NULL; + } + + av_strlcatf(buff, size, "a=rtpmap:%d theora/9000\r\n" + "a=fmtp:%d delivery-method=inline; " + "width=%d; height=%d; sampling=%s; " + "configuration=%s\r\n", + payload_type, payload_type, + c->width, c->height, pix_fmt, config); + break; + } default: /* Nothing special to do here... */ break;
_______________________________________________ FFmpeg-soc mailing list FFmpeg-soc@mplayerhq.hu https://lists.mplayerhq.hu/mailman/listinfo/ffmpeg-soc