Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / plugins / livevideo / livevideo.C
diff --git a/cinelerra-5.1/plugins/livevideo/livevideo.C b/cinelerra-5.1/plugins/livevideo/livevideo.C
new file mode 100644 (file)
index 0000000..c675acf
--- /dev/null
@@ -0,0 +1,649 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ */
+
+#include "asset.h"
+#include "bcdisplayinfo.h"
+#include "bcsignals.h"
+#include "channel.h"
+#include "channeldb.h"
+#include "clip.h"
+#include "bchash.h"
+#include "edlsession.h"
+#include "filexml.h"
+#include "guicast.h"
+#include "language.h"
+#include "libdv.h"
+#include "libmjpeg.h"
+#include "mwindow.h"
+#include "picture.h"
+#include "pluginvclient.h"
+#include "pluginserver.h"
+#include "recordconfig.h"
+#include "transportque.inc"
+#include "vframe.h"
+#include "videodevice.h"
+#include "videodevice.inc"
+
+#include <string.h>
+#include <stdint.h>
+
+#define HISTORY_FRAMES 30
+class LiveVideo;
+class LiveVideoWindow;
+
+
+class LiveVideoConfig
+{
+public:
+       LiveVideoConfig();
+       void copy_from(LiveVideoConfig &src);
+       int equivalent(LiveVideoConfig &src);
+       void interpolate(LiveVideoConfig &prev, 
+               LiveVideoConfig &next, 
+               int64_t prev_frame, 
+               int64_t next_frame, 
+               int64_t current_frame);
+       int channel;
+};
+
+
+// Without access to the video device, the ChannelPicker can't
+// do any of the things it was designed to.  Instead, just provide
+// a list of channels.
+class LiveChannelList : public BC_ListBox
+{
+public:
+       LiveChannelList(LiveVideo *plugin, 
+               LiveVideoWindow *gui, 
+               int x, 
+               int y,
+               int w,
+               int h);
+       int handle_event();
+       LiveVideo *plugin;
+       LiveVideoWindow *gui;
+};
+
+class LiveChannelSelect : public BC_Button
+{
+public:
+       LiveChannelSelect(LiveVideo *plugin, 
+               LiveVideoWindow *gui, 
+               int x, 
+               int y);
+       int handle_event();
+       LiveVideo *plugin;
+       LiveVideoWindow *gui;
+};
+
+
+class LiveVideoWindow : public PluginClientWindow
+{
+public:
+       LiveVideoWindow(LiveVideo *plugin);
+       ~LiveVideoWindow();
+
+       void create_objects();
+
+       int resize_event(int w, int h);
+
+       ArrayList<BC_ListBoxItem*> channel_list;
+       BC_Title *title;
+       LiveChannelList *list;
+       LiveChannelSelect *select;
+       LiveVideo *plugin;
+};
+
+
+
+
+
+
+class LiveVideo : public PluginVClient
+{
+public:
+       LiveVideo(PluginServer *server);
+       ~LiveVideo();
+
+
+       PLUGIN_CLASS_MEMBERS(LiveVideoConfig);
+
+       int process_buffer(VFrame *frame,
+               int64_t start_position,
+               double frame_rate);
+       int is_realtime();
+       int is_multichannel();
+       int is_synthesis();
+       void save_data(KeyFrame *keyframe);
+       void read_data(KeyFrame *keyframe);
+       void update_gui();
+       void render_stop();
+
+       ChannelDB *channeldb;
+       VideoDevice *vdevice;
+// Colormodel the device generates
+       int input_cmodel;
+// Temporary for colormodel conversion
+       VFrame *temp;
+// What configuration parameters the device supports
+       Channel master_channel;
+       PictureConfig *picture;
+       int prev_channel;
+       int w, h;
+// Decompressors for different video drivers
+       dv_t *dv;
+       mjpeg_t *mjpeg;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+LiveVideoConfig::LiveVideoConfig()
+{
+       channel = 0;
+}
+
+void LiveVideoConfig::copy_from(LiveVideoConfig &src)
+{
+       this->channel = src.channel;
+}
+
+int LiveVideoConfig::equivalent(LiveVideoConfig &src)
+{
+       return (this->channel == src.channel);
+}
+
+void LiveVideoConfig::interpolate(LiveVideoConfig &prev, 
+       LiveVideoConfig &next, 
+       int64_t prev_frame, 
+       int64_t next_frame, 
+       int64_t current_frame)
+{
+       this->channel = prev.channel;
+}
+
+
+
+
+
+LiveVideoWindow::LiveVideoWindow(LiveVideo *plugin)
+ : PluginClientWindow(plugin, 
+       plugin->w, 
+       plugin->h, 
+       100, 
+       100, 
+       1)
+{
+       this->plugin = plugin;
+}
+
+LiveVideoWindow::~LiveVideoWindow()
+{
+       channel_list.remove_all_objects();
+}
+
+void LiveVideoWindow::create_objects()
+{
+       int x = 10, y = 10;
+
+       EDLSession *session = plugin->PluginClient::get_edlsession();
+       if(session)
+               VideoDevice::load_channeldb(plugin->channeldb, session->vconfig_in);
+       for(int i = 0; i < plugin->channeldb->size(); i++)
+       {
+               BC_ListBoxItem *current;
+               channel_list.append(current = 
+                       new BC_ListBoxItem(plugin->channeldb->get(i)->title));
+               if(i == plugin->config.channel) current->set_selected(1);
+       }
+
+       add_subwindow(title = new BC_Title(x, y, _("Channels:")));
+       y += title->get_h() + 5;
+       add_subwindow(list = new LiveChannelList(plugin, 
+               this, 
+               x, 
+               y,
+               get_w() - x - 10,
+               get_h() - y - BC_OKButton::calculate_h() - 10 - 10));
+       y += list->get_h() + 10;
+       add_subwindow(select = new LiveChannelSelect(plugin, 
+               this, 
+               x, 
+               y));
+       show_window();
+}
+
+
+
+int LiveVideoWindow::resize_event(int w, int h)
+{
+       int list_bottom = get_h() - list->get_y() - list->get_h();
+       int list_side = get_w() - list->get_x() - list->get_w();
+       int select_top = get_h() - select->get_y();
+
+       title->reposition_window(title->get_x(), title->get_y());
+
+       list->reposition_window(list->get_x(),
+               list->get_y(),
+               w - list->get_x() - list_side,
+               h - list->get_y() - list_bottom);
+       select->reposition_window(select->get_x(),
+               h - select_top);
+       plugin->w = w;
+       plugin->h = h;
+       return 1;
+}
+
+
+
+
+LiveChannelList::LiveChannelList(LiveVideo *plugin, 
+       LiveVideoWindow *gui, 
+       int x, 
+       int y,
+       int w,
+       int h)
+ : BC_ListBox(x, 
+       y, 
+       w, 
+       h,
+       LISTBOX_TEXT,                   // Display text list or icons
+       &gui->channel_list) // Each column has an ArrayList of BC_ListBoxItems.
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int LiveChannelList::handle_event()
+{
+       plugin->config.channel = get_selection_number(0, 0);
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+LiveChannelSelect::LiveChannelSelect(LiveVideo *plugin, 
+       LiveVideoWindow *gui, 
+       int x, 
+       int y)
+ :  BC_Button(x, y, 
+       BC_WindowBase::get_resources()->ok_images)
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int LiveChannelSelect::handle_event()
+{
+       plugin->config.channel = gui->list->get_selection_number(0, 0);
+       plugin->send_configure_change();
+       return 1;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+REGISTER_PLUGIN(LiveVideo)
+
+
+
+
+
+
+LiveVideo::LiveVideo(PluginServer *server)
+ : PluginVClient(server)
+{
+       vdevice = 0;
+       temp = 0;
+       channeldb = new ChannelDB;
+       w = 320;
+       h = 640;
+       prev_channel = 0;
+       dv = 0;
+       mjpeg = 0;
+       picture = 0;
+       this->server = server;
+       
+}
+
+
+LiveVideo::~LiveVideo()
+{
+       
+       if(vdevice)
+       {
+               vdevice->interrupt_crash();
+               vdevice->close_all();
+               delete vdevice;
+       }
+
+       delete channeldb;
+       delete temp;
+       if(dv) dv_delete(dv);
+       if(mjpeg) mjpeg_delete(mjpeg);
+       delete picture;
+}
+
+
+
+int LiveVideo::process_buffer(VFrame *frame,
+       int64_t start_position,
+       double frame_rate)
+{
+       load_configuration();
+//printf("LiveVideo::process_buffer 10 start_position=%lld buffer_size=%d size=%d\n", 
+//start_position, get_buffer_size(), size);
+
+       EDLSession *session = PluginClient::get_edlsession();
+       if(!vdevice)
+       {
+               if(session)
+               {
+                       vdevice = new VideoDevice(server ? server->mwindow : 0);
+                       vdevice->open_input(session->vconfig_in, 
+                               0, 
+                               0,
+                               1.0,
+                               frame_rate);
+
+// The color model depends on the asset configured by the user for recording.
+// Unfortunately, get_best_colormodel returns the best colormodel for displaying
+// on the record monitor, not the colormodel supported by the device.
+// Some devices can read directly to the best colormodel and some can't.
+                       switch(session->vconfig_in->driver)
+                       {
+                               case CAPTURE_FIREWIRE:
+                               case CAPTURE_IEC61883:
+                               case CAPTURE_BUZ:
+                               case VIDEO4LINUX2JPEG:
+                               case CAPTURE_JPEG_WEBCAM:
+                                       input_cmodel = BC_COMPRESSED;
+                                       break;
+                               default:
+                                       input_cmodel = vdevice->get_best_colormodel(session->recording_format);
+                                       break;
+                       }
+
+
+// Load the picture config from the main defaults file.
+
+// Load channel table
+                       VideoDevice::load_channeldb(channeldb, session->vconfig_in);
+
+                       if(!picture)
+                       {
+                               picture = new PictureConfig;
+                               picture->load_defaults();
+                       }
+
+// Picture must have usage from driver before it can load defaults.
+                       master_channel.copy_usage(vdevice->channel);
+                       picture->copy_usage(vdevice->picture);
+                       picture->load_defaults();
+
+// Need to load picture defaults but this requires MWindow.
+                       vdevice->set_picture(picture);
+                       vdevice->set_channel(channeldb->get(config.channel));
+               }
+               prev_channel = config.channel;
+       }
+
+       if(session && vdevice)
+       {
+// Update channel
+               if(prev_channel != config.channel)
+               {
+                       prev_channel = config.channel;
+                       vdevice->set_picture(picture);
+                       vdevice->set_channel(channeldb->get(config.channel));
+               }
+
+       
+               (void)vdevice->config_updated();
+               VFrame *input = frame;
+               if(input_cmodel != frame->get_color_model() ||
+                       session->vconfig_in->w != frame->get_w() ||
+                       session->vconfig_in->h != frame->get_h())
+               {
+                       if(!temp)
+                       {
+                               temp = new VFrame(0, 
+                                       -1,
+                                       session->vconfig_in->w,
+                                       session->vconfig_in->h,
+                                       input_cmodel,
+                                       -1);
+                       }
+                       input = temp;
+               }
+
+               vdevice->read_buffer(input);
+
+               if(input != frame)
+               {
+                       if(input->get_color_model() != BC_COMPRESSED)
+                       {
+                               int w = MIN(session->vconfig_in->w, frame->get_w());
+                               int h = MIN(session->vconfig_in->h, frame->get_h());
+                               BC_CModels::transfer(frame->get_rows(), /* Leave NULL if non existent */
+                                       input->get_rows(),
+                                       frame->get_y(), /* Leave NULL if non existent */
+                                       frame->get_u(),
+                                       frame->get_v(),
+                                       input->get_y(), /* Leave NULL if non existent */
+                                       input->get_u(),
+                                       input->get_v(),
+                                       0,        /* Dimensions to capture from input frame */
+                                       0, 
+                                       w, 
+                                       h,
+                                       0,       /* Dimensions to project on output frame */
+                                       0, 
+                                       w, 
+                                       h,
+                                       input->get_color_model(), 
+                                       frame->get_color_model(),
+                                       0,         /* When transfering BC_RGBA8888 to non-alpha this is the background color in 0xRRGGBB hex */
+                                       input->get_bytes_per_line(),       /* For planar use the luma rowspan */
+                                       frame->get_bytes_per_line());     /* For planar use the luma rowspan */
+                               frame->set_opengl_state(VFrame::RAM);
+                       }
+                       else
+                       if(input->get_compressed_size())
+                       {
+                               switch(session->vconfig_in->driver)
+                               {
+                                       case CAPTURE_FIREWIRE:
+                                       case CAPTURE_IEC61883:
+// Decompress a DV frame from the driver
+                                               if(!dv)
+                                                       dv = dv_new();
+                                               dv_read_video(((dv_t*)dv), 
+                                                       frame->get_rows(), 
+                                                       input->get_data(), 
+                                                       input->get_compressed_size(),
+                                                       frame->get_color_model());
+                                               frame->set_opengl_state(VFrame::RAM);
+                                               break;
+
+                                       case CAPTURE_BUZ:
+                                       case VIDEO4LINUX2JPEG:
+                                               if(!mjpeg)
+                                                       mjpeg = mjpeg_new(frame->get_w(), 
+                                                               frame->get_h(), 
+                                                               2);  // fields
+                                               mjpeg_decompress(mjpeg, 
+                                                       input->get_data(), 
+                                                       input->get_compressed_size(), 
+                                                       input->get_field2_offset(), 
+                                                       frame->get_rows(), 
+                                                       frame->get_y(), 
+                                                       frame->get_u(), 
+                                                       frame->get_v(),
+                                                       frame->get_color_model(),
+                                                       get_project_smp() + 1);
+                                               break;
+                                       
+                                       case CAPTURE_JPEG_WEBCAM:
+                                               if(!mjpeg)
+                                                       mjpeg = mjpeg_new(frame->get_w(), 
+                                                               frame->get_h(), 
+                                                               1);  // fields
+// printf("LiveVideo::process_buffer %d %p %d\n", 
+// __LINE__, 
+// input->get_data(), 
+// input->get_compressed_size());
+                                               mjpeg_decompress(mjpeg, 
+                                                       input->get_data(), 
+                                                       input->get_compressed_size(), 
+                                                       0, 
+                                                       frame->get_rows(), 
+                                                       frame->get_y(), 
+                                                       frame->get_u(), 
+                                                       frame->get_v(),
+                                                       frame->get_color_model(),
+                                                       get_project_smp() + 1);
+                                               break;
+                               }
+                       }
+                       else
+                       {
+                               printf("LiveVideo::process_buffer %d zero size image\n", __LINE__);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+void LiveVideo::render_stop()
+{
+       if(vdevice)
+       {
+               vdevice->interrupt_crash();
+               vdevice->close_all();
+               delete vdevice;
+               vdevice = 0;
+       }
+       delete picture;
+       picture = 0;
+}
+
+
+const char* LiveVideo::plugin_title() { return _("Live Video"); }
+int LiveVideo::is_realtime() { return 1; }
+int LiveVideo::is_multichannel() { return 0; }
+int LiveVideo::is_synthesis() { return 1; }
+
+
+
+NEW_WINDOW_MACRO(LiveVideo, LiveVideoWindow)
+
+LOAD_CONFIGURATION_MACRO(LiveVideo, LiveVideoConfig)
+
+
+
+void LiveVideo::save_data(KeyFrame *keyframe)
+{
+       FileXML output;
+       output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
+       output.tag.set_title("LIVEVIDEO");
+       output.tag.set_property("CHANNEL", config.channel);
+       output.append_tag();
+       output.tag.set_title("/LIVEVIDEO");
+       output.append_tag();
+       output.append_newline();
+       output.terminate_string();
+}
+
+void LiveVideo::read_data(KeyFrame *keyframe)
+{
+       FileXML input;
+
+       input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
+
+       int result = 0;
+
+       while(!result)
+       {
+               result = input.read_tag();
+
+               if(!result)
+               {
+                       if(input.tag.title_is("LIVEVIDEO"))
+                       {
+                               config.channel = input.tag.get_property("CHANNEL", config.channel);
+                       }
+               }
+       }
+}
+
+void LiveVideo::update_gui()
+{
+       if(thread)
+       {
+               if(load_configuration())
+               {
+                       thread->window->lock_window("LiveVideo::update_gui");
+                       ((LiveVideoWindow*)thread->window)->list->set_selected(
+                               &((LiveVideoWindow*)thread->window)->channel_list, 
+                               config.channel, 
+                               1);
+                       ((LiveVideoWindow*)thread->window)->list->draw_items(1);
+                       thread->window->unlock_window();
+               }
+       }
+}
+
+
+
+
+