/* * CINELERRA * Copyright (C) 2008 Adam Williams * * 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 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); record->set_do_cursor(); // 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(); }