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