Commit 91af5601 authored by Josh Allmann's avatar Josh Allmann Committed by Martin Storsjö

Add RTP packetization of Theora and Vorbis

Patch by Josh Allmann, joshua dot allmann at gmail

Originally committed as revision 24735 to svn://svn.ffmpeg.org/ffmpeg/trunk
parent a63dbf39
......@@ -27,6 +27,7 @@ version <next>:
- SubRip subtitle file muxer and demuxer
- Chinese AVS encoding via libxavs
- ffprobe -show_packets option added
- RTP packetization of Theora and Vorbis
......
......@@ -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
......
......@@ -23,7 +23,7 @@
#define LIBAVFORMAT_VERSION_MAJOR 52
#define LIBAVFORMAT_VERSION_MINOR 78
#define LIBAVFORMAT_VERSION_MICRO 0
#define LIBAVFORMAT_VERSION_MICRO 1
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
LIBAVFORMAT_VERSION_MINOR, \
......
......@@ -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,13 @@ 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
s->num_frames = 0;
goto defaultcase;
case CODEC_ID_AMR_NB:
case CODEC_ID_AMR_WB:
if (!s->max_frames_per_packet)
......@@ -155,6 +164,7 @@ static int rtp_write_header(AVFormatContext *s1)
case CODEC_ID_AAC:
s->num_frames = 0;
default:
defaultcase:
if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
av_set_pts_info(st, 32, 1, st->codec->sample_rate);
}
......@@ -393,6 +403,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);
......
......@@ -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 */
/*
* 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
break;
}
// Set ident. Must match the one in sdp.c
// 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;
// set fragment
// 0 - whole frame (possibly multiple frames)
// 1 - first fragment
// 2 - fragment continuation
// 3 - last fragmement
frag = size <= max_pkt_size ? 0 : 1;
if (!frag && !xdt) { // do we have a whole frame of raw data?
unsigned end_ptr = (unsigned)s->buf + 6 + max_pkt_size; // what we're allowed to write
unsigned ptr = (unsigned)s->buf_ptr + 2 + size; // what we're going to write
int remaining = end_ptr - ptr;
if ((s->num_frames > 0 && remaining < 0) ||
s->num_frames >= s->max_frames_per_packet) {
// send previous packets now; no room for new data
ff_rtp_send_data(s1, s->buf, s->buf_ptr - s->buf, 0);
s->num_frames = 0;
}
// buffer current frame to send later
if (0 == s->num_frames) s->timestamp = s->cur_timestamp;
s->num_frames++;
// Set packet header. Normally, this is OR'd with frag and xdt,
// but those are zero, so omitted here
*q++ = s->num_frames;
if (s->num_frames > 1) q = s->buf_ptr; // jump ahead if needed
*q++ = (size >> 8) & 0xff;
*q++ = size & 0xff;
memcpy(q, buff, size);
q += size;
s->buf_ptr = q;
return;
} else if (s->num_frames) {
// immediately send buffered frames if buffer is not raw data,
// or if current frame is fragmented.
ff_rtp_send_data(s1, s->buf, s->buf_ptr - s->buf, 0);
}
s->timestamp = s->cur_timestamp;
s->num_frames = 0;
s->buf_ptr = q;
while (size > 0) {
int len = (!frag || frag == 3) ? size : max_pkt_size;
q = s->buf_ptr;
// set packet headers
*q++ = (frag << 6) | (xdt << 4); // num_frames = 0
*q++ = (len >> 8) & 0xff;
*q++ = len & 0xff;
// set packet body
memcpy(q, buff, len);
q += len;
buff += len;
size -= len;
ff_rtp_send_data(s1, s->buf, q - s->buf, 0);
frag = size <= max_pkt_size ? 3 : 2;
}
}
......@@ -52,7 +52,7 @@ int rtsp_default_protocols = (1 << RTSP_LOWER_TRANSPORT_UDP);
#define SELECT_TIMEOUT_MS 100
#define READ_PACKET_TIMEOUT_S 10
#define MAX_TIMEOUTS READ_PACKET_TIMEOUT_S * 1000 / SELECT_TIMEOUT_MS
#define SDP_MAX_SIZE 8192
#define SDP_MAX_SIZE 16384
static void get_word_until_chars(char *buf, int buf_size,
const char *sep, const char **pp)
......
......@@ -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,75 @@ 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.\n");
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);
if (!config)
goto xiph_fail;
encoded_config = av_malloc(AV_BASE64_SIZE(config_len));
if (!encoded_config) {
av_free(config);
goto xiph_fail;
}
config[0] = config[1] = config[2] = 0;
config[3] = 1;
config[4] = 0xfe; // ident must match the one in rtpenc_xiph.c
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;
xiph_fail:
av_log(c, AV_LOG_ERROR,
"Not enough memory for configuration string\n");
return NULL;
}
static char *sdp_write_media_attributes(char *buff, int size, AVCodecContext *c, int payload_type)
{
char *config = NULL;
......@@ -297,6 +367,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/90000\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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment