rework batchrender, renderfarm improvements, ffmpeg audio quality, a few fixes
[goodguy/history.git] / cinelerra-5.1 / cinelerra / dvdcreate.C
1 #include "asset.h"
2 #include "bchash.h"
3 #include "clip.h"
4 #include "dvdcreate.h"
5 #include "edl.h"
6 #include "edit.h"
7 #include "edits.h"
8 #include "edlsession.h"
9 #include "file.h"
10 #include "filexml.h"
11 #include "keyframe.h"
12 #include "labels.h"
13 #include "mainerror.h"
14 #include "mainundo.h"
15 #include "mwindow.h"
16 #include "mwindowgui.h"
17 #include "plugin.h"
18 #include "pluginset.h"
19 #include "preferences.h"
20 #include "rescale.h"
21 #include "track.h"
22 #include "tracks.h"
23
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <sys/stat.h>
28 #include <sys/statfs.h>
29
30
31 #define DVD_PAL_4x3     0
32 #define DVD_PAL_16x9    1
33 #define DVD_NTSC_4x3    2
34 #define DVD_NTSC_16x9   3
35
36 #define DVD_NORM_PAL    0
37 #define DVD_NORM_NTSC   1
38
39 #define DVD_ASPECT_4x3  0
40 #define DVD_ASPECT_16x9 1
41
42 static struct dvd_norm {
43         const char *name;
44         int w, h;
45         double framerate;
46 } dvd_norms[] = {
47         { "PAL",  720,576, 25 },
48         { "NTSC", 720,480, 29.97 },
49 };
50
51 static struct dvd_aspect {
52         int w, h;
53 } dvd_aspects[] = {
54         { 4, 3, },
55         { 16, 9, },
56 };
57
58 // DVD Creation
59
60 static struct dvd_format {
61         int norm, aspect;
62 } dvd_formats[] = {
63         { DVD_NORM_PAL,  DVD_ASPECT_4x3, },
64         { DVD_NORM_PAL,  DVD_ASPECT_16x9, },
65         { DVD_NORM_NTSC, DVD_ASPECT_4x3, },
66         { DVD_NORM_NTSC, DVD_ASPECT_16x9, },
67 };
68
69 const int64_t CreateDVD_Thread::DVD_SIZE = 4700000000;
70 const int CreateDVD_Thread::DVD_STREAMS = 1;
71 const int CreateDVD_Thread::DVD_WIDTH = 720;
72 const int CreateDVD_Thread::DVD_HEIGHT = 480;
73 const double CreateDVD_Thread::DVD_ASPECT_WIDTH = 4.;
74 const double CreateDVD_Thread::DVD_ASPECT_HEIGHT = 3.;
75 const double CreateDVD_Thread::DVD_WIDE_ASPECT_WIDTH = 16.;
76 const double CreateDVD_Thread::DVD_WIDE_ASPECT_HEIGHT = 9.;
77 const double CreateDVD_Thread::DVD_FRAMERATE = 30000. / 1001.;
78 const int CreateDVD_Thread::DVD_MAX_BITRATE = 8000000;
79 const int CreateDVD_Thread::DVD_CHANNELS = 2;
80 const int CreateDVD_Thread::DVD_WIDE_CHANNELS = 6;
81 const double CreateDVD_Thread::DVD_SAMPLERATE = 48000;
82 const double CreateDVD_Thread::DVD_KAUDIO_RATE = 224;
83
84
85 CreateDVD_MenuItem::CreateDVD_MenuItem(MWindow *mwindow)
86  : BC_MenuItem(_("DVD Render..."), _("Shift-D"), 'D')
87 {
88         set_shift(1);
89         this->mwindow = mwindow;
90 }
91
92 int CreateDVD_MenuItem::handle_event()
93 {
94         mwindow->create_dvd->start();
95         return 1;
96 }
97
98
99 CreateDVD_Thread::CreateDVD_Thread(MWindow *mwindow)
100  : BC_DialogThread()
101 {
102         this->mwindow = mwindow;
103         this->gui = 0;
104         this->use_deinterlace = 0;
105         this->use_scale = 0;
106         this->use_histogram = 0;
107         this->use_inverse_telecine = 0;
108         this->use_wide_audio = 0;
109         this->use_ffmpeg = 0;
110         this->use_resize_tracks = 0;
111         this->use_label_chapters = 0;
112
113         this->dvd_size = DVD_SIZE;
114         this->dvd_width = DVD_WIDTH;
115         this->dvd_height = DVD_HEIGHT;
116         this->dvd_aspect_width = DVD_ASPECT_WIDTH;
117         this->dvd_aspect_height = DVD_ASPECT_HEIGHT;
118         this->dvd_framerate = DVD_FRAMERATE;
119         this->dvd_samplerate = DVD_SAMPLERATE;
120         this->dvd_max_bitrate = DVD_MAX_BITRATE;
121         this->dvd_kaudio_rate = DVD_KAUDIO_RATE;
122         this->max_w = this->max_h = 0;
123 }
124
125 CreateDVD_Thread::~CreateDVD_Thread()
126 {
127         close_window();
128 }
129
130 int CreateDVD_Thread::create_dvd_jobs(ArrayList<BatchRenderJob*> *jobs, const char *asset_dir)
131 {
132         EDL *edl = mwindow->edl;
133         if( !edl || !edl->session ) {
134                 char msg[BCTEXTLEN];
135                 sprintf(msg, _("No EDL/Session"));
136                 MainError::show_error(msg);
137                 return 1;
138         }
139         EDLSession *session = edl->session;
140
141         double total_length = edl->tracks->total_length();
142         if( total_length <= 0 ) {
143                 char msg[BCTEXTLEN];
144                 sprintf(msg, _("No content: %s"), asset_title);
145                 MainError::show_error(msg);
146                 return 1;
147         }
148
149         if( mkdir(asset_dir, 0777) ) {
150                 char err[BCTEXTLEN], msg[BCTEXTLEN];
151                 strerror_r(errno, err, sizeof(err));
152                 sprintf(msg, _("Unable to create directory: %s\n-- %s"), asset_dir, err);
153                 MainError::show_error(msg);
154                 return 1;
155         }
156
157         double old_samplerate = session->sample_rate;
158         double old_framerate = session->frame_rate;
159
160         session->video_channels = DVD_STREAMS;
161         session->video_tracks = DVD_STREAMS;
162         session->frame_rate = dvd_framerate;
163         session->output_w = dvd_width;
164         session->output_h = dvd_height;
165         session->aspect_w = dvd_aspect_width;
166         session->aspect_h = dvd_aspect_height;
167         session->sample_rate = dvd_samplerate;
168         session->audio_channels = session->audio_tracks =
169                 use_wide_audio ? DVD_WIDE_CHANNELS : DVD_CHANNELS;
170
171         char script_filename[BCTEXTLEN];
172         sprintf(script_filename, "%s/dvd.sh", asset_dir);
173         int fd = open(script_filename, O_WRONLY+O_CREAT+O_TRUNC, 0755);
174         FILE *fp = fdopen(fd, "w");
175         if( !fp ) {
176                 char err[BCTEXTLEN], msg[BCTEXTLEN];
177                 strerror_r(errno, err, sizeof(err));
178                 sprintf(msg, _("Unable to save: %s\n-- %s"), script_filename, err);
179                 MainError::show_error(msg);
180                 return 1;
181         }
182         fprintf(fp,"#!/bin/bash\n");
183         fprintf(fp,"echo \"running %s\" $# $*\n", script_filename);
184         fprintf(fp,"\n");
185         const char *exec_path = File::get_cinlib_path();
186         fprintf(fp,"PATH=$PATH:%s\n",exec_path);
187         if( mwindow->preferences->use_renderfarm ||
188             (use_label_chapters && edl->labels ) ) {
189                 if( !use_ffmpeg ) {
190                         fprintf(fp, "cat > $1/dvd.m2v $1/dvd.m2v0*\n");
191                         fprintf(fp, "mplex -M -f 8 -o $1/dvd.mpg $1/dvd.m2v $1/dvd.ac3\n");
192                 }
193                 else
194                         fprintf(fp, "ffmpeg -f concat -safe 0 -i <(for f in \"$1/dvd.mpg0\"*; do "
195                                         "echo \"file '$f'\"; done) -c copy -y $1/dvd.mpg\n");
196         }
197         fprintf(fp,"rm -rf $1/iso\n");
198         fprintf(fp,"mkdir -p $1/iso\n");
199         fprintf(fp,"\n");
200 // dvdauthor ver 0.7.0 requires this to work
201         int norm = dvd_formats[use_standard].norm;
202         const char *name = dvd_norms[norm].name;
203         fprintf(fp,"export VIDEO_FORMAT=%s\n", name);
204         fprintf(fp,"dvdauthor -x - <<eof\n");
205         fprintf(fp,"<dvdauthor dest=\"$1/iso\">\n");
206         fprintf(fp,"  <vmgm>\n");
207         fprintf(fp,"    <fpc> jump title 1; </fpc>\n");
208         fprintf(fp,"  </vmgm>\n");
209         fprintf(fp,"  <titleset>\n");
210         fprintf(fp,"    <titles>\n");
211         char std[BCSTRLEN], *cp = std;
212         for( const char *np=name; *np!=0; ++cp,++np) *cp = *np + 'a'-'A';
213         *cp = 0;
214         fprintf(fp,"    <video format=\"%s\" aspect=\"%d:%d\" resolution=\"%dx%d\"/>\n",
215                 std, (int)session->aspect_w, (int)session->aspect_h,
216                 session->output_w, session->output_h);
217         fprintf(fp,"    <audio format=\"ac3\" lang=\"en\"/>\n");
218         fprintf(fp,"    <pgc>\n");
219         fprintf(fp,"      <vob file=\"$1/dvd.mpg\" chapters=\"");
220         if( use_label_chapters && edl->labels ) {
221                 Label *label = edl->labels->first;
222                 while( label ) {
223                         int secs = label->position;
224                         int mins = secs / 60;
225                         int frms = (label->position-secs) * session->frame_rate;
226                         fprintf(fp,"%d:%02d:%02d.%d", mins/60, mins%60, secs%60, frms);
227                         if( (label=label->next) != 0 ) fprintf(fp, ",");
228                 }
229         }
230         else {
231                 int mins = 0;
232                 for( int secs=0 ; secs<total_length; secs+=10*60 ) {
233                         mins = secs / 60;
234                         fprintf(fp,"%d:%02d:00,", mins/60, mins%60);
235                 }
236                 fprintf(fp,"%d:%02d:00", mins/60, mins%60);
237         }
238         fprintf(fp,"\"/>\n");
239         fprintf(fp,"    </pgc>\n");
240         fprintf(fp,"    </titles>\n");
241         fprintf(fp,"  </titleset>\n");
242         fprintf(fp,"</dvdauthor>\n");
243         fprintf(fp,"eof\n");
244         fprintf(fp,"\n");
245         fprintf(fp,"echo To burn dvd, load blank media and run:\n");
246         fprintf(fp,"echo growisofs -dvd-compat -Z /dev/dvd -dvd-video $1/iso\n");
247         fprintf(fp,"kill $$\n");
248         fprintf(fp,"\n");
249         fclose(fp);
250
251         if( use_wide_audio ) {
252                 session->audio_channels = session->audio_tracks = DVD_WIDE_CHANNELS;
253                 session->achannel_positions[0] = 90;
254                 session->achannel_positions[1] = 150;
255                 session->achannel_positions[2] = 30;
256                 session->achannel_positions[3] = 210;
257                 session->achannel_positions[4] = 330;
258                 session->achannel_positions[5] = 270;
259                 if( edl->tracks->recordable_audio_tracks() == DVD_WIDE_CHANNELS )
260                         mwindow->remap_audio(MWindow::AUDIO_1_TO_1);
261         }
262         else {
263                 session->audio_channels = session->audio_tracks = DVD_CHANNELS;
264                 session->achannel_positions[0] = 180;
265                 session->achannel_positions[1] = 0;
266                 if( edl->tracks->recordable_audio_tracks() == DVD_WIDE_CHANNELS )
267                         mwindow->remap_audio(MWindow::AUDIO_5_1_TO_2);
268         }
269
270         double new_samplerate = session->sample_rate;
271         double new_framerate = session->frame_rate;
272         edl->retrack();
273         edl->rechannel();
274         edl->resample(old_samplerate, new_samplerate, TRACK_AUDIO);
275         edl->resample(old_framerate, new_framerate, TRACK_VIDEO);
276
277         int64_t aud_size = ((dvd_kaudio_rate * total_length)/8 + 1000-1) * 1000;
278         int64_t vid_size = dvd_size*0.96 - aud_size;
279         int64_t vid_bitrate = (vid_size * 8) / total_length;
280         vid_bitrate /= 1000;  vid_bitrate *= 1000;
281         if( vid_bitrate > dvd_max_bitrate )
282                 vid_bitrate = dvd_max_bitrate;
283
284         char xml_filename[BCTEXTLEN];
285         sprintf(xml_filename, "%s/dvd.xml", asset_dir);
286         FileXML xml_file;
287         edl->save_xml(&xml_file, xml_filename, 0, 0);
288         xml_file.terminate_string();
289         if( xml_file.write_to_file(xml_filename) ) {
290                 char msg[BCTEXTLEN];
291                 sprintf(msg, _("Unable to save: %s"), xml_filename);
292                 MainError::show_error(msg);
293                 return 1;
294         }
295
296         BatchRenderJob *job = new BatchRenderJob(mwindow->preferences, use_label_chapters);
297         jobs->append(job);
298         strcpy(&job->edl_path[0], xml_filename);
299         Asset *asset = job->asset;
300
301         asset->layers = DVD_STREAMS;
302         asset->frame_rate = session->frame_rate;
303         asset->width = session->output_w;
304         asset->height = session->output_h;
305         asset->aspect_ratio = session->aspect_w / session->aspect_h;
306
307         if( use_ffmpeg ) {
308                 char option_path[BCTEXTLEN];
309                 sprintf(&asset->path[0],"%s/dvd.mpg", asset_dir);
310                 asset->format = FILE_FFMPEG;
311                 strcpy(asset->fformat, "dvd");
312
313                 asset->audio_data = 1;
314                 strcpy(asset->acodec, "dvd.dvd");
315                 FFMPEG::set_option_path(option_path, "audio/%s", asset->acodec);
316                 FFMPEG::load_options(option_path, asset->ff_audio_options,
317                          sizeof(asset->ff_audio_options));
318                 asset->ff_audio_bitrate = dvd_kaudio_rate * 1000;
319
320                 asset->video_data = 1;
321                 strcpy(asset->vcodec, "dvd.dvd");
322                 FFMPEG::set_option_path(option_path, "video/%s", asset->vcodec);
323                 FFMPEG::load_options(option_path, asset->ff_video_options,
324                          sizeof(asset->ff_video_options));
325                 asset->ff_video_bitrate = vid_bitrate;
326                 asset->ff_video_quality = -1;
327         }
328         else {
329                 sprintf(&asset->path[0],"%s/dvd.m2v", asset_dir);
330                 asset->video_data = 1;
331                 asset->format = FILE_VMPEG;
332                 asset->vmpeg_cmodel = BC_YUV420P;
333                 asset->vmpeg_fix_bitrate = 1;
334                 asset->vmpeg_bitrate = vid_bitrate;
335                 asset->vmpeg_quantization = 15;
336                 asset->vmpeg_iframe_distance = 15;
337                 asset->vmpeg_progressive = 0;
338                 asset->vmpeg_denoise = 0;
339                 asset->vmpeg_seq_codes = 0;
340                 asset->vmpeg_derivative = 2;
341                 asset->vmpeg_preset = 8;
342                 asset->vmpeg_field_order = 0;
343                 asset->vmpeg_pframe_distance = 0;
344                 job = new BatchRenderJob(mwindow->preferences, 0, 0);
345                 jobs->append(job);
346                 strcpy(&job->edl_path[0], xml_filename);
347                 asset = job->asset;
348
349                 sprintf(&asset->path[0],"%s/dvd.ac3", asset_dir);
350                 asset->audio_data = 1;
351                 asset->format = FILE_AC3;
352                 asset->channels = session->audio_channels;
353                 asset->sample_rate = session->sample_rate;
354                 asset->bits = 16;
355                 asset->byte_order = 0;
356                 asset->signed_ = 1;
357                 asset->header = 0;
358                 asset->dither = 0;
359                 asset->ac3_bitrate = dvd_kaudio_rate;
360         }
361
362         job = new BatchRenderJob(mwindow->preferences, 0, 0);
363         jobs->append(job);
364         job->edl_path[0] = '@';
365         strcpy(&job->edl_path[1], script_filename);
366         strcpy(&job->asset->path[0], asset_dir);
367
368         return 0;
369 }
370
371 void CreateDVD_Thread::handle_close_event(int result)
372 {
373         if( result ) return;
374         mwindow->defaults->update("WORK_DIRECTORY", tmp_path);
375         mwindow->batch_render->load_defaults(mwindow->defaults);
376         mwindow->undo->update_undo_before();
377         KeyFrame keyframe;  char data[BCTEXTLEN];
378         if( use_deinterlace ) {
379                 sprintf(data,"<DEINTERLACE MODE=1>");
380                 keyframe.set_data(data);
381                 insert_video_plugin("Deinterlace", &keyframe);
382         }
383         if( use_inverse_telecine ) {
384                 sprintf(data,"<IVTC FRAME_OFFSET=0 FIRST_FIELD=0 "
385                         "AUTOMATIC=1 AUTO_THRESHOLD=2.0e+00 PATTERN=2>");
386                 keyframe.set_data(data);
387                 insert_video_plugin("Inverse Telecine", &keyframe);
388         }
389         if( use_scale != Rescale::none ) {
390                 double dvd_aspect = dvd_aspect_height > 0 ? dvd_aspect_width/dvd_aspect_height : 1;
391
392                 Tracks *tracks = mwindow->edl->tracks;
393                 for( Track *vtrk=tracks->first; vtrk; vtrk=vtrk->next ) {
394                         if( vtrk->data_type != TRACK_VIDEO ) continue;
395                         if( !vtrk->record ) continue;
396                         vtrk->expand_view = 1;
397                         PluginSet *plugin_set = new PluginSet(mwindow->edl, vtrk);
398                         vtrk->plugin_set.append(plugin_set);
399                         Edits *edits = vtrk->edits;
400                         for( Edit *edit=edits->first; edit; edit=edit->next ) {
401                                 Indexable *indexable = edit->get_source();
402                                 if( !indexable ) continue;
403                                 Rescale in(indexable);
404                                 Rescale out(dvd_width, dvd_height, dvd_aspect);
405                                 float src_w, src_h, dst_w, dst_h;
406                                 in.rescale(out,use_scale, src_w,src_h, dst_w,dst_h);
407                                 sprintf(data,"<SCALERATIO TYPE=%d"
408                                         " IN_W=%d IN_H=%d IN_ASPECT_RATIO=%f"
409                                         " OUT_W=%d OUT_H=%d OUT_ASPECT_RATIO=%f"
410                                         " SRC_X=%f SRC_Y=%f SRC_W=%f SRC_H=%f"
411                                         " DST_X=%f DST_Y=%f DST_W=%f DST_H=%f>", use_scale,
412                                         in.w, in.h, in.aspect, out.w, out.h, out.aspect,
413                                         0., 0., src_w, src_h, 0., 0., dst_w, dst_h);
414                                 keyframe.set_data(data);
415                                 plugin_set->insert_plugin(_("Scale Ratio"),
416                                         edit->startproject, edit->length,
417                                         PLUGIN_STANDALONE, 0, &keyframe, 0);
418                         }
419                         vtrk->optimize();
420                 }
421         }
422
423         if( use_resize_tracks )
424                 resize_tracks();
425         if( use_histogram ) {
426 #if 0
427                 sprintf(data, "<HISTOGRAM OUTPUT_MIN_0=0 OUTPUT_MAX_0=1 "
428                         "OUTPUT_MIN_1=0 OUTPUT_MAX_1=1 "
429                         "OUTPUT_MIN_2=0 OUTPUT_MAX_2=1 "
430                         "OUTPUT_MIN_3=0 OUTPUT_MAX_3=1 "
431                         "AUTOMATIC=0 THRESHOLD=9.0-01 PLOT=0 SPLIT=0>"
432                         "<POINTS></POINTS><POINTS></POINTS><POINTS></POINTS>"
433                         "<POINTS><POINT X=6.0e-02 Y=0>"
434                                 "<POINT X=9.4e-01 Y=1></POINTS>");
435 #else
436                 sprintf(data, "<HISTOGRAM AUTOMATIC=0 THRESHOLD=1.0e-01 "
437                         "PLOT=0 SPLIT=0 W=440 H=500 PARADE=0 MODE=3 "
438                         "LOW_OUTPUT_0=0 HIGH_OUTPUT_0=1 LOW_INPUT_0=0 HIGH_INPUT_0=1 GAMMA_0=1 "
439                         "LOW_OUTPUT_1=0 HIGH_OUTPUT_1=1 LOW_INPUT_1=0 HIGH_INPUT_1=1 GAMMA_1=1 "
440                         "LOW_OUTPUT_2=0 HIGH_OUTPUT_2=1 LOW_INPUT_2=0 HIGH_INPUT_2=1 GAMMA_2=1 "
441                         "LOW_OUTPUT_3=0 HIGH_OUTPUT_3=1 LOW_INPUT_3=0.044 HIGH_INPUT_3=0.956 "
442                         "GAMMA_3=1>");
443 #endif
444                 keyframe.set_data(data);
445                 insert_video_plugin("Histogram", &keyframe);
446         }
447         char asset_dir[BCTEXTLEN], jobs_path[BCTEXTLEN];
448         snprintf(asset_dir, sizeof(asset_dir), "%s/%s", tmp_path, asset_title);
449         snprintf(jobs_path, sizeof(jobs_path), "%s/dvd.jobs", asset_dir);
450         mwindow->batch_render->reset(jobs_path);
451         int ret = create_dvd_jobs(&mwindow->batch_render->jobs, asset_dir);
452         mwindow->undo->update_undo_after(_("create dvd"), LOAD_ALL);
453         mwindow->resync_guis();
454         if( ret ) return;
455         mwindow->batch_render->save_jobs();
456         mwindow->batch_render->start();
457 }
458
459 BC_Window* CreateDVD_Thread::new_gui()
460 {
461         strcpy(tmp_path,"/tmp");
462         mwindow->defaults->get("WORK_DIRECTORY", tmp_path);
463         memset(asset_title,0,sizeof(asset_title));
464         time_t dt;  time(&dt);
465         struct tm dtm;  localtime_r(&dt, &dtm);
466         sprintf(asset_title, "dvd_%02d%02d%02d-%02d%02d%02d",
467                 dtm.tm_year+1900, dtm.tm_mon+1, dtm.tm_mday,
468                 dtm.tm_hour, dtm.tm_min, dtm.tm_sec);
469         use_deinterlace = 0;
470         use_scale = Rescale::none;
471         use_histogram = 0;
472         use_inverse_telecine = 0;
473         use_wide_audio = 0;
474         use_ffmpeg = 0;
475         use_resize_tracks = 0;
476         use_label_chapters = 0;
477         use_standard = DVD_NTSC_4x3;
478
479         dvd_size = DVD_SIZE;
480         dvd_width = DVD_WIDTH;
481         dvd_height = DVD_HEIGHT;
482         dvd_aspect_width = DVD_ASPECT_WIDTH;
483         dvd_aspect_height = DVD_ASPECT_HEIGHT;
484         dvd_framerate = DVD_FRAMERATE;
485         dvd_samplerate = DVD_SAMPLERATE;
486         dvd_max_bitrate = DVD_MAX_BITRATE;
487         dvd_kaudio_rate = DVD_KAUDIO_RATE;
488         max_w = 0; max_h = 0;
489
490         int has_standard = -1;
491         if( mwindow->edl ) {
492                 EDLSession *session = mwindow->edl->session;
493                 double framerate = session->frame_rate;
494                 double aspect_ratio = session->aspect_h > 0 ?
495                         session->aspect_w / session->aspect_h > 0 : 1;
496                 int output_w = session->output_w, output_h = session->output_h;
497 // match the session to any known standard
498                 for( int i=0; i<(int)(sizeof(dvd_formats)/sizeof(dvd_formats[0])); ++i ) {
499                         int norm = dvd_formats[i].norm;
500                         if( !EQUIV(framerate, dvd_norms[norm].framerate) ) continue;
501                         if( output_w != dvd_norms[norm].w ) continue;
502                         if( output_h != dvd_norms[norm].h ) continue;
503                         int aspect = dvd_formats[i].aspect;
504                         double dvd_aspect_ratio =
505                                 (double)dvd_aspects[aspect].w / dvd_aspects[aspect].h;
506                         if( !EQUIV(aspect_ratio, dvd_aspect_ratio) ) continue;
507                         has_standard = i;  break;
508                 }
509                 if( has_standard < 0 ) {
510 // or use the default standard
511                         if( !strcmp(mwindow->default_standard, "NTSC") ) has_standard = DVD_NTSC_4x3;
512                         else if( !strcmp(mwindow->default_standard, "PAL") ) has_standard = DVD_PAL_4x3;
513                 }
514         }
515         if( has_standard >= 0 )
516                 use_standard = has_standard;
517
518         option_presets();
519         int scr_x = mwindow->gui->get_screen_x(0, -1);
520         int scr_w = mwindow->gui->get_screen_w(0, -1);
521         int scr_h = mwindow->gui->get_screen_h(0, -1);
522         int w = 520, h = 280;
523         int x = scr_x + scr_w/2 - w/2, y = scr_h/2 - h/2;
524
525         gui = new CreateDVD_GUI(this, x, y, w, h);
526         gui->create_objects();
527         return gui;
528 }
529
530
531 CreateDVD_OK::CreateDVD_OK(CreateDVD_GUI *gui, int x, int y)
532  : BC_OKButton(x, y)
533 {
534         this->gui = gui;
535         set_tooltip(_("end setup, start batch render"));
536 }
537
538 CreateDVD_OK::~CreateDVD_OK()
539 {
540 }
541
542 int CreateDVD_OK::button_press_event()
543 {
544         if(get_buttonpress() == 1 && is_event_win() && cursor_inside()) {
545                 gui->set_done(0);
546                 return 1;
547         }
548         return 0;
549 }
550
551 int CreateDVD_OK::keypress_event()
552 {
553         return 0;
554 }
555
556
557 CreateDVD_Cancel::CreateDVD_Cancel(CreateDVD_GUI *gui, int x, int y)
558  : BC_CancelButton(x, y)
559 {
560         this->gui = gui;
561 }
562
563 CreateDVD_Cancel::~CreateDVD_Cancel()
564 {
565 }
566
567 int CreateDVD_Cancel::button_press_event()
568 {
569         if(get_buttonpress() == 1 && is_event_win() && cursor_inside()) {
570                 gui->set_done(1);
571                 return 1;
572         }
573         return 0;
574 }
575
576
577 CreateDVD_DiskSpace::CreateDVD_DiskSpace(CreateDVD_GUI *gui, int x, int y)
578  : BC_Title(x, y, "", MEDIUMFONT, GREEN)
579 {
580         this->gui = gui;
581 }
582
583 CreateDVD_DiskSpace::~CreateDVD_DiskSpace()
584 {
585 }
586
587 int64_t CreateDVD_DiskSpace::tmp_path_space()
588 {
589         const char *path = gui->thread->tmp_path;
590         if( access(path,R_OK+W_OK) ) return 0;
591         struct statfs sfs;
592         if( statfs(path, &sfs) ) return 0;
593         return (int64_t)sfs.f_bsize * sfs.f_bfree;
594 }
595
596 void CreateDVD_DiskSpace::update()
597 {
598         static const char *suffix[] = { "", "KB", "MB", "GB", "TB", "PB" };
599         int64_t disk_space = tmp_path_space();
600         double media_size = 15e9, msz = 0, m = 1;
601         char sfx[BCSTRLEN];
602         if( sscanf(gui->media_size->get_text(), "%lf%s", &msz, sfx) == 2 ) {
603                 int i = sizeof(suffix)/sizeof(suffix[0]);
604                 while( --i >= 0 && strcmp(sfx, suffix[i]) );
605                 while( --i >= 0 ) m *= 1000;
606                 media_size = msz * m;
607         }
608         m = gui->thread->use_ffmpeg ? 2 : 3;
609         int color = disk_space < media_size*m ? RED : GREEN;
610         int i = 0;
611         for( int64_t space=disk_space; i<5 && (space/=1000)>0; disk_space=space, ++i );
612         char text[BCTEXTLEN];
613         sprintf(text, "%s%3jd%s", _("disk space: "), disk_space, suffix[i]);
614         gui->disk_space->BC_Title::update(text);
615         gui->disk_space->set_color(color);
616 }
617
618 CreateDVD_TmpPath::CreateDVD_TmpPath(CreateDVD_GUI *gui, int x, int y, int w)
619  : BC_TextBox(x, y, w, 1, -(int)sizeof(gui->thread->tmp_path),
620                 gui->thread->tmp_path, 1, MEDIUMFONT)
621 {
622         this->gui = gui;
623 }
624
625 CreateDVD_TmpPath::~CreateDVD_TmpPath()
626 {
627 }
628
629 int CreateDVD_TmpPath::handle_event()
630 {
631         get_text();
632         gui->disk_space->update();
633         return 1;
634 }
635
636
637 CreateDVD_AssetTitle::CreateDVD_AssetTitle(CreateDVD_GUI *gui, int x, int y, int w)
638  : BC_TextBox(x, y, w, 1, -(int)sizeof(gui->thread->asset_title),
639                 gui->thread->asset_title, 1, MEDIUMFONT)
640 {
641         this->gui = gui;
642 }
643
644 CreateDVD_AssetTitle::~CreateDVD_AssetTitle()
645 {
646 }
647
648 int CreateDVD_AssetTitle::handle_event()
649 {
650         get_text();
651         return 1;
652 }
653
654
655 CreateDVD_Deinterlace::CreateDVD_Deinterlace(CreateDVD_GUI *gui, int x, int y)
656  : BC_CheckBox(x, y, &gui->thread->use_deinterlace, _("Deinterlace"))
657 {
658         this->gui = gui;
659 }
660
661 CreateDVD_Deinterlace::~CreateDVD_Deinterlace()
662 {
663 }
664
665 int CreateDVD_Deinterlace::handle_event()
666 {
667         if( get_value() ) {
668                 gui->need_inverse_telecine->set_value(0);
669                 gui->thread->use_inverse_telecine = 0;
670         }
671         return BC_CheckBox::handle_event();
672 }
673
674
675 CreateDVD_InverseTelecine::CreateDVD_InverseTelecine(CreateDVD_GUI *gui, int x, int y)
676  : BC_CheckBox(x, y, &gui->thread->use_inverse_telecine, _("Inverse Telecine"))
677 {
678         this->gui = gui;
679 }
680
681 CreateDVD_InverseTelecine::~CreateDVD_InverseTelecine()
682 {
683 }
684
685 int CreateDVD_InverseTelecine::handle_event()
686 {
687         if( get_value() ) {
688                 gui->need_deinterlace->set_value(0);
689                 gui->thread->use_deinterlace = 0;
690         }
691         return BC_CheckBox::handle_event();
692 }
693
694
695 CreateDVD_ResizeTracks::CreateDVD_ResizeTracks(CreateDVD_GUI *gui, int x, int y)
696  : BC_CheckBox(x, y, &gui->thread->use_resize_tracks, _("Resize Tracks"))
697 {
698         this->gui = gui;
699 }
700
701 CreateDVD_ResizeTracks::~CreateDVD_ResizeTracks()
702 {
703 }
704
705
706 CreateDVD_Histogram::CreateDVD_Histogram(CreateDVD_GUI *gui, int x, int y)
707  : BC_CheckBox(x, y, &gui->thread->use_histogram, _("Histogram"))
708 {
709         this->gui = gui;
710 }
711
712 CreateDVD_Histogram::~CreateDVD_Histogram()
713 {
714 }
715
716 CreateDVD_LabelChapters::CreateDVD_LabelChapters(CreateDVD_GUI *gui, int x, int y)
717  : BC_CheckBox(x, y, &gui->thread->use_label_chapters, _("Chapters at Labels"))
718 {
719         this->gui = gui;
720 }
721
722 CreateDVD_LabelChapters::~CreateDVD_LabelChapters()
723 {
724 }
725
726 CreateDVD_WideAudio::CreateDVD_WideAudio(CreateDVD_GUI *gui, int x, int y)
727  : BC_CheckBox(x, y, &gui->thread->use_wide_audio, _("Audio 5.1"))
728 {
729         this->gui = gui;
730 }
731
732 CreateDVD_WideAudio::~CreateDVD_WideAudio()
733 {
734 }
735
736 CreateDVD_UseFFMpeg::CreateDVD_UseFFMpeg(CreateDVD_GUI *gui, int x, int y)
737  : BC_CheckBox(x, y, &gui->thread->use_ffmpeg, _("Use FFMPEG"))
738 {
739         this->gui = gui;
740 }
741
742 CreateDVD_UseFFMpeg::~CreateDVD_UseFFMpeg()
743 {
744 }
745
746
747
748
749 CreateDVD_GUI::CreateDVD_GUI(CreateDVD_Thread *thread, int x, int y, int w, int h)
750  : BC_Window(_(PROGRAM_NAME ": Create DVD"), x, y, w, h, 50, 50, 1, 0, 1)
751 {
752         this->thread = thread;
753         at_x = at_y = tmp_x = tmp_y = 0;
754         ok_x = ok_y = ok_w = ok_h = 0;
755         cancel_x = cancel_y = cancel_w = cancel_h = 0;
756         asset_title = 0;
757         tmp_path = 0;
758         btmp_path = 0;
759         disk_space = 0;
760         standard = 0;
761         scale = 0;
762         need_deinterlace = 0;
763         need_inverse_telecine = 0;
764         need_resize_tracks = 0;
765         need_histogram = 0;
766         need_wide_audio = 0;
767         need_label_chapters = 0;
768         ok = 0;
769         cancel = 0;
770 }
771
772 CreateDVD_GUI::~CreateDVD_GUI()
773 {
774 }
775
776 void CreateDVD_GUI::create_objects()
777 {
778         lock_window("CreateDVD_GUI::create_objects");
779         int pady = BC_TextBox::calculate_h(this, MEDIUMFONT, 0, 1) + 5;
780         int padx = BC_Title::calculate_w(this, (char*)"X", MEDIUMFONT);
781         int x = padx/2, y = pady/2;
782         BC_Title *title = new BC_Title(x, y, _("Title:"), MEDIUMFONT, YELLOW);
783         add_subwindow(title);
784         at_x = x + title->get_w();  at_y = y;
785         asset_title = new CreateDVD_AssetTitle(this, at_x, at_y, get_w()-at_x-10);
786         add_subwindow(asset_title);
787         y += title->get_h() + pady/2;
788         title = new BC_Title(x, y, _("Work path:"), MEDIUMFONT, YELLOW);
789         add_subwindow(title);
790         tmp_x = x + title->get_w();  tmp_y = y;
791         tmp_path = new CreateDVD_TmpPath(this, tmp_x, tmp_y,  get_w()-tmp_x-35);
792         add_subwindow(tmp_path);
793         btmp_path = new BrowseButton(thread->mwindow->theme, this, tmp_path,
794                 tmp_x+tmp_path->get_w(), tmp_y, "/tmp",
795                 _("Work path"), _("Select a Work directory:"), 1);
796         add_subwindow(btmp_path);
797         y += title->get_h() + pady/2;
798         disk_space = new CreateDVD_DiskSpace(this, x, y);
799         add_subwindow(disk_space);
800         int x0 = get_w() - 170;
801         title = new BC_Title(x0, y, _("Media:"), MEDIUMFONT, YELLOW);
802         add_subwindow(title);
803         int x1 = x0+title->get_w()+padx;
804         media_size = new CreateDVD_MediaSize(this, x1, y);
805         media_size->create_objects();
806         media_sizes.append(new BC_ListBoxItem("4.7GB"));
807         media_sizes.append(new BC_ListBoxItem("8.3GB"));
808         media_size->update_list(&media_sizes);
809         media_size->update(media_sizes[0]->get_text());
810         disk_space->update();
811         y += disk_space->get_h() + pady/2;
812         title = new BC_Title(x, y, _("Format:"), MEDIUMFONT, YELLOW);
813         add_subwindow(title);
814         standard = new CreateDVD_Format(this, title->get_w() + padx, y);
815         add_subwindow(standard);
816         standard->create_objects();
817         x0 -= 30;
818         title = new BC_Title(x0, y, _("Scale:"), MEDIUMFONT, YELLOW);
819         add_subwindow(title);
820         x1 = x0+title->get_w()+padx;
821         scale = new CreateDVD_Scale(this, x1, y);
822         add_subwindow(scale);
823         scale->create_objects();
824         y += standard->get_h() + pady/2;
825         need_deinterlace = new CreateDVD_Deinterlace(this, x, y);
826         add_subwindow(need_deinterlace);
827         x1 = x + 170;
828         int x2 = x1 + 170;
829         need_inverse_telecine = new CreateDVD_InverseTelecine(this, x1, y);
830         add_subwindow(need_inverse_telecine);
831         need_use_ffmpeg = new CreateDVD_UseFFMpeg(this, x2, y);
832         add_subwindow(need_use_ffmpeg);
833         y += need_deinterlace->get_h() + pady/2;
834         need_histogram = new CreateDVD_Histogram(this, x, y);
835         add_subwindow(need_histogram);
836         need_wide_audio = new CreateDVD_WideAudio(this, x1, y);
837         add_subwindow(need_wide_audio);
838         need_resize_tracks = new CreateDVD_ResizeTracks(this, x2, y);
839         add_subwindow(need_resize_tracks);
840         y += need_histogram->get_h() + pady/2;
841         need_label_chapters = new CreateDVD_LabelChapters(this, x1, y);
842         add_subwindow(need_label_chapters);
843         ok_w = BC_OKButton::calculate_w();
844         ok_h = BC_OKButton::calculate_h();
845         ok_x = 10;
846         ok_y = get_h() - ok_h - 10;
847         ok = new CreateDVD_OK(this, ok_x, ok_y);
848         add_subwindow(ok);
849         cancel_w = BC_CancelButton::calculate_w();
850         cancel_h = BC_CancelButton::calculate_h();
851         cancel_x = get_w() - cancel_w - 10,
852         cancel_y = get_h() - cancel_h - 10;
853         cancel = new CreateDVD_Cancel(this, cancel_x, cancel_y);
854         add_subwindow(cancel);
855         show_window();
856         unlock_window();
857 }
858
859 int CreateDVD_GUI::resize_event(int w, int h)
860 {
861         asset_title->reposition_window(at_x, at_y, get_w()-at_x-10);
862         tmp_path->reposition_window(tmp_x, tmp_y,  get_w()-tmp_x-35);
863         btmp_path->reposition_window(tmp_x+tmp_path->get_w(), tmp_y);
864         ok_y = h - ok_h - 10;
865         ok->reposition_window(ok_x, ok_y);
866         cancel_x = w - cancel_w - 10,
867         cancel_y = h - cancel_h - 10;
868         cancel->reposition_window(cancel_x, cancel_y);
869         return 0;
870 }
871
872 int CreateDVD_GUI::translation_event()
873 {
874         return 1;
875 }
876
877 int CreateDVD_GUI::close_event()
878 {
879         set_done(1);
880         return 1;
881 }
882
883 void CreateDVD_GUI::update()
884 {
885         scale->set_value(thread->use_scale);
886         need_deinterlace->set_value(thread->use_deinterlace);
887         need_inverse_telecine->set_value(thread->use_inverse_telecine);
888         need_use_ffmpeg->set_value(thread->use_ffmpeg);
889         need_resize_tracks->set_value(thread->use_resize_tracks);
890         need_histogram->set_value(thread->use_histogram);
891         need_wide_audio->set_value(thread->use_wide_audio);
892         need_label_chapters->set_value(thread->use_label_chapters);
893 }
894
895 int CreateDVD_Thread::
896 insert_video_plugin(const char *title, KeyFrame *default_keyframe)
897 {
898         Tracks *tracks = mwindow->edl->tracks;
899         for( Track *vtrk=tracks->first; vtrk; vtrk=vtrk->next ) {
900                 if( vtrk->data_type != TRACK_VIDEO ) continue;
901                 if( !vtrk->record ) continue;
902                 vtrk->expand_view = 1;
903                 PluginSet *plugin_set = new PluginSet(mwindow->edl, vtrk);
904                 vtrk->plugin_set.append(plugin_set);
905                 Edits *edits = vtrk->edits;
906                 for( Edit *edit=edits->first; edit; edit=edit->next ) {
907                         plugin_set->insert_plugin(_(title),
908                                 edit->startproject, edit->length,
909                                 PLUGIN_STANDALONE, 0, default_keyframe, 0);
910                 }
911                 vtrk->optimize();
912         }
913         return 0;
914 }
915
916 int CreateDVD_Thread::
917 resize_tracks()
918 {
919         Tracks *tracks = mwindow->edl->tracks;
920         int trk_w = max_w, trk_h = max_h;
921         if( trk_w < dvd_width ) trk_w = dvd_width;
922         if( trk_h < dvd_height ) trk_h = dvd_height;
923         for( Track *vtrk=tracks->first; vtrk; vtrk=vtrk->next ) {
924                 if( vtrk->data_type != TRACK_VIDEO ) continue;
925                 if( !vtrk->record ) continue;
926                 vtrk->track_w = trk_w;
927                 vtrk->track_h = trk_h;
928         }
929         return 0;
930 }
931
932 int CreateDVD_Thread::
933 option_presets()
934 {
935 // reset only probed options
936         use_deinterlace = 0;
937         use_scale = Rescale::none;
938         use_resize_tracks = 0;
939         use_wide_audio = 0;
940         use_label_chapters = 0;
941
942         if( !mwindow->edl ) return 1;
943
944         int norm = dvd_formats[use_standard].norm;
945         dvd_width = dvd_norms[norm].w;
946         dvd_height = dvd_norms[norm].h;
947         dvd_framerate = dvd_norms[norm].framerate;
948         int aspect = dvd_formats[use_standard].aspect;
949         dvd_aspect_width = dvd_aspects[aspect].w;
950         dvd_aspect_height = dvd_aspects[aspect].h;
951         double dvd_aspect = dvd_aspect_height > 0 ? dvd_aspect_width/dvd_aspect_height : 1;
952
953         Tracks *tracks = mwindow->edl->tracks;
954         max_w = 0;  max_h = 0;
955         int has_deinterlace = 0, has_scale = 0;
956         for( Track *trk=tracks->first; trk; trk=trk->next ) {
957                 if( !trk->record ) continue;
958                 Edits *edits = trk->edits;
959                 switch( trk->data_type ) {
960                 case TRACK_VIDEO:
961                         for( Edit *edit=edits->first; edit; edit=edit->next ) {
962                                 if( edit->silence() ) continue;
963                                 Indexable *indexable = edit->get_source();
964                                 int w = indexable->get_w();
965                                 if( w > max_w ) max_w = w;
966                                 if( w != dvd_width ) use_scale = Rescale::scaled;
967                                 int h = indexable->get_h();
968                                 if( h > max_h ) max_h = h;
969                                 if( h != dvd_height ) use_scale = Rescale::scaled;
970                                 float aw, ah;
971                                 MWindow::create_aspect_ratio(aw, ah, w, h);
972                                 double aspect = ah > 0 ? aw / ah : 1;
973                                 if( !EQUIV(aspect, dvd_aspect) ) use_scale = Rescale::scaled;
974                         }
975                         for( int i=0; i<trk->plugin_set.size(); ++i ) {
976                                 for( Plugin *plugin = (Plugin*)trk->plugin_set[i]->first;
977                                                 plugin; plugin=(Plugin*)plugin->next ) {
978                                         if( !strcmp(plugin->title, "Deinterlace") )
979                                                 has_deinterlace = 1;
980                                         if( !strcmp(plugin->title, "Auto Scale") ||
981                                             !strcmp(plugin->title, "Scale Ratio") ||
982                                             !strcmp(plugin->title, "Scale") )
983                                                 has_scale = 1;
984                                 }
985                         }
986                         break;
987                 }
988         }
989         if( has_scale )
990                 use_scale = Rescale::none;
991         if( use_scale != Rescale::none ) {
992                 if( max_w != dvd_width ) use_resize_tracks = 1;
993                 if( max_h != dvd_height ) use_resize_tracks = 1;
994         }
995         for( Track *trk=tracks->first; trk && !use_resize_tracks; trk=trk->next ) {
996                 if( !trk->record ) continue;
997                 switch( trk->data_type ) {
998                 case TRACK_VIDEO:
999                         if( trk->track_w != max_w ) use_resize_tracks = 1;
1000                         if( trk->track_h != max_h ) use_resize_tracks = 1;
1001                         break;
1002                 }
1003         }
1004         if( !has_deinterlace && max_h > 2*dvd_height ) use_deinterlace = 1;
1005         Labels *labels = mwindow->edl->labels;
1006         use_label_chapters = labels && labels->first ? 1 : 0;
1007
1008         if( tracks->recordable_audio_tracks() == DVD_WIDE_CHANNELS )
1009                 use_wide_audio = 1;
1010
1011         return 0;
1012 }
1013
1014
1015
1016 CreateDVD_FormatItem::CreateDVD_FormatItem(CreateDVD_Format *popup,
1017                 int standard, const char *text)
1018  : BC_MenuItem(text)
1019 {
1020         this->popup = popup;
1021         this->standard = standard;
1022 }
1023
1024 CreateDVD_FormatItem::~CreateDVD_FormatItem()
1025 {
1026 }
1027
1028 int CreateDVD_FormatItem::handle_event()
1029 {
1030         popup->set_text(get_text());
1031         popup->gui->thread->use_standard = standard;
1032         return popup->handle_event();
1033 }
1034
1035
1036 CreateDVD_Format::CreateDVD_Format(CreateDVD_GUI *gui, int x, int y)
1037  : BC_PopupMenu(x, y, 180, "", 1)
1038 {
1039         this->gui = gui;
1040 }
1041
1042 CreateDVD_Format::~CreateDVD_Format()
1043 {
1044 }
1045
1046 void CreateDVD_Format::create_objects()
1047 {
1048         for( int i=0; i<(int)(sizeof(dvd_formats)/sizeof(dvd_formats[0])); ++i ) {
1049                 int norm = dvd_formats[i].norm;
1050                 int aspect = dvd_formats[i].aspect;
1051                 char item_text[BCTEXTLEN];
1052                 sprintf(item_text,"%4s (%5.2f) %dx%d",
1053                         dvd_norms[norm].name, dvd_norms[norm].framerate,
1054                         dvd_aspects[aspect].w, dvd_aspects[aspect].h);
1055                 add_item(new CreateDVD_FormatItem(this, i, item_text));
1056         }
1057         set_value(gui->thread->use_standard);
1058 }
1059
1060 int CreateDVD_Format::handle_event()
1061 {
1062         gui->thread->option_presets();
1063         gui->update();
1064         return 1;
1065 }
1066
1067
1068 CreateDVD_ScaleItem::CreateDVD_ScaleItem(CreateDVD_Scale *popup,
1069                 int scale, const char *text)
1070  : BC_MenuItem(text)
1071 {
1072         this->popup = popup;
1073         this->scale = scale;
1074 }
1075
1076 CreateDVD_ScaleItem::~CreateDVD_ScaleItem()
1077 {
1078 }
1079
1080 int CreateDVD_ScaleItem::handle_event()
1081 {
1082         popup->gui->thread->use_scale = scale;
1083         popup->set_value(scale);
1084         return popup->handle_event();
1085 }
1086
1087
1088 CreateDVD_Scale::CreateDVD_Scale(CreateDVD_GUI *gui, int x, int y)
1089  : BC_PopupMenu(x, y, 100, "", 1)
1090 {
1091         this->gui = gui;
1092 }
1093
1094 CreateDVD_Scale::~CreateDVD_Scale()
1095 {
1096 }
1097
1098 void CreateDVD_Scale::create_objects()
1099 {
1100
1101         for( int i=0; i<(int)Rescale::n_scale_types; ++i ) {
1102                 add_item(new CreateDVD_ScaleItem(this, i, Rescale::scale_types[i]));
1103         }
1104         set_value(gui->thread->use_scale);
1105 }
1106
1107 int CreateDVD_Scale::handle_event()
1108 {
1109         gui->update();
1110         return 1;
1111 }
1112
1113
1114 CreateDVD_MediaSize::CreateDVD_MediaSize(CreateDVD_GUI *gui, int x, int y)
1115  : BC_PopupTextBox(gui, 0, 0, x, y, 70,50)
1116 {
1117         this->gui = gui;
1118 }
1119
1120 CreateDVD_MediaSize::~CreateDVD_MediaSize()
1121 {
1122 }
1123
1124 int CreateDVD_MediaSize::handle_event()
1125 {
1126         gui->disk_space->update();
1127         return 1;
1128 }
1129