Commit 04cb15ae authored by Howard Chu's avatar Howard Chu Committed by Daniel Stenberg
Browse files

RTMP: initial support added, powered by librtmp

librtmp is found at http://rtmpdump.mplayerhq.hu/
parent bc8fc980
Loading
Loading
Loading
Loading
+78 −0
Original line number Diff line number Diff line
@@ -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}
])

+6 −0
Original line number Diff line number Diff line
@@ -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
+2 −2
Original line number Diff line number Diff line
@@ -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

lib/curl_rtmp.c

0 → 100644
+267 −0
Original line number Diff line number Diff line
/***************************************************************************
 *                      _   _ ____  _
 *  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 */

lib/curl_rtmp.h

0 → 100644
+34 −0
Original line number Diff line number Diff line
#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 */
Loading