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