Third set of 50 GPL attribution for CV-Contributors added +
[goodguy/cinelerra.git] / cinelerra-5.1 / cinelerra / fileffmpeg.C
1 /*
2  * CINELERRA
3  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
4  * Copyright (C) 2010 Monty Montgomery
5  * Copyright (C) 2012-2014 Paolo Rampino
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 <stdio.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 // work around for __STDC_CONSTANT_MACROS
29 #include <lzma.h>
30
31 #include "asset.h"
32 #include "bcwindowbase.h"
33 #include "bitspopup.h"
34 #include "ctype.h"
35 #include "edl.h"
36 #include "ffmpeg.h"
37 #include "filebase.h"
38 #include "file.h"
39 #include "fileffmpeg.h"
40 #include "filesystem.h"
41 #include "indexfile.h"
42 #include "interlacemodes.h"
43 #include "language.h"
44 #include "mainerror.h"
45 #include "mainprogress.h"
46 #include "mutex.h"
47 #include "preferences.h"
48 #include "videodevice.inc"
49
50 #ifdef FFMPEG3
51 #define url filename
52 #endif
53
54 FileFFMPEG::FileFFMPEG(Asset *asset, File *file)
55  : FileBase(asset, file)
56 {
57         ff = 0;
58         if(asset->format == FILE_UNKNOWN)
59                 asset->format = FILE_FFMPEG;
60 }
61
62 FileFFMPEG::~FileFFMPEG()
63 {
64         delete ff;
65 }
66
67
68 FFMpegConfigNum::FFMpegConfigNum(BC_Window *window,
69                 int x, int y, char *title_text, int *output)
70  : BC_TumbleTextBox(window, *output, -1, INT_MAX, xS(100), y, xS(100))
71 {
72         this->window = window;
73         this->x = x;  this->y = y;
74         this->title_text = title_text;
75         this->output = output;
76 }
77
78 FFMpegConfigNum::~FFMpegConfigNum()
79 {
80 }
81
82 void FFMpegConfigNum::create_objects()
83 {
84         window->add_subwindow(title = new BC_Title(x, y, title_text));
85         BC_TumbleTextBox::create_objects();
86 }
87
88 int FFMpegConfigNum::update_param(const char *param, const char *opts)
89 {
90         char value[BCSTRLEN];
91         if( !FFMPEG::get_ff_option(param, opts, value) ) {
92                 if( (*output = atoi(value)) < 0 ) {
93                         disable(1);
94                         return 0;
95                 }
96         }
97         BC_TumbleTextBox::update((int64_t)*output);
98         enable();
99         return 1;
100 }
101
102 int FFMpegConfigNum::handle_event()
103 {
104         *output = atoi(get_text());
105         return 1;
106 }
107
108 FFMpegAudioNum::FFMpegAudioNum(BC_Window *window,
109                 int x, int y, char *title_text, int *output)
110  : FFMpegConfigNum(window, x, y, title_text, output)
111 {
112 }
113
114 int FFMpegAudioBitrate::handle_event()
115 {
116         int ret = FFMpegAudioNum::handle_event();
117         Asset *asset = window()->asset;
118         if( asset->ff_audio_bitrate > 0 )
119                 window()->quality->disable();
120         else if( !window()->quality->get_textbox()->is_hidden() )
121                 window()->quality->enable();
122         return ret;
123 }
124
125 int FFMpegAudioQuality::handle_event()
126 {
127         int ret = FFMpegAudioNum::handle_event();
128         Asset *asset = window()->asset;
129         if( asset->ff_audio_quality >= 0 )
130                 window()->bitrate->disable();
131         else if( !window()->bitrate->get_textbox()->is_hidden() )
132                 window()->bitrate->enable();
133         return ret;
134 }
135
136 FFMpegVideoNum::FFMpegVideoNum(BC_Window *window,
137                 int x, int y, char *title_text, int *output)
138  : FFMpegConfigNum(window, x, y, title_text, output)
139 {
140 }
141
142 int FFMpegVideoBitrate::handle_event()
143 {
144         int ret = FFMpegVideoNum::handle_event();
145         Asset *asset = window()->asset;
146         if( asset->ff_video_bitrate > 0 )
147                 window()->quality->disable();
148         else if( !window()->quality->get_textbox()->is_hidden() )
149                 window()->quality->enable();
150         return ret;
151 }
152
153 int FFMpegVideoQuality::handle_event()
154 {
155         int ret = FFMpegVideoNum::handle_event();
156         Asset *asset = window()->asset;
157         if( asset->ff_video_quality >= 0 )
158                 window()->bitrate->disable();
159         else if( !window()->bitrate->get_textbox()->is_hidden() )
160                 window()->bitrate->enable();
161         return ret;
162 }
163
164 FFMpegPixelFormat::FFMpegPixelFormat(FFMPEGConfigVideo *vid_config,
165         int x, int y, int w, int list_h)
166  : BC_PopupTextBox(vid_config, 0, 0, x, y, w, list_h)
167 {
168         this->vid_config = vid_config;
169 }
170
171 int FFMpegPixelFormat::handle_event()
172 {
173         strncpy(vid_config->asset->ff_pixel_format, get_text(),
174                 sizeof(vid_config->asset->ff_pixel_format));
175         return 1;
176 }
177
178 void FFMpegPixelFormat::update_formats()
179 {
180         pixfmts.remove_all_objects();
181         char video_codec[BCSTRLEN]; video_codec[0] = 0;
182         const char *vcodec = vid_config->asset->vcodec;
183         const AVCodec *av_codec = !FFMPEG::get_codec(video_codec, "video", vcodec) ?
184                 avcodec_find_encoder_by_name(video_codec) : 0;
185         const AVPixelFormat *pix_fmts = av_codec ? av_codec->pix_fmts : 0;
186         if( pix_fmts ) {
187                 for( int i=0; pix_fmts[i]>=0; ++i ) {
188                         const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmts[i]);
189                         if( desc ) pixfmts.append(new BC_ListBoxItem(desc->name));
190                 }
191         }
192         update_list(&pixfmts);
193 }
194
195 FFMpegSampleFormat::FFMpegSampleFormat(FFMPEGConfigAudio *aud_config,
196         int x, int y, int w, int list_h)
197  : BC_PopupTextBox(aud_config, 0, 0, x, y, w, list_h)
198 {
199         this->aud_config = aud_config;
200 }
201
202 int FFMpegSampleFormat::handle_event()
203 {
204         strncpy(aud_config->asset->ff_sample_format, get_text(),
205                 sizeof(aud_config->asset->ff_sample_format));
206         return 1;
207 }
208
209 void FFMpegSampleFormat::update_formats()
210 {
211         samplefmts.remove_all_objects();
212         char audio_codec[BCSTRLEN]; audio_codec[0] = 0;
213         const char *acodec = aud_config->asset->acodec;
214         const AVCodec *av_codec = !FFMPEG::get_codec(audio_codec, "audio", acodec) ?
215                 avcodec_find_encoder_by_name(audio_codec) : 0;
216         const AVSampleFormat *sample_fmts = av_codec ? av_codec->sample_fmts : 0;
217         if( sample_fmts ) {
218                 for( int i=0; sample_fmts[i]>=0; ++i ) {
219                         const char *name = av_get_sample_fmt_name(sample_fmts[i]);
220                         if( name ) samplefmts.append(new BC_ListBoxItem(name));
221                 }
222         }
223         update_list(&samplefmts);
224 }
225
226 void FileFFMPEG::set_options(char *cp, int len, const char *bp)
227 {
228         char *ep = cp + len-2, ch = 0;
229         while( cp < ep && *bp != 0 ) { ch = *bp++; *cp++ = ch; }
230         if( ch != '\n' ) *cp++ = '\n';
231         *cp = 0;
232 }
233
234 void FileFFMPEG::get_parameters(BC_WindowBase *parent_window,
235                 Asset *asset, BC_WindowBase *&format_window,
236                 int audio_options, int video_options, EDL *edl)
237 {
238         Asset *ff_asset = new Asset();
239         ff_asset->copy_from(asset, 0);
240         int wx, wy;
241         parent_window->get_pop_cursor(wx, wy);
242         if( audio_options ) {
243                 FFMPEGConfigAudio *window = new FFMPEGConfigAudio(parent_window,
244                         wx, wy, ff_asset, edl);
245                 format_window = window;
246                 window->create_objects();
247                 if( !window->run_window() ) {
248                         asset->copy_from(ff_asset,0);
249                         set_options(asset->ff_audio_options,
250                                 sizeof(asset->ff_audio_options),
251                                 window->audio_options->get_text());
252                 }
253                 delete window;
254         }
255         else if( video_options ) {
256                 FFMPEGConfigVideo *window = new FFMPEGConfigVideo(parent_window,
257                         wx, wy, ff_asset, edl);
258                 format_window = window;
259                 window->create_objects();
260                 if( !window->run_window() ) {
261                         asset->copy_from(ff_asset,0);
262                         set_options(asset->ff_video_options,
263                                 sizeof(asset->ff_video_options),
264                                 window->video_options->get_text());
265                 }
266                 delete window;
267         }
268         ff_asset->remove_user();
269 }
270
271 int FileFFMPEG::check_sig(Asset *asset)
272 {
273         char *ptr = strstr(asset->path, ".pcm");
274         if( ptr ) return 0;
275         ptr = strstr(asset->path, ".raw");
276         if( ptr ) return 0;
277
278         FFMPEG ffmpeg(0);
279         int ret = !ffmpeg.init_decoder(asset->path) &&
280                 !ffmpeg.open_decoder() ? 1 : 0;
281         return ret;
282 }
283
284 void FileFFMPEG::get_info(char *path, char *text, int len)
285 {
286         char *cp = text;
287         FFMPEG ffmpeg(0);
288         cp += sprintf(cp, _("file path: %s\n"), path);
289         struct stat st;
290         int ret = 0;
291         if( stat(path, &st) < 0 ) {
292                 cp += sprintf(cp, _(" err: %s\n"), strerror(errno));
293                 ret = 1;
294         }
295         else {
296                 cp += sprintf(cp, _("  %jd bytes\n"), st.st_size);
297         }
298         if( !ret ) ret = ffmpeg.init_decoder(path);
299         if( !ret ) ret = ffmpeg.open_decoder();
300         if( !ret ) {
301                 cp += sprintf(cp, _("info:\n"));
302                 ffmpeg.info(cp, len-(cp-text));
303         }
304         else
305                 sprintf(cp, _("== open failed\n"));
306 }
307
308 int FileFFMPEG::get_video_info(int track, int &pid, double &framerate,
309                 int &width, int &height, char *title)
310 {
311         if( !ff ) return -1;
312         pid = ff->ff_video_pid(track);
313         framerate = ff->ff_frame_rate(track);
314         width = ff->ff_video_width(track);
315         height = ff->ff_video_height(track);
316         if( title ) *title = 0;
317         return 0;
318 }
319
320 int FileFFMPEG::get_audio_for_video(int vstream, int astream, int64_t &channel_mask)
321 {
322         if( !ff ) return 1;
323         return ff->ff_audio_for_video(vstream, astream, channel_mask);
324 }
325
326 int FileFFMPEG::select_video_stream(Asset *asset, int vstream)
327 {
328         if( !ff || !asset->video_data ) return 1;
329         asset->width = ff->ff_video_width(vstream);
330         asset->height = ff->ff_video_height(vstream);
331         if( (asset->video_length = ff->ff_video_frames(vstream)) < 2 )
332                 asset->video_length = asset->video_length < 0 ? 0 : -1;
333         asset->frame_rate = ff->ff_frame_rate(vstream);
334         return 0;
335 }
336
337 int FileFFMPEG::select_audio_stream(Asset *asset, int astream)
338 {
339         if( !ff || !asset->audio_data ) return 1;
340         asset->sample_rate = ff->ff_sample_rate(astream);
341         asset->audio_length = ff->ff_audio_samples(astream);
342         return 0;
343 }
344
345 int FileFFMPEG::open_file(int rd, int wr)
346 {
347         int result = 0;
348         if( ff ) return 1;
349         ff = new FFMPEG(this);
350         
351         if( rd ) {
352                 result = ff->init_decoder(asset->path);
353                 if( !result ) result = ff->open_decoder();
354                 if( !result ) {
355                         int audio_channels = ff->ff_total_audio_channels();
356                         if( audio_channels > 0 ) {
357                                 asset->audio_data = 1;
358                                 asset->channels = audio_channels;
359                                 asset->sample_rate = ff->ff_sample_rate(0);
360                                 asset->audio_length = ff->ff_audio_samples(0);
361                                 strcpy(asset->acodec, ff->ff_audio_format(0));
362                         }
363                         int video_layers = ff->ff_total_video_layers();
364                         if( video_layers > 0 ) {
365                                 asset->video_data = 1;
366                                 asset->aspect_ratio = ff->ff_aspect_ratio(0);
367                                 if (!asset->interlace_mode) asset->interlace_mode = ff->ff_interlace(0);
368                                 if ( ff->ff_video_frames(0) > 1 ) {
369 //                              ff->video_probe(1);
370                                  if (!asset->interlace_mode && (ff->interlace_from_codec) ) asset->interlace_mode = ff->video_probe(1); 
371                                 }
372                                 if( !asset->layers ) asset->layers = video_layers;
373                                 asset->actual_width = ff->ff_video_width(0);
374                                 asset->actual_height = ff->ff_video_height(0);
375                                 if( !asset->width ) asset->width = asset->actual_width;
376                                 if( !asset->height ) asset->height = asset->actual_height;
377                                 if( !asset->video_length &&
378                                     (asset->video_length = ff->ff_video_frames(0)) < 2 )
379                                         asset->video_length = asset->video_length < 0 ? 0 : -1;
380                                 if( !asset->frame_rate ) asset->frame_rate = ff->ff_frame_rate(0);
381                                 if( asset->ff_color_range < 0 )
382                                         asset->ff_color_range = ff->ff_color_range(0);
383                                 if( asset->ff_color_space < 0 )
384                                         asset->ff_color_space = ff->ff_color_space(0);
385                                 strcpy(asset->vcodec, ff->ff_video_codec(0));
386                         }
387                         IndexState *index_state = asset->index_state;
388                         index_state->read_markers(file->preferences->index_directory, asset->path);
389                 }
390         }
391         else if( wr ) {
392                 result = ff->init_encoder(asset->path);
393                 // must be in this order or dvdauthor will fail
394                 if( !result && asset->video_data )
395                         result = ff->open_encoder("video", asset->vcodec);
396                 if( !result && asset->audio_data )
397                         result = ff->open_encoder("audio", asset->acodec);
398         }
399         return result;
400 }
401
402 int FileFFMPEG::close_file()
403 {
404         delete ff;
405         ff = 0;
406         return 0;
407 }
408
409
410 int FileFFMPEG::write_samples(double **buffer, int64_t len)
411 {
412         if( !ff || len < 0 ) return -1;
413         int stream = 0;
414         int ret = ff->encode(stream, buffer, len);
415         return ret;
416 }
417
418 int FileFFMPEG::write_frames(VFrame ***frames, int len)
419 {
420         if( !ff ) return -1;
421         int ret = 0, layer = 0;
422         for(int i = 0; i < 1; i++) {
423                 for(int j = 0; j < len && !ret; j++) {
424                         VFrame *frame = frames[i][j];
425                         ret = ff->encode(layer, frame);
426                 }
427         }
428         return ret;
429 }
430
431
432 int FileFFMPEG::read_samples(double *buffer, int64_t len)
433 {
434         if( !ff || len < 0 ) return -1;
435         int ch = file->current_channel;
436         int64_t pos = file->current_sample;
437         int ret = ff->decode(ch, pos, buffer, len);
438         if( ret > 0 ) return 0;
439         memset(buffer,0,len*sizeof(*buffer));
440         return -1;
441 }
442
443 int FileFFMPEG::read_frame(VFrame *frame)
444 {
445         if( !ff ) return -1;
446         int layer = file->current_layer;
447         int64_t pos = asset->video_length >= 0 ? file->current_frame : 0;
448         int ret = ff->decode(layer, pos, frame);
449         frame->set_status(ret);
450         if( ret >= 0 ) return 0;
451         frame->clear_frame();
452         return -1;
453 }
454
455
456 int64_t FileFFMPEG::get_memory_usage()
457 {
458         return 0;
459 }
460
461 int FileFFMPEG::colormodel_supported(int colormodel)
462 {
463         return colormodel;
464 }
465
466
467 int FileFFMPEG::get_best_colormodel(int driver, int vstream)
468 {
469         if( vstream < 0 ) vstream = 0;
470         int is_mpeg = !ff ? 0 : ff->ff_video_mpeg_color_range(vstream);
471
472         switch(driver) {
473         case PLAYBACK_X11:
474         case PLAYBACK_X11_GL: return is_mpeg ? BC_YUV888 : BC_RGB888;
475         case PLAYBACK_X11_XV: return BC_YUV420P;
476         }
477
478         return BC_RGB888;
479 }
480
481 int FileFFMPEG::get_best_colormodel(Asset *asset, int driver)
482 {
483         switch(driver) {
484 // the direct X11 color model requires scaling in the codec
485         case SCREENCAPTURE:
486         case PLAYBACK_X11:
487         case PLAYBACK_X11_GL: return BC_RGB888;
488         case PLAYBACK_X11_XV: return BC_YUV420P;
489         }
490
491         return BC_YUV420P;
492 }
493
494
495 FFMPEGConfigWindow::FFMPEGConfigWindow(const char *title,
496                 BC_WindowBase *parent_window,
497                 int x, int y, int w, int h,
498                 Asset *asset, EDL *edl)
499  : BC_Window(title, x, y, w, h)
500 {
501         this->parent_window = parent_window;
502         this->asset = asset;
503         this->edl = edl;
504         avctx = 0;
505         fmt_ctx = 0;
506         ff_options_dialog = 0;
507         format_name = 0;
508         codec_name = 0;
509 }
510
511 FFMPEGConfigWindow::~FFMPEGConfigWindow()
512 {
513         delete ff_options_dialog;
514 }
515
516 void FFMPEGConfigWindow::start(AVCodecContext *avctx)
517 {
518         this->avctx = avctx;
519         ff_options_dialog->start();
520 }
521
522 void FFMPEGConfigWindow::start(AVFormatContext *fmt_ctx)
523 {
524         this->fmt_ctx = fmt_ctx;
525         ff_options_dialog->start();
526 }
527
528 //======
529
530 FFMPEGConfigAudio::FFMPEGConfigAudio(BC_WindowBase *parent_window,
531                 int x, int y, Asset *asset, EDL *edl)
532  : FFMPEGConfigWindow(_(PROGRAM_NAME ": Audio Preset"), parent_window, x, y,
533                 xS(420), yS(420), asset, edl)
534 {
535         preset_popup = 0;
536         bitrate = 0;
537         audio_options = 0;
538         format_name = asset->fformat;
539         codec_name = asset->acodec;
540 // *** CONTEXT_HELP ***
541         context_help_set_keyword("Options for Render with FFmpeg");
542 }
543
544 FFMPEGConfigAudio::~FFMPEGConfigAudio()
545 {
546         lock_window("FFMPEGConfigAudio::~FFMPEGConfigAudio");
547         delete preset_popup;
548         presets.remove_all_objects();
549         delete audio_options;
550         unlock_window();
551 }
552
553 void FFMPEGConfigAudio::read_options()
554 {
555         const char *options = audio_options->get_text();
556         int options_len = strlen(options);
557         ff_options_dialog->load_options(options, options_len);
558 }
559 void FFMPEGConfigAudio::save_options()
560 {
561         char options[BCTEXTLEN];
562         ff_options_dialog->store_options(options, sizeof(options)-1);
563         audio_options->update(options);
564 }
565
566 void FFMPEGConfigAudio::load_options()
567 {
568         FFMPEG::load_audio_options(asset, edl);
569 }
570
571 void FFMPEGConfigAudio::create_objects()
572 {
573         int x = xS(10), y = yS(10);
574         lock_window("FFMPEGConfigAudio::create_objects");
575
576         FileSystem fs;
577         char option_path[BCTEXTLEN];
578         FFMPEG::set_option_path(option_path, "audio");
579         fs.update(option_path);
580         int total_files = fs.total_files();
581         for(int i = 0; i < total_files; i++) {
582                 const char *name = fs.get_entry(i)->get_name();
583                 if( asset->fformat[0] != 0 ) {
584                         const char *ext = strrchr(name,'.');
585                         if( !ext ) continue;
586                         if( strcmp(asset->fformat, ++ext) ) continue;
587                 }
588                 presets.append(new BC_ListBoxItem(name));
589         }
590
591         if( asset->acodec[0] ) {
592                 int k = presets.size();
593                 while( --k >= 0 && strcmp(asset->acodec, presets[k]->get_text()) );
594                 if( k < 0 ) asset->acodec[0] = 0;
595         }
596
597         if( !asset->acodec[0] && presets.size() > 0 )
598                 strcpy(asset->acodec, presets[0]->get_text());
599
600         add_tool(new BC_Title(x, y, _("Preset:")));
601         y += yS(25);
602         preset_popup = new FFMPEGConfigAudioPopup(this, x, y);
603         preset_popup->create_objects();
604
605         y += yS(50);
606         bitrate = new FFMpegAudioBitrate(this, x, y, _("Bitrate:"), &asset->ff_audio_bitrate);
607         bitrate->create_objects();
608         bitrate->set_increment(1000);
609         bitrate->set_boundaries((int64_t)0, (int64_t)INT_MAX);
610         y += bitrate->get_h() + 5;
611         quality = new FFMpegAudioQuality(this, x, y, _("Quality:"), &asset->ff_audio_quality);
612         quality->create_objects();
613         quality->set_increment(1);
614         quality->set_boundaries((int64_t)-1, (int64_t)51);
615         y += quality->get_h() + yS(10);
616
617         add_subwindow(new BC_Title(x, y, _("Samples:")));
618         sample_format = new FFMpegSampleFormat(this, x+xS(90), y, xS(100), yS(120));
619         sample_format->create_objects();
620         if( asset->acodec[0] ) {
621                 sample_format->update_formats();
622                 if( !asset->ff_audio_options[0] )
623                         load_options();
624         }
625         if( !asset->ff_sample_format[0] ) strcpy(asset->ff_sample_format, _("None"));
626         sample_format->update(asset->ff_sample_format);
627         y += sample_format->get_h() + yS(10);
628
629         BC_Title *title = new BC_Title(x, y, _("Audio Options:"));
630         add_subwindow(title);
631
632         ff_options_dialog = new FFOptionsAudioDialog(this);
633         int x1 = x + title->get_w() + xS(8);
634         add_subwindow(view_audio = new FFOptionsViewAudio(this, x1, y, _("view")));
635         x1 += x + view_audio->get_w() + xS(20);
636         view_format = new FFOptionsViewFormat(this, edl, asset, x1, y, _("format"));
637         add_subwindow(view_format);
638
639         y += yS(25);
640         audio_options = new FFAudioOptions(this, x, y, get_w()-x-xS(20), 8,
641                  sizeof(asset->ff_audio_options)-1, asset->ff_audio_options);
642         audio_options->create_objects();
643         add_subwindow(new BC_OKButton(this));
644         add_subwindow(new BC_CancelButton(this));
645         show_window(1);
646
647         bitrate->update_param("cin_bitrate", asset->ff_audio_options);
648         quality->update_param("cin_quality", asset->ff_audio_options);
649
650         if( asset->ff_audio_bitrate > 0 ) quality->disable();
651         else if( asset->ff_audio_quality >= 0 ) bitrate->disable();
652
653         unlock_window();
654 }
655
656 int FFMPEGConfigAudio::close_event()
657 {
658         set_done(1);
659         return 1;
660 }
661
662 FFAudioOptions::FFAudioOptions(FFMPEGConfigAudio *audio_popup,
663         int x, int y, int w, int rows, int size, char *text)
664  : BC_ScrollTextBox(audio_popup, x, y, w, rows, text, size)
665 {
666         this->audio_popup = audio_popup;
667 }
668
669
670 FFMPEGConfigAudioPopup::FFMPEGConfigAudioPopup(FFMPEGConfigAudio *popup, int x, int y)
671  : BC_PopupTextBox(popup, &popup->presets, popup->asset->acodec, x, y, xS(300), yS(300))
672 {
673         this->popup = popup;
674 }
675
676 int FFMPEGConfigAudioPopup::handle_event()
677 {
678         strcpy(popup->asset->acodec, get_text());
679         popup->sample_format->update_formats();
680         Asset *asset = popup->asset;
681         asset->ff_audio_bitrate = 0;  asset->ff_audio_quality = -1;
682         popup->load_options();
683         popup->audio_options->update(asset->ff_audio_options);
684         popup->audio_options->set_text_row(0);
685
686         popup->bitrate->update_param("cin_bitrate", asset->ff_audio_options);
687         popup->quality->update_param("cin_quality", asset->ff_audio_options);
688         popup->sample_format->update(asset->ff_sample_format);
689         return 1;
690 }
691
692 //======
693
694 FFMPEGConfigVideo::FFMPEGConfigVideo(BC_WindowBase *parent_window,
695                 int x, int y, Asset *asset, EDL *edl)
696  : FFMPEGConfigWindow(_(PROGRAM_NAME ": Video Preset"), parent_window, x, y,
697                 xS(420), yS(420), asset, edl)
698 {
699         preset_popup = 0;
700         pixel_format = 0;
701         format_name = asset->fformat;
702         codec_name = asset->vcodec;
703
704         bitrate = 0;
705         quality = 0;
706         video_options = 0;
707 // *** CONTEXT_HELP ***
708         context_help_set_keyword("Options for Render with FFmpeg");
709 }
710
711 FFMPEGConfigVideo::~FFMPEGConfigVideo()
712 {
713         lock_window("FFMPEGConfigVideo::~FFMPEGConfigVideo");
714         delete preset_popup;
715         delete pixel_format;
716         delete video_options;
717         presets.remove_all_objects();
718         unlock_window();
719 }
720
721 void FFMPEGConfigVideo::read_options()
722 {
723         const char *options = video_options->get_text();
724         int options_len = strlen(options);
725         ff_options_dialog->load_options(options, options_len);
726 }
727 void FFMPEGConfigVideo::save_options()
728 {
729         char options[BCTEXTLEN];
730         ff_options_dialog->store_options(options, sizeof(options)-1);
731         video_options->update(options);
732 }
733
734 void FFMPEGConfigVideo::load_options()
735 {
736         FFMPEG::load_video_options(asset, edl);
737 }
738
739 void FFMPEGConfigVideo::create_objects()
740 {
741         int x = xS(10), y = yS(10);
742         lock_window("FFMPEGConfigVideo::create_objects");
743
744         add_subwindow(new BC_Title(x, y, _("Compression:")));
745         y += yS(25);
746
747         FileSystem fs;
748         char option_path[BCTEXTLEN];
749         FFMPEG::set_option_path(option_path, "video");
750         fs.update(option_path);
751         int total_files = fs.total_files();
752         for(int i = 0; i < total_files; i++) {
753                 const char *name = fs.get_entry(i)->get_name();
754                 if( asset->fformat[0] != 0 ) {
755                         const char *ext = strrchr(name,'.');
756                         if( !ext ) continue;
757                         if( strcmp(asset->fformat, ++ext) ) continue;
758                 }
759                 presets.append(new BC_ListBoxItem(name));
760         }
761
762         if( asset->vcodec[0] ) {
763                 int k = presets.size();
764                 while( --k >= 0 && strcmp(asset->vcodec, presets[k]->get_text()) );
765                 if( k < 0 ) asset->vcodec[0] = 0;
766         }
767
768         if( !asset->vcodec[0] && presets.size() > 0 )
769                 strcpy(asset->vcodec, presets[0]->get_text());
770
771         preset_popup = new FFMPEGConfigVideoPopup(this, x, y);
772         preset_popup->create_objects();
773
774         if( asset->ff_video_bitrate > 0 && asset->ff_video_quality >= 0 ) {
775                 asset->ff_video_bitrate = 0;  asset->ff_video_quality = -1;
776         }
777
778         y += yS(50);
779         bitrate = new FFMpegVideoBitrate(this, x, y, _("Bitrate:"), &asset->ff_video_bitrate);
780         bitrate->create_objects();
781         bitrate->set_increment(100000);
782         bitrate->set_boundaries((int64_t)0, (int64_t)INT_MAX);
783         y += bitrate->get_h() + 5;
784         quality = new FFMpegVideoQuality(this, x, y, _("Quality:"), &asset->ff_video_quality);
785         quality->create_objects();
786         quality->set_increment(1);
787         quality->set_boundaries((int64_t)-1, (int64_t)51);
788         y += quality->get_h() + yS(10);
789
790         add_subwindow(new BC_Title(x, y, _("Pixels:")));
791         pixel_format = new FFMpegPixelFormat(this, x+xS(90), y, xS(100), yS(120));
792         pixel_format->create_objects();
793         if( asset->vcodec[0] ) {
794                 pixel_format->update_formats();
795                 if( !asset->ff_video_options[0] )
796                         load_options();
797         }
798         if( !asset->ff_pixel_format[0] ) strcpy(asset->ff_pixel_format, _("None"));
799         pixel_format->update(asset->ff_pixel_format);
800         y += pixel_format->get_h() + yS(10);
801
802         BC_Title *title = new BC_Title(x, y, _("Video Options:"));
803         add_subwindow(title);
804
805         ff_options_dialog = new FFOptionsVideoDialog(this);
806         int x1 = x + title->get_w() + 8;
807         add_subwindow(view_video = new FFOptionsViewVideo(this, x1, y, _("view")));
808         x1 += x + view_video->get_w() + xS(20);
809         view_format = new FFOptionsViewFormat(this, edl, asset, x1, y, _("format"));
810         add_subwindow(view_format);
811
812         y += yS(25);
813         video_options = new FFVideoOptions(this, x, y, get_w()-x-xS(20), 8,
814                  sizeof(asset->ff_video_options)-1, asset->ff_video_options);
815         video_options->create_objects();
816         add_subwindow(new BC_OKButton(this));
817         add_subwindow(new BC_CancelButton(this));
818         show_window(1);
819
820         bitrate->update_param("cin_bitrate", asset->ff_video_options);
821         quality->update_param("cin_quality", asset->ff_video_options);
822
823         if( asset->ff_video_bitrate > 0 ) quality->disable();
824         else if( asset->ff_video_quality >= 0 ) bitrate->disable();
825         unlock_window();
826 }
827
828 int FFMPEGConfigVideo::close_event()
829 {
830         set_done(1);
831         return 1;
832 }
833
834 FFVideoOptions::FFVideoOptions(FFMPEGConfigVideo *video_popup,
835         int x, int y, int w, int rows, int size, char *text)
836  : BC_ScrollTextBox(video_popup, x, y, w, rows, text, size)
837 {
838         this->video_popup = video_popup;
839 }
840
841
842 FFMPEGConfigVideoPopup::FFMPEGConfigVideoPopup(FFMPEGConfigVideo *popup, int x, int y)
843  : BC_PopupTextBox(popup, &popup->presets, popup->asset->vcodec, x, y, xS(300), yS(300))
844 {
845         this->popup = popup;
846 }
847
848 int FFMPEGConfigVideoPopup::handle_event()
849 {
850         strcpy(popup->asset->vcodec, get_text());
851         popup->pixel_format->update_formats();
852         Asset *asset = popup->asset;
853         asset->ff_video_bitrate = 0;  asset->ff_video_quality = -1;
854         popup->load_options();
855         popup->video_options->update(asset->ff_video_options);
856         popup->video_options->set_text_row(0);
857
858         popup->bitrate->update_param("cin_bitrate", asset->ff_video_options);
859         popup->quality->update_param("cin_quality", asset->ff_video_options);
860         popup->pixel_format->update(asset->ff_pixel_format);
861         return 1;
862 }
863
864 //======
865
866 FFMPEGConfigFormat::FFMPEGConfigFormat(FFOptionsFormatViewDialog *view_dialog,
867                 int x, int y, Asset *asset, EDL *edl)
868  : FFMPEGConfigWindow(_(PROGRAM_NAME ": Format Preset"),
869                 view_dialog->view_format, x, y, xS(420), yS(300), asset, edl)
870 {
871         this->view_dialog = view_dialog;
872         format_options = 0;
873         format_name = asset->fformat;
874         codec_name = 0;
875 // *** CONTEXT_HELP ***
876         context_help_set_keyword("Modifying FFmpeg Format Options");
877 }
878
879 FFMPEGConfigFormat::~FFMPEGConfigFormat()
880 {
881         lock_window("FFMPEGConfigFormat::~FFMPEGConfigFormat");
882         delete format_options;
883         unlock_window();
884 }
885
886 void FFMPEGConfigFormat::read_options()
887 {
888         const char *options = format_options->get_text();
889         int options_len = strlen(options);
890         ff_options_dialog->load_options(options, options_len);
891 }
892 void FFMPEGConfigFormat::save_options()
893 {
894         char options[BCTEXTLEN];
895         ff_options_dialog->store_options(options, sizeof(options)-1);
896         format_options->update(options);
897 }
898 void FFMPEGConfigFormat::save_changes()
899 {
900         read_options();
901         char *options = asset->ff_format_options;
902         int options_len = sizeof(asset->ff_format_options)-1;
903         ff_options_dialog->store_options(options, options_len);
904 }
905
906 void FFMPEGConfigFormat::load_options()
907 {
908         Asset *asset = view_dialog->view_format->asset;
909         EDL *edl = view_dialog->view_format->edl;
910         FFMPEG::load_format_options(asset, edl);
911 }
912
913 void FFMPEGConfigFormat::create_objects()
914 {
915         int x = xS(10), y = yS(10);
916         lock_window("FFMPEGConfigFormat::create_objects");
917         Asset *asset = view_dialog->view_format->asset;
918         BC_Title *title;
919         add_subwindow(title = new BC_Title(x, y, _("Format:")));
920         int x1 = x + title->get_w() + 8;
921         add_subwindow(new BC_Title(x1, y, asset->fformat));
922         y += yS(25);
923
924         add_subwindow(title = new BC_Title(x, y, _("Format Options:")));
925
926         ff_options_dialog = new FFOptionsFormatDialog(this);
927         x1 = x + title->get_w() + 8;
928         add_subwindow(new FFOptionsFormatView(this, x1, y, _("view")));
929
930         y += yS(25);
931         format_options = new FFFormatOptions(this, x, y, get_w()-x-xS(20), 8,
932                  sizeof(asset->ff_format_options)-1, asset->ff_format_options);
933         format_options->create_objects();
934         add_subwindow(new BC_OKButton(this));
935         add_subwindow(new BC_CancelButton(this));
936         show_window(1);
937         unlock_window();
938 }
939
940 int FFMPEGConfigFormat::close_event()
941 {
942         set_done(1);
943         return 1;
944 }
945
946 FFFormatOptions::FFFormatOptions(FFMPEGConfigFormat *format_popup,
947         int x, int y, int w, int rows, int size, char *text)
948  : BC_ScrollTextBox(format_popup, x, y, w, rows, text, size)
949 {
950         this->format_popup = format_popup;
951 }
952
953 //======
954
955 FFMPEGScanProgress::FFMPEGScanProgress(IndexFile *index_file, MainProgressBar *progress_bar,
956                 const char *title, int64_t length, int64_t *position, int *canceled)
957  : Thread(1, 0, 0)
958 {
959         this->index_file = index_file;
960         this->progress_bar = progress_bar;
961         strcpy(this->progress_title, title);
962         this->length = length;
963         this->position = position;
964         this->canceled = canceled;
965         done = 0;
966         start();
967 }
968
969 FFMPEGScanProgress::~FFMPEGScanProgress()
970 {
971         done = 1;
972         cancel();
973         join();
974 }
975
976 void FFMPEGScanProgress::run()
977 {
978         while( !done ) {
979                 if( progress_bar->update(*position) ) {
980                         *canceled = done = 1;
981                         break;
982                 }
983                 index_file->redraw_edits(0);
984                 usleep(500000);
985         }
986 }
987
988 int FileFFMPEG::get_index(IndexFile *index_file, MainProgressBar *progress_bar)
989 {
990         if( !ff ) return -1;
991         if( !file->preferences->ffmpeg_marker_indexes ) return 1;
992
993         IndexState *index_state = index_file->get_state();
994         if( index_state->index_status != INDEX_NOTTESTED ) return 0;
995         index_state->reset_index();
996         index_state->reset_markers();
997         index_state->index_status = INDEX_BUILDING;
998
999         for( int aidx=0; aidx<ff->ffaudio.size(); ++aidx ) {
1000                 FFAudioStream *aud = ff->ffaudio[aidx];
1001                 index_state->add_audio_stream(aud->channels, aud->length);
1002         }
1003
1004         FileSystem fs;
1005         int64_t file_bytes = fs.get_size(ff->fmt_ctx->url);
1006         char *index_path = index_file->index_filename;
1007
1008         int canceled = 0;
1009         int64_t scan_position = 0;
1010         FFMPEGScanProgress *scan_progress = 0;
1011         if( progress_bar ) {
1012                 char progress_title[BCTEXTLEN];
1013                 sprintf(progress_title, _("Creating %s\n"), index_path);
1014                 progress_bar->update_title(progress_title, 1);
1015                 progress_bar->update_length(file_bytes);
1016                 scan_progress = new FFMPEGScanProgress(index_file,
1017                                 progress_bar, progress_title, file_bytes,
1018                                 &scan_position, &canceled);
1019         }
1020
1021         index_state->index_bytes = file_bytes;
1022         index_state->init_scan(file->preferences->index_size);
1023
1024         if( ff->scan(index_state, &scan_position, &canceled) || canceled ) {
1025                 index_state->reset_index();
1026                 index_state->reset_markers();
1027                 canceled = 1;
1028         }
1029
1030         delete scan_progress;
1031         if( canceled ) return 1;
1032
1033         index_state->marker_status = MARKERS_READY;
1034         return index_state->create_index(index_path, asset);
1035 }
1036
1037
1038 FFOptions_OptPanel::
1039 FFOptions_OptPanel(FFOptionsWindow *fwin, int x, int y, int w, int h)
1040  : BC_ListBox(x, y, w, h, LISTBOX_TEXT), opts(items[0]), vals(items[1])
1041 {
1042         this->fwin = fwin;
1043         update();  // init col/wid/columns
1044 }
1045
1046 FFOptions_OptPanel::
1047 ~FFOptions_OptPanel()
1048 {
1049 }
1050
1051 void FFOptions_OptPanel::create_objects()
1052 {
1053         const char *cols[] = { _("option"), _("value"), };
1054         const int col1_w = xS(150);
1055         int wids[] = { col1_w, get_w()-col1_w };
1056         BC_ListBox::update(&items[0], &cols[0], &wids[0], sizeof(items)/sizeof(items[0]));
1057 }
1058
1059 int FFOptions_OptPanel::update()
1060 {
1061         opts.remove_all();
1062         vals.remove_all();
1063         FFOptions &options = fwin->options;
1064         for( int i=0; i<options.size(); ++i ) {
1065                 FFOptions_Opt *opt = options[i];
1066                 opts.append(opt->item_name);
1067                 vals.append(opt->item_value);
1068         }
1069         draw_items(1);
1070         return 0;
1071 }
1072
1073 int FFOptions_OptPanel::cursor_leave_event()
1074 {
1075         hide_tooltip();
1076         return 0;
1077 }
1078
1079
1080 FFOptions_OptName::FFOptions_OptName(FFOptions_Opt *opt, const char *nm)
1081 {
1082         this->opt = opt;
1083         set_text(nm);
1084 }
1085
1086 FFOptions_OptName::~FFOptions_OptName()
1087 {
1088 }
1089
1090 FFOptions_OptValue::FFOptions_OptValue(FFOptions_Opt *opt)
1091 {
1092         this->opt = opt;
1093 }
1094
1095
1096 void FFOptions_OptValue::update()
1097 {
1098         if( !opt ) return;
1099         char val[BCTEXTLEN];  val[0] = 0;
1100         opt->get(val, sizeof(val));
1101         update(val);
1102 }
1103
1104 void FFOptions_OptValue::update(const char *v)
1105 {
1106         set_text(v);
1107 }
1108
1109 FFOptions_Opt::FFOptions_Opt(FFOptions *options, const AVOption *opt, const char *nm)
1110 {
1111         this->options = options;
1112         this->opt = opt;
1113         item_name = new FFOptions_OptName(this, nm);
1114         item_value = new FFOptions_OptValue(this);
1115 }
1116
1117 FFOptions_Opt::~FFOptions_Opt()
1118 {
1119         delete item_name;
1120         delete item_value;
1121 }
1122
1123 char *FFOptions_Opt::get(char *vp, int sz)
1124 {
1125         char *cp = vp;
1126         *cp = 0;
1127         if( !opt ) return cp;
1128
1129         void *obj = (void *)options->obj;
1130         uint8_t *bp = 0;
1131         if( av_opt_get(obj, opt->name, 0, &bp) >= 0 && bp != 0 ) {
1132                 const char *val = (const char *)bp;
1133                 if( opt->unit && *val ) {
1134                         int id = atoi(val);
1135                         const char *uid = unit_name(id);
1136                         if( uid ) val = uid;
1137                 }
1138                 cp = sz >= 0 ? strncpy(vp,val,sz) : strcpy(vp, val);
1139                 if( sz > 0 ) vp[sz-1] = 0;
1140                 av_freep(&bp);
1141         }
1142
1143         return cp;
1144 }
1145
1146 void FFOptions_Opt::set(const char *val)
1147 {
1148         void *obj = (void *)options->obj;
1149         if( !obj || !opt ) return;
1150         av_opt_set(obj, opt->name, val, 0);
1151 }
1152
1153
1154 FFOptionsKindItem::FFOptionsKindItem(FFOptionsKind *kind, const char *text, int idx)
1155  : BC_MenuItem(text)
1156 {
1157         this->kind = kind;
1158         this->idx = idx;
1159 }
1160
1161 FFOptionsKindItem::~FFOptionsKindItem()
1162 {
1163 }
1164
1165 int FFOptionsKindItem::handle_event()
1166 {
1167         FFOptionsWindow *fwin = kind->fwin;
1168         FFOptions &options = fwin->options;
1169         options.initialize(fwin, idx);
1170         kind->set_text(get_text());
1171         fwin->draw();
1172         return 1;
1173 }
1174
1175 const char *FFOptionsKind::kinds[] = {
1176         N_("codec"),    // FF_KIND_CODEC
1177         N_("format"),   // FF_KIND_FORMAT
1178         N_("ffmpeg"),   // FF_KIND_FFMPEG
1179 };
1180
1181 FFOptionsKind::
1182 FFOptionsKind(FFOptionsWindow *fwin, int x, int y, int w)
1183  : BC_PopupMenu(x, y, w, "")
1184 {
1185         this->fwin = fwin;
1186 }
1187
1188 FFOptionsKind::
1189 ~FFOptionsKind()
1190 {
1191 }
1192
1193 void FFOptionsKind::create_objects()
1194 {
1195         add_item(new FFOptionsKindItem(this, _(kinds[FF_KIND_CODEC]), FF_KIND_CODEC));
1196         add_item(new FFOptionsKindItem(this, _(kinds[FF_KIND_FFMPEG]), FF_KIND_FFMPEG));
1197 }
1198
1199 int FFOptionsKind::handle_event()
1200 {
1201         return 1;
1202 }
1203
1204 void FFOptionsKind::set(int k)
1205 {
1206         this->kind = k;
1207         set_text(_(kinds[k]));
1208 }
1209
1210 FFOptionsText::
1211 FFOptionsText(FFOptionsWindow *fwin, int x, int y, int w)
1212  : BC_TextBox(x, y, w, 1, (char *)"")
1213 {
1214         this->fwin = fwin;
1215 }
1216
1217 FFOptionsText::
1218 ~FFOptionsText()
1219 {
1220 }
1221
1222 int FFOptionsText::handle_event()
1223 {
1224         return 0;
1225 }
1226
1227 FFOptionsUnits::
1228 FFOptionsUnits(FFOptionsWindow *fwin, int x, int y, int w)
1229  : BC_PopupMenu(x, y, w, "")
1230 {
1231         this->fwin = fwin;
1232 }
1233
1234 FFOptionsUnits::
1235 ~FFOptionsUnits()
1236 {
1237 }
1238
1239 int FFOptionsUnits::handle_event()
1240 {
1241         const char *text = get_text();
1242         if( text && fwin->selected ) {
1243                 fwin->selected->set(text);
1244                 fwin->selected->item_value->update();
1245                 av_dict_set(&fwin->dialog->ff_opts,
1246                         fwin->selected->item_name->get_text(),
1247                         fwin->selected->item_value->get_text(), 0);
1248                 fwin->draw();
1249         }
1250         return 1;
1251 }
1252
1253 FFOptionsApply::
1254 FFOptionsApply(FFOptionsWindow *fwin, int x, int y)
1255  : BC_GenericButton(x, y, _("Apply"))
1256 {
1257         this->fwin = fwin;
1258 }
1259
1260 FFOptionsApply::
1261 ~FFOptionsApply()
1262 {
1263 }
1264
1265 int FFOptionsApply::handle_event()
1266 {
1267         const char *text = fwin->text->get_text();
1268         if( text && fwin->selected ) {
1269                 fwin->selected->set(text);
1270                 fwin->selected->item_value->update();
1271                 av_dict_set(&fwin->dialog->ff_opts,
1272                         fwin->selected->item_name->get_text(),
1273                         fwin->selected->item_value->get_text(), 0);
1274                 fwin->draw();
1275         }
1276         return 1;
1277 }
1278
1279 FFOptions::FFOptions()
1280 {
1281         obj = 0;
1282 }
1283
1284 FFOptions::~FFOptions()
1285 {
1286         remove_all_objects();
1287 }
1288
1289 int FFOptions::cmpr(const void *a, const void *b)
1290 {
1291         FFOptions_Opt *ap = *(FFOptions_Opt **)a;
1292         FFOptions_Opt *bp = *(FFOptions_Opt **)b;
1293         const char *vap = ap->item_name->get_text();
1294         const char *vbp = bp->item_name->get_text();
1295         return strcmp(vap, vbp);
1296 }
1297
1298 void FFOptions::initialize(FFOptionsWindow *win, int kind)
1299 {
1300         remove_all_objects();
1301         win->selected = 0;
1302         const void *obj = 0;
1303         switch( kind ) {
1304         case FF_KIND_CODEC:
1305                 obj = (const void *)win->dialog->cfg_window->avctx->priv_data;
1306                 break;
1307         case FF_KIND_FFMPEG:
1308                 obj = (const void *)win->dialog->cfg_window->avctx;
1309                 break;
1310         case FF_KIND_FORMAT:
1311                 obj = (const void *)win->dialog->cfg_window->fmt_ctx->priv_data;
1312                 break;
1313         }
1314         this->win = win;
1315         this->obj = obj;
1316         if( obj ) {
1317                 FFOptions &conf = *this;
1318                 const AVOption *opt = 0;
1319                 while( (opt=av_opt_next(obj, opt)) != 0 ) {
1320                         if( opt->type == AV_OPT_TYPE_CONST ) continue;
1321                         int dupl = 0;
1322                         for( int i=0; !dupl && i<size(); ++i ) {
1323                                 FFOptions_Opt *fopt = conf[i];
1324                                 const AVOption *op = fopt->opt;
1325                                 if( op->offset != opt->offset ) continue;
1326                                 if( op->type != opt->type ) continue;
1327                                 dupl = 1;
1328                                 if( strlen(op->name) < strlen(opt->name) )
1329                                         fopt->opt = opt;
1330                         }
1331                         if( dupl ) continue;
1332                         FFOptions_Opt *fopt = new FFOptions_Opt(this, opt, opt->name);
1333                         append(fopt);
1334                         AVDictionaryEntry *elem = av_dict_get(win->dialog->ff_opts,
1335                                         opt->name, 0, AV_DICT_IGNORE_SUFFIX);
1336                         if( elem && elem->value ) fopt->set(elem->value);
1337                         char val[BCTEXTLEN], *vp = fopt->get(val, sizeof(val));
1338                         fopt->item_value->update(vp);
1339                 }
1340                 qsort(&values[0],size(),sizeof(values[0]),cmpr);
1341         }
1342         win->panel->update();
1343         win->panel->set_yposition(0);
1344 }
1345
1346 int FFOptions::update()
1347 {
1348         int ret = 0;
1349         FFOptions &conf = *this;
1350
1351         for( int i=0; i<size(); ++i ) {
1352                 FFOptions_Opt *fopt = conf[i];
1353                 char val[BCTEXTLEN], *vp = fopt->get(val, sizeof(val));
1354                 if( !vp || !strcmp(val, fopt->item_value->get_text()) ) continue;
1355                 fopt->item_value->update(val);
1356                 ++ret;
1357         }
1358         return ret;
1359 }
1360
1361 void FFOptions::dump(FILE *fp)
1362 {
1363         if( !obj ) return;
1364         const AVOption *opt = 0;
1365         FFOptions &conf = *this;
1366
1367         while( (opt=av_opt_next(obj, opt)) != 0 ) {
1368                 if( opt->type == AV_OPT_TYPE_CONST ) continue;
1369                 int k = size();
1370                 while( --k >= 0 && strcmp(opt->name, conf[k]->opt->name) );
1371                 if( k < 0 ) continue;
1372                 FFOptions_Opt *fopt = conf[k];
1373                 char val[BCTEXTLEN], *vp = fopt->get(val,sizeof(val));
1374                 fprintf(fp, "  %s:=%s", opt->name, vp);
1375                 if( opt->unit ) {
1376                         char unt[BCTEXTLEN], *up = unt;
1377                         fopt->units(up);
1378                         fprintf(fp, "%s", unt);
1379                 }
1380                 fprintf(fp, "\n");
1381         }
1382 }
1383
1384
1385 void FFOptionsWindow::update(FFOptions_Opt *opt)
1386 {
1387         if( selected != opt ) {
1388                 if( selected ) selected->item_name->set_selected(0);
1389                 selected = opt;
1390                 if( selected ) selected->item_name->set_selected(1);
1391         }
1392         clear_box(0,0, 0,panel->get_y());
1393         char str[BCTEXTLEN], *sp;
1394         *(sp=str) = 0;
1395         if( opt ) opt->types(sp);
1396         type->update(str);
1397         *(sp=str) = 0;
1398         if( opt ) opt->ranges(sp);
1399         range->update(str);
1400         while( units->total_items() ) units->del_item(0);
1401         char unit[BCSTRLEN];  strcpy(unit, "()");
1402         if( opt && opt->opt ) {
1403                 ArrayList<const char *> names;
1404                 int n = 0;
1405                 if( opt->opt->unit ) {
1406                         n = opt->units(names);
1407                         if( n > 0 ) strcpy(unit,opt->opt->unit);
1408                 }
1409                 for( int i=0; i<n; ++i )
1410                         units->add_item(new BC_MenuItem(names[i], 0));
1411         }
1412         units->set_text(unit);
1413         char val[BCTEXTLEN];  val[0] = 0;
1414         if( opt ) opt->get(val, sizeof(val));
1415         text->update(val);
1416
1417         panel->update();
1418 }
1419
1420 void FFOptions_OptPanel::show_tip(const char *tip)
1421 {
1422         if( !tip ) return;
1423         int len = strlen(tip);
1424         if( len > (int)sizeof(tip_text)-1 ) len = sizeof(tip_text)-1;
1425         strncpy(tip_text,tip,len);
1426         tip_text[len] = 0;
1427         int line_limit = 60;
1428         int limit2 = line_limit/2;
1429         int limit4 = line_limit/4-2;
1430         char *cp = tip_text, *dp = cp+len;
1431         int n;  char *bp, *ep, *pp, *sp;
1432         while( cp < dp ) {
1433                 for( ep=cp; ep<dp && *ep!='\n'; ++ep );
1434                 // target about half remaining line, constrain line_limit
1435                 if( (n=(ep-1-cp)/2) < limit2 || n > line_limit )
1436                         n = line_limit;
1437                 // search for last punct, last space before line_limit
1438                 for( bp=cp, pp=sp=0; --n>=0 && cp<ep; ++cp ) {
1439                         if( ispunct(*cp) && isspace(*(cp+1)) ) pp = cp;
1440                         else if( isspace(*cp) ) sp = cp;
1441                 }
1442                 // line not empty
1443                 if( cp < ep ) {
1444                         // first, after punctuation
1445                         if( pp && pp-bp >= limit4 )
1446                                 cp = pp+1;
1447                         // then, on spaces
1448                         else if( sp ) {
1449                                 cp = sp;
1450                         }
1451                         // last, on next space
1452                         else {
1453                                 while( cp<dp && !isspace(*cp) ) ++cp;
1454                         }
1455                         // add new line
1456                         if( !*cp ) break;
1457                         *cp++ = '\n';
1458                 }
1459         }
1460         fwin->panel->set_tooltip(tip_text);
1461         fwin->panel->show_tooltip();
1462 }
1463
1464 int FFOptions_OptPanel::selection_changed()
1465 {
1466         FFOptions_Opt *opt = 0;
1467         BC_ListBoxItem *item = get_selection(0, 0);
1468         if( item ) {
1469                 FFOptions_OptName *opt_name = (FFOptions_OptName *)item;
1470                 opt = opt_name->opt;
1471         }
1472         fwin->update(opt);
1473         if( opt ) show_tip(opt->tip());
1474         return 1;
1475 }
1476
1477
1478 int FFOptions_Opt::types(char *rp)
1479 {
1480         const char *cp = "";
1481         if( opt ) switch (opt->type) {
1482         case AV_OPT_TYPE_FLAGS: cp = N_("<flags>");  break;
1483         case AV_OPT_TYPE_INT: cp = N_("<int>"); break;
1484         case AV_OPT_TYPE_INT64: cp = N_("<int64>"); break;
1485         case AV_OPT_TYPE_DOUBLE: cp = N_("<double>"); break;
1486         case AV_OPT_TYPE_FLOAT: cp = N_("<float>"); break;
1487         case AV_OPT_TYPE_STRING: cp = N_("<string>"); break;
1488         case AV_OPT_TYPE_RATIONAL: cp = N_("<rational>"); break;
1489         case AV_OPT_TYPE_BINARY: cp = N_("<binary>"); break;
1490         case AV_OPT_TYPE_IMAGE_SIZE: cp = N_("<image_size>"); break;
1491         case AV_OPT_TYPE_VIDEO_RATE: cp = N_("<video_rate>"); break;
1492         case AV_OPT_TYPE_PIXEL_FMT: cp = N_("<pix_fmt>"); break;
1493         case AV_OPT_TYPE_SAMPLE_FMT: cp = N_("<sample_fmt>"); break;
1494         case AV_OPT_TYPE_DURATION: cp = N_("<duration>"); break;
1495         case AV_OPT_TYPE_COLOR: cp = N_("<color>"); break;
1496         case AV_OPT_TYPE_CHANNEL_LAYOUT: cp = N_("<channel_layout>");  break;
1497         case AV_OPT_TYPE_BOOL: cp = N_("<bool>");  break;
1498         default: cp = N_("<undef>");  break;
1499         }
1500         return sprintf(rp, "%s", _(cp));
1501 }
1502 int FFOptions_Opt::scalar(double d, char *rp)
1503 {
1504         const char *cp = 0;
1505              if( d == INT_MAX ) cp = "INT_MAX";
1506         else if( d == INT_MIN ) cp = "INT_MIN";
1507         else if( d == UINT32_MAX ) cp = "UINT32_MAX";
1508         else if( d == (double)INT64_MAX ) cp = "I64_MAX";
1509         else if( d == INT64_MIN ) cp = "I64_MIN";
1510         else if( d == FLT_MAX ) cp = "FLT_MAX";
1511         else if( d == FLT_MIN ) cp = "FLT_MIN";
1512         else if( d == -FLT_MAX ) cp = "-FLT_MAX";
1513         else if( d == -FLT_MIN ) cp = "-FLT_MIN";
1514         else if( d == DBL_MAX ) cp = "DBL_MAX";
1515         else if( d == DBL_MIN ) cp = "DBL_MIN";
1516         else if( d == -DBL_MAX ) cp = "-DBL_MAX";
1517         else if( d == -DBL_MIN ) cp = "-DBL_MIN";
1518         else if( d == 0 ) cp = signbit(d) ? "-0" : "0";
1519         else if( isnan(d) ) cp = signbit(d) ? "-NAN" : "NAN";
1520         else if( isinf(d) ) cp = signbit(d) ? "-INF" : "INF";
1521         else return sprintf(rp, "%g", d);
1522         return sprintf(rp, "%s", cp);
1523 }
1524
1525 int FFOptions_Opt::ranges(char *rp)
1526 {
1527         if( !opt ) return 0;
1528         void *obj = (void *)options->obj;
1529         if( !obj ) return 0;
1530
1531         switch (opt->type) {
1532         case AV_OPT_TYPE_INT:
1533         case AV_OPT_TYPE_INT64:
1534         case AV_OPT_TYPE_DOUBLE:
1535         case AV_OPT_TYPE_FLOAT: break;
1536         default: return 0;;
1537         }
1538         AVOptionRanges *r = 0;
1539         char *cp = rp;
1540         if( av_opt_query_ranges(&r, obj, opt->name, AV_OPT_SEARCH_FAKE_OBJ) < 0 ) return 0;
1541         for( int i=0; i<r->nb_ranges; ++i ) {
1542                 cp += sprintf(cp, " (");  cp += scalar(r->range[i]->value_min, cp);
1543                 cp += sprintf(cp, "..");  cp += scalar(r->range[i]->value_max, cp);
1544                 cp += sprintf(cp, ")");
1545         }
1546         av_opt_freep_ranges(&r);
1547         return cp - rp;
1548 }
1549
1550 int FFOptions_Opt::units(ArrayList<const char *> &names)
1551 {
1552         if( !opt || !opt->unit ) return 0;
1553         const void *obj = options->obj;
1554         if( !obj ) return 0;
1555
1556         ArrayList<const AVOption *> opts;
1557         const AVOption *opt = NULL;
1558         while( (opt=av_opt_next(obj, opt)) != 0 ) {
1559                 if( !opt->unit ) continue;
1560                 if( opt->type != AV_OPT_TYPE_CONST ) continue;
1561                 if( strcmp(this->opt->unit, opt->unit) ) continue;
1562                 int i = opts.size();
1563                 while( --i >= 0 ) {
1564                         if( opts[i]->default_val.i64 != opt->default_val.i64 ) continue;
1565                         if( strlen(opts[i]->name) < strlen(opt->name) ) opts[i] = opt;
1566                         break;
1567                 }
1568                 if( i >= 0 ) continue;
1569                 opts.append(opt);
1570         }
1571
1572         for( int i=0; i<opts.size(); ++i )
1573                 names.append(opts[i]->name);
1574
1575         return names.size();
1576 }
1577
1578 int FFOptions_Opt::units(char *rp)
1579 {
1580         ArrayList<const char *> names;
1581         int n = units(names);
1582         if( !n ) return 0;
1583         char *cp = rp;
1584         cp += sprintf(cp, " [%s:", this->opt->unit);
1585         for( int i=0; i<n; ++i )
1586                 cp += sprintf(cp, " %s", names[i]);
1587         cp += sprintf(cp, "]:");
1588         return cp - rp;
1589 }
1590
1591 const char *FFOptions_Opt::unit_name(int id)
1592 {
1593         if( !opt || !opt->unit ) return 0;
1594         const void *obj = options->obj;
1595         if( !obj ) return 0;
1596
1597         const char *ret = 0;
1598         const AVOption *opt = NULL;
1599         while( (opt=av_opt_next(obj, opt)) != 0 ) {
1600                 if( !opt->unit ) continue;
1601                 if( opt->type != AV_OPT_TYPE_CONST ) continue;
1602                 if( strcmp(this->opt->unit, opt->unit) ) continue;
1603                 if( opt->default_val.i64 != id ) continue;
1604                 if( !ret ) { ret = opt->name;  continue; }
1605                 if( strlen(ret) < strlen(opt->name) ) ret = opt->name;
1606         }
1607
1608         return ret;
1609 }
1610
1611 const char *FFOptions_Opt::tip()
1612 {
1613         return !opt ? 0 : opt->help;
1614 }
1615
1616
1617 FFOptionsWindow::FFOptionsWindow(FFOptionsDialog *dialog, int x, int y)
1618  : BC_Window(_(PROGRAM_NAME ": Options"), x, y, xS(640), yS(400))
1619 {
1620         this->dialog = dialog;
1621         this->selected = 0;
1622         this->kind = 0;
1623 // *** CONTEXT_HELP ***
1624         context_help_set_keyword("Modifying FFmpeg Format Options");
1625 }
1626
1627 FFOptionsWindow::~FFOptionsWindow()
1628 {
1629 }
1630
1631 void FFOptionsWindow::create_objects()
1632 {
1633         int xs8 = xS(8), xs10 = xS(10);
1634         int ys10 = yS(10);
1635         lock_window("FFOptionsWindow::create_objects");
1636         BC_Title *title;
1637         const char *format_name = dialog->cfg_window->format_name;
1638         const char *codec_name = dialog->cfg_window->codec_name;
1639         int x0 = xs10, y0 = ys10;
1640         int x = x0, y = y0;
1641         add_subwindow(title = new BC_Title(x, y, _("Format: ")));
1642         x += title->get_w();
1643         add_subwindow(new BC_Title(x, y, format_name));
1644         if( codec_name ) {
1645                 x = x0 + xS(150);
1646                 add_subwindow(title = new BC_Title(x, y, _("Codec: ")));
1647                 x += title->get_w();
1648                 add_subwindow(new BC_Title(x, y, codec_name));
1649         }
1650         x = x0;  y += title->get_h() + ys10;  y0 = y;
1651         add_subwindow(title = new BC_Title(x, y, _("Type: ")));
1652         x += title->get_w() + xs8;
1653         add_subwindow(type = new BC_Title(x, y, (char *)""));
1654         x = x0 + xS(150);
1655         add_subwindow(title = new BC_Title(x, y, _("Range: ")));
1656         x += title->get_w() + xs8;
1657         add_subwindow(range = new BC_Title(x, y, (char *)""));
1658
1659         x = x0;  y += title->get_h() + ys10;
1660         add_subwindow(units = new FFOptionsUnits(this, x, y, xS(120)));
1661         x += units->get_w() + xs8;
1662         int x1 = get_w() - BC_GenericButton::calculate_w(this, _("Apply")) - xs8;
1663         add_subwindow(text = new FFOptionsText(this, x, y, x1-x - xs8));
1664         add_subwindow(apply = new FFOptionsApply(this, x1, y));
1665         y += units->get_h() + ys10;
1666         if( codec_name ) {
1667                 add_subwindow(kind = new FFOptionsKind(this, x1, y0, apply->get_w()));
1668                 kind->create_objects();
1669                 const char *kind_text = _("Kind:");
1670                 x1 -= BC_Title::calculate_w(this, kind_text) + xs8;
1671                 add_subwindow(kind_title = new BC_Title(x1, y0, kind_text));
1672         }
1673         y0 = y;
1674         panel_x = x0;  panel_y = y0;
1675         panel_w = get_w()-xs10 - panel_x;
1676         panel_h = get_h()-ys10 - panel_y - BC_OKButton::calculate_h();
1677         panel = new FFOptions_OptPanel(this, panel_x, panel_y, panel_w, panel_h);
1678         add_subwindow(panel);
1679         add_subwindow(new BC_OKButton(this));
1680         add_subwindow(new BC_CancelButton(this));
1681         panel->create_objects();
1682         int k = codec_name ? FF_KIND_CODEC : FF_KIND_FORMAT;
1683         options.initialize(this, k);
1684         if( kind )
1685                 kind->set(k);
1686         draw();
1687         show_window(1);
1688         unlock_window();
1689 }
1690
1691 void FFOptionsWindow::draw()
1692 {
1693         update(selected);
1694 }
1695
1696 int FFOptionsWindow::resize_event(int w, int h)
1697 {
1698         int xs8 = xS(8), xs10 = xS(10);
1699         int ys10 = yS(10);
1700         if( kind ) {
1701                 int x0 = w - xs8 - kind->get_w();
1702                 int y0 = kind->get_y();
1703                 kind->reposition_window(x0, y0);
1704                 x0 -= kind_title->get_w() + xs8;
1705                 kind_title->reposition_window(x0, y0);
1706         }
1707         int x1 = get_w() - apply->get_w() - xs8;
1708         int y1 = units->get_y();
1709         apply->reposition_window(x1, y1);
1710         int x0 = units->get_x() + units->get_w() + xs8;
1711         int y0 = units->get_y();
1712         text->reposition_window(x0,y0, x1-x0-xs8);
1713         panel_w = get_w()-xs10 - panel_x;
1714         panel_h = get_h()-ys10 - panel_y - BC_OKButton::calculate_h();
1715         panel->reposition_window(panel_x,panel_y, panel_w, panel_h);
1716         return 1;
1717 }
1718
1719 FFOptionsDialog::FFOptionsDialog(FFMPEGConfigWindow *cfg_window)
1720  : BC_DialogThread()
1721 {
1722         this->cfg_window = cfg_window;
1723         options_window = 0;
1724         ff_opts = 0;
1725 }
1726
1727 FFOptionsDialog::~FFOptionsDialog()
1728 {
1729         close_window();
1730         av_dict_free(&ff_opts);
1731 }
1732
1733 void FFOptionsDialog::load_options(const char *bp, int len)
1734 {
1735         av_dict_free(&ff_opts);
1736         char line[BCTEXTLEN];
1737         char key[BCSTRLEN], val[BCTEXTLEN];
1738         const char *dp = bp + len-1;
1739         int no = 0;
1740         while( bp < dp && *bp != 0 ) {
1741                 ++no;
1742                 char *cp = line, *ep = cp+sizeof(line)-1;
1743                 while( *bp && cp<ep && (*cp=*bp++)!='\n' ) ++cp;
1744                 *cp = 0;
1745                 if( line[0] == '#' ) {
1746                         sprintf(key,"#%d", no);
1747                         av_dict_set(&ff_opts, key, line, 0);
1748                         continue;
1749                 }
1750                 if( line[0] == '\n' ) continue;
1751                 if( FFMPEG::scan_option_line(line, key, val) ) continue;
1752                 av_dict_set(&ff_opts, key, val, 0);
1753         }
1754 }
1755
1756 void FFOptionsDialog::store_options(char *cp, int len)
1757 {
1758         char *ep = cp + len-1;
1759         AVDictionaryEntry *elem = 0;
1760         while( (elem=av_dict_get(ff_opts, "", elem, AV_DICT_IGNORE_SUFFIX)) != 0 ) {
1761                 if( elem->key[0] == '#' ) {
1762                         cp += snprintf(cp,ep-cp, "%s\n", elem->value);
1763                         continue;
1764                 }
1765                 cp += snprintf(cp,ep-cp, "%s=%s\n", elem->key, elem->value);
1766         }
1767         *cp = 0;
1768 }
1769
1770
1771 void FFOptionsDialog::start()
1772 {
1773         if( options_window ) {
1774                 options_window->lock_window("FFOptionsDialog::start");
1775                 options_window->raise_window();
1776                 options_window->unlock_window();
1777                 return;
1778         }
1779         cfg_window->get_pop_cursor(wx, wy);
1780         cfg_window->read_options();
1781         BC_DialogThread::start();
1782 }
1783
1784 BC_Window* FFOptionsDialog::new_gui()
1785 {
1786         options_window = new FFOptionsWindow(this, wx, wy);
1787         options_window->create_objects();
1788         return options_window;
1789 }
1790
1791 void FFOptionsDialog::handle_done_event(int result)
1792 {
1793         if( !result ) {
1794                 cfg_window->lock_window("FFMPEGConfigFormat::save_options");
1795                 cfg_window->save_options();
1796                 cfg_window->unlock_window();
1797         }
1798         options_window = 0;
1799 }
1800
1801 FFOptionsAudioDialog::FFOptionsAudioDialog(FFMPEGConfigAudio *aud_config)
1802  : FFOptionsDialog(aud_config)
1803 {
1804         this->aud_config = aud_config;
1805 }
1806
1807 FFOptionsAudioDialog::~FFOptionsAudioDialog()
1808 {
1809         close_window();
1810 }
1811
1812 void FFOptionsAudioDialog::update_options(const char *options)
1813 {
1814         aud_config->lock_window("FFOptionsAudioDialog::update_options");
1815         aud_config->audio_options->update(options);
1816         aud_config->unlock_window();
1817 }
1818
1819 FFOptionsVideoDialog::FFOptionsVideoDialog(FFMPEGConfigVideo *vid_config)
1820  : FFOptionsDialog(vid_config)
1821 {
1822         this->vid_config = vid_config;
1823 }
1824
1825 FFOptionsVideoDialog::~FFOptionsVideoDialog()
1826 {
1827         close_window();
1828 }
1829
1830 void FFOptionsVideoDialog::update_options(const char *options)
1831 {
1832         vid_config->lock_window("FFOptionsVideoDialog::update_options");
1833         vid_config->video_options->update(options);
1834         vid_config->unlock_window();
1835 }
1836
1837 FFOptionsFormatDialog::FFOptionsFormatDialog(FFMPEGConfigFormat *fmt_config)
1838  : FFOptionsDialog(fmt_config)
1839 {
1840         this->fmt_config = fmt_config;
1841 }
1842
1843 FFOptionsFormatDialog::~FFOptionsFormatDialog()
1844 {
1845         close_window();
1846 }
1847
1848 void FFOptionsFormatDialog::update_options(const char *options)
1849 {
1850         fmt_config->lock_window("FFOptionsFormatDialog::update_options");
1851         fmt_config->format_options->update(options);
1852         fmt_config->unlock_window();
1853 }
1854
1855
1856 FFOptionsViewAudio::FFOptionsViewAudio(FFMPEGConfigAudio *aud_config,
1857                 int x, int y, const char *text)
1858  : BC_GenericButton(x, y, text)
1859 {
1860         this->aud_config = aud_config;
1861         avctx = 0;
1862 }
1863
1864 FFOptionsViewAudio::~FFOptionsViewAudio()
1865 {
1866         avcodec_free_context(&avctx);
1867 }
1868
1869 int FFOptionsViewAudio::handle_event()
1870 {
1871         int ret = 0;
1872         Asset *asset = aud_config->asset;
1873         const char *name = asset->acodec;
1874         char audio_format[BCSTRLEN];  audio_format[0] = 0;
1875         char audio_codec[BCSTRLEN];   audio_codec[0] = 0;
1876         const AVCodec *codec = !ret &&
1877             !FFMPEG::get_format(audio_format, "audio", name) &&
1878             !FFMPEG::get_codec(audio_codec, "audio", name) ?
1879                 avcodec_find_encoder_by_name(audio_codec) : 0;
1880         if( !ret && !codec ) {
1881                 eprintf(_("no codec named: %s: %s"), name, audio_codec);
1882                 ret = 1;
1883         }
1884         avcodec_free_context(&avctx);
1885         if( !ret && !(avctx = avcodec_alloc_context3(codec)) ) {
1886                 eprintf(_("no codec context: %s: %s"), name, audio_codec);
1887                 ret = 1;
1888         }
1889         if( !ret )
1890                 aud_config->start(avctx);
1891         return 1;
1892 }
1893
1894 FFOptionsViewVideo::FFOptionsViewVideo(FFMPEGConfigVideo *vid_config,
1895                 int x, int y, const char *text)
1896  : BC_GenericButton(x, y, text)
1897 {
1898         this->vid_config = vid_config;
1899         avctx = 0;
1900 }
1901
1902 FFOptionsViewVideo::~FFOptionsViewVideo()
1903 {
1904         avcodec_free_context(&avctx);
1905 }
1906
1907 int FFOptionsViewVideo::handle_event()
1908 {
1909         int ret = 0;
1910         Asset *asset = vid_config->asset;
1911         const char *name = asset->vcodec;
1912         char video_format[BCSTRLEN];  video_format[0] = 0;
1913         char video_codec[BCSTRLEN];   video_codec[0] = 0;
1914         const AVCodec *codec = !ret &&
1915             !FFMPEG::get_format(video_format, "video", name) &&
1916             !FFMPEG::get_codec(video_codec, "video", name) ?
1917                 avcodec_find_encoder_by_name(video_codec) : 0;
1918         if( !ret && !codec ) {
1919                 eprintf(_("no codec named: %s: %s"), name, video_codec);
1920                 ret = 1;
1921         }
1922         avcodec_free_context(&avctx);
1923         if( !ret && !(avctx = avcodec_alloc_context3(codec)) ) {
1924                 eprintf(_("no codec context: %s: %s"), name, video_codec);
1925                 ret = 1;
1926         }
1927
1928         if( !ret )
1929                 vid_config->start(avctx);
1930         return 1;
1931 }
1932
1933 FFOptionsViewFormat::FFOptionsViewFormat(FFMPEGConfigWindow *cfg_window,
1934                 EDL *edl, Asset *asset, int x, int y, const char *text)
1935  : BC_GenericButton(x, y, text)
1936 {
1937         this->cfg_window = cfg_window;
1938         this->edl = edl;
1939         this->asset = asset;
1940         format_dialog = 0;
1941 }
1942
1943 FFOptionsViewFormat::~FFOptionsViewFormat()
1944 {
1945         delete format_dialog;
1946 }
1947
1948 int FFOptionsViewFormat::handle_event()
1949 {
1950         delete format_dialog;
1951         int wx, wy;
1952         get_pop_cursor(wx, wy);
1953         format_dialog = new FFOptionsFormatViewDialog(this, wx, wy);
1954         format_dialog->start();
1955         return 1;
1956 }
1957
1958
1959 FFOptionsFormatView::FFOptionsFormatView(FFMPEGConfigFormat *fmt_config,
1960                 int x, int y, const char *text)
1961  : BC_GenericButton(x, y, text)
1962 {
1963         this->fmt_config = fmt_config;
1964         fmt_ctx = 0;
1965 }
1966
1967 FFOptionsFormatView::~FFOptionsFormatView()
1968 {
1969         avformat_free_context(fmt_ctx);
1970 }
1971
1972 int FFOptionsFormatView::handle_event()
1973 {
1974         Asset *asset = fmt_config->asset;
1975         char *format_name = asset->fformat;
1976         char replace_name0[] = "mov";
1977         char replace_name1[] = "mpegts";
1978         char replace_name2[] = "matroska";
1979         if (!strcmp(format_name, "qt"))
1980                 format_name = replace_name0; // fixup
1981         if (!strcmp(format_name, "m2ts"))
1982                 format_name = replace_name1; // fixup
1983         if (!strcmp(format_name, "mkv"))
1984                 format_name = replace_name2; // fixup
1985         avformat_free_context(fmt_ctx);  fmt_ctx = 0;
1986         int ret = avformat_alloc_output_context2(&fmt_ctx, 0, format_name, 0);
1987         if( ret || !fmt_ctx ) {
1988                 eprintf(_("no format named: %s"), format_name);
1989                 ret = 1;
1990         }
1991         if( !ret )
1992                 fmt_config->start(fmt_ctx);
1993         return 1;
1994 }
1995
1996 FFOptionsFormatViewDialog::FFOptionsFormatViewDialog(FFOptionsViewFormat *view_format,
1997                 int wx, int wy)
1998 {
1999         this->view_format = view_format;
2000         this->wx = wx;
2001         this->wy = wy;
2002         cfg_window = 0;
2003 }
2004
2005 FFOptionsFormatViewDialog::~FFOptionsFormatViewDialog()
2006 {
2007         close_window();
2008 }
2009
2010 BC_Window *FFOptionsFormatViewDialog::new_gui()
2011 {
2012         cfg_window = new FFMPEGConfigFormat(this, wx, wy,
2013                 view_format->asset, view_format->edl);
2014         cfg_window->create_objects();
2015         cfg_window->read_options();
2016         return cfg_window;
2017 }
2018
2019 void FFOptionsFormatViewDialog::handle_done_event(int result)
2020 {
2021         if( !result )
2022                 cfg_window->save_changes();
2023         cfg_window = 0;
2024 }
2025