additional TERMUX related mods from Andrew
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / livevideo / livevideo.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "asset.h"
23 #include "bcdisplayinfo.h"
24 #include "bcsignals.h"
25 #include "channel.h"
26 #include "channeldb.h"
27 #include "clip.h"
28 #include "bchash.h"
29 #include "edl.h"
30 #include "edlsession.h"
31 #include "filexml.h"
32 #include "guicast.h"
33 #include "language.h"
34 #if HAVE_LIBDV
35 #include "libdv.h"
36 #endif
37 #include "libmjpeg.h"
38 #include "mwindow.h"
39 #include "picture.h"
40 #include "pluginvclient.h"
41 #include "pluginserver.h"
42 #include "recordconfig.h"
43 #include "transportque.inc"
44 #include "vframe.h"
45 #include "videodevice.h"
46 #include "videodevice.inc"
47
48 #include <string.h>
49 #include <stdint.h>
50
51 #define HISTORY_FRAMES 30
52 class LiveVideo;
53 class LiveVideoWindow;
54
55
56 class LiveVideoConfig
57 {
58 public:
59         LiveVideoConfig();
60         void copy_from(LiveVideoConfig &src);
61         int equivalent(LiveVideoConfig &src);
62         void interpolate(LiveVideoConfig &prev,
63                 LiveVideoConfig &next,
64                 int64_t prev_frame,
65                 int64_t next_frame,
66                 int64_t current_frame);
67         int channel;
68 };
69
70
71 // Without access to the video device, the ChannelPicker can't
72 // do any of the things it was designed to.  Instead, just provide
73 // a list of channels.
74 class LiveChannelList : public BC_ListBox
75 {
76 public:
77         LiveChannelList(LiveVideo *plugin,
78                 LiveVideoWindow *gui,
79                 int x,
80                 int y,
81                 int w,
82                 int h);
83         int handle_event();
84         LiveVideo *plugin;
85         LiveVideoWindow *gui;
86 };
87
88 class LiveChannelSelect : public BC_Button
89 {
90 public:
91         LiveChannelSelect(LiveVideo *plugin,
92                 LiveVideoWindow *gui,
93                 int x,
94                 int y);
95         int handle_event();
96         LiveVideo *plugin;
97         LiveVideoWindow *gui;
98 };
99
100
101 class LiveVideoWindow : public PluginClientWindow
102 {
103 public:
104         LiveVideoWindow(LiveVideo *plugin);
105         ~LiveVideoWindow();
106
107         void create_objects();
108
109         int resize_event(int w, int h);
110
111         ArrayList<BC_ListBoxItem*> channel_list;
112         BC_Title *title;
113         LiveChannelList *list;
114         LiveChannelSelect *select;
115         LiveVideo *plugin;
116 };
117
118
119
120
121
122
123 class LiveVideo : public PluginVClient
124 {
125 public:
126         LiveVideo(PluginServer *server);
127         ~LiveVideo();
128
129
130         PLUGIN_CLASS_MEMBERS(LiveVideoConfig);
131
132         int process_buffer(VFrame *frame,
133                 int64_t start_position,
134                 double frame_rate);
135         int is_realtime();
136         int is_multichannel();
137         int is_synthesis();
138         void save_data(KeyFrame *keyframe);
139         void read_data(KeyFrame *keyframe);
140         void update_gui();
141         void render_stop();
142
143         ChannelDB *channeldb;
144         VideoDevice *vdevice;
145 // Colormodel the device generates
146         int input_cmodel;
147 // Temporary for colormodel conversion
148         VFrame *temp;
149 // What configuration parameters the device supports
150         Channel master_channel;
151         PictureConfig *picture;
152         int prev_channel;
153         int w, h;
154 // Decompressors for different video drivers
155 #if HAVE_DV
156         dv_t *dv;
157 #endif
158         mjpeg_t *mjpeg;
159 };
160
161
162
163
164
165
166
167
168
169
170
171
172 LiveVideoConfig::LiveVideoConfig()
173 {
174         channel = 0;
175 }
176
177 void LiveVideoConfig::copy_from(LiveVideoConfig &src)
178 {
179         this->channel = src.channel;
180 }
181
182 int LiveVideoConfig::equivalent(LiveVideoConfig &src)
183 {
184         return (this->channel == src.channel);
185 }
186
187 void LiveVideoConfig::interpolate(LiveVideoConfig &prev,
188         LiveVideoConfig &next,
189         int64_t prev_frame,
190         int64_t next_frame,
191         int64_t current_frame)
192 {
193         this->channel = prev.channel;
194 }
195
196
197
198
199
200 LiveVideoWindow::LiveVideoWindow(LiveVideo *plugin)
201  : PluginClientWindow(plugin,
202         plugin->w,
203         plugin->h,
204         xS(100),
205         yS(100),
206         1)
207 {
208         this->plugin = plugin;
209 }
210
211 LiveVideoWindow::~LiveVideoWindow()
212 {
213         channel_list.remove_all_objects();
214 }
215
216 void LiveVideoWindow::create_objects()
217 {
218         int xs10 = xS(10);
219         int ys5 = yS(5), ys10 = yS(10);
220         int x = xs10, y = ys10;
221
222         EDLSession *session = plugin->get_edl()->session;
223         if(session)
224                 VideoDevice::load_channeldb(plugin->channeldb, session->vconfig_in);
225         for(int i = 0; i < plugin->channeldb->size(); i++)
226         {
227                 BC_ListBoxItem *current;
228                 channel_list.append(current =
229                         new BC_ListBoxItem(plugin->channeldb->get(i)->title));
230                 if(i == plugin->config.channel) current->set_selected(1);
231         }
232
233         add_subwindow(title = new BC_Title(x, y, _("Channels:")));
234         y += title->get_h() + ys5;
235         add_subwindow(list = new LiveChannelList(plugin,
236                 this,
237                 x,
238                 y,
239                 get_w() - x - xs10,
240                 get_h() - y - BC_OKButton::calculate_h() - ys10 - ys10));
241         y += list->get_h() + ys10;
242         add_subwindow(select = new LiveChannelSelect(plugin,
243                 this,
244                 x,
245                 y));
246         show_window();
247 }
248
249
250
251 int LiveVideoWindow::resize_event(int w, int h)
252 {
253         int list_bottom = get_h() - list->get_y() - list->get_h();
254         int list_side = get_w() - list->get_x() - list->get_w();
255         int select_top = get_h() - select->get_y();
256
257         title->reposition_window(title->get_x(), title->get_y());
258
259         list->reposition_window(list->get_x(),
260                 list->get_y(),
261                 w - list->get_x() - list_side,
262                 h - list->get_y() - list_bottom);
263         select->reposition_window(select->get_x(),
264                 h - select_top);
265         plugin->w = w;
266         plugin->h = h;
267         return 1;
268 }
269
270
271
272
273 LiveChannelList::LiveChannelList(LiveVideo *plugin,
274         LiveVideoWindow *gui,
275         int x,
276         int y,
277         int w,
278         int h)
279  : BC_ListBox(x,
280         y,
281         w,
282         h,
283         LISTBOX_TEXT,                   // Display text list or icons
284         &gui->channel_list) // Each column has an ArrayList of BC_ListBoxItems.
285 {
286         this->plugin = plugin;
287         this->gui = gui;
288 }
289
290 int LiveChannelList::handle_event()
291 {
292         plugin->config.channel = get_selection_number(0, 0);
293         plugin->send_configure_change();
294         return 1;
295 }
296
297
298 LiveChannelSelect::LiveChannelSelect(LiveVideo *plugin,
299         LiveVideoWindow *gui,
300         int x,
301         int y)
302  :  BC_Button(x, y,
303         BC_WindowBase::get_resources()->ok_images)
304 {
305         this->plugin = plugin;
306         this->gui = gui;
307 }
308
309 int LiveChannelSelect::handle_event()
310 {
311         plugin->config.channel = gui->list->get_selection_number(0, 0);
312         plugin->send_configure_change();
313         return 1;
314 }
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341 REGISTER_PLUGIN(LiveVideo)
342
343
344
345
346
347
348 LiveVideo::LiveVideo(PluginServer *server)
349  : PluginVClient(server)
350 {
351         vdevice = 0;
352         temp = 0;
353         channeldb = new ChannelDB;
354         w = xS(320);
355         h = yS(640);
356         prev_channel = 0;
357 #if HAVE_DV
358         dv = 0;
359 #endif
360         mjpeg = 0;
361         picture = 0;
362         this->server = server;
363
364 }
365
366
367 LiveVideo::~LiveVideo()
368 {
369
370         if(vdevice)
371         {
372                 vdevice->interrupt_crash();
373                 vdevice->close_all();
374                 delete vdevice;
375         }
376
377         delete channeldb;
378         delete temp;
379 #if HAVE_DV
380         if(dv) dv_delete(dv);
381 #endif
382         if(mjpeg) mjpeg_delete(mjpeg);
383         delete picture;
384 }
385
386
387
388 int LiveVideo::process_buffer(VFrame *frame,
389         int64_t start_position,
390         double frame_rate)
391 {
392         load_configuration();
393 //printf("LiveVideo::process_buffer 10 start_position=%lld buffer_size=%d size=%d\n",
394 //start_position, get_buffer_size(), size);
395
396         EDLSession *session = get_edl()->session;
397         if(!vdevice)
398         {
399                 if(session)
400                 {
401                         vdevice = new VideoDevice(server ? server->mwindow : 0);
402                         vdevice->open_input(session->vconfig_in,
403                                 0,
404                                 0,
405                                 1.0,
406                                 frame_rate);
407
408 // The color model depends on the asset configured by the user for recording.
409 // Unfortunately, get_best_colormodel returns the best colormodel for displaying
410 // on the record monitor, not the colormodel supported by the device.
411 // Some devices can read directly to the best colormodel and some can't.
412                         switch(session->vconfig_in->driver)
413                         {
414                                 case CAPTURE_FIREWIRE:
415                                 case CAPTURE_IEC61883:
416                                 case VIDEO4LINUX2JPEG:
417                                 case CAPTURE_JPEG_WEBCAM:
418                                         input_cmodel = BC_COMPRESSED;
419                                         break;
420                                 default:
421                                         input_cmodel = vdevice->get_best_colormodel(session->recording_format);
422                                         break;
423                         }
424
425
426 // Load the picture config from the main defaults file.
427
428 // Load channel table
429                         VideoDevice::load_channeldb(channeldb, session->vconfig_in);
430
431                         if(!picture)
432                         {
433                                 picture = new PictureConfig;
434                                 picture->load_defaults();
435                         }
436
437 // Picture must have usage from driver before it can load defaults.
438                         master_channel.copy_usage(vdevice->channel);
439                         picture->copy_usage(vdevice->picture);
440                         picture->load_defaults();
441
442 // Need to load picture defaults but this requires MWindow.
443                         vdevice->set_picture(picture);
444                         vdevice->set_channel(channeldb->get(config.channel));
445                 }
446                 prev_channel = config.channel;
447         }
448
449         if(session && vdevice)
450         {
451 // Update channel
452                 if(prev_channel != config.channel)
453                 {
454                         prev_channel = config.channel;
455                         vdevice->set_picture(picture);
456                         vdevice->set_channel(channeldb->get(config.channel));
457                 }
458
459
460                 (void)vdevice->config_updated();
461                 VFrame *input = frame;
462                 if(input_cmodel != frame->get_color_model() ||
463                         session->vconfig_in->w != frame->get_w() ||
464                         session->vconfig_in->h != frame->get_h())
465                 {
466                         if(!temp)
467                         {
468                                 temp = new VFrame(session->vconfig_in->w, session->vconfig_in->h,
469                                                 input_cmodel, 0);
470                         }
471                         input = temp;
472                 }
473
474                 vdevice->read_buffer(input);
475
476                 if(input != frame)
477                 {
478                         if(input->get_color_model() != BC_COMPRESSED)
479                         {
480                                 int w = MIN(session->vconfig_in->w, frame->get_w());
481                                 int h = MIN(session->vconfig_in->h, frame->get_h());
482                                 BC_CModels::transfer(frame->get_rows(), /* Leave NULL if non existent */
483                                         input->get_rows(),
484                                         frame->get_y(), /* Leave NULL if non existent */
485                                         frame->get_u(),
486                                         frame->get_v(),
487                                         input->get_y(), /* Leave NULL if non existent */
488                                         input->get_u(),
489                                         input->get_v(),
490                                         0,        /* Dimensions to capture from input frame */
491                                         0,
492                                         w,
493                                         h,
494                                         0,       /* Dimensions to project on output frame */
495                                         0,
496                                         w,
497                                         h,
498                                         input->get_color_model(),
499                                         frame->get_color_model(),
500                                         0,         /* When transfering BC_RGBA8888 to non-alpha this is the background color in 0xRRGGBB hex */
501                                         input->get_bytes_per_line(),       /* For planar use the luma rowspan */
502                                         frame->get_bytes_per_line());     /* For planar use the luma rowspan */
503                                 frame->set_opengl_state(VFrame::RAM);
504                         }
505                         else
506                         if(input->get_compressed_size())
507                         {
508                                 switch(session->vconfig_in->driver)
509                                 {
510 #if HAVE_DV
511                                         case CAPTURE_FIREWIRE:
512                                         case CAPTURE_IEC61883:
513 // Decompress a DV frame from the driver
514                                                 if(!dv)
515                                                         dv = dv_new();
516                                                 dv_read_video(((dv_t*)dv),
517                                                         frame->get_rows(),
518                                                         input->get_data(),
519                                                         input->get_compressed_size(),
520                                                         frame->get_color_model());
521                                                 frame->set_opengl_state(VFrame::RAM);
522                                                 break;
523 #endif
524                                         case VIDEO4LINUX2JPEG:
525                                                 if(!mjpeg)
526                                                         mjpeg = mjpeg_new(frame->get_w(),
527                                                                 frame->get_h(),
528                                                                 2);  // fields
529                                                 mjpeg_decompress(mjpeg,
530                                                         input->get_data(),
531                                                         input->get_compressed_size(),
532                                                         input->get_field2_offset(),
533                                                         frame->get_rows(),
534                                                         frame->get_y(),
535                                                         frame->get_u(),
536                                                         frame->get_v(),
537                                                         frame->get_color_model(),
538                                                         get_project_smp() + 1);
539                                                 break;
540
541                                         case CAPTURE_JPEG_WEBCAM:
542                                                 if(!mjpeg)
543                                                         mjpeg = mjpeg_new(frame->get_w(),
544                                                                 frame->get_h(),
545                                                                 1);  // fields
546 // printf("LiveVideo::process_buffer %d %p %d\n",
547 // __LINE__,
548 // input->get_data(),
549 // input->get_compressed_size());
550                                                 mjpeg_decompress(mjpeg,
551                                                         input->get_data(),
552                                                         input->get_compressed_size(),
553                                                         0,
554                                                         frame->get_rows(),
555                                                         frame->get_y(),
556                                                         frame->get_u(),
557                                                         frame->get_v(),
558                                                         frame->get_color_model(),
559                                                         get_project_smp() + 1);
560                                                 break;
561                                 }
562                         }
563                         else
564                         {
565                                 printf("LiveVideo::process_buffer %d zero size image\n", __LINE__);
566                         }
567                 }
568         }
569
570         return 0;
571 }
572
573 void LiveVideo::render_stop()
574 {
575         if(vdevice)
576         {
577                 vdevice->interrupt_crash();
578                 vdevice->close_all();
579                 delete vdevice;
580                 vdevice = 0;
581         }
582         delete picture;
583         picture = 0;
584 }
585
586
587 const char* LiveVideo::plugin_title() { return N_("Live Video"); }
588 int LiveVideo::is_realtime() { return 1; }
589 int LiveVideo::is_multichannel() { return 0; }
590 int LiveVideo::is_synthesis() { return 1; }
591
592
593
594 NEW_WINDOW_MACRO(LiveVideo, LiveVideoWindow)
595
596 LOAD_CONFIGURATION_MACRO(LiveVideo, LiveVideoConfig)
597
598
599
600 void LiveVideo::save_data(KeyFrame *keyframe)
601 {
602         FileXML output;
603         output.set_shared_output(keyframe->xbuf);
604         output.tag.set_title("LIVEVIDEO");
605         output.tag.set_property("CHANNEL", config.channel);
606         output.append_tag();
607         output.tag.set_title("/LIVEVIDEO");
608         output.append_tag();
609         output.append_newline();
610         output.terminate_string();
611 }
612
613 void LiveVideo::read_data(KeyFrame *keyframe)
614 {
615         FileXML input;
616
617         input.set_shared_input(keyframe->xbuf);
618
619         int result = 0;
620
621         while(!result)
622         {
623                 result = input.read_tag();
624
625                 if(!result)
626                 {
627                         if(input.tag.title_is("LIVEVIDEO"))
628                         {
629                                 config.channel = input.tag.get_property("CHANNEL", config.channel);
630                         }
631                 }
632         }
633 }
634
635 void LiveVideo::update_gui()
636 {
637         if(thread)
638         {
639                 if(load_configuration())
640                 {
641                         thread->window->lock_window("LiveVideo::update_gui");
642                         ((LiveVideoWindow*)thread->window)->list->set_selected(
643                                 &((LiveVideoWindow*)thread->window)->channel_list,
644                                 config.channel,
645                                 1);
646                         ((LiveVideoWindow*)thread->window)->list->draw_items(1);
647                         thread->window->unlock_window();
648                 }
649         }
650 }
651
652
653
654
655