Patch by Ryan Martell % rdm4 A martellventures P com % Original thread: Date: Oct 9, 2006 4:55 PM Subject: [Ffmpeg-devel] RTP patches & RFC Actual committed patch: Date: Oct 26, 2006 4:29 PM Originally committed as revision 6798 to svn://svn.ffmpeg.org/ffmpeg/trunktags/v0.5
| @@ -416,6 +416,7 @@ void av_register_all(void) | |||||
| #ifdef CONFIG_REDIR_DEMUXER | #ifdef CONFIG_REDIR_DEMUXER | ||||
| av_register_input_format(&redir_demuxer); | av_register_input_format(&redir_demuxer); | ||||
| #endif | #endif | ||||
| av_register_rtp_dynamic_payload_handlers(); | |||||
| #endif /* CONFIG_NETWORK */ | #endif /* CONFIG_NETWORK */ | ||||
| #ifdef CONFIG_SEGAFILM_DEMUXER | #ifdef CONFIG_SEGAFILM_DEMUXER | ||||
| av_register_input_format(&segafilm_demuxer); | av_register_input_format(&segafilm_demuxer); | ||||
| @@ -170,6 +170,9 @@ int pcm_read_seek(AVFormatContext *s, | |||||
| /* rtsp.c */ | /* rtsp.c */ | ||||
| int redir_open(AVFormatContext **ic_ptr, ByteIOContext *f); | int redir_open(AVFormatContext **ic_ptr, ByteIOContext *f); | ||||
| /* rtp.c */ | |||||
| void av_register_rtp_dynamic_payload_handlers(); | |||||
| #if 0 | #if 0 | ||||
| extern AVImageFormat pnm_image_format; | extern AVImageFormat pnm_image_format; | ||||
| @@ -33,6 +33,13 @@ | |||||
| #endif | #endif | ||||
| #include <netdb.h> | #include <netdb.h> | ||||
| #include "rtp_internal.h" | |||||
| //#define RTP_H264 | |||||
| #ifdef RTP_H264 | |||||
| #include "rtp_h264.h" | |||||
| #endif | |||||
| //#define DEBUG | //#define DEBUG | ||||
| @@ -179,42 +186,26 @@ AVRtpPayloadType_t AVRtpPayloadTypes[]= | |||||
| {-1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1} | {-1, "", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE, -1, -1} | ||||
| }; | }; | ||||
| AVRtpDynamicPayloadType_t AVRtpDynamicPayloadTypes[]= | |||||
| /* statistics functions */ | |||||
| RTPDynamicProtocolHandler *RTPFirstDynamicPayloadHandler= NULL; | |||||
| static RTPDynamicProtocolHandler mp4v_es_handler= {"MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4}; | |||||
| static RTPDynamicProtocolHandler mpeg4_generic_handler= {"mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_MPEG4AAC}; | |||||
| static void register_dynamic_payload_handler(RTPDynamicProtocolHandler *handler) | |||||
| { | { | ||||
| {"MP4V-ES", CODEC_TYPE_VIDEO, CODEC_ID_MPEG4}, | |||||
| {"mpeg4-generic", CODEC_TYPE_AUDIO, CODEC_ID_MPEG4AAC}, | |||||
| {"", CODEC_TYPE_UNKNOWN, CODEC_ID_NONE} | |||||
| }; | |||||
| handler->next= RTPFirstDynamicPayloadHandler; | |||||
| RTPFirstDynamicPayloadHandler= handler; | |||||
| } | |||||
| struct RTPDemuxContext { | |||||
| AVFormatContext *ic; | |||||
| AVStream *st; | |||||
| int payload_type; | |||||
| uint32_t ssrc; | |||||
| uint16_t seq; | |||||
| uint32_t timestamp; | |||||
| uint32_t base_timestamp; | |||||
| uint32_t cur_timestamp; | |||||
| int max_payload_size; | |||||
| MpegTSContext *ts; /* only used for MP2T payloads */ | |||||
| int read_buf_index; | |||||
| int read_buf_size; | |||||
| /* rtcp sender statistics receive */ | |||||
| int64_t last_rtcp_ntp_time; | |||||
| int64_t first_rtcp_ntp_time; | |||||
| uint32_t last_rtcp_timestamp; | |||||
| /* rtcp sender statistics */ | |||||
| unsigned int packet_count; | |||||
| unsigned int octet_count; | |||||
| unsigned int last_octet_count; | |||||
| int first_packet; | |||||
| /* buffer for output */ | |||||
| uint8_t buf[RTP_MAX_PACKET_LENGTH]; | |||||
| uint8_t *buf_ptr; | |||||
| /* special infos for au headers parsing */ | |||||
| rtp_payload_data_t *rtp_payload_data; | |||||
| }; | |||||
| void av_register_rtp_dynamic_payload_handlers() | |||||
| { | |||||
| register_dynamic_payload_handler(&mp4v_es_handler); | |||||
| register_dynamic_payload_handler(&mpeg4_generic_handler); | |||||
| #ifdef RTP_H264 | |||||
| register_dynamic_payload_handler(&ff_h264_dynamic_handler); | |||||
| #endif | |||||
| } | |||||
| int rtp_get_codec_info(AVCodecContext *codec, int payload_type) | int rtp_get_codec_info(AVCodecContext *codec, int payload_type) | ||||
| { | { | ||||
| @@ -271,6 +262,7 @@ static int rtcp_parse_packet(RTPDemuxContext *s, const unsigned char *buf, int l | |||||
| * open a new RTP parse context for stream 'st'. 'st' can be NULL for | * open a new RTP parse context for stream 'st'. 'st' can be NULL for | ||||
| * MPEG2TS streams to indicate that they should be demuxed inside the | * MPEG2TS streams to indicate that they should be demuxed inside the | ||||
| * rtp demux (otherwise CODEC_ID_MPEG2TS packets are returned) | * rtp demux (otherwise CODEC_ID_MPEG2TS packets are returned) | ||||
| * TODO: change this to not take rtp_payload data, and use the new dynamic payload system. | |||||
| */ | */ | ||||
| RTPDemuxContext *rtp_parse_open(AVFormatContext *s1, AVStream *st, int payload_type, rtp_payload_data_t *rtp_payload_data) | RTPDemuxContext *rtp_parse_open(AVFormatContext *s1, AVStream *st, int payload_type, rtp_payload_data_t *rtp_payload_data) | ||||
| { | { | ||||
| @@ -298,6 +290,9 @@ RTPDemuxContext *rtp_parse_open(AVFormatContext *s1, AVStream *st, int payload_t | |||||
| case CODEC_ID_MP2: | case CODEC_ID_MP2: | ||||
| case CODEC_ID_MP3: | case CODEC_ID_MP3: | ||||
| case CODEC_ID_MPEG4: | case CODEC_ID_MPEG4: | ||||
| #ifdef RTP_H264 | |||||
| case CODEC_ID_H264: | |||||
| #endif | |||||
| st->need_parsing = 1; | st->need_parsing = 1; | ||||
| break; | break; | ||||
| default: | default: | ||||
| @@ -374,6 +369,9 @@ int rtp_parse_packet(RTPDemuxContext *s, AVPacket *pkt, | |||||
| if (!buf) { | if (!buf) { | ||||
| /* return the next packets, if any */ | /* return the next packets, if any */ | ||||
| if(s->st && s->parse_packet) { | |||||
| return s->parse_packet(s, pkt, 0, NULL, 0); | |||||
| } else { | |||||
| if (s->read_buf_index >= s->read_buf_size) | if (s->read_buf_index >= s->read_buf_size) | ||||
| return -1; | return -1; | ||||
| ret = mpegts_parse_packet(s->ts, pkt, s->buf + s->read_buf_index, | ret = mpegts_parse_packet(s->ts, pkt, s->buf + s->read_buf_index, | ||||
| @@ -385,6 +383,7 @@ int rtp_parse_packet(RTPDemuxContext *s, AVPacket *pkt, | |||||
| return 1; | return 1; | ||||
| else | else | ||||
| return 0; | return 0; | ||||
| } | |||||
| } | } | ||||
| if (len < 12) | if (len < 12) | ||||
| @@ -428,6 +427,7 @@ int rtp_parse_packet(RTPDemuxContext *s, AVPacket *pkt, | |||||
| return 1; | return 1; | ||||
| } | } | ||||
| } else { | } else { | ||||
| // at this point, the RTP header has been stripped; This is ASSUMING that there is only 1 CSRC, which in't wise. | |||||
| switch(st->codec->codec_id) { | switch(st->codec->codec_id) { | ||||
| case CODEC_ID_MP2: | case CODEC_ID_MP2: | ||||
| /* better than nothing: skip mpeg audio RTP header */ | /* better than nothing: skip mpeg audio RTP header */ | ||||
| @@ -457,8 +457,12 @@ int rtp_parse_packet(RTPDemuxContext *s, AVPacket *pkt, | |||||
| memcpy(pkt->data, buf, len); | memcpy(pkt->data, buf, len); | ||||
| break; | break; | ||||
| default: | default: | ||||
| if(s->parse_packet) { | |||||
| return s->parse_packet(s, pkt, timestamp, buf, len); | |||||
| } else { | |||||
| av_new_packet(pkt, len); | av_new_packet(pkt, len); | ||||
| memcpy(pkt->data, buf, len); | memcpy(pkt->data, buf, len); | ||||
| } | |||||
| break; | break; | ||||
| } | } | ||||
| @@ -511,6 +515,7 @@ int rtp_parse_packet(RTPDemuxContext *s, AVPacket *pkt, | |||||
| void rtp_parse_close(RTPDemuxContext *s) | void rtp_parse_close(RTPDemuxContext *s) | ||||
| { | { | ||||
| // TODO: fold this into the protocol specific data fields. | |||||
| if (!strcmp(AVRtpPayloadTypes[s->payload_type].enc_name, "MP2T")) { | if (!strcmp(AVRtpPayloadTypes[s->payload_type].enc_name, "MP2T")) { | ||||
| mpegts_parse_close(s->ts); | mpegts_parse_close(s->ts); | ||||
| } | } | ||||
| @@ -89,13 +89,6 @@ typedef struct AVRtpPayloadType_s | |||||
| int audio_channels; | int audio_channels; | ||||
| } AVRtpPayloadType_t; | } AVRtpPayloadType_t; | ||||
| typedef struct AVRtpDynamicPayloadType_s /* payload type >= 96 */ | |||||
| { | |||||
| const char enc_name[50]; /* XXX: still why 50 ? ;-) */ | |||||
| enum CodecType codec_type; | |||||
| enum CodecID codec_id; | |||||
| } AVRtpDynamicPayloadType_t; | |||||
| #if 0 | #if 0 | ||||
| typedef enum { | typedef enum { | ||||
| RTCP_SR = 200, | RTCP_SR = 200, | ||||
| @@ -122,6 +115,4 @@ typedef enum { | |||||
| #endif | #endif | ||||
| extern AVRtpPayloadType_t AVRtpPayloadTypes[]; | extern AVRtpPayloadType_t AVRtpPayloadTypes[]; | ||||
| extern AVRtpDynamicPayloadType_t AVRtpDynamicPayloadTypes[]; | |||||
| #endif /* RTP_H */ | #endif /* RTP_H */ | ||||
| @@ -0,0 +1,86 @@ | |||||
| /* | |||||
| * RTP definitions | |||||
| * Copyright (c) 2006 Ryan Martell <rdm4@martellventures.com> | |||||
| * | |||||
| * This library 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 of the License, or (at your option) any later version. | |||||
| * | |||||
| * This library 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 this library; if not, write to the Free Software | |||||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||||
| */ | |||||
| // this is a bit of a misnomer, because rtp & rtsp internal structures and prototypes are in here. | |||||
| #ifndef RTP_INTERNAL_H | |||||
| #define RTP_INTERNAL_H | |||||
| typedef int (*DynamicPayloadPacketHandlerProc) (struct RTPDemuxContext * s, | |||||
| AVPacket * pkt, | |||||
| uint32_t timestamp, | |||||
| const uint8_t * buf, | |||||
| int len); | |||||
| typedef struct RTPDynamicProtocolHandler_s { | |||||
| // fields from AVRtpDynamicPayloadType_s | |||||
| const char enc_name[50]; /* XXX: still why 50 ? ;-) */ | |||||
| enum CodecType codec_type; | |||||
| enum CodecID codec_id; | |||||
| // may be null | |||||
| int (*parse_sdp_a_line) (AVStream * stream, | |||||
| void *protocol_data, | |||||
| const char *line); ///< Parse the a= line from the sdp field | |||||
| void *(*open) (); ///< allocate any data needed by the rtp parsing for this dynamic data. | |||||
| void (*close)(void *protocol_data); ///< free any data needed by the rtp parsing for this dynamic data. | |||||
| DynamicPayloadPacketHandlerProc parse_packet; ///< parse handler for this dynamic packet. | |||||
| struct RTPDynamicProtocolHandler_s *next; | |||||
| } RTPDynamicProtocolHandler; | |||||
| // moved out of rtp.c, because the h264 decoder needs to know about this structure.. | |||||
| struct RTPDemuxContext { | |||||
| AVFormatContext *ic; | |||||
| AVStream *st; | |||||
| int payload_type; | |||||
| uint32_t ssrc; | |||||
| uint16_t seq; | |||||
| uint32_t timestamp; | |||||
| uint32_t base_timestamp; | |||||
| uint32_t cur_timestamp; | |||||
| int max_payload_size; | |||||
| struct MpegTSContext *ts; /* only used for MP2T payloads */ | |||||
| int read_buf_index; | |||||
| int read_buf_size; | |||||
| /* rtcp sender statistics receive */ | |||||
| int64_t last_rtcp_ntp_time; // TODO: move into statistics | |||||
| int64_t first_rtcp_ntp_time; // TODO: move into statistics | |||||
| uint32_t last_rtcp_timestamp; // TODO: move into statistics | |||||
| /* rtcp sender statistics */ | |||||
| unsigned int packet_count; // TODO: move into statistics (outgoing) | |||||
| unsigned int octet_count; // TODO: move into statistics (outgoing) | |||||
| unsigned int last_octet_count; // TODO: move into statistics (outgoing) | |||||
| int first_packet; | |||||
| /* buffer for output */ | |||||
| uint8_t buf[RTP_MAX_PACKET_LENGTH]; | |||||
| uint8_t *buf_ptr; | |||||
| /* special infos for au headers parsing */ | |||||
| rtp_payload_data_t *rtp_payload_data; // TODO: Move into dynamic payload handlers | |||||
| /* dynamic payload stuff */ | |||||
| DynamicPayloadPacketHandlerProc parse_packet; ///< This is also copied from the dynamic protocol handler structure | |||||
| void *dynamic_protocol_context; ///< This is a copy from the values setup from the sdp parsing, in rtsp.c don't free me. | |||||
| }; | |||||
| extern RTPDynamicProtocolHandler *RTPFirstDynamicPayloadHandler; | |||||
| #endif /* RTP_INTERNAL_H */ | |||||
| @@ -30,6 +30,8 @@ | |||||
| # include "barpainet.h" | # include "barpainet.h" | ||||
| #endif | #endif | ||||
| #include "rtp_internal.h" | |||||
| //#define DEBUG | //#define DEBUG | ||||
| //#define DEBUG_RTP_TCP | //#define DEBUG_RTP_TCP | ||||
| @@ -69,6 +71,9 @@ typedef struct RTSPStream { | |||||
| int sdp_ttl; /* IP TTL (from SDP content - not used in RTSP) */ | int sdp_ttl; /* IP TTL (from SDP content - not used in RTSP) */ | ||||
| int sdp_payload_type; /* payload type - only used in SDP */ | int sdp_payload_type; /* payload type - only used in SDP */ | ||||
| rtp_payload_data_t rtp_payload_data; /* rtp payload parsing infos from SDP */ | rtp_payload_data_t rtp_payload_data; /* rtp payload parsing infos from SDP */ | ||||
| RTPDynamicProtocolHandler *dynamic_handler; ///< Only valid if it's a dynamic protocol. (This is the handler structure) | |||||
| void *dynamic_protocol_context; ///< Only valid if it's a dynamic protocol. (This is any private data associated with the dynamic protocol) | |||||
| } RTSPStream; | } RTSPStream; | ||||
| static int rtsp_read_play(AVFormatContext *s); | static int rtsp_read_play(AVFormatContext *s); | ||||
| @@ -142,7 +147,7 @@ static void get_word(char *buf, int buf_size, const char **pp) | |||||
| /* parse the rtpmap description: <codec_name>/<clock_rate>[/<other | /* parse the rtpmap description: <codec_name>/<clock_rate>[/<other | ||||
| params>] */ | params>] */ | ||||
| static int sdp_parse_rtpmap(AVCodecContext *codec, int payload_type, const char *p) | |||||
| static int sdp_parse_rtpmap(AVCodecContext *codec, RTSPStream *rtsp_st, int payload_type, const char *p) | |||||
| { | { | ||||
| char buf[256]; | char buf[256]; | ||||
| int i; | int i; | ||||
| @@ -153,12 +158,18 @@ static int sdp_parse_rtpmap(AVCodecContext *codec, int payload_type, const char | |||||
| see if we can handle this kind of payload */ | see if we can handle this kind of payload */ | ||||
| get_word_sep(buf, sizeof(buf), "/", &p); | get_word_sep(buf, sizeof(buf), "/", &p); | ||||
| if (payload_type >= RTP_PT_PRIVATE) { | if (payload_type >= RTP_PT_PRIVATE) { | ||||
| /* We are in dynmaic payload type case ... search into AVRtpDynamicPayloadTypes[] */ | |||||
| for (i = 0; AVRtpDynamicPayloadTypes[i].codec_id != CODEC_ID_NONE; ++i) | |||||
| if (!strcmp(buf, AVRtpDynamicPayloadTypes[i].enc_name) && (codec->codec_type == AVRtpDynamicPayloadTypes[i].codec_type)) { | |||||
| codec->codec_id = AVRtpDynamicPayloadTypes[i].codec_id; | |||||
| RTPDynamicProtocolHandler *handler= RTPFirstDynamicPayloadHandler; | |||||
| while(handler) { | |||||
| if (!strcmp(buf, handler->enc_name) && (codec->codec_type == handler->codec_type)) { | |||||
| codec->codec_id = handler->codec_id; | |||||
| rtsp_st->dynamic_handler= handler; | |||||
| if(handler->open) { | |||||
| rtsp_st->dynamic_protocol_context= handler->open(); | |||||
| } | |||||
| break; | break; | ||||
| } | } | ||||
| handler= handler->next; | |||||
| } | |||||
| } else { | } else { | ||||
| /* We are in a standard case ( from http://www.iana.org/assignments/rtp-parameters) */ | /* We are in a standard case ( from http://www.iana.org/assignments/rtp-parameters) */ | ||||
| /* search into AVRtpPayloadTypes[] */ | /* search into AVRtpPayloadTypes[] */ | ||||
| @@ -440,7 +451,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, | |||||
| st = s->streams[i]; | st = s->streams[i]; | ||||
| rtsp_st = st->priv_data; | rtsp_st = st->priv_data; | ||||
| if (rtsp_st->sdp_payload_type == payload_type) { | if (rtsp_st->sdp_payload_type == payload_type) { | ||||
| sdp_parse_rtpmap(st->codec, payload_type, p); | |||||
| sdp_parse_rtpmap(st->codec, rtsp_st, payload_type, p); | |||||
| } | } | ||||
| } | } | ||||
| } else if (strstart(p, "fmtp:", &p)) { | } else if (strstart(p, "fmtp:", &p)) { | ||||
| @@ -451,7 +462,13 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, | |||||
| st = s->streams[i]; | st = s->streams[i]; | ||||
| rtsp_st = st->priv_data; | rtsp_st = st->priv_data; | ||||
| if (rtsp_st->sdp_payload_type == payload_type) { | if (rtsp_st->sdp_payload_type == payload_type) { | ||||
| if(rtsp_st->dynamic_handler && rtsp_st->dynamic_handler->parse_sdp_a_line) { | |||||
| if(!rtsp_st->dynamic_handler->parse_sdp_a_line(st, rtsp_st->dynamic_protocol_context, buf)) { | |||||
| sdp_parse_fmtp(st, p); | |||||
| } | |||||
| } else { | |||||
| sdp_parse_fmtp(st, p); | sdp_parse_fmtp(st, p); | ||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -788,6 +805,8 @@ static void rtsp_close_streams(RTSPState *rt) | |||||
| rtp_parse_close(rtsp_st->rtp_ctx); | rtp_parse_close(rtsp_st->rtp_ctx); | ||||
| if (rtsp_st->rtp_handle) | if (rtsp_st->rtp_handle) | ||||
| url_close(rtsp_st->rtp_handle); | url_close(rtsp_st->rtp_handle); | ||||
| if (rtsp_st->dynamic_handler && rtsp_st->dynamic_protocol_context) | |||||
| rtsp_st->dynamic_handler->close(rtsp_st->dynamic_protocol_context); | |||||
| } | } | ||||
| av_free(rtsp_st); | av_free(rtsp_st); | ||||
| } | } | ||||
| @@ -980,6 +999,11 @@ static int rtsp_read_header(AVFormatContext *s, | |||||
| if (!rtsp_st->rtp_ctx) { | if (!rtsp_st->rtp_ctx) { | ||||
| err = AVERROR_NOMEM; | err = AVERROR_NOMEM; | ||||
| goto fail; | goto fail; | ||||
| } else { | |||||
| if(rtsp_st->dynamic_handler) { | |||||
| rtsp_st->rtp_ctx->dynamic_protocol_context= rtsp_st->dynamic_protocol_context; | |||||
| rtsp_st->rtp_ctx->parse_packet= rtsp_st->dynamic_handler->parse_packet; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -1326,6 +1350,11 @@ static int sdp_read_header(AVFormatContext *s, | |||||
| if (!rtsp_st->rtp_ctx) { | if (!rtsp_st->rtp_ctx) { | ||||
| err = AVERROR_NOMEM; | err = AVERROR_NOMEM; | ||||
| goto fail; | goto fail; | ||||
| } else { | |||||
| if(rtsp_st->dynamic_handler) { | |||||
| rtsp_st->rtp_ctx->dynamic_protocol_context= rtsp_st->dynamic_protocol_context; | |||||
| rtsp_st->rtp_ctx->parse_packet= rtsp_st->dynamic_handler->parse_packet; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| return 0; | return 0; | ||||