]> granicus.if.org Git - curl/commitdiff
RTMP: initial support added, powered by librtmp
authorHoward Chu <hyc@highlandsun.com>
Wed, 12 May 2010 21:07:20 +0000 (23:07 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 12 May 2010 21:07:20 +0000 (23:07 +0200)
librtmp is found at http://rtmpdump.mplayerhq.hu/

configure.ac
include/curl/curl.h
lib/Makefile.inc
lib/curl_rtmp.c [new file with mode: 0644]
lib/curl_rtmp.h [new file with mode: 0644]
lib/url.c
lib/urldata.h

index 5983d4a5a7ea87b4d5fee776ac2264eaa61e33dc..f937495a3de2f20d7e65ebd076d80062aa4446ed 100644 (file)
@@ -136,6 +136,7 @@ curl_verbose_msg="enabled (--disable-verbose)"
    curl_ldap_msg="no      (--enable-ldap / --with-ldap-lib / --with-lber-lib)"
   curl_ldaps_msg="no      (--enable-ldaps)"
    curl_rtsp_msg="no      (--enable-rtsp)"
+   curl_rtmp_msg="no      (--with-librtmp)"
 
 dnl
 dnl Save anything in $LIBS for later
@@ -1992,6 +1993,79 @@ if test X"$OPT_LIBSSH2" != Xno; then
   fi
 fi
 
+dnl **********************************************************************
+dnl Check for the presence of LIBRTMP libraries and headers
+dnl **********************************************************************
+
+dnl Default to compiler & linker defaults for LIBRTMP files & libraries.
+OPT_LIBRTMP=off
+AC_ARG_WITH(librtmp,dnl
+AC_HELP_STRING([--with-librtmp=PATH],[Where to look for librtmp, PATH points to the LIBRTMP installation (default: /usr/local/lib); when possible, set the PKG_CONFIG_PATH environment variable instead of using this option])
+AC_HELP_STRING([--without-librtmp], [disable LIBRTMP]),
+  OPT_LIBRTMP=$withval)
+
+if test X"$OPT_LIBRTMP" != Xno; then
+  dnl backup the pre-librtmp variables
+  CLEANLDFLAGS="$LDFLAGS"
+  CLEANCPPFLAGS="$CPPFLAGS"
+  CLEANLIBS="$LIBS"
+
+  case "$OPT_LIBRTMP" in
+  yes)
+    dnl --with-librtmp (without path) used
+    CURL_CHECK_PKGCONFIG(librtmp)
+
+    if test "$PKGCONFIG" != "no" ; then
+      LIB_RTMP=`$PKGCONFIG --libs-only-l librtmp`
+      LD_RTMP=`$PKGCONFIG --libs-only-L librtmp`
+      CPP_RTMP=`$PKGCONFIG --cflags-only-I librtmp`
+      version=`$PKGCONFIG --modversion librtmp`
+      DIR_RTMP=`echo $LD_RTMP | $SED -e 's/-L//'`
+    fi
+
+    ;;
+  off)
+    dnl no --with-librtmp option given, just check default places
+    ;;
+  *)
+    dnl use the given --with-librtmp spot
+    PREFIX_RTMP=$OPT_LIBRTMP
+    ;;
+  esac
+
+  dnl if given with a prefix, we set -L and -I based on that
+  if test -n "$PREFIX_RTMP"; then
+    LD_RTMP=-L${PREFIX_RTMP}/lib$libsuff
+    CPP_RTMP=-I${PREFIX_RTMP}/include
+    DIR_RTMP=${PREFIX_RTMP}/lib$libsuff
+  fi
+
+  LDFLAGS="$LDFLAGS $LD_RTMP"
+  CPPFLAGS="$CPPFLAGS $CPP_RTMP"
+  LIBS="$LIBS $LIB_RTMP"
+
+  AC_CHECK_LIB(rtmp, RTMP_Init)
+
+  AC_CHECK_HEADERS(librtmp/rtmp.h,
+    curl_rtmp_msg="enabled (librtmp)"
+    LIBRTMP_ENABLED=1
+    AC_DEFINE(USE_LIBRTMP, 1, [if librtmp is in use])
+    AC_SUBST(USE_LIBRTMP, [1])
+  )
+
+  if test X"$OPT_LIBRTMP" != Xoff &&
+     test "$LIBRTMP_ENABLED" != "1"; then
+    AC_MSG_ERROR([librtmp libs and/or directories were not found where specified!])
+  fi
+
+  if test "$LIBRTMP_ENABLED" != "1"; then
+    dnl no librtmp, revert back to clean variables
+    LDFLAGS=$CLEANLDFLAGS
+    CPPFLAGS=$CLEANCPPFLAGS
+    LIBS=$CLEANLIBS
+  fi
+fi
+
 dnl **********************************************************************
 dnl Check for the presence of IDN libraries and headers
 dnl **********************************************************************
