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