Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / cinelerra / recordvideo.C
diff --git a/cinelerra-5.1/cinelerra/recordvideo.C b/cinelerra-5.1/cinelerra/recordvideo.C
new file mode 100644 (file)
index 0000000..2abd343
--- /dev/null
@@ -0,0 +1,311 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "asset.h"
+#include "batch.h"
+#include "bcsignals.h"
+#include "clip.h"
+#include "condition.h"
+#include "edl.h"
+#include "edlsession.h"
+#include "errorbox.h"
+#include "file.h"
+#include "filethread.h"
+#include "language.h"
+#include "libmjpeg.h"
+#include "mutex.h"
+#include "mwindow.h"
+#include "mwindowgui.h"
+#include "preferences.h"
+#include "record.h"
+#include "recordaudio.h"
+#include "recordgui.h"
+#include "recordvideo.h"
+#include "recordmonitor.h"
+#include "units.h"
+#include "vframe.h"
+#include "videodevice.h"
+
+#include <unistd.h>
+
+
+RecordVideo::RecordVideo(MWindow *mwindow, Record *record)
+ : Thread(1, 0, 0)
+{
+       this->mwindow = mwindow;
+       this->record = record;
+       this->gui = record->record_gui;
+       trigger_lock = new Condition(0, "RecordVideo::trigger_lock");
+        pause_record_lock = new Condition(0, "RecordVideo::pause_record_lock");
+        record_paused_lock = new Condition(0, "RecordVideo::record_paused_lock");
+       frame_ptr = 0;
+       buffer_frame = 0;
+       reset_parameters();
+       done = -1;
+}
+
+RecordVideo::~RecordVideo()
+{
+       stop_recording();
+       delete_buffer();
+       delete trigger_lock;
+        delete pause_record_lock;
+        delete record_paused_lock;
+}
+
+void RecordVideo::reset_parameters()
+{
+       write_result = 0;
+       writing_file = 0;
+       grab_result = 0;
+       buffer_frames = mwindow->edl->session->video_write_length;
+       buffer_position = 0;
+       recording_paused = 0;
+       record_start = 0;
+       trigger_lock->reset();
+        pause_record_lock->reset();
+        record_paused_lock->reset();
+}
+
+void RecordVideo::arm_recording()
+{
+       reset_parameters();
+       done = 0;
+       Thread::start();
+}
+
+void RecordVideo::start_recording()
+{
+       trigger_lock->unlock();
+}
+
+void RecordVideo::stop_recording()
+{
+       if( done ) return;
+// Device won't exist if interrupting a cron job
+       done = 1;
+       if( record->vdevice ) {
+// Interrupt IEEE1394 crashes
+               record->vdevice->interrupt_crash();
+// Interrupt video4linux crashes
+               if( record->vdevice->get_failed() )
+                       Thread::cancel();
+       }
+       Thread::join();
+// Joined in RecordThread
+}
+
+
+VFrame *RecordVideo::get_buffer()
+{
+       VFrame *result = 0;
+       record->file_lock->lock();
+       writing_file = record->writing_file > 0 && record->do_video ? 1 : 0;
+       if( writing_file ) {
+               if( !frame_ptr ) {
+                       frame_ptr = record->file->get_video_buffer();
+                       buffer_position = 0;
+               }
+               result = frame_ptr[0][buffer_position];
+
+       }
+       record->file_lock->unlock();
+       if( !result ) {
+               if( !buffer_frame ) {
+                       if( !record->fixed_compression ) {
+                               Asset *asset = record->default_asset;
+                               int w = asset->width, h = asset->height;
+                               int cmodel = record->vdevice->get_best_colormodel(asset);
+                               buffer_frame = new VFrame(w, h, cmodel);
+                       }
+                       else
+                               buffer_frame = new VFrame;
+               }
+               buffer_position = 0;
+               result = buffer_frame;
+       }
+       return result;
+}
+
+void RecordVideo::delete_buffer()
+{
+       RecordMonitorThread *thr = !record->record_monitor ?
+                0 : record->record_monitor->thread;
+       if( thr && thr->running() ) thr->lock_input();
+       frame_ptr = 0;
+       if( buffer_frame ) {
+               delete buffer_frame;
+               buffer_frame = 0;
+       }
+       if( thr && thr->running() ) thr->unlock_input();
+}
+
+void RecordVideo::config_update()
+{
+       VideoDevice *vdevice = record->vdevice;
+       vdevice->config_update();
+       int width = vdevice->get_iwidth();
+       int height = vdevice->get_iheight();
+       double frame_rate = vdevice->get_irate();
+       float awidth, aheight;
+       MWindow::create_aspect_ratio(awidth, aheight, width, height);
+       EDLSession *session = record->edl->session;
+       SESSION->aspect_w = session->aspect_w = awidth;
+       SESSION->aspect_h = session->aspect_h = aheight;
+       SESSION->output_w = session->output_w = width;
+       SESSION->output_h = session->output_h = height;
+       Asset *rf_asset = SESSION->recording_format;
+       Asset *df_asset = record->default_asset;
+       rf_asset->width  = df_asset->width  = width;
+       rf_asset->height = df_asset->height = height;
+       rf_asset->frame_rate = df_asset->frame_rate = frame_rate;
+}
+
+void RecordVideo::run()
+{
+// Number of frames for user to know about.
+       gui->reset_video();
+
+// Wait for trigger
+       trigger_lock->lock("RecordVideo::run");
+
+       while( !done && !write_result ) {
+               if( recording_paused ) {
+                       pause_record_lock->unlock();
+                       record_paused_lock->lock();
+               }
+               if( done ) break;
+               VideoDevice *vdevice = record->vdevice;
+               VFrame *capture_frame = get_buffer();
+               vdevice->set_field_order(record->reverse_interlace);
+// Capture a frame
+               grab_result = read_buffer(capture_frame);
+               if( done ) break;
+               if( vdevice->config_updated() ) {
+                       flush_buffer();
+                       delete_buffer();
+                       config_update();
+                       gui->reset_video();
+                       record->record_monitor->reconfig();
+                       continue;
+               }
+               if( grab_result ) {
+                       Timer::delay(250);
+                       continue;
+               }
+               decompress_buffer(capture_frame);
+               record->resync();
+               write_buffer();
+               if( record->monitor_video && capture_frame->get_data() )
+                       if( !writing_file || !record->is_behind() )
+                               record->record_monitor->update(capture_frame);
+               if( writing_file && record->fill_underrun_frames ) {
+                       VFrame *last_frame = capture_frame;
+                       int fill = record->dropped;
+                       while( --fill >= 0 ) {
+                               capture_frame = get_buffer();
+                               capture_frame->copy_from(last_frame);
+                               last_frame = capture_frame;
+                               write_buffer();
+                       }
+               }
+               else
+                       record->written_frames += record->dropped;
+               if( record->single_frame ) {
+                       record->single_frame = 0;
+                       record->stop_writing_file();
+               }
+               if( done ) break;
+               if( !done ) done = write_result;
+               if( done ) break;
+               record->check_batch_complete();
+       }
+
+SET_TRACE
+       flush_buffer();
+       delete_buffer();
+SET_TRACE
+//TRACE("RecordVideo::run 2");
+       if( write_result ) {
+               ErrorBox error_box(_(PROGRAM_NAME ": Error"),
+                       mwindow->gui->get_abs_cursor_x(1),
+                       mwindow->gui->get_abs_cursor_y(1));
+                       error_box.create_objects(_("No space left on disk."));
+               error_box.run_window();
+       }
+SET_TRACE
+}
+
+int RecordVideo::read_buffer(VFrame *frame)
+{
+       return record->vdevice->read_buffer(frame);
+}
+
+void RecordVideo::decompress_buffer(VFrame *frame)
+{
+       if( !strcmp(record->default_asset->vcodec, CODEC_TAG_MJPEG) &&
+               record->vdevice->is_compressed(0, 1)) {
+               unsigned char *data = frame->get_data();
+               int64_t size = frame->get_compressed_size();
+               //int64_t allocation = frame->get_compressed_allocated();
+               if( data ) {
+                       int64_t field2_offset = mjpeg_get_field2(data, size);
+                       frame->set_compressed_size(size);
+                       frame->set_field2_offset(field2_offset);
+               }
+       }
+}
+
+int RecordVideo::flush_buffer()
+{
+       record->file_lock->lock();
+       if( writing_file && frame_ptr ) {
+               int result = record->file->write_video_buffer(buffer_position);
+               if( result ) write_result = 1;
+               frame_ptr = 0;
+       }
+       record->file_lock->unlock();
+       buffer_position = 0;
+       return write_result;
+}
+
+int RecordVideo::write_buffer()
+{
+       ++record->written_frames;
+       if( ++buffer_position >= buffer_frames )
+               flush_buffer();
+// HACK
+write_result = 0;
+       return write_result;
+}
+
+void RecordVideo::pause_recording()
+{
+       recording_paused = 1;
+       pause_record_lock->lock();
+}
+
+void RecordVideo::resume_recording()
+{
+       recording_paused = 0;
+       record_paused_lock->unlock();
+}
+