@@ -2664,6 +2738,9 @@ fi
 if test "x$CURL_DISABLE_RTSP" != "x1"; then
   SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS RTSP"
 fi
+if test "x$USE_LIBRTMP" = "x1"; then
+  SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS RTMP RTMPT RTMPE RTMPTE RTMPS RTMPTS"
+fi
 
 dnl replace spaces with newlines
 dnl sort the lines
@@ -2747,6 +2824,7 @@ AC_MSG_NOTICE([Configured to build curl/libcurl:
   LDAP support:    ${curl_ldap_msg}
   LDAPS support:   ${curl_ldaps_msg}
   RTSP support:    ${curl_rtsp_msg}
+  RTMP support:    ${curl_rtmp_msg}
   Protocols:       ${SUPPORT_PROTOCOLS}
 ])
 
index e63596828a4b1cae4c9d3bee1a02aad1a18af347..802136690794f5c419688e3f975054dc4dec2c6a 100644 (file)
@@ -623,6 +623,12 @@ typedef enum {
 #define CURLPROTO_SMTP   (1<<16)
 #define CURLPROTO_SMTPS  (1<<17)
 #define CURLPROTO_RTSP   (1<<18)
+#define CURLPROTO_RTMP   (1<<19)
+#define CURLPROTO_RTMPT  (1<<20)
+#define CURLPROTO_RTMPE  (1<<21)
+#define CURLPROTO_RTMPTE (1<<22)
+#define CURLPROTO_RTMPS  (1<<23)
+#define CURLPROTO_RTMPTS (1<<24)
 #define CURLPROTO_ALL    (~0) /* enable everything */
 
 /* long may be 32 or 64 bits, but we should never depend on anything else
index e35e8bb574fee1e218fb670899dd03cd9dfa986d..e0c091f4ab72b66175ca78d4e38b0f16a07eb4eb 100644 (file)
@@ -12,7 +12,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c   \
   strdup.c socks.c ssh.c nss.c qssl.c rawstr.c curl_addrinfo.c          \
   socks_gssapi.c socks_sspi.c curl_sspi.c slist.c nonblock.c           \
   curl_memrchr.c imap.c pop3.c smtp.c pingpong.c rtsp.c curl_threads.c \
-  warnless.c hmac.c polarssl.c
+  warnless.c hmac.c polarssl.c curl_rtmp.c
 
 HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h      \
   progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h     \
@@ -25,4 +25,4 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h     \
   tftp.h sockaddr.h splay.h strdup.h setup_once.h socks.h ssh.h nssg.h \
   curl_base64.h rawstr.h curl_addrinfo.h curl_sspi.h slist.h nonblock.h        \
   curl_memrchr.h imap.h pop3.h smtp.h pingpong.h rtsp.h curl_threads.h \
-  warnless.h curl_hmac.h polarssl.h
+  warnless.h curl_hmac.h polarssl.h curl_rtmp.h
diff --git a/lib/curl_rtmp.c b/lib/curl_rtmp.c
new file mode 100644 (file)
index 0000000..4700719
--- /dev/null
@@ -0,0 +1,267 @@
+/***************************************************************************
+ *                      _   _ ____  _
+ *  Project         ___| | | |  _ \| |
+ *                 / __| | | | |_) | |
+ *                | (__| |_| |  _ <| |___
+ *                 \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2010, Howard Chu, <hyc@highlandsun.com>
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifdef USE_LIBRTMP
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include <librtmp/rtmp.h>
+
+#ifdef _WIN32
+#define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e)
+#define SET_RCVTIMEO(tv,s)   int tv = s*1000
+#else
+#define SET_RCVTIMEO(tv,s)   struct timeval tv = {s,0}
+#endif
+
+#define DEF_BUFTIME    (2*60*60*1000)    /* 2 hours */
+
+static CURLcode rtmp_setup(struct connectdata *conn);
+static CURLcode rtmp_do(struct connectdata *conn, bool *done);
+static CURLcode rtmp_done(struct connectdata *conn, CURLcode, bool premature);
+static CURLcode rtmp_connect(struct connectdata *conn, bool *done);
+static CURLcode rtmp_disconnect(struct connectdata *conn);
+
+static Curl_recv rtmp_recv;
+static Curl_send rtmp_send;
+
+/*
+ * RTMP protocol handler.h, based on http://rtmpdump.mplayerhq.hu
+ */
+
+const struct Curl_handler Curl_handler_rtmp = {
+  "RTMP",                               /* scheme */
+  rtmp_setup,                           /* setup_connection */
+  rtmp_do,                              /* do_it */
+  rtmp_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  rtmp_connect,                         /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  rtmp_disconnect,                      /* disconnect */
+  PORT_RTMP,                            /* defport */
+  PROT_RTMP                             /* protocol */
+};
+
+const struct Curl_handler Curl_handler_rtmpt = {
+  "RTMPT",                              /* scheme */
+  rtmp_setup,                           /* setup_connection */
+  rtmp_do,                              /* do_it */
+  rtmp_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  rtmp_connect,                         /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  rtmp_disconnect,                      /* disconnect */
+  PORT_RTMPT,                           /* defport */
+  PROT_RTMPT                            /* protocol */
+};
+
+const struct Curl_handler Curl_handler_rtmpe = {
+  "RTMPE",                              /* scheme */
+  rtmp_setup,                           /* setup_connection */
+  rtmp_do,                              /* do_it */
+  rtmp_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  rtmp_connect,                         /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  rtmp_disconnect,                      /* disconnect */
+  PORT_RTMP,                            /* defport */
+  PROT_RTMPE                            /* protocol */
+};
+
+const struct Curl_handler Curl_handler_rtmpte = {
+  "RTMPTE",                             /* scheme */
+  rtmp_setup,                           /* setup_connection */
+  rtmp_do,                              /* do_it */
+  rtmp_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  rtmp_connect,                         /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  rtmp_disconnect,                      /* disconnect */
+  PORT_RTMPT,                           /* defport */
+  PROT_RTMPTE                           /* protocol */
+};
+
+const struct Curl_handler Curl_handler_rtmps = {
+  "RTMPS",                              /* scheme */
+  rtmp_setup,                           /* setup_connection */
+  rtmp_do,                              /* do_it */
+  rtmp_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  rtmp_connect,                         /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  rtmp_disconnect,                      /* disconnect */
+  PORT_RTMPS,                           /* defport */
+  PROT_RTMPS                            /* protocol */
+};
+const struct Curl_handler Curl_handler_rtmpts = {
+  "RTMPTS",                             /* scheme */
+  rtmp_setup,                           /* setup_connection */
+  rtmp_do,                              /* do_it */
+  rtmp_done,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  rtmp_connect,                         /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  rtmp_disconnect,                      /* disconnect */
+  PORT_RTMPS,                           /* defport */
+  PROT_RTMPTS                           /* protocol */
+};
+
+static CURLcode rtmp_setup(struct connectdata *conn)
+{
+  int rc;
+  RTMP *r = RTMP_Alloc();
+
+  if (!r)
+    return CURLE_OUT_OF_MEMORY;
+
+  RTMP_Init(r);
+  RTMP_SetBufferMS(r, DEF_BUFTIME);
+  if (!RTMP_SetupURL(r, conn->data->change.url)) {
+    RTMP_Free(r);
+    return CURLE_URL_MALFORMAT;
+  }
+  conn->proto.generic = r;
+  return CURLE_OK;
+}
+
+static CURLcode rtmp_connect(struct connectdata *conn, bool *done)
+{
+  RTMP *r = conn->proto.generic;
+  SET_RCVTIMEO(tv,10);
+
+  r->m_sb.sb_socket = conn->sock[FIRSTSOCKET];
+
+  /* We have to know if it's a write before we send the
+   * connect request packet
+   */
+  if (conn->data->set.upload)
+    r->Link.protocol |= RTMP_FEATURE_WRITE;
+
+  /* For plain streams, use the buffer toggle trick to keep data flowing */
+  if (!(r->Link.lFlags & RTMP_LF_LIVE) && !(r->Link.protocol & RTMP_FEATURE_HTTP))
+    r->Link.lFlags |= RTMP_LF_BUFX;
+
+  curlx_nonblock(r->m_sb.sb_socket, FALSE);
+  setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
+
+  if (!RTMP_Connect1(r, NULL))
+    return CURLE_FAILED_INIT;
+
+  /* Clients must send a periodic BytesReceived report to the server */
+  r->m_bSendCounter = true;
+
+  *done = TRUE;
+  conn->recv[FIRSTSOCKET] = rtmp_recv;
+  conn->send[FIRSTSOCKET] = rtmp_send;
+  return CURLE_OK;
+}
+
+static CURLcode rtmp_do(struct connectdata *conn, bool *done)
+{
+  RTMP *r = conn->proto.generic;
+
+  if (!RTMP_ConnectStream(r, 0))
+    return CURLE_FAILED_INIT;
+
+  if (conn->data->set.upload) {
+    Curl_pgrsSetUploadSize(conn->data, conn->data->set.infilesize);
+    Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL);
+  } else
+    Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL);
+  *done = TRUE;
+  return CURLE_OK;
+}
+
+static CURLcode rtmp_done(struct connectdata *conn, CURLcode status,
+                          bool premature)
+{
+  return CURLE_OK;
+}
+
+static CURLcode rtmp_disconnect(struct connectdata *conn)
+{
+  RTMP *r = conn->proto.generic;
+  if (r) {
+    conn->proto.generic = NULL;
+    RTMP_Close(r);
+    RTMP_Free(r);
+  }
+  return CURLE_OK;
+}
+
+static ssize_t rtmp_recv(struct connectdata *conn, int sockindex, char *buf,
+                         size_t len, CURLcode *err)
+{
+  RTMP *r = conn->proto.generic;
+  ssize_t nread;
+
+  nread = RTMP_Read(r, buf, len);
+  if (nread < 0) {
+    if (r->m_read.status == RTMP_READ_COMPLETE ||
+        r->m_read.status == RTMP_READ_EOF) {
+      conn->data->req.size = conn->data->req.bytecount;
+      nread = 0;
+    } else
+      *err = CURLE_RECV_ERROR;
+  }
+  return nread;
+}
+
+static ssize_t rtmp_send(struct connectdata *conn, int sockindex,
+                         const void *buf, size_t len, CURLcode *err)
+{
+  RTMP *r = conn->proto.generic;
+  ssize_t num;
+
+  num = RTMP_Write(r, (char *)buf, len);
+  if (num < 0) {
+    *err = CURLE_SEND_ERROR;
+  }
+  return num;
+}
+#endif  /* USE_LIBRTMP */
diff --git a/lib/curl_rtmp.h b/lib/curl_rtmp.h
new file mode 100644 (file)
index 0000000..35776aa
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef __CURL_RTMP_H
+#define __CURL_RTMP_H
+
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2010, Howard Chu, <hyc@highlandsun.com>
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+#ifndef CURL_DISABLE_RTMP
+extern const struct Curl_handler Curl_handler_rtmp;
+extern const struct Curl_handler Curl_handler_rtmpt;
+extern const struct Curl_handler Curl_handler_rtmpe;
+extern const struct Curl_handler Curl_handler_rtmpte;
+extern const struct Curl_handler Curl_handler_rtmps;
+extern const struct Curl_handler Curl_handler_rtmpts;
+#endif
+
+#endif /* __CURL_RTMP_H */
index ec7f46a3e636841825ed0dd2a3a6506532508450..1db65cab77617514b47e681a140fe821c9a11bd6 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -137,6 +137,7 @@ void idn_free (void *ptr); /* prototype from idn-free.h, not provided by
 #include "http_ntlm.h"
 #include "socks.h"
 #include "rtsp.h"
+#include "curl_rtmp.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -226,6 +227,15 @@ static const struct Curl_handler * const protocols[] = {
   &Curl_handler_rtsp,
 #endif
 
+#ifdef USE_LIBRTMP
+  &Curl_handler_rtmp,
+  &Curl_handler_rtmpt,
+  &Curl_handler_rtmpe,
+  &Curl_handler_rtmpte,
+  &Curl_handler_rtmps,
+  &Curl_handler_rtmpts,
+#endif
+
   (struct Curl_handler *) NULL
 };
 
index 30782c2a2b4fda1a07deaa461fecb00b6dae27a8..94e904fe5d1aff629683d97c405645f0bf6d7417 100644 (file)
@@ -43,6 +43,9 @@
 #define PORT_SMTP 25
 #define PORT_SMTPS 465 /* sometimes called SSMTP */
 #define PORT_RTSP 554
