Mercurial > libavformat.hg
changeset 5726:04b39763b51b libavformat
Rename RTP depacketizer files from rtp_* to rtpdec_*
author | mstorsjo |
---|---|
date | Sun, 28 Feb 2010 11:03:14 +0000 |
parents | 334f223fc455 |
children | 74142991b333 |
files | Makefile rtp_asf.c rtp_asf.h rtp_h264.c rtp_h264.h rtp_vorbis.c rtp_vorbis.h rtpdec.c rtpdec_asf.c rtpdec_asf.h rtpdec_h264.c rtpdec_h264.h rtpdec_vorbis.c rtpdec_vorbis.h rtsp.c |
diffstat | 15 files changed, 1040 insertions(+), 1040 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Sun Feb 28 03:59:15 2010 +0000 +++ b/Makefile Sun Feb 28 11:03:14 2010 +0000 @@ -216,10 +216,10 @@ rtp.o \ rtpdec.o \ rtpdec_amr.o \ + rtpdec_asf.o \ rtpdec_h263.o \ - rtp_asf.o \ - rtp_h264.o \ - rtp_vorbis.o + rtpdec_h264.o \ + rtpdec_vorbis.o OBJS-$(CONFIG_SEGAFILM_DEMUXER) += segafilm.o OBJS-$(CONFIG_SHORTEN_DEMUXER) += raw.o id3v2.o OBJS-$(CONFIG_SIFF_DEMUXER) += siff.o
--- a/rtp_asf.c Sun Feb 28 03:59:15 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,281 +0,0 @@ -/* - * Microsoft RTP/ASF support. - * Copyright (c) 2008 Ronald S. Bultje - * - * 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 - */ - -/** - * @file libavformat/rtp_asf.c - * @brief Microsoft RTP/ASF support - * @author Ronald S. Bultje <rbultje@ronald.bitfreak.net> - */ - -#include <libavutil/base64.h> -#include <libavutil/avstring.h> -#include <libavutil/intreadwrite.h> -#include "rtp.h" -#include "rtp_asf.h" -#include "rtsp.h" -#include "asf.h" - -/** - * From MSDN 2.2.1.4, we learn that ASF data packets over RTP should not - * contain any padding. Unfortunately, the header min/max_pktsize are not - * updated (thus making min_pktsize invalid). Here, we "fix" these faulty - * min_pktsize values in the ASF file header. - * @return 0 on success, <0 on failure (currently -1). - */ -static int rtp_asf_fix_header(uint8_t *buf, int len) -{ - uint8_t *p = buf, *end = buf + len; - - if (len < sizeof(ff_asf_guid) * 2 + 22 || - memcmp(p, ff_asf_header, sizeof(ff_asf_guid))) { - return -1; - } - p += sizeof(ff_asf_guid) + 14; - do { - uint64_t chunksize = AV_RL64(p + sizeof(ff_asf_guid)); - if (memcmp(p, ff_asf_file_header, sizeof(ff_asf_guid))) { - if (chunksize > end - p) - return -1; - p += chunksize; - continue; - } - - /* skip most of the file header, to min_pktsize */ - p += 6 * 8 + 3 * 4 + sizeof(ff_asf_guid) * 2; - if (p + 8 <= end && AV_RL32(p) == AV_RL32(p + 4)) { - /* and set that to zero */ - AV_WL32(p, 0); - return 0; - } - break; - } while (end - p >= sizeof(ff_asf_guid) + 8); - - return -1; -} - -/** - * The following code is basically a buffered ByteIOContext, - * with the added benefit of returning -EAGAIN (instead of 0) - * on packet boundaries, such that the ASF demuxer can return - * safely and resume business at the next packet. - */ -static int packetizer_read(void *opaque, uint8_t *buf, int buf_size) -{ - return AVERROR(EAGAIN); -} - -static void init_packetizer(ByteIOContext *pb, uint8_t *buf, int len) -{ - init_put_byte(pb, buf, len, 0, NULL, packetizer_read, NULL, NULL); - - /* this "fills" the buffer with its current content */ - pb->pos = len; - pb->buf_end = buf + len; -} - -void ff_wms_parse_sdp_a_line(AVFormatContext *s, const char *p) -{ - if (av_strstart(p, "pgmpu:data:application/vnd.ms.wms-hdr.asfv1;base64,", &p)) { - ByteIOContext pb; - RTSPState *rt = s->priv_data; - int len = strlen(p) * 6 / 8; - char *buf = av_mallocz(len); - av_base64_decode(buf, p, len); - - if (rtp_asf_fix_header(buf, len) < 0) - av_log(s, AV_LOG_ERROR, - "Failed to fix invalid RTSP-MS/ASF min_pktsize\n"); - init_packetizer(&pb, buf, len); - if (rt->asf_ctx) { - av_close_input_stream(rt->asf_ctx); - rt->asf_ctx = NULL; - } - av_open_input_stream(&rt->asf_ctx, &pb, "", &asf_demuxer, NULL); - rt->asf_pb_pos = url_ftell(&pb); - av_free(buf); - rt->asf_ctx->pb = NULL; - } -} - -static int asfrtp_parse_sdp_line(AVFormatContext *s, int stream_index, - PayloadContext *asf, const char *line) -{ - if (av_strstart(line, "stream:", &line)) { - RTSPState *rt = s->priv_data; - - s->streams[stream_index]->id = strtol(line, NULL, 10); - - if (rt->asf_ctx) { - int i; - - for (i = 0; i < rt->asf_ctx->nb_streams; i++) { - if (s->streams[stream_index]->id == rt->asf_ctx->streams[i]->id) { - *s->streams[stream_index]->codec = - *rt->asf_ctx->streams[i]->codec; - rt->asf_ctx->streams[i]->codec->extradata_size = 0; - rt->asf_ctx->streams[i]->codec->extradata = NULL; - av_set_pts_info(s->streams[stream_index], 32, 1, 1000); - } - } - } - } - - return 0; -} - -struct PayloadContext { - ByteIOContext *pktbuf, pb; - char *buf; -}; - -/** - * @return 0 when a packet was written into /p pkt, and no more data is left; - * 1 when a packet was written into /p pkt, and more packets might be left; - * <0 when not enough data was provided to return a full packet, or on error. - */ -static int asfrtp_parse_packet(AVFormatContext *s, PayloadContext *asf, - AVStream *st, AVPacket *pkt, - uint32_t *timestamp, - const uint8_t *buf, int len, int flags) -{ - ByteIOContext *pb = &asf->pb; - int res, mflags, len_off; - RTSPState *rt = s->priv_data; - - if (!rt->asf_ctx) - return -1; - - if (len > 0) { - int off, out_len; - - if (len < 4) - return -1; - - init_put_byte(pb, buf, len, 0, NULL, NULL, NULL, NULL); - mflags = get_byte(pb); - if (mflags & 0x80) - flags |= RTP_FLAG_KEY; - len_off = get_be24(pb); - if (mflags & 0x20) /**< relative timestamp */ - url_fskip(pb, 4); - if (mflags & 0x10) /**< has duration */ - url_fskip(pb, 4); - if (mflags & 0x8) /**< has location ID */ - url_fskip(pb, 4); - off = url_ftell(pb); - - av_freep(&asf->buf); - if (!(mflags & 0x40)) { - /** - * If 0x40 is not set, the len_off field specifies an offset of this - * packet's payload data in the complete (reassembled) ASF packet. - * This is used to spread one ASF packet over multiple RTP packets. - */ - if (asf->pktbuf && len_off != url_ftell(asf->pktbuf)) { - uint8_t *p; - url_close_dyn_buf(asf->pktbuf, &p); - asf->pktbuf = NULL; - av_free(p); - } - if (!len_off && !asf->pktbuf && - (res = url_open_dyn_buf(&asf->pktbuf)) < 0) - return res; - if (!asf->pktbuf) - return AVERROR(EIO); - - put_buffer(asf->pktbuf, buf + off, len - off); - if (!(flags & RTP_FLAG_MARKER)) - return -1; - out_len = url_close_dyn_buf(asf->pktbuf, &asf->buf); - asf->pktbuf = NULL; - } else { - /** - * If 0x40 is set, the len_off field specifies the length of the - * next ASF packet that can be read from this payload data alone. - * This is commonly the same as the payload size, but could be - * less in case of packet splitting (i.e. multiple ASF packets in - * one RTP packet). - */ - if (len_off != len) { - av_log_missing_feature(s, - "RTSP-MS packet splitting", 1); - return -1; - } - asf->buf = av_malloc(len - off); - out_len = len - off; - memcpy(asf->buf, buf + off, len - off); - } - - init_packetizer(pb, asf->buf, out_len); - pb->pos += rt->asf_pb_pos; - pb->eof_reached = 0; - rt->asf_ctx->pb = pb; - } - - for (;;) { - int i; - - res = av_read_packet(rt->asf_ctx, pkt); - rt->asf_pb_pos = url_ftell(pb); - if (res != 0) - break; - for (i = 0; i < s->nb_streams; i++) { - if (s->streams[i]->id == rt->asf_ctx->streams[pkt->stream_index]->id) { - pkt->stream_index = i; - return 1; // FIXME: return 0 if last packet - } - } - av_free_packet(pkt); - } - - return res == 1 ? -1 : res; -} - -static PayloadContext *asfrtp_new_context(void) -{ - return av_mallocz(sizeof(PayloadContext)); -} - -static void asfrtp_free_context(PayloadContext *asf) -{ - if (asf->pktbuf) { - uint8_t *p = NULL; - url_close_dyn_buf(asf->pktbuf, &p); - asf->pktbuf = NULL; - av_free(p); - } - av_freep(&asf->buf); - av_free(asf); -} - -#define RTP_ASF_HANDLER(n, s, t) \ -RTPDynamicProtocolHandler ff_ms_rtp_ ## n ## _handler = { \ - .enc_name = s, \ - .codec_type = t, \ - .codec_id = CODEC_ID_NONE, \ - .parse_sdp_a_line = asfrtp_parse_sdp_line, \ - .open = asfrtp_new_context, \ - .close = asfrtp_free_context, \ - .parse_packet = asfrtp_parse_packet, \ -}; - -RTP_ASF_HANDLER(asf_pfv, "x-asf-pf", CODEC_TYPE_VIDEO); -RTP_ASF_HANDLER(asf_pfa, "x-asf-pf", CODEC_TYPE_AUDIO);
--- a/rtp_asf.h Sun Feb 28 03:59:15 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/* - * Microsoft RTP/ASF support. - * Copyright (c) 2008 Ronald S. Bultje - * - * 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 - */ - -#ifndef AVFORMAT_RTP_ASF_H -#define AVFORMAT_RTP_ASF_H - -#include "avformat.h" -#include "rtpdec.h" - -/** - * Parse a Windows Media Server-specific SDP line - * - * @param s RTSP demux context - * @param line the SDP line to be parsed - */ -void ff_wms_parse_sdp_a_line(AVFormatContext *s, const char *p); - -/** - * Handlers for the x-asf-pf payloads (the payload ID for RTP/ASF). - * Defined and implemented in rtp_asf.c, registered in rtpdec.c. - */ -extern RTPDynamicProtocolHandler ff_ms_rtp_asf_pfv_handler, - ff_ms_rtp_asf_pfa_handler; - -#endif /* AVFORMAT_RTP_ASF_H */
--- a/rtp_h264.c Sun Feb 28 03:59:15 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,416 +0,0 @@ -/* - * RTP H264 Protocol (RFC3984) - * Copyright (c) 2006 Ryan Martell - * - * 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 - */ - -/** -* @file libavformat/rtp_h264.c - * @brief H.264 / RTP Code (RFC3984) - * @author Ryan Martell <rdm4@martellventures.com> - * - * @note Notes: - * Notes: - * This currently supports packetization mode: - * Single Nal Unit Mode (0), or - * Non-Interleaved Mode (1). It currently does not support - * Interleaved Mode (2). (This requires implementing STAP-B, MTAP16, MTAP24, FU-B packet types) - * - * @note TODO: - * 1) RTCP sender reports for udp streams are required.. - * - */ - -#include "libavutil/base64.h" -#include "libavutil/avstring.h" -#include "libavcodec/get_bits.h" -#include "avformat.h" -#include "mpegts.h" - -#include <unistd.h> -#include "network.h" -#include <assert.h> - -#include "rtpdec.h" -#include "rtp_h264.h" - -/** - RTP/H264 specific private data. -*/ -struct PayloadContext { - unsigned long cookie; ///< sanity check, to make sure we get the pointer we're expecting. - - //sdp setup parameters - uint8_t profile_idc; ///< from the sdp setup parameters. - uint8_t profile_iop; ///< from the sdp setup parameters. - uint8_t level_idc; ///< from the sdp setup parameters. - int packetization_mode; ///< from the sdp setup parameters. -#ifdef DEBUG - int packet_types_received[32]; -#endif -}; - -#define MAGIC_COOKIE (0xdeadbeef) ///< Cookie for the extradata; to verify we are what we think we are, and that we haven't been freed. -#define DEAD_COOKIE (0xdeaddead) ///< Cookie for the extradata; once it is freed. - -/* ---------------- private code */ -static void sdp_parse_fmtp_config_h264(AVStream * stream, - PayloadContext * h264_data, - char *attr, char *value) -{ - AVCodecContext *codec = stream->codec; - assert(codec->codec_id == CODEC_ID_H264); - assert(h264_data != NULL); - - if (!strcmp(attr, "packetization-mode")) { - av_log(codec, AV_LOG_DEBUG, "RTP Packetization Mode: %d\n", atoi(value)); - h264_data->packetization_mode = atoi(value); - /* - Packetization Mode: - 0 or not present: Single NAL mode (Only nals from 1-23 are allowed) - 1: Non-interleaved Mode: 1-23, 24 (STAP-A), 28 (FU-A) are allowed. - 2: Interleaved Mode: 25 (STAP-B), 26 (MTAP16), 27 (MTAP24), 28 (FU-A), and 29 (FU-B) are allowed. - */ - if (h264_data->packetization_mode > 1) - av_log(codec, AV_LOG_ERROR, - "Interleaved RTP mode is not supported yet."); - } else if (!strcmp(attr, "profile-level-id")) { - if (strlen(value) == 6) { - char buffer[3]; - // 6 characters=3 bytes, in hex. - uint8_t profile_idc; - uint8_t profile_iop; - uint8_t level_idc; - - buffer[0] = value[0]; buffer[1] = value[1]; buffer[2] = '\0'; - profile_idc = strtol(buffer, NULL, 16); - buffer[0] = value[2]; buffer[1] = value[3]; - profile_iop = strtol(buffer, NULL, 16); - buffer[0] = value[4]; buffer[1] = value[5]; - level_idc = strtol(buffer, NULL, 16); - - // set the parameters... - av_log(codec, AV_LOG_DEBUG, - "RTP Profile IDC: %x Profile IOP: %x Level: %x\n", - profile_idc, profile_iop, level_idc); - h264_data->profile_idc = profile_idc; - h264_data->profile_iop = profile_iop; - h264_data->level_idc = level_idc; - } - } else if (!strcmp(attr, "sprop-parameter-sets")) { - uint8_t start_sequence[]= { 0, 0, 1 }; - codec->extradata_size= 0; - codec->extradata= NULL; - - while (*value) { - char base64packet[1024]; - uint8_t decoded_packet[1024]; - uint32_t packet_size; - char *dst = base64packet; - - while (*value && *value != ',' - && (dst - base64packet) < sizeof(base64packet) - 1) { - *dst++ = *value++; - } - *dst++ = '\0'; - - if (*value == ',') - value++; - - packet_size= av_base64_decode(decoded_packet, base64packet, sizeof(decoded_packet)); - if (packet_size) { - uint8_t *dest= av_malloc(packet_size+sizeof(start_sequence)+codec->extradata_size); - if(dest) - { - if(codec->extradata_size) - { - // av_realloc? - memcpy(dest, codec->extradata, codec->extradata_size); - av_free(codec->extradata); - } - - memcpy(dest+codec->extradata_size, start_sequence, sizeof(start_sequence)); - memcpy(dest+codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size); - - codec->extradata= dest; - codec->extradata_size+= sizeof(start_sequence)+packet_size; - } else { - av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!"); - } - } - } - av_log(codec, AV_LOG_DEBUG, "Extradata set to %p (size: %d)!", codec->extradata, codec->extradata_size); - } -} - -// return 0 on packet, no more left, 1 on packet, 1 on partial packet... -static int h264_handle_packet(AVFormatContext *ctx, - PayloadContext *data, - AVStream *st, - AVPacket * pkt, - uint32_t * timestamp, - const uint8_t * buf, - int len, int flags) -{ - uint8_t nal = buf[0]; - uint8_t type = (nal & 0x1f); - int result= 0; - uint8_t start_sequence[]= {0, 0, 1}; - -#ifdef DEBUG - assert(data); - assert(data->cookie == MAGIC_COOKIE); -#endif - assert(buf); - - if (type >= 1 && type <= 23) - type = 1; // simplify the case. (these are all the nal types used internally by the h264 codec) - switch (type) { - case 0: // undefined; - result= -1; - break; - - case 1: - av_new_packet(pkt, len+sizeof(start_sequence)); - memcpy(pkt->data, start_sequence, sizeof(start_sequence)); - memcpy(pkt->data+sizeof(start_sequence), buf, len); -#ifdef DEBUG - data->packet_types_received[nal & 0x1f]++; -#endif - break; - - case 24: // STAP-A (one packet, multiple nals) - // consume the STAP-A NAL - buf++; - len--; - // first we are going to figure out the total size.... - { - int pass= 0; - int total_length= 0; - uint8_t *dst= NULL; - - for(pass= 0; pass<2; pass++) { - const uint8_t *src= buf; - int src_len= len; - - do { - uint16_t nal_size = AV_RB16(src); // this going to be a problem if unaligned (can it be?) - - // consume the length of the aggregate... - src += 2; - src_len -= 2; - - if (nal_size <= src_len) { - if(pass==0) { - // counting... - total_length+= sizeof(start_sequence)+nal_size; - } else { - // copying - assert(dst); - memcpy(dst, start_sequence, sizeof(start_sequence)); - dst+= sizeof(start_sequence); - memcpy(dst, src, nal_size); -#ifdef DEBUG - data->packet_types_received[*src & 0x1f]++; -#endif - dst+= nal_size; - } - } else { - av_log(ctx, AV_LOG_ERROR, - "nal size exceeds length: %d %d\n", nal_size, src_len); - } - - // eat what we handled... - src += nal_size; - src_len -= nal_size; - - if (src_len < 0) - av_log(ctx, AV_LOG_ERROR, - "Consumed more bytes than we got! (%d)\n", src_len); - } while (src_len > 2); // because there could be rtp padding.. - - if(pass==0) { - // now we know the total size of the packet (with the start sequences added) - av_new_packet(pkt, total_length); - dst= pkt->data; - } else { - assert(dst-pkt->data==total_length); - } - } - } - break; - - case 25: // STAP-B - case 26: // MTAP-16 - case 27: // MTAP-24 - case 29: // FU-B - av_log(ctx, AV_LOG_ERROR, - "Unhandled type (%d) (See RFC for implementation details\n", - type); - result= -1; - break; - - case 28: // FU-A (fragmented nal) - buf++; - len--; // skip the fu_indicator - { - // these are the same as above, we just redo them here for clarity... - uint8_t fu_indicator = nal; - uint8_t fu_header = *buf; // read the fu_header. - uint8_t start_bit = fu_header >> 7; -// uint8_t end_bit = (fu_header & 0x40) >> 6; - uint8_t nal_type = (fu_header & 0x1f); - uint8_t reconstructed_nal; - - // reconstruct this packet's true nal; only the data follows.. - reconstructed_nal = fu_indicator & (0xe0); // the original nal forbidden bit and NRI are stored in this packet's nal; - reconstructed_nal |= nal_type; - - // skip the fu_header... - buf++; - len--; - -#ifdef DEBUG - if (start_bit) - data->packet_types_received[nal_type]++; -#endif - if(start_bit) { - // copy in the start sequence, and the reconstructed nal.... - av_new_packet(pkt, sizeof(start_sequence)+sizeof(nal)+len); - memcpy(pkt->data, start_sequence, sizeof(start_sequence)); - pkt->data[sizeof(start_sequence)]= reconstructed_nal; - memcpy(pkt->data+sizeof(start_sequence)+sizeof(nal), buf, len); - } else { - av_new_packet(pkt, len); - memcpy(pkt->data, buf, len); - } - } - break; - - case 30: // undefined - case 31: // undefined - default: - av_log(ctx, AV_LOG_ERROR, "Undefined type (%d)", type); - result= -1; - break; - } - - pkt->stream_index = st->index; - - return result; -} - -/* ---------------- public code */ -static PayloadContext *h264_new_context(void) -{ - PayloadContext *data = - av_mallocz(sizeof(PayloadContext) + - FF_INPUT_BUFFER_PADDING_SIZE); - - if (data) { - data->cookie = MAGIC_COOKIE; - } - - return data; -} - -static void h264_free_context(PayloadContext *data) -{ -#ifdef DEBUG - int ii; - - for (ii = 0; ii < 32; ii++) { - if (data->packet_types_received[ii]) - av_log(NULL, AV_LOG_DEBUG, "Received %d packets of type %d\n", - data->packet_types_received[ii], ii); - } -#endif - - assert(data); - assert(data->cookie == MAGIC_COOKIE); - - // avoid stale pointers (assert) - data->cookie = DEAD_COOKIE; - - // and clear out this... - av_free(data); -} - -static int parse_h264_sdp_line(AVFormatContext *s, int st_index, - PayloadContext *h264_data, const char *line) -{ - AVStream *stream = s->streams[st_index]; - AVCodecContext *codec = stream->codec; - const char *p = line; - - assert(h264_data->cookie == MAGIC_COOKIE); - - if (av_strstart(p, "framesize:", &p)) { - char buf1[50]; - char *dst = buf1; - - // remove the protocol identifier.. - while (*p && *p == ' ') p++; // strip spaces. - while (*p && *p != ' ') p++; // eat protocol identifier - while (*p && *p == ' ') p++; // strip trailing spaces. - while (*p && *p != '-' && (dst - buf1) < sizeof(buf1) - 1) { - *dst++ = *p++; - } - *dst = '\0'; - - // a='framesize:96 320-240' - // set our parameters.. - codec->width = atoi(buf1); - codec->height = atoi(p + 1); // skip the - - codec->pix_fmt = PIX_FMT_YUV420P; - } else if (av_strstart(p, "fmtp:", &p)) { - char attr[256]; - char value[4096]; - - // remove the protocol identifier.. - while (*p && *p == ' ') p++; // strip spaces. - while (*p && *p != ' ') p++; // eat protocol identifier - while (*p && *p == ' ') p++; // strip trailing spaces. - - /* loop on each attribute */ - while (ff_rtsp_next_attr_and_value - (&p, attr, sizeof(attr), value, sizeof(value))) { - /* grab the codec extra_data from the config parameter of the fmtp line */ - sdp_parse_fmtp_config_h264(stream, h264_data, attr, value); - } - } else if (av_strstart(p, "cliprect:", &p)) { - // could use this if we wanted. - } - - av_set_pts_info(stream, 33, 1, 90000); // 33 should be right, because the pts is 64 bit? (done elsewhere; this is a one time thing) - - return 0; // keep processing it the normal way... -} - -/** -This is the structure for expanding on the dynamic rtp protocols (makes everything static. yay!) -*/ -RTPDynamicProtocolHandler ff_h264_dynamic_handler = { - .enc_name = "H264", - .codec_type = CODEC_TYPE_VIDEO, - .codec_id = CODEC_ID_H264, - .parse_sdp_a_line = parse_h264_sdp_line, - .open = h264_new_context, - .close = h264_free_context, - .parse_packet = h264_handle_packet -};
--- a/rtp_h264.h Sun Feb 28 03:59:15 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -/* - * RTP H264 Protocol (RFC3984) - * Copyright (c) 2006 Ryan Martell - * - * 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 - */ - -#ifndef AVFORMAT_RTP_H264_H -#define AVFORMAT_RTP_H264_H - -#include "rtpdec.h" - -extern RTPDynamicProtocolHandler ff_h264_dynamic_handler; - -#endif /* AVFORMAT_RTP_H264_H */
--- a/rtp_vorbis.c Sun Feb 28 03:59:15 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,218 +0,0 @@ -/* - * RTP Vorbis Protocol (RFC5215) - * Copyright (c) 2009 Colin McQuillan - * - * 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 - */ - -/** - * @file libavformat/rtp_vorbis.c - * @brief Vorbis / RTP Code (RFC 5215) - * @author Colin McQuillan <m.niloc@gmail.com> - */ - -#include "libavutil/base64.h" -#include "libavutil/avstring.h" -#include "libavcodec/bytestream.h" - -#include <assert.h> - -#include "rtpdec.h" -#include "rtp_vorbis.h" - -/** - * RTP/Vorbis specific private data. - */ -struct PayloadContext { - unsigned ident; ///< 24-bit stream configuration identifier -}; - -/** - * Length encoding described in RFC5215 section 3.1.1. - */ -static int get_base128(const uint8_t ** buf, const uint8_t * buf_end) -{ - int n = 0; - for (; *buf < buf_end; ++*buf) { - n <<= 7; - n += **buf & 0x7f; - if (!(**buf & 0x80)) { - ++*buf; - return n; - } - } - return 0; -} - -/** - * Out-of-band headers, described in RFC 5251 section 3.2.1 - */ -static unsigned int -parse_packed_headers(const uint8_t * packed_headers, - const uint8_t * packed_headers_end, - AVCodecContext * codec, PayloadContext * vorbis_data) -{ - unsigned num_packed, num_headers, length, length1, length2; - uint8_t *ptr; - - num_packed = bytestream_get_be32(&packed_headers); - vorbis_data->ident = bytestream_get_be24(&packed_headers); - length = bytestream_get_be16(&packed_headers); - num_headers = get_base128(&packed_headers, packed_headers_end); - length1 = get_base128(&packed_headers, packed_headers_end); - length2 = get_base128(&packed_headers, packed_headers_end); - - if (num_packed != 1 || num_headers > 3) { - av_log(codec, AV_LOG_ERROR, - "Unimplemented number of headers: %d packed headers, %d headers\n", - num_packed, num_headers); - return AVERROR_PATCHWELCOME; - } - - if (packed_headers_end - packed_headers != length || - length1 > length || length2 > length - length1) { - av_log(codec, AV_LOG_ERROR, - "Bad packed header lengths (%d,%d,%d,%d)\n", length1, - length2, packed_headers_end - packed_headers, length); - return AVERROR_INVALIDDATA; - } - - ptr = codec->extradata = av_mallocz(length + length / 255 + 64); - if (!ptr) { - av_log(codec, AV_LOG_ERROR, "Out of memory"); - return AVERROR_NOMEM; - } - *ptr++ = 2; - ptr += av_xiphlacing(ptr, length1); - ptr += av_xiphlacing(ptr, length2); - memcpy(ptr, packed_headers, length); - ptr += length; - codec->extradata_size = ptr - codec->extradata; - - return 0; -} - -int -ff_vorbis_parse_fmtp_config(AVCodecContext * codec, - void *vorbis_data, char *attr, char *value) -{ - int result = 0; - assert(codec->codec_id == CODEC_ID_VORBIS); - assert(vorbis_data); - - // The configuration value is a base64 encoded packed header - if (!strcmp(attr, "configuration")) { - uint8_t *decoded_packet = NULL; - int packet_size; - size_t decoded_alloc = strlen(value) / 4 * 3 + 4; - - if (decoded_alloc <= INT_MAX) { - decoded_packet = av_malloc(decoded_alloc); - if (decoded_packet) { - packet_size = - av_base64_decode(decoded_packet, value, decoded_alloc); - - result = parse_packed_headers - (decoded_packet, decoded_packet + packet_size, codec, - vorbis_data); - } else { - av_log(codec, AV_LOG_ERROR, - "Out of memory while decoding SDP configuration.\n"); - result = AVERROR_NOMEM; - } - } else { - av_log(codec, AV_LOG_ERROR, "Packet too large\n"); - result = AVERROR_INVALIDDATA; - } - av_free(decoded_packet); - } - return result; -} - -static PayloadContext *vorbis_new_context(void) -{ - return av_mallocz(sizeof(PayloadContext)); -} - -static void vorbis_free_context(PayloadContext * data) -{ - av_free(data); -} - -/** - * Handle payload as described in RFC 5215 section 2.2 - */ -static int -vorbis_handle_packet(AVFormatContext * ctx, - PayloadContext * data, - AVStream * st, - AVPacket * pkt, - uint32_t * timestamp, - const uint8_t * buf, int len, int flags) -{ - int ident, fragmented, vdt, num_pkts, pkt_len; - - if (len < 6) { - av_log(ctx, AV_LOG_ERROR, "Invalid %d byte packet\n", len); - return AVERROR_INVALIDDATA; - } - - ident = AV_RB24(buf); - fragmented = buf[3] >> 6; - vdt = (buf[3] >> 4) & 3; - num_pkts = buf[3] & 7; - pkt_len = AV_RB16(buf + 4); - - if (pkt_len > len - 6) { - av_log(ctx, AV_LOG_ERROR, - "Invalid packet length %d in %d byte packet\n", pkt_len, - len); - return AVERROR_INVALIDDATA; - } - - if (ident != data->ident) { - av_log(ctx, AV_LOG_ERROR, - "Unimplemented Vorbis SDP configuration change detected\n"); - return AVERROR_PATCHWELCOME; - } - - if (fragmented != 0 || vdt != 0 || num_pkts != 1) { - av_log(ctx, AV_LOG_ERROR, - "Unimplemented RTP Vorbis packet settings (%d,%d,%d)\n", - fragmented, vdt, num_pkts); - return AVERROR_PATCHWELCOME; - } - - if (av_new_packet(pkt, pkt_len)) { - av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); - return AVERROR_NOMEM; - } - - memcpy(pkt->data, buf + 6, pkt_len); - pkt->stream_index = st->index; - return 0; -} - -RTPDynamicProtocolHandler ff_vorbis_dynamic_handler = { - .enc_name = "vorbis", - .codec_type = CODEC_TYPE_AUDIO, - .codec_id = CODEC_ID_VORBIS, - .parse_sdp_a_line = NULL, - .open = vorbis_new_context, - .close = vorbis_free_context, - .parse_packet = vorbis_handle_packet -};
--- a/rtp_vorbis.h Sun Feb 28 03:59:15 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -/* - * RTP Vorbis Protocol (RFC 5215) - * Copyright (c) 2009 Colin McQuillan - * - * 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 - */ - -#ifndef AVFORMAT_RTP_VORBIS_H -#define AVFORMAT_RTP_VORBIS_H - -#include "libavcodec/avcodec.h" -#include "rtpdec.h" - -/** - * Handle a Vorbis-specific FMTP parameter - * - * @param codec The context of the codec - * @param ctx Private Vorbis RTP context - * @param attr Format-specific parameter name - * @param value Format-specific paremeter value - */ -int -ff_vorbis_parse_fmtp_config(AVCodecContext * codec, - void *ctx, char *attr, char *value); - -/** - * Vorbis RTP callbacks. - */ -extern RTPDynamicProtocolHandler ff_vorbis_dynamic_handler; - -#endif /* AVFORMAT_RTP_VORBIS_H */
--- a/rtpdec.c Sun Feb 28 03:59:15 2010 +0000 +++ b/rtpdec.c Sun Feb 28 11:03:14 2010 +0000 @@ -30,11 +30,11 @@ #include "network.h" #include "rtpdec.h" -#include "rtp_asf.h" -#include "rtp_h264.h" -#include "rtp_vorbis.h" #include "rtpdec_amr.h" +#include "rtpdec_asf.h" #include "rtpdec_h263.h" +#include "rtpdec_h264.h" +#include "rtpdec_vorbis.h" //#define DEBUG
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtpdec_asf.c Sun Feb 28 11:03:14 2010 +0000 @@ -0,0 +1,281 @@ +/* + * Microsoft RTP/ASF support. + * Copyright (c) 2008 Ronald S. Bultje + * + * 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 + */ + +/** + * @file libavformat/rtpdec_asf.c + * @brief Microsoft RTP/ASF support + * @author Ronald S. Bultje <rbultje@ronald.bitfreak.net> + */ + +#include <libavutil/base64.h> +#include <libavutil/avstring.h> +#include <libavutil/intreadwrite.h> +#include "rtp.h" +#include "rtpdec_asf.h" +#include "rtsp.h" +#include "asf.h" + +/** + * From MSDN 2.2.1.4, we learn that ASF data packets over RTP should not + * contain any padding. Unfortunately, the header min/max_pktsize are not + * updated (thus making min_pktsize invalid). Here, we "fix" these faulty + * min_pktsize values in the ASF file header. + * @return 0 on success, <0 on failure (currently -1). + */ +static int rtp_asf_fix_header(uint8_t *buf, int len) +{ + uint8_t *p = buf, *end = buf + len; + + if (len < sizeof(ff_asf_guid) * 2 + 22 || + memcmp(p, ff_asf_header, sizeof(ff_asf_guid))) { + return -1; + } + p += sizeof(ff_asf_guid) + 14; + do { + uint64_t chunksize = AV_RL64(p + sizeof(ff_asf_guid)); + if (memcmp(p, ff_asf_file_header, sizeof(ff_asf_guid))) { + if (chunksize > end - p) + return -1; + p += chunksize; + continue; + } + + /* skip most of the file header, to min_pktsize */ + p += 6 * 8 + 3 * 4 + sizeof(ff_asf_guid) * 2; + if (p + 8 <= end && AV_RL32(p) == AV_RL32(p + 4)) { + /* and set that to zero */ + AV_WL32(p, 0); + return 0; + } + break; + } while (end - p >= sizeof(ff_asf_guid) + 8); + + return -1; +} + +/** + * The following code is basically a buffered ByteIOContext, + * with the added benefit of returning -EAGAIN (instead of 0) + * on packet boundaries, such that the ASF demuxer can return + * safely and resume business at the next packet. + */ +static int packetizer_read(void *opaque, uint8_t *buf, int buf_size) +{ + return AVERROR(EAGAIN); +} + +static void init_packetizer(ByteIOContext *pb, uint8_t *buf, int len) +{ + init_put_byte(pb, buf, len, 0, NULL, packetizer_read, NULL, NULL); + + /* this "fills" the buffer with its current content */ + pb->pos = len; + pb->buf_end = buf + len; +} + +void ff_wms_parse_sdp_a_line(AVFormatContext *s, const char *p) +{ + if (av_strstart(p, "pgmpu:data:application/vnd.ms.wms-hdr.asfv1;base64,", &p)) { + ByteIOContext pb; + RTSPState *rt = s->priv_data; + int len = strlen(p) * 6 / 8; + char *buf = av_mallocz(len); + av_base64_decode(buf, p, len); + + if (rtp_asf_fix_header(buf, len) < 0) + av_log(s, AV_LOG_ERROR, + "Failed to fix invalid RTSP-MS/ASF min_pktsize\n"); + init_packetizer(&pb, buf, len); + if (rt->asf_ctx) { + av_close_input_stream(rt->asf_ctx); + rt->asf_ctx = NULL; + } + av_open_input_stream(&rt->asf_ctx, &pb, "", &asf_demuxer, NULL); + rt->asf_pb_pos = url_ftell(&pb); + av_free(buf); + rt->asf_ctx->pb = NULL; + } +} + +static int asfrtp_parse_sdp_line(AVFormatContext *s, int stream_index, + PayloadContext *asf, const char *line) +{ + if (av_strstart(line, "stream:", &line)) { + RTSPState *rt = s->priv_data; + + s->streams[stream_index]->id = strtol(line, NULL, 10); + + if (rt->asf_ctx) { + int i; + + for (i = 0; i < rt->asf_ctx->nb_streams; i++) { + if (s->streams[stream_index]->id == rt->asf_ctx->streams[i]->id) { + *s->streams[stream_index]->codec = + *rt->asf_ctx->streams[i]->codec; + rt->asf_ctx->streams[i]->codec->extradata_size = 0; + rt->asf_ctx->streams[i]->codec->extradata = NULL; + av_set_pts_info(s->streams[stream_index], 32, 1, 1000); + } + } + } + } + + return 0; +} + +struct PayloadContext { + ByteIOContext *pktbuf, pb; + char *buf; +}; + +/** + * @return 0 when a packet was written into /p pkt, and no more data is left; + * 1 when a packet was written into /p pkt, and more packets might be left; + * <0 when not enough data was provided to return a full packet, or on error. + */ +static int asfrtp_parse_packet(AVFormatContext *s, PayloadContext *asf, + AVStream *st, AVPacket *pkt, + uint32_t *timestamp, + const uint8_t *buf, int len, int flags) +{ + ByteIOContext *pb = &asf->pb; + int res, mflags, len_off; + RTSPState *rt = s->priv_data; + + if (!rt->asf_ctx) + return -1; + + if (len > 0) { + int off, out_len; + + if (len < 4) + return -1; + + init_put_byte(pb, buf, len, 0, NULL, NULL, NULL, NULL); + mflags = get_byte(pb); + if (mflags & 0x80) + flags |= RTP_FLAG_KEY; + len_off = get_be24(pb); + if (mflags & 0x20) /**< relative timestamp */ + url_fskip(pb, 4); + if (mflags & 0x10) /**< has duration */ + url_fskip(pb, 4); + if (mflags & 0x8) /**< has location ID */ + url_fskip(pb, 4); + off = url_ftell(pb); + + av_freep(&asf->buf); + if (!(mflags & 0x40)) { + /** + * If 0x40 is not set, the len_off field specifies an offset of this + * packet's payload data in the complete (reassembled) ASF packet. + * This is used to spread one ASF packet over multiple RTP packets. + */ + if (asf->pktbuf && len_off != url_ftell(asf->pktbuf)) { + uint8_t *p; + url_close_dyn_buf(asf->pktbuf, &p); + asf->pktbuf = NULL; + av_free(p); + } + if (!len_off && !asf->pktbuf && + (res = url_open_dyn_buf(&asf->pktbuf)) < 0) + return res; + if (!asf->pktbuf) + return AVERROR(EIO); + + put_buffer(asf->pktbuf, buf + off, len - off); + if (!(flags & RTP_FLAG_MARKER)) + return -1; + out_len = url_close_dyn_buf(asf->pktbuf, &asf->buf); + asf->pktbuf = NULL; + } else { + /** + * If 0x40 is set, the len_off field specifies the length of the + * next ASF packet that can be read from this payload data alone. + * This is commonly the same as the payload size, but could be + * less in case of packet splitting (i.e. multiple ASF packets in + * one RTP packet). + */ + if (len_off != len) { + av_log_missing_feature(s, + "RTSP-MS packet splitting", 1); + return -1; + } + asf->buf = av_malloc(len - off); + out_len = len - off; + memcpy(asf->buf, buf + off, len - off); + } + + init_packetizer(pb, asf->buf, out_len); + pb->pos += rt->asf_pb_pos; + pb->eof_reached = 0; + rt->asf_ctx->pb = pb; + } + + for (;;) { + int i; + + res = av_read_packet(rt->asf_ctx, pkt); + rt->asf_pb_pos = url_ftell(pb); + if (res != 0) + break; + for (i = 0; i < s->nb_streams; i++) { + if (s->streams[i]->id == rt->asf_ctx->streams[pkt->stream_index]->id) { + pkt->stream_index = i; + return 1; // FIXME: return 0 if last packet + } + } + av_free_packet(pkt); + } + + return res == 1 ? -1 : res; +} + +static PayloadContext *asfrtp_new_context(void) +{ + return av_mallocz(sizeof(PayloadContext)); +} + +static void asfrtp_free_context(PayloadContext *asf) +{ + if (asf->pktbuf) { + uint8_t *p = NULL; + url_close_dyn_buf(asf->pktbuf, &p); + asf->pktbuf = NULL; + av_free(p); + } + av_freep(&asf->buf); + av_free(asf); +} + +#define RTP_ASF_HANDLER(n, s, t) \ +RTPDynamicProtocolHandler ff_ms_rtp_ ## n ## _handler = { \ + .enc_name = s, \ + .codec_type = t, \ + .codec_id = CODEC_ID_NONE, \ + .parse_sdp_a_line = asfrtp_parse_sdp_line, \ + .open = asfrtp_new_context, \ + .close = asfrtp_free_context, \ + .parse_packet = asfrtp_parse_packet, \ +}; + +RTP_ASF_HANDLER(asf_pfv, "x-asf-pf", CODEC_TYPE_VIDEO); +RTP_ASF_HANDLER(asf_pfa, "x-asf-pf", CODEC_TYPE_AUDIO);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtpdec_asf.h Sun Feb 28 11:03:14 2010 +0000 @@ -0,0 +1,43 @@ +/* + * Microsoft RTP/ASF support. + * Copyright (c) 2008 Ronald S. Bultje + * + * 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 + */ + +#ifndef AVFORMAT_RTPDEC_ASF_H +#define AVFORMAT_RTPDEC_ASF_H + +#include "avformat.h" +#include "rtpdec.h" + +/** + * Parse a Windows Media Server-specific SDP line + * + * @param s RTSP demux context + * @param line the SDP line to be parsed + */ +void ff_wms_parse_sdp_a_line(AVFormatContext *s, const char *p); + +/** + * Handlers for the x-asf-pf payloads (the payload ID for RTP/ASF). + * Defined and implemented in rtp_asf.c, registered in rtpdec.c. + */ +extern RTPDynamicProtocolHandler ff_ms_rtp_asf_pfv_handler, + ff_ms_rtp_asf_pfa_handler; + +#endif /* AVFORMAT_RTPDEC_ASF_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtpdec_h264.c Sun Feb 28 11:03:14 2010 +0000 @@ -0,0 +1,416 @@ +/* + * RTP H264 Protocol (RFC3984) + * Copyright (c) 2006 Ryan Martell + * + * 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 + */ + +/** +* @file libavformat/rtpdec_h264.c + * @brief H.264 / RTP Code (RFC3984) + * @author Ryan Martell <rdm4@martellventures.com> + * + * @note Notes: + * Notes: + * This currently supports packetization mode: + * Single Nal Unit Mode (0), or + * Non-Interleaved Mode (1). It currently does not support + * Interleaved Mode (2). (This requires implementing STAP-B, MTAP16, MTAP24, FU-B packet types) + * + * @note TODO: + * 1) RTCP sender reports for udp streams are required.. + * + */ + +#include "libavutil/base64.h" +#include "libavutil/avstring.h" +#include "libavcodec/get_bits.h" +#include "avformat.h" +#include "mpegts.h" + +#include <unistd.h> +#include "network.h" +#include <assert.h> + +#include "rtpdec.h" +#include "rtpdec_h264.h" + +/** + RTP/H264 specific private data. +*/ +struct PayloadContext { + unsigned long cookie; ///< sanity check, to make sure we get the pointer we're expecting. + + //sdp setup parameters + uint8_t profile_idc; ///< from the sdp setup parameters. + uint8_t profile_iop; ///< from the sdp setup parameters. + uint8_t level_idc; ///< from the sdp setup parameters. + int packetization_mode; ///< from the sdp setup parameters. +#ifdef DEBUG + int packet_types_received[32]; +#endif +}; + +#define MAGIC_COOKIE (0xdeadbeef) ///< Cookie for the extradata; to verify we are what we think we are, and that we haven't been freed. +#define DEAD_COOKIE (0xdeaddead) ///< Cookie for the extradata; once it is freed. + +/* ---------------- private code */ +static void sdp_parse_fmtp_config_h264(AVStream * stream, + PayloadContext * h264_data, + char *attr, char *value) +{ + AVCodecContext *codec = stream->codec; + assert(codec->codec_id == CODEC_ID_H264); + assert(h264_data != NULL); + + if (!strcmp(attr, "packetization-mode")) { + av_log(codec, AV_LOG_DEBUG, "RTP Packetization Mode: %d\n", atoi(value)); + h264_data->packetization_mode = atoi(value); + /* + Packetization Mode: + 0 or not present: Single NAL mode (Only nals from 1-23 are allowed) + 1: Non-interleaved Mode: 1-23, 24 (STAP-A), 28 (FU-A) are allowed. + 2: Interleaved Mode: 25 (STAP-B), 26 (MTAP16), 27 (MTAP24), 28 (FU-A), and 29 (FU-B) are allowed. + */ + if (h264_data->packetization_mode > 1) + av_log(codec, AV_LOG_ERROR, + "Interleaved RTP mode is not supported yet."); + } else if (!strcmp(attr, "profile-level-id")) { + if (strlen(value) == 6) { + char buffer[3]; + // 6 characters=3 bytes, in hex. + uint8_t profile_idc; + uint8_t profile_iop; + uint8_t level_idc; + + buffer[0] = value[0]; buffer[1] = value[1]; buffer[2] = '\0'; + profile_idc = strtol(buffer, NULL, 16); + buffer[0] = value[2]; buffer[1] = value[3]; + profile_iop = strtol(buffer, NULL, 16); + buffer[0] = value[4]; buffer[1] = value[5]; + level_idc = strtol(buffer, NULL, 16); + + // set the parameters... + av_log(codec, AV_LOG_DEBUG, + "RTP Profile IDC: %x Profile IOP: %x Level: %x\n", + profile_idc, profile_iop, level_idc); + h264_data->profile_idc = profile_idc; + h264_data->profile_iop = profile_iop; + h264_data->level_idc = level_idc; + } + } else if (!strcmp(attr, "sprop-parameter-sets")) { + uint8_t start_sequence[]= { 0, 0, 1 }; + codec->extradata_size= 0; + codec->extradata= NULL; + + while (*value) { + char base64packet[1024]; + uint8_t decoded_packet[1024]; + uint32_t packet_size; + char *dst = base64packet; + + while (*value && *value != ',' + && (dst - base64packet) < sizeof(base64packet) - 1) { + *dst++ = *value++; + } + *dst++ = '\0'; + + if (*value == ',') + value++; + + packet_size= av_base64_decode(decoded_packet, base64packet, sizeof(decoded_packet)); + if (packet_size) { + uint8_t *dest= av_malloc(packet_size+sizeof(start_sequence)+codec->extradata_size); + if(dest) + { + if(codec->extradata_size) + { + // av_realloc? + memcpy(dest, codec->extradata, codec->extradata_size); + av_free(codec->extradata); + } + + memcpy(dest+codec->extradata_size, start_sequence, sizeof(start_sequence)); + memcpy(dest+codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size); + + codec->extradata= dest; + codec->extradata_size+= sizeof(start_sequence)+packet_size; + } else { + av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!"); + } + } + } + av_log(codec, AV_LOG_DEBUG, "Extradata set to %p (size: %d)!", codec->extradata, codec->extradata_size); + } +} + +// return 0 on packet, no more left, 1 on packet, 1 on partial packet... +static int h264_handle_packet(AVFormatContext *ctx, + PayloadContext *data, + AVStream *st, + AVPacket * pkt, + uint32_t * timestamp, + const uint8_t * buf, + int len, int flags) +{ + uint8_t nal = buf[0]; + uint8_t type = (nal & 0x1f); + int result= 0; + uint8_t start_sequence[]= {0, 0, 1}; + +#ifdef DEBUG + assert(data); + assert(data->cookie == MAGIC_COOKIE); +#endif + assert(buf); + + if (type >= 1 && type <= 23) + type = 1; // simplify the case. (these are all the nal types used internally by the h264 codec) + switch (type) { + case 0: // undefined; + result= -1; + break; + + case 1: + av_new_packet(pkt, len+sizeof(start_sequence)); + memcpy(pkt->data, start_sequence, sizeof(start_sequence)); + memcpy(pkt->data+sizeof(start_sequence), buf, len); +#ifdef DEBUG + data->packet_types_received[nal & 0x1f]++; +#endif + break; + + case 24: // STAP-A (one packet, multiple nals) + // consume the STAP-A NAL + buf++; + len--; + // first we are going to figure out the total size.... + { + int pass= 0; + int total_length= 0; + uint8_t *dst= NULL; + + for(pass= 0; pass<2; pass++) { + const uint8_t *src= buf; + int src_len= len; + + do { + uint16_t nal_size = AV_RB16(src); // this going to be a problem if unaligned (can it be?) + + // consume the length of the aggregate... + src += 2; + src_len -= 2; + + if (nal_size <= src_len) { + if(pass==0) { + // counting... + total_length+= sizeof(start_sequence)+nal_size; + } else { + // copying + assert(dst); + memcpy(dst, start_sequence, sizeof(start_sequence)); + dst+= sizeof(start_sequence); + memcpy(dst, src, nal_size); +#ifdef DEBUG + data->packet_types_received[*src & 0x1f]++; +#endif + dst+= nal_size; + } + } else { + av_log(ctx, AV_LOG_ERROR, + "nal size exceeds length: %d %d\n", nal_size, src_len); + } + + // eat what we handled... + src += nal_size; + src_len -= nal_size; + + if (src_len < 0) + av_log(ctx, AV_LOG_ERROR, + "Consumed more bytes than we got! (%d)\n", src_len); + } while (src_len > 2); // because there could be rtp padding.. + + if(pass==0) { + // now we know the total size of the packet (with the start sequences added) + av_new_packet(pkt, total_length); + dst= pkt->data; + } else { + assert(dst-pkt->data==total_length); + } + } + } + break; + + case 25: // STAP-B + case 26: // MTAP-16 + case 27: // MTAP-24 + case 29: // FU-B + av_log(ctx, AV_LOG_ERROR, + "Unhandled type (%d) (See RFC for implementation details\n", + type); + result= -1; + break; + + case 28: // FU-A (fragmented nal) + buf++; + len--; // skip the fu_indicator + { + // these are the same as above, we just redo them here for clarity... + uint8_t fu_indicator = nal; + uint8_t fu_header = *buf; // read the fu_header. + uint8_t start_bit = fu_header >> 7; +// uint8_t end_bit = (fu_header & 0x40) >> 6; + uint8_t nal_type = (fu_header & 0x1f); + uint8_t reconstructed_nal; + + // reconstruct this packet's true nal; only the data follows.. + reconstructed_nal = fu_indicator & (0xe0); // the original nal forbidden bit and NRI are stored in this packet's nal; + reconstructed_nal |= nal_type; + + // skip the fu_header... + buf++; + len--; + +#ifdef DEBUG + if (start_bit) + data->packet_types_received[nal_type]++; +#endif + if(start_bit) { + // copy in the start sequence, and the reconstructed nal.... + av_new_packet(pkt, sizeof(start_sequence)+sizeof(nal)+len); + memcpy(pkt->data, start_sequence, sizeof(start_sequence)); + pkt->data[sizeof(start_sequence)]= reconstructed_nal; + memcpy(pkt->data+sizeof(start_sequence)+sizeof(nal), buf, len); + } else { + av_new_packet(pkt, len); + memcpy(pkt->data, buf, len); + } + } + break; + + case 30: // undefined + case 31: // undefined + default: + av_log(ctx, AV_LOG_ERROR, "Undefined type (%d)", type); + result= -1; + break; + } + + pkt->stream_index = st->index; + + return result; +} + +/* ---------------- public code */ +static PayloadContext *h264_new_context(void) +{ + PayloadContext *data = + av_mallocz(sizeof(PayloadContext) + + FF_INPUT_BUFFER_PADDING_SIZE); + + if (data) { + data->cookie = MAGIC_COOKIE; + } + + return data; +} + +static void h264_free_context(PayloadContext *data) +{ +#ifdef DEBUG + int ii; + + for (ii = 0; ii < 32; ii++) { + if (data->packet_types_received[ii]) + av_log(NULL, AV_LOG_DEBUG, "Received %d packets of type %d\n", + data->packet_types_received[ii], ii); + } +#endif + + assert(data); + assert(data->cookie == MAGIC_COOKIE); + + // avoid stale pointers (assert) + data->cookie = DEAD_COOKIE; + + // and clear out this... + av_free(data); +} + +static int parse_h264_sdp_line(AVFormatContext *s, int st_index, + PayloadContext *h264_data, const char *line) +{ + AVStream *stream = s->streams[st_index]; + AVCodecContext *codec = stream->codec; + const char *p = line; + + assert(h264_data->cookie == MAGIC_COOKIE); + + if (av_strstart(p, "framesize:", &p)) { + char buf1[50]; + char *dst = buf1; + + // remove the protocol identifier.. + while (*p && *p == ' ') p++; // strip spaces. + while (*p && *p != ' ') p++; // eat protocol identifier + while (*p && *p == ' ') p++; // strip trailing spaces. + while (*p && *p != '-' && (dst - buf1) < sizeof(buf1) - 1) { + *dst++ = *p++; + } + *dst = '\0'; + + // a='framesize:96 320-240' + // set our parameters.. + codec->width = atoi(buf1); + codec->height = atoi(p + 1); // skip the - + codec->pix_fmt = PIX_FMT_YUV420P; + } else if (av_strstart(p, "fmtp:", &p)) { + char attr[256]; + char value[4096]; + + // remove the protocol identifier.. + while (*p && *p == ' ') p++; // strip spaces. + while (*p && *p != ' ') p++; // eat protocol identifier + while (*p && *p == ' ') p++; // strip trailing spaces. + + /* loop on each attribute */ + while (ff_rtsp_next_attr_and_value + (&p, attr, sizeof(attr), value, sizeof(value))) { + /* grab the codec extra_data from the config parameter of the fmtp line */ + sdp_parse_fmtp_config_h264(stream, h264_data, attr, value); + } + } else if (av_strstart(p, "cliprect:", &p)) { + // could use this if we wanted. + } + + av_set_pts_info(stream, 33, 1, 90000); // 33 should be right, because the pts is 64 bit? (done elsewhere; this is a one time thing) + + return 0; // keep processing it the normal way... +} + +/** +This is the structure for expanding on the dynamic rtp protocols (makes everything static. yay!) +*/ +RTPDynamicProtocolHandler ff_h264_dynamic_handler = { + .enc_name = "H264", + .codec_type = CODEC_TYPE_VIDEO, + .codec_id = CODEC_ID_H264, + .parse_sdp_a_line = parse_h264_sdp_line, + .open = h264_new_context, + .close = h264_free_context, + .parse_packet = h264_handle_packet +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtpdec_h264.h Sun Feb 28 11:03:14 2010 +0000 @@ -0,0 +1,29 @@ +/* + * RTP H264 Protocol (RFC3984) + * Copyright (c) 2006 Ryan Martell + * + * 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 + */ + +#ifndef AVFORMAT_RTPDEC_H264_H +#define AVFORMAT_RTPDEC_H264_H + +#include "rtpdec.h" + +extern RTPDynamicProtocolHandler ff_h264_dynamic_handler; + +#endif /* AVFORMAT_RTPDEC_H264_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtpdec_vorbis.c Sun Feb 28 11:03:14 2010 +0000 @@ -0,0 +1,218 @@ +/* + * RTP Vorbis Protocol (RFC5215) + * Copyright (c) 2009 Colin McQuillan + * + * 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 + */ + +/** + * @file libavformat/rtpdec_vorbis.c + * @brief Vorbis / RTP Code (RFC 5215) + * @author Colin McQuillan <m.niloc@gmail.com> + */ + +#include "libavutil/base64.h" +#include "libavutil/avstring.h" +#include "libavcodec/bytestream.h" + +#include <assert.h> + +#include "rtpdec.h" +#include "rtpdec_vorbis.h" + +/** + * RTP/Vorbis specific private data. + */ +struct PayloadContext { + unsigned ident; ///< 24-bit stream configuration identifier +}; + +/** + * Length encoding described in RFC5215 section 3.1.1. + */ +static int get_base128(const uint8_t ** buf, const uint8_t * buf_end) +{ + int n = 0; + for (; *buf < buf_end; ++*buf) { + n <<= 7; + n += **buf & 0x7f; + if (!(**buf & 0x80)) { + ++*buf; + return n; + } + } + return 0; +} + +/** + * Out-of-band headers, described in RFC 5251 section 3.2.1 + */ +static unsigned int +parse_packed_headers(const uint8_t * packed_headers, + const uint8_t * packed_headers_end, + AVCodecContext * codec, PayloadContext * vorbis_data) +{ + unsigned num_packed, num_headers, length, length1, length2; + uint8_t *ptr; + + num_packed = bytestream_get_be32(&packed_headers); + vorbis_data->ident = bytestream_get_be24(&packed_headers); + length = bytestream_get_be16(&packed_headers); + num_headers = get_base128(&packed_headers, packed_headers_end); + length1 = get_base128(&packed_headers, packed_headers_end); + length2 = get_base128(&packed_headers, packed_headers_end); + + if (num_packed != 1 || num_headers > 3) { + av_log(codec, AV_LOG_ERROR, + "Unimplemented number of headers: %d packed headers, %d headers\n", + num_packed, num_headers); + return AVERROR_PATCHWELCOME; + } + + if (packed_headers_end - packed_headers != length || + length1 > length || length2 > length - length1) { + av_log(codec, AV_LOG_ERROR, + "Bad packed header lengths (%d,%d,%d,%d)\n", length1, + length2, packed_headers_end - packed_headers, length); + return AVERROR_INVALIDDATA; + } + + ptr = codec->extradata = av_mallocz(length + length / 255 + 64); + if (!ptr) { + av_log(codec, AV_LOG_ERROR, "Out of memory"); + return AVERROR_NOMEM; + } + *ptr++ = 2; + ptr += av_xiphlacing(ptr, length1); + ptr += av_xiphlacing(ptr, length2); + memcpy(ptr, packed_headers, length); + ptr += length; + codec->extradata_size = ptr - codec->extradata; + + return 0; +} + +int +ff_vorbis_parse_fmtp_config(AVCodecContext * codec, + void *vorbis_data, char *attr, char *value) +{ + int result = 0; + assert(codec->codec_id == CODEC_ID_VORBIS); + assert(vorbis_data); + + // The configuration value is a base64 encoded packed header + if (!strcmp(attr, "configuration")) { + uint8_t *decoded_packet = NULL; + int packet_size; + size_t decoded_alloc = strlen(value) / 4 * 3 + 4; + + if (decoded_alloc <= INT_MAX) { + decoded_packet = av_malloc(decoded_alloc); + if (decoded_packet) { + packet_size = + av_base64_decode(decoded_packet, value, decoded_alloc); + + result = parse_packed_headers + (decoded_packet, decoded_packet + packet_size, codec, + vorbis_data); + } else { + av_log(codec, AV_LOG_ERROR, + "Out of memory while decoding SDP configuration.\n"); + result = AVERROR_NOMEM; + } + } else { + av_log(codec, AV_LOG_ERROR, "Packet too large\n"); + result = AVERROR_INVALIDDATA; + } + av_free(decoded_packet); + } + return result; +} + +static PayloadContext *vorbis_new_context(void) +{ + return av_mallocz(sizeof(PayloadContext)); +} + +static void vorbis_free_context(PayloadContext * data) +{ + av_free(data); +} + +/** + * Handle payload as described in RFC 5215 section 2.2 + */ +static int +vorbis_handle_packet(AVFormatContext * ctx, + PayloadContext * data, + AVStream * st, + AVPacket * pkt, + uint32_t * timestamp, + const uint8_t * buf, int len, int flags) +{ + int ident, fragmented, vdt, num_pkts, pkt_len; + + if (len < 6) { + av_log(ctx, AV_LOG_ERROR, "Invalid %d byte packet\n", len); + return AVERROR_INVALIDDATA; + } + + ident = AV_RB24(buf); + fragmented = buf[3] >> 6; + vdt = (buf[3] >> 4) & 3; + num_pkts = buf[3] & 7; + pkt_len = AV_RB16(buf + 4); + + if (pkt_len > len - 6) { + av_log(ctx, AV_LOG_ERROR, + "Invalid packet length %d in %d byte packet\n", pkt_len, + len); + return AVERROR_INVALIDDATA; + } + + if (ident != data->ident) { + av_log(ctx, AV_LOG_ERROR, + "Unimplemented Vorbis SDP configuration change detected\n"); + return AVERROR_PATCHWELCOME; + } + + if (fragmented != 0 || vdt != 0 || num_pkts != 1) { + av_log(ctx, AV_LOG_ERROR, + "Unimplemented RTP Vorbis packet settings (%d,%d,%d)\n", + fragmented, vdt, num_pkts); + return AVERROR_PATCHWELCOME; + } + + if (av_new_packet(pkt, pkt_len)) { + av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); + return AVERROR_NOMEM; + } + + memcpy(pkt->data, buf + 6, pkt_len); + pkt->stream_index = st->index; + return 0; +} + +RTPDynamicProtocolHandler ff_vorbis_dynamic_handler = { + .enc_name = "vorbis", + .codec_type = CODEC_TYPE_AUDIO, + .codec_id = CODEC_ID_VORBIS, + .parse_sdp_a_line = NULL, + .open = vorbis_new_context, + .close = vorbis_free_context, + .parse_packet = vorbis_handle_packet +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rtpdec_vorbis.h Sun Feb 28 11:03:14 2010 +0000 @@ -0,0 +1,45 @@ +/* + * RTP Vorbis Protocol (RFC 5215) + * Copyright (c) 2009 Colin McQuillan + * + * 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 + */ + +#ifndef AVFORMAT_RTPDEC_VORBIS_H +#define AVFORMAT_RTPDEC_VORBIS_H + +#include "libavcodec/avcodec.h" +#include "rtpdec.h" + +/** + * Handle a Vorbis-specific FMTP parameter + * + * @param codec The context of the codec + * @param ctx Private Vorbis RTP context + * @param attr Format-specific parameter name + * @param value Format-specific paremeter value + */ +int +ff_vorbis_parse_fmtp_config(AVCodecContext * codec, + void *ctx, char *attr, char *value); + +/** + * Vorbis RTP callbacks. + */ +extern RTPDynamicProtocolHandler ff_vorbis_dynamic_handler; + +#endif /* AVFORMAT_RTPDEC_VORBIS_H */