15 #define L2L_PACKET_LEN 1024
18 static int pckoffs = 0;
19 static size_t pcksize = 0;
21 static boolean has_last_delta_ticks;
22 static char *hdr = NULL;
23 static boolean fps_can_change;
26 return ((a > 0) ? a : -a);
32 #define STREAM_BUF_SIZE 1024*1024*128 // allow 8MB buffer size; actually we use twice this much: - TODO - make pref
33 static volatile boolean buffering;
35 void *streambuf(
void *arg) {
43 uint8_t tmpbuf[STREAM_BUF_SIZE];
54 if (lstream->
bufoffs + btowrite > STREAM_BUF_SIZE) {
55 btowrite = STREAM_BUF_SIZE - lstream->
bufoffs;
57 tmpbufoffs += btowrite;
72 static size_t l2l_rcv_packet(
lives_vstream_t *lstream,
size_t buflen,
void *buf) {
74 static size_t bufoffs = 0;
76 size_t end = bufoffs + buflen;
78 while ((end < STREAM_BUF_SIZE && lstream->bufoffs >= bufoffs && lstream->
bufoffs <= end) || (end >= STREAM_BUF_SIZE &&
80 lstream->
bufoffs <= (end - STREAM_BUF_SIZE)))) {
87 if (buflen + bufoffs > STREAM_BUF_SIZE) {
88 btoread = STREAM_BUF_SIZE - bufoffs;
92 buf = (
void *)((uint8_t *)buf + btoread);
96 return buflen + btoread;
110 static boolean lives_stream_in_chunks(
lives_vstream_t *lstream,
size_t buflen, uint8_t *buf,
int xx) {
116 if (copied > buflen) copied = buflen;
123 if (buflen > 0) l2l_rcv_packet(lstream, buflen, (
void *)((uint8_t *)buf + copied));
129 static size_t l2l_rcv_packet(
lives_vstream_t *lstream,
size_t buflen,
void *buf) {
149 static boolean lives_stream_in_chunks(
lives_vstream_t *lstream,
size_t buflen, uint8_t *buf,
int bfsize) {
154 if (pckoffs < pcksize) {
156 copied = pcksize - pckoffs;
157 if (copied > buflen) copied = buflen;
169 if (copied == -2)
return FALSE;
179 }
while (copied == -1);
189 boolean sync =
FALSE;
191 if (pckoffs == pcksize) {
198 while (strncmp(pckbuf + pckoffs,
"P", 1)) {
199 if (++pckoffs == pcksize) {
205 if (++pckoffs == pcksize) {
210 if (strncmp(pckbuf + pckoffs,
"A", 1))
continue;
211 if (++pckoffs == pcksize) {
216 if (strncmp(pckbuf + pckoffs,
"C", 1))
continue;
217 if (++pckoffs == pcksize) {
222 if (strncmp(pckbuf + pckoffs,
"K", 1))
continue;
223 if (++pckoffs == pcksize) {
228 if (strncmp(pckbuf + pckoffs,
"E", 1))
continue;
229 if (++pckoffs == pcksize) {
234 if (strncmp(pckbuf + pckoffs,
"T", 1))
continue;
235 if (++pckoffs == pcksize) {
240 if (strncmp(pckbuf + pckoffs,
" ", 1))
continue;
249 size_t hdrsize = 0, csize;
253 boolean sync =
FALSE;
255 if (pckoffs == pcksize) {
256 pcksize += l2l_rcv_packet(lstream,
L2L_PACKET_LEN, pckbuf + pckoffs);
261 if (pckoffs == pcksize) {
264 pcksize = (pcksize + 1) >> 1;
270 pcksize += l2l_rcv_packet(lstream,
L2L_PACKET_LEN, pckbuf + pckoffs);
274 while (strncmp(pckbuf + pckoffs,
"D", 1)) {
275 hdr_buf[hdrsize++] = pckbuf[pckoffs];
276 if (hdrsize > 1000) {
283 if (++pckoffs == pcksize) {
286 pcksize = (pcksize + 1) >> 1;
292 pcksize += l2l_rcv_packet(lstream,
L2L_PACKET_LEN, pckbuf + pckoffs);
297 if (++pckoffs == pcksize) {
300 pcksize = (pcksize + 1) >> 1;
306 pcksize += l2l_rcv_packet(lstream,
L2L_PACKET_LEN, pckbuf + pckoffs);
310 if (strncmp(pckbuf + pckoffs,
"A", 1)) {
311 lives_memcpy(&hdr_buf[hdrsize], pckbuf + pckoffs - 1, 2);
316 if (++pckoffs == pcksize) {
319 pcksize = (pcksize + 1) >> 1;
325 pcksize += l2l_rcv_packet(lstream,
L2L_PACKET_LEN, pckbuf + pckoffs);
329 if (strncmp(pckbuf + pckoffs,
"T", 1)) {
330 lives_memcpy(&hdr_buf[hdrsize], pckbuf + pckoffs - 2, 3);
335 if (++pckoffs == pcksize) {
338 pcksize = (pcksize + 1) >> 1;
344 pcksize += l2l_rcv_packet(lstream,
L2L_PACKET_LEN, pckbuf + pckoffs);
348 if (strncmp(pckbuf + pckoffs,
"A", 1)) {
349 lives_memcpy(&hdr_buf[hdrsize], pckbuf + pckoffs - 3, 4);
362 hdr_buf[hdrsize] = 0;
366 return lives_strdup(hdr_buf);
370 static 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;
374 if (!hdr || !array || !array[0] || !array[1] || !array[2] || !array[3]) {
375 if (array) lives_strfreev(array);
379 ptype = atoi(array[0]);
380 pid = atoi(array[1]);
382 if (pid != strid || ptype != strtype) {
384 if (array) lives_strfreev(array);
388 lstream->
flags = atoi(array[2]);
389 lstream->
dsize = atoi(array[3]);
393 lstream->
timecode = strtoll(array[4], NULL, 10);
395 lstream->
timecode = strtol(array[4], NULL, 10);
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]);
408 lives_strfreev(array);
423 pthread_attr_t pattr;
426 boolean done =
FALSE;
441 d_print(
_(
"Waiting for LiVES stream on port %d..."), port);
450 pthread_attr_init(&pattr);
451 if (pthread_attr_setstacksize(&pattr, STREAM_BUF_SIZE * 4)) {
456 do_error_dialog(
_(
"LiVES to LiVES stream error: Could not set buffer size !\n"));
461 pthread_create(&stthread, &pattr, streambuf, (
void *)lstream);
462 pthread_attr_destroy(&pattr);
471 pthread_join(stthread, NULL);
486 l2l_get_packet_sync(lstream);
491 pthread_join(stthread, NULL);
503 hdr = l2l_get_packet_header(lstream);
508 pthread_join(stthread, NULL);
525 uint8_t *tmpbuf = (uint8_t *)
lives_malloc(lstream->dsize);
526 lives_stream_in_chunks(lstream, lstream->dsize, tmpbuf, lstream->dsize * 4);
528 lives_printerr(
"unrecognised packet in stream - dropping it.\n");
534 pthread_join(stthread, NULL);
552 else fps_can_change =
FALSE;
556 do_error_dialog(
_(
"\n\nUnable to open stream, framerate does not match fixed rate.\n"));
560 pthread_join(stthread, NULL);
580 pthread_join(stthread, NULL);
595 cfile->fps = lstream->fps;
596 cfile->hsize = lstream->hsize;
597 cfile->vsize = lstream->vsize;
599 cfile->ext_src = lstream;
607 cfile->asampsize = 0;
624 lives_snprintf(
cfile->type, 40,
"LiVES to LiVES stream in");
626 if (!strcmp(host,
"INADDR_ANY")) hostname = (
_(
"any host"));
627 else hostname = (
_(
"host %d"));
629 d_print(
_(
"Opened LiVES to LiVES stream from %s on port %d"), hostname, port);
633 if (
cfile->achans == 0) {
643 has_last_delta_ticks =
FALSE;
660 pthread_join(stthread, NULL);
667 cfile->ext_src = NULL;
676 static ticks_t last_delta_ticks = 0;
681 size_t framedataread = 0;
684 boolean timeout =
FALSE;
687 int myflags = 0, width = 0, height = 0;
696 l2l_get_packet_sync(lstream);
699 hdr = l2l_get_packet_header(lstream);
722 lives_stream_in_chunks(lstream, lstream->
dsize, tmpbuf, lstream->
dsize * 4);
724 lives_printerr(
"unrecognised packet in stream - dropping it.\n");
737 d_print(
_(
"Detected new framerate for stream:\n"));
739 d_print(
_(
"Syncing to external framerate of %s frames per second.\n"),
742 has_last_delta_ticks =
FALSE;
746 #define DROP_AGING_FRAMES
747 #ifdef DROP_AGING_FRAMES
754 if (has_last_delta_ticks && (
abs64(currticks - lstream->
timecode)) < last_delta_ticks) {
757 lives_stream_in_chunks(lstream, lstream->
dsize, tmpbuf, lstream->
dsize * 4);
759 #ifdef DEBUG_STREAM_AGING
760 lives_printerr(
"packet too early (!) - dropping it.\n");
776 if (!has_last_delta_ticks) {
779 has_last_delta_ticks =
TRUE;
786 if (lstream->
hsize != width || lstream->
vsize != height) {
788 d_print(
_(
"Detected frame size change to %d x %d\n"), lstream->
hsize, lstream->
vsize);
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);
804 if (lstream->
hsize != width || lstream->
vsize != height) {
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);
816 pixel_data = weed_get_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, NULL);
819 case WEED_PALETTE_RGB24:
820 target_size = lstream->
hsize * lstream->
vsize * 3 - framedataread;
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;
827 if (target_size >= lstream->
dsize) {
828 if (!lives_stream_in_chunks(lstream, target_size, (uint8_t *)pixel_data[0] + framedataread, lstream->
dsize * 12)) {
839 if (framedataread >= lstream->
hsize * lstream->
vsize * 3) {
844 case WEED_PALETTE_YUV420P:
847 if (framedataread < lstream->hsize * lstream->
vsize) {
848 target_size = lstream->
hsize * lstream->
vsize - framedataread;
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;
855 if (target_size >= lstream->
dsize) {
857 if (!lives_stream_in_chunks(lstream, lstream->
dsize, (uint8_t *)pixel_data[0] + framedataread, lstream->
dsize * 9)) {
869 size_t dsize = lstream->
dsize;
871 if (!lives_stream_in_chunks(lstream, lstream->
dsize, fbuffer, lstream->
dsize * 8)) {
880 lives_memcpy((uint8_t *)pixel_data[0] + framedataread, fbuffer, target_size);
881 dsize -= target_size;
882 fbufoffs += target_size;
884 target_size = (lstream->
hsize * lstream->
vsize) >> 2;
885 if (target_size > dsize) target_size = dsize;
887 if (target_size > 0)
lives_memcpy((uint8_t *)pixel_data[1], fbuffer + fbufoffs, target_size);
889 dsize -= target_size;
890 fbufoffs += target_size;
892 target_size = (lstream->
hsize * lstream->
vsize) >> 2;
893 if (target_size > dsize) target_size = dsize;
895 if (target_size > 0)
lives_memcpy((uint8_t *)pixel_data[2], fbuffer + fbufoffs, target_size);
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;
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);
923 size_t dsize = lstream->
dsize;
925 lives_stream_in_chunks(lstream, lstream->
dsize, fbuffer, 0);
931 lives_memcpy((uint8_t *)pixel_data[1] + framedataread - lstream->
hsize * lstream->
vsize, fbuffer, target_size);
933 dsize -= target_size;
934 fbufoffs += target_size;
936 target_size = (lstream->
hsize * lstream->
vsize) >> 2;
937 if (target_size > dsize) target_size = dsize;
939 if (target_size > 0)
lives_memcpy((uint8_t *)pixel_data[2], fbuffer + fbufoffs, target_size);
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;
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);
965 framedataread += lstream->
dsize;
968 if (framedataread >= (lstream->
hsize * lstream->
vsize * 3) >> 1) {
1007 if (resp == LIVES_RESPONSE_CANCEL) {
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"),
1034 if (response == LIVES_RESPONSE_OK) {
1040 }
else host = lives_strdup(
"INADDR_ANY");
1057 static void pandhw_anyhost_toggled(LiVESToggleButton *tbut, livespointer user_data) {
1077 LiVESWidget *dialog_vbox;
1081 LiVESSList *radiobutton_group = NULL;
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."));
1110 (tmp2 = (
_(
"Accept incoming LiVES streams from any connected host."))));
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);
1123 &radiobutton_group, LIVES_BOX(hbox),
1124 (tmp2 = (
_(
"Accept LiVES streams from the specified host only."))));