+#define PORT_RTMP 1935
+#define PORT_RTMPT PORT_HTTP
+#define PORT_RTMPS PORT_HTTPS
 
 #define DICT_MATCH "/MATCH:"
 #define DICT_MATCH2 "/M:"
@@ -706,13 +709,19 @@ struct connectdata {
 #define PROT_SMTP    CURLPROTO_SMTP
 #define PROT_SMTPS   CURLPROTO_SMTPS
 #define PROT_RTSP    CURLPROTO_RTSP
-
-/* (1<<18) is currently the highest used bit in the public bitmask. We make
+#define PROT_RTMP    CURLPROTO_RTMP
+#define PROT_RTMPT   CURLPROTO_RTMPT
+#define PROT_RTMPE   CURLPROTO_RTMPE
+#define PROT_RTMPTE  CURLPROTO_RTMPTE
+#define PROT_RTMPS   CURLPROTO_RTMPS
+#define PROT_RTMPTS  CURLPROTO_RTMPTS
+
+/* (1<<24) is currently the highest used bit in the public bitmask. We make
    sure we use "private bits" above the public ones to make things easier. */
 
-#define PROT_EXTMASK 0xfffff
+#define PROT_EXTMASK 0xffffff
 
-#define PROT_SSL     (1<<25) /* protocol requires SSL */
+#define PROT_SSL     (1<<29) /* protocol requires SSL */
 
 /* these ones need action before socket close */
 #define PROT_CLOSEACTION (PROT_FTP | PROT_IMAP | PROT_POP3)
@@ -864,6 +873,7 @@ struct connectdata {
     struct pop3_conn pop3c;
     struct smtp_conn smtpc;
     struct rtsp_conn rtspc;
+    void *generic;
   } proto;
 
   int cselect_bits; /* bitmask of socket events */