1 // LiVES - LiVES stream engine
2 // (c) G. Finch 2008 - 2011 <salsaman@xs4all.nl,salsaman@gmail.com>
3 // released under the GNU GPL 3 or later
4 // see file COPYING or www.gnu.org for details
5 
6 #include "videoplugin.h"
7 
8 #include <inttypes.h>
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 
12 #ifndef IS_MINGW
13 #include <netinet/in.h>
14 #endif
15 
16 //////////////////////////////////////
17 
18 static int palette_list[3];
19 
20 static int clampings[3];
21 
22 static char plugin_version[64] = "LiVES to LiVES streaming engine version 1.1";
23 
24 static boolean(*render_fn)(int hsize, int vsize, int64_t tc, void **pixel_data);
25 boolean render_frame_stream(int hsize, int vsize, int64_t tc, void **pixel_data);
26 boolean render_frame_unknown(int hsize, int vsize, int64_t tc, void **pixel_data);
27 
28 /////////////////////////////////////////////////////////////////////////
29 
30 typedef struct {
31   int hsize;
32   int vsize;
33   double fps;
34   int palette;
35   int YUV_clamping;
36   size_t mtu;
37   void *handle;
38 } lives_stream_t;
39 
40 
41 static lives_stream_t *lstream;
42 
43 //////////////////////////////////////////////
44 #include <stdio.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <netdb.h>
48 #include <sys/socket.h>
49 #include <string.h>
50 #include <errno.h>
51 
52 typedef struct {
53   struct sockaddr_in serv_addr;
54   int sockfd;
55   int len;
56   void *addr;
57 } desc;
58 
59 
lstream_alloc(void)60 lives_stream_t *lstream_alloc(void) {
61   lives_stream_t *lstream = (lives_stream_t *) malloc(sizeof(lives_stream_t));
62   if (!lstream) return NULL;
63   lstream->handle = NULL;
64   lstream->YUV_clamping = WEED_YUV_CLAMPING_CLAMPED;
65   return lstream;
66 }
67 
68 
OpenHTMSocket(char * host,int portnumber)69 void *OpenHTMSocket(char *host, int portnumber) {
70   int sockfd;
71   struct sockaddr_in cl_addr;
72   desc *o;
73   struct hostent *hostsEntry;
74   uint64_t address;
75 
76   o = (desc *)malloc(sizeof(desc));
77   if (o == NULL) return NULL;
78 
79   o->len = sizeof(cl_addr);
80   memset((char *)&o->serv_addr, 0, sizeof(o->serv_addr));
81   o->serv_addr.sin_family = AF_INET;
82 
83   hostsEntry = gethostbyname(host);
84   if (hostsEntry == NULL) {
85     herror(NULL);
86     return NULL;
87   }
88 
89   address = *((uint64_t *) hostsEntry->h_addr_list[0]);
90   o->serv_addr.sin_addr.s_addr = address;
91 
92   o->serv_addr.sin_port = htons(portnumber);
93   o->addr = &(o->serv_addr);
94 
95   if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) {
96     memset((char *)&cl_addr, 0, sizeof(cl_addr));
97     cl_addr.sin_family = AF_INET;
98     cl_addr.sin_addr.s_addr = htonl(INADDR_ANY);
99     cl_addr.sin_port = htons(0);
100 
101     if (bind(sockfd, (struct sockaddr *) &cl_addr, sizeof(cl_addr)) < 0) {
102       fprintf(stderr, "could not bind\n");
103       close(sockfd);
104       sockfd = -1;
105     }
106   } else fprintf(stderr, "unable to make socket\n");
107 
108   if (sockfd < 0) {
109     free(o);
110     o = NULL;
111   } else o->sockfd = sockfd;
112 
113   if (o != NULL && strcmp(host, "INADDR_ANY")) {
114     connect(sockfd, o->addr, sizeof(cl_addr));
115   }
116 
117   return o;
118 }
119 
120 
sendudp(const struct sockaddr * sp,int sockfd,int length,size_t count,void * b)121 static boolean sendudp(const struct sockaddr *sp, int sockfd, int length, size_t count, void  *b) {
122   ssize_t res;
123   size_t mcount = count;
124   if (lstream->mtu > 0 && mcount > lstream->mtu) mcount = lstream->mtu;
125 
126   while (count > 0) {
127     if (mcount > count) mcount = count;
128     if ((res = sendto(sockfd, b, mcount, 0, sp, length)) == -1) {
129       if (errno == EMSGSIZE) {
130         mcount >>= 1;
131         lstream->mtu = mcount;
132       } else return FALSE;
133     } else {
134       count -= res;
135       b += res;
136     }
137   }
138   return TRUE;
139 }
140 
141 
lives_stream_out(void * buffer,size_t length)142 static int lives_stream_out(void *buffer, size_t length) {
143   desc *o = (desc *)(lstream->handle);
144   return sendudp(o->addr, o->sockfd, o->len, length, buffer);
145 }
146 
147 
lstream_close_socket(lives_stream_t * lstream)148 void lstream_close_socket(lives_stream_t *lstream) {
149   desc *o = (desc *)(lstream->handle);
150   close(o->sockfd);
151   free(o);
152 }
153 
154 
155 ////////////////
156 
module_check_init(void)157 const char *module_check_init(void) {
158   render_fn = &render_frame_unknown;
159 
160   lstream = lstream_alloc();
161 
162   return NULL;
163 }
164 
165 
version(void)166 const char *version(void) {
167   return plugin_version;
168 }
169 
get_description(void)170 const char *get_description(void) {
171   return "The LiVES 2 LiVES stream plugin allows streaming to another copy of LiVES.\n";
172 }
173 
get_palette_list(void)174 const int *get_palette_list(void) {
175   palette_list[0] = WEED_PALETTE_YUV420P;
176   palette_list[1] = WEED_PALETTE_RGB24;
177   palette_list[2] = WEED_PALETTE_END;
178   return palette_list;
179 }
180 
get_yuv_palette_clamping(int palette)181 const int *get_yuv_palette_clamping(int palette) {
182   if (palette == WEED_PALETTE_YUV420P) {
183     clampings[0] = WEED_YUV_CLAMPING_UNCLAMPED;
184     clampings[1] = WEED_YUV_CLAMPING_CLAMPED;
185     clampings[2] = -1;
186   } else clampings[0] = -1;
187   return clampings;
188 }
189 
190 
set_yuv_palette_clamping(int clamping_type)191 boolean set_yuv_palette_clamping(int clamping_type) {
192   if (clamping_type == WEED_YUV_CLAMPING_CLAMPED || clamping_type == WEED_YUV_CLAMPING_UNCLAMPED) {
193     lstream->YUV_clamping = clamping_type;
194     return TRUE;
195   }
196   return FALSE;
197 }
198 
199 
get_capabilities(int palette)200 uint64_t get_capabilities(int palette) {
201   return 0;
202 }
203 
204 
set_palette(int palette)205 boolean set_palette(int palette) {
206   if (!lstream) return FALSE;
207   if (palette == WEED_PALETTE_YUV420P || palette == WEED_PALETTE_RGB24) {
208     lstream->palette = palette;
209     render_fn = &render_frame_stream;
210     return TRUE;
211   }
212   // invalid palette
213   return FALSE;
214 }
215 
get_init_rfx(int intention)216 const char *get_init_rfx(int intention) {
217   return \
218          "<define>\\n\
219 |1.7\\n\
220 </define>\\n\
221 <language_code>\\n\
222 0xF0\\n\
223 </language_code>\\n\
224 <params> \\n\
225 ip1|_IP Address|string|127|3| \\n\
226 ip2||string|0|3| \\n\
227 ip3||string|0|3| \\n\
228 ip4||string|1|3| \\n\
229 port|_Port|num0|8888|1|65535 \\n\
230 </params> \\n\
231 <param_window> \\n\
232 layout|\\\"Enter an IP address and port to stream to LiVES output to.\\\"| \\n\
233 layout|\\\"In the other copy of LiVES, you must select Advanced/Receive LiVES stream from...\\\"| \\n\
234 layout|\\\"You are advised to start with a small frame size and low framerate,\\\"| \\n\
235 layout|\\\"and increase this if your network bandwidth allows it.\\\"| \\n\
236 layout|p0|\\\".\\\"|p1|\\\".\\\"|p2|\\\".\\\"|p3|fill|fill|fill|fill| \\n\
237 layout|p4|fill\\n\
238 </param_window> \\n\
239 <onchange> \\n\
240 </onchange> \\n\
241 ";
242 }
243 
get_fps_list(int palette)244 const char *get_fps_list(int palette) {
245   return "8|12|16|24|25|30|50|60";
246 }
247 
248 
set_fps(double in_fps)249 boolean set_fps(double in_fps) {
250   lstream->fps = in_fps;
251   return TRUE;
252 }
253 
init_screen(int width,int height,boolean fullscreen,uint64_t window_id,int argc,char ** argv)254 boolean init_screen(int width, int height, boolean fullscreen, uint64_t window_id, int argc, char **argv) {
255   char host[16];
256   int port;
257 
258   if (lstream->palette == WEED_PALETTE_END) {
259     fprintf(stderr, "lives2lives_stream plugin error: No palette was set !\n");
260     return FALSE;
261   }
262 
263   if (argc > 0) {
264     snprintf(host, 16, "%s.%s.%s.%s", argv[0], argv[1], argv[2], argv[3]);
265     port = atoi(argv[4]);
266     lstream->handle = OpenHTMSocket(host, port);
267     if (lstream->handle == NULL) {
268       fprintf(stderr, "lives2lives_stream plugin error: Could not open port !\n");
269       return FALSE;
270     }
271   }
272 
273   lstream->mtu = 0;
274 
275   return TRUE;
276 }
277 
278 
render_frame(int hsize,int vsize,int64_t tc,void ** pixel_data,void ** rd,void ** pp)279 boolean render_frame(int hsize, int vsize, int64_t tc, void **pixel_data, void **rd, void **pp) {
280   // call the function which was set in set_palette
281   return render_fn(hsize, vsize, tc, pixel_data);
282 }
283 
render_frame_stream(int hsize,int vsize,int64_t tc,void ** pixel_data)284 boolean render_frame_stream(int hsize, int vsize, int64_t tc, void **pixel_data) {
285   char hdrstr[128];
286   size_t hdrstrlen;
287   int mcount;
288   int dsize = 0;
289 
290   // send: 8 bytes "PACKET "
291   // n bytes header
292 
293   // format is: (uint32_t)packet_type [1=video], (uint32_t)stream_id, (uint32_t)flags, (uint32_t)packet_length
294   //
295   // then for video type:
296   // (int64_t)timecode, (int32_t)width in macropixels, (int32_t)height, (double)fps, (int32_t)palette
297   // last 4 entries reserved for: (int32_t)yuv sampling [0=mpeg], (int32_t)yuv clamping [0=clamped,1=unclamped],
298   // (int32_t)yuv subspace [1=YCbCr]; (int32_t)compression [0=uncompressed]
299 
300   // flags for video type are currently: bit 0 set == packet is continuation of current frame
301 
302   // 4 bytes "DATA"
303 
304   // packet_length bytes data
305 
306   // on stream end send "STREND" instead of "PACKET "
307 
308   if (lstream == NULL || lstream->handle == NULL) return FALSE;
309 
310   if (lstream->palette == WEED_PALETTE_YUV420P) dsize = hsize * vsize * 3 / 2;
311   else if (lstream->palette == WEED_PALETTE_RGB24) dsize = hsize * vsize * 3;
312 
313   mcount = dsize * 4;
314   setsockopt(((desc *)(lstream->handle))->sockfd, SOL_SOCKET, SO_SNDBUF, (void *) &mcount, sizeof(mcount));
315 
316   snprintf(hdrstr, 128, "1 0 0 %d %"PRId64" %d %d %.8f %d 1 %d 0 0 ", dsize, tc, hsize, vsize, lstream->fps, lstream->palette,
317            lstream->YUV_clamping);
318 
319   hdrstrlen = strlen(hdrstr);
320 
321   lives_stream_out("PACKET ", 7);
322   lives_stream_out(hdrstr, hdrstrlen);
323   lives_stream_out("DATA", 4);
324 
325   if (lstream->palette == WEED_PALETTE_YUV420P) {
326     lives_stream_out(pixel_data[0], hsize * vsize);
327     lives_stream_out(pixel_data[1], (hsize * vsize) >> 2);
328     lives_stream_out(pixel_data[2], (hsize * vsize) >> 2);
329   } else if (lstream->palette == WEED_PALETTE_RGB24) {
330     lives_stream_out(pixel_data[0], dsize);
331   }
332 
333   return TRUE;
334 }
335 
336 
render_frame_unknown(int hsize,int vsize,int64_t tc,void ** pixel_data)337 boolean render_frame_unknown(int hsize, int vsize, int64_t tc, void **pixel_data) {
338   if (lstream->palette == WEED_PALETTE_END) {
339     fprintf(stderr, "lives2lives_stream plugin error: No palette was set !\n");
340     return 0;
341   }
342   return FALSE;
343 }
344 
exit_screen(int16_t mouse_x,int16_t mouse_y)345 void exit_screen(int16_t mouse_x, int16_t mouse_y) {
346   if (lstream != NULL && lstream->handle != NULL) {
347     lives_stream_out("STREND", 6);
348     lstream_close_socket(lstream);
349   }
350   lstream->handle = NULL;
351 }
352 
353 
module_unload(void)354 void module_unload(void) {
355   if (lstream != NULL) {
356     free(lstream);
357     lstream = NULL;
358   }
359 }
360 
361 
362