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