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