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