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