LiVES 3.2.0
stream.c
Go to the documentation of this file.
1// stream.c
2// LiVES
3// (c) G. Finch 2008 - 2018 <salsaman+lives@gmail.com>
4// released under the GNU GPL 3 or later
5// see file ../COPYING for licensing details
6
7// TODO - implement multicast streaming
8
9#include "main.h"
10#include "stream.h"
11#include "htmsocket.h"
12#include "interface.h"
13#include "effects-weed.h"
14
15#define L2L_PACKET_LEN 1024
16
17static char pckbuf[L2L_PACKET_LEN * 2];
18static int pckoffs = 0;
19static size_t pcksize = 0;
20
21static boolean has_last_delta_ticks;
22static char *hdr = NULL;
23static boolean fps_can_change;
24
25LIVES_INLINE int64_t abs64(int64_t a) {
26 return ((a > 0) ? a : -a);
27}
28
29//#define USE_STRMBUF
30#ifdef USE_STRMBUF
31
32#define STREAM_BUF_SIZE 1024*1024*128 // allow 8MB buffer size; actually we use twice this much: - TODO - make pref
33static volatile boolean buffering;
34
35void *streambuf(void *arg) {
36 // read bytes from udp port and load into ringbuffer
37 lives_vstream_t *lstream = (lives_vstream_t *)arg;
38
39 ssize_t res;
40 size_t btowrite;
41 size_t tmpbufoffs;
42
43 uint8_t tmpbuf[STREAM_BUF_SIZE];
44
45 lstream->bufoffs = 0;
46
47 while (buffering) {
48 // keep calling lives_stream_in until main thread sets buffering to FALSE
49 res = lives_stream_in(lstream->handle, STREAM_BUF_SIZE, tmpbuf, 0);
50 if (res > 0) {
51 // got packet of size res
52 tmpbufoffs = 0;
53 btowrite = res;
54 if (lstream->bufoffs + btowrite > STREAM_BUF_SIZE) {
55 btowrite = STREAM_BUF_SIZE - lstream->bufoffs;
56 lives_memcpy(lstream->buffer + lstream->bufoffs, tmpbuf, btowrite);
57 tmpbufoffs += btowrite;
58 res -= btowrite;
59 lstream->bufoffs = 0;
60 }
61 lives_memcpy(lstream->buffer + lstream->bufoffs, tmpbuf + tmpbufoffs, res);
62 lstream->bufoffs += res;
63 lstream->reading = TRUE;
64 }
65 //else lstream->reading=FALSE;
66 }
67 pthread_exit(NULL);
68 return NULL;
69}
70
71
72static size_t l2l_rcv_packet(lives_vstream_t *lstream, size_t buflen, void *buf) {
73 // take a packet from our stream buffer
74 static size_t bufoffs = 0;
75 size_t btoread = 0;
76 size_t end = bufoffs + buflen;
77
78 while ((end < STREAM_BUF_SIZE && lstream->bufoffs >= bufoffs && lstream->bufoffs <= end) || (end >= STREAM_BUF_SIZE &&
79 (lstream->bufoffs >= bufoffs ||
80 lstream->bufoffs <= (end - STREAM_BUF_SIZE)))) {
81 lives_usleep(1000);
82 }
83
84 while (1) {
85 // loop until we read the packet, or the user cancels
86 if (lstream->reading) {
87 if (buflen + bufoffs > STREAM_BUF_SIZE) {
88 btoread = STREAM_BUF_SIZE - bufoffs;
89 lives_memcpy(buf, (void *)((uint8_t *)lstream->buffer + bufoffs), btoread);
90 bufoffs = 0;
91 buflen -= btoread;
92 buf = (void *)((uint8_t *)buf + btoread);
93 }
94 lives_memcpy(buf, (void *)((uint8_t *)lstream->buffer + bufoffs), buflen);
95 bufoffs += buflen;
96 return buflen + btoread;
97 } else {
98 weed_plant_t *frame_layer = mainw->frame_layer;
99 mainw->frame_layer = NULL;
101 mainw->frame_layer = frame_layer;
103 if (mainw->cancelled) return buflen + btoread;
104 lives_usleep(prefs->sleep_time);
105 }
106 }
107}
108
109
110static boolean lives_stream_in_chunks(lives_vstream_t *lstream, size_t buflen, uint8_t *buf, int xx) {
111 // read first from pckbuf, then from streambuf
112 size_t copied = 0;
113 if (pckoffs < L2L_PACKET_LEN) {
114 // use up our pckbuf
115 copied = L2L_PACKET_LEN - pckoffs;
116 if (copied > buflen) copied = buflen;
117
118 lives_memcpy(buf, pckbuf, copied);
119
120 buflen -= copied;
121 pckoffs += copied;
122 }
123 if (buflen > 0) l2l_rcv_packet(lstream, buflen, (void *)((uint8_t *)buf + copied));
124 return TRUE;
125}
126#else
127
128
129static size_t l2l_rcv_packet(lives_vstream_t *lstream, size_t buflen, void *buf) {
130 int ret;
131 do {
132 ret = lives_stream_in(lstream->handle, buflen, buf, 0);
133 if (ret == -1) {
134 weed_plant_t *frame_layer = mainw->frame_layer;
135 mainw->frame_layer = NULL;
137 mainw->frame_layer = frame_layer;
139 if (mainw->cancelled) {
140 return -1;
141 }
142 lives_usleep(prefs->sleep_time);
143 }
144 } while (ret == -1);
145 return ret;
146}
147
148
149static boolean lives_stream_in_chunks(lives_vstream_t *lstream, size_t buflen, uint8_t *buf, int bfsize) {
150 // return FALSE if we could not set socket buffer size
151
152 size_t copied;
153
154 if (pckoffs < pcksize) {
155 // use up our pckbuf
156 copied = pcksize - pckoffs;
157 if (copied > buflen) copied = buflen;
158
159 lives_memcpy(buf, pckbuf, copied);
160
161 buflen -= copied;
162 pckoffs += copied;
163 buf += copied;
164 }
165 while (buflen > 0) {
166 // read in the rest
167 do {
168 copied = lives_stream_in(lstream->handle, buflen, buf, bfsize);
169 if (copied == -2) return FALSE;
170 if (copied == -1) {
171 weed_plant_t *frame_layer = mainw->frame_layer;
172 mainw->frame_layer = NULL;
174 mainw->frame_layer = frame_layer;
176 if (mainw->cancelled) return TRUE;
177 lives_usleep(prefs->sleep_time);
178 }
179 } while (copied == -1);
180 buflen -= copied;
181 buf += copied;
182 }
183 return TRUE;
184}
185
186#endif
187
188static void l2l_get_packet_sync(lives_vstream_t *lstream) {
189 boolean sync = FALSE;
190
191 if (pckoffs == pcksize) {
192 pcksize = l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf);
193 pckoffs = 0;
194 if (mainw->cancelled) return;
195 }
196
197 while (!sync) {
198 while (strncmp(pckbuf + pckoffs, "P", 1)) {
199 if (++pckoffs == pcksize) {
200 pcksize = l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf);
201 pckoffs = 0;
202 if (mainw->cancelled) return;
203 }
204 }
205 if (++pckoffs == pcksize) {
206 pcksize = l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf);
207 pckoffs = 0;
208 if (mainw->cancelled) return;
209 }
210 if (strncmp(pckbuf + pckoffs, "A", 1)) continue;
211 if (++pckoffs == pcksize) {
212 pcksize = l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf);
213 pckoffs = 0;
214 if (mainw->cancelled) return;
215 }
216 if (strncmp(pckbuf + pckoffs, "C", 1)) continue;
217 if (++pckoffs == pcksize) {
218 pcksize = l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf);
219 pckoffs = 0;
220 if (mainw->cancelled) return;
221 }
222 if (strncmp(pckbuf + pckoffs, "K", 1)) continue;
223 if (++pckoffs == pcksize) {
224 pcksize = l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf);
225 pckoffs = 0;
226 if (mainw->cancelled) return;
227 }
228 if (strncmp(pckbuf + pckoffs, "E", 1)) continue;
229 if (++pckoffs == pcksize) {
230 pcksize = l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf);
231 pckoffs = 0;
232 if (mainw->cancelled) return;
233 }
234 if (strncmp(pckbuf + pckoffs, "T", 1)) continue;
235 if (++pckoffs == pcksize) {
236 pcksize = l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf);
237 pckoffs = 0;
238 if (mainw->cancelled) return;
239 }
240 if (strncmp(pckbuf + pckoffs, " ", 1)) continue;
241
242 sync = TRUE;
243 }
244 pckoffs++;
245}
246
247
248static char *l2l_get_packet_header(lives_vstream_t *lstream) {
249 size_t hdrsize = 0, csize;
250
251 char hdr_buf[1024];
252
253 boolean sync = FALSE;
254
255 if (pckoffs == pcksize) {
256 pcksize += l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf + pckoffs);
257 if (mainw->cancelled) return NULL;
258 }
259
260 while (!sync) {
261 if (pckoffs == pcksize) {
262 if (pcksize > L2L_PACKET_LEN) {
263 csize = pcksize;
264 pcksize = (pcksize + 1) >> 1;
265 csize -= pcksize;
266 lives_memcpy(pckbuf, pckbuf + pcksize, csize);
267 pckoffs -= pcksize;
268 pcksize = csize;
269 }
270 pcksize += l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf + pckoffs);
271 if (mainw->cancelled) return NULL;
272 }
273
274 while (strncmp(pckbuf + pckoffs, "D", 1)) {
275 hdr_buf[hdrsize++] = pckbuf[pckoffs];
276 if (hdrsize > 1000) {
277 if (pckoffs >= L2L_PACKET_LEN) {
278 lives_memcpy(pckbuf, pckbuf + L2L_PACKET_LEN, L2L_PACKET_LEN);
279 pckoffs -= L2L_PACKET_LEN;
280 }
281 return NULL;
282 }
283 if (++pckoffs == pcksize) {
284 if (pcksize > L2L_PACKET_LEN) {
285 csize = pcksize;
286 pcksize = (pcksize + 1) >> 1;
287 csize -= pcksize;
288 lives_memcpy(pckbuf, pckbuf + pcksize, csize);
289 pckoffs -= pcksize;
290 pcksize = csize;
291 }
292 pcksize += l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf + pckoffs);
293 if (mainw->cancelled) return NULL;
294 }
295 }
296
297 if (++pckoffs == pcksize) {
298 if (pcksize > L2L_PACKET_LEN) {
299 csize = pcksize;
300 pcksize = (pcksize + 1) >> 1;
301 csize -= pcksize;
302 lives_memcpy(pckbuf, pckbuf + pcksize, csize);
303 pckoffs -= pcksize;
304 pcksize = csize;
305 }
306 pcksize += l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf + pckoffs);
307 if (mainw->cancelled) return NULL;
308 }
309
310 if (strncmp(pckbuf + pckoffs, "A", 1)) {
311 lives_memcpy(&hdr_buf[hdrsize], pckbuf + pckoffs - 1, 2);
312 hdrsize += 2;
313 continue;
314 }
315
316 if (++pckoffs == pcksize) {
317 if (pcksize > L2L_PACKET_LEN) {
318 csize = pcksize;
319 pcksize = (pcksize + 1) >> 1;
320 csize -= pcksize;
321 lives_memcpy(pckbuf, pckbuf + pcksize, csize);
322 pckoffs -= pcksize;
323 pcksize = csize;
324 }
325 pcksize += l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf + pckoffs);
326 if (mainw->cancelled) return NULL;
327 }
328
329 if (strncmp(pckbuf + pckoffs, "T", 1)) {
330 lives_memcpy(&hdr_buf[hdrsize], pckbuf + pckoffs - 2, 3);
331 hdrsize += 3;
332 continue;
333 }
334
335 if (++pckoffs == pcksize) {
336 if (pcksize > L2L_PACKET_LEN) {
337 csize = pcksize;
338 pcksize = (pcksize + 1) >> 1;
339 csize -= pcksize;
340 lives_memcpy(pckbuf, pckbuf + pcksize, csize);
341 pckoffs -= pcksize;
342 pcksize = csize;
343 }
344 pcksize += l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf + pckoffs);
345 if (mainw->cancelled) return NULL;
346 }
347
348 if (strncmp(pckbuf + pckoffs, "A", 1)) {
349 lives_memcpy(&hdr_buf[hdrsize], pckbuf + pckoffs - 3, 4);
350 hdrsize += 4;
351 continue;
352 }
353 sync = TRUE;
354 }
355
356 if (pckoffs >= L2L_PACKET_LEN) {
357 lives_memcpy(pckbuf, pckbuf + L2L_PACKET_LEN, L2L_PACKET_LEN);
358 pckoffs -= L2L_PACKET_LEN;
359 pcksize -= L2L_PACKET_LEN;
360 }
361
362 hdr_buf[hdrsize] = 0;
363
364 pckoffs++;
365
366 return lives_strdup(hdr_buf);
367}
368
369
370static boolean l2l_parse_packet_header(lives_vstream_t *lstream, int strtype, int strid) {
371 char **array = lives_strsplit(hdr, " ", -1);
372 int pid = -1, ptype = -1;
373
374 if (!hdr || !array || !array[0] || !array[1] || !array[2] || !array[3]) {
375 if (array) lives_strfreev(array);
376 return FALSE;
377 }
378
379 ptype = atoi(array[0]);
380 pid = atoi(array[1]);
381
382 if (pid != strid || ptype != strtype) {
383 // wrong stream id, or not video
384 if (array) lives_strfreev(array);
385 return FALSE;
386 }
387
388 lstream->flags = atoi(array[2]);
389 lstream->dsize = atoi(array[3]);
390
391 if (!(lstream->flags & LIVES_VSTREAM_FLAGS_IS_CONTINUATION)) {
392 if (capable->cpu_bits == 32) {
393 lstream->timecode = strtoll(array[4], NULL, 10);
394 } else {
395 lstream->timecode = strtol(array[4], NULL, 10);
396 }
397
398 lstream->hsize = atoi(array[5]);
399 lstream->vsize = atoi(array[6]);
400 lstream->fps = lives_strtod(array[7], NULL);
401 lstream->palette = atoi(array[8]);
402 lstream->YUV_sampling = atoi(array[9]);
403 lstream->YUV_clamping = atoi(array[10]);
404 lstream->YUV_subspace = atoi(array[11]);
405 lstream->compression_type = atoi(array[12]);
406 }
407
408 lives_strfreev(array);
409 lstream->data_ready = TRUE;
410
411 return TRUE;
412}
413
414
415void lives2lives_read_stream(const char *host, int port) {
417
418 char *tmp;
419 char *hostname;
420
421#ifdef USE_STRMBUF
422 pthread_t stthread;
423 pthread_attr_t pattr;
424#endif
425
426 boolean done = FALSE;
427
428 int old_file = mainw->current_file, new_file;
429
431
432 lstream->handle = OpenHTMSocket(host, port, FALSE);
433 if (!lstream->handle) {
435 do_error_dialog(_("LiVES to LiVES stream error: Could not open port !\n"));
438 return;
439 }
440
441 d_print(_("Waiting for LiVES stream on port %d..."), port);
442
444 do_threaded_dialog(_("\nWaiting for stream"), TRUE);
445
446#ifdef USE_STRMBUF
447 buffering = TRUE;
448 lstream->reading = FALSE;
449
450 pthread_attr_init(&pattr);
451 if (pthread_attr_setstacksize(&pattr, STREAM_BUF_SIZE * 4)) {
453 CloseHTMSocket(lstream->handle);
454 lives_free(lstream);
456 do_error_dialog(_("LiVES to LiVES stream error: Could not set buffer size !\n"));
458 return;
459 }
460 lstream->buffer = lives_malloc(STREAM_BUF_SIZE);
461 pthread_create(&stthread, &pattr, streambuf, (void *)lstream);
462 pthread_attr_destroy(&pattr);
463#endif
464
465 pcksize = l2l_rcv_packet(lstream, L2L_PACKET_LEN, pckbuf);
466 pckoffs = 0;
467 if (mainw->cancelled) {
469#ifdef USE_STRMBUF
470 buffering = FALSE;
471 pthread_join(stthread, NULL);
472#endif
473 CloseHTMSocket(lstream->handle);
474#ifdef USE_STRMBUF
475 lives_free(lstream->buffer);
476#endif
477 lives_free(lstream);
480 return;
481 }
482
483 while (!done) {
484 do {
485 // get video stream 0 PACKET
486 l2l_get_packet_sync(lstream);
487 if (mainw->cancelled) {
489#ifdef USE_STRMBUF
490 buffering = FALSE;
491 pthread_join(stthread, NULL);
492#endif
493 CloseHTMSocket(lstream->handle);
494#ifdef USE_STRMBUF
495 lives_free(lstream->buffer);
496#endif
497 lives_free(lstream);
500 return;
501 }
502 // get packet header
503 hdr = l2l_get_packet_header(lstream);
504 if (mainw->cancelled) {
506#ifdef USE_STRMBUF
507 buffering = FALSE;
508 pthread_join(stthread, NULL);
509#endif
510 CloseHTMSocket(lstream->handle);
511#ifdef USE_STRMBUF
512 lives_free(lstream->buffer);
513#endif
514 lives_free(lstream);
517 return;
518 }
519 } while (!hdr);
520 // parse packet header
521 done = l2l_parse_packet_header(lstream, LIVES_STREAM_TYPE_VIDEO, 0);
522 if (lstream->flags & LIVES_VSTREAM_FLAGS_IS_CONTINUATION) done = FALSE;
523 if (!done) {
524 // wrong packet type or id, or a continuation packet
525 uint8_t *tmpbuf = (uint8_t *)lives_malloc(lstream->dsize);
526 lives_stream_in_chunks(lstream, lstream->dsize, tmpbuf, lstream->dsize * 4);
527 // throw this packet away
528 lives_printerr("unrecognised packet in stream - dropping it.\n");
529 lives_free(tmpbuf);
530 if (mainw->cancelled) {
532#ifdef USE_STRMBUF
533 buffering = FALSE;
534 pthread_join(stthread, NULL);
535#endif
536 CloseHTMSocket(lstream->handle);
537#ifdef USE_STRMBUF
538 lives_free(lstream->buffer);
539#endif
540 lives_free(lstream);
543 return;
544 }
545 }
546 lives_free(hdr);
547 }
548
550
551 if (mainw->fixed_fpsd <= 0.) fps_can_change = TRUE;
552 else fps_can_change = FALSE;
553
554 if (mainw->fixed_fpsd > 0. && (cfile->fps != mainw->fixed_fpsd)) {
556 do_error_dialog(_("\n\nUnable to open stream, framerate does not match fixed rate.\n"));
558#ifdef USE_STRMBUF
559 buffering = FALSE;
560 pthread_join(stthread, NULL);
561#endif
562 CloseHTMSocket(lstream->handle);
563#ifdef USE_STRMBUF
564 lives_free(lstream->buffer);
565#endif
566 lives_free(lstream);
569 return;
570 }
571
572 // now we should have lstream details
573
574 //
575 new_file = mainw->first_free_file;
576 if (!get_new_handle(new_file, "LiVES to LiVES stream")) {
577 mainw->error = TRUE;
578#ifdef USE_STRMBUF
579 buffering = FALSE;
580 pthread_join(stthread, NULL);
581#endif
582 CloseHTMSocket(lstream->handle);
583#ifdef USE_STRMBUF
584 lives_free(lstream->buffer);
585#endif
586 lives_free(lstream);
589 return;
590 }
591
592 mainw->current_file = new_file;
593 cfile->clip_type = CLIP_TYPE_LIVES2LIVES;
594
595 cfile->fps = lstream->fps;
596 cfile->hsize = lstream->hsize;
597 cfile->vsize = lstream->vsize;
598
599 cfile->ext_src = lstream;
600 cfile->ext_src_type = LIVES_EXT_SRC_STREAM;
601
602 switch_to_file((mainw->current_file = old_file), new_file);
603 set_main_title(cfile->file_name, 0);
605
606 cfile->achans = 0;
607 cfile->asampsize = 0;
608
609 // open as a clip with 1 frame
610 cfile->start = cfile->end = cfile->frames = 1;
611 cfile->arps = cfile->arate = 0;
612 mainw->fixed_fpsd = cfile->pb_fps = cfile->fps;
613
614 cfile->opening = FALSE;
615 mainw->proc_ptr = NULL;
616
617 cfile->changed = FALSE;
618
619 // allow clip switching
620 cfile->is_loaded = TRUE;
621
622 d_print("\n");
623
624 lives_snprintf(cfile->type, 40, "LiVES to LiVES stream in");
625
626 if (!strcmp(host, "INADDR_ANY")) hostname = (_("any host"));
627 else hostname = (_("host %d"));
628
629 d_print(_("Opened LiVES to LiVES stream from %s on port %d"), hostname, port);
630 lives_free(hostname);
631 d_print(_(" size=%dx%d bpp=%d fps=%.3f\nAudio: "), cfile->hsize, cfile->vsize, cfile->bpp, cfile->fps);
632
633 if (cfile->achans == 0) {
634 d_print(_("none\n"));
635 } else {
636 d_print(P_("%d Hz %d channel %d bps\n", "%d Hz %d channels %d bps\n", cfile->achans), cfile->arate, cfile->achans,
637 cfile->asampsize);
638 }
639
640 d_print(_("Syncing to external framerate of %s frames per second.\n"), (tmp = remove_trailing_zeroes(mainw->fixed_fpsd)));
641 lives_free(tmp);
642
643 has_last_delta_ticks = FALSE;
644
645 // if not playing, start playing
646 if (!LIVES_IS_PLAYING) {
648 return;
649 }
650 // TODO - else...
651
652 if (mainw->current_file != old_file &&
653 mainw->current_file != new_file) old_file = mainw->current_file; // we could have rendered to a new file
654
655 mainw->fixed_fpsd = -1.;
656 d_print(_("Sync lock off.\n"));
657 mainw->current_file = new_file;
658#ifdef USE_STRMBUF
659 buffering = FALSE;
660 pthread_join(stthread, NULL);
661#endif
662 CloseHTMSocket(lstream->handle);
663#ifdef USE_STRMBUF
664 lives_free(lstream->buffer);
665#endif
666 lives_free(cfile->ext_src);
667 cfile->ext_src = NULL;
668 cfile->ext_src_type = LIVES_EXT_SRC_NONE;
669
670 close_current_file(old_file);
672}
673
674
676 static ticks_t last_delta_ticks = 0;
677 ticks_t currticks;
678
679 void **pixel_data;
680
681 size_t framedataread = 0;
682 size_t target_size;
683
684 boolean timeout = FALSE; // TODO
685 boolean done;
686
687 int myflags = 0, width = 0, height = 0;
688
689 while (!timeout) {
690 // loop until we read all frame data, or we get a new frame
691 done = FALSE;
692 if (!lstream->data_ready) {
693 while (!done) {
694 // get video stream 0 PACKET
695 do {
696 l2l_get_packet_sync(lstream);
697 if (mainw->cancelled) return;
698 // get packet header
699 hdr = l2l_get_packet_header(lstream);
700 if (mainw->cancelled) return;
701 } while (!hdr && mainw->cancelled == CANCEL_NONE);
702 if (mainw->cancelled) {
703 lives_free(hdr);
704 hdr = NULL;
705 return;
706 }
707 // parse packet header
708 done = l2l_parse_packet_header(lstream, LIVES_STREAM_TYPE_VIDEO, 0);
710 done = FALSE;
712 // we missed some continuation packets, just return what we have
713 lstream->data_ready = TRUE;
714 lives_free(hdr);
715 hdr = NULL;
716 return;
717 }
718
719 if (!done) {
720 // wrong packet type or id, or a continuation of previous frame
721 uint8_t *tmpbuf = (uint8_t *)lives_malloc(lstream->dsize);
722 lives_stream_in_chunks(lstream, lstream->dsize, tmpbuf, lstream->dsize * 4);
723 // throw this packet away
724 lives_printerr("unrecognised packet in stream - dropping it.\n");
725 lives_free(tmpbuf);
726 if (mainw->cancelled) {
727 lives_free(hdr);
728 hdr = NULL;
729 return;
730 }
731 }
732 lives_free(hdr);
733 hdr = NULL;
734
735 if (lstream->fps != mainw->fixed_fpsd && fps_can_change) {
736 char *tmp;
737 d_print(_("Detected new framerate for stream:\n"));
738 mainw->files[clip]->fps = mainw->fixed_fpsd = lstream->fps;
739 d_print(_("Syncing to external framerate of %s frames per second.\n"),
741 lives_free(tmp);
742 has_last_delta_ticks = FALSE;
743 if (clip == mainw->current_file) set_main_title(cfile->file_name, 0);
744 }
745
746#define DROP_AGING_FRAMES
747#ifdef DROP_AGING_FRAMES
748 // this seems to help smoothing when recording, however I have only tested it on one machine
749 // where frames were being generated and streamed and then received
750 // - needs testing in other situations
751 currticks = lives_get_current_ticks();
752
753 if (mainw->record && !mainw->record_paused) {
754 if (has_last_delta_ticks && (abs64(currticks - lstream->timecode)) < last_delta_ticks) {
755 // drop this frame
756 uint8_t *tmpbuf = (uint8_t *)lives_malloc(lstream->dsize);
757 lives_stream_in_chunks(lstream, lstream->dsize, tmpbuf, lstream->dsize * 4);
758 // throw this packet away
759#ifdef DEBUG_STREAM_AGING
760 lives_printerr("packet too early (!) - dropping it.\n");
761#endif
762 lives_free(tmpbuf);
763 done = FALSE;
764 if (mainw->cancelled) {
765 lives_free(hdr);
766 hdr = NULL;
767 return;
768 }
769 }
770 }
771 last_delta_ticks = ((ticks_t)(last_delta_ticks >> 1) + (ticks_t)((abs64(currticks - lstream->timecode)) >> 1));
772#endif
773 }
774 }
775
776 if (!has_last_delta_ticks) {
777 last_delta_ticks = abs64(mainw->currticks - lstream->timecode);
778 }
779 has_last_delta_ticks = TRUE;
780
781 lstream->data_ready = FALSE;
782
783 width = mainw->files[clip]->hsize;
784 height = mainw->files[clip]->vsize;
785
786 if (lstream->hsize != width || lstream->vsize != height) {
787 // frame size changed...
788 d_print(_("Detected frame size change to %d x %d\n"), lstream->hsize, lstream->vsize);
789
790 mainw->files[clip]->hsize = lstream->hsize;
791 mainw->files[clip]->vsize = lstream->vsize;
792
793 if (clip == mainw->current_file) {
794 set_main_title(cfile->file_name, 0);
795 }
797 }
798
799 width = height = 0;
800
801 if (weed_plant_has_leaf(layer, WEED_LEAF_HEIGHT)) height = weed_get_int_value(layer, WEED_LEAF_HEIGHT, NULL);
802 if (weed_plant_has_leaf(layer, WEED_LEAF_WIDTH)) width = weed_get_int_value(layer, WEED_LEAF_WIDTH, NULL);
803
804 if (lstream->hsize != width || lstream->vsize != height) {
806 }
807
808 if (!weed_plant_has_leaf(layer, WEED_LEAF_PIXEL_DATA) || !weed_get_voidptr_value(layer, WEED_LEAF_PIXEL_DATA, NULL)) {
809 weed_set_int_value(layer, WEED_LEAF_WIDTH, lstream->hsize);
810 weed_set_int_value(layer, WEED_LEAF_HEIGHT, lstream->vsize);
811 weed_set_int_value(layer, WEED_LEAF_CURRENT_PALETTE, lstream->palette);
812 weed_set_int_value(layer, WEED_LEAF_YUV_CLAMPING, lstream->YUV_clamping);
814 }
815
816 pixel_data = weed_get_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, NULL);
817
818 switch (lstream->palette) {
819 case WEED_PALETTE_RGB24:
820 target_size = lstream->hsize * lstream->vsize * 3 - framedataread;
821#ifdef USE_STRMBUF
822 if (target_size > lstream->dsize) target_size = lstream->dsize;
823 lives_stream_in_chunks(lstream, target_size, (uint8_t *)pixel_data[0] + framedataread, 0);
824 lstream->dsize -= target_size;
825 framedataread += target_size;
826#else
827 if (target_size >= lstream->dsize) {
828 if (!lives_stream_in_chunks(lstream, target_size, (uint8_t *)pixel_data[0] + framedataread, lstream->dsize * 12)) {
829 do_rmem_max_error(lstream->dsize * 12);
831 }
832 if (mainw->cancelled) {
833 lives_free(pixel_data);
834 return;
835 }
836 }
837#endif
838 lives_free(pixel_data);
839 if (framedataread >= lstream->hsize * lstream->vsize * 3) {
840 return;
841 }
843 break;
844 case WEED_PALETTE_YUV420P:
845 // assume uncompressed, - TODO
846
847 if (framedataread < lstream->hsize * lstream->vsize) {
848 target_size = lstream->hsize * lstream->vsize - framedataread;
849#ifdef USE_STRMBUF
850 if (target_size > lstream->dsize) target_size = lstream->dsize;
851 lives_stream_in_chunks(lstream, target_size, (uint8_t *)pixel_data[0] + framedataread, 0);
852 lstream->dsize -= target_size;
853 framedataread += target_size;
854#else
855 if (target_size >= lstream->dsize) {
856 // packet contains data for single plane
857 if (!lives_stream_in_chunks(lstream, lstream->dsize, (uint8_t *)pixel_data[0] + framedataread, lstream->dsize * 9)) {
858 do_rmem_max_error(lstream->dsize * 9);
860 }
861 if (mainw->cancelled) {
862 lives_free(pixel_data);
863 return;
864 }
865 } else {
866 // this packet contains data for multiple planes
867 uint8_t *fbuffer = (uint8_t *)lives_malloc(lstream->dsize);
868 size_t fbufoffs = 0;
869 size_t dsize = lstream->dsize;
870
871 if (!lives_stream_in_chunks(lstream, lstream->dsize, fbuffer, lstream->dsize * 8)) {
872 do_rmem_max_error(lstream->dsize * 8);
874 }
875 if (mainw->cancelled) {
876 lives_free(pixel_data);
877 lives_free(fbuffer);
878 return;
879 }
880 lives_memcpy((uint8_t *)pixel_data[0] + framedataread, fbuffer, target_size);
881 dsize -= target_size;
882 fbufoffs += target_size;
883
884 target_size = (lstream->hsize * lstream->vsize) >> 2;
885 if (target_size > dsize) target_size = dsize;
886
887 if (target_size > 0) lives_memcpy((uint8_t *)pixel_data[1], fbuffer + fbufoffs, target_size);
888
889 dsize -= target_size;
890 fbufoffs += target_size;
891
892 target_size = (lstream->hsize * lstream->vsize) >> 2;
893 if (target_size > dsize) target_size = dsize;
894
895 if (target_size > 0) lives_memcpy((uint8_t *)pixel_data[2], fbuffer + fbufoffs, target_size);
896
897 lives_free(fbuffer);
898 }
899#endif
900 }
901#ifdef USE_STRMBUF
902 if (framedataread < (lstream->hsize * lstream->vsize * 5) >> 2) {
903 target_size = ((lstream->hsize * lstream->vsize * 5) >> 2) - framedataread;
904 if (target_size > lstream->dsize) target_size = lstream->dsize;
905 lives_stream_in_chunks(lstream, target_size,
906 (uint8_t *)pixel_data[1] + framedataread - lstream->hsize * lstream->vsize, 0);
907 lstream->dsize -= target_size;
908 framedataread += target_size;
909#else
910 else if (framedataread < (lstream->hsize * lstream->vsize * 5) >> 2) {
911 target_size = ((lstream->hsize * lstream->vsize * 5) >> 2) - framedataread;
912 if (target_size >= lstream->dsize) {
913 lives_stream_in_chunks(lstream, lstream->dsize,
914 ((uint8_t *)pixel_data[1] + framedataread - lstream->hsize * lstream->vsize), 0);
915 if (mainw->cancelled) {
916 lives_free(pixel_data);
917 return;
918 }
919 } else {
920 // this packet contains data for multiple planes
921 uint8_t *fbuffer = (uint8_t *)lives_malloc(lstream->dsize);
922 size_t fbufoffs = 0;
923 size_t dsize = lstream->dsize;
924
925 lives_stream_in_chunks(lstream, lstream->dsize, fbuffer, 0);
926 if (mainw->cancelled) {
927 lives_free(pixel_data);
928 lives_free(fbuffer);
929 return;
930 }
931 lives_memcpy((uint8_t *)pixel_data[1] + framedataread - lstream->hsize * lstream->vsize, fbuffer, target_size);
932
933 dsize -= target_size;
934 fbufoffs += target_size;
935
936 target_size = (lstream->hsize * lstream->vsize) >> 2;
937 if (target_size > dsize) target_size = dsize;
938
939 if (target_size > 0) lives_memcpy((uint8_t *)pixel_data[2], fbuffer + fbufoffs, target_size);
940
941 lives_free(fbuffer);
942 }
943#endif
944 }
945#ifdef USE_STRMBUF
946 if (framedataread < (lstream->hsize * lstream->vsize * 6) >> 2) {
947 target_size = ((lstream->hsize * lstream->vsize) >> 2) - framedataread + ((lstream->hsize * lstream->vsize * 5) >> 2);
948 if (target_size > lstream->dsize) target_size = lstream->dsize;
949 lives_stream_in_chunks(lstream, target_size, (uint8_t *)pixel_data[2] + framedataread -
950 ((lstream->hsize * lstream->vsize * 5) >> 2), 0);
951 lstream->dsize -= target_size;
952 framedataread += target_size;
953 }
954#else
955 else {
956 target_size = ((lstream->hsize * lstream->vsize * 3) >> 1) - framedataread;
957 if (target_size >= lstream->dsize) target_size = lstream->dsize;
958 lives_stream_in_chunks(lstream, target_size,
959 ((uint8_t *)pixel_data[2] + framedataread - ((lstream->hsize * lstream->vsize * 5) >> 2)), 0);
960 if (mainw->cancelled) {
961 lives_free(pixel_data);
962 return;
963 }
964 }
965 framedataread += lstream->dsize;
966#endif
967 lives_free(pixel_data);
968 if (framedataread >= (lstream->hsize * lstream->vsize * 3) >> 1) {
969 return;
970 }
972 break;
973 }
974 }
975}
976
977
979
980// gui bits
981
982void on_send_lives2lives_activate(LiVESMenuItem *menuitem, livespointer user_data) {
983 _vppaw *vppa;
984
985 char *orig_name = lives_strdup(mainw->string_constants[LIVES_STRING_CONSTANT_NONE]);
986 char *tmp;
987
988 int resp;
989
990 if (mainw->vpp) {
991 lives_free(orig_name);
992 orig_name = lives_strdup(mainw->vpp->name);
993 }
994
995 if (!mainw->vpp || strcmp(mainw->vpp->name, "lives2lives_stream")) {
996 lives_snprintf(future_prefs->vpp_name, 64, "lives2lives_stream");
997 }
998 vppa = on_vpp_advanced_clicked(NULL, LIVES_INT_TO_POINTER(LIVES_INTENTION_STREAM));
999 resp = lives_dialog_run(LIVES_DIALOG(vppa->dialog));
1000
1001 if (vppa->rfx) {
1002 tmp = lives_build_filename(prefs->workdir, vppa->rfx->name, NULL);
1003 lives_rm(tmp);
1004 lives_free(tmp);
1005 }
1006
1007 if (resp == LIVES_RESPONSE_CANCEL) {
1008 lives_free(orig_name);
1009 return;
1010 }
1011
1012 set_vpp(FALSE);
1013
1014 if (strcmp(orig_name, "lives2lives_stream")) {
1016 do_info_dialogf(_("\nLiVES will stream whenever it is in full screen/separate window mode.\n"
1017 "To reset this behaviour, go to Tools/Preferences/Playback,\nand set the playback "
1018 "plugin back to %s\n"),
1019 orig_name);
1021 }
1022 lives_free(orig_name);
1023}
1024
1025
1026void on_open_lives2lives_activate(LiVESMenuItem *menuitem, livespointer user_data) {
1028
1029 char *host = NULL;
1030
1031 int port = 0;
1032 int response = lives_dialog_run(LIVES_DIALOG(pandh->dialog));
1033
1034 if (response == LIVES_RESPONSE_OK) {
1035 if (!lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(pandh->rb_anyhost))) {
1036 host = lives_strdup_printf("%s.%s.%s.%s", lives_entry_get_text(LIVES_ENTRY(pandh->entry1)),
1037 lives_entry_get_text(LIVES_ENTRY(pandh->entry2)),
1038 lives_entry_get_text(LIVES_ENTRY(pandh->entry3)),
1039 lives_entry_get_text(LIVES_ENTRY(pandh->entry4)));
1040 } else host = lives_strdup("INADDR_ANY");
1041 port = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(pandh->port_spin));
1043 }
1044
1045 lives_free(pandh);
1046
1048
1049 if (host) {
1050 // start receiving
1051 lives2lives_read_stream(host, port);
1052 lives_free(host);
1053 }
1054}
1055
1056
1057static void pandhw_anyhost_toggled(LiVESToggleButton *tbut, livespointer user_data) {
1058 lives_pandh_w *pandhw = (lives_pandh_w *)user_data;
1059
1065 } else {
1070 }
1071}
1072
1073
1075 // type = 0 lives2lives stream input
1076
1077 LiVESWidget *dialog_vbox;
1078 LiVESWidget *hbox;
1079 LiVESWidget *label;
1080
1081 LiVESSList *radiobutton_group = NULL;
1082
1083 lives_pandh_w *pandhw = (lives_pandh_w *)(lives_malloc(sizeof(lives_pandh_w)));
1084
1085 char *tmp, *tmp2;
1086
1087 pandhw->dialog = lives_standard_dialog_new(_("Receive LiVES Stream"), TRUE, -1, -1);
1088
1089 dialog_vbox = lives_dialog_get_content_area(LIVES_DIALOG(pandhw->dialog));
1090
1091 label = lives_standard_label_new(_("You can receive streams from another copy of LiVES."));
1092 lives_box_pack_start(LIVES_BOX(dialog_vbox), label, FALSE, FALSE, widget_opts.packing_height);
1093
1095 _("In the source copy of LiVES, you must select Advanced/Send stream to LiVES\n"
1096 "or select the lives2lives_stream playback plugin in Preferences."));
1097 lives_box_pack_start(LIVES_BOX(dialog_vbox), label, FALSE, FALSE, widget_opts.packing_height);
1098
1099 add_hsep_to_box(LIVES_BOX(dialog_vbox));
1100
1101 label = lives_standard_label_new(_("Select the host to receive the stream from (or allow any host to stream)."));
1102 lives_box_pack_start(LIVES_BOX(dialog_vbox), label, FALSE, FALSE, widget_opts.packing_height);
1103
1104 hbox = lives_hbox_new(FALSE, 0);
1105 lives_box_pack_start(LIVES_BOX(dialog_vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1106
1107 pandhw->rb_anyhost = lives_standard_radio_button_new((tmp = (_("Accept LiVES streams from _any host"))),
1108 &radiobutton_group,
1109 LIVES_BOX(hbox),
1110 (tmp2 = (_("Accept incoming LiVES streams from any connected host."))));
1111
1112 lives_free(tmp);
1113 lives_free(tmp2);
1114
1115 lives_signal_connect_after(LIVES_GUI_OBJECT(pandhw->rb_anyhost), LIVES_WIDGET_TOGGLED_SIGNAL,
1116 LIVES_GUI_CALLBACK(pandhw_anyhost_toggled),
1117 (livespointer)pandhw);
1118
1119 hbox = lives_hbox_new(FALSE, 0);
1120 lives_box_pack_start(LIVES_BOX(dialog_vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1121
1122 lives_standard_radio_button_new((tmp = (_("Accept LiVES streams only from the _specified host:"))),
1123 &radiobutton_group, LIVES_BOX(hbox),
1124 (tmp2 = (_("Accept LiVES streams from the specified host only."))));
1125
1126 lives_free(tmp); lives_free(tmp2);
1127
1129
1130 hbox = lives_hbox_new(FALSE, 0);
1131 lives_box_pack_start(LIVES_BOX(dialog_vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1132
1133 pandhw->entry1 = lives_standard_entry_new("", "127", 3, 3, LIVES_BOX(hbox), NULL);
1134 pandhw->entry2 = lives_standard_entry_new(".", "0", 3, 3, LIVES_BOX(hbox), NULL);
1135 pandhw->entry3 = lives_standard_entry_new(".", "0", 3, 3, LIVES_BOX(hbox), NULL);
1136 pandhw->entry4 = lives_standard_entry_new(".", "0", 3, 3, LIVES_BOX(hbox), NULL);
1137
1142
1143 label = lives_standard_label_new(_("Enter the port number to listen for LiVES streams on:"));
1144 lives_box_pack_start(LIVES_BOX(dialog_vbox), label, FALSE, FALSE, widget_opts.packing_height);
1145
1146 hbox = lives_hbox_new(FALSE, 0);
1147 lives_box_pack_start(LIVES_BOX(dialog_vbox), hbox, FALSE, FALSE, widget_opts.packing_height);
1148
1149 pandhw->port_spin = lives_standard_spin_button_new(_("Port"), 48888., 1., 65535, 1., 1., 0, LIVES_BOX(hbox), NULL);
1150
1152
1153 return pandhw;
1154}
boolean create_empty_pixel_data(weed_layer_t *layer, boolean black_fill, boolean may_contig)
creates pixel data for layer
Definition: colourspace.c:9058
void weed_layer_pixel_data_free(weed_layer_t *layer)
free pixel_data from layer
weed_plant_t weed_layer_t
Definition: colourspace.h:71
LIVES_GLOBAL_INLINE void do_rmem_max_error(int size)
Definition: dialogs.c:3755
void do_threaded_dialog(const char *trans_text, boolean has_cancel)
Definition: dialogs.c:3849
LiVESResponseType do_info_dialogf(const char *fmt,...)
Definition: dialogs.c:773
void end_threaded_dialog(void)
Definition: dialogs.c:3883
LIVES_GLOBAL_INLINE LiVESResponseType do_error_dialog(const char *text)
Definition: dialogs.c:749
void threaded_dialog_spin(double fraction)
Definition: dialogs.c:3823
LIVES_GLOBAL_INLINE void frame_size_update(void)
Definition: gui.c:4479
void add_to_clipmenu(void)
Definition: gui.c:4512
void * OpenHTMSocket(const char *host, int portnumber, boolean sender)
Definition: htmsocket.c:40
ssize_t lives_stream_in(void *htmrecvhandle, size_t length, void *buffer, int bfsize)
Definition: htmsocket.c:163
void CloseHTMSocket(void *htmsendhandle)
Definition: htmsocket.c:173
LIVES_GLOBAL_INLINE ticks_t lives_get_current_ticks(void)
Definition: machinestate.c:835
#define lives_free
Definition: machinestate.h:52
#define lives_malloc
Definition: machinestate.h:46
#define lives_memcpy
Definition: machinestate.h:55
void set_main_title(const char *file, int untitled)
Definition: main.c:5005
mainwindow * mainw
Definition: main.c:103
void switch_to_file(int old_file, int new_file)
Definition: main.c:9646
void close_current_file(int file_to_switch_to)
close current file, and try to switch to file_to_switch_to
Definition: main.c:9373
void d_print_cancelled(void)
Definition: utils.c:2610
void start_playback_async(int type)
Definition: saveplay.c:96
#define LIVES_IS_PLAYING
Definition: main.h:840
#define LIVES_INLINE
Definition: main.h:238
boolean get_new_handle(int index, const char *name)
Definition: saveplay.c:3821
#define LIVES_EXT_SRC_NONE
Definition: main.h:1043
int lives_rm(const char *file)
Definition: utils.c:4395
int64_t ticks_t
Definition: main.h:97
void d_print_failed(void)
Definition: utils.c:2615
capability * capable
Definition: main.h:627
#define cfile
Definition: main.h:1833
void d_print(const char *fmt,...)
Definition: utils.c:2542
#define LIVES_EXT_SRC_STREAM
Definition: main.h:1047
@ CLIP_TYPE_LIVES2LIVES
type for LiVES to LiVES streaming
Definition: main.h:770
char * remove_trailing_zeroes(double val)
Definition: utils.c:5395
@ CANCEL_NONE
no cancel
Definition: main.h:701
@ CANCEL_ERROR
cancelled because of error
Definition: main.h:740
@ LIVES_STRING_CONSTANT_NONE
Definition: mainwindow.h:371
_vppaw * on_vpp_advanced_clicked(LiVESButton *button, livespointer user_data)
Definition: plugins.c:727
@ LIVES_INTENTION_STREAM
Definition: plugins.h:46
void set_vpp(boolean set_in_prefs)
Definition: preferences.c:476
_prefs * prefs
Definition: preferences.h:847
_future_prefs * future_prefs
Definition: preferences.h:848
void weed_layer_set_from_lives2lives(weed_layer_t *layer, int clip, lives_vstream_t *lstream)
Definition: stream.c:675
#define L2L_PACKET_LEN
Definition: stream.c:15
void on_open_lives2lives_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: stream.c:1026
void lives2lives_read_stream(const char *host, int port)
Definition: stream.c:415
void on_send_lives2lives_activate(LiVESMenuItem *menuitem, livespointer user_data)
Definition: stream.c:982
lives_pandh_w * create_pandh_dialog(int type)
Definition: stream.c:1074
LIVES_INLINE int64_t abs64(int64_t a)
Definition: stream.c:25
#define LIVES_VSTREAM_FLAGS_IS_CONTINUATION
Definition: stream.h:39
#define LIVES_STREAM_TYPE_VIDEO
Definition: stream.h:36
char vpp_name[64]
new video playback plugin
Definition: preferences.h:801
char workdir[PATH_MAX]
kept in locale encoding
Definition: preferences.h:61
int sleep_time
Definition: preferences.h:176
video playback plugin window - fixed part
Definition: plugins.h:729
lives_rfx_t * rfx
Definition: plugins.h:737
LiVESWidget * dialog
Definition: plugins.h:731
short cpu_bits
Definition: main.h:583
int vsize
frame height (vertical) in pixels
Definition: main.h:897
int hsize
frame width (horizontal) in pixels (NOT macropixels !)
Definition: main.h:896
double fps
Definition: main.h:893
LiVESWidget * entry4
Definition: stream.h:55
LiVESWidget * entry2
Definition: stream.h:53
LiVESWidget * port_spin
Definition: stream.h:56
LiVESWidget * entry1
Definition: stream.h:52
LiVESWidget * rb_anyhost
Definition: stream.h:57
LiVESWidget * entry3
Definition: stream.h:54
LiVESWidget * dialog
Definition: stream.h:51
char * name
the name of the executable (so we can run it !)
Definition: plugins.h:626
int64_t timecode
Definition: stream.h:15
size_t dsize
Definition: stream.h:26
volatile boolean reading
Definition: stream.h:30
boolean data_ready
Definition: stream.h:27
void * handle
Definition: stream.h:28
int YUV_clamping
Definition: stream.h:21
int YUV_subspace
Definition: stream.h:22
int YUV_sampling
Definition: stream.h:20
void * buffer
Definition: stream.h:31
volatile size_t bufoffs
Definition: stream.h:32
int compression_type
Definition: stream.h:23
double fps
Definition: stream.h:18
uint32_t flags
Definition: stream.h:13
_vid_playback_plugin * vpp
video plugin
Definition: mainwindow.h:1572
weed_plant_t * frame_layer
Definition: mainwindow.h:948
volatile ticks_t currticks
wall clock time, updated whenever lives_get_*_ticks is called
Definition: mainwindow.h:1005
double fixed_fpsd
<=0. means free playback
Definition: mainwindow.h:990
lives_clip_t * files[MAX_FILES+1]
+1 for the clipboard
Definition: mainwindow.h:729
volatile boolean record
Definition: mainwindow.h:794
char * string_constants[NUM_LIVES_STRING_CONSTANTS]
Definition: mainwindow.h:1539
volatile lives_cancel_t cancelled
Definition: mainwindow.h:798
int current_file
Definition: mainwindow.h:727
LiVESWidget * open_lives2lives
Definition: mainwindow.h:1118
boolean error
Definition: mainwindow.h:801
volatile boolean record_paused
pause during recording
Definition: mainwindow.h:1557
xprocess * proc_ptr
Definition: mainwindow.h:1090
int first_free_file
Definition: mainwindow.h:728
boolean non_modal
non-modal for dialogs
int packing_height
vertical pixels between widgets
#define lives_strdup_printf(fmt,...)
Definition: support.c:27
#define _(String)
Definition: support.h:44
#define P_(String, StringPlural, n)
Definition: support.h:46
#define TRUE
Definition: videoplugin.h:59
#define FALSE
Definition: videoplugin.h:60
LiVESWidget * lives_standard_entry_new(const char *labeltext, const char *txt, int dispwidth, int maxchars, LiVESBox *box, const char *tooltip)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_toggle_button_get_active(LiVESToggleButton *button)
WIDGET_HELPER_GLOBAL_INLINE LiVESResponseType lives_dialog_run(LiVESDialog *dialog)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_box_pack_start(LiVESBox *box, LiVESWidget *child, boolean expand, boolean fill, uint32_t padding)
LiVESWidget * lives_standard_dialog_new(const char *title, boolean add_std_buttons, int width, int height)
WIDGET_HELPER_GLOBAL_INLINE const char * lives_entry_get_text(LiVESEntry *entry)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_set_sensitive(LiVESWidget *widget, boolean state)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_dialog_get_content_area(LiVESDialog *dialog)
LiVESWidget * add_hsep_to_box(LiVESBox *box)
WIDGET_HELPER_GLOBAL_INLINE int lives_spin_button_get_value_as_int(LiVESSpinButton *button)
LIVES_GLOBAL_INLINE boolean lives_widget_destroy(LiVESWidget *widget)
WIDGET_HELPER_GLOBAL_INLINE boolean lives_widget_show_all(LiVESWidget *widget)
boolean lives_widget_context_update(void)
WIDGET_HELPER_GLOBAL_INLINE LiVESWidget * lives_hbox_new(boolean homogeneous, int spacing)
LiVESWidget * lives_standard_label_new(const char *text)
LiVESWidget * lives_standard_spin_button_new(const char *labeltext, double val, double min, double max, double step, double page, int dp, LiVESBox *box, const char *tooltip)
LiVESWidget * lives_standard_radio_button_new(const char *labeltext, LiVESSList **rbgroup, LiVESBox *box, const char *tooltip)
widget_opts_t widget_opts