Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / cinelerra / videodevice.C
diff --git a/cinelerra-5.1/cinelerra/videodevice.C b/cinelerra-5.1/cinelerra/videodevice.C
new file mode 100644 (file)
index 0000000..8f9f6bb
--- /dev/null
@@ -0,0 +1,863 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008-2013 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 "assets.h"
+#include "bccapture.h"
+#include "bcsignals.h"
+#include "channel.h"
+#include "channeldb.h"
+#include "chantables.h"
+#include "edl.h"
+#include "edlsession.h"
+#include "file.inc"
+#include "libdv.h"
+#include "libmjpeg.h"
+#include "mainmenu.h"
+#include "mutex.h"
+#include "mwindow.h"
+#include "mwindowgui.h"
+#include "picture.h"
+#include "playbackconfig.h"
+#include "playbackengine.h"
+#include "preferences.h"
+#include "recordconfig.h"
+#include "recordgui.h"
+#include "recordmonitor.h"
+#include "record.h"
+#ifdef HAVE_FIREWIRE
+#include "vdevice1394.h"
+#endif
+#include "vdevicebuz.h"
+#include "vdevicedvb.h"
+#include "vdevicev4l.h"
+#include "vdevicev4l2.h"
+#include "vdevicev4l2jpeg.h"
+#include "vdevicev4l2mpeg.h"
+#include "vdevicex11.h"
+#include "videoconfig.h"
+#include "videodevice.h"
+#include "videowindow.h"
+#include "videowindowgui.h"
+#include "vframe.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+KeepaliveThread::KeepaliveThread(VideoDevice *device)
+ : Thread(1, 0, 0)
+{
+       still_alive = 1;
+       failed = 0;
+       interrupted = 0;
+       set_synchronous(1);
+       this->device = device;
+       capturing = 0;
+       startup_lock = new Mutex("KeepaliveThread::startup_lock");
+}
+
+KeepaliveThread::~KeepaliveThread()
+{
+       delete startup_lock;
+}
+
+int KeepaliveThread::start_keepalive()
+{
+       startup_lock->lock("KeepaliveThread::start_keepalive 1");
+       start();
+       startup_lock->lock("KeepaliveThread::start_keepalive 2");
+       startup_lock->unlock();
+       return 0;
+}
+
+void KeepaliveThread::run()
+{
+       startup_lock->unlock();
+       while(!interrupted)
+       {
+               still_alive = 0;
+// Give the capture a moment
+// Should fix the delay in case users want slower frame rates.
+               timer.delay((long)(KEEPALIVE_DELAY * 1000));
+
+// See if a capture happened
+               if(still_alive == 0 && capturing)
+               {
+//                     printf("KeepaliveThread::run: device crashed\n");
+                       failed++;
+               }
+               else
+                       failed = 0;
+       }
+}
+
+int KeepaliveThread::reset_keepalive()
+{
+       still_alive = 1;
+       return 0;
+}
+
+int KeepaliveThread::get_failed()
+{
+       if(failed) return 1; else return 0;
+}
+
+int KeepaliveThread::stop()
+{
+       interrupted = 1;
+
+// Force an immediate exit even if capture_frame worked.
+       Thread::cancel();
+       Thread::join();
+       return 0;
+}
+
+
+
+
+
+
+
+VideoDevice::VideoDevice(MWindow *mwindow)
+{
+       this->mwindow = mwindow;
+       in_config = new VideoInConfig;
+       out_config = new VideoOutConfig;
+       channel = new Channel;
+       picture = new PictureConfig();
+       sharing_lock = new Mutex("VideoDevice::sharing_lock");
+       channel_lock = new Mutex("VideoDevice::channel_lock");
+       picture_lock = new Mutex("VideoDevice::picture_lock");
+       initialize();
+}
+
+
+VideoDevice::~VideoDevice()
+{
+       input_sources.remove_all_objects();
+       delete in_config;
+       delete out_config;
+       delete channel;
+       delete picture;
+       delete sharing_lock;
+       delete channel_lock;
+       delete picture_lock;
+}
+
+int VideoDevice::initialize()
+{
+       sharing = 0;
+       done_sharing = 0;
+       sharing_lock->reset();
+       orate = irate = 0;
+       out_w = out_h = 0;
+       r = w = 0;
+       is_playing_back = is_recording = 0;
+       input_x = 0;
+       input_y = 0;
+       input_z = 1;
+       frame_resized = 0;
+       frame_rate = 0;
+       timestamp = -1.;
+       capturing = 0;
+       keepalive = 0;
+       swap_bytes = 0;
+       in_config_updated = 0;
+       input_base = 0;
+       output_base = 0;
+       output_format = 0;
+       interrupt = 0;
+       adevice = 0;
+       quality = 80;
+       cpus = 1;
+       single_frame = 0;
+       channel_changed = 0;
+       picture_changed = 0;
+       return 0;
+}
+
+int VideoDevice::open_input(VideoInConfig *config,
+       int input_x,
+       int input_y,
+       float input_z,
+       double frame_rate)
+{
+       int result = 0;
+
+       *this->in_config = *config;
+
+       r = 1;
+       this->input_z = -1;   // Force initialization.
+       this->frame_rate = frame_rate;
+       if( input_base ) return 1; // device already open
+
+       switch(in_config->driver) {
+               case VIDEO4LINUX:
+                       keepalive = new KeepaliveThread(this);
+                       keepalive->start_keepalive();
+                       break;
+
+
+#ifdef HAVE_VIDEO4LINUX2
+
+               case VIDEO4LINUX2:
+               case VIDEO4LINUX2JPEG:
+               case VIDEO4LINUX2MPEG:
+               case CAPTURE_JPEG_WEBCAM:
+               case CAPTURE_YUYV_WEBCAM:
+                       break;
+
+#endif
+
+               case SCREENCAPTURE:
+                       this->input_x = input_x;
+                       this->input_y = input_y;
+                       break;
+               case CAPTURE_LML:
+               case CAPTURE_BUZ:
+//printf("VideoDevice 1\n");
+                       keepalive = new KeepaliveThread(this);
+                       keepalive->start_keepalive();
+                       break;
+#ifdef HAVE_FIREWIRE
+               case CAPTURE_FIREWIRE:
+               case CAPTURE_IEC61883:
+                       break;
+#endif
+
+#ifdef HAVE_DVB
+               case CAPTURE_DVB:
+                       break;
+#endif
+
+               default:
+                       return 1;
+       }
+
+       new_device_base();
+       if( !input_base ) return 1;
+       result = input_base->open_input();
+       if(!result) capturing = 1;
+       in_config_updated = 0;
+       return 0;
+}
+
+VDeviceBase* VideoDevice::new_device_base()
+{
+       switch(in_config->driver) {
+#ifdef HAVE_VIDEO4LINUX
+       case VIDEO4LINUX:
+               return input_base = new VDeviceV4L(this);
+#endif
+
+#ifdef HAVE_VIDEO4LINUX2
+       case VIDEO4LINUX2:
+       case CAPTURE_JPEG_WEBCAM:
+       case CAPTURE_YUYV_WEBCAM:
+               return input_base = new VDeviceV4L2(this);
+       case VIDEO4LINUX2JPEG:
+               return input_base = new VDeviceV4L2JPEG(this);
+       case VIDEO4LINUX2MPEG:
+               return input_base = new VDeviceV4L2MPEG(this);
+#endif
+
+       case SCREENCAPTURE:
+               return input_base = new VDeviceX11(this, 0);
+
+#ifdef HAVE_VIDEO4LINUX
+       case CAPTURE_BUZ:
+               return input_base = new VDeviceBUZ(this);
+       case CAPTURE_LML:
+               return input_base = new VDeviceLML(this);
+#endif
+
+#ifdef HAVE_FIREWIRE
+       case CAPTURE_FIREWIRE:
+       case CAPTURE_IEC61883:
+               return input_base = new VDevice1394(this);
+#endif
+
+#ifdef HAVE_DVB
+       case CAPTURE_DVB:
+               return input_base = new VDeviceDVB(this);
+#endif
+       }
+       return 0;
+}
+
+static const char* get_channeldb_path(VideoInConfig *vconfig_in)
+{
+       char *path = 0;
+       switch(vconfig_in->driver)
+       {
+               case VIDEO4LINUX:
+                       path = (char*)"channels_v4l";
+                       break;
+               case VIDEO4LINUX2:
+               case CAPTURE_JPEG_WEBCAM:
+               case CAPTURE_YUYV_WEBCAM:
+                       path = (char*)"channels_v4l2";
+                       break;
+               case VIDEO4LINUX2JPEG:
+                       path = (char*)"channels_v4l2jpeg";
+                       break;
+               case VIDEO4LINUX2MPEG:
+                       path = (char*)"channels_v4l2mpeg";
+                       break;
+               case CAPTURE_BUZ:
+                       path = (char*)"channels_buz";
+                       break;
+               case CAPTURE_DVB:
+                       path = (char*)"channels_dvb";
+                       break;
+       }
+       return path;
+}
+
+void VideoDevice::load_channeldb(ChannelDB *channeldb, VideoInConfig *vconfig_in)
+{
+       channeldb->load(get_channeldb_path(vconfig_in));
+}
+
+void VideoDevice::save_channeldb(ChannelDB *channeldb, VideoInConfig *vconfig_in)
+{
+       channeldb->save(get_channeldb_path(vconfig_in));
+}
+
+
+VDeviceBase* VideoDevice::get_input_base()
+{
+       return input_base;
+}
+
+VDeviceBase* VideoDevice::get_output_base()
+{
+       return output_base;
+}
+
+DeviceMPEGInput *VideoDevice::mpeg_device()
+{
+       return !input_base ? 0 : input_base->mpeg_device();
+}
+
+int VideoDevice::is_compressed(int driver, int use_file, int use_fixed)
+{
+// FileMOV needs to have write_frames called so the start codes get scanned.
+       return ((driver == CAPTURE_BUZ && use_fixed) ||
+               (driver == VIDEO4LINUX2JPEG && use_fixed) ||
+               (driver == CAPTURE_JPEG_WEBCAM && use_fixed) ||
+               driver == CAPTURE_LML ||
+               driver == CAPTURE_FIREWIRE ||
+               driver == CAPTURE_IEC61883);
+}
+
+int VideoDevice::is_compressed(int use_file, int use_fixed)
+{
+       return is_compressed(in_config->driver, use_file, use_fixed);
+}
+
+
+void VideoDevice::fix_asset(Asset *asset, int driver)
+{
+// Fix asset using legacy routine
+       const char *vcodec = 0;
+       switch(driver) {
+       case CAPTURE_IEC61883:
+       case CAPTURE_FIREWIRE:
+               vcodec = CODEC_TAG_DVSD;
+               break;
+
+       case CAPTURE_BUZ:
+       case CAPTURE_LML:
+       case VIDEO4LINUX2JPEG:
+               vcodec = CODEC_TAG_MJPEG;
+               break;
+
+       case CAPTURE_JPEG_WEBCAM:
+               vcodec = CODEC_TAG_JPEG;
+               break;
+       }
+       if( vcodec ) {
+               asset->format = FILE_FFMPEG;
+               strcpy(asset->vcodec, vcodec);
+               return;
+       }
+
+// Fix asset using inherited routine
+       new_device_base();
+
+       if(input_base) input_base->fix_asset(asset);
+       delete input_base;
+       input_base = 0;
+}
+
+
+const char* VideoDevice::drivertostr(int driver)
+{
+       switch(driver) {
+       case PLAYBACK_X11:     return PLAYBACK_X11_TITLE;
+       case PLAYBACK_X11_XV:  return PLAYBACK_X11_XV_TITLE;
+       case PLAYBACK_X11_GL:  return PLAYBACK_X11_GL_TITLE;
+       case PLAYBACK_BUZ:     return PLAYBACK_BUZ_TITLE;
+       case VIDEO4LINUX:      return VIDEO4LINUX_TITLE;
+       case VIDEO4LINUX2:     return VIDEO4LINUX2_TITLE;
+       case VIDEO4LINUX2JPEG: return VIDEO4LINUX2JPEG_TITLE;
+       case VIDEO4LINUX2MPEG: return VIDEO4LINUX2MPEG_TITLE;
+       case CAPTURE_JPEG_WEBCAM: return CAPTURE_JPEG_WEBCAM_TITLE;
+       case CAPTURE_YUYV_WEBCAM: return CAPTURE_YUYV_WEBCAM_TITLE;
+       case SCREENCAPTURE:    return SCREENCAPTURE_TITLE;
+       case CAPTURE_BUZ:      return CAPTURE_BUZ_TITLE;
+       case CAPTURE_LML:      return CAPTURE_LML_TITLE;
+       case CAPTURE_DVB:      return CAPTURE_DVB_TITLE;
+#ifdef HAVE_FIREWIRE
+       case CAPTURE_FIREWIRE: return CAPTURE_FIREWIRE_TITLE;
+       case CAPTURE_IEC61883: return CAPTURE_IEC61883_TITLE;
+#endif
+       }
+       return "";
+}
+
+int VideoDevice::get_best_colormodel(Asset *asset)
+{
+       return input_base ? input_base->get_best_colormodel(asset) : BC_RGB888;
+}
+
+int VideoDevice::drop_frames(int frames)
+{
+       return input_base ? input_base->drop_frames(frames) : 1;
+}
+
+double VideoDevice::device_timestamp()
+{
+       return input_base ? input_base->device_timestamp() : -1.;
+}
+
+double VideoDevice::get_timestamp()
+{
+       return timestamp;
+}
+
+int VideoDevice::close_all()
+{
+       if(w) {
+               if( output_base ) {
+                       output_base->close_all();
+                       delete output_base;
+                       output_base = 0;
+               }
+       }
+
+       if(r && capturing) {
+               capturing = 0;
+               if(input_base) {
+                       input_base->close_all();
+                       delete input_base;
+                       input_base = 0;
+               }
+               if(keepalive) {
+                       keepalive->stop();
+                       delete keepalive;
+               }
+       }
+
+       input_sources.remove_all_objects();
+       initialize();
+       return 0;
+}
+
+
+int VideoDevice::set_adevice(AudioDevice *adev)
+{
+       adevice = adev;
+       return 0;
+}
+
+
+ArrayList<Channel*>* VideoDevice::get_inputs()
+{
+       return &input_sources;
+}
+
+Channel* VideoDevice::new_input_source(char *device_name)
+{
+       for( int i=0; i<input_sources.total; ++i ) {
+               if(!strcmp(input_sources.values[i]->device_name, device_name))
+                       return input_sources.values[i];
+       }
+       Channel *item = new Channel;
+       strcpy(item->device_name, device_name);
+       input_sources.append(item);
+       return item;
+}
+
+int VideoDevice::get_failed()
+{
+       return keepalive ? keepalive->get_failed() : 0;
+}
+
+int VideoDevice::interrupt_crash()
+{
+       return input_base ? input_base->interrupt_crash() : 0;
+}
+
+int VideoDevice::set_translation(int in_x, int in_y)
+{
+       input_x = in_x;
+       input_y = in_y;
+       return 0;
+}
+
+int VideoDevice::set_field_order(int odd_field_first)
+{
+       this->odd_field_first = odd_field_first;
+       return 0;
+}
+
+int VideoDevice::set_channel(Channel *channel)
+{
+       int result = 0;
+       if( channel ) {
+               channel_lock->lock("VideoDevice::set_channel");
+               this->channel->copy_settings(channel);
+               channel_changed = 1;
+               channel_lock->unlock();
+               result = input_base ? input_base->set_channel(channel) :
+                        output_base ? output_base->set_channel(channel) :
+                        1;
+       }
+       return result;
+}
+
+int VideoDevice::set_captioning(int mode)
+{
+       if( input_base )
+               input_base->set_captioning(mode);
+       return 0;
+}
+
+void VideoDevice::set_quality(int quality)
+{
+       this->quality = quality;
+}
+
+void VideoDevice::set_cpus(int cpus)
+{
+       this->cpus = cpus;
+}
+
+int VideoDevice::set_picture(PictureConfig *picture)
+{
+       if( picture ) {
+               picture_lock->lock("VideoDevice::set_picture");
+               this->picture->copy_settings(picture);
+               picture_changed = 1;
+               picture_lock->unlock();
+
+               if(input_base) return input_base->set_picture(picture);
+       }
+       return 0;
+}
+
+int VideoDevice::update_translation()
+{
+       float frame_in_capture_x1f, frame_in_capture_x2f, frame_in_capture_y1f, frame_in_capture_y2f;
+       float capture_in_frame_x1f, capture_in_frame_x2f, capture_in_frame_y1f, capture_in_frame_y2f;
+       //int z_changed = 0;
+
+       if(frame_resized) {
+               input_x = new_input_x;
+               input_y = new_input_y;
+               if( in_config->driver == VIDEO4LINUX ||
+                       in_config->driver == VIDEO4LINUX2 ) {
+                       if(input_z != new_input_z) {
+                               input_z = new_input_z;
+                               //z_changed = 1;
+
+                               capture_w = (int)((float)in_config->w * input_z + 0.5);
+                               capture_h = (int)((float)in_config->h * input_z + 0.5);
+
+// Need to align to multiple of 4
+                               capture_w &= ~3;
+                               capture_h &= ~3;
+                       }
+
+                       frame_in_capture_x1f = (float)input_x * input_z + capture_w / 2 - in_config->w / 2;
+                       frame_in_capture_x2f = (float)input_x * input_z  + capture_w / 2 + in_config->w / 2;
+                       frame_in_capture_y1f = (float)input_y * input_z  + capture_h / 2 - in_config->h / 2;
+                       frame_in_capture_y2f = (float)input_y * input_z  + capture_h / 2 + in_config->h / 2;
+
+                       capture_in_frame_x1f = 0;
+                       capture_in_frame_y1f = 0;
+                       capture_in_frame_x2f = in_config->w;
+                       capture_in_frame_y2f = in_config->h;
+
+                       if(frame_in_capture_x1f < 0) { capture_in_frame_x1f -= frame_in_capture_x1f; frame_in_capture_x1f = 0; }
+                       if(frame_in_capture_y1f < 0) { capture_in_frame_y1f -= frame_in_capture_y1f; frame_in_capture_y1f = 0; }
+                       if(frame_in_capture_x2f > capture_w) { capture_in_frame_x2f -= frame_in_capture_x2f - capture_w; frame_in_capture_x2f = capture_w; }
+                       if(frame_in_capture_y2f > capture_h) { capture_in_frame_y2f -= frame_in_capture_y2f - capture_h; frame_in_capture_y2f = capture_h; }
+
+                       frame_in_capture_x1 = (int)frame_in_capture_x1f;
+                       frame_in_capture_y1 = (int)frame_in_capture_y1f;
+                       frame_in_capture_x2 = (int)frame_in_capture_x2f;
+                       frame_in_capture_y2 = (int)frame_in_capture_y2f;
+
+                       capture_in_frame_x1 = (int)capture_in_frame_x1f;
+                       capture_in_frame_y1 = (int)capture_in_frame_y1f;
+                       capture_in_frame_x2 = (int)capture_in_frame_x2f;
+                       capture_in_frame_y2 = (int)capture_in_frame_y2f;
+
+                       frame_resized = 0;
+               }
+       }
+       return 0;
+}
+
+int VideoDevice::set_latency_counter(int value)
+{
+       latency_counter = value;
+       return 0;
+}
+
+int VideoDevice::has_signal()
+{
+       return input_base ? input_base->has_signal() : 0;
+}
+
+int VideoDevice::create_channeldb(ArrayList<Channel*> *channeldb)
+{
+       return input_base ? input_base->create_channeldb(channeldb) : 1;
+}
+
+
+int VideoDevice::read_buffer(VFrame *frame)
+{
+       int result = 0;
+       if(!capturing) return 0;
+
+//printf("VideoDevice::read_buffer %p %p\n", frame, input_base);
+       if(input_base) {
+// Reset the keepalive thread
+               if(keepalive) keepalive->capturing = 1;
+               result = input_base->read_buffer(frame);
+               timestamp = frame->get_timestamp();
+               if( timestamp < 0 ) {
+                       struct timeval tv;  gettimeofday(&tv, 0);
+                       timestamp = tv.tv_sec + tv.tv_usec / 1000000.0;
+               }
+               if(keepalive) {
+                       keepalive->capturing = 0;
+                       keepalive->reset_keepalive();
+               }
+               return result;
+       }
+
+       return 0;
+}
+
+
+// ================================= OUTPUT ==========================================
+
+
+int VideoDevice::open_output(VideoOutConfig *config, float rate,
+       int out_w, int out_h, Canvas *output, int single_frame)
+{
+       w = 1;
+//printf("VideoDevice::open_output 1 %d\n", out_config->driver);
+       *this->out_config = *config;
+//printf("VideoDevice::open_output 1 %d\n", out_config->driver);
+       this->out_w = out_w;
+       this->out_h = out_h;
+       this->orate = rate;
+       this->single_frame = single_frame;
+
+//printf("VideoDevice::open_output 1 %d\n", out_config->driver);
+       switch(out_config->driver) {
+#ifdef HAVE_VIDEO4LINUX
+       case PLAYBACK_BUZ:
+               output_base = new VDeviceBUZ(this);
+               break;
+       case PLAYBACK_LML:
+               output_base = new VDeviceLML(this);
+               break;
+#endif
+       case PLAYBACK_X11:
+       case PLAYBACK_X11_XV:
+       case PLAYBACK_X11_GL:
+               output_base = new VDeviceX11(this, output);
+               break;
+
+#ifdef HAVE_FIREWIRE
+       case PLAYBACK_DV1394:
+       case PLAYBACK_FIREWIRE:
+       case PLAYBACK_IEC61883:
+               output_base = new VDevice1394(this);
+               break;
+#endif
+       }
+//printf("VideoDevice::open_output 2 %d\n", out_config->driver);
+
+       if(output_base->open_output()) {
+               delete output_base;
+               output_base = 0;
+       }
+//printf("VideoDevice::open_output 3 %d\n", out_config->driver);
+       return output_base ? 0 : 1;
+}
+
+
+
+int VideoDevice::start_playback()
+{
+// arm buffer before doing this
+       is_playing_back = 1;
+       interrupt = 0;
+       return output_base ? output_base->start_playback() : 1;
+}
+
+int VideoDevice::stop_playback()
+{
+       if( output_base ) output_base->stop_playback();
+       is_playing_back = 0;
+       interrupt = 0;
+       return 0;
+}
+
+void VideoDevice::goose_input()
+{
+       if(input_base) input_base->goose_input();
+}
+
+void VideoDevice::new_output_buffer(VFrame **output, int colormodel)
+{
+       if(!output_base) return;
+       output_base->new_output_buffer(output, colormodel);
+}
+
+
+int VideoDevice::interrupt_playback()
+{
+       interrupt = 1;
+       return 0;
+}
+
+int VideoDevice::write_buffer(VFrame *output, EDL *edl)
+{
+       return output_base ? output_base->write_buffer(output, edl) : 1;
+}
+
+int VideoDevice::output_visible()
+{
+       return output_base ? output_base->output_visible() : 0;
+}
+
+BC_Bitmap* VideoDevice::get_bitmap()
+{
+       return output_base ? output_base->get_bitmap() : 0;
+}
+
+
+int VideoDevice::set_cloexec_flag(int desc, int value)
+{
+       int oldflags = fcntl(desc, F_GETFD, 0);
+       if( oldflags < 0 ) return oldflags;
+       if( value != 0 ) 
+               oldflags |= FD_CLOEXEC;
+       else
+               oldflags &= ~FD_CLOEXEC;
+       return fcntl(desc, F_SETFD, oldflags);
+}
+
+
+void VideoDevice::auto_update(double rate, int width, int height)
+{
+       if( !in_config || !in_config->follow_video ) return;
+       capture_w = width;
+       capture_h = height;
+       frame_rate = rate;
+       in_config_updated = 1;
+}
+
+int VideoDevice::config_updated()
+{
+       if( !in_config || !in_config->follow_video ) return 0;
+       return in_config_updated;
+}
+
+void VideoDevice::config_update()
+{
+       in_config_updated = 0;
+       VideoInConfig *vconfig_in = mwindow->edl->session->vconfig_in;
+       vconfig_in->w = capture_w;
+       vconfig_in->h = capture_h;
+       vconfig_in->in_framerate = frame_rate;
+}
+
+
+int VideoDevice::start_toc(const char *path, const char *toc_path)
+{
+       return input_base ? input_base->start_toc(path, toc_path) : -1;
+}
+
+int VideoDevice::start_record(int fd, int bsz)
+{
+       return input_base ? input_base->start_record(fd, bsz) : -1;
+}
+
+int VideoDevice::stop_record()
+{
+       return input_base ? input_base->stop_record() : -1;
+}
+
+
+int VideoDevice::total_video_streams()
+{
+       return input_base ? input_base->total_video_streams() : 0;
+}
+
+
+// skimming
+int VideoDevice::get_video_pid(int track)
+{
+       return !input_base ? -1 : input_base->get_video_pid(track);
+
+}
+
+int VideoDevice::get_video_info(int track, int &pid,
+               double &framerate, int &width, int &height, char *title)
+{
+       return !input_base ? 1 : input_base->
+               get_video_info(track, pid, framerate, width, height, title);
+}
+
+int VideoDevice::get_thumbnail(int stream, int64_t &position,
+               unsigned char *&thumbnail, int &ww, int &hh)
+{
+       return !input_base ? 1 :
+               input_base->get_thumbnail(stream, position, thumbnail, ww, hh);
+}
+
+int VideoDevice::set_skimming(int track, int skim, skim_fn fn, void *vp)
+{
+       return !input_base ? 1 :
+               input_base->set_skimming(track, skim, fn, vp);
+}
+