no longer need ffmpeg patch0 which was for Termux
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / recordmonitor.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2011 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include "asset.h"
22 #include "bcdialog.h"
23 #include "bcsignals.h"
24 #include "xfer/xfer.h"
25 #include "channelpicker.h"
26 #include "condition.h"
27 #include "cursors.h"
28 #include "devicedvbinput.h"
29 #ifdef HAVE_DV
30 #include "libdv.h"
31 #endif
32 #include "edl.h"
33 #include "edlsession.h"
34 #include "keys.h"
35 #include "language.h"
36 #include "mainsession.h"
37 #include "meterpanel.h"
38 #include "mwindow.h"
39 #include "playbackconfig.h"
40 #include "preferences.h"
41 #include "record.h"
42 #include "recordconfig.h"
43 #include "recordgui.h"
44 #include "recordscopes.h"
45 #include "recordtransport.h"
46 #include "recordmonitor.h"
47 #include "signalstatus.h"
48 #include "theme.h"
49 #include "videodevice.inc"
50 #include "vframe.h"
51 #include "videodevice.h"
52 #include "vdevicedvb.h"
53
54
55 RecordMonitor::RecordMonitor(MWindow *mwindow, Record *record)
56  : Thread(1, 0, 0)
57 {
58         this->mwindow = mwindow;
59         this->record = record;
60         device = 0;
61         thread = 0;
62         scope_thread = 0;
63 }
64
65
66 RecordMonitor::~RecordMonitor()
67 {
68         delete thread;
69         window->set_done(0);
70         Thread::join();
71         if( device ) {
72                 device->close_all();
73                 delete device;
74         }
75         delete scope_thread;
76         delete window;
77 }
78
79 void RecordMonitor::create_objects()
80 {
81         int min_w = xS(150);
82
83         if( !record->default_asset->video_data )
84                 min_w = MeterPanel::get_meters_width(mwindow->theme,
85                         record->default_asset->channels, 1);
86
87         window = new RecordMonitorGUI(mwindow, record, this, min_w);
88         window->create_objects();
89
90         if( record->default_asset->video_data ) {
91 // Configure the output for record monitoring
92                 VideoOutConfig config;
93                 device = new VideoDevice;
94
95 // Override default device for X11 drivers
96                 if(mwindow->edl->session->playback_config->vconfig->driver ==
97                         PLAYBACK_X11_XV) config.driver = PLAYBACK_X11_XV;
98                 config.x11_use_fields = 0;
99                 device->open_output(&config,
100                         record->default_asset->frame_rate,
101                         record->default_asset->width,
102                         record->default_asset->height,
103                         window->canvas, 0);
104
105                 scope_thread = new RecordScopeThread(mwindow, this);
106
107                 if(mwindow->session->record_scope) {
108                         scope_thread->start();
109                 }
110
111                 thread = new RecordMonitorThread(mwindow, record, this);
112                 thread->start_playback();
113         }
114
115         Thread::start();
116 }
117
118
119 void RecordMonitor::run()
120 {
121         window->run_window();
122         close_threads();
123 }
124
125 void RecordMonitor::close_threads()
126 {
127         if(window->channel_picker) window->channel_picker->close_threads();
128 }
129
130 int RecordMonitor::update(VFrame *vframe)
131 {
132         return thread->write_frame(vframe);
133 }
134
135 void RecordMonitor::update_channel(char *text)
136 {
137         if( window->channel_picker )
138                 window->channel_picker->channel_text->update(text);
139 }
140
141 int RecordMonitor::get_mbuttons_height()
142 {
143         return RECBUTTON_HEIGHT;
144 }
145
146 void RecordMonitor::fix_size(int &w, int &h, int width_given, float aspect_ratio)
147 {
148         w = width_given;
149         h = (int)((float)width_given / aspect_ratio);
150 }
151
152 float RecordMonitor::get_scale(int w)
153 {
154         if( mwindow->edl->get_aspect_ratio() >
155                 (float)record->frame_w / record->frame_h ) {
156                 return (float)w /
157                         ((float)record->frame_h *
158                         mwindow->edl->get_aspect_ratio());
159         }
160         else {
161                 return (float)w / record->frame_w;
162         }
163 }
164
165 int RecordMonitor::get_canvas_height()
166 {
167         return window->get_h() - get_mbuttons_height();
168 }
169
170 int RecordMonitor::get_channel_x()
171 {
172 //      return 240;
173         return xS(5);
174 }
175
176 int RecordMonitor::get_channel_y()
177 {
178         return yS(2);
179 }
180
181 void RecordMonitor::stop_playback()
182 {
183         if( !thread || thread->finished() ) return;
184         window->enable_signal_status(0);
185         if( thread ) {
186                 thread->stop_playback();
187         }
188 }
189
190 void RecordMonitor::start_playback()
191 {
192         if( thread ) {
193                 thread->output_lock->reset();
194                 thread->start_playback();
195         }
196         window->enable_signal_status(1);
197 }
198
199
200 void RecordMonitor::reconfig()
201 {
202         stop_playback();
203         VideoOutConfig config = *device->out_config;
204         device->close_all();
205         device->open_output(&config,
206                 record->default_asset->frame_rate,
207                 record->default_asset->width,
208                 record->default_asset->height,
209                 window->canvas, 0);
210         start_playback();
211         redraw();
212 }
213
214 void RecordMonitor::redraw()
215 {
216         if( thread && window && thread->record->video_window_open )
217                 window->redraw();
218 }
219
220 void RecordMonitor::display_vframe(VFrame *in, int x, int y,
221                 int alpha, double secs, double scale)
222 {
223         if( !thread ) return;
224         thread->display_vframe(in, x, y, alpha, secs, scale);
225 }
226
227 void RecordMonitor::undisplay_vframe()
228 {
229         if( !thread ) return;
230         thread->undisplay_vframe();
231 }
232
233 RecordMonitorGUI::RecordMonitorGUI(MWindow *mwindow,
234         Record *record, RecordMonitor *thread, int min_w)
235  : BC_Window(_(PROGRAM_NAME ": Video in"),
236                         mwindow->session->rmonitor_x,
237                         mwindow->session->rmonitor_y,
238                         mwindow->session->rmonitor_w,
239                         mwindow->session->rmonitor_h,
240                         min_w, yS(50), 1, 1, 1, -1,
241                         mwindow->get_cwindow_display())
242 {
243 //printf("%d %d\n", mwindow->session->rmonitor_w, mwindow->theme->rmonitor_meter_x);
244         this->mwindow = mwindow;
245         this->thread = thread;
246         this->record = record;
247 #ifdef HAVE_FIREWIRE
248         avc = 0;
249         avc1394_transport = 0;
250         avc1394transport_title = 0;
251         avc1394transport_timecode = 0;
252         avc1394transport_thread = 0;
253 #endif
254         bitmap = 0;
255         channel_picker = 0;
256         reverse_interlace = 0;
257         meters = 0;
258         canvas = 0;
259         cursor_toggle = 0;
260         big_cursor_toggle = 0;
261         current_operation = MONITOR_NONE;
262         signal_status = 0;
263 // *** CONTEXT_HELP ***
264         context_help_set_keyword("Capturing and Recording Media");
265 }
266
267 RecordMonitorGUI::~RecordMonitorGUI()
268 {
269         lock_window("RecordMonitorGUI::~RecordMonitorGUI");
270 #ifdef HAVE_DVB
271         delete signal_status;
272 #endif
273         delete canvas;
274         delete cursor_toggle;
275         delete big_cursor_toggle;
276         delete bitmap;
277         if( channel_picker ) delete channel_picker;
278 #ifdef HAVE_FIREWIRE
279         delete avc1394transport_thread;
280         delete avc;
281         delete avc1394_transport;
282         delete avc1394transport_title;
283 #endif
284         delete meters;
285         unlock_window();
286 }
287
288 void RecordMonitorGUI::create_objects()
289 {
290 // y offset for video canvas if we have the transport controls
291         lock_window("RecordMonitorGUI::create_objects");
292         int driver = mwindow->edl->session->vconfig_in->driver;
293         int do_channel = (driver == CAPTURE_DVB ||
294                         driver == VIDEO4LINUX2 ||
295                         driver == VIDEO4LINUX2JPEG ||
296                         driver == VIDEO4LINUX2MPEG ||
297                         driver == CAPTURE_JPEG_WEBCAM ||
298                         driver == CAPTURE_YUYV_WEBCAM);
299         int do_cursor = driver == SCREENCAPTURE;
300         int do_scopes = do_channel || driver == SCREENCAPTURE;
301         int do_interlace = driver == VIDEO4LINUX2JPEG;
302         int background_done = 0;
303         int do_audio = record->default_asset->audio_data;
304         int do_video = record->default_asset->video_data;
305         int do_meters = record->metering_audio;
306         int channels = !do_meters ? 0 : record->default_asset->channels;
307
308         mwindow->theme->get_rmonitor_sizes(do_meters, do_video,
309                 do_channel || do_scopes, do_interlace, 0, channels);
310
311
312         if(do_video) {
313 #ifdef HAVE_FIREWIRE
314                 if( driver == CAPTURE_FIREWIRE || driver == CAPTURE_IEC61883 ) {
315                         avc = new AVC1394Control;
316                         if( avc->device > -1 ) {
317                                 mwindow->theme->get_rmonitor_sizes(do_meters, do_video,
318                                          do_channel, do_interlace, 1, channels);
319                                 mwindow->theme->draw_rmonitor_bg(this);
320                                 background_done = 1;
321
322                                 avc1394_transport = new AVC1394Transport(mwindow,
323                                         avc,
324                                         this,
325                                         mwindow->theme->rmonitor_tx_x,
326                                         mwindow->theme->rmonitor_tx_y);
327                                 avc1394_transport->create_objects();
328
329                                 add_subwindow(avc1394transport_timecode =
330                                         new BC_Title(avc1394_transport->x_end,
331                                                 mwindow->theme->rmonitor_tx_y + yS(10),
332                                                 "00:00:00:00",
333                                                 MEDIUM_7SEGMENT,
334                                                 BLACK));
335
336                                 avc1394transport_thread =
337                                         new AVC1394TransportThread(avc1394transport_timecode,
338                                                 avc);
339
340                                 avc1394transport_thread->start();
341
342                         }
343                 }
344 #endif
345
346                 if( !background_done ) {
347                         mwindow->theme->draw_rmonitor_bg(this);
348                         background_done = 1;
349                 }
350
351                 mwindow->theme->rmonitor_canvas_w = MAX(xS(10), mwindow->theme->rmonitor_canvas_w);
352                 mwindow->theme->rmonitor_canvas_h = MAX(yS(10), mwindow->theme->rmonitor_canvas_h);
353                 canvas = new RecordMonitorCanvas(mwindow, this, record,
354                         mwindow->theme->rmonitor_canvas_x,
355                         mwindow->theme->rmonitor_canvas_y,
356                         mwindow->theme->rmonitor_canvas_w,
357                         mwindow->theme->rmonitor_canvas_h);
358                 canvas->create_objects(0);
359
360 #ifdef HAVE_DVB
361                 if( driver == CAPTURE_DVB ) {
362                         int ssw = SignalStatus::calculate_w(this);
363                         signal_status = new SignalStatus(this, get_w()-ssw-xS(3), 0);
364                         add_subwindow(signal_status);
365                         signal_status->create_objects();
366                 }
367 #endif
368
369                 int x = mwindow->theme->widget_border;
370                 int y = mwindow->theme->widget_border;
371                 if( do_channel ) {
372                         channel_picker = new RecordChannelPicker(mwindow,
373                                 record, thread, this, record->channeldb,
374                                 mwindow->theme->rmonitor_channel_x,
375                                 mwindow->theme->rmonitor_channel_y);
376                         channel_picker->create_objects();
377                         x += channel_picker->get_w() + mwindow->theme->widget_border;
378                 }
379                 if( driver == VIDEO4LINUX2JPEG ) {
380                         add_subwindow(reverse_interlace = new ReverseInterlace(record,
381                                 mwindow->theme->rmonitor_interlace_x,
382                                 mwindow->theme->rmonitor_interlace_y));
383                         x += reverse_interlace->get_w() + mwindow->theme->widget_border;
384                 }
385
386                 if( do_scopes ) {
387                         scope_toggle = new ScopeEnable(mwindow, thread, x, y);
388                         add_subwindow(scope_toggle);
389                         x += scope_toggle->get_w() + mwindow->theme->widget_border;
390                 }
391
392                 if( do_cursor ) {
393                         add_subwindow(cursor_toggle = new DoCursor(record,
394                                 x, 
395                                 y));
396                         x += cursor_toggle->get_w() + mwindow->theme->widget_border;
397                         add_subwindow(big_cursor_toggle = new DoBigCursor(record,
398                                 x, 
399                                 y));
400                         x += big_cursor_toggle->get_w() + mwindow->theme->widget_border;
401                 }
402
403                 add_subwindow(monitor_menu = new BC_PopupMenu(0, 0, 0, "", 0));
404                 monitor_menu->add_item(new RecordMonitorFullsize(mwindow, this));
405         }
406
407
408         if( !background_done ) {
409                 mwindow->theme->draw_rmonitor_bg(this);
410                 background_done = 1;
411         }
412
413         if( do_audio ) {
414                 meters = new MeterPanel(mwindow,
415                         this,
416                         mwindow->theme->rmonitor_meter_x,
417                         mwindow->theme->rmonitor_meter_y,
418                         record->default_asset->video_data ? -1 : mwindow->theme->rmonitor_meter_w,
419                         mwindow->theme->rmonitor_meter_h,
420                         channels, do_meters, 1, 0);
421                 meters->create_objects();
422         }
423         unlock_window();
424 }
425
426 int RecordMonitorGUI::button_press_event()
427 {
428         if( canvas && canvas->get_fullscreen() && canvas->get_canvas())
429                 return canvas->button_press_event_base(canvas->get_canvas());
430
431         if( get_buttonpress() == 2 ) {
432                 return 0;
433         }
434         else
435 // Right button
436         if( get_buttonpress() == 3 ) {
437                 monitor_menu->activate_menu();
438                 return 1;
439         }
440         return 0;
441 }
442
443 int RecordMonitorGUI::cursor_leave_event()
444 {
445         if(canvas && canvas->get_canvas())
446                 return canvas->cursor_leave_event_base(canvas->get_canvas());
447         return 0;
448 }
449
450 int RecordMonitorGUI::cursor_enter_event()
451 {
452         if( canvas && canvas->get_canvas() )
453                 return canvas->cursor_enter_event_base(canvas->get_canvas());
454         return 0;
455 }
456
457 int RecordMonitorGUI::button_release_event()
458 {
459         if( canvas && canvas->get_canvas() )
460                 return canvas->button_release_event();
461         return 0;
462 }
463
464 int RecordMonitorGUI::cursor_motion_event()
465 {
466         if( canvas && canvas->get_canvas() ) {
467                 canvas->get_canvas()->unhide_cursor();
468                 return canvas->cursor_motion_event();
469         }
470         return 0;
471 }
472
473 int RecordMonitorGUI::keypress_event()
474 {
475         int result = 0;
476
477         switch(get_keypress()) {
478         case LEFT:
479                 if( !ctrl_down() ) {
480                         record->record_gui->set_translation(--(record->video_x), record->video_y, record->video_zoom);
481                 }
482                 else {
483                         record->video_zoom -= 0.1;
484                         record->record_gui->set_translation(record->video_x, record->video_y, record->video_zoom);
485                 }
486                 result = 1;
487                 break;
488         case RIGHT:
489                 if( !ctrl_down() ) {
490                         record->record_gui->set_translation(++(record->video_x), record->video_y, record->video_zoom);
491                 }
492                 else {
493                         record->video_zoom += 0.1;
494                         record->record_gui->set_translation(record->video_x, record->video_y, record->video_zoom);
495                 }
496                 result = 1;
497                 break;
498         case UP:
499                 if( !ctrl_down() ) {
500                         record->record_gui->set_translation(record->video_x, --(record->video_y), record->video_zoom);
501                 }
502                 else {
503                         record->video_zoom -= 0.1;
504                         record->record_gui->set_translation(record->video_x, record->video_y, record->video_zoom);
505                 }
506                 result = 1;
507                 break;
508         case DOWN:
509                 if( !ctrl_down() ) {
510                         record->record_gui->set_translation(record->video_x, ++(record->video_y), record->video_zoom);
511                 }
512                 else {
513                         record->video_zoom += 0.1;
514                         record->record_gui->set_translation(record->video_x, record->video_y, record->video_zoom);
515                 }
516                 result = 1;
517                 break;
518         case 'w':
519                 close_event();
520                 break;
521
522         default:
523                 if(canvas) result = canvas->keypress_event(this);
524 #ifdef HAVE_FIREWIRE
525                 if(!result && avc1394_transport)
526                         result = avc1394_transport->keypress_event(get_keypress());
527 #endif
528                 break;
529         }
530
531         if( !result )
532                 result = context_help_check_and_show();
533
534         return result;
535 }
536
537
538 int RecordMonitorGUI::translation_event()
539 {
540 //printf("MWindowGUI::translation_event 1 %d %d\n", get_x(), get_y());
541         mwindow->session->rmonitor_x = get_x();
542         mwindow->session->rmonitor_y = get_y();
543         return 0;
544 }
545
546 int RecordMonitorGUI::resize_event(int w, int h)
547 {
548         int driver = mwindow->edl->session->vconfig_in->driver;
549         int do_channel = (driver == CAPTURE_DVB ||
550                         driver == VIDEO4LINUX2 ||
551                         driver == VIDEO4LINUX2JPEG ||
552                         driver == VIDEO4LINUX2MPEG ||
553                         driver == CAPTURE_JPEG_WEBCAM ||
554                         driver == CAPTURE_YUYV_WEBCAM);
555         int do_scopes = do_channel || driver == SCREENCAPTURE;
556         int do_interlace = (driver == VIDEO4LINUX2JPEG);
557         int do_avc = 0;
558 #ifdef HAVE_FIREWIRE
559         do_avc = avc1394_transport ? 1 : 0;
560 #endif
561         int do_meters = meters && record->default_asset->audio_data &&
562                 record->metering_audio;
563         int do_video = record->default_asset->video_data;
564
565         mwindow->session->rmonitor_x = get_x();
566         mwindow->session->rmonitor_y = get_y();
567         mwindow->session->rmonitor_w = w;
568         mwindow->session->rmonitor_h = h;
569
570         mwindow->theme->get_rmonitor_sizes(do_meters, do_video,
571                 do_channel || do_scopes, do_interlace, do_avc,
572                 record->default_asset->channels);
573         mwindow->theme->draw_rmonitor_bg(this);
574
575
576 //      record_transport->reposition_window(mwindow->theme->rmonitor_tx_x,
577 //              mwindow->theme->rmonitor_tx_y);
578 #ifdef HAVE_FIREWIRE
579         if(avc1394_transport)
580         {
581                 avc1394_transport->reposition_window(mwindow->theme->rmonitor_tx_x,
582                         mwindow->theme->rmonitor_tx_y);
583         }
584 #endif
585
586         
587         if( channel_picker ) {
588                 channel_picker->reposition();
589         }
590         if( reverse_interlace ) {
591                 reverse_interlace->reposition_window(
592                         reverse_interlace->get_x(),
593                         reverse_interlace->get_y());
594         }
595         if( cursor_toggle ) {
596                 cursor_toggle->reposition_window(
597                         cursor_toggle->get_x(),
598                         cursor_toggle->get_y());
599                 big_cursor_toggle->reposition_window(
600                         big_cursor_toggle->get_x(),
601                         big_cursor_toggle->get_y());
602         }
603
604         if( canvas && do_video ) {
605                 canvas->reposition_window(0,
606                         mwindow->theme->rmonitor_canvas_x,
607                         mwindow->theme->rmonitor_canvas_y,
608                         mwindow->theme->rmonitor_canvas_w,
609                         mwindow->theme->rmonitor_canvas_h);
610         }
611
612         if( do_meters ) {
613                 meters->reposition_window(mwindow->theme->rmonitor_meter_x,
614                         mwindow->theme->rmonitor_meter_y,
615                         do_video ? -1 : mwindow->theme->rmonitor_meter_w,
616                         mwindow->theme->rmonitor_meter_h);
617                 meters->set_meters(record->default_asset->channels,1);
618         }
619         else if( meters ) {
620                 meters->set_meters(0,0);
621         }
622
623         set_title();
624         BC_WindowBase::resize_event(w, h);
625         flash();
626         return 1;
627 }
628
629 int RecordMonitorGUI::redraw()
630 {
631         lock_window("RecordMonitorGUI::redraw");
632         int w = mwindow->session->rmonitor_w;
633         int h = mwindow->session->rmonitor_h;
634         int result = resize_event(w, h);
635         unlock_window();
636         return result;
637 }
638
639 int RecordMonitorGUI::set_title()
640 {
641 return 0;
642         char string[1024];
643         int scale;
644
645         scale = (int)(thread->get_scale(thread->record->video_window_w) * 100 + 0.5);
646
647         sprintf(string, _(PROGRAM_NAME ": Video in %d%%"), scale);
648         BC_Window::set_title(string);
649         return 0;
650 }
651
652 int RecordMonitorGUI::close_event()
653 {
654         thread->record->set_video_monitoring(0);
655         thread->record->set_audio_monitoring(0);
656         thread->record->video_window_open = 0;
657         unlock_window();
658         lock_window("RecordMonitorGUI::close_event");
659         hide_window();
660         return 0;
661 }
662
663 int RecordMonitorGUI::create_bitmap()
664 {
665         if(bitmap && (bitmap->get_w() != get_w() ||
666                         bitmap->get_h() != thread->get_canvas_height())) {
667                 delete bitmap;
668                 bitmap = 0;
669         }
670
671         if( !bitmap && canvas ) {
672 //              bitmap = canvas->new_bitmap(get_w(), thread->get_canvas_height());
673         }
674         return 0;
675 }
676
677
678 DoCursor::DoCursor(Record *record, int x, int y)
679  : BC_CheckBox(x, y, record->do_cursor, _("Record cursor"))
680 {
681         this->record = record;
682 }
683
684 DoCursor::~DoCursor()
685 {
686 }
687
688 int DoCursor::handle_event()
689 {
690         record->do_cursor = get_value();
691         return 0;
692 }
693
694
695 DoBigCursor::DoBigCursor(Record *record, int x, int y)
696  : BC_CheckBox(x, y, record->do_big_cursor, _("Big cursor"))
697 {
698         this->record = record;
699 }
700
701 DoBigCursor::~DoBigCursor()
702 {
703 }
704
705 int DoBigCursor::handle_event()
706 {
707         record->do_big_cursor = get_value();
708         return 0;
709 }
710
711
712 void RecordMonitorGUI::enable_signal_status(int enable)
713 {
714 #ifdef HAVE_DVB
715         if( !signal_status ) return;
716         signal_status->lock_window("RecordMonitorGUI::enable_signal_status");
717         if( !enable )
718                 signal_status->hide_window();
719         else
720                 signal_status->show_window();
721         signal_status->unlock_window();
722         DeviceDVBInput *dvb_input = record->dvb_device();
723         if( dvb_input )
724                 dvb_input->set_signal_status(!enable ? 0 : signal_status);
725 #endif
726 }
727
728 void RecordMonitorGUI::
729 display_video_text(int x, int y, const char *text, int font,
730         int bg_color, int color, int alpha, double secs, double scale)
731 {
732         lock_window("RecordMonitorGUI::display_text");
733         set_font(font);
734         int ch = get_text_height(font);
735         int h = get_text_height(font,text) + ch/2;
736         int w = get_text_width(font, text) + ch;
737         BC_Pixmap pixmap(this, w, h);
738         set_opaque();
739         set_color(bg_color);
740         draw_box(0, 0, w, h, &pixmap);
741         set_color(color);
742         draw_text(ch/2, ch, text, strlen(text), &pixmap);
743         BC_Bitmap bitmap(this, w, h, BC_RGB888, 0);
744         VFrame in(&bitmap, w, h, BC_RGB888, -1);
745         Drawable drawable = pixmap.get_pixmap();
746         bitmap.read_drawable(drawable, 0, 0, &in);
747         unlock_window();
748         record->display_vframe(&in, x, y, alpha, secs, scale);
749 }
750
751 ReverseInterlace::ReverseInterlace(Record *record, int x, int y)
752  : BC_CheckBox(x, y, record->reverse_interlace, _("Swap fields"))
753 {
754         this->record = record;
755 }
756
757 ReverseInterlace::~ReverseInterlace()
758 {
759 }
760
761 int ReverseInterlace::handle_event()
762 {
763         record->reverse_interlace = get_value();
764         return 0;
765 }
766
767 RecordMonitorCanvas::RecordMonitorCanvas(MWindow *mwindow,
768         RecordMonitorGUI *window, Record *record,
769         int x, int y, int w, int h)
770  : Canvas(mwindow, window, x, y, w, h,
771         record->default_asset->width,
772         record->default_asset->height,
773         0)
774 {
775         this->window = window;
776         this->mwindow = mwindow;
777         this->record = record;
778 //printf("RecordMonitorCanvas::RecordMonitorCanvas 1 %d %d %d %d\n",
779 //x, y, w, h);
780 //printf("RecordMonitorCanvas::RecordMonitorCanvas 2\n");
781 }
782
783 RecordMonitorCanvas::~RecordMonitorCanvas()
784 {
785 }
786
787 void RecordMonitorCanvas::create_objects(EDL *edl)
788 {
789         Canvas::create_objects(edl);
790         canvas_menu->add_item(new CanvasPopupResetTranslation(this));
791 }
792
793 int RecordMonitorCanvas::get_output_w()
794 {
795         return record->default_asset->width;
796 }
797
798 int RecordMonitorCanvas::get_output_h()
799 {
800         return record->default_asset->height;
801 }
802
803
804 int RecordMonitorCanvas::button_press_event()
805 {
806
807         if(Canvas::button_press_event()) return 1;
808         if( mwindow->edl->session->vconfig_in->driver == SCREENCAPTURE ) {
809                 window->current_operation = MONITOR_TRANSLATE;
810                 window->translate_x_origin = record->video_x;
811                 window->translate_y_origin = record->video_y;
812                 window->cursor_x_origin = get_cursor_x();
813                 window->cursor_y_origin = get_cursor_y();
814         }
815
816         return 0;
817 }
818
819 void RecordMonitorCanvas::zoom_resize_window(float percentage)
820 {
821         int canvas_w, canvas_h;
822         calculate_sizes(mwindow->edl->get_aspect_ratio(),
823                 record->default_asset->width, record->default_asset->height,
824                 percentage, canvas_w, canvas_h);
825         int new_w, new_h;
826         new_w = canvas_w + (window->get_w() - mwindow->theme->rmonitor_canvas_w);
827         new_h = canvas_h + (window->get_h() - mwindow->theme->rmonitor_canvas_h);
828         window->resize_window(new_w, new_h);
829         window->resize_event(new_w, new_h);
830 }
831
832
833 int RecordMonitorCanvas::button_release_event()
834 {
835         window->current_operation = MONITOR_NONE;
836         return 0;
837 }
838
839 int RecordMonitorCanvas::cursor_motion_event()
840 {
841 //SET_TRACE
842         if( window->current_operation == MONITOR_TRANSLATE ) {
843 //SET_TRACE
844                 record->set_translation(
845                         get_cursor_x() - window->cursor_x_origin + window->translate_x_origin,
846                         get_cursor_y() - window->cursor_y_origin + window->translate_y_origin);
847 //SET_TRACE
848         }
849
850         return 0;
851 }
852
853 int RecordMonitorCanvas::cursor_enter_event()
854 {
855         if(mwindow->edl->session->vconfig_in->driver == SCREENCAPTURE)
856                 set_cursor(MOVE_CURSOR);
857         return 0;
858 }
859
860 void RecordMonitorCanvas::reset_translation()
861 {
862         record->set_translation(0, 0);
863 }
864
865 int RecordMonitorCanvas::keypress_event()
866 {
867         if( !get_canvas() ) return 0;
868
869         switch(get_canvas()->get_keypress()) {
870         case LEFT:
871                 record->set_translation(--record->video_x, record->video_y);
872                 break;
873         case RIGHT:
874                 record->set_translation(++record->video_x, record->video_y);
875                 break;
876         case UP:
877                 record->set_translation(record->video_x, --record->video_y);
878                 break;
879         case DOWN:
880                 record->set_translation(record->video_x, ++record->video_y);
881                 break;
882         default:
883                 return 0;
884         }
885         return 1;
886 }
887
888 int RecordMonitorCanvas::keypress_event(RecordMonitorGUI *window)
889 {
890         return Canvas::keypress_event(window);
891 }
892
893
894 RecordMonitorFullsize::RecordMonitorFullsize(MWindow *mwindow,
895         RecordMonitorGUI *window)
896  : BC_MenuItem(_("Zoom 100%"))
897 {
898         this->mwindow = mwindow;
899         this->window = window;
900 }
901 int RecordMonitorFullsize::handle_event()
902 {
903         return 1;
904 }
905
906
907
908
909
910
911
912
913 // ================================== slippery playback ============================
914
915
916 RecordMonitorThread::RecordMonitorThread(MWindow *mwindow,
917         Record *record,
918         RecordMonitor *record_monitor)
919  : Thread(1, 0, 0)
920 {
921         this->mwindow = mwindow;
922         this->record_monitor = record_monitor;
923         this->record = record;
924         this->ovly = 0;
925         reset_parameters();
926         output_lock = new Condition(1, "RecordMonitor::output_lock");
927         input_lock = new Condition(1, "RecordMonitor::input_lock");
928 }
929
930 void RecordMonitorThread::reset_parameters()
931 {
932         input_frame = 0;
933         output_frame = 0;
934         shared_data = 0;
935         jpeg_engine = 0;
936         dv_engine = 0;
937         ready = 0;
938         done = 1;
939 }
940
941
942 RecordMonitorThread::~RecordMonitorThread()
943 {
944         stop_playback();
945         if( input_frame && !shared_data )
946                 delete input_frame;
947         delete ovly;
948         delete output_lock;
949         delete input_lock;
950 }
951
952 void RecordMonitorThread::init_output_format()
953 {
954 //printf("RecordMonitorThread::init_output_format 1\n");
955         switch(mwindow->edl->session->vconfig_in->driver) {
956         case SCREENCAPTURE:
957                 output_colormodel = record->vdevice->get_best_colormodel(record->default_asset);
958                 break;
959
960         case VIDEO4LINUX2JPEG:
961                 jpeg_engine = new RecVideoMJPGThread(record, this,
962                         mwindow->edl->session->vconfig_in->v4l2jpeg_in_fields);
963                 jpeg_engine->start_rendering();
964                 output_colormodel = BC_YUV422P;
965                 break;
966
967         case CAPTURE_FIREWIRE:
968         case CAPTURE_IEC61883:
969                 dv_engine = new RecVideoDVThread(record, this);
970                 dv_engine->start_rendering();
971                 output_colormodel = BC_YUV422P;
972                 break;
973
974         case CAPTURE_JPEG_WEBCAM:
975                 jpeg_engine = new RecVideoMJPGThread(record, this, 1);
976                 jpeg_engine->start_rendering();
977                 output_colormodel = BC_YUV420P;
978                 break;
979
980         case CAPTURE_YUYV_WEBCAM:
981                 output_colormodel = BC_YUV422;
982                 break;
983
984
985         case CAPTURE_DVB:
986         case VIDEO4LINUX2:
987         case VIDEO4LINUX2MPEG:
988                 output_colormodel = record->vdevice->get_best_colormodel(record->default_asset);
989 //printf("RecordMonitorThread::init_output_format 2 %d\n", output_colormodel);
990                 break;
991         }
992 }
993
994 int RecordMonitorThread::start_playback()
995 {
996         ready = 1;
997         done = 0;
998         output_frame = 0;
999         output_lock->lock("RecordMonitorThread::start_playback");
1000         Thread::start();
1001         return 0;
1002 }
1003
1004 int RecordMonitorThread::stop_playback()
1005 {
1006         if( done ) return 0;
1007         done = 1;
1008         output_lock->unlock();
1009         Thread::join();
1010 //printf("RecordMonitorThread::stop_playback 1\n");
1011
1012         switch(mwindow->edl->session->vconfig_in->driver) {
1013         case VIDEO4LINUX2JPEG:
1014                 if( jpeg_engine ) {
1015                         jpeg_engine->stop_rendering();
1016                         delete jpeg_engine;
1017                         jpeg_engine = 0;
1018                 }
1019                 break;
1020
1021         case CAPTURE_FIREWIRE:
1022         case CAPTURE_IEC61883:
1023                 if( dv_engine ) {
1024                         dv_engine->stop_rendering();
1025                         delete dv_engine;
1026                         dv_engine = 0;
1027                 }
1028                 break;
1029         case CAPTURE_DVB:
1030         case VIDEO4LINUX2MPEG:
1031                 break;
1032         }
1033 //printf("RecordMonitorThread::stop_playback 4\n");
1034
1035         return 0;
1036 }
1037
1038 int RecordMonitorThread::write_frame(VFrame *new_frame)
1039 {
1040         if( ready ) {
1041                 ready = 0;
1042                 shared_data = (new_frame->get_color_model() != BC_COMPRESSED);
1043
1044
1045 // Need to wait until after Record creates the input device before starting monitor
1046 // because the input device deterimes the output format.
1047 // First time
1048                 if( !output_frame ) init_output_format();
1049                 if( !shared_data ) {
1050                         if(!input_frame) input_frame = new VFrame;
1051                         input_frame->allocate_compressed_data(new_frame->get_compressed_size());
1052                         memcpy(input_frame->get_data(),
1053                                 new_frame->get_data(),
1054                                 new_frame->get_compressed_size());
1055                         input_frame->set_compressed_size(new_frame->get_compressed_size());
1056                         input_frame->set_field2_offset(new_frame->get_field2_offset());
1057                 }
1058                 else {
1059                         input_lock->lock("RecordMonitorThread::write_frame");
1060                         input_frame = new_frame;
1061                 }
1062                 output_lock->unlock();
1063         }
1064         return 0;
1065 }
1066
1067 int RecordMonitorThread::render_jpeg()
1068 {
1069 //printf("RecordMonitorThread::render_jpeg 1\n");
1070         jpeg_engine->render_frame(input_frame, input_frame->get_compressed_size());
1071 //printf("RecordMonitorThread::render_jpeg 2\n");
1072         return 0;
1073 }
1074
1075 int RecordMonitorThread::render_dv()
1076 {
1077         dv_engine->render_frame(input_frame, input_frame->get_compressed_size());
1078         return 0;
1079 }
1080
1081 // VFrame xfer all/odd/even inputs
1082 void RecordMonitorThread::render_uncompressed()
1083 {
1084         VFrame *in = input_frame, *out = output_frame;
1085         out->set_timestamp(in->get_timestamp());
1086         out->copy_params(in);
1087
1088         unsigned char *in_ptrs[4], *out_ptrs[4];
1089         unsigned char **inp, **outp;
1090         if( BC_CModels::is_planar(in->get_color_model()) ) {
1091                 in_ptrs[0] = in->get_y();
1092                 in_ptrs[1] = in->get_u();
1093                 in_ptrs[2] = in->get_v();
1094                 in_ptrs[3] = in->get_a();
1095                 inp = in_ptrs;
1096         }
1097         else
1098                 inp = in->get_rows();
1099         if( BC_CModels::is_planar(out->get_color_model()) ) {
1100                 out_ptrs[0] = out->get_y();
1101                 out_ptrs[1] = out->get_u();
1102                 out_ptrs[2] = out->get_v();
1103                 out_ptrs[3] = out->get_a();
1104                 outp = out_ptrs;
1105         }
1106         else
1107                 outp = out->get_rows();
1108
1109         int out_colormodel = out->get_color_model();
1110         int out_x = 0, out_y = 0;
1111         int out_w = out->get_w(), out_h = out->get_h();
1112         int out_rowspan = out->get_bytes_per_line();
1113         int inp_colormodel = in->get_color_model();
1114         int inp_x = 0, inp_y = 0;
1115         int inp_w = in->get_w(), inp_h = in->get_h();
1116         int inp_rowspan = in->get_bytes_per_line();
1117         int bg_color = 0;
1118
1119         int ret = 1;
1120         if( inp_w > 0 && inp_h > 0 && out_w > 0 && out_h > 0 ) {
1121                 BC_Xfer xfer(outp, out_colormodel, out_x, out_y, out_w, out_h, out_rowspan,
1122                         inp, inp_colormodel, inp_x, inp_y, inp_w, inp_h, inp_rowspan,
1123                         bg_color,0xff);
1124                 int *row_table = xfer.row_table;
1125                 switch( record->deinterlace ) {
1126                 case RECORD_LACE_NONE:
1127                         break;
1128                 case RECORD_LACE_EVEN: {
1129                         int inp_y1 = inp_y;
1130                         for( int i=0; i<out_h; ++i ) {
1131                                 if( (row_table[i] &= ~1) < inp_y1 )
1132                                         row_table[i] = inp_y1;
1133                         }
1134                         break; }
1135                 case RECORD_LACE_ODD: {
1136                         int inp_y2 = inp_y + inp_h-1;
1137                         for( int i=0; i<out_h; ++i ) {
1138                                 if( (row_table[i] |= 1) > inp_y2 )
1139                                         row_table[i] = inp_y2;
1140                         }
1141                         break; }
1142                 }
1143                 ret = xfer.xfer();
1144         }
1145         if( ret )
1146                 printf("RecordMonitorThread::render_uncompressed failed: "
1147                         "%d %d(%dx%d) to %d(%dx%d)\n", __LINE__,
1148                         inp_colormodel, inp_w, inp_h, out_colormodel, out_w, out_h);
1149 }
1150
1151 void RecordMonitorThread::show_output_frame()
1152 {
1153         if( ovly && ovly->overlay(output_frame) )
1154                 undisplay_vframe();
1155         record_monitor->device->write_buffer(output_frame, record->edl);
1156 }
1157
1158
1159 void RecordMonitorThread::unlock_input()
1160 {
1161         if(shared_data) input_lock->unlock();
1162 }
1163
1164 void RecordMonitorThread::lock_input()
1165 {
1166         if(shared_data) input_lock->lock();
1167 }
1168
1169 int RecordMonitorThread::render_frame()
1170 {
1171         switch(mwindow->edl->session->vconfig_in->driver) {
1172         case VIDEO4LINUX2JPEG:
1173         case CAPTURE_JPEG_WEBCAM:
1174                 render_jpeg();
1175                 break;
1176
1177         case CAPTURE_FIREWIRE:
1178         case CAPTURE_IEC61883:
1179                 render_dv();
1180                 break;
1181
1182         default:
1183                 render_uncompressed();
1184                 break;
1185         }
1186
1187         return 0;
1188 }
1189
1190 void RecordMonitorThread::new_output_frame()
1191 {
1192         record_monitor->device->new_output_buffer(&output_frame, 
1193                 output_colormodel,
1194                 record->edl);
1195 }
1196
1197 void RecordMonitorThread::
1198 display_vframe(VFrame *in, int x, int y, int alpha, double secs, double scale)
1199 {
1200         delete ovly;
1201         int ticks = secs * SESSION->vconfig_in->in_framerate;
1202         scale *= SESSION->vconfig_in->h / 1080.;
1203         ovly = new RecVideoOverlay(in, x, y, ticks, scale, alpha/255.);
1204 }
1205
1206 RecVideoOverlay::
1207 RecVideoOverlay(VFrame *vframe, int x, int y, int ticks, float scale, float alpha)
1208 {
1209         this->x = x;
1210         this->y = y;
1211         this->ticks = ticks;
1212         this->scale = scale;
1213         this->alpha = alpha;
1214         this->vframe = new VFrame(*vframe);
1215 }
1216
1217 RecVideoOverlay::
1218 ~RecVideoOverlay()
1219 {
1220         delete vframe;
1221 }
1222
1223 int RecVideoOverlay::
1224 overlay(VFrame *out)
1225 {
1226         VFrame *in = vframe;
1227         int xx = x * scale, yy = y * scale;
1228         int w = in->get_w(), h = in->get_h();
1229         int ww = w * scale, hh = h * scale;
1230         BC_CModels::transfer(out->get_rows(), in->get_rows(),
1231                 out->get_y(), out->get_u(), out->get_v(),
1232                 in->get_y(), in->get_u(), in->get_v(),
1233                 0, 0, w, h, xx, yy, ww, hh,
1234                 in->get_color_model(), out->get_color_model(), 0,
1235                 in->get_bytes_per_line(), out->get_bytes_per_line());
1236         return ticks > 0 && --ticks == 0 ? 1 : 0;
1237 }
1238
1239 void RecordMonitorThread::
1240 undisplay_vframe()
1241 {
1242         delete ovly;  ovly = 0;
1243 }
1244
1245 void RecordMonitorThread::run()
1246 {
1247 //printf("RecordMonitorThread::run 1 %d\n", getpid());
1248         while(!done) {
1249 // Wait for next frame
1250 //SET_TRACE
1251                 output_lock->lock("RecordMonitorThread::run");
1252
1253                 if(done) {
1254                         unlock_input();
1255                         return;
1256                 }
1257 //SET_TRACE
1258                 new_output_frame();
1259 //SET_TRACE
1260                 render_frame();
1261 //SET_TRACE
1262                 record_monitor->scope_thread->process(output_frame);
1263 //SET_TRACE
1264                 show_output_frame();
1265 //SET_TRACE
1266                 unlock_input();
1267 // Get next frame
1268                 ready = 1;
1269         }
1270 }
1271
1272
1273
1274 RecVideoMJPGThread::RecVideoMJPGThread(Record *record,
1275         RecordMonitorThread *thread,
1276         int fields)
1277 {
1278         this->record = record;
1279         this->thread = thread;
1280         mjpeg = 0;
1281         this->fields = fields;
1282 }
1283
1284 RecVideoMJPGThread::~RecVideoMJPGThread()
1285 {
1286 }
1287
1288 int RecVideoMJPGThread::start_rendering()
1289 {
1290         mjpeg = mjpeg_new(record->default_asset->width,
1291                 record->default_asset->height,
1292                 fields);
1293 //printf("RecVideoMJPGThread::start_rendering 1 %p\n", mjpeg);
1294         return 0;
1295 }
1296
1297 int RecVideoMJPGThread::stop_rendering()
1298 {
1299 //printf("RecVideoMJPGThread::stop_rendering 1 %p\n", mjpeg);
1300         if(mjpeg) mjpeg_delete(mjpeg);
1301 //printf("RecVideoMJPGThread::stop_rendering 2\n");
1302         return 0;
1303 }
1304
1305 int RecVideoMJPGThread::render_frame(VFrame *frame, long size)
1306 {
1307 // printf("RecVideoMJPGThread::render_frame %d %02x%02x %02x%02x\n",
1308 // frame->get_field2_offset(),
1309 // frame->get_data()[0],
1310 // frame->get_data()[1],
1311 // frame->get_data()[frame->get_field2_offset()],
1312 // frame->get_data()[frame->get_field2_offset() + 1]);
1313 //frame->set_field2_offset(0);
1314         mjpeg_decompress(mjpeg,
1315                 frame->get_data(),
1316                 frame->get_compressed_size(),
1317                 frame->get_field2_offset(),
1318                 thread->output_frame->get_rows(),
1319                 thread->output_frame->get_y(),
1320                 thread->output_frame->get_u(),
1321                 thread->output_frame->get_v(),
1322                 thread->output_frame->get_color_model(),
1323                 record->mwindow->preferences->processors);
1324         return 0;
1325 }
1326
1327
1328
1329
1330 RecVideoDVThread::RecVideoDVThread(Record *record, RecordMonitorThread *thread)
1331 {
1332         this->record = record;
1333         this->thread = thread;
1334         dv = 0;
1335 }
1336
1337 RecVideoDVThread::~RecVideoDVThread()
1338 {
1339 }
1340
1341
1342 int RecVideoDVThread::start_rendering()
1343 {
1344 #ifdef HAVE_DV
1345         dv = dv_new();
1346 #endif
1347         return 0;
1348 }
1349
1350 int RecVideoDVThread::stop_rendering()
1351 {
1352 #ifdef HAVE_DV
1353         if( dv ) { dv_delete(((dv_t*)dv));  dv = 0; }
1354 #endif
1355         return 0;
1356 }
1357
1358 int RecVideoDVThread::render_frame(VFrame *frame, long size)
1359 {
1360 #ifdef HAVE_DV
1361         unsigned char *yuv_planes[3];
1362         yuv_planes[0] = thread->output_frame->get_y();
1363         yuv_planes[1] = thread->output_frame->get_u();
1364         yuv_planes[2] = thread->output_frame->get_v();
1365         dv_read_video(((dv_t*)dv),
1366                 yuv_planes,
1367                 frame->get_data(),
1368                 frame->get_compressed_size(),
1369                 thread->output_frame->get_color_model());
1370 #endif
1371         return 0;
1372 }
1373