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, /* perform_getsock */ 75 rtmp_disconnect, /* disconnect */ 76 ZERO_NULL, /* readwrite */ 77 PORT_RTMP, /* defport */ 78 CURLPROTO_RTMP, /* protocol */ 79 PROTOPT_NONE /* flags*/ 80}; 81 82const struct Curl_handler Curl_handler_rtmpt = { 83 "RTMPT", /* scheme */ 84 rtmp_setup, /* setup_connection */ 85 rtmp_do, /* do_it */ 86 rtmp_done, /* done */ 87 ZERO_NULL, /* do_more */ 88 rtmp_connect, /* connect_it */ 89 ZERO_NULL, /* connecting */ 90 ZERO_NULL, /* doing */ 91 ZERO_NULL, /* proto_getsock */ 92 ZERO_NULL, /* doing_getsock */ 93 ZERO_NULL, /* perform_getsock */ 94 rtmp_disconnect, /* disconnect */ 95 ZERO_NULL, /* readwrite */ 96 PORT_RTMPT, /* defport */ 97 CURLPROTO_RTMPT, /* protocol */ 98 PROTOPT_NONE /* flags*/ 99}; 100 101const struct Curl_handler Curl_handler_rtmpe = { 102 "RTMPE", /* scheme */ 103 rtmp_setup, /* setup_connection */ 104 rtmp_do, /* do_it */ 105 rtmp_done, /* done */ 106 ZERO_NULL, /* do_more */ 107 rtmp_connect, /* connect_it */ 108 ZERO_NULL, /* connecting */ 109 ZERO_NULL, /* doing */ 110 ZERO_NULL, /* proto_getsock */ 111 ZERO_NULL, /* doing_getsock */ 112 ZERO_NULL, /* perform_getsock */ 113 rtmp_disconnect, /* disconnect */ 114 ZERO_NULL, /* readwrite */ 115 PORT_RTMP, /* defport */ 116 CURLPROTO_RTMPE, /* protocol */ 117 PROTOPT_NONE /* flags*/ 118}; 119 120const struct Curl_handler Curl_handler_rtmpte = { 121 "RTMPTE", /* scheme */ 122 rtmp_setup, /* setup_connection */ 123 rtmp_do, /* do_it */ 124 rtmp_done, /* done */ 125 ZERO_NULL, /* do_more */ 126 rtmp_connect, /* connect_it */ 127 ZERO_NULL, /* connecting */ 128 ZERO_NULL, /* doing */ 129 ZERO_NULL, /* proto_getsock */ 130 ZERO_NULL, /* doing_getsock */ 131 ZERO_NULL, /* perform_getsock */ 132 rtmp_disconnect, /* disconnect */ 133 ZERO_NULL, /* readwrite */ 134 PORT_RTMPT, /* defport */ 135 CURLPROTO_RTMPTE, /* protocol */ 136 PROTOPT_NONE /* flags*/ 137}; 138 139const struct Curl_handler Curl_handler_rtmps = { 140 "RTMPS", /* scheme */ 141 rtmp_setup, /* setup_connection */ 142 rtmp_do, /* do_it */ 143 rtmp_done, /* done */ 144 ZERO_NULL, /* do_more */ 145 rtmp_connect, /* connect_it */ 146 ZERO_NULL, /* connecting */ 147 ZERO_NULL, /* doing */ 148 ZERO_NULL, /* proto_getsock */ 149 ZERO_NULL, /* doing_getsock */ 150 ZERO_NULL, /* perform_getsock */ 151 rtmp_disconnect, /* disconnect */ 152 ZERO_NULL, /* readwrite */ 153 PORT_RTMPS, /* defport */ 154 CURLPROTO_RTMPS, /* protocol */ 155 PROTOPT_NONE /* flags*/ 156}; 157 158const struct Curl_handler Curl_handler_rtmpts = { 159 "RTMPTS", /* scheme */ 160 rtmp_setup, /* setup_connection */ 161 rtmp_do, /* do_it */ 162 rtmp_done, /* done */ 163 ZERO_NULL, /* do_more */ 164 rtmp_connect, /* connect_it */ 165 ZERO_NULL, /* connecting */ 166 ZERO_NULL, /* doing */ 167 ZERO_NULL, /* proto_getsock */ 168 ZERO_NULL, /* doing_getsock */ 169 ZERO_NULL, /* perform_getsock */ 170 rtmp_disconnect, /* disconnect */ 171 ZERO_NULL, /* readwrite */ 172 PORT_RTMPS, /* defport */ 173 CURLPROTO_RTMPTS, /* protocol */ 174 PROTOPT_NONE /* flags*/ 175}; 176 177static CURLcode rtmp_setup(struct connectdata *conn) 178{ 179 RTMP *r = RTMP_Alloc(); 180 181 if(!r) 182 return CURLE_OUT_OF_MEMORY; 183 184 RTMP_Init(r); 185 RTMP_SetBufferMS(r, DEF_BUFTIME); 186 if(!RTMP_SetupURL(r, conn->data->change.url)) { 187 RTMP_Free(r); 188 return CURLE_URL_MALFORMAT; 189 } 190 conn->proto.generic = r; 191 return CURLE_OK; 192} 193 194static CURLcode rtmp_connect(struct connectdata *conn, bool *done) 195{ 196 RTMP *r = conn->proto.generic; 197 SET_RCVTIMEO(tv,10); 198 199 r->m_sb.sb_socket = conn->sock[FIRSTSOCKET]; 200 201 /* We have to know if it's a write before we send the 202 * connect request packet 203 */ 204 if(conn->data->set.upload) 205 r->Link.protocol |= RTMP_FEATURE_WRITE; 206 207 /* For plain streams, use the buffer toggle trick to keep data flowing */ 208 if(!(r->Link.lFlags & RTMP_LF_LIVE) && 209 !(r->Link.protocol & RTMP_FEATURE_HTTP)) 210 r->Link.lFlags |= RTMP_LF_BUFX; 211 212 curlx_nonblock(r->m_sb.sb_socket, FALSE); 213 setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, 214 (char *)&tv, sizeof(tv)); 215 216 if(!RTMP_Connect1(r, NULL)) 217 return CURLE_FAILED_INIT; 218 219 /* Clients must send a periodic BytesReceived report to the server */ 220 r->m_bSendCounter = true; 221 222 *done = TRUE; 223 conn->recv[FIRSTSOCKET] = rtmp_recv; 224 conn->send[FIRSTSOCKET] = rtmp_send; 225 return CURLE_OK; 226} 227 228static CURLcode rtmp_do(struct connectdata *conn, bool *done) 229{ 230 RTMP *r = conn->proto.generic; 231 232 if(!RTMP_ConnectStream(r, 0)) 233 return CURLE_FAILED_INIT; 234 235 if(conn->data->set.upload) { 236 Curl_pgrsSetUploadSize(conn->data, conn->data->set.infilesize); 237 Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL); 238 } 239 else 240 Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, NULL, -1, NULL); 241 *done = TRUE; 242 return CURLE_OK; 243} 244 245static CURLcode rtmp_done(struct connectdata *conn, CURLcode status, 246 bool premature) 247{ 248 (void)conn; /* unused */ 249 (void)status; /* unused */ 250 (void)premature; /* unused */ 251 252 return CURLE_OK; 253} 254 255static CURLcode rtmp_disconnect(struct connectdata *conn, 256 bool dead_connection) 257{ 258 RTMP *r = conn->proto.generic; 259 (void)dead_connection; 260 if(r) { 261 conn->proto.generic = NULL; 262 RTMP_Close(r); 263 RTMP_Free(r); 264 } 265 return CURLE_OK; 266} 267 268static ssize_t rtmp_recv(struct connectdata *conn, int sockindex, char *buf, 269 size_t len, CURLcode *err) 270{ 271 RTMP *r = conn->proto.generic; 272 ssize_t nread; 273 274 (void)sockindex; /* unused */ 275 276 nread = RTMP_Read(r, buf, len); 277 if(nread < 0) { 278 if(r->m_read.status == RTMP_READ_COMPLETE || 279 r->m_read.status == RTMP_READ_EOF) { 280 conn->data->req.size = conn->data->req.bytecount; 281 nread = 0; 282 } 283 else 284 *err = CURLE_RECV_ERROR; 285 } 286 return nread; 287} 288 289static ssize_t rtmp_send(struct connectdata *conn, int sockindex, 290 const void *buf, size_t len, CURLcode *err) 291{ 292 RTMP *r = conn->proto.generic; 293 ssize_t num; 294 295 (void)sockindex; /* unused */ 296 297 num = RTMP_Write(r, (char *)buf, len); 298 if(num < 0) 299 *err = CURLE_SEND_ERROR; 300 301 return num; 302} 303#endif /* USE_LIBRTMP */ 304