ffmpeg param visibility, high422 x264 profile, bd/dvd enhancements
[goodguy/history.git] / cinelerra-5.1 / cinelerra / fileffmpeg.C
1
2 #include <stdio.h>
3 #include <stdint.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <string.h>
7 // work around for __STDC_CONSTANT_MACROS
8 #include <lzma.h>
9
10 #include "asset.h"
11 #include "bcwindowbase.h"
12 #include "bitspopup.h"
13 #include "ffmpeg.h"
14 #include "filebase.h"
15 #include "file.h"
16 #include "fileffmpeg.h"
17 #include "filesystem.h"
18 #include "indexfile.h"
19 #include "mainerror.h"
20 #include "mainprogress.h"
21 #include "mutex.h"
22 #include "preferences.h"
23 #include "videodevice.inc"
24
25 FileFFMPEG::FileFFMPEG(Asset *asset, File *file)
26   : FileBase(asset, file)
27 {
28         ff = 0;
29         if(asset->format == FILE_UNKNOWN)
30                 asset->format = FILE_FFMPEG;
31 }
32
33 FileFFMPEG::~FileFFMPEG()
34 {
35         delete ff;
36 }
37
38
39 FFMpegConfigNum::FFMpegConfigNum(BC_Window *window,
40                 int x, int y, char *title_text, int *output)
41  : BC_TumbleTextBox(window, (int64_t)*output,
42         (int64_t)-1, (int64_t)25000000, 100, y, 100)
43 {
44         this->window = window;
45         this->x = x;  this->y = y;
46         this->title_text = title_text;
47         this->output = output;
48 }
49
50 FFMpegConfigNum::~FFMpegConfigNum()
51 {
52 }
53
54 void FFMpegConfigNum::create_objects()
55 {
56         window->add_subwindow(title = new BC_Title(x, y, title_text));
57         BC_TumbleTextBox::create_objects();
58 }
59
60 int FFMpegConfigNum::handle_event()
61 {
62         *output = atol(get_text());
63         return 1;
64 }
65
66 FFMpegAudioNum::FFMpegAudioNum(BC_Window *window,
67                 int x, int y, char *title_text, int *output)
68  : FFMpegConfigNum(window, x, y, title_text, output)
69 {
70 }
71
72 int FFMpegAudioBitrate::handle_event()
73 {
74         int ret = FFMpegAudioNum::handle_event();
75         return ret;
76 }
77
78 FFMpegVideoNum::FFMpegVideoNum(BC_Window *window,
79                 int x, int y, char *title_text, int *output)
80  : FFMpegConfigNum(window, x, y, title_text, output)
81 {
82 }
83
84 int FFMpegVideoBitrate::handle_event()
85 {
86         int ret = FFMpegVideoNum::handle_event();
87         Asset *asset = window()->asset;
88         if( asset->ff_video_bitrate )
89                 window()->quality->disable();
90         else
91                 window()->quality->enable();
92         return ret;
93 }
94
95 int FFMpegVideoQuality::handle_event()
96 {
97         int ret = FFMpegVideoNum::handle_event();
98         Asset *asset = window()->asset;
99         if( asset->ff_video_quality )
100                 window()->bitrate->disable();
101         else
102                 window()->bitrate->enable();
103         return ret;
104 }
105
106 void FileFFMPEG::get_parameters(BC_WindowBase *parent_window,
107                 Asset *asset, BC_WindowBase *&format_window,
108                 int audio_options, int video_options)
109 {
110         if(audio_options) {
111                 FFMPEGConfigAudio *window = new FFMPEGConfigAudio(parent_window, asset);
112                 format_window = window;
113                 window->create_objects();
114                 window->run_window();
115                 delete window;
116         }
117         else if(video_options) {
118                 FFMPEGConfigVideo *window = new FFMPEGConfigVideo(parent_window, asset);
119                 format_window = window;
120                 window->create_objects();
121                 window->run_window();
122                 delete window;
123         }
124 }
125
126 int FileFFMPEG::check_sig(Asset *asset)
127 {
128         char *ptr = strstr(asset->path, ".pcm");
129         if( ptr ) return 0;
130         ptr = strstr(asset->path, ".raw");
131         if( ptr ) return 0;
132
133         FFMPEG ffmpeg(0);
134         int ret = !ffmpeg.init_decoder(asset->path) &&
135                 !ffmpeg.open_decoder() ? 1 : 0;
136         return ret;
137 }
138
139 void FileFFMPEG::get_info(char *path, char *text, int len)
140 {
141         char *cp = text;
142         FFMPEG ffmpeg(0);
143         cp += sprintf(cp, _("file path: %s\n"), path);
144         struct stat st;
145         int ret = 0;
146         if( stat(path, &st) < 0 ) {
147                 cp += sprintf(cp, _(" err: %s\n"), strerror(errno));
148                 ret = 1;
149         }
150         else {
151                 cp += sprintf(cp, _("  %jd bytes\n"), st.st_size);
152         }
153         if( !ret ) ret = ffmpeg.init_decoder(path);
154         if( !ret ) ret = ffmpeg.open_decoder();
155         if( !ret ) {
156                 cp += sprintf(cp, _("info:\n"));
157                 ffmpeg.info(cp, len-(cp-text));
158         }
159         else
160                 sprintf(cp, _("== open failed\n"));
161 }
162
163 int FileFFMPEG::get_video_info(int track, int &pid, double &framerate,
164                 int &width, int &height, char *title)
165 {
166         if( !ff ) return -1;
167         pid = ff->ff_video_pid(track);
168         framerate = ff->ff_frame_rate(track);
169         width = ff->ff_video_width(track);
170         height = ff->ff_video_height(track);
171         if( title ) *title = 0;
172         return 0;
173 }
174
175 int FileFFMPEG::get_audio_for_video(int vstream, int astream, int64_t &channel_mask)
176 {
177         if( !ff ) return 1;
178         return ff->ff_audio_for_video(vstream, astream, channel_mask);
179 }
180
181 int FileFFMPEG::select_video_stream(Asset *asset, int vstream)
182 {
183         if( !ff || !asset->video_data ) return 1;
184         asset->width = ff->ff_video_width(vstream);
185         asset->height = ff->ff_video_height(vstream);
186         asset->video_length = ff->ff_video_frames(vstream);
187         if( (asset->video_length = ff->ff_video_frames(vstream)) < 2 )
188                 asset->video_length = asset->video_length < 0 ? 0 : -1;
189         asset->frame_rate = ff->ff_frame_rate(vstream);
190         return 0;
191 }
192
193 int FileFFMPEG::select_audio_stream(Asset *asset, int astream)
194 {
195         if( !ff || !asset->audio_data ) return 1;
196         asset->sample_rate = ff->ff_sample_rate(astream);
197         asset->audio_length = ff->ff_audio_samples(astream);
198         return 0;
199 }
200
201 int FileFFMPEG::open_file(int rd, int wr)
202 {
203         int result = 0;
204         if( ff ) return 1;
205         ff = new FFMPEG(this);
206
207         if( rd ) {
208                 result = ff->init_decoder(asset->path);
209                 if( !result ) result = ff->open_decoder();
210                 if( !result ) {
211                         int audio_channels = ff->ff_total_audio_channels();
212                         if( audio_channels > 0 ) {
213                                 asset->audio_data = 1;
214                                 asset->channels = audio_channels;
215                                 asset->sample_rate = ff->ff_sample_rate(0);
216                                 asset->audio_length = ff->ff_audio_samples(0);
217                         }
218                         int video_layers = ff->ff_total_video_layers();
219                         if( video_layers > 0 ) {
220                                 asset->video_data = 1;
221                                 if( !asset->layers ) asset->layers = video_layers;
222                                 asset->actual_width = ff->ff_video_width(0);
223                                 asset->actual_height = ff->ff_video_height(0);
224                                 if( !asset->width ) asset->width = asset->actual_width;
225                                 if( !asset->height ) asset->height = asset->actual_height;
226                                 if( !asset->video_length &&
227                                     (asset->video_length = ff->ff_video_frames(0)) < 2 )
228                                         asset->video_length = asset->video_length < 0 ? 0 : -1;
229                                 if( !asset->frame_rate ) asset->frame_rate = ff->ff_frame_rate(0);
230                         }
231                         IndexState *index_state = asset->index_state;
232                         index_state->read_markers(file->preferences->index_directory, asset->path);
233                 }
234         }
235         else if( wr ) {
236                 result = ff->init_encoder(asset->path);
237                 // must be in this order or dvdauthor will fail
238                 if( !result && asset->video_data )
239                         result = ff->open_encoder("video", asset->vcodec);
240                 if( !result && asset->audio_data )
241                         result = ff->open_encoder("audio", asset->acodec);
242         }
243         return result;
244 }
245
246 int FileFFMPEG::close_file()
247 {
248         delete ff;
249         ff = 0;
250         return 0;
251 }
252
253
254 int FileFFMPEG::write_samples(double **buffer, int64_t len)
255 {
256         if( !ff || len < 0 ) return -1;
257         int stream = 0;
258         int ret = ff->encode(stream, buffer, len);
259         return ret;
260 }
261
262 int FileFFMPEG::write_frames(VFrame ***frames, int len)
263 {
264         if( !ff ) return -1;
265         int ret = 0, layer = 0;
266         for(int i = 0; i < 1; i++) {
267                 for(int j = 0; j < len && !ret; j++) {
268                         VFrame *frame = frames[i][j];
269                         ret = ff->encode(layer, frame);
270                 }
271         }
272         return ret;
273 }
274
275
276 int FileFFMPEG::read_samples(double *buffer, int64_t len)
277 {
278         if( !ff || len < 0 ) return -1;
279         int ch = file->current_channel;
280         int64_t pos = file->current_sample;
281         int ret = ff->decode(ch, pos, buffer, len);
282         if( ret > 0 ) return 0;
283         memset(buffer,0,len*sizeof(*buffer));
284         return -1;
285 }
286
287 int FileFFMPEG::read_frame(VFrame *frame)
288 {
289         if( !ff ) return -1;
290         int layer = file->current_layer;
291         int64_t pos = asset->video_length >= 0 ? file->current_frame : 0;
292         int ret = ff->decode(layer, pos, frame);
293         frame->set_status(ret);
294         if( ret >= 0 ) return 0;
295         frame->clear_frame();
296         return -1;
297 }
298
299
300 int64_t FileFFMPEG::get_memory_usage()
301 {
302         return 0;
303 }
304
305 int FileFFMPEG::colormodel_supported(int colormodel)
306 {
307         return colormodel;
308 }
309
310 int FileFFMPEG::get_best_colormodel(Asset *asset, int driver)
311 {
312         switch(driver) {
313         case PLAYBACK_X11:      return BC_RGB888;
314         case PLAYBACK_X11_GL:   return BC_YUV888;
315         }
316         return BC_YUV420P;
317 }
318
319 //======
320
321 FFMPEGConfigAudio::FFMPEGConfigAudio(BC_WindowBase *parent_window, Asset *asset)
322  : BC_Window(_(PROGRAM_NAME ": Audio Preset"),
323         parent_window->get_abs_cursor_x(1),
324         parent_window->get_abs_cursor_y(1),
325         420, 420)
326 {
327         this->parent_window = parent_window;
328         this->asset = asset;
329         preset_popup = 0;
330
331         bitrate = 0;
332         audio_options = 0;
333         ff_options_dialog = 0;
334 }
335
336 FFMPEGConfigAudio::~FFMPEGConfigAudio()
337 {
338         delete ff_options_dialog;
339         lock_window("FFMPEGConfigAudio::~FFMPEGConfigAudio");
340         delete preset_popup;
341         presets.remove_all_objects();
342         unlock_window();
343 }
344
345 void FFMPEGConfigAudio::create_objects()
346 {
347         int x = 10, y = 10;
348         lock_window("FFMPEGConfigAudio::create_objects");
349
350         FileSystem fs;
351         char option_path[BCTEXTLEN];
352         FFMPEG::set_option_path(option_path, "audio");
353         fs.update(option_path);
354         int total_files = fs.total_files();
355         for(int i = 0; i < total_files; i++) {
356                 const char *name = fs.get_entry(i)->get_name();
357                 if( asset->fformat[0] != 0 ) {
358                         const char *ext = strrchr(name,'.');
359                         if( !ext ) continue;
360                         if( strcmp(asset->fformat, ++ext) ) continue;
361                 }
362                 presets.append(new BC_ListBoxItem(name));
363         }
364
365         if( asset->acodec[0] ) {
366                 int k = presets.size();
367                 while( --k >= 0 && strcmp(asset->acodec, presets[k]->get_text()) );
368                 if( k < 0 ) asset->acodec[0] = 0;
369         }
370
371         if( !asset->acodec[0] && presets.size() > 0 )
372                 strcpy(asset->acodec, presets[0]->get_text());
373
374         add_tool(new BC_Title(x, y, _("Preset:")));
375         y += 25;
376         preset_popup = new FFMPEGConfigAudioPopup(this, x, y);
377         preset_popup->create_objects();
378
379         y += 50;
380         bitrate = new FFMpegAudioBitrate(this, x, y, _("Bitrate:"), &asset->ff_audio_bitrate);
381         bitrate->create_objects();
382         bitrate->set_increment(1000);
383
384         y += bitrate->get_h() + 10;
385         BC_Title *title = new BC_Title(x, y, _("Audio Options:"));
386         add_subwindow(title);
387
388         ff_options_dialog = new FFOptionsAudioDialog(this);
389         int x1 = x + title->get_w() + 8;
390         add_subwindow(new FFOptionsViewAudio(this, x1, y, _("view")));
391
392         y += 25;
393         if( !asset->ff_audio_options[0] && asset->acodec[0] ) {
394                 FFMPEG::set_option_path(option_path, "audio/%s", asset->acodec);
395                 FFMPEG::load_options(option_path, asset->ff_audio_options,
396                          sizeof(asset->ff_audio_options));
397         }
398         audio_options = new FFAudioOptions(this, x, y, get_w()-x-20, 10,
399                  sizeof(asset->ff_audio_options)-1, asset->ff_audio_options);
400         audio_options->create_objects();
401         add_subwindow(new BC_OKButton(this));
402         
403         show_window(1);
404         bitrate->handle_event();
405         unlock_window();
406 }
407
408 int FFMPEGConfigAudio::close_event()
409 {
410         set_done(0);
411         return 1;
412 }
413
414 void FFMPEGConfigAudio::update_options()
415 {
416         audio_options->update(asset->ff_audio_options);
417 }
418
419 FFAudioOptions::FFAudioOptions(FFMPEGConfigAudio *audio_popup,
420         int x, int y, int w, int rows, int size, char *text)
421  : BC_ScrollTextBox(audio_popup, x, y, w, rows, text, size)
422 {
423         this->audio_popup = audio_popup;
424 }
425
426 int FFAudioOptions::handle_event()
427 {
428         strcpy(audio_popup->asset->ff_audio_options, get_text());
429         return 1;
430 }
431
432
433 FFMPEGConfigAudioPopup::FFMPEGConfigAudioPopup(FFMPEGConfigAudio *popup, int x, int y)
434  : BC_PopupTextBox(popup, &popup->presets, popup->asset->acodec, x, y, 300, 300)
435 {
436         this->popup = popup;
437 }
438
439 int FFMPEGConfigAudioPopup::handle_event()
440 {
441         strcpy(popup->asset->acodec, get_text());
442         Asset *asset = popup->asset;
443         char option_path[BCTEXTLEN];
444         FFMPEG::set_option_path(option_path, "audio/%s", asset->acodec);
445         FFMPEG::load_options(option_path, asset->ff_audio_options,
446                          sizeof(asset->ff_audio_options));
447         popup->audio_options->update(asset->ff_audio_options);
448         return 1;
449 }
450
451
452 FFMPEGConfigAudioToggle::FFMPEGConfigAudioToggle(FFMPEGConfigAudio *popup,
453         char *title_text, int x, int y, int *output)
454  : BC_CheckBox(x, y, *output, title_text)
455 {
456         this->popup = popup;
457         this->output = output;
458 }
459 int FFMPEGConfigAudioToggle::handle_event()
460 {
461         *output = get_value();
462         return 1;
463 }
464
465 //======
466
467 FFMPEGConfigVideo::FFMPEGConfigVideo(BC_WindowBase *parent_window, Asset *asset)
468  : BC_Window(_(PROGRAM_NAME ": Video Preset"),
469         parent_window->get_abs_cursor_x(1),
470         parent_window->get_abs_cursor_y(1),
471         420, 420)
472 {
473         this->parent_window = parent_window;
474         this->asset = asset;
475         preset_popup = 0;
476
477         bitrate = 0;
478         quality = 0;
479         video_options = 0;
480 }
481
482 FFMPEGConfigVideo::~FFMPEGConfigVideo()
483 {
484         lock_window("FFMPEGConfigVideo::~FFMPEGConfigVideo");
485         if(preset_popup) delete preset_popup;
486         presets.remove_all_objects();
487         unlock_window();
488 }
489
490 void FFMPEGConfigVideo::create_objects()
491 {
492         int x = 10, y = 10;
493         lock_window("FFMPEGConfigVideo::create_objects");
494
495         add_subwindow(new BC_Title(x, y, _("Compression:")));
496         y += 25;
497
498         FileSystem fs;
499         char option_path[BCTEXTLEN];
500         FFMPEG::set_option_path(option_path, "video");
501         fs.update(option_path);
502         int total_files = fs.total_files();
503         for(int i = 0; i < total_files; i++) {
504                 const char *name = fs.get_entry(i)->get_name();
505                 if( asset->fformat[0] != 0 ) {
506                         const char *ext = strrchr(name,'.');
507                         if( !ext ) continue;
508                         if( strcmp(asset->fformat, ++ext) ) continue;
509                 }
510                 presets.append(new BC_ListBoxItem(name));
511         }
512
513         if( asset->vcodec[0] ) {
514                 int k = presets.size();
515                 while( --k >= 0 && strcmp(asset->vcodec, presets[k]->get_text()) );
516                 if( k < 0 ) asset->vcodec[0] = 0;
517         }
518
519         if( !asset->vcodec[0] && presets.size() > 0 )
520                 strcpy(asset->vcodec, presets[0]->get_text());
521
522         preset_popup = new FFMPEGConfigVideoPopup(this, x, y);
523         preset_popup->create_objects();
524
525         if( asset->ff_video_bitrate && asset->ff_video_quality ) {
526                 asset->ff_video_bitrate = 0;
527                 asset->ff_video_quality = 0;
528         }
529
530         y += 50;
531         bitrate = new FFMpegVideoBitrate(this, x, y, _("Bitrate:"), &asset->ff_video_bitrate);
532         bitrate->create_objects();
533         bitrate->set_increment(100000);
534         y += bitrate->get_h() + 5;
535         quality = new FFMpegVideoQuality(this, x, y, _("Quality:"), &asset->ff_video_quality);
536         quality->create_objects();
537         quality->set_increment(1);
538         quality->set_boundaries((int64_t)0, (int64_t)31);
539
540         y += quality->get_h() + 10;
541         BC_Title *title = new BC_Title(x, y, _("Video Options:"));
542         add_subwindow(title);
543
544         ff_options_dialog = new FFOptionsVideoDialog(this);
545         int x1 = x + title->get_w() + 8;
546         add_subwindow(new FFOptionsViewVideo(this, x1, y, _("view")));
547
548         y += 25;
549         if( !asset->ff_video_options[0] && asset->vcodec[0] ) {
550                 FFMPEG::set_option_path(option_path, "video/%s", asset->vcodec);
551                 FFMPEG::load_options(option_path, asset->ff_video_options,
552                          sizeof(asset->ff_video_options));
553         }
554
555         video_options = new FFVideoOptions(this, x, y, get_w()-x-20, 10,
556                  sizeof(asset->ff_video_options)-1, asset->ff_video_options);
557         video_options->create_objects();
558
559         add_subwindow(new BC_OKButton(this));
560         show_window(1);
561         if( asset->ff_video_bitrate )
562                 quality->disable();
563         if( asset->ff_video_quality )
564                 bitrate->disable();
565         unlock_window();
566 }
567
568 int FFMPEGConfigVideo::close_event()
569 {
570         set_done(0);
571         return 1;
572 }
573
574 void FFMPEGConfigVideo::update_options()
575 {
576         video_options->update(asset->ff_video_options);
577 }
578
579 FFVideoOptions::FFVideoOptions(FFMPEGConfigVideo *video_popup,
580         int x, int y, int w, int rows, int size, char *text)
581  : BC_ScrollTextBox(video_popup, x, y, w, rows, text, size)
582 {
583         this->video_popup = video_popup;
584 }
585
586 int FFVideoOptions::handle_event()
587 {
588         strcpy(video_popup->asset->ff_video_options, get_text());
589         return 1;
590 }
591
592
593 FFMPEGConfigVideoPopup::FFMPEGConfigVideoPopup(FFMPEGConfigVideo *popup, int x, int y)
594  : BC_PopupTextBox(popup, &popup->presets, popup->asset->vcodec, x, y, 300, 300)
595 {
596         this->popup = popup;
597 }
598
599 int FFMPEGConfigVideoPopup::handle_event()
600 {
601         strcpy(popup->asset->vcodec, get_text());
602         Asset *asset = popup->asset;
603         char option_path[BCTEXTLEN];
604         FFMPEG::set_option_path(option_path, "video/%s", asset->vcodec);
605         FFMPEG::load_options(option_path, asset->ff_video_options,
606                          sizeof(asset->ff_video_options));
607         popup->video_options->update(asset->ff_video_options);
608         return 1;
609 }
610
611
612 FFMPEGConfigVideoToggle::FFMPEGConfigVideoToggle(FFMPEGConfigVideo *popup,
613         char *title_text, int x, int y, int *output)
614  : BC_CheckBox(x, y, *output, title_text)
615 {
616         this->popup = popup;
617         this->output = output;
618 }
619 int FFMPEGConfigVideoToggle::handle_event()
620 {
621         *output = get_value();
622         return 1;
623 }
624
625 FFMPEGScanProgress::FFMPEGScanProgress(IndexFile *index_file, MainProgressBar *progress_bar,
626                 const char *title, int64_t length, int64_t *position, int *canceled)
627  : Thread(1, 0, 0)
628 {
629         this->index_file = index_file;
630         this->progress_bar = progress_bar;
631         strcpy(this->progress_title, title);
632         this->length = length;
633         this->position = position;
634         this->canceled = canceled;
635         done = 0;
636         start();
637 }
638
639 FFMPEGScanProgress::~FFMPEGScanProgress()
640 {
641         done = 1;
642         cancel();
643         join();
644 }
645
646 void FFMPEGScanProgress::run()
647 {
648         while( !done ) {
649                 if( progress_bar->update(*position) ) {
650                         *canceled = done = 1;
651                         break;
652                 }
653                 index_file->redraw_edits(0);
654                 usleep(500000);
655         }
656 }
657
658 int FileFFMPEG::get_index(IndexFile *index_file, MainProgressBar *progress_bar)
659 {
660         if( !ff ) return -1;
661         if( !file->preferences->ffmpeg_marker_indexes ) return 1;
662
663         IndexState *index_state = index_file->get_state();
664         if( index_state->index_status != INDEX_NOTTESTED ) return 0;
665         index_state->reset_index();
666         index_state->reset_markers();
667         index_state->index_status = INDEX_BUILDING;
668
669         for( int aidx=0; aidx<ff->ffaudio.size(); ++aidx ) {
670                 FFAudioStream *aud = ff->ffaudio[aidx];
671                 index_state->add_audio_stream(aud->channels, aud->length);
672         }
673
674         FileSystem fs;
675         int64_t file_bytes = fs.get_size(ff->fmt_ctx->filename);
676         char *index_path = index_file->index_filename;
677
678         int canceled = 0;
679         int64_t scan_position = 0;
680         FFMPEGScanProgress *scan_progress = 0;
681         if( progress_bar ) {
682                 char progress_title[BCTEXTLEN];
683                 sprintf(progress_title, _("Creating %s\n"), index_path);
684                 progress_bar->update_title(progress_title, 1);
685                 progress_bar->update_length(file_bytes);
686                 scan_progress = new FFMPEGScanProgress(index_file,
687                                 progress_bar, progress_title, file_bytes,
688                                 &scan_position, &canceled);
689         }
690
691         index_state->index_bytes = file_bytes;
692         index_state->init_scan(file->preferences->index_size);
693
694         if( ff->scan(index_state, &scan_position, &canceled) || canceled ) {
695                 index_state->reset_index();
696                 index_state->reset_markers();
697                 canceled = 1;
698         }
699
700         delete scan_progress;
701         if( canceled ) return 1;
702
703         index_state->marker_status = MARKERS_READY;
704         return index_state->create_index(index_path, asset);
705 }
706
707
708 FFOptions_OptPanel::
709 FFOptions_OptPanel(FFOptionsWindow *fwin, int x, int y, int w, int h)
710  : BC_ListBox(x, y, w, h, LISTBOX_TEXT), opts(items[0]), vals(items[1])
711 {
712         this->fwin = fwin;
713         update();  // init col/wid/columns
714 }
715
716 FFOptions_OptPanel::
717 ~FFOptions_OptPanel()
718 {
719 }
720
721 void FFOptions_OptPanel::create_objects()
722 {
723         const char *cols[] = { "option", "value", };
724         const int col1_w = 150;
725         int wids[] = { col1_w, get_w()-col1_w };
726         BC_ListBox::update(&items[0], &cols[0], &wids[0], sizeof(items)/sizeof(items[0]));
727 }
728
729 int FFOptions_OptPanel::update()
730 {
731         opts.remove_all();
732         vals.remove_all();
733         FFOptions &options = fwin->options;
734         for( int i=0; i<options.size(); ++i ) {
735                 FFOptions_Opt *opt = options[i];
736                 opts.append(opt->item_name);
737                 vals.append(opt->item_value);
738         }
739         draw_items(1);
740         return 0;
741 }
742
743 int FFOptions_OptPanel::cursor_leave_event()
744 {
745         hide_tooltip();
746         return 0;
747 }
748
749
750 FFOptions_OptName::FFOptions_OptName(FFOptions_Opt *opt, const char *nm)
751 {
752         this->opt = opt;
753         set_text(nm);
754 }
755
756 FFOptions_OptName::~FFOptions_OptName()
757 {
758 }
759
760 FFOptions_OptValue::FFOptions_OptValue(FFOptions_Opt *opt)
761 {
762         this->opt = opt;
763 }
764
765
766 void FFOptions_OptValue::update()
767 {
768         if( !opt ) return;
769         char val[BCTEXTLEN];  val[0] = 0;
770         opt->get(val, sizeof(val));
771         update(val);
772 }
773
774 void FFOptions_OptValue::update(const char *v)
775 {
776         set_text(v);
777 }
778
779 FFOptions_Opt::FFOptions_Opt(FFOptions *options, const AVOption *opt, const char *nm)
780 {
781         this->options = options;
782         this->opt = opt;
783         item_name = new FFOptions_OptName(this, nm);
784         item_value = new FFOptions_OptValue(this);
785 }
786
787 FFOptions_Opt::~FFOptions_Opt()
788 {
789         delete item_name;
790         delete item_value;
791 }
792
793 char *FFOptions_Opt::get(char *vp, int sz)
794 {
795         char *cp = vp, *ep = vp+sz-1;
796         *cp = 0;
797         if( !opt ) return cp;
798
799         void *obj = (void *)options->obj;
800         uint8_t *bp = 0;
801         if( av_opt_get(obj, opt->name, 0, &bp) >= 0 && bp != 0 ) {
802                 const char *val = (const char *)bp;
803                 if( opt->unit && *val ) {
804                         int id = atoi(val);
805                         const char *uid = unit_name(id);
806                         if( uid ) val = uid;
807                 }
808                 cp = sz >= 0 ? strncpy(vp,val,sz) : strcpy(vp, val);
809                 if( sz > 0 ) vp[sz-1] = 0;
810                 av_freep(&bp);
811                 return cp;
812         }
813
814         *vp = 0;
815         return cp;
816 }
817
818 void FFOptions_Opt::set(const char *val)
819 {
820         void *obj = (void *)options->obj;
821         if( !obj || !opt ) return;
822         av_opt_set(obj, opt->name, val, 0);
823 }
824
825
826 FFOptionsKindItem::FFOptionsKindItem(FFOptionsKind *kind, const char *text, int idx)
827  : BC_MenuItem(text)
828 {
829         this->kind = kind;
830         this->idx = idx;
831 }
832
833 FFOptionsKindItem::~FFOptionsKindItem()
834 {
835 }
836
837 int FFOptionsKindItem::handle_event()
838 {
839         FFOptionsWindow *fwin = kind->fwin;
840         FFOptions &options = fwin->options;
841         options.initialize(fwin, idx);
842         fwin->draw();
843         return 1;
844 }
845
846 const char *FFOptionsKind::kinds[] = {
847         N_("codec"),    // FF_KIND_CODEC
848         N_("ffmpeg"),   // FF_KIND_FFMPEG
849 };
850
851 FFOptionsKind::
852 FFOptionsKind(FFOptionsWindow *fwin, int x, int y, int w)
853  : BC_PopupMenu(x, y, w-calculate_w(0), "")
854 {
855         this->fwin = fwin;
856 }
857
858 FFOptionsKind::
859 ~FFOptionsKind()
860 {
861 }
862
863 void FFOptionsKind::create_objects()
864 {
865         for( int i=0; i<(int)(sizeof(kinds)/sizeof(kinds[0])); ++i )
866                 add_item(new FFOptionsKindItem(this, _(kinds[i]), i));
867 }
868
869 int FFOptionsKind::handle_event()
870 {
871         return 1;
872 }
873
874 void FFOptionsKind::set(int k)
875 {
876         this->kind = k;
877         set_text(kinds[k]);
878 }
879
880 FFOptionsText::
881 FFOptionsText(FFOptionsWindow *fwin, int x, int y, int w)
882  : BC_TextBox(x, y, w, 1, (char *)"")
883 {
884         this->fwin = fwin;
885 }
886
887 FFOptionsText::
888 ~FFOptionsText()
889 {
890 }
891
892 int FFOptionsText::handle_event()
893 {
894         return 0;
895 }
896
897 FFOptionsUnits::
898 FFOptionsUnits(FFOptionsWindow *fwin, int x, int y, int w)
899  : BC_PopupMenu(x, y, w, "")
900 {
901         this->fwin = fwin;
902 }
903
904 FFOptionsUnits::
905 ~FFOptionsUnits()
906 {
907 }
908
909 int FFOptionsUnits::handle_event()
910 {
911         const char *text = get_text();
912         if( text && fwin->selected ) {
913                 fwin->selected->set(text);
914                 fwin->selected->item_value->update();
915                 av_dict_set(&fwin->dialog->ff_opts,
916                         fwin->selected->item_name->get_text(),
917                         fwin->selected->item_value->get_text(), 0);
918                 fwin->draw();
919         }
920         return 1;
921 }
922
923 FFOptionsApply::
924 FFOptionsApply(FFOptionsWindow *fwin, int x, int y)
925  : BC_GenericButton(x, y, _("Apply"))
926 {
927         this->fwin = fwin;
928 }
929
930 FFOptionsApply::
931 ~FFOptionsApply()
932 {
933 }
934
935 int FFOptionsApply::handle_event()
936 {
937         const char *text = fwin->text->get_text();
938         if( text && fwin->selected ) {
939                 fwin->selected->set(text);
940                 fwin->selected->item_value->update();
941                 av_dict_set(&fwin->dialog->ff_opts,
942                         fwin->selected->item_name->get_text(),
943                         fwin->selected->item_value->get_text(), 0);
944                 fwin->draw();
945         }
946         return 1;
947 }
948
949 FFOptions::FFOptions()
950 {
951         avctx = 0;
952         obj = 0;
953 }
954
955 FFOptions::~FFOptions()
956 {
957         remove_all_objects();
958         avcodec_free_context(&avctx);
959 }
960
961 int FFOptions::cmpr(const void *a, const void *b)
962 {
963         FFOptions_Opt *ap = *(FFOptions_Opt **)a;
964         FFOptions_Opt *bp = *(FFOptions_Opt **)b;
965         const char *vap = ap->item_name->get_text();
966         const char *vbp = bp->item_name->get_text();
967         return strcmp(vap, vbp);
968 }
969
970 void FFOptions::initialize(FFOptionsWindow *win, int kind)
971 {
972         remove_all_objects();
973         this->win = win;
974         win->selected = 0;
975         obj = 0;
976         if( !avctx )
977                 avctx = avcodec_alloc_context3(win->dialog->codec);
978
979         switch( kind ) {
980         case FF_KIND_CODEC:
981                 obj = (const void *)avctx->priv_data;
982                 break;
983         case FF_KIND_FFMPEG:
984                 obj = (const void *)avctx;
985                 break;
986         }
987
988         if( obj ) {
989                 FFOptions &conf = *this;
990                 const AVOption *opt = 0;
991                 while( (opt=av_opt_next(obj, opt)) != 0 ) {
992                         if( opt->type == AV_OPT_TYPE_CONST ) continue;
993                         int dupl = 0;
994                         for( int i=0; !dupl && i<size(); ++i ) {
995                                 FFOptions_Opt *fopt = conf[i];
996                                 const AVOption *op = fopt->opt;
997                                 if( op->offset != opt->offset ) continue;
998                                 if( op->type != opt->type ) continue;
999                                 dupl = 1;
1000                                 if( strlen(op->name) < strlen(opt->name) )
1001                                         fopt->opt = opt;
1002                         }
1003                         if( dupl ) continue;
1004                         FFOptions_Opt *fopt = new FFOptions_Opt(this, opt, opt->name);
1005                         append(fopt);
1006                         char val[BCTEXTLEN], *vp = fopt->get(val, sizeof(val));
1007                         fopt->item_value->update(vp);
1008                 }
1009         }
1010
1011         qsort(&values[0],size(),sizeof(values[0]),cmpr);
1012         win->kind->set(kind);
1013         win->panel->update();
1014         win->panel->set_yposition(0);
1015 }
1016
1017 int FFOptions::update()
1018 {
1019         int ret = 0;
1020         FFOptions &conf = *this;
1021
1022         for( int i=0; i<size(); ++i ) {
1023                 FFOptions_Opt *fopt = conf[i];
1024                 char val[BCTEXTLEN], *vp = fopt->get(val, sizeof(val));
1025                 if( !vp || !strcmp(val, fopt->item_value->get_text()) ) continue;
1026                 fopt->item_value->update(val);
1027                 ++ret;
1028         }
1029         return ret;
1030 }
1031
1032 void FFOptions::dump(FILE *fp)
1033 {
1034         if( !obj ) return;
1035         const AVOption *opt = 0;
1036         FFOptions &conf = *this;
1037
1038         while( (opt=av_opt_next(obj, opt)) != 0 ) {
1039                 if( opt->type == AV_OPT_TYPE_CONST ) continue;
1040                 int k = size();
1041                 while( --k >= 0 && strcmp(opt->name, conf[k]->opt->name) );
1042                 if( k < 0 ) continue;
1043                 FFOptions_Opt *fopt = conf[k];
1044                 char val[BCTEXTLEN], *vp = fopt->get(val,sizeof(val));
1045                 fprintf(fp, "  %s:=%s", opt->name, vp);
1046                 if( opt->unit ) {
1047                         char unt[BCTEXTLEN], *up = unt;
1048                         fopt->units(up);
1049                         fprintf(fp, "%s", unt);
1050                 }
1051                 fprintf(fp, "\n");
1052         }
1053 }
1054
1055
1056 void FFOptionsWindow::update(FFOptions_Opt *opt)
1057 {
1058         if( selected != opt ) {
1059                 if( selected ) selected->item_name->set_selected(0);
1060                 selected = opt;
1061                 if( selected ) selected->item_name->set_selected(1);
1062         }
1063         clear_box(0,0, 0,panel->get_y());
1064         char str[BCTEXTLEN], *sp;
1065         *(sp=str) = 0;
1066         if( opt ) opt->types(sp);
1067         type->update(str);
1068         *(sp=str) = 0;
1069         if( opt ) opt->ranges(sp);
1070         range->update(str);
1071         while( units->total_items() ) units->del_item(0);
1072         char unit[BCSTRLEN];  strcpy(unit, "()");
1073         if( opt && opt->opt ) {
1074                 ArrayList<const char *> names;
1075                 int n = 0;
1076                 if( opt->opt->unit ) {
1077                         n = opt->units(names);
1078                         if( n > 0 ) strcpy(unit,opt->opt->unit);
1079                 }
1080                 for( int i=0; i<n; ++i )
1081                         units->add_item(new BC_MenuItem(names[i], 0));
1082         }
1083         units->set_text(unit);
1084         char val[BCTEXTLEN];  val[0] = 0;
1085         if( opt ) opt->get(val, sizeof(val));
1086         text->update(val);
1087
1088         panel->update();
1089 }
1090
1091 int FFOptions_OptPanel::selection_changed()
1092 {
1093         FFOptions_Opt *opt = 0;
1094         BC_ListBoxItem *item = get_selection(0, 0);
1095         if( item ) {
1096                 FFOptions_OptName *opt_name = (FFOptions_OptName *)item;
1097                 opt = opt_name->opt;
1098         }
1099         fwin->update(opt);
1100         fwin->panel->set_tooltip(!opt ? 0 : opt->tip());
1101         fwin->panel->show_tooltip();
1102         return 1;
1103 }
1104
1105
1106 int FFOptions_Opt::types(char *rp)
1107 {
1108         const char *cp = "";
1109         if( opt ) switch (opt->type) {
1110         case AV_OPT_TYPE_FLAGS: cp = "<flags>";  break;
1111         case AV_OPT_TYPE_INT: cp = "<int>"; break;
1112         case AV_OPT_TYPE_INT64: cp = "<int64>"; break;
1113         case AV_OPT_TYPE_DOUBLE: cp = "<double>"; break;
1114         case AV_OPT_TYPE_FLOAT: cp = "<float>"; break;
1115         case AV_OPT_TYPE_STRING: cp = "<string>"; break;
1116         case AV_OPT_TYPE_RATIONAL: cp = "<rational>"; break;
1117         case AV_OPT_TYPE_BINARY: cp = "<binary>"; break;
1118         case AV_OPT_TYPE_IMAGE_SIZE: cp = "<image_size>"; break;
1119         case AV_OPT_TYPE_VIDEO_RATE: cp = "<video_rate>"; break;
1120         case AV_OPT_TYPE_PIXEL_FMT: cp = "<pix_fmt>"; break;
1121         case AV_OPT_TYPE_SAMPLE_FMT: cp = "<sample_fmt>"; break;
1122         case AV_OPT_TYPE_DURATION: cp = "<duration>"; break;
1123         case AV_OPT_TYPE_COLOR: cp = "<color>"; break;
1124         case AV_OPT_TYPE_CHANNEL_LAYOUT: cp = "<channel_layout>";  break;
1125         case AV_OPT_TYPE_BOOL: cp = "<bool>";  break;
1126         default: cp = "<undef>";  break;
1127         }
1128         return sprintf(rp, "%s", cp);
1129 }
1130 int FFOptions_Opt::scalar(double d, char *rp)
1131 {
1132         const char *cp = 0;
1133              if( d == INT_MAX ) cp = "INT_MAX";
1134         else if( d == INT_MIN ) cp = "INT_MIN";
1135         else if( d == UINT32_MAX ) cp = "UINT32_MAX";
1136         else if( d == (double)INT64_MAX ) cp = "I64_MAX";
1137         else if( d == INT64_MIN ) cp = "I64_MIN";
1138         else if( d == FLT_MAX ) cp = "FLT_MAX";
1139         else if( d == FLT_MIN ) cp = "FLT_MIN";
1140         else if( d == -FLT_MAX ) cp = "-FLT_MAX";
1141         else if( d == -FLT_MIN ) cp = "-FLT_MIN";
1142         else if( d == DBL_MAX ) cp = "DBL_MAX";
1143         else if( d == DBL_MIN ) cp = "DBL_MIN";
1144         else if( d == -DBL_MAX ) cp = "-DBL_MAX";
1145         else if( d == -DBL_MIN ) cp = "-DBL_MIN";
1146         else if( d == 0 ) cp = signbit(d) ? "-0" : "0";
1147         else if( isnan(d) ) cp = signbit(d) ? "-NAN" : "NAN";
1148         else if( isinf(d) ) cp = signbit(d) ? "-INF" : "INF";
1149         else return sprintf(rp, "%g", d);
1150         return sprintf(rp, "%s", cp);
1151 }
1152
1153 int FFOptions_Opt::ranges(char *rp)
1154 {
1155         if( !opt ) return 0;
1156         void *obj = (void *)options->obj;
1157         if( !obj ) return 0;
1158
1159         switch (opt->type) {
1160         case AV_OPT_TYPE_INT:
1161         case AV_OPT_TYPE_INT64:
1162         case AV_OPT_TYPE_DOUBLE:
1163         case AV_OPT_TYPE_FLOAT: break;
1164         default: return 0;;
1165         }
1166         AVOptionRanges *r = 0;
1167         char *cp = rp;
1168         if( av_opt_query_ranges(&r, obj, opt->name, AV_OPT_SEARCH_FAKE_OBJ) < 0 ) return 0;
1169         for( int i=0; i<r->nb_ranges; ++i ) {
1170                 cp += sprintf(cp, " (");  cp += scalar(r->range[i]->value_min, cp);
1171                 cp += sprintf(cp, "..");  cp += scalar(r->range[i]->value_max, cp);
1172                 cp += sprintf(cp, ")");
1173         }
1174         av_opt_freep_ranges(&r);
1175         return cp - rp;
1176 }
1177
1178 int FFOptions_Opt::units(ArrayList<const char *> &names)
1179 {
1180         if( !opt || !opt->unit ) return 0;
1181         const void *obj = options->obj;
1182         if( !obj ) return 0;
1183
1184         ArrayList<const AVOption *> opts;
1185         const AVOption *opt = NULL;
1186         while( (opt=av_opt_next(obj, opt)) != 0 ) {
1187                 if( !opt->unit ) continue;
1188                 if( opt->type != AV_OPT_TYPE_CONST ) continue;
1189                 if( strcmp(this->opt->unit, opt->unit) ) continue;
1190                 int i = opts.size();
1191                 while( --i >= 0 ) {
1192                         if( opts[i]->default_val.i64 != opt->default_val.i64 ) continue;
1193                         if( strlen(opts[i]->name) < strlen(opt->name) ) opts[i] = opt;
1194                         break;
1195                 }
1196                 if( i >= 0 ) continue;
1197                 opts.append(opt);
1198         }
1199
1200         for( int i=0; i<opts.size(); ++i )
1201                 names.append(opts[i]->name);
1202
1203         return names.size();
1204 }
1205
1206 int FFOptions_Opt::units(char *rp)
1207 {
1208         ArrayList<const char *> names;
1209         int n = units(names);
1210         if( !n ) return 0;
1211         char *cp = rp;
1212         cp += sprintf(cp, " [%s:", this->opt->unit);
1213         for( int i=0; i<n; ++i )
1214                 cp += sprintf(cp, " %s", names[i]);
1215         cp += sprintf(cp, "]:");
1216         return cp - rp;
1217 }
1218
1219 const char *FFOptions_Opt::unit_name(int id)
1220 {
1221         if( !opt || !opt->unit ) return 0;
1222         const void *obj = options->obj;
1223         if( !obj ) return 0;
1224
1225         const char *ret = 0;
1226         const AVOption *opt = NULL;
1227         while( (opt=av_opt_next(obj, opt)) != 0 ) {
1228                 if( !opt->unit ) continue;
1229                 if( opt->type != AV_OPT_TYPE_CONST ) continue;
1230                 if( strcmp(this->opt->unit, opt->unit) ) continue;
1231                 if( opt->default_val.i64 != id ) continue;
1232                 if( !ret ) { ret = opt->name;  continue; }
1233                 if( strlen(ret) < strlen(opt->name) ) ret = opt->name;
1234         }
1235
1236         return ret;
1237 }
1238
1239 const char *FFOptions_Opt::tip()
1240 {
1241         return !opt ? 0 : opt->help;
1242 }
1243
1244
1245 FFOptionsWindow::FFOptionsWindow(FFOptionsDialog *dialog)
1246  : BC_Window(PROGRAM_NAME ": Options", 60, 30, 640, 400)
1247 {
1248         this->dialog = dialog;
1249         this->selected = 0;
1250 }
1251
1252 FFOptionsWindow::~FFOptionsWindow()
1253 {
1254 }
1255
1256 void FFOptionsWindow::create_objects()
1257 {
1258         BC_Title *title;
1259         int x = 10, y = 10;
1260         add_subwindow(title = new BC_Title(x, y, dialog->codec_name));
1261         y += title->get_h() + 10;
1262         int x0 = x, y0 = y;
1263         add_subwindow(title = new BC_Title(x0, y0, _("Type: ")));
1264         x0 += title->get_w() + 8;
1265         add_subwindow(type = new BC_Title(x0, y0, (char *)""));
1266         x0 = x + 150;
1267         add_subwindow(title = new BC_Title(x0, y0, _("Range: ")));
1268         x0 += title->get_w() + 8;
1269         add_subwindow(range = new BC_Title(x0, y0, (char *)""));
1270         x0 = x;
1271         y += title->get_h() + 10;
1272         add_subwindow(units = new FFOptionsUnits(this, x0, y, 120));
1273         x0 += units->get_w() + 8;
1274         int x1 = get_w() - BC_GenericButton::calculate_w(this, _("Apply")) - 8;
1275         add_subwindow(text = new FFOptionsText(this, x0, y, x1-x0 - 8));
1276         add_subwindow(apply = new FFOptionsApply(this, x1, y));
1277         y += units->get_h() + 10;
1278         add_subwindow(kind = new FFOptionsKind(this, x1, y0, apply->get_w()));
1279         kind->create_objects();
1280         const char *kind_text = _("Kind:");
1281         x1 -= BC_Title::calculate_w(this, kind_text) + 8;
1282         add_subwindow(kind_title = new BC_Title(x1, y0, kind_text));
1283
1284         panel_x = x;  panel_y = y;
1285         panel_w = get_w()-10 - panel_x;
1286         panel_h = get_h()-10 - panel_y - BC_OKButton::calculate_h();
1287         panel = new FFOptions_OptPanel(this, panel_x, panel_y, panel_w, panel_h);
1288         add_subwindow(panel);
1289         add_subwindow(new BC_OKButton(this));
1290         add_subwindow(new BC_CancelButton(this));
1291         panel->create_objects();
1292         options.initialize(this, FF_KIND_CODEC);
1293         draw();
1294         show_window(1);
1295 }
1296
1297 void FFOptionsWindow::draw()
1298 {
1299         update(selected);
1300 }
1301
1302 int FFOptionsWindow::resize_event(int w, int h)
1303 {
1304         int x1 = w - 8 - kind->get_w();
1305         int y = kind->get_y();
1306         kind->reposition_window(x1, y);
1307         x1 -= kind_title->get_w() + 8;
1308         kind_title->reposition_window(x1,y);
1309         x1 = get_w() - apply->get_w() - 8;
1310         int y1 = units->get_y();
1311         apply->reposition_window(x1, y1);
1312         int x0 = units->get_x() + units->get_w() + 8;
1313         int y0 = units->get_y();
1314         text->reposition_window(x0,y0, x1-x0-8);
1315         panel_w = get_w()-10 - panel_x;
1316         panel_h = get_h()-10 - panel_y;
1317         panel->reposition_window(panel_x,panel_y, panel_w, panel_h);
1318         return 1;
1319 }
1320
1321 FFOptionsDialog::FFOptionsDialog()
1322  : BC_DialogThread()
1323 {
1324         this->options_window = 0;
1325         this->codec_name = 0;
1326         this->codec = 0;
1327         this->ff_options = 0;
1328         this->ff_len = 0;
1329         this->ff_opts = 0;
1330 }
1331
1332 FFOptionsDialog::~FFOptionsDialog()
1333 {
1334         close_window();
1335         delete [] codec_name;
1336 }
1337
1338 void FFOptionsDialog::load_options()
1339 {
1340         char line[BCTEXTLEN];
1341         char key[BCSTRLEN], val[BCTEXTLEN];
1342         char *bp = ff_options, *dp = bp + ff_len-1;
1343         int no = 0;
1344         while( bp < dp && *bp != 0 ) {
1345                 ++no;
1346                 char *cp = line, *ep = cp+sizeof(line)-1;
1347                 while( *bp && cp<ep && (*cp=*bp++)!='\n' ) ++cp;
1348                 *cp = 0;
1349                 if( line[0] == '#' ) {
1350                         sprintf(key,"#%d", no);
1351                         av_dict_set(&ff_opts, key, line, 0);
1352                         continue;
1353                 }
1354                 if( line[0] == '\n' ) continue;
1355                 if( FFMPEG::scan_option_line(line, key, val) ) continue;
1356                 av_dict_set(&ff_opts, key, val, 0);
1357         }
1358 }
1359
1360 void FFOptionsDialog::store_options()
1361 {
1362         char *cp = ff_options, *ep = cp + ff_len-1;
1363         AVDictionaryEntry *elem = 0;
1364         while( (elem=av_dict_get(ff_opts, "", elem, AV_DICT_IGNORE_SUFFIX)) != 0 ) {
1365                 if( elem->key[0] == '#' ) {
1366                         cp += snprintf(cp,ep-cp, "%s\n", elem->value);
1367                         continue;
1368                 }
1369                 cp += snprintf(cp,ep-cp, "%s=%s\n", elem->key, elem->value);
1370         }
1371         *cp = 0;
1372 }
1373
1374 void FFOptionsDialog::start(const char *codec_name, AVCodec *codec, char *options, int len)
1375 {
1376         if( options_window ) {
1377                 options_window->lock_window("FFOptionsDialog::start");
1378                 options_window->raise_window();
1379                 options_window->unlock_window();
1380                 return;
1381         }
1382
1383         this->codec_name = cstrdup(codec_name);
1384         this->codec = codec;
1385         this->ff_opts = 0;
1386         this->ff_options = options;
1387         this->ff_len = len;
1388         load_options();
1389
1390         BC_DialogThread::start();
1391 }
1392
1393 BC_Window* FFOptionsDialog::new_gui()
1394 {
1395         options_window = new FFOptionsWindow(this);
1396         options_window->create_objects();
1397         return options_window;
1398 }
1399
1400 void FFOptionsDialog::handle_done_event(int result)
1401 {
1402         if( !result ) {
1403                 store_options();
1404                 update_options();
1405         }
1406         options_window = 0;
1407         delete [] codec_name;  codec_name = 0;
1408         av_dict_free(&ff_opts);
1409 }
1410
1411 FFOptionsAudioDialog::FFOptionsAudioDialog(FFMPEGConfigAudio *aud_config)
1412 {
1413         this->aud_config = aud_config;
1414 }
1415
1416 FFOptionsAudioDialog::~FFOptionsAudioDialog()
1417 {
1418 }
1419
1420 void FFOptionsAudioDialog::update_options()
1421 {
1422         aud_config->update_options();
1423 }
1424
1425 FFOptionsVideoDialog::FFOptionsVideoDialog(FFMPEGConfigVideo *vid_config)
1426 {
1427         this->vid_config = vid_config;
1428 }
1429
1430 FFOptionsVideoDialog::~FFOptionsVideoDialog()
1431 {
1432 }
1433
1434 void FFOptionsVideoDialog::update_options()
1435 {
1436         vid_config->update_options();
1437 }
1438
1439
1440 FFOptionsViewAudio::FFOptionsViewAudio(FFMPEGConfigAudio *aud_config, int x, int y, const char *text)
1441  : BC_GenericButton(x, y, text)
1442 {
1443         this->aud_config = aud_config;
1444 }
1445
1446 FFOptionsViewAudio::~FFOptionsViewAudio()
1447 {
1448 }
1449
1450 int FFOptionsViewAudio::handle_event()
1451 {
1452         AVCodec *codec = 0;
1453         char audio_codec[BCSTRLEN]; audio_codec[0] = 0;
1454         Asset *asset = aud_config->asset;
1455         const char *name = asset->acodec;
1456         if( !FFMPEG::get_codec(audio_codec, "audio", name) )
1457                 codec = avcodec_find_encoder_by_name(audio_codec);
1458         if( !codec ) {
1459                 eprintf(_("no codec named: %s: %s"), name, audio_codec);
1460                 return 1;
1461         }
1462         aud_config->ff_options_dialog->start(audio_codec, codec,
1463                  asset->ff_audio_options, sizeof(asset->ff_audio_options));
1464         return 1;
1465 }
1466
1467 FFOptionsViewVideo::FFOptionsViewVideo(FFMPEGConfigVideo *vid_config, int x, int y, const char *text)
1468  : BC_GenericButton(x, y, text)
1469 {
1470         this->vid_config = vid_config;
1471 }
1472
1473 FFOptionsViewVideo::~FFOptionsViewVideo()
1474 {
1475 }
1476
1477 int FFOptionsViewVideo::handle_event()
1478 {
1479         AVCodec *codec = 0;
1480         char video_codec[BCSTRLEN]; video_codec[0] = 0;
1481         Asset *asset = vid_config->asset;
1482         const char *name = asset->vcodec;
1483         if( !FFMPEG::get_codec(video_codec, "video", name) )
1484                 codec = avcodec_find_encoder_by_name(video_codec);
1485         if( !codec ) {
1486                 eprintf(_("no codec named: %s: %s"), name, video_codec);
1487                 return 1;
1488         }
1489         vid_config->ff_options_dialog->start(video_codec, codec,
1490                  asset->ff_video_options, sizeof(asset->ff_video_options));
1491         return 1;
1492 }
1493