diff --git a/configure.ac b/configure.ac index 9fafe08f0..8ca865dec 100644 --- a/configure.ac +++ b/configure.ac @@ -210,6 +210,7 @@ AC_CHECK_LIB(avutil,ff_gcd,,AC_MSG_WARN(libavutil.a may be required for MPEG str AC_CHECK_LIB(avcodec,avcodec_init,,AC_MSG_WARN(libavcodec.a is required for MPEG streaming)) AC_CHECK_LIB(avformat,av_new_stream,,AC_MSG_WARN(libavformat.a is required for MPEG streaming),-lavcodec) AC_CHECK_LIB(swscale,sws_scale,,,-lswscale) +AC_CHECK_LIB(bz2,BZ2_bzCompress,,AC_MSG_WARN(zm requires libbz2.a for recent versions of ffmpeg)) # Checks for header files. AC_FUNC_ALLOCA @@ -218,12 +219,14 @@ AC_CHECK_HEADERS([fcntl.h limits.h memory.h netdb.h netinet/in.h stddef.h stdlib AC_CHECK_HEADERS(linux/videodev.h,,AC_MSG_ERROR(zm requires Video4Linux to be installed),) AC_CHECK_HEADERS(linux/videodev2.h,,AC_MSG_WARN(zm requires Video4Linux2 to be installed for V4L2 support),) AC_CHECK_HEADERS(mysql/mysql.h,,AC_MSG_ERROR(zm requires MySQL headers - check that MySQL development packages are installed),) -AC_CHECK_HEADERS(ffmpeg/avcodec.h,,,) +AC_CHECK_HEADERS(libavutil/avutil.h,,,) +AC_CHECK_HEADERS(ffmpeg/avutil.h,,,) AC_CHECK_HEADERS(libavcodec/avcodec.h,,,) -AC_CHECK_HEADERS(ffmpeg/avformat.h,,,) +AC_CHECK_HEADERS(ffmpeg/avcodec.h,,,) AC_CHECK_HEADERS(libavformat/avformat.h,,,) -AC_CHECK_HEADERS(ffmpeg/swscale.h,,,) +AC_CHECK_HEADERS(ffmpeg/avformat.h,,,) AC_CHECK_HEADERS(libswscale/swscale.h,,,) +AC_CHECK_HEADERS(ffmpeg/swscale.h,,,) AC_CHECK_HEADERS(pcre/pcre.h,,,) AC_CHECK_HEADERS(pcre.h,,,) if test "$ENABLE_MMAP" = "yes"; then diff --git a/src/Makefile.am b/src/Makefile.am index 0493ff543..ece72aa84 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -48,7 +48,7 @@ zm_SOURCES = \ zm_rtp_data.cpp \ zm_rtp_source.cpp \ zm_rtsp.cpp \ - zm_sdp.cpp \ + zm_sdp.c \ zm_signal.cpp \ zm_stream.cpp \ zm_thread.cpp \ @@ -101,7 +101,7 @@ noinst_HEADERS = \ zm_rtp.h \ zm_rtp_source.h \ zm_rtsp.cpp \ - zm_sdp.cpp \ + zm_sdp.c \ zm_signal.h \ zm_stream.h \ zm_thread.h \ diff --git a/src/zm_ffmpeg.h b/src/zm_ffmpeg.h index a9031b892..096ab9ce3 100644 --- a/src/zm_ffmpeg.h +++ b/src/zm_ffmpeg.h @@ -17,15 +17,19 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include "zm.h" - -#if HAVE_LIBAVCODEC - #ifndef ZM_FFMPEG_H #define ZM_FFMPEG_H +#if HAVE_LIBAVCODEC + extern "C" { -#define __STDC_CONSTANT_MACROS +#if HAVE_LIBAVUTIL_AVUTIL_H +#include +#elif HAVE_FFMPEG_AVUTIL_H +#include +#else +#error "No location for avutils.h found" +#endif #if HAVE_LIBAVCODEC_AVCODEC_H #include #elif HAVE_FFMPEG_AVCODEC_H @@ -62,7 +66,7 @@ extern "C" { #else // FFMPEG_VERSION_INT #define ZM_FFMPEG_SVN 1 #endif // FFMPEG_VERSION_INT - -#endif // ZM_FFMPEG_H #endif // HAVE_LIBAVCODEC + +#endif // ZM_FFMPEG_H diff --git a/src/zm_mpeg.h b/src/zm_mpeg.h index fe291a18f..bc3853e5a 100644 --- a/src/zm_mpeg.h +++ b/src/zm_mpeg.h @@ -17,13 +17,13 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#if HAVE_LIBAVCODEC - #ifndef ZM_MPEG_H #define ZM_MPEG_H #include "zm_ffmpeg.h" +#if HAVE_LIBAVCODEC + class VideoStream { protected: @@ -65,6 +65,6 @@ public: double EncodeFrame( uint8_t *buffer, int buffer_size, bool add_timestamp=false, unsigned int timestamp=0 ); }; -#endif // ZM_MPEG_H - #endif // HAVE_LIBAVCODEC + +#endif // ZM_MPEG_H diff --git a/src/zm_remote_camera_rtsp.cpp b/src/zm_remote_camera_rtsp.cpp index 8eb333eda..f67e5a5c9 100644 --- a/src/zm_remote_camera_rtsp.cpp +++ b/src/zm_remote_camera_rtsp.cpp @@ -20,7 +20,9 @@ #include "zm_remote_camera_rtsp.h" #include "zm_mem_utils.h" +extern "C" { #include "zm_sdp.h" +} #include #include diff --git a/src/zm_rtsp.cpp b/src/zm_rtsp.cpp index c65efc681..c2a2caf62 100644 --- a/src/zm_rtsp.cpp +++ b/src/zm_rtsp.cpp @@ -24,6 +24,7 @@ #include "zm_db.h" #include +#include #include int RtspThread::mMinDataPort = 0; diff --git a/src/zm_rtsp.h b/src/zm_rtsp.h index b97f0dc0d..7b9b767a3 100644 --- a/src/zm_rtsp.h +++ b/src/zm_rtsp.h @@ -17,10 +17,13 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // -#ifndef ZM_RTP_RTSP_H -#define ZM_RTP_RTSP_H +#ifndef ZM_RTSP_H +#define ZM_RTSP_H +extern "C" { #include "zm_sdp.h" +} + #include "zm_mpeg.h" #include "zm_comms.h" #include "zm_thread.h" @@ -122,4 +125,4 @@ public: } }; -#endif // ZM_RTP_RTSP_H +#endif // ZM_RTSP_H diff --git a/src/zm_sdp.cpp b/src/zm_sdp.c similarity index 50% rename from src/zm_sdp.cpp rename to src/zm_sdp.c index 9e0c25c32..f49b469e1 100644 --- a/src/zm_sdp.cpp +++ b/src/zm_sdp.c @@ -1,40 +1,30 @@ #include "zm_ffmpeg.h" #include "zm_sdp.h" +// +// This file contains chunks of ffmpeg code that are not currently exported. +// The main thing we are after is the sdp parser +// + +#if HAVE_LIBAVUTIL_AVUTIL_H +#include +#elif HAVE_FFMPEG_AVUTIL_H +#include +#else +#error "No location for avstring.h found" +#endif + +#include +#include +#include #include +#include -static int inet_aton (const char * str, struct in_addr * add) -{ - unsigned int add1 = 0, add2 = 0, add3 = 0, add4 = 0; +// +// Part of libavformat/avformat.h that is usually obscured +// - if (sscanf(str, "%d.%d.%d.%d", &add1, &add2, &add3, &add4) != 4) - return 0; - - if (!add1 || (add1|add2|add3|add4) > 255) return 0; - - add->s_addr=(add4<<24)+(add3<<16)+(add2<<8)+add1; - - return 1; -} - -static void __dynarray_add(unsigned long **tab_ptr, int *nb_ptr, unsigned long elem) -{ - int nb, nb_alloc; - unsigned long *tab; - - nb = *nb_ptr; - tab = *tab_ptr; - if ((nb & (nb - 1)) == 0) { - if (nb == 0) - nb_alloc = 1; - else - nb_alloc = nb * 2; - tab = (long unsigned int *)av_realloc(tab, nb_alloc * sizeof(unsigned long)); - *tab_ptr = tab; - } - tab[nb++] = elem; - *nb_ptr = nb; -} +void ff_dynarray_add(unsigned long **tab_ptr, int *nb_ptr, unsigned long elem); #ifdef __GNUC__ #define dynarray_add(tab, nb_ptr, elem)\ @@ -42,79 +32,71 @@ do {\ typeof(tab) _tab = (tab);\ typeof(elem) _elem = (elem);\ (void)sizeof(**_tab == _elem); /* check that types are compatible */\ - __dynarray_add((unsigned long **)_tab, nb_ptr, (unsigned long)_elem);\ + ff_dynarray_add((unsigned long **)_tab, nb_ptr, (unsigned long)_elem);\ } while(0) #else #define dynarray_add(tab, nb_ptr, elem)\ do {\ - __dynarray_add((unsigned long **)(tab), nb_ptr, (unsigned long)(elem));\ + ff_dynarray_add((unsigned long **)(tab), nb_ptr, (unsigned long)(elem));\ } while(0) #endif -static void url_split(char *proto, int proto_size, +time_t mktimegm(struct tm *tm); +struct tm *brktimegm(time_t secs, struct tm *tm); +const char *small_strptime(const char *p, const char *fmt, + struct tm *dt); + +struct in_addr; +int resolve_host(struct in_addr *sin_addr, const char *hostname); + +void url_split(char *proto, int proto_size, char *authorization, int authorization_size, char *hostname, int hostname_size, int *port_ptr, char *path, int path_size, - const char *url) + const char *url); + +// +// Part of libavformat/rtsp.c +// + +//#include +//#include /* for select() prototype */ +//#include "network.h" + +typedef struct RTSPStream { + URLContext *rtp_handle; /* RTP stream handle */ + RTPDemuxContext *rtp_ctx; /* RTP parse context */ + + int stream_index; /* corresponding stream index, if any. -1 if none (MPEG2TS case) */ + int interleaved_min, interleaved_max; /* interleave ids, if TCP transport */ + char control_url[1024]; /* url for this stream (from SDP) */ + + int sdp_port; /* port (from SDP content - not used in RTSP) */ + struct in_addr sdp_ip; /* IP address (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 */ + 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; + +static int rtsp_read_play(AVFormatContext *s); + +/* XXX: currently, the only way to change the protocols consists in + changing this variable */ + +static int rtsp_probe(AVProbeData *p) { - const char *p, *ls, *at, *col, *brk; - - if (port_ptr) *port_ptr = -1; - if (proto_size > 0) proto[0] = 0; - if (authorization_size > 0) authorization[0] = 0; - if (hostname_size > 0) hostname[0] = 0; - if (path_size > 0) path[0] = 0; - - /* parse protocol */ - if ((p = strchr(url, ':'))) { - av_strlcpy(proto, url, FFMIN(proto_size, p + 1 - url)); - p++; /* skip ':' */ - if (*p == '/') p++; - if (*p == '/') p++; - } else { - /* no protocol means plain filename */ - av_strlcpy(path, url, path_size); - return; - } - - /* separate path from hostname */ - ls = strchr(p, '/'); - if(!ls) - ls = strchr(p, '?'); - if(ls) - av_strlcpy(path, ls, path_size); - else - ls = &p[strlen(p)]; // XXX - - /* the rest is hostname, use that to parse auth/port */ - if (ls != p) { - /* authorization (user[:pass]@hostname) */ - if ((at = strchr(p, '@')) && at < ls) { - av_strlcpy(authorization, p, - FFMIN(authorization_size, at + 1 - p)); - p = at + 1; /* skip '@' */ - } - - if (*p == '[' && (brk = strchr(p, ']')) && brk < ls) { - /* [host]:port */ - av_strlcpy(hostname, p + 1, - FFMIN(hostname_size, brk - p)); - if (brk[1] == ':' && port_ptr) - *port_ptr = atoi(brk + 2); - } else if ((col = strchr(p, ':')) && col < ls) { - av_strlcpy(hostname, p, - FFMIN(col + 1 - p, hostname_size)); - if (port_ptr) *port_ptr = atoi(col + 1); - } else - av_strlcpy(hostname, p, - FFMIN(ls + 1 - p, hostname_size)); - } + if (av_strstart(p->filename, "rtsp:", NULL)) + return AVPROBE_SCORE_MAX; + return 0; } static int redir_isspace(int c) { - return (c == ' ' || c == '\t' || c == '\n' || c == '\r'); + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; } static void skip_spaces(const char **pp) @@ -224,8 +206,6 @@ static int sdp_parse_rtpmap(AVCodecContext *codec, RTSPStream *rtsp_st, int payl break; case CODEC_TYPE_VIDEO: av_log(codec, AV_LOG_DEBUG, " video codec set to : %s\n", c_name); - // Total hack here I suspect - codec->sample_rate = i; break; default: break; @@ -273,7 +253,7 @@ static void sdp_parse_fmtp_config(AVCodecContext *codec, char *attr, char *value if (!strcmp(attr, "config")) { /* decode the hexa encoded parameter */ int len = hex_to_data(NULL, value); - codec->extradata = (uint8_t *)av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE); + codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE); if (!codec->extradata) return; codec->extradata_size = len; @@ -314,7 +294,7 @@ static void sdp_parse_fmtp(AVStream *st, const char *p) char value[4096]; int i; - RTSPStream *rtsp_st = (RTSPStream *)st->priv_data; + RTSPStream *rtsp_st = st->priv_data; AVCodecContext *codec = st->codec; rtp_payload_data_t *rtp_payload_data = &rtsp_st->rtp_payload_data; @@ -370,7 +350,7 @@ typedef struct SDPParseState { static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, int letter, const char *buf) { - RTSPState *rt = (RTSPState *)s->priv_data; + RTSPState *rt = s->priv_data; char buf1[64], st_type[64]; const char *p; int codec_type, payload_type, i; @@ -406,7 +386,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, s1->default_ttl = ttl; } else { st = s->streams[s->nb_streams - 1]; - rtsp_st = (RTSPStream *)st->priv_data; + rtsp_st = st->priv_data; rtsp_st->sdp_ip = sdp_ip; rtsp_st->sdp_ttl = ttl; } @@ -430,7 +410,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, } else { return; } - rtsp_st = (RTSPStream *)av_mallocz(sizeof(RTSPStream)); + rtsp_st = av_mallocz(sizeof(RTSPStream)); if (!rtsp_st) return; rtsp_st->stream_index = -1; @@ -456,7 +436,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, return; st->priv_data = rtsp_st; rtsp_st->stream_index = st->index; - st->codec->codec_type = (CodecType)codec_type; + st->codec->codec_type = codec_type; if (rtsp_st->sdp_payload_type < RTP_PT_PRIVATE) { /* if standard payload type, we can find the codec right now */ rtp_get_codec_info(st->codec, rtsp_st->sdp_payload_type); @@ -470,7 +450,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, char proto[32]; /* get the control url */ st = s->streams[s->nb_streams - 1]; - rtsp_st = (RTSPStream *)st->priv_data; + rtsp_st = st->priv_data; /* XXX: may need to add full url resolution */ url_split(proto, sizeof(proto), NULL, 0, NULL, 0, NULL, NULL, 0, p); @@ -487,7 +467,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, payload_type = atoi(buf1); for(i = 0; i < s->nb_streams;i++) { st = s->streams[i]; - rtsp_st = (RTSPStream *)st->priv_data; + rtsp_st = st->priv_data; if (rtsp_st->sdp_payload_type == payload_type) { sdp_parse_rtpmap(st->codec, rtsp_st, payload_type, p); } @@ -498,7 +478,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, payload_type = atoi(buf1); for(i = 0; i < s->nb_streams;i++) { st = s->streams[i]; - rtsp_st = (RTSPStream *)st->priv_data; + rtsp_st = st->priv_data; 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)) { @@ -515,7 +495,7 @@ static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1, payload_type = atoi(buf1); for(i = 0; i < s->nb_streams;i++) { st = s->streams[i]; - rtsp_st = (RTSPStream *)st->priv_data; + rtsp_st = st->priv_data; if (rtsp_st->sdp_payload_type == payload_type) { if(rtsp_st->dynamic_handler && rtsp_st->dynamic_handler->parse_sdp_a_line) { rtsp_st->dynamic_handler->parse_sdp_a_line(st, rtsp_st->dynamic_protocol_context, buf); @@ -589,3 +569,434 @@ static void rtsp_parse_range(int *min_ptr, int *max_ptr, const char **pp) } *pp = p; } + +/* XXX: only one transport specification is parsed */ +static void rtsp_parse_transport(RTSPHeader *reply, const char *p) +{ + char transport_protocol[16]; + char profile[16]; + char lower_transport[16]; + char parameter[16]; + RTSPTransportField *th; + char buf[256]; + + reply->nb_transports = 0; + + for(;;) { + skip_spaces(&p); + if (*p == '\0') + break; + + th = &reply->transports[reply->nb_transports]; + + get_word_sep(transport_protocol, sizeof(transport_protocol), + "/", &p); + if (*p == '/') + p++; + if (!strcasecmp (transport_protocol, "rtp")) { + get_word_sep(profile, sizeof(profile), "/;,", &p); + lower_transport[0] = '\0'; + /* rtp/avp/ */ + if (*p == '/') { + p++; + get_word_sep(lower_transport, sizeof(lower_transport), + ";,", &p); + } + } else if (!strcasecmp (transport_protocol, "x-pn-tng")) { + /* x-pn-tng/ */ + get_word_sep(lower_transport, sizeof(lower_transport), "/;,", &p); + profile[0] = '\0'; + } + if (!strcasecmp(lower_transport, "TCP")) + th->protocol = RTSP_PROTOCOL_RTP_TCP; + else + th->protocol = RTSP_PROTOCOL_RTP_UDP; + + if (*p == ';') + p++; + /* get each parameter */ + while (*p != '\0' && *p != ',') { + get_word_sep(parameter, sizeof(parameter), "=;,", &p); + if (!strcmp(parameter, "port")) { + if (*p == '=') { + p++; + rtsp_parse_range(&th->port_min, &th->port_max, &p); + } + } else if (!strcmp(parameter, "client_port")) { + if (*p == '=') { + p++; + rtsp_parse_range(&th->client_port_min, + &th->client_port_max, &p); + } + } else if (!strcmp(parameter, "server_port")) { + if (*p == '=') { + p++; + rtsp_parse_range(&th->server_port_min, + &th->server_port_max, &p); + } + } else if (!strcmp(parameter, "interleaved")) { + if (*p == '=') { + p++; + rtsp_parse_range(&th->interleaved_min, + &th->interleaved_max, &p); + } + } else if (!strcmp(parameter, "multicast")) { + if (th->protocol == RTSP_PROTOCOL_RTP_UDP) + th->protocol = RTSP_PROTOCOL_RTP_UDP_MULTICAST; + } else if (!strcmp(parameter, "ttl")) { + if (*p == '=') { + p++; + th->ttl = strtol(p, (char **)&p, 10); + } + } else if (!strcmp(parameter, "destination")) { + struct in_addr ipaddr; + + if (*p == '=') { + p++; + get_word_sep(buf, sizeof(buf), ";,", &p); + if (inet_aton(buf, &ipaddr)) + th->destination = ntohl(ipaddr.s_addr); + } + } + while (*p != ';' && *p != '\0' && *p != ',') + p++; + if (*p == ';') + p++; + } + if (*p == ',') + p++; + + reply->nb_transports++; + } +} + +static int url_readbuf(URLContext *h, unsigned char *buf, int size) +{ + int ret, len; + + len = 0; + while (len < size) { + ret = url_read(h, buf+len, size-len); + if (ret < 1) + return ret; + len += ret; + } + return len; +} + +/* skip a RTP/TCP interleaved packet */ +static void rtsp_skip_packet(AVFormatContext *s) +{ + RTSPState *rt = s->priv_data; + int ret, len, len1; + uint8_t buf[1024]; + + ret = url_readbuf(rt->rtsp_hd, buf, 3); + if (ret != 3) + return; + len = AV_RB16(buf + 1); +#ifdef DEBUG + printf("skipping RTP packet len=%d\n", len); +#endif + /* skip payload */ + while (len > 0) { + len1 = len; + if (len1 > sizeof(buf)) + len1 = sizeof(buf); + ret = url_readbuf(rt->rtsp_hd, buf, len1); + if (ret != len1) + return; + len -= len1; + } +} + +static void rtsp_send_cmd(AVFormatContext *s, + const char *cmd, RTSPHeader *reply, + unsigned char **content_ptr) +{ + RTSPState *rt = s->priv_data; + char buf[4096], buf1[1024], *q; + unsigned char ch; + const char *p; + int content_length, line_count; + unsigned char *content = NULL; + + memset(reply, 0, sizeof(RTSPHeader)); + + rt->seq++; + av_strlcpy(buf, cmd, sizeof(buf)); + snprintf(buf1, sizeof(buf1), "CSeq: %d\r\n", rt->seq); + av_strlcat(buf, buf1, sizeof(buf)); + if (rt->session_id[0] != '\0' && !strstr(cmd, "\nIf-Match:")) { + snprintf(buf1, sizeof(buf1), "Session: %s\r\n", rt->session_id); + av_strlcat(buf, buf1, sizeof(buf)); + } + av_strlcat(buf, "\r\n", sizeof(buf)); +#ifdef DEBUG + printf("Sending:\n%s--\n", buf); +#endif + url_write(rt->rtsp_hd, buf, strlen(buf)); + + /* parse reply (XXX: use buffers) */ + line_count = 0; + rt->last_reply[0] = '\0'; + for(;;) { + q = buf; + for(;;) { + if (url_readbuf(rt->rtsp_hd, &ch, 1) != 1) + break; + if (ch == '\n') + break; + if (ch == '$') { + /* XXX: only parse it if first char on line ? */ + rtsp_skip_packet(s); + } else if (ch != '\r') { + if ((q - buf) < sizeof(buf) - 1) + *q++ = ch; + } + } + *q = '\0'; +#ifdef DEBUG + printf("line='%s'\n", buf); +#endif + /* test if last line */ + if (buf[0] == '\0') + break; + p = buf; + if (line_count == 0) { + /* get reply code */ + get_word(buf1, sizeof(buf1), &p); + get_word(buf1, sizeof(buf1), &p); + reply->status_code = atoi(buf1); + } else { + rtsp_parse_line(reply, p); + av_strlcat(rt->last_reply, p, sizeof(rt->last_reply)); + av_strlcat(rt->last_reply, "\n", sizeof(rt->last_reply)); + } + line_count++; + } + + if (rt->session_id[0] == '\0' && reply->session_id[0] != '\0') + av_strlcpy(rt->session_id, reply->session_id, sizeof(rt->session_id)); + + content_length = reply->content_length; + if (content_length > 0) { + /* leave some room for a trailing '\0' (useful for simple parsing) */ + content = av_malloc(content_length + 1); + (void)url_readbuf(rt->rtsp_hd, content, content_length); + content[content_length] = '\0'; + } + if (content_ptr) + *content_ptr = content; + else + av_free(content); +} + + +/* close and free RTSP streams */ +static void rtsp_close_streams(RTSPState *rt) +{ + int i; + RTSPStream *rtsp_st; + + for(i=0;inb_rtsp_streams;i++) { + rtsp_st = rt->rtsp_streams[i]; + if (rtsp_st) { + if (rtsp_st->rtp_ctx) + rtp_parse_close(rtsp_st->rtp_ctx); + if (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(rt->rtsp_streams); +} + +#ifdef CONFIG_RTSP_DEMUXER +AVInputFormat rtsp_demuxer = { + "rtsp", + NULL_IF_CONFIG_SMALL("RTSP input format"), + sizeof(RTSPState), + rtsp_probe, + rtsp_read_header, + rtsp_read_packet, + rtsp_read_close, + rtsp_read_seek, + .flags = AVFMT_NOFILE, + .read_play = rtsp_read_play, + .read_pause = rtsp_read_pause, +}; +#endif + +static int sdp_probe(AVProbeData *p1) +{ + const char *p = p1->buf, *p_end = p1->buf + p1->buf_size; + + /* we look for a line beginning "c=IN IP4" */ + while (p < p_end && *p != '\0') { + if (p + sizeof("c=IN IP4") - 1 < p_end && av_strstart(p, "c=IN IP4", NULL)) + return AVPROBE_SCORE_MAX / 2; + + while(p < p_end - 1 && *p != '\n') p++; + if (++p >= p_end) + break; + if (*p == '\r') + p++; + } + return 0; +} + +#define SDP_MAX_SIZE 8192 + +static int sdp_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + RTSPState *rt = s->priv_data; + RTSPStream *rtsp_st; + int size, i, err; + char *content; + char url[1024]; + AVStream *st; + + /* read the whole sdp file */ + /* XXX: better loading */ + content = av_malloc(SDP_MAX_SIZE); + size = get_buffer(s->pb, content, SDP_MAX_SIZE - 1); + if (size <= 0) { + av_free(content); + return AVERROR_INVALIDDATA; + } + content[size] ='\0'; + + sdp_parse(s, content); + av_free(content); + + /* open each RTP stream */ + for(i=0;inb_rtsp_streams;i++) { + rtsp_st = rt->rtsp_streams[i]; + + snprintf(url, sizeof(url), "rtp://%s:%d?localport=%d&ttl=%d", + inet_ntoa(rtsp_st->sdp_ip), + rtsp_st->sdp_port, + rtsp_st->sdp_port, + rtsp_st->sdp_ttl); + if (url_open(&rtsp_st->rtp_handle, url, URL_RDWR) < 0) { + err = AVERROR_INVALIDDATA; + goto fail; + } + /* open the RTP context */ + st = NULL; + if (rtsp_st->stream_index >= 0) + st = s->streams[rtsp_st->stream_index]; + if (!st) + s->ctx_flags |= AVFMTCTX_NOHEADER; + rtsp_st->rtp_ctx = rtp_parse_open(s, st, rtsp_st->rtp_handle, rtsp_st->sdp_payload_type, &rtsp_st->rtp_payload_data); + if (!rtsp_st->rtp_ctx) { + err = AVERROR(ENOMEM); + 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; + fail: + rtsp_close_streams(rt); + return err; +} + +static int sdp_read_packet(AVFormatContext *s, + AVPacket *pkt) +{ + return rtsp_read_packet(s, pkt); +} + +static int sdp_read_close(AVFormatContext *s) +{ + RTSPState *rt = s->priv_data; + rtsp_close_streams(rt); + return 0; +} + +#ifdef CONFIG_SDP_DEMUXER +AVInputFormat sdp_demuxer = { + "sdp", + NULL_IF_CONFIG_SMALL("SDP"), + sizeof(RTSPState), + sdp_probe, + sdp_read_header, + sdp_read_packet, + sdp_read_close, +}; +#endif + +#ifdef CONFIG_REDIR_DEMUXER +/* dummy redirector format (used directly in av_open_input_file now) */ +static int redir_probe(AVProbeData *pd) +{ + const char *p; + p = pd->buf; + while (redir_isspace(*p)) + p++; + if (av_strstart(p, "http://", NULL) || + av_strstart(p, "rtsp://", NULL)) + return AVPROBE_SCORE_MAX; + return 0; +} + +static int redir_read_header(AVFormatContext *s, AVFormatParameters *ap) +{ + char buf[4096], *q; + int c; + AVFormatContext *ic = NULL; + ByteIOContext *f = s->pb; + + /* parse each URL and try to open it */ + c = url_fgetc(f); + while (c != URL_EOF) { + /* skip spaces */ + for(;;) { + if (!redir_isspace(c)) + break; + c = url_fgetc(f); + } + if (c == URL_EOF) + break; + /* record url */ + q = buf; + for(;;) { + if (c == URL_EOF || redir_isspace(c)) + break; + if ((q - buf) < sizeof(buf) - 1) + *q++ = c; + c = url_fgetc(f); + } + *q = '\0'; + //printf("URL='%s'\n", buf); + /* try to open the media file */ + if (av_open_input_file(&ic, buf, NULL, 0, NULL) == 0) + break; + } + if (!ic) + return AVERROR(EIO); + + *s = *ic; + url_fclose(f); + + return 0; +} + +AVInputFormat redir_demuxer = { + "redir", + NULL_IF_CONFIG_SMALL("Redirector format"), + 0, + redir_probe, + redir_read_header, + NULL, + NULL, +}; + +#endif diff --git a/src/zm_sdp.h b/src/zm_sdp.h index 1dddf7725..8568eb2ea 100644 --- a/src/zm_sdp.h +++ b/src/zm_sdp.h @@ -1,23 +1,93 @@ #ifndef ZM_SDP_H #define ZM_SDP_H -#include -#include -#include -#include +#include "config.h" +#include "zm_ffmpeg.h" -#include +#if HAVE_LIBAVFORMAT_AVFORMAT_H +#include +#elif HAVE_FFMPEG_AVFORMAT_H +#include +#else +#error "No location for rtsp.h found" +#endif -extern "C" +// +// This file contains chunks of ffmpeg code that are not currently exported. +// The main thing we are after is the sdp parser +// + +// +// Part of libavformat/rtp.h +// + +#define RTP_MIN_PACKET_LENGTH 12 +#define RTP_MAX_PACKET_LENGTH 1500 /* XXX: suppress this define */ + +int rtp_get_codec_info(AVCodecContext *codec, int payload_type); + +/** return < 0 if unknown payload type */ +int rtp_get_payload_type(AVCodecContext *codec); + +typedef struct RTPDemuxContext RTPDemuxContext; +typedef struct rtp_payload_data_s rtp_payload_data_s; +RTPDemuxContext *rtp_parse_open(AVFormatContext *s1, AVStream *st, URLContext *rtpc, int payload_type, rtp_payload_data_s *rtp_payload_data); +int rtp_parse_packet(RTPDemuxContext *s, AVPacket *pkt, + const uint8_t *buf, int len); +void rtp_parse_close(RTPDemuxContext *s); + +int rtp_get_local_port(URLContext *h); +int rtp_set_remote_url(URLContext *h, const char *uri); +void rtp_get_file_handles(URLContext *h, int *prtp_fd, int *prtcp_fd); + +/** + * some rtp servers assume client is dead if they don't hear from them... + * so we send a Receiver Report to the provided ByteIO context + * (we don't have access to the rtcp handle from here) + */ +int rtp_check_and_send_back_rr(RTPDemuxContext *s, int count); + +#define RTP_PT_PRIVATE 96 +#define RTP_VERSION 2 +#define RTP_MAX_SDES 256 /**< maximum text length for SDES */ + +/* RTCP paquets use 0.5 % of the bandwidth */ +#define RTCP_TX_RATIO_NUM 5 +#define RTCP_TX_RATIO_DEN 1000 + +/** Structure listing useful vars to parse RTP packet payload*/ +typedef struct rtp_payload_data_s { -//#include "network.h" -#include "ffmpeg/avstring.h" -#include "ffmpeg/rtsp.h" + int sizelength; + int indexlength; + int indexdeltalength; + int profile_level_id; + int streamtype; + int objecttype; + char *mode; -//#include -//#include "avcodec.h" -#include "ffmpeg/rtp.h" -} + /** mpeg 4 AU headers */ + struct AUHeaders { + int size; + int index; + int cts_flag; + int cts; + int dts_flag; + int dts; + int rap_flag; + int streamstate; + } *au_headers; + int nb_au_headers; + int au_headers_length_bytes; + int cur_au_index; +} rtp_payload_data_t; + +// +// Part of libavformat/rtp_internal.h +// + +#include +//#include "rtp.h" // these statistics are used for rtcp receiver reports... typedef struct { @@ -111,6 +181,24 @@ struct RTPDemuxContext { extern RTPDynamicProtocolHandler *RTPFirstDynamicPayloadHandler; +int rtsp_next_attr_and_value(const char **p, char *attr, int attr_size, char *value, int value_size); ///< from rtsp.c, but used by rtp dynamic protocol handlers. + +void ff_rtp_send_data(AVFormatContext *s1, const uint8_t *buf1, int len, int m); +const char *ff_rtp_enc_name(int payload_type); +enum CodecID ff_rtp_codec_id(const char *buf, enum CodecType codec_type); + +void av_register_rtp_dynamic_payload_handlers(void); + +// // +// Part of libavformat/rtsp.c +// // + +#include +#include /* for select() prototype */ + +//#define DEBUG +//#define DEBUG_RTP_TCP + enum RTSPClientState { RTSP_STATE_IDLE, RTSP_STATE_PLAYING, @@ -134,35 +222,10 @@ typedef struct RTSPState { RTPDemuxContext *cur_rtp; } RTSPState; -typedef struct RTSPStream { - URLContext *rtp_handle; /* RTP stream handle */ - RTPDemuxContext *rtp_ctx; /* RTP parse context */ - - int stream_index; /* corresponding stream index, if any. -1 if none (MPEG2TS case) */ - int interleaved_min, interleaved_max; /* interleave ids, if TCP transport */ - char control_url[1024]; /* url for this stream (from SDP) */ - - int sdp_port; /* port (from SDP content - not used in RTSP) */ - struct in_addr sdp_ip; /* IP address (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 */ - 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; - -extern "C" -{ -int rtsp_next_attr_and_value(const char **p, char *attr, int attr_size, char *value, int value_size); ///< from rtsp.c, but used by rtp dynamic protocol handlers. - -void ff_rtp_send_data(AVFormatContext *s1, const uint8_t *buf1, int len, int m); -const char *ff_rtp_enc_name(int payload_type); -enum CodecID ff_rtp_codec_id(const char *buf, enum CodecType codec_type); - -void av_register_rtp_dynamic_payload_handlers(void); -} +// +// Declaration from libavformat/rtsp.c +// int sdp_parse(AVFormatContext *s, const char *content); -#endif //ZM_SDP_H +#endif // ZM_SDP_H diff --git a/src/zm_thread.cpp b/src/zm_thread.cpp index 1f0fe09a5..01c59fac7 100644 --- a/src/zm_thread.cpp +++ b/src/zm_thread.cpp @@ -22,6 +22,7 @@ #include "zm_debug.h" #include "zm_utils.h" +#include #include #include