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