Credit Andrew - improve in-tree documentation
[goodguy/cinelerra.git] / cinelerra / videodevice.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008-2013 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 "assets.h"
24 #include "bccapture.h"
25 #include "bcsignals.h"
26 #include "channel.h"
27 #include "channeldb.h"
28 #include "chantables.h"
29 #include "edl.h"
30 #include "edlsession.h"
31 #include "file.inc"
32 #ifdef HAVE_DV
33 #include "libdv.h"
34 #endif
35 #include "libmjpeg.h"
36 #include "mainmenu.h"
37 #include "mutex.h"
38 #include "mwindow.h"
39 #include "mwindowgui.h"
40 #include "picture.h"
41 #include "playbackconfig.h"
42 #include "playbackengine.h"
43 #include "preferences.h"
44 #include "recordconfig.h"
45 #include "recordgui.h"
46 #include "recordmonitor.h"
47 #include "record.h"
48 #ifdef HAVE_FIREWIRE
49 #include "vdevice1394.h"
50 #endif
51 #include "vdevicedvb.h"
52 #include "vdevicev4l2.h"
53 #include "vdevicev4l2jpeg.h"
54 #include "vdevicev4l2mpeg.h"
55 #include "vdevicex11.h"
56 #include "videoconfig.h"
57 #include "videodevice.h"
58 #include "videowindow.h"
59 #include "videowindowgui.h"
60 #include "vframe.h"
61
62 #include <string.h>
63 #include <unistd.h>
64 #include <fcntl.h>
65
66 KeepaliveThread::KeepaliveThread(VideoDevice *device)
67  : Thread(1, 0, 0)
68 {
69         still_alive = 1;
70         failed = 0;
71         interrupted = 0;
72         set_synchronous(1);
73         this->device = device;
74         capturing = 0;
75         startup_lock = new Mutex("KeepaliveThread::startup_lock");
76 }
77
78 KeepaliveThread::~KeepaliveThread()
79 {
80         delete startup_lock;
81 }
82
83 int KeepaliveThread::start_keepalive()
84 {
85         startup_lock->lock("KeepaliveThread::start_keepalive 1");
86         start();
87         startup_lock->lock("KeepaliveThread::start_keepalive 2");
88         startup_lock->unlock();
89         return 0;
90 }
91
92 void KeepaliveThread::run()
93 {
94         startup_lock->unlock();
95         while(!interrupted)
96         {
97                 still_alive = 0;
98 // Give the capture a moment
99 // Should fix the delay in case users want slower frame rates.
100                 timer.delay((long)(KEEPALIVE_DELAY * 1000));
101
102 // See if a capture happened
103                 if(still_alive == 0 && capturing)
104                 {
105 //                      printf("KeepaliveThread::run: device crashed\n");
106                         failed++;
107                 }
108                 else
109                         failed = 0;
110         }
111 }
112
113 int KeepaliveThread::reset_keepalive()
114 {
115         still_alive = 1;
116         return 0;
117 }
118
119 int KeepaliveThread::get_failed()
120 {
121         if(failed) return 1; else return 0;
122 }
123
124 int KeepaliveThread::stop()
125 {
126         interrupted = 1;
127
128 // Force an immediate exit even if capture_frame worked.
129         Thread::cancel();
130         Thread::join();
131         return 0;
132 }
133
134
135
136
137
138
139
140 VideoDevice::VideoDevice(MWindow *mwindow)
141 {
142         this->mwindow = mwindow;
143         in_config = new VideoInConfig;
144         out_config = new VideoOutConfig;
145         channel = new Channel;
146         picture = new PictureConfig();
147         sharing_lock = new Mutex("VideoDevice::sharing_lock");
148         channel_lock = new Mutex("VideoDevice::channel_lock");
149         picture_lock = new Mutex("VideoDevice::picture_lock");
150         initialize();
151 }
152
153
154 VideoDevice::~VideoDevice()
155 {
156         input_sources.remove_all_objects();
157         delete in_config;
158         delete out_config;
159         delete channel;
160         delete picture;
161         delete sharing_lock;
162         delete channel_lock;
163         delete picture_lock;
164 }
165
166 int VideoDevice::initialize()
167 {
168         sharing = 0;
169         done_sharing = 0;
170         sharing_lock->reset();
171         orate = irate = 0;
172         out_w = out_h = 0;
173         r = w = 0;
174         is_playing_back = is_recording = 0;
175         input_x = 0;
176         input_y = 0;
177         input_z = 1;
178         frame_resized = 0;
179         frame_rate = 0;
180         timestamp = -1.;
181         capturing = 0;
182         keepalive = 0;
183         swap_bytes = 0;
184         in_config_updated = 0;
185         input_base = 0;
186         output_base = 0;
187         output_format = 0;
188         interrupt = 0;
189         adevice = 0;
190         quality = 80;
191         cpus = 1;
192         single_frame = 0;
193         channel_changed = 0;
194         picture_changed = 0;
195         odd_field_first = 0;
196         do_cursor = 0;
197         return 0;
198 }
199
200 int VideoDevice::open_input(VideoInConfig *config,
201         int input_x,
202         int input_y,
203         float input_z,
204         double frame_rate)
205 {
206         int result = 0;
207
208         *this->in_config = *config;
209
210         r = 1;
211         this->input_z = -1;   // Force initialization.
212         this->frame_rate = frame_rate;
213         if( input_base ) return 1; // device already open
214
215         switch(in_config->driver) {
216 #ifdef HAVE_VIDEO4LINUX2
217
218                 case VIDEO4LINUX2:
219                 case VIDEO4LINUX2JPEG:
220                 case VIDEO4LINUX2MPEG:
221                 case CAPTURE_JPEG_WEBCAM:
222                 case CAPTURE_YUYV_WEBCAM:
223                         break;
224
225 #endif
226
227                 case SCREENCAPTURE:
228                         this->input_x = input_x;
229                         this->input_y = input_y;
230                         break;
231 #ifdef HAVE_FIREWIRE
232                 case CAPTURE_FIREWIRE:
233                 case CAPTURE_IEC61883:
234                         break;
235 #endif
236
237 #ifdef HAVE_DVB
238                 case CAPTURE_DVB:
239                         break;
240 #endif
241
242                 default:
243                         return 1;
244         }
245
246         new_device_base();
247         if( !input_base ) return 1;
248         result = input_base->open_input();
249         if(!result) capturing = 1;
250         in_config_updated = 0;
251         return 0;
252 }
253
254 VDeviceBase* VideoDevice::new_device_base()
255 {
256         switch(in_config->driver) {
257 #ifdef HAVE_VIDEO4LINUX2
258         case VIDEO4LINUX2:
259         case CAPTURE_JPEG_WEBCAM:
260         case CAPTURE_YUYV_WEBCAM:
261                 return input_base = new VDeviceV4L2(this);
262         case VIDEO4LINUX2JPEG:
263                 return input_base = new VDeviceV4L2JPEG(this);
264         case VIDEO4LINUX2MPEG:
265                 return input_base = new VDeviceV4L2MPEG(this);
266 #endif
267
268         case SCREENCAPTURE:
269                 return input_base = new VDeviceX11(this, 0);
270
271 #ifdef HAVE_FIREWIRE
272         case CAPTURE_FIREWIRE:
273         case CAPTURE_IEC61883:
274                 return input_base = new VDevice1394(this);
275 #endif
276
277 #ifdef HAVE_DVB
278         case CAPTURE_DVB:
279                 return input_base = new VDeviceDVB(this);
280 #endif
281         }
282         return 0;
283 }
284
285 static const char* get_channeldb_path(VideoInConfig *vconfig_in)
286 {
287         char *path = 0;
288         switch(vconfig_in->driver)
289         {
290                 case VIDEO4LINUX2:
291                 case CAPTURE_JPEG_WEBCAM:
292                 case CAPTURE_YUYV_WEBCAM:
293                         path = (char*)"channels_v4l2";
294                         break;
295                 case VIDEO4LINUX2JPEG:
296                         path = (char*)"channels_v4l2jpeg";
297                         break;
298                 case VIDEO4LINUX2MPEG:
299                         path = (char*)"channels_v4l2mpeg";
300                         break;
301                 case CAPTURE_DVB:
302                         path = (char*)"channels_dvb";
303                         break;
304         }
305         return path;
306 }
307
308 void VideoDevice::load_channeldb(ChannelDB *channeldb, VideoInConfig *vconfig_in)
309 {
310         channeldb->load(get_channeldb_path(vconfig_in));
311 }
312
313 void VideoDevice::save_channeldb(ChannelDB *channeldb, VideoInConfig *vconfig_in)
314 {
315         channeldb->save(get_channeldb_path(vconfig_in));
316 }
317
318
319 VDeviceBase* VideoDevice::get_input_base()
320 {
321         return input_base;
322 }
323
324 VDeviceBase* VideoDevice::get_output_base()
325 {
326         return output_base;
327 }
328
329 DeviceMPEGInput *VideoDevice::mpeg_device()
330 {
331         return !input_base ? 0 : input_base->mpeg_device();
332 }
333
334 int VideoDevice::is_compressed(int driver, int use_file, int use_fixed)
335 {
336 // FileMOV needs to have write_frames called so the start codes get scanned.
337         return ((driver == VIDEO4LINUX2JPEG && use_fixed) ||
338                 (driver == CAPTURE_JPEG_WEBCAM && use_fixed) ||
339                 driver == CAPTURE_FIREWIRE ||
340                 driver == CAPTURE_IEC61883);
341 }
342
343 int VideoDevice::is_compressed(int use_file, int use_fixed)
344 {
345         return is_compressed(in_config->driver, use_file, use_fixed);
346 }
347
348
349 void VideoDevice::fix_asset(Asset *asset, int driver)
350 {
351 // Fix asset using legacy routine
352         const char *vcodec = 0;
353         switch(driver) {
354 #ifdef HAVE_DV
355         case CAPTURE_IEC61883:
356         case CAPTURE_FIREWIRE:
357                 vcodec = CODEC_TAG_DVSD;
358                 break;
359 #endif
360         case VIDEO4LINUX2JPEG:
361                 vcodec = CODEC_TAG_MJPEG;
362                 break;
363
364         case CAPTURE_JPEG_WEBCAM:
365                 vcodec = CODEC_TAG_JPEG;
366                 break;
367         }
368         if( vcodec ) {
369                 asset->format = FILE_FFMPEG;
370                 strcpy(asset->vcodec, vcodec);
371                 return;
372         }
373
374 // Fix asset using inherited routine
375         new_device_base();
376
377         if(input_base) input_base->fix_asset(asset);
378         delete input_base;
379         input_base = 0;
380 }
381
382
383 const char* VideoDevice::drivertostr(int driver)
384 {
385         switch(driver) {
386         case PLAYBACK_X11:     return PLAYBACK_X11_TITLE;
387         case PLAYBACK_X11_XV:  return PLAYBACK_X11_XV_TITLE;
388         case PLAYBACK_X11_GL:  return PLAYBACK_X11_GL_TITLE;
389         case VIDEO4LINUX2:     return VIDEO4LINUX2_TITLE;
390         case VIDEO4LINUX2JPEG: return VIDEO4LINUX2JPEG_TITLE;
391         case VIDEO4LINUX2MPEG: return VIDEO4LINUX2MPEG_TITLE;
392         case CAPTURE_JPEG_WEBCAM: return CAPTURE_JPEG_WEBCAM_TITLE;
393         case CAPTURE_YUYV_WEBCAM: return CAPTURE_YUYV_WEBCAM_TITLE;
394         case SCREENCAPTURE:    return SCREENCAPTURE_TITLE;
395         case CAPTURE_DVB:      return CAPTURE_DVB_TITLE;
396 #ifdef HAVE_FIREWIRE
397         case CAPTURE_FIREWIRE: return CAPTURE_FIREWIRE_TITLE;
398         case CAPTURE_IEC61883: return CAPTURE_IEC61883_TITLE;
399 #endif
400         }
401         return "";
402 }
403
404 int VideoDevice::get_best_colormodel(Asset *asset)
405 {
406         return input_base ? input_base->get_best_colormodel(asset) : BC_RGB888;
407 }
408
409 int VideoDevice::drop_frames(int frames)
410 {
411         return input_base ? input_base->drop_frames(frames) : 1;
412 }
413
414 double VideoDevice::device_timestamp()
415 {
416         return input_base ? input_base->device_timestamp() : -1.;
417 }
418
419 double VideoDevice::get_timestamp()
420 {
421         return timestamp;
422 }
423
424 int VideoDevice::close_all()
425 {
426         if(w) {
427                 if( output_base ) {
428                         output_base->close_all();
429                         delete output_base;
430                         output_base = 0;
431                 }
432         }
433
434         if(r && capturing) {
435                 capturing = 0;
436                 if(input_base) {
437                         input_base->close_all();
438                         delete input_base;
439                         input_base = 0;
440                 }
441                 if(keepalive) {
442                         keepalive->stop();
443                         delete keepalive;
444                 }
445         }
446
447         input_sources.remove_all_objects();
448         initialize();
449         return 0;
450 }
451
452
453 int VideoDevice::set_adevice(AudioDevice *adev)
454 {
455         adevice = adev;
456         return 0;
457 }
458
459
460 ArrayList<Channel*>* VideoDevice::get_inputs()
461 {
462         return &input_sources;
463 }
464
465 Channel* VideoDevice::new_input_source(char *device_name)
466 {
467         for( int i=0; i<input_sources.total; ++i ) {
468                 if(!strcmp(input_sources.values[i]->device_name, device_name))
469                         return input_sources.values[i];
470         }
471         Channel *item = new Channel;
472         strcpy(item->device_name, device_name);
473         input_sources.append(item);
474         return item;
475 }
476
477 int VideoDevice::get_failed()
478 {
479         return keepalive ? keepalive->get_failed() : 0;
480 }
481
482 int VideoDevice::interrupt_crash()
483 {
484         return input_base ? input_base->interrupt_crash() : 0;
485 }
486
487 int VideoDevice::set_translation(int in_x, int in_y)
488 {
489         input_x = in_x;
490         input_y = in_y;
491         return 0;
492 }
493
494 int VideoDevice::set_field_order(int odd_field_first)
495 {
496         this->odd_field_first = odd_field_first;
497         return 0;
498 }
499
500 void VideoDevice::set_do_cursor(int do_cursor, int do_big_cursor)
501 {
502         int cursor_scale = 0;
503         if(do_cursor)
504         {
505                 cursor_scale = 1;
506                 if(do_big_cursor)
507                 {
508                         cursor_scale = 2;
509                 }
510         }
511
512         this->do_cursor = cursor_scale;
513 }
514
515 int VideoDevice::set_channel(Channel *channel)
516 {
517         int result = 0;
518         if( channel ) {
519                 channel_lock->lock("VideoDevice::set_channel");
520                 this->channel->copy_settings(channel);
521                 channel_changed = 1;
522                 channel_lock->unlock();
523                 result = input_base ? input_base->set_channel(channel) :
524                          output_base ? output_base->set_channel(channel) :
525                          1;
526         }
527         return result;
528 }
529
530 int VideoDevice::set_captioning(int mode)
531 {
532         if( input_base )
533                 input_base->set_captioning(mode);
534         return 0;
535 }
536
537 void VideoDevice::set_quality(int quality)
538 {
539         this->quality = quality;
540 }
541
542 void VideoDevice::set_cpus(int cpus)
543 {
544         this->cpus = cpus;
545 }
546
547 int VideoDevice::set_picture(PictureConfig *picture)
548 {
549         if( picture ) {
550                 picture_lock->lock("VideoDevice::set_picture");
551                 this->picture->copy_settings(picture);
552                 picture_changed = 1;
553                 picture_lock->unlock();
554
555                 if(input_base) return input_base->set_picture(picture);
556         }
557         return 0;
558 }
559
560 int VideoDevice::update_translation()
561 {
562         float frame_in_capture_x1f, frame_in_capture_x2f, frame_in_capture_y1f, frame_in_capture_y2f;
563         float capture_in_frame_x1f, capture_in_frame_x2f, capture_in_frame_y1f, capture_in_frame_y2f;
564         //int z_changed = 0;
565
566         if(frame_resized) {
567                 input_x = new_input_x;
568                 input_y = new_input_y;
569                 if( in_config->driver == VIDEO4LINUX2 ) {
570                         if(input_z != new_input_z) {
571                                 input_z = new_input_z;
572                                 //z_changed = 1;
573
574                                 capture_w = (int)((float)in_config->w * input_z + 0.5);
575                                 capture_h = (int)((float)in_config->h * input_z + 0.5);
576
577 // Need to align to multiple of 4
578                                 capture_w &= ~3;
579                                 capture_h &= ~3;
580                         }
581
582                         frame_in_capture_x1f = (float)input_x * input_z + capture_w / 2 - in_config->w / 2;
583                         frame_in_capture_x2f = (float)input_x * input_z  + capture_w / 2 + in_config->w / 2;
584                         frame_in_capture_y1f = (float)input_y * input_z  + capture_h / 2 - in_config->h / 2;
585                         frame_in_capture_y2f = (float)input_y * input_z  + capture_h / 2 + in_config->h / 2;
586
587                         capture_in_frame_x1f = 0;
588                         capture_in_frame_y1f = 0;
589                         capture_in_frame_x2f = in_config->w;
590                         capture_in_frame_y2f = in_config->h;
591
592                         if(frame_in_capture_x1f < 0) { capture_in_frame_x1f -= frame_in_capture_x1f; frame_in_capture_x1f = 0; }
593                         if(frame_in_capture_y1f < 0) { capture_in_frame_y1f -= frame_in_capture_y1f; frame_in_capture_y1f = 0; }
594                         if(frame_in_capture_x2f > capture_w) { capture_in_frame_x2f -= frame_in_capture_x2f - capture_w; frame_in_capture_x2f = capture_w; }
595                         if(frame_in_capture_y2f > capture_h) { capture_in_frame_y2f -= frame_in_capture_y2f - capture_h; frame_in_capture_y2f = capture_h; }
596
597                         frame_in_capture_x1 = (int)frame_in_capture_x1f;
598                         frame_in_capture_y1 = (int)frame_in_capture_y1f;
599                         frame_in_capture_x2 = (int)frame_in_capture_x2f;
600                         frame_in_capture_y2 = (int)frame_in_capture_y2f;
601
602                         capture_in_frame_x1 = (int)capture_in_frame_x1f;
603                         capture_in_frame_y1 = (int)capture_in_frame_y1f;
604                         capture_in_frame_x2 = (int)capture_in_frame_x2f;
605                         capture_in_frame_y2 = (int)capture_in_frame_y2f;
606
607                         frame_resized = 0;
608                 }
609         }
610         return 0;
611 }
612
613 int VideoDevice::set_latency_counter(int value)
614 {
615         latency_counter = value;
616         return 0;
617 }
618
619 int VideoDevice::has_signal()
620 {
621         return input_base ? input_base->has_signal() : 0;
622 }
623
624 int VideoDevice::create_channeldb(ArrayList<Channel*> *channeldb)
625 {
626         return input_base ? input_base->create_channeldb(channeldb) : 1;
627 }
628
629
630 int VideoDevice::read_buffer(VFrame *frame)
631 {
632         int result = 0;
633         if(!capturing) return 0;
634
635 //printf("VideoDevice::read_buffer %p %p\n", frame, input_base);
636         if(input_base) {
637 // Reset the keepalive thread
638                 if(keepalive) keepalive->capturing = 1;
639                 result = input_base->read_buffer(frame);
640                 timestamp = frame->get_timestamp();
641                 if( timestamp < 0 ) {
642                         struct timeval tv;  gettimeofday(&tv, 0);
643                         timestamp = tv.tv_sec + tv.tv_usec / 1000000.0;
644                 }
645                 if(keepalive) {
646                         keepalive->capturing = 0;
647                         keepalive->reset_keepalive();
648                 }
649                 return result;
650         }
651
652         return 0;
653 }
654
655
656 // ================================= OUTPUT ==========================================
657
658
659 int VideoDevice::open_output(VideoOutConfig *config, float rate,
660         int out_w, int out_h, Canvas *output, int single_frame)
661 {
662         w = 1;
663 //printf("VideoDevice::open_output 1 %d\n", out_config->driver);
664         *this->out_config = *config;
665 //printf("VideoDevice::open_output 1 %d\n", out_config->driver);
666         this->out_w = out_w;
667         this->out_h = out_h;
668         this->orate = rate;
669         this->single_frame = single_frame;
670
671 //printf("VideoDevice::open_output 1 %d\n", out_config->driver);
672         switch(out_config->driver) {
673         case PLAYBACK_X11:
674         case PLAYBACK_X11_XV:
675         case PLAYBACK_X11_GL:
676                 output_base = new VDeviceX11(this, output);
677                 break;
678
679 #ifdef HAVE_FIREWIRE
680         case PLAYBACK_DV1394:
681         case PLAYBACK_FIREWIRE:
682         case PLAYBACK_IEC61883:
683                 output_base = new VDevice1394(this);
684                 break;
685 #endif
686         }
687 //printf("VideoDevice::open_output 2 %d\n", out_config->driver);
688
689         if(output_base->open_output()) {
690                 delete output_base;
691                 output_base = 0;
692         }
693 //printf("VideoDevice::open_output 3 %d\n", out_config->driver);
694         return output_base ? 0 : 1;
695 }
696
697
698
699 int VideoDevice::start_playback()
700 {
701 // arm buffer before doing this
702         is_playing_back = 1;
703         interrupt = 0;
704         return output_base ? output_base->start_playback() : 1;
705 }
706
707 int VideoDevice::stop_playback()
708 {
709         if( output_base ) output_base->stop_playback();
710         is_playing_back = 0;
711         interrupt = 0;
712         return 0;
713 }
714
715 void VideoDevice::goose_input()
716 {
717         if(input_base) input_base->goose_input();
718 }
719
720 void VideoDevice::new_output_buffer(VFrame **output, int colormodel, EDL *edl)
721 {
722         if(!output_base) return;
723         output_base->new_output_buffer(output, colormodel, edl);
724 }
725
726
727 int VideoDevice::interrupt_playback()
728 {
729         interrupt = 1;
730         return 0;
731 }
732
733 int VideoDevice::write_buffer(VFrame *output, EDL *edl)
734 {
735         return output_base ? output_base->write_buffer(output, edl) : 1;
736 }
737
738 int VideoDevice::output_visible()
739 {
740         return output_base ? output_base->output_visible() : 0;
741 }
742
743 BC_Bitmap* VideoDevice::get_bitmap()
744 {
745         return output_base ? output_base->get_bitmap() : 0;
746 }
747
748
749 int VideoDevice::set_cloexec_flag(int desc, int value)
750 {
751         int oldflags = fcntl(desc, F_GETFD, 0);
752         if( oldflags < 0 ) return oldflags;
753         if( value != 0 )
754                 oldflags |= FD_CLOEXEC;
755         else
756                 oldflags &= ~FD_CLOEXEC;
757         return fcntl(desc, F_SETFD, oldflags);
758 }
759
760
761 void VideoDevice::auto_update(double rate, int width, int height)
762 {
763         if( !in_config || !in_config->follow_video ) return;
764         capture_w = width;
765         capture_h = height;
766         frame_rate = rate;
767         in_config_updated = 1;
768 }
769
770 int VideoDevice::config_updated()
771 {
772         if( !in_config || !in_config->follow_video ) return 0;
773         return in_config_updated;
774 }
775
776 void VideoDevice::config_update()
777 {
778         in_config_updated = 0;
779         VideoInConfig *vconfig_in = mwindow->edl->session->vconfig_in;
780         vconfig_in->w = capture_w;
781         vconfig_in->h = capture_h;
782         vconfig_in->in_framerate = frame_rate;
783 }
784
785
786 int VideoDevice::start_toc(const char *path, const char *toc_path)
787 {
788         return input_base ? input_base->start_toc(path, toc_path) : -1;
789 }
790
791 int VideoDevice::start_record(int fd, int bsz)
792 {
793         return input_base ? input_base->start_record(fd, bsz) : -1;
794 }
795
796 int VideoDevice::stop_record()
797 {
798         return input_base ? input_base->stop_record() : -1;
799 }
800
801
802 int VideoDevice::total_video_streams()
803 {
804         return input_base ? input_base->total_video_streams() : 0;
805 }
806
807
808 // skimming
809 int VideoDevice::get_video_pid(int track)
810 {
811         return !input_base ? -1 : input_base->get_video_pid(track);
812
813 }
814
815 int VideoDevice::get_video_info(int track, int &pid,
816                 double &framerate, int &width, int &height, char *title)
817 {
818         return !input_base ? 1 : input_base->
819                 get_video_info(track, pid, framerate, width, height, title);
820 }
821
822 int VideoDevice::get_thumbnail(int stream, int64_t &position,
823                 unsigned char *&thumbnail, int &ww, int &hh)
824 {
825         return !input_base ? 1 :
826                 input_base->get_thumbnail(stream, position, thumbnail, ww, hh);
827 }
828
829 int VideoDevice::set_skimming(int track, int skim, skim_fn fn, void *vp)
830 {
831         return !input_base ? 1 :
832                 input_base->set_skimming(track, skim, fn, vp);
833 }
834