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