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