add bluray dv, misc fixes
[goodguy/history.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 // Capture a frame
200                 grab_result = read_buffer(capture_frame);
201                 if( done ) break;
202                 if( vdevice->config_updated() ) {
203                         flush_buffer();
204                         delete_buffer();
205                         config_update();
206                         gui->reset_video();
207                         record->record_monitor->reconfig();
208                         continue;
209                 }
210                 if( grab_result ) {
211                         Timer::delay(250);
212                         continue;
213                 }
214                 decompress_buffer(capture_frame);
215                 record->resync();
216                 write_buffer();
217                 if( record->monitor_video && capture_frame->get_data() )
218                         if( !writing_file || !record->is_behind() )
219                                 record->record_monitor->update(capture_frame);
220                 if( writing_file && record->fill_underrun_frames ) {
221                         VFrame *last_frame = capture_frame;
222                         int fill = record->dropped;
223                         while( --fill >= 0 ) {
224                                 capture_frame = get_buffer();
225                                 capture_frame->copy_from(last_frame);
226                                 last_frame = capture_frame;
227                                 write_buffer();
228                         }
229                 }
230                 else
231                         record->written_frames += record->dropped;
232                 if( record->single_frame ) {
233                         record->single_frame = 0;
234                         record->stop_writing_file();
235                 }
236                 if( done ) break;
237                 if( !done ) done = write_result;
238                 if( done ) break;
239                 record->check_batch_complete();
240         }
241
242 SET_TRACE
243         flush_buffer();
244         delete_buffer();
245 SET_TRACE
246 //TRACE("RecordVideo::run 2");
247         if( write_result ) {
248                 ErrorBox error_box(_(PROGRAM_NAME ": Error"),
249                         mwindow->gui->get_abs_cursor_x(1),
250                         mwindow->gui->get_abs_cursor_y(1));
251                         error_box.create_objects(_("No space left on disk."));
252                 error_box.run_window();
253         }
254 SET_TRACE
255 }
256
257 int RecordVideo::read_buffer(VFrame *frame)
258 {
259         return record->vdevice->read_buffer(frame);
260 }
261
262 void RecordVideo::decompress_buffer(VFrame *frame)
263 {
264         if( !strcmp(record->default_asset->vcodec, CODEC_TAG_MJPEG) &&
265                 record->vdevice->is_compressed(0, 1)) {
266                 unsigned char *data = frame->get_data();
267                 int64_t size = frame->get_compressed_size();
268                 //int64_t allocation = frame->get_compressed_allocated();
269                 if( data ) {
270                         int64_t field2_offset = mjpeg_get_field2(data, size);
271                         frame->set_compressed_size(size);
272                         frame->set_field2_offset(field2_offset);
273                 }
274         }
275 }
276
277 int RecordVideo::flush_buffer()
278 {
279         record->file_lock->lock();
280         if( writing_file && frame_ptr ) {
281                 int result = record->file->write_video_buffer(buffer_position);
282                 if( result ) write_result = 1;
283                 frame_ptr = 0;
284         }
285         record->file_lock->unlock();
286         buffer_position = 0;
287         return write_result;
288 }
289
290 int RecordVideo::write_buffer()
291 {
292         ++record->written_frames;
293         if( ++buffer_position >= buffer_frames )
294                 flush_buffer();
295 // HACK
296 write_result = 0;
297         return write_result;
298 }
299
300 void RecordVideo::pause_recording()
301 {
302         recording_paused = 1;
303         pause_record_lock->lock();
304 }
305
306 void RecordVideo::resume_recording()
307 {
308         recording_paused = 0;
309         record_paused_lock->unlock();
310 }
311