1 // yuv4mpeg.c
2 // LiVES
3 // (c) G. Finch 2004 - 2019 <salsaman+lives@gmail.com>
4 // released under the GNU GPL 3 or later
5 // see file ../COPYING or www.gnu.org for licensing details
6 
7 #include "main.h"
8 #include "interface.h"
9 #include "lives-yuv4mpeg.h"
10 #include "effects-weed.h"
11 
12 static boolean gotbroken;
13 
14 typedef struct y4data {
15   const char *filename;
16   lives_yuv4m_t *yuv4mpeg;
17 
18   int fd;
19   int i;
20 } y4data;
21 
22 static int yuvout, hsize_out, vsize_out;
23 
24 // lists of cards in use
25 static LiVESList *fw_cards = NULL;
26 
27 
lives_yuv4mpeg_alloc(void)28 static lives_yuv4m_t *lives_yuv4mpeg_alloc(void) {
29   lives_yuv4m_t *yuv4mpeg = (lives_yuv4m_t *) malloc(sizeof(lives_yuv4m_t));
30   if (!yuv4mpeg) return NULL;
31   yuv4mpeg->sar = y4m_sar_UNKNOWN;
32   yuv4mpeg->dar = y4m_dar_4_3;
33   y4m_init_stream_info(&(yuv4mpeg->streaminfo));
34   y4m_init_frame_info(&(yuv4mpeg->frameinfo));
35   yuv4mpeg->filename = NULL;
36   yuv4mpeg->name = NULL;
37   yuv4mpeg->fd = -1;
38   yuv4mpeg->ready = FALSE;
39   return yuv4mpeg;
40 }
41 
42 
y4open_thread(void * arg)43 static void *y4open_thread(void *arg) {
44   y4data *thread_data = (y4data *)arg;
45   int fd = lives_open2(thread_data->filename, O_RDONLY);
46   thread_data->fd = fd;
47   pthread_exit(NULL);
48 }
49 
50 
y4header_thread(void * arg)51 static void *y4header_thread(void *arg) {
52   y4data *thread_data = (y4data *)arg;
53   lives_yuv4m_t *yuv4mpeg = thread_data->yuv4mpeg;
54   thread_data->i = y4m_read_stream_header(yuv4mpeg->fd, &(yuv4mpeg->streaminfo));
55   pthread_exit(NULL);
56 }
57 
58 
fill_read(int fd,char * buf,size_t count)59 static void fill_read(int fd, char *buf, size_t count) {
60   size_t bytes = 0;
61   ssize_t got;
62 
63   do {
64     got = read(fd, buf + bytes, count - bytes);
65     if (got < 0) return;
66     bytes += got;
67   } while (bytes < count);
68 }
69 
70 
y4frame_thread(void * arg)71 static void *y4frame_thread(void *arg) {
72   y4data *thread_data = (y4data *)arg;
73   lives_yuv4m_t *yuv4mpeg = thread_data->yuv4mpeg;
74   char buff[5], bchar;
75 
76   thread_data->i = Y4M_OK;
77 
78   // read 5 bytes FRAME
79   fill_read(yuv4mpeg->fd, buff, 5);
80 
81   if (strncmp(buff, "FRAME", 5)) {
82     if (!gotbroken) {
83       thread_data->i = Y4M_ERR_MAGIC;
84       pthread_exit(NULL);
85     }
86 
87     do {
88       lives_memmove(buff, buff + 1, 4);
89       fill_read(yuv4mpeg->fd, buff + 4, 1);
90     } while (strncmp(buff, "FRAME", 5));
91   }
92 
93   do {
94     fill_read(yuv4mpeg->fd, &bchar, 1);
95   } while (strncmp(&bchar, "\n", 1));
96 
97   // read YUV420
98   fill_read(yuv4mpeg->fd, (char *)yuv4mpeg->pixel_data[0], yuv4mpeg->hsize * yuv4mpeg->vsize);
99   fill_read(yuv4mpeg->fd, (char *)yuv4mpeg->pixel_data[1], yuv4mpeg->hsize * yuv4mpeg->vsize / 4);
100   fill_read(yuv4mpeg->fd, (char *)yuv4mpeg->pixel_data[2], yuv4mpeg->hsize * yuv4mpeg->vsize / 4);
101 
102   pthread_exit(NULL);
103 }
104 
105 
lives_yuv_stream_start_read(lives_clip_t * sfile)106 static boolean lives_yuv_stream_start_read(lives_clip_t *sfile) {
107   double ofps = sfile->fps;
108 
109   lives_yuv4m_t *yuv4mpeg = (lives_yuv4m_t *)sfile->ext_src;
110 
111   pthread_t y4thread;
112 
113   char *filename = yuv4mpeg->filename, *tmp;
114 
115   ticks_t timeout;
116   lives_alarm_t alarm_handle = 0;
117 
118   int ohsize = sfile->hsize;
119   int ovsize = sfile->vsize;
120 
121   y4data thread_data;
122 
123   int i;
124 
125   if (!filename) return FALSE;
126 
127   if (yuv4mpeg->fd == -1) {
128     // create a thread to open the fifo
129 
130     thread_data.filename = filename;
131 
132     pthread_create(&y4thread, NULL, y4open_thread, (void *)&thread_data);
133 
134     alarm_handle = lives_alarm_set(LIVES_SHORTEST_TIMEOUT);
135 
136     d_print("");
137     d_print(_("Waiting for yuv4mpeg frames..."));
138 
139     gotbroken = FALSE;
140 
141     while ((timeout = lives_alarm_check(alarm_handle)) > 0 && !pthread_kill(y4thread, 0)) {
142       // wait for thread to complete or timeout
143       lives_usleep(prefs->sleep_time);
144       lives_widget_context_update();
145     }
146     lives_alarm_clear(alarm_handle);
147 
148     if (timeout == 0) {
149       // timeout - kill thread and wait for it to terminateo
150       pthread_cancel(y4thread);
151       pthread_join(y4thread, NULL);
152 
153       d_print_failed();
154       d_print(_("Unable to open the incoming video stream\n"));
155 
156       yuv4mpeg->fd = thread_data.fd;
157 
158       if (yuv4mpeg->fd >= 0) {
159         close(yuv4mpeg->fd);
160         yuv4mpeg->fd = -1;
161       }
162       return FALSE;
163     }
164 
165     pthread_join(y4thread, NULL);
166 
167     yuv4mpeg->fd = thread_data.fd;
168 
169     if (yuv4mpeg->fd < 0) {
170       return FALSE;
171     }
172   }
173 
174   // create a thread to open the stream header
175   thread_data.yuv4mpeg = yuv4mpeg;
176   pthread_create(&y4thread, NULL, y4header_thread, &thread_data);
177   alarm_handle = lives_alarm_set(LIVES_SHORT_TIMEOUT);
178 
179   while ((timeout = lives_alarm_check(alarm_handle)) > 0 && !pthread_kill(y4thread, 0)) {
180     // wait for thread to complete or timeout
181     lives_usleep(prefs->sleep_time);
182     lives_widget_context_update();
183   }
184   lives_alarm_clear(alarm_handle);
185 
186   if (timeout == 0) {
187     // timeout - kill thread and wait for it to terminate
188     pthread_cancel(y4thread);
189     pthread_join(y4thread, NULL);
190     d_print(_("Unable to read the stream header\n"));
191     return FALSE;
192   }
193 
194   pthread_join(y4thread, NULL);
195 
196   i = thread_data.i;
197 
198   if (i != Y4M_OK) {
199     char *tmp;
200     d_print((tmp = lives_strdup_printf("yuv4mpeg: %s\n", y4m_strerr(i))));
201     lives_free(tmp);
202     return FALSE;
203   }
204 
205   d_print(_("got header\n"));
206 
207   sfile->hsize = yuv4mpeg->hsize = y4m_si_get_width(&(yuv4mpeg->streaminfo));
208   sfile->vsize = yuv4mpeg->vsize = y4m_si_get_height(&(yuv4mpeg->streaminfo));
209 
210   sfile->fps = cfile->pb_fps = lives_strtod(lives_strdup_printf("%.8f", Y4M_RATIO_DBL
211                                (y4m_si_get_framerate(&(yuv4mpeg->streaminfo)))), NULL);
212 
213   if (sfile->hsize * sfile->vsize == 0) {
214     do_error_dialog(lives_strdup_printf(_("Video dimensions: %d x %d are invalid. Stream cannot be opened"),
215                                         sfile->hsize, sfile->vsize));
216     return FALSE;
217   }
218 
219   if (sfile->hsize != ohsize || sfile->vsize != ovsize || sfile->fps != ofps) {
220     set_main_title(sfile->file_name, 0);
221   }
222 
223   d_print((tmp = lives_strdup_printf(_("Reset clip values for %s: size=%dx%d fps=%.3f\n"), yuv4mpeg->name,
224                                      cfile->hsize, yuv4mpeg->vsize, cfile->bpp, cfile->fps)));
225   lives_free(tmp);
226 
227   yuv4mpeg->ready = TRUE;
228 
229   return TRUE;
230 }
231 
232 
lives_yuv_stream_stop_read(lives_yuv4m_t * yuv4mpeg)233 void lives_yuv_stream_stop_read(lives_yuv4m_t *yuv4mpeg) {
234   y4m_fini_stream_info(&(yuv4mpeg->streaminfo));
235   y4m_fini_frame_info(&(yuv4mpeg->frameinfo));
236   yuv4mpeg->sar = y4m_sar_UNKNOWN;
237   yuv4mpeg->dar = y4m_dar_4_3;
238   if (yuv4mpeg->fd != -1) close(yuv4mpeg->fd);
239 
240   if (yuv4mpeg->filename) {
241     lives_rm(yuv4mpeg->filename);
242     lives_free(yuv4mpeg->filename);
243   }
244 
245   if (yuv4mpeg->name) lives_free(yuv4mpeg->name);
246 
247   if (yuv4mpeg->type == YUV4_TYPE_FW) fw_cards = lives_list_remove(fw_cards, LIVES_INT_TO_POINTER(yuv4mpeg->cardno));
248   if (yuv4mpeg->type == YUV4_TYPE_TV) mainw->videodevs = lives_list_remove(mainw->videodevs,
249         LIVES_INT_TO_POINTER(yuv4mpeg->cardno));
250 }
251 
252 
weed_layer_set_from_yuv4m(weed_layer_t * layer,lives_clip_t * sfile)253 void weed_layer_set_from_yuv4m(weed_layer_t *layer, lives_clip_t *sfile) {
254   lives_yuv4m_t *yuv4mpeg = (lives_yuv4m_t *)(sfile->ext_src);
255 
256   y4data thread_data;
257 
258   pthread_t y4thread;
259 
260   ticks_t timeout;
261 
262   lives_alarm_t alarm_handle;
263 
264   int error;
265 
266   if (!yuv4mpeg->ready) lives_yuv_stream_start_read(sfile);
267 
268   weed_set_int_value(layer, WEED_LEAF_WIDTH, sfile->hsize);
269   weed_set_int_value(layer, WEED_LEAF_HEIGHT, sfile->vsize);
270   weed_set_int_value(layer, WEED_LEAF_CURRENT_PALETTE, WEED_PALETTE_YUV420P);
271   weed_set_int_value(layer, WEED_LEAF_YUV_SUBSPACE, WEED_YUV_SUBSPACE_YCBCR);
272 
273   create_empty_pixel_data(layer, TRUE, TRUE);
274 
275   if (!yuv4mpeg->ready) {
276     return;
277   }
278 
279   yuv4mpeg->pixel_data = weed_get_voidptr_array(layer, WEED_LEAF_PIXEL_DATA, &error);
280 
281   // create a thread to open the stream header
282 
283   thread_data.yuv4mpeg = yuv4mpeg;
284   pthread_create(&y4thread, NULL, y4frame_thread, &thread_data);
285 
286   alarm_handle = lives_alarm_set(LIVES_SHORTEST_TIMEOUT);
287 
288   while ((timeout = lives_alarm_check(alarm_handle)) > 0 && !pthread_kill(y4thread, 0)) {
289     // wait for thread to complete or timeout
290     lives_usleep(prefs->sleep_time);
291   }
292   lives_alarm_clear(alarm_handle);
293 
294   if (timeout == 0) {
295     // timeout - kill thread and wait for it to terminate
296     pthread_cancel(y4thread);
297     d_print(_("Unable to read the incoming video frame\n"));
298     gotbroken = TRUE;
299   } else gotbroken = FALSE;
300 
301   pthread_join(y4thread, NULL);
302 
303   lives_free(yuv4mpeg->pixel_data);
304   yuv4mpeg->pixel_data = NULL;
305 
306   weed_set_int_value(layer, WEED_LEAF_YUV_SAMPLING, WEED_YUV_SAMPLING_MPEG);
307 
308   return;
309 }
310 
311 
open_yuv4m_inner(const char * filename,const char * fname,int new_file,int type,int cardno)312 static boolean open_yuv4m_inner(const char *filename, const char *fname, int new_file, int type, int cardno) {
313   // create a virtual clip
314   int old_file = mainw->current_file;
315 
316   lives_yuv4m_t *yuv4mpeg;
317 
318   cfile->clip_type = CLIP_TYPE_YUV4MPEG;
319 
320   // get size of frames, arate, achans, asamps, signed endian
321   yuv4mpeg = lives_yuv4mpeg_alloc();
322 
323   yuv4mpeg->fd = -1;
324 
325   yuv4mpeg->filename = lives_strdup(filename);
326   yuv4mpeg->name = lives_strdup(fname);
327 
328   yuv4mpeg->type = type;
329   yuv4mpeg->cardno = cardno;
330 
331   cfile->ext_src = yuv4mpeg;
332   cfile->ext_src_type = LIVES_EXT_SRC_FIFO;
333 
334   cfile->bpp = 12;
335 
336   cfile->start = cfile->end = cfile->frames = 1;
337 
338   cfile->hsize = DEF_GEN_WIDTH;
339   cfile->vsize = DEF_GEN_HEIGHT;
340 
341   cfile->img_type = IMG_TYPE_BEST; // override the pref
342 
343   cfile->is_loaded = TRUE;
344 
345   add_to_clipmenu();
346 
347   switch_to_file((mainw->current_file = old_file), new_file);
348 
349   return TRUE;
350 }
351 
352 
on_open_yuv4m_activate(LiVESMenuItem * menuitem,livespointer user_data)353 void on_open_yuv4m_activate(LiVESMenuItem *menuitem, livespointer user_data) {
354   // open a general yuvmpeg stream
355   // start "playing" but open frames in yuv4mpeg format on stdin
356 
357   int old_file = mainw->current_file, new_file = mainw->first_free_file;
358   char *tmp;
359   char *filename;
360   char *fname;
361 
362   char *audio_real, *audio_fake;
363 
364   if (menuitem && !do_yuv4m_open_warning()) return;
365 
366   fname = (_("yuv4mpeg stream"));
367 
368   if (!get_new_handle(new_file, fname)) {
369     lives_free(fname);
370     return;
371   }
372 
373   mainw->current_file = new_file;
374 
375   if (!strlen(prefs->yuvin)) {
376     filename = choose_file(NULL, NULL, NULL, LIVES_FILE_CHOOSER_ACTION_OPEN, _("Open _yuv4mpeg stream (fifo)"), NULL);
377     if (!filename) return;
378   } else filename = lives_strdup(prefs->yuvin);
379 
380   mkfifo(filename, S_IRUSR | S_IWUSR);
381 
382   if (!open_yuv4m_inner(filename, fname, new_file, YUV4_TYPE_GENERIC, 0)) {
383     close_current_file(old_file);
384     lives_free(filename);
385     lives_free(fname);
386     return;
387   }
388 
389   lives_free(fname);
390 
391   if (!lives_yuv_stream_start_read(cfile)) {
392     close_current_file(old_file);
393     lives_free(filename);
394     return;
395   }
396 
397   new_file = mainw->current_file;
398 
399   lives_snprintf(cfile->type, 40, "%s", _("yu4mpeg stream in"));
400 
401   d_print((tmp = lives_strdup_printf(_("Opened yuv4mpeg stream on %s"), filename)));
402   lives_free(tmp);
403   lives_free(filename);
404 
405   d_print(_("Audio: "));
406 
407   if (cfile->achans == 0) {
408     d_print(_("none\n"));
409   } else {
410     d_print((tmp = lives_strdup_printf(P_("%d Hz %d channel %d bps\n", "%d Hz %d channels %d bps\n", cfile->achans),
411                                        cfile->arate, cfile->achans, cfile->asampsize)));
412     lives_free(tmp);
413   }
414 
415   // if not playing, start playing
416   if (!LIVES_IS_PLAYING) {
417     // temp kludge, symlink audiodump.pcm to wav file, then pretend we are playing
418     // an opening preview . Doesn't work with fifo.
419     // and we dont really care if it doesnt work
420 
421     // but what it means is, if we have an audio file or stream at
422     // "prefs->workdir/audiodump.pcm" we will try to play it
423 
424     // real is workdir/audiodump.pcm
425     audio_real = get_audio_file_name(-1, TRUE);
426     // fake is workdir/handle/audiodump.pcm
427     audio_fake = get_audio_file_name(mainw->current_file, TRUE);
428 
429     // fake file will go away when we close the current clip
430     lives_ln(audio_real, audio_fake);
431 
432     lives_free(audio_real);
433     lives_free(audio_fake);
434 
435     lives_free(tmp);
436 
437     mainw->pre_play_file = old_file;
438     // start playing
439     start_playback_async(7);
440     return;
441   }
442   // TODO - else...
443 
444   if (mainw->current_file != old_file && mainw->current_file != new_file)
445     old_file = mainw->current_file; // we could have rendered to a new file
446 
447   mainw->current_file = new_file;
448 
449   // close this temporary clip
450   close_current_file(old_file);
451 }
452 
453 
454 ///////////////////////////////////////////////////////////////////////////////
455 // write functions - not used currently
456 
lives_yuv_stream_start_write(lives_yuv4m_t * yuv4mpeg,const char * filename,int hsize,int vsize,double fps)457 boolean lives_yuv_stream_start_write(lives_yuv4m_t *yuv4mpeg, const char *filename, int hsize, int vsize, double fps) {
458   int i;
459 
460   if (mainw->fixed_fpsd > -1. && mainw->fixed_fpsd != fps) {
461     do_error_dialog(lives_strdup_printf(_("Unable to set display framerate to %.3f fps.\n\n"), fps));
462     return FALSE;
463   }
464   mainw->fixed_fpsd = fps;
465 
466   if (!filename) filename = lives_strdup_printf("%s/streamout.yuv", prefs->workdir);
467 
468   // TODO - do_threaded_dialog
469   if ((yuvout = creat(filename, O_CREAT)) < 0) {
470     do_error_dialog(lives_strdup_printf(_("Unable to open yuv4mpeg out stream %s\n"), filename));
471     return FALSE;
472   }
473 
474   if (mainw->fixed_fpsd > 23.9999 && mainw->fixed_fpsd < 24.0001) {
475     y4m_si_set_framerate(&(yuv4mpeg->streaminfo), y4m_fps_FILM);
476   } else return FALSE;
477   y4m_si_set_interlace(&(yuv4mpeg->streaminfo), Y4M_ILACE_NONE);
478 
479   y4m_si_set_width(&(yuv4mpeg->streaminfo), (hsize_out = hsize));
480   y4m_si_set_height(&(yuv4mpeg->streaminfo), (vsize_out = vsize));
481   y4m_si_set_sampleaspect(&(yuv4mpeg->streaminfo), yuv4mpeg->sar);
482 
483   i = y4m_write_stream_header(yuvout, &(yuv4mpeg->streaminfo));
484 
485   if (i != Y4M_OK) return FALSE;
486 
487   return TRUE;
488 }
489 
490 
lives_yuv_stream_write_frame(lives_yuv4m_t * yuv4mpeg,void * pixel_data)491 boolean lives_yuv_stream_write_frame(lives_yuv4m_t *yuv4mpeg, void *pixel_data) {
492   // pixel_data is planar yuv420 data
493   int i;
494 
495   uint8_t *planes[3];
496   uint8_t *pixels = (uint8_t *)pixel_data;
497 
498   planes[0] = &(pixels[0]);
499   planes[1] = &(pixels[hsize_out * vsize_out]);
500   planes[2] = &(pixels[hsize_out * vsize_out * 5 / 4]);
501 
502   i = y4m_write_frame(yuvout, &(yuv4mpeg->streaminfo),
503                       &(yuv4mpeg->frameinfo), (uint8_t **)&planes[0]);
504   if (i != Y4M_OK) return FALSE;
505   return TRUE;
506 }
507 
508 
lives_yuv_stream_stop_write(lives_yuv4m_t * yuv4mpeg)509 void lives_yuv_stream_stop_write(lives_yuv4m_t *yuv4mpeg) {
510   y4m_fini_stream_info(&(yuv4mpeg->streaminfo));
511   y4m_fini_frame_info(&(yuv4mpeg->frameinfo));
512   close(yuvout);
513 }
514 
515 
516 //////////////////////////////////////////////////////////////
517 
518 /**
519    @brief add live input peripherals
520 
521    some time in the future it would be nice to implement these via videojack
522 
523    advantages would be: - no longer necessary to have mjpegtools
524    - multiple copies of LiVES could share the same input at (almost) zero cost
525 
526    for each of these functions:
527    - prompt user for name of device, etc.
528 
529    check if device already opened, if so exit
530 
531    create clip with default values; clip type is YUV4MPEG
532 
533    create a fifo file
534    set mplayer reading from device and writing yuv4mpeg
535 
536    start reading - update clip values
537 
538    note: we add the clip to the menu and to mainw->cliplist
539    beware when handling mainw->cliplist
540 */
on_live_tvcard_activate(LiVESMenuItem * menuitem,livespointer user_data)541 void on_live_tvcard_activate(LiVESMenuItem *menuitem, livespointer user_data) {
542   int cardno = 0;
543 
544   int new_file = mainw->first_free_file;
545 
546   int response;
547 
548   char *com, *tmp;
549   char *fifofile;
550 
551   char *chanstr;
552   char *devstr;
553 
554   char *fname;
555 
556   LiVESWidget *card_dialog;
557 
558   lives_tvcardw_t *tvcardw;
559 
560   if (!HAS_EXTERNAL_PLAYER || USE_MPV) {
561     do_need_mplayer_dialog();
562     return;
563   }
564 
565   fifofile = lives_strdup_printf("%s/tvpic_%d.y4m", prefs->workdir, capable->mainpid);
566 
567   mainw->open_deint = FALSE;
568 
569   card_dialog = create_cdtrack_dialog(LIVES_DEVICE_TV_CARD, NULL);
570 
571   tvcardw = (lives_tvcardw_t *)lives_widget_object_get_data(LIVES_WIDGET_OBJECT(card_dialog), "tvcard_data");
572 
573   response = lives_dialog_run(LIVES_DIALOG(card_dialog));
574   if (response == LIVES_RESPONSE_CANCEL) {
575     lives_widget_destroy(card_dialog);
576     lives_free(fifofile);
577     lives_free(tvcardw);
578     return;
579   }
580 
581   cardno = (int)mainw->fx1_val;
582   chanstr = lives_strdup_printf("%d", (int)mainw->fx2_val);
583 
584   if (lives_list_find(mainw->videodevs, LIVES_INT_TO_POINTER(cardno))) {
585     lives_widget_destroy(card_dialog);
586     do_card_in_use_error();
587     lives_free(chanstr);
588     lives_free(fifofile);
589     lives_free(tvcardw);
590     return;
591   }
592 
593   fname = lives_strdup_printf(_("TV card %d"), cardno);
594 
595   if (!get_new_handle(new_file, fname)) {
596     lives_widget_destroy(card_dialog);
597     lives_free(chanstr);
598     lives_free(fifofile);
599     lives_free(fname);
600     lives_free(tvcardw);
601     return;
602   }
603 
604   devstr = lives_strdup_printf("/dev/video%d", cardno);
605 
606   if (!check_dev_busy(devstr)) {
607     lives_widget_destroy(card_dialog);
608     do_dev_busy_error(fname);
609     lives_free(devstr);
610     lives_free(chanstr);
611     lives_free(fifofile);
612     lives_free(fname);
613     lives_free(tvcardw);
614     return;
615   }
616 
617   mainw->videodevs = lives_list_append(mainw->videodevs, LIVES_INT_TO_POINTER(cardno));
618 
619   mainw->current_file = new_file;
620 
621   cfile->deinterlace = mainw->open_deint;
622 
623   lives_rm(fifofile);
624   mkfifo(fifofile, S_IRUSR | S_IWUSR);
625 
626   if (!tvcardw->use_advanced) {
627     com = lives_strdup_printf("%s open_tv_card \"%s\" \"%s\" \"%s\" \"%s\"", prefs->backend, cfile->handle, chanstr,
628                               devstr, fifofile);
629   } else {
630     double fps = 0.;
631     const char *driver = NULL, *outfmt = NULL;
632     int width = 0, height = 0;
633     int input = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(tvcardw->spinbuttoni));
634 
635     if (!lives_toggle_button_get_active(LIVES_TOGGLE_BUTTON(tvcardw->radiobuttond))) {
636       width = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(tvcardw->spinbuttonw));
637       height = lives_spin_button_get_value_as_int(LIVES_SPIN_BUTTON(tvcardw->spinbuttonh));
638       fps = lives_spin_button_get_value(LIVES_SPIN_BUTTON(tvcardw->spinbuttonf));
639     }
640 
641     driver = lives_combo_get_active_text(LIVES_COMBO(tvcardw->combod));
642     outfmt = lives_combo_get_active_text(LIVES_COMBO(tvcardw->comboo));
643 
644     com = lives_strdup_printf("%s open_tv_card \"%s\" \"%s\" \"%s\" \"%s\" %d %d %d %.3f \"%s\" \"%s\"",
645                               prefs->backend, cfile->handle, chanstr,
646                               devstr, fifofile, input, width, height, fps, driver, outfmt);
647   }
648   lives_widget_destroy(card_dialog);
649   lives_free(tvcardw);
650 
651   lives_system(com, FALSE);
652   lives_free(com);
653 
654   if (THREADVAR(com_failed)) {
655     THREADVAR(com_failed) = FALSE;
656     lives_free(fname);
657     lives_free(chanstr);
658     lives_free(fifofile);
659     lives_free(devstr);
660     return;
661   }
662 
663   if (!open_yuv4m_inner(fifofile, fname, new_file, YUV4_TYPE_TV, cardno)) {
664     lives_free(fname);
665     lives_free(chanstr);
666     lives_free(fifofile);
667     lives_free(devstr);
668     return;
669   }
670 
671   lives_snprintf(cfile->type, 40, "%s", fname);
672 
673   d_print((tmp = lives_strdup_printf(_("Opened TV card %d (%s)"), cardno, devstr)));
674 
675   lives_free(tmp);
676   lives_free(fname);
677   lives_free(chanstr);
678   lives_free(devstr);
679   lives_free(fifofile);
680 }
681 
682 
on_live_fw_activate(LiVESMenuItem * menuitem,livespointer user_data)683 void on_live_fw_activate(LiVESMenuItem *menuitem, livespointer user_data) {
684   char *com, *tmp;
685   int cardno;
686   int cache = 1024;
687 
688   int new_file = mainw->first_free_file;
689 
690   int response;
691 
692   char *fifofile = lives_strdup_printf("%s/firew_%d.y4m", prefs->workdir, capable->mainpid);
693   char *fname;
694 
695   LiVESWidget *card_dialog;
696 
697   mainw->open_deint = FALSE;
698 
699   card_dialog = create_cdtrack_dialog(LIVES_DEVICE_FW_CARD, NULL);
700   response = lives_dialog_run(LIVES_DIALOG(card_dialog));
701   if (response == LIVES_RESPONSE_CANCEL) {
702     lives_widget_destroy(card_dialog);
703     lives_free(fifofile);
704     return;
705   }
706 
707   cardno = (int)mainw->fx1_val;
708 
709   lives_widget_destroy(card_dialog);
710 
711   if (lives_list_find(fw_cards, LIVES_INT_TO_POINTER(cardno))) {
712     lives_free(fifofile);
713     do_card_in_use_error();
714     return;
715   }
716 
717   fname = lives_strdup_printf(_("Firewire card %d"), cardno);
718 
719   if (!get_new_handle(new_file, fname)) {
720     lives_free(fifofile);
721     lives_free(fname);
722     return;
723   }
724 
725   fw_cards = lives_list_append(fw_cards, LIVES_INT_TO_POINTER(cardno));
726 
727   mainw->current_file = new_file;
728   cfile->deinterlace = mainw->open_deint;
729 
730   lives_rm(fifofile);
731   mkfifo(fifofile, S_IRUSR | S_IWUSR);
732 
733   com = lives_strdup_printf("%s open_fw_card \"%s\" %d %d \"%s\"", prefs->backend, cfile->handle, cardno, cache, fifofile);
734   lives_system(com, FALSE);
735   lives_free(com);
736 
737   if (THREADVAR(com_failed)) {
738     THREADVAR(com_failed) = FALSE;
739     lives_free(fname);
740     lives_free(fifofile);
741     return;
742   }
743 
744   if (!open_yuv4m_inner(fifofile, fname, new_file, YUV4_TYPE_FW, cardno)) {
745     lives_free(fname);
746     lives_free(fifofile);
747     return;
748   }
749 
750   lives_snprintf(cfile->type, 40, "%s", fname);
751 
752   d_print((tmp = lives_strdup_printf(_("Opened firewire card %d"), cardno)));
753 
754   lives_free(tmp);
755   lives_free(fname);
756   lives_free(fifofile);
757 }
758