quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

curl_rtmp.c (14706B)


      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 "url.h"
     33 #include "curlx/nonblock.h" /* for curlx_nonblock */
     34 #include "progress.h" /* for Curl_pgrsSetUploadSize */
     35 #include "transfer.h"
     36 #include "curlx/warnless.h"
     37 #include <curl/curl.h>
     38 #include <librtmp/rtmp.h>
     39 
     40 /* The last 3 #include files should be in this order */
     41 #include "curl_printf.h"
     42 #include "curl_memory.h"
     43 #include "memdebug.h"
     44 
     45 #if defined(_WIN32) && !defined(USE_LWIPSOCK)
     46 #define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e)
     47 #define SET_RCVTIMEO(tv,s)   int tv = s*1000
     48 #elif defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD)
     49 #define SET_RCVTIMEO(tv,s)   int tv = s*1000
     50 #else
     51 #define SET_RCVTIMEO(tv,s)   struct timeval tv = {s,0}
     52 #endif
     53 
     54 #define DEF_BUFTIME    (2*60*60*1000)    /* 2 hours */
     55 
     56 /* meta key for storing RTMP* at connection */
     57 #define CURL_META_RTMP_CONN   "meta:proto:rtmp:conn"
     58 
     59 
     60 static CURLcode rtmp_setup_connection(struct Curl_easy *data,
     61                                       struct connectdata *conn);
     62 static CURLcode rtmp_do(struct Curl_easy *data, bool *done);
     63 static CURLcode rtmp_done(struct Curl_easy *data, CURLcode, bool premature);
     64 static CURLcode rtmp_connect(struct Curl_easy *data, bool *done);
     65 static CURLcode rtmp_disconnect(struct Curl_easy *data,
     66                                 struct connectdata *conn, bool dead);
     67 
     68 static Curl_recv rtmp_recv;
     69 static Curl_send rtmp_send;
     70 
     71 /*
     72  * RTMP protocol handler.h, based on https://rtmpdump.mplayerhq.hu
     73  */
     74 
     75 const struct Curl_handler Curl_handler_rtmp = {
     76   "rtmp",                               /* scheme */
     77   rtmp_setup_connection,                /* setup_connection */
     78   rtmp_do,                              /* do_it */
     79   rtmp_done,                            /* done */
     80   ZERO_NULL,                            /* do_more */
     81   rtmp_connect,                         /* connect_it */
     82   ZERO_NULL,                            /* connecting */
     83   ZERO_NULL,                            /* doing */
     84   ZERO_NULL,                            /* proto_getsock */
     85   ZERO_NULL,                            /* doing_getsock */
     86   ZERO_NULL,                            /* domore_getsock */
     87   ZERO_NULL,                            /* perform_getsock */
     88   rtmp_disconnect,                      /* disconnect */
     89   ZERO_NULL,                            /* write_resp */
     90   ZERO_NULL,                            /* write_resp_hd */
     91   ZERO_NULL,                            /* connection_check */
     92   ZERO_NULL,                            /* attach connection */
     93   ZERO_NULL,                            /* follow */
     94   PORT_RTMP,                            /* defport */
     95   CURLPROTO_RTMP,                       /* protocol */
     96   CURLPROTO_RTMP,                       /* family */
     97   PROTOPT_NONE                          /* flags */
     98 };
     99 
    100 const struct Curl_handler Curl_handler_rtmpt = {
    101   "rtmpt",                              /* scheme */
    102   rtmp_setup_connection,                /* setup_connection */
    103   rtmp_do,                              /* do_it */
    104   rtmp_done,                            /* done */
    105   ZERO_NULL,                            /* do_more */
    106   rtmp_connect,                         /* connect_it */
    107   ZERO_NULL,                            /* connecting */
    108   ZERO_NULL,                            /* doing */
    109   ZERO_NULL,                            /* proto_getsock */
    110   ZERO_NULL,                            /* doing_getsock */
    111   ZERO_NULL,                            /* domore_getsock */
    112   ZERO_NULL,                            /* perform_getsock */
    113   rtmp_disconnect,                      /* disconnect */
    114   ZERO_NULL,                            /* write_resp */
    115   ZERO_NULL,                            /* write_resp_hd */
    116   ZERO_NULL,                            /* connection_check */
    117   ZERO_NULL,                            /* attach connection */
    118   ZERO_NULL,                            /* follow */
    119   PORT_RTMPT,                           /* defport */
    120   CURLPROTO_RTMPT,                      /* protocol */
    121   CURLPROTO_RTMPT,                      /* family */
    122   PROTOPT_NONE                          /* flags */
    123 };
    124 
    125 const struct Curl_handler Curl_handler_rtmpe = {
    126   "rtmpe",                              /* 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,                            /* write_resp */
    140   ZERO_NULL,                            /* write_resp_hd */
    141   ZERO_NULL,                            /* connection_check */
    142   ZERO_NULL,                            /* attach connection */
    143   ZERO_NULL,                            /* follow */
    144   PORT_RTMP,                            /* defport */
    145   CURLPROTO_RTMPE,                      /* protocol */
    146   CURLPROTO_RTMPE,                      /* family */
    147   PROTOPT_NONE                          /* flags */
    148 };
    149 
    150 const struct Curl_handler Curl_handler_rtmpte = {
    151   "rtmpte",                             /* scheme */
    152   rtmp_setup_connection,                /* setup_connection */
    153   rtmp_do,                              /* do_it */
    154   rtmp_done,                            /* done */
    155   ZERO_NULL,                            /* do_more */
    156   rtmp_connect,                         /* connect_it */
    157   ZERO_NULL,                            /* connecting */
    158   ZERO_NULL,                            /* doing */
    159   ZERO_NULL,                            /* proto_getsock */
    160   ZERO_NULL,                            /* doing_getsock */
    161   ZERO_NULL,                            /* domore_getsock */
    162   ZERO_NULL,                            /* perform_getsock */
    163   rtmp_disconnect,                      /* disconnect */
    164   ZERO_NULL,                            /* write_resp */
    165   ZERO_NULL,                            /* write_resp_hd */
    166   ZERO_NULL,                            /* connection_check */
    167   ZERO_NULL,                            /* attach connection */
    168   ZERO_NULL,                            /* follow */
    169   PORT_RTMPT,                           /* defport */
    170   CURLPROTO_RTMPTE,                     /* protocol */
    171   CURLPROTO_RTMPTE,                     /* family */
    172   PROTOPT_NONE                          /* flags */
    173 };
    174 
    175 const struct Curl_handler Curl_handler_rtmps = {
    176   "rtmps",                              /* scheme */
    177   rtmp_setup_connection,                /* setup_connection */
    178   rtmp_do,                              /* do_it */
    179   rtmp_done,                            /* done */
    180   ZERO_NULL,                            /* do_more */
    181   rtmp_connect,                         /* connect_it */
    182   ZERO_NULL,                            /* connecting */
    183   ZERO_NULL,                            /* doing */
    184   ZERO_NULL,                            /* proto_getsock */
    185   ZERO_NULL,                            /* doing_getsock */
    186   ZERO_NULL,                            /* domore_getsock */
    187   ZERO_NULL,                            /* perform_getsock */
    188   rtmp_disconnect,                      /* disconnect */
    189   ZERO_NULL,                            /* write_resp */
    190   ZERO_NULL,                            /* write_resp_hd */
    191   ZERO_NULL,                            /* connection_check */
    192   ZERO_NULL,                            /* attach connection */
    193   ZERO_NULL,                            /* follow */
    194   PORT_RTMPS,                           /* defport */
    195   CURLPROTO_RTMPS,                      /* protocol */
    196   CURLPROTO_RTMP,                       /* family */
    197   PROTOPT_NONE                          /* flags */
    198 };
    199 
    200 const struct Curl_handler Curl_handler_rtmpts = {
    201   "rtmpts",                             /* scheme */
    202   rtmp_setup_connection,                /* setup_connection */
    203   rtmp_do,                              /* do_it */
    204   rtmp_done,                            /* done */
    205   ZERO_NULL,                            /* do_more */
    206   rtmp_connect,                         /* connect_it */
    207   ZERO_NULL,                            /* connecting */
    208   ZERO_NULL,                            /* doing */
    209   ZERO_NULL,                            /* proto_getsock */
    210   ZERO_NULL,                            /* doing_getsock */
    211   ZERO_NULL,                            /* domore_getsock */
    212   ZERO_NULL,                            /* perform_getsock */
    213   rtmp_disconnect,                      /* disconnect */
    214   ZERO_NULL,                            /* write_resp */
    215   ZERO_NULL,                            /* write_resp_hd */
    216   ZERO_NULL,                            /* connection_check */
    217   ZERO_NULL,                            /* attach connection */
    218   ZERO_NULL,                            /* follow */
    219   PORT_RTMPS,                           /* defport */
    220   CURLPROTO_RTMPTS,                     /* protocol */
    221   CURLPROTO_RTMPT,                      /* family */
    222   PROTOPT_NONE                          /* flags */
    223 };
    224 
    225 static void rtmp_conn_dtor(void *key, size_t klen, void *entry)
    226 {
    227   RTMP *r = entry;
    228   (void)key;
    229   (void)klen;
    230   RTMP_Close(r);
    231   RTMP_Free(r);
    232 }
    233 
    234 static CURLcode rtmp_setup_connection(struct Curl_easy *data,
    235                                       struct connectdata *conn)
    236 {
    237   RTMP *r = RTMP_Alloc();
    238   if(!r ||
    239     Curl_conn_meta_set(conn, CURL_META_RTMP_CONN, r, rtmp_conn_dtor))
    240     return CURLE_OUT_OF_MEMORY;
    241 
    242   RTMP_Init(r);
    243   RTMP_SetBufferMS(r, DEF_BUFTIME);
    244   if(!RTMP_SetupURL(r, data->state.url)) {
    245     RTMP_Free(r);
    246     return CURLE_URL_MALFORMAT;
    247   }
    248   return CURLE_OK;
    249 }
    250 
    251 static CURLcode rtmp_connect(struct Curl_easy *data, bool *done)
    252 {
    253   struct connectdata *conn = data->conn;
    254   RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
    255   SET_RCVTIMEO(tv, 10);
    256 
    257   if(!r)
    258     return CURLE_FAILED_INIT;
    259 
    260   r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET];
    261 
    262   /* We have to know if it is a write before we send the
    263    * connect request packet
    264    */
    265   if(data->state.upload)
    266     r->Link.protocol |= RTMP_FEATURE_WRITE;
    267 
    268   /* For plain streams, use the buffer toggle trick to keep data flowing */
    269   if(!(r->Link.lFlags & RTMP_LF_LIVE) &&
    270      !(r->Link.protocol & RTMP_FEATURE_HTTP))
    271     r->Link.lFlags |= RTMP_LF_BUFX;
    272 
    273   (void)curlx_nonblock(r->m_sb.sb_socket, FALSE);
    274   setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO,
    275              (char *)&tv, sizeof(tv));
    276 
    277   if(!RTMP_Connect1(r, NULL))
    278     return CURLE_FAILED_INIT;
    279 
    280   /* Clients must send a periodic BytesReceived report to the server */
    281   r->m_bSendCounter = TRUE;
    282 
    283   *done = TRUE;
    284   conn->recv[FIRSTSOCKET] = rtmp_recv;
    285   conn->send[FIRSTSOCKET] = rtmp_send;
    286   return CURLE_OK;
    287 }
    288 
    289 static CURLcode rtmp_do(struct Curl_easy *data, bool *done)
    290 {
    291   struct connectdata *conn = data->conn;
    292   RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
    293 
    294   if(!r || !RTMP_ConnectStream(r, 0))
    295     return CURLE_FAILED_INIT;
    296 
    297   if(data->state.upload) {
    298     Curl_pgrsSetUploadSize(data, data->state.infilesize);
    299     Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
    300   }
    301   else
    302     Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
    303   *done = TRUE;
    304   return CURLE_OK;
    305 }
    306 
    307 static CURLcode rtmp_done(struct Curl_easy *data, CURLcode status,
    308                           bool premature)
    309 {
    310   (void)data; /* unused */
    311   (void)status; /* unused */
    312   (void)premature; /* unused */
    313 
    314   return CURLE_OK;
    315 }
    316 
    317 static CURLcode rtmp_disconnect(struct Curl_easy *data,
    318                                 struct connectdata *conn,
    319                                 bool dead_connection)
    320 {
    321   RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
    322   (void)data;
    323   (void)dead_connection;
    324   if(r)
    325     Curl_conn_meta_remove(conn, CURL_META_RTMP_CONN);
    326   return CURLE_OK;
    327 }
    328 
    329 static CURLcode rtmp_recv(struct Curl_easy *data, int sockindex, char *buf,
    330                           size_t len, size_t *pnread)
    331 {
    332   struct connectdata *conn = data->conn;
    333   RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
    334   CURLcode result = CURLE_OK;
    335   ssize_t nread;
    336 
    337   (void)sockindex; /* unused */
    338   *pnread = 0;
    339   if(!r)
    340     return CURLE_FAILED_INIT;
    341 
    342   nread = RTMP_Read(r, buf, curlx_uztosi(len));
    343   if(nread < 0) {
    344     if(r->m_read.status == RTMP_READ_COMPLETE ||
    345        r->m_read.status == RTMP_READ_EOF) {
    346       data->req.size = data->req.bytecount;
    347     }
    348     else
    349       result = CURLE_RECV_ERROR;
    350   }
    351   else
    352     *pnread = (size_t)nread;
    353 
    354   return result;
    355 }
    356 
    357 static CURLcode rtmp_send(struct Curl_easy *data, int sockindex,
    358                           const void *buf, size_t len, bool eos,
    359                           size_t *pnwritten)
    360 {
    361   struct connectdata *conn = data->conn;
    362   RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
    363   ssize_t nwritten;
    364 
    365   (void)sockindex; /* unused */
    366   (void)eos; /* unused */
    367   *pnwritten = 0;
    368   if(!r)
    369     return CURLE_FAILED_INIT;
    370 
    371   nwritten = RTMP_Write(r, (const char *)buf, curlx_uztosi(len));
    372   if(nwritten < 0)
    373     return CURLE_SEND_ERROR;
    374 
    375   *pnwritten = (size_t)nwritten;
    376   return CURLE_OK;
    377 }
    378 
    379 void Curl_rtmp_version(char *version, size_t len)
    380 {
    381   char suff[2];
    382   if(RTMP_LIB_VERSION & 0xff) {
    383     suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1;
    384     suff[1] = '\0';
    385   }
    386   else
    387     suff[0] = '\0';
    388 
    389   msnprintf(version, len, "librtmp/%d.%d%s",
    390             RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff,
    391             suff);
    392 }
    393 
    394 #endif  /* USE_LIBRTMP */