1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2010, Howard Chu, <hyc@highlandsun.com> 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at http://curl.haxx.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 ***************************************************************************/ 22 23#include "setup.h" 24 25#ifdef USE_LIBRTMP 26 27#include "urldata.h" 28#include "nonblock.h" /* for curlx_nonblock */ 29#include "progress.h" /* for Curl_pgrsSetUploadSize */ 30#include "transfer.h" 31#include <curl/curl.h> 32#include <librtmp/rtmp.h> 33 34#define _MPRINTF_REPLACE /* use our functions only */ 35#include <curl/mprintf.h> 36 37#include "curl_memory.h" 38/* The last #include file should be: */ 39#include "memdebug.h" 40 41#ifdef _WIN32 42#define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e) 43#define SET_RCVTIMEO(tv,s) int tv = s*1000 44#else 45#define SET_RCVTIMEO(tv,s) struct timeval tv = {s,0} 46#endif 47 48#define DEF_BUFTIME (2*60*60*1000) /* 2 hours */ 49 50static CURLcode rtmp_setup(struct connectdata *conn); 51static CURLcode rtmp_do(struct connectdata *conn, bool *done); 52static CURLcode rtmp_done(struct connectdata *conn, CURLcode, bool premature); 53static CURLcode rtmp_connect(struct connectdata *conn, bool *done); 54static CURLcode rtmp_disconnect(struct connectdata *conn, bool dead); 55 56static Curl_recv rtmp_recv; 57static Curl_send rtmp_send; 58 59/* 60 * RTMP protocol handler.h, based on http://rtmpdump.mplayerhq.hu 61 */ 62 63const struct Curl_handler Curl_handler_rtmp = { 64 "RTMP", /* scheme */ 65 rtmp_setup, /* setup_connection */ 66 rtmp_do, /* do_it */ 67 rtmp_done, /* done */ 68 ZERO_NULL, /* do_more */ 69 rtmp_connect, /* connect_it */ 70 ZERO_NULL, /* connecting */ 71 ZERO_NULL, /* doing */ 72 ZERO_NULL, /* proto_getsock */ 73 ZERO_NULL, /* doing_getsock */ 74 ZERO_NULL, /* domore_getsock */ 75 ZERO_NULL, /* perform_getsock */ 76 rtmp_disconnect, /* disconnect */ 77 ZERO_NULL, /* readwrite */ 78 PORT_RTMP, /* defport */ 79 CURLPROTO_RTMP, /* protocol */ 80 PROTOPT_NONE /* flags*/ 81}; 82 83const struct Curl_handler Curl_handler_rtmpt = { 84 "RTMPT", /* scheme */ 85 rtmp_setup, /* setup_connection */ 86 rtmp_do, /* do_it */ 87 rtmp_done, /* done */ 88 ZERO_NULL, /* do_more */ 89 rtmp_connect, /* connect_it */ 90 ZERO_NULL, /* connecting */ 91 ZERO_NULL, /* doing */ 92 ZERO_NULL, /* proto_getsock */ 93 ZERO_NULL, /* doing_getsock */ 94 ZERO_NULL, /* domore_getsock */ 95 ZERO_NULL, /* perform_getsock */ 96 rtmp_disconnect, /* disconnect */ 97 ZERO_NULL, /* readwrite */ 98 PORT_RTMPT, /* defport */ 99 CURLPROTO_RTMPT, /* protocol */ 100 PROTOPT_NONE /* flags*/ 101}; 102 103const struct Curl_handler Curl_handler_rtmpe = { 104 "RTMPE", /* scheme */ 105 rtmp_setup, /* setup_connection */ 106 rtmp_do, /* do_it */ 107 rtmp_done, /* done */ 108 ZERO_NULL, /* do_more */ 109 rtmp_connect, /* connect_it */ 110 ZERO_NULL, /* connecting */ 111 ZERO_NULL, /* doing */ 112 ZERO_NULL, /* proto_getsock */ 113 ZERO_NULL, /* doing_getsock */ 114 ZERO_NULL, /* domore_getsock */ 115 ZERO_NULL, /* perform_getsock */ 116 rtmp_disconnect, /* disconnect */ 117 ZERO_NULL, /* readwrite */ 118 PORT_RTMP, /* defport */ 119 CURLPROTO_RTMPE, /* protocol */ 120 PROTOPT_NONE /* flags*/ 121}; 122 123const struct Curl_handler Curl_handler_rtmpte = { 124 "RTMPTE", /* scheme */ 125 rtmp_setup, /* setup_connection */ 126 rtmp_do, /* do_it */ 127 rtmp_done, /* done */ 128 ZERO_NULL, /* do_more */ 129 rtmp_connect, /* connect_it */ 130 ZERO_NULL, /* connecting */ 131 ZERO_NULL, /* doing */ 132 ZERO_NULL, /* proto_getsock */ 133 ZERO_NULL, /* doing_getsock */ 134 ZERO_NULL, /* domore_getsock */ 135 ZERO_NULL, /* perform_getsock */ 136 rtmp_disconnect, /* disconnect */ 137 ZERO_NULL, /* readwrite */ 138 PORT_RTMPT, /* defport */ 139 CURLPROTO_RTMPTE, /* protocol */ 140 PROTOPT_NONE /* flags*/ 141}; 142 143const struct Curl_handler Curl_handler_rtmps = { 144 "RTMPS", /* scheme */ 145 rtmp_setup, /* setup_connection */ 146 rtmp_do, /* do_it */ 147 rtmp_done, /* done */ 148 ZERO_NULL, /* do_more */ 149 rtmp_connect, /* connect_it */ 150 ZERO_NULL, /* connecting */ 151 ZERO_NULL, /* doing */ 152 ZERO_NULL, /* proto_getsock */ 153 ZERO_NULL, /* doing_getsock */ 154 ZERO_NULL, /* domore_getsock */ 155 ZERO_NULL, /* perform_getsock */ 156 rtmp_disconnect, /* disconnect */ 157 ZERO_NULL, /* readwrite */ 158 PORT_RTMPS, /* defport */ 159 CURLPROTO_RTMPS, /* protocol */ 160 PROTOPT_NONE /* flags*/ 161}; 162 163const struct Curl_handler Curl_handler_rtmpts = { 164 "RTMPTS", /* scheme */ 165 rtmp_setup, /* setup_connection */ 166 rtmp_do, /* do_it */ 167 rtmp_done, /* done */ 168 ZERO_NULL, /* do_more */ 169 rtmp_connect, /* connect_it */ 170 ZERO_NULL, /* connecting */ 171 ZERO_NULL, /* doing */ 172 ZERO_NULL, /* proto_getsock */ 173 ZERO_NULL, /* doing_getsock */ 174 ZERO_NULL, /* domore_getsock */ 175 ZERO_NULL, /* perform_getsock */ 176 rtmp_disconnect, /* disconnect */ 177 ZERO_NULL, /* readwrite */ 178 PORT_RTMPS, /* defport */ 179 CURLPROTO_RTMPTS, /* protocol */ 180 PROTOPT_NONE /* flags*/ 181}; 182 183static CURLcode rtmp_setup(struct connectdata *conn) 184{ 185 RTMP *r = RTMP_Alloc(); 186 187 if(!r) 188 return CURLE_OUT_OF_MEMORY; 189 190 RTMP_Init(r); 191 RTMP_SetBufferMS(r, DEF_BUFTIME); 192 if(!RTMP_SetupURL(r, conn->data->change.url)) { 193 RTMP_Free(r); 194 return CURLE_URL_MALFORMAT; 195 } 196 conn->proto.generic = r; 197 return CURLE_OK; 198} 199 200static CURLcode rtmp_connect(struct connectdata *conn, bool *done) 201{ 202 RTMP *r = conn->proto.generic; 203 SET_RCVTIMEO(tv,10); 204 205 r->m_sb.sb_socket = conn->sock[FIRSTSOCKET]; 206 207 /* We have to know if it's a write before we send the 208 * connect request packet 209 */ 210 if(conn->data->set.upload) 211 r->Link.protocol |= RTMP_FEATURE_WRITE; 212 213 /* For plain streams, use the buffer toggle trick to keep data flowing */ 214 if(!(r->Link.lFlags & RTMP_LF_LIVE) && 215 !(r->Link.protocol & RTMP_FEATURE_HTTP)) 216 r->Link.lFlags |= RTMP_LF_BUFX; 217 218 curlx_nonblock(r->m_sb.sb_socket, FALSE); 219 setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, 220 (char *)&tv, sizeof(tv)); 221 222 if(!RTMP_Connect1(r, NULL)) 223 return CURLE_FAILED_INIT; 224 225 /* Clients must send a periodic BytesReceived report to the server */ 226 r->m_bSendCounter = true; 227 228 *done = TRUE; 229 conn->recv[FIRSTSOCKET] = rtmp_recv; 230 conn->send[FIRSTSOCKET] = rtmp_send; 231 return CURLE_OK; 232} 233 234static CURLcode rtmp_do(struct connectdata *conn, bool *done) 235{ 236 RTMP *r = conn->proto.generic; 237 238 if(!RTMP_ConnectStream(r, 0)) 239 return CURLE_FAILED_INIT; 240 241 if(conn->data->set.upload) { 242 Curl_pgrsSetUploadSize(conn->data, conn->data->set.infilesize); 243 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL); 244 } 245 else 246 Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL); 247 *done = TRUE; 248 return CURLE_OK; 249} 250 251static CURLcode rtmp_done(struct connectdata *conn, CURLcode status, 252 bool premature) 253{ 254 (void)conn; /* unused */ 255 (void)status; /* unused */ 256 (void)premature; /* unused */ 257 258 return CURLE_OK; 259} 260 261static CURLcode rtmp_disconnect(struct connectdata *conn, 262 bool dead_connection) 263{ 264 RTMP *r = conn->proto.generic; 265 (void)dead_connection; 266 if(r) { 267 conn->proto.generic = NULL; 268 RTMP_Close(r); 269 RTMP_Free(r); 270 } 271 return CURLE_OK; 272} 273 274static ssize_t rtmp_recv(struct connectdata *conn, int sockindex, char *buf, 275 size_t len, CURLcode *err) 276{ 277 RTMP *r = conn->proto.generic; 278 ssize_t nread; 279 280 (void)sockindex; /* unused */ 281 282 nread = RTMP_Read(r, buf, len); 283 if(nread < 0) { 284 if(r->m_read.status == RTMP_READ_COMPLETE || 285 r->m_read.status == RTMP_READ_EOF) { 286 conn->data->req.size = conn->data->req.bytecount; 287 nread = 0; 288 } 289 else 290 *err = CURLE_RECV_ERROR; 291 } 292 return nread; 293} 294 295static ssize_t rtmp_send(struct connectdata *conn, int sockindex, 296 const void *buf, size_t len, CURLcode *err) 297{ 298 RTMP *r = conn->proto.generic; 299 ssize_t num; 300 301 (void)sockindex; /* unused */ 302 303 num = RTMP_Write(r, (char *)buf, len); 304 if(num < 0) 305 *err = CURLE_SEND_ERROR; 306 307 return num; 308} 309#endif /* USE_LIBRTMP */ 310