diff --git a/include/curl/curl.h b/include/curl/curl.h index e635968..8021366 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -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 diff --git a/lib/curl_rtmp.c b/lib/curl_rtmp.c new file mode 100644 index 0000000..a73a490 --- /dev/null +++ b/lib/curl_rtmp.c @@ -0,0 +1,267 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2010, Howard Chu, + * + * 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" + +#ifndef CURL_DISABLE_RTMP + +#include "urldata.h" +#include +#include + +#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 (!conn) + 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 = rtmp_recv; + conn->send = 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 /* CURL_DISABLE_RTMP */ diff --git a/lib/curl_rtmp.h b/lib/curl_rtmp.h new file mode 100644 index 0000000..35776aa --- /dev/null +++ b/lib/curl_rtmp.h @@ -0,0 +1,34 @@ +#ifndef __CURL_RTMP_H +#define __CURL_RTMP_H + +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2010, Howard Chu, + * + * 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 */ diff --git a/lib/url.c b/lib/url.c index 7975656..1d4be37 100644 --- 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 @@ -231,6 +232,15 @@ static const struct Curl_handler * const protocols[] = { &Curl_handler_rtsp, #endif +#ifndef CURL_DISABLE_RTMP + &Curl_handler_rtmp, + &Curl_handler_rtmpt, + &Curl_handler_rtmpe, + &Curl_handler_rtmpte, + &Curl_handler_rtmps, + &Curl_handler_rtmpts, +#endif + (struct Curl_handler *) NULL }; diff --git a/lib/urldata.h b/lib/urldata.h index efc56d7..06494f9 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -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 */