identity sh-f11=cam/sh-f12=proj keyframes, odd jpeg fix, zoom submenu, shudmp
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / recordvideo.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 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 "batch.h"
24 #include "bcsignals.h"
25 #include "clip.h"
26 #include "condition.h"
27 #include "edl.h"
28 #include "edlsession.h"
29 #include "errorbox.h"
30 #include "file.h"
31 #include "filethread.h"
32 #include "language.h"
33 #include "libmjpeg.h"
34 #include "mutex.h"
35 #include "mwindow.h"
36 #include "mwindowgui.h"
37 #include "preferences.h"
38 #include "record.h"
39 #include "recordaudio.h"
40 #include "recordgui.h"
41 #include "recordvideo.h"
42 #include "recordmonitor.h"
43 #include "units.h"
44 #include "vframe.h"
45 #include "videodevice.h"
46
47 #include <unistd.h>
48
49
50 RecordVideo::RecordVideo(MWindow *mwindow, Record *record)
51  : Thread(1, 0, 0)
52 {
53         this->mwindow = mwindow;
54         this->record = record;
55         this->gui = record->record_gui;
56         trigger_lock = new Condition(0, "RecordVideo::trigger_lock");
57         pause_record_lock = new Condition(0, "RecordVideo::pause_record_lock");
58         record_paused_lock = new Condition(0, "RecordVideo::record_paused_lock");
59         frame_ptr = 0;
60         buffer_frame = 0;
61         reset_parameters();
62         done = -1;
63 }
64
65 RecordVideo::~RecordVideo()
66 {
67         stop_recording();
68         delete_buffer();
69         delete trigger_lock;
70         delete pause_record_lock;
71         delete record_paused_lock;
72 }
73
74 void RecordVideo::reset_parameters()
75 {
76         write_result = 0;
77         writing_file = 0;
78         grab_result = 0;
79         buffer_frames = mwindow->edl->session->video_write_length;
80         buffer_position = 0;
81         recording_paused = 0;
82         record_start = 0;
83         trigger_lock->reset();
84         pause_record_lock->reset();
85         record_paused_lock->reset();
86 }
87
88 void RecordVideo::arm_recording()
89 {
90         reset_parameters();
91         done = 0;
92         Thread::start();
93 }
94
95 void RecordVideo::start_recording()
96 {
97         trigger_lock->unlock();
98 }
99
100 void RecordVideo::stop_recording()
101 {
102         if( done ) return;
103 // Device won't exist if interrupting a cron job
104         done = 1;
105         if( record->vdevice ) {
106 // Interrupt IEEE1394 crashes
107                 record->vdevice->interrupt_crash();
108 // Interrupt video4linux crashes
109                 if( record->vdevice->get_failed() )
110                         Thread::cancel();
111         }
112         Thread::join();
113 // Joined in RecordThread
114 }
115
116
117 VFrame *RecordVideo::get_buffer()
118 {
119         VFrame *result = 0;
120         record->file_lock->lock();
121         writing_file = record->writing_file > 0 && record->do_video ? 1 : 0;
122         if( writing_file ) {
123                 if( !frame_ptr ) {
124                         frame_ptr = record->file->get_video_buffer();
125                         buffer_position = 0;
126                 }
127                 result = frame_ptr[0][buffer_position];
128
129         }
130         record->file_lock->unlock();
131         if( !result ) {
132                 if( !buffer_frame ) {
133                         if( !record->fixed_compression ) {
134                                 Asset *asset = record->default_asset;
135                                 int w = asset->width, h = asset->height;
136                                 int cmodel = record->vdevice->get_best_colormodel(asset);
137                                 buffer_frame = new VFrame(w, h, cmodel);
138                         }
139                         else
140                                 buffer_frame = new VFrame;
141                 }
142                 buffer_position = 0;
143                 result = buffer_frame;
144         }
145         return result;
146 }
147
148 void RecordVideo::delete_buffer()
149 {
150         RecordMonitorThread *thr = !record->record_monitor ?
151                  0 : record->record_monitor->thread;
152         if( thr && thr->running() ) thr->lock_input();
153         frame_ptr = 0;
154         if( buffer_frame ) {
155                 delete buffer_frame;
156                 buffer_frame = 0;
157         }
158         if( thr && thr->running() ) thr->unlock_input();
159 }
160
161 void RecordVideo::config_update()
162 {
163         VideoDevice *vdevice = record->vdevice;
164         vdevice->config_update();
165         int width = vdevice->get_iwidth();
166         int height = vdevice->get_iheight();
167         double frame_rate = vdevice->get_irate();
168         float awidth, aheight;
169         MWindow::create_aspect_ratio(awidth, aheight, width, height);
170         EDLSession *session = record->edl->session;
171         SESSION->aspect_w = session->aspect_w = awidth;
172         SESSION->aspect_h = session->aspect_h = aheight;
173         SESSION->output_w = session->output_w = width;
174         SESSION->output_h = session->output_h = height;
175         Asset *rf_asset = SESSION->recording_format;
176         Asset *df_asset = record->default_asset;
177         rf_asset->width  = df_asset->width  = width;
178         rf_asset->height = df_asset->height = height;
179         rf_asset->frame_rate = df_asset->frame_rate = frame_rate;
180 }
181
182 void RecordVideo::run()
183 {
184 // Number of frames for user to know about.
185         gui->reset_video();
186
187 // Wait for trigger
188         trigger_lock->lock("RecordVideo::run");
189
190         while( !done && !write_result ) {
191                 if( recording_paused ) {
192                         pause_record_lock->unlock();
193                         record_paused_lock->lock();
194                 }
195                 if( done ) break;
196                 VideoDevice *vdevice = record->vdevice;
197                 VFrame *capture_frame = get_buffer();
198                 vdevice->set_field_order(record->reverse_interlace);
199                 record->set_do_cursor();
200 // Capture a frame
201                 grab_result = read_buffer(capture_frame);
202                 if( done ) break;
203                 if( vdevice->config_updated() ) {
204                         flush_buffer();
205                         delete_buffer();
206                         config_update();
207                         gui->reset_video();
208                         record->record_monitor->reconfig();
209                         continue;
210                 }
211                 if( grab_result ) {
212                         Timer::delay(250);
213                         continue;
214                 }
215                 decompress_buffer(capture_frame);
216                 record->resync();
217                 write_buffer();
218                 if( record->monitor_video && capture_frame->get_data() )
219                         if( !writing_file || !record->is_behind() )
220                                 record->record_monitor->update(capture_frame);
221                 if( writing_file && record->fill_underrun_frames ) {
222                         VFrame *last_frame = capture_frame;
223                         int fill = record->dropped;
224                         while( --fill >= 0 ) {
225                                 capture_frame = get_buffer();
226                                 capture_frame->copy_from(last_frame);
227                                 last_frame = capture_frame;
228                                 write_buffer();
229                         }
230                 }
231                 else
232                         record->written_frames += record->dropped;
233                 if( record->single_frame ) {
234                         record->single_frame = 0;
235                         record->stop_writing_file();
236                 }
237                 if( done ) break;
238                 if( !done ) done = write_result;
239                 if( done ) break;
240                 record->check_batch_complete();
241         }
242
243 SET_TRACE
244         flush_buffer();
245         delete_buffer();
246 SET_TRACE
247 //TRACE("RecordVideo::run 2");
248         if( write_result ) {
249                 ErrorBox error_box(_(PROGRAM_NAME ": Error"),
250                         mwindow->gui->get_abs_cursor_x(1),
251                         mwindow->gui->get_abs_cursor_y(1));
252                         error_box.create_objects(_("No space left on disk."));
253                 error_box.run_window();
254         }
255 SET_TRACE
256 }
257
258 int RecordVideo::read_buffer(VFrame *frame)
259 {
260         return record->vdevice->read_buffer(frame);
261 }
262
263 void RecordVideo::decompress_buffer(VFrame *frame)
264 {
265         if( !strcmp(record->default_asset->vcodec, CODEC_TAG_MJPEG) &&
266                 record->vdevice->is_compressed(0, 1)) {
267                 unsigned char *data = frame->get_data();
268                 int64_t size = frame->get_compressed_size();
269                 //int64_t allocation = frame->get_compressed_allocated();
270                 if( data ) {
271                         int64_t field2_offset = mjpeg_get_field2(data, size);
272                         frame->set_compressed_size(size);
273                         frame->set_field2_offset(field2_offset);
274                 }
275         }
276 }
277
278 int RecordVideo::flush_buffer()
279 {
280         record->file_lock->lock();
281         if( writing_file && frame_ptr ) {
282                 int result = record->file->write_video_buffer(buffer_position);
283                 if( result ) write_result = 1;
284                 frame_ptr = 0;
285         }
286         record->file_lock->unlock();
287         buffer_position = 0;
288         return write_result;
289 }
290
291 int RecordVideo::write_buffer()
292 {
293         ++record->written_frames;
294         if( ++buffer_position >= buffer_frames )
295                 flush_buffer();
296 // HACK
297 write_result = 0;
298         return write_result;
299 }
300
301 void RecordVideo::pause_recording()
302 {
303         recording_paused = 1;
304         pause_record_lock->lock();
305 }
306
307 void RecordVideo::resume_recording()
308 {
309         recording_paused = 0;
310         record_paused_lock->unlock();
311 }
312