improve ffmpeg format selection popup menu, more ffmpeg audio fixes
[goodguy/history.git] / cinelerra-5.0 / 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 "mutex.h"
19 #include "videodevice.inc"
20
21 FileFFMPEG::FileFFMPEG(Asset *asset, File *file)
22   : FileBase(asset, file)
23 {
24         ff = 0;
25         if(asset->format == FILE_UNKNOWN)
26                 asset->format = FILE_FFMPEG;
27 }
28
29 FileFFMPEG::~FileFFMPEG()
30 {
31         delete ff;
32 }
33
34
35 FFMpegConfigNum::FFMpegConfigNum(BC_Window *window,
36                 int x, int y, char *title_text, int *output)
37  : BC_TumbleTextBox(window, (int64_t)*output,
38         (int64_t)-1, (int64_t)25000000, 100, y, 100)
39 {
40         this->window = window;
41         this->x = x;  this->y = y;
42         this->title_text = title_text;
43         this->output = output;
44 }
45
46 FFMpegConfigNum::~FFMpegConfigNum()
47 {
48 }
49
50 void FFMpegConfigNum::create_objects()
51 {
52         window->add_subwindow(title = new BC_Title(x, y, title_text));
53         BC_TumbleTextBox::create_objects();
54 }
55
56 int FFMpegConfigNum::handle_event()
57 {
58         *output = atol(get_text());
59         return 1;
60 }
61
62 FFMpegAudioNum::FFMpegAudioNum(BC_Window *window,
63                 int x, int y, char *title_text, int *output)
64  : FFMpegConfigNum(window, x, y, title_text, output)
65 {
66 }
67
68 int FFMpegAudioBitrate::handle_event()
69 {
70         int ret = FFMpegAudioNum::handle_event();
71         return ret;
72 }
73
74 FFMpegVideoNum::FFMpegVideoNum(BC_Window *window,
75                 int x, int y, char *title_text, int *output)
76  : FFMpegConfigNum(window, x, y, title_text, output)
77 {
78 }
79
80 int FFMpegVideoBitrate::handle_event()
81 {
82         int ret = FFMpegVideoNum::handle_event();
83         Asset *asset = window()->asset;
84         if( asset->ff_video_bitrate )
85                 window()->quality->disable();
86         else
87                 window()->quality->enable();
88         return ret;
89 }
90
91 int FFMpegVideoQuality::handle_event()
92 {
93         int ret = FFMpegVideoNum::handle_event();
94         Asset *asset = window()->asset;
95         if( asset->ff_video_quality )
96                 window()->bitrate->disable();
97         else
98                 window()->bitrate->enable();
99         return ret;
100 }
101
102 void FileFFMPEG::get_parameters(BC_WindowBase *parent_window,
103                 Asset *asset, BC_WindowBase *&format_window,
104                 int audio_options, int video_options)
105 {
106         if(audio_options) {
107                 FFMPEGConfigAudio *window = new FFMPEGConfigAudio(parent_window, asset);
108                 format_window = window;
109                 window->create_objects();
110                 window->run_window();
111                 delete window;
112         }
113         else if(video_options) {
114                 FFMPEGConfigVideo *window = new FFMPEGConfigVideo(parent_window, asset);
115                 format_window = window;
116                 window->create_objects();
117                 window->run_window();
118                 delete window;
119         }
120 }
121
122 int FileFFMPEG::check_sig(Asset *asset)
123 {
124         char *ptr = strstr(asset->path, ".pcm");
125         if( ptr ) return 0;
126         ptr = strstr(asset->path, ".raw");
127         if( ptr ) return 0;
128
129         FFMPEG ffmpeg(0);
130         int ret = !ffmpeg.init_decoder(asset->path) &&
131                 !ffmpeg.open_decoder() ? 1 : 0;
132         return ret;
133 }
134
135 void FileFFMPEG::get_info(char *path, char *text)
136 {
137         char *cp = text;
138         FFMPEG ffmpeg(0);
139         cp += sprintf(cp, "file path: %s\n", path);
140         struct stat st;
141         int ret = 0;
142         if( stat(path, &st) < 0 ) {
143                 cp += sprintf(cp, " err: %s\n", strerror(errno));
144                 ret = 1;
145         }
146         else {
147                 cp += sprintf(cp, "  %jd bytes\n", st.st_size);
148         }
149         if( !ret ) ret = ffmpeg.init_decoder(path);
150         if( !ret ) ret = ffmpeg.open_decoder();
151         if( !ret ) {
152                 cp += sprintf(cp, "info:\n");
153                 ffmpeg.info(cp, BCTEXTLEN-(cp-text));
154         }
155         else
156                 sprintf(cp, "== open failed\n");
157 }
158
159 int FileFFMPEG::get_video_info(int track, int &pid, double &framerate,
160                 int &width, int &height, char *title)
161 {
162         if( !ff ) return -1;
163         pid = ff->ff_video_pid(track);
164         framerate = ff->ff_frame_rate(track);
165         width = ff->ff_video_width(track);
166         height = ff->ff_video_height(track);
167         if( title ) *title = 0;
168         return 0;
169 }
170
171 int FileFFMPEG::get_audio_for_video(int vstream, int astream, int64_t &channel_mask)
172 {
173         if( !ff ) return 1;
174         return ff->ff_audio_for_video(vstream, astream, channel_mask);
175 }
176
177 int FileFFMPEG::select_video_stream(Asset *asset, int vstream)
178 {
179         if( !ff || !asset->video_data ) return 1;
180         asset->width = ff->ff_video_width(vstream);
181         asset->height = ff->ff_video_height(vstream);
182         asset->video_length = ff->ff_video_frames(vstream);
183         asset->frame_rate = ff->ff_frame_rate(vstream);
184         return 0;
185 }
186
187 int FileFFMPEG::select_audio_stream(Asset *asset, int astream)
188 {
189         if( !ff || !asset->audio_data ) return 1;
190         asset->channels = ff->ff_audio_channels(astream);
191         asset->sample_rate = ff->ff_sample_rate(astream);
192         asset->audio_length = ff->ff_audio_samples(astream);
193         return 0;
194 }
195
196 int FileFFMPEG::open_file(int rd, int wr)
197 {
198         int result = 0;
199         if( ff ) return 1;
200         ff = new FFMPEG(this);
201
202         if( rd ) {
203                 result = ff->init_decoder(asset->path);
204                 if( !result ) result = ff->open_decoder();
205                 if( !result ) {
206                         int audio_channels = ff->ff_total_audio_channels();
207                         if( audio_channels > 0 ) {
208                                 asset->audio_data = 1;
209                                 asset->channels = audio_channels;
210                                 asset->sample_rate = ff->ff_sample_rate(0);
211                                 asset->audio_length = ff->ff_audio_samples(0);
212                         }
213                         int video_layers = ff->ff_total_video_layers();
214                         if( video_layers > 0 ) {
215                                 asset->video_data = 1;
216                                 if( !asset->layers ) asset->layers = video_layers;
217                                 asset->actual_width = ff->ff_video_width(0);
218                                 asset->actual_height = ff->ff_video_height(0);
219                                 if( !asset->width ) asset->width = asset->actual_width;
220                                 if( !asset->height ) asset->height = asset->actual_height;
221                                 if( !asset->video_length ) asset->video_length = ff->ff_video_frames(0);
222                                 if( !asset->frame_rate ) asset->frame_rate = ff->ff_frame_rate(0);
223                         }
224                 }
225         }
226         else if( wr ) {
227                 result = ff->init_encoder(asset->path);
228                 // must be in this order or dvdauthor will fail
229                 if( !result && asset->video_data )
230                         result = ff->open_encoder("video", asset->vcodec);
231                 if( !result && asset->audio_data )
232                         result = ff->open_encoder("audio", asset->acodec);
233         }
234         return result;
235 }
236
237 int FileFFMPEG::close_file()
238 {
239         delete ff;
240         ff = 0;
241         return 0;
242 }
243
244
245 int FileFFMPEG::set_video_position(int64_t pos)
246 {
247         if( !ff || pos < 0 || pos >= asset->video_length )
248                 return 1;
249         return 0;
250 }
251
252
253 int FileFFMPEG::set_audio_position(int64_t pos)
254 {
255         if( !ff || pos < 0 || pos >= asset->audio_length )
256                 return 1;
257         return 0;
258 }
259
260
261 int FileFFMPEG::write_samples(double **buffer, int64_t len)
262 {
263         if( !ff || len < 0 ) return -1;
264         int stream = 0;
265         int ret = ff->encode(stream, buffer, len);
266         return ret;
267 }
268
269 int FileFFMPEG::write_frames(VFrame ***frames, int len)
270 {
271         if( !ff ) return -1;
272         int ret = 0, layer = 0;
273         for(int i = 0; i < 1; i++) {
274                 for(int j = 0; j < len && !ret; j++) {
275                         VFrame *frame = frames[i][j];
276                         ret = ff->encode(layer, frame);
277                 }
278         }
279         return ret;
280 }
281
282
283 int FileFFMPEG::read_samples(double *buffer, int64_t len)
284 {
285         if( !ff || len < 0 ) return -1;
286         int ch = file->current_channel;
287         int64_t pos = file->current_sample;
288         ff->decode(ch, pos, buffer, len);
289         return 0;
290 }
291
292 int FileFFMPEG::read_frame(VFrame *frame)
293 {
294         if( !ff ) return -1;
295         int layer = file->current_layer;
296         int64_t pos = file->current_frame;
297         ff->decode(layer, pos, frame);
298         return 0;
299 }
300
301
302 int64_t FileFFMPEG::get_memory_usage()
303 {
304         return 0;
305 }
306
307 int FileFFMPEG::colormodel_supported(int colormodel)
308 {
309         return colormodel;
310 }
311
312 int FileFFMPEG::get_best_colormodel(Asset *asset, int driver)
313 {
314         switch(driver) {
315         case PLAYBACK_X11:      return BC_RGB888;
316         case PLAYBACK_X11_GL:   return BC_YUV888;
317         }
318         return BC_YUV420P;
319 }
320
321 static void load_options(const char *path, char *bfr, int len)
322 {
323         *bfr = 0;
324         FILE *fp = fopen(path, "r");
325         if( !fp ) return;
326         fgets(bfr, len, fp); // skip hdr
327         len = fread(bfr, 1, len-1, fp);
328         if( len < 0 ) len = 0;
329         bfr[len] = 0;
330         fclose(fp);
331 }
332
333 //======
334 extern void get_exe_path(char *result); // from main.C
335
336 FFMPEGConfigAudio::FFMPEGConfigAudio(BC_WindowBase *parent_window, Asset *asset)
337  : BC_Window(PROGRAM_NAME ": Audio Preset",
338         parent_window->get_abs_cursor_x(1),
339         parent_window->get_abs_cursor_y(1),
340         420, 420)
341 {
342         this->parent_window = parent_window;
343         this->asset = asset;
344         preset_popup = 0;
345
346         bitrate = 0;
347         audio_options = 0;
348 }
349
350 FFMPEGConfigAudio::~FFMPEGConfigAudio()
351 {
352         lock_window("FFMPEGConfigAudio::~FFMPEGConfigAudio");
353         if(preset_popup) delete preset_popup;
354         presets.remove_all_objects();
355         unlock_window();
356 }
357
358 void FFMPEGConfigAudio::create_objects()
359 {
360         int x = 10, y = 10;
361         lock_window("FFMPEGConfigAudio::create_objects");
362
363         FileSystem fs;
364         char option_path[BCTEXTLEN];
365         FFMPEG::set_option_path(option_path, "/audio");
366         fs.update(option_path);
367         int total_files = fs.total_files();
368         for(int i = 0; i < total_files; i++) {
369                 const char *name = fs.get_entry(i)->get_name();
370                 if( asset->fformat[0] != 0 ) {
371                         const char *ext = strrchr(name,'.');
372                         if( !ext ) ext = name;
373                         else if( !strcmp("opts", ++ext) ) continue;
374                         if( strcmp(asset->fformat, ext) ) continue;
375                 }
376                 presets.append(new BC_ListBoxItem(name));
377         }
378
379         if( asset->acodec[0] ) {
380                 int k = presets.size();
381                 while( --k >= 0 && strcmp(asset->acodec, presets[k]->get_text()) );
382                 if( k < 0 ) asset->acodec[0] = 0;
383         }
384
385         if( !asset->acodec[0] && presets.size() > 0 )
386                 strcpy(asset->acodec, presets[0]->get_text());
387
388         add_tool(new BC_Title(x, y, _("Preset:")));
389         y += 25;
390         preset_popup = new FFMPEGConfigAudioPopup(this, x, y);
391         preset_popup->create_objects();
392
393         y += 50;
394         bitrate = new FFMpegAudioBitrate(this, x, y, _("Bitrate:"), &asset->ff_audio_bitrate);
395         bitrate->create_objects();
396         bitrate->set_increment(1000);
397
398         y += bitrate->get_h() + 10;
399         add_subwindow(new BC_Title(x, y, _("Audio Options:")));
400         y += 25;
401         if( !asset->ff_audio_options[0] && asset->acodec[0] ) {
402                 FFMPEG::set_option_path(option_path, "audio/%s", asset->acodec);
403                 load_options(option_path, asset->ff_audio_options,
404                          sizeof(asset->ff_audio_options));
405         }
406         add_subwindow(audio_options = new FFAudioOptions(this, x, y, get_w()-x-20, 10,
407                  sizeof(asset->ff_audio_options)-1, asset->ff_audio_options));
408         add_subwindow(new BC_OKButton(this));
409         show_window(1);
410         bitrate->handle_event();
411         unlock_window();
412 }
413
414 int FFMPEGConfigAudio::close_event()
415 {
416         set_done(0);
417         return 1;
418 }
419
420
421 FFAudioOptions::FFAudioOptions(FFMPEGConfigAudio *audio_popup,
422         int x, int y, int w, int rows, int size, char *text)
423  : BC_TextBox(x, y, w, rows, size, text)
424 {
425         this->audio_popup = audio_popup;
426 }
427
428 int FFAudioOptions::handle_event()
429 {
430         strcpy(audio_popup->asset->ff_audio_options, get_text());
431         return 1;
432 }
433
434
435 FFMPEGConfigAudioPopup::FFMPEGConfigAudioPopup(FFMPEGConfigAudio *popup, int x, int y)
436  : BC_PopupTextBox(popup, &popup->presets, popup->asset->acodec, x, y, 300, 300)
437 {
438         this->popup = popup;
439 }
440
441 int FFMPEGConfigAudioPopup::handle_event()
442 {
443         strcpy(popup->asset->acodec, get_text());
444         Asset *asset = popup->asset;
445         char option_path[BCTEXTLEN];
446         FFMPEG::set_option_path(option_path, "audio/%s", asset->acodec);
447         load_options(option_path, asset->ff_audio_options,
448                          sizeof(asset->ff_audio_options));
449         popup->audio_options->update(asset->ff_audio_options);
450         return 1;
451 }
452
453
454 FFMPEGConfigAudioToggle::FFMPEGConfigAudioToggle(FFMPEGConfigAudio *popup,
455         char *title_text, int x, int y, int *output)
456  : BC_CheckBox(x, y, *output, title_text)
457 {
458         this->popup = popup;
459         this->output = output;
460 }
461 int FFMPEGConfigAudioToggle::handle_event()
462 {
463         *output = get_value();
464         return 1;
465 }
466
467
468 //======
469
470 FFMPEGConfigVideo::FFMPEGConfigVideo(BC_WindowBase *parent_window, Asset *asset)
471  : BC_Window(PROGRAM_NAME ": Video Preset",
472         parent_window->get_abs_cursor_x(1),
473         parent_window->get_abs_cursor_y(1),
474         420, 420)
475 {
476         this->parent_window = parent_window;
477         this->asset = asset;
478         preset_popup = 0;
479
480         bitrate = 0;
481         video_options = 0;
482 }
483
484 FFMPEGConfigVideo::~FFMPEGConfigVideo()
485 {
486         lock_window("FFMPEGConfigVideo::~FFMPEGConfigVideo");
487         if(preset_popup) delete preset_popup;
488         presets.remove_all_objects();
489         unlock_window();
490 }
491
492 void FFMPEGConfigVideo::create_objects()
493 {
494         int x = 10, y = 10;
495         lock_window("FFMPEGConfigVideo::create_objects");
496
497         add_subwindow(new BC_Title(x, y, _("Compression:")));
498         y += 25;
499
500         FileSystem fs;
501         char option_path[BCTEXTLEN];
502         FFMPEG::set_option_path(option_path, "video");
503         fs.update(option_path);
504         int total_files = fs.total_files();
505         for(int i = 0; i < total_files; i++) {
506                 const char *name = fs.get_entry(i)->get_name();
507                 if( asset->fformat[0] != 0 ) {
508                         const char *ext = strrchr(name,'.');
509                         if( !ext ) ext = name;
510                         else if( !strcmp("opts", ++ext) ) continue;
511                         if( strcmp(asset->fformat, ext) ) continue;
512                 }
513                 presets.append(new BC_ListBoxItem(name));
514         }
515
516         if( asset->vcodec[0] ) {
517                 int k = presets.size();
518                 while( --k >= 0 && strcmp(asset->vcodec, presets[k]->get_text()) );
519                 if( k < 0 ) asset->vcodec[0] = 0;
520         }
521
522         if( !asset->vcodec[0] && presets.size() > 0 )
523                 strcpy(asset->vcodec, presets[0]->get_text());
524
525         preset_popup = new FFMPEGConfigVideoPopup(this, x, y);
526         preset_popup->create_objects();
527
528         if( asset->ff_video_bitrate && asset->ff_video_quality ) {
529                 asset->ff_video_bitrate = 0;
530                 asset->ff_video_quality = 0;
531         }
532
533         y += 50;
534         bitrate = new FFMpegVideoBitrate(this, x, y, _("Bitrate:"), &asset->ff_video_bitrate);
535         bitrate->create_objects();
536         bitrate->set_increment(100000);
537         y += bitrate->get_h() + 5;
538         quality = new FFMpegVideoQuality(this, x, y, _("Quality:"), &asset->ff_video_quality);
539         quality->create_objects();
540         quality->set_increment(1);
541         quality->set_boundaries((int64_t)0, (int64_t)31);
542
543         y += quality->get_h() + 10;
544         add_subwindow(new BC_Title(x, y, _("Video Options:")));
545         y += 25;
546         if( !asset->ff_video_options[0] && asset->vcodec[0] ) {
547                 FFMPEG::set_option_path(option_path, "video/%s", asset->vcodec);
548                 load_options(option_path, asset->ff_video_options,
549                          sizeof(asset->ff_video_options));
550         }
551         add_subwindow(video_options = new FFVideoOptions(this, x, y, get_w()-x-20, 10,
552                  sizeof(asset->ff_video_options)-1, asset->ff_video_options));
553
554         add_subwindow(new BC_OKButton(this));
555         show_window(1);
556         if( asset->ff_video_bitrate )
557                 quality->disable();
558         if( asset->ff_video_quality )
559                 bitrate->disable();
560         unlock_window();
561 }
562
563 int FFMPEGConfigVideo::close_event()
564 {
565         set_done(0);
566         return 1;
567 }
568
569
570 FFVideoOptions::FFVideoOptions(FFMPEGConfigVideo *video_popup,
571         int x, int y, int w, int rows, int size, char *text)
572  : BC_TextBox(x, y, w, rows, size, text)
573 {
574         this->video_popup = video_popup;
575 }
576
577 int FFVideoOptions::handle_event()
578 {
579         strcpy(video_popup->asset->ff_video_options, get_text());
580         return 1;
581 }
582
583
584 FFMPEGConfigVideoPopup::FFMPEGConfigVideoPopup(FFMPEGConfigVideo *popup, int x, int y)
585  : BC_PopupTextBox(popup, &popup->presets, popup->asset->vcodec, x, y, 300, 300)
586 {
587         this->popup = popup;
588 }
589
590 int FFMPEGConfigVideoPopup::handle_event()
591 {
592         strcpy(popup->asset->vcodec, get_text());
593         Asset *asset = popup->asset;
594         char option_path[BCTEXTLEN];
595         FFMPEG::set_option_path(option_path, "video/%s", asset->vcodec);
596         load_options(option_path, asset->ff_video_options,
597                          sizeof(asset->ff_video_options));
598         popup->video_options->update(asset->ff_video_options);
599         return 1;
600 }
601
602
603 FFMPEGConfigVideoToggle::FFMPEGConfigVideoToggle(FFMPEGConfigVideo *popup,
604         char *title_text, int x, int y, int *output)
605  : BC_CheckBox(x, y, *output, title_text)
606 {
607         this->popup = popup;
608         this->output = output;
609 }
610 int FFMPEGConfigVideoToggle::handle_event()
611 {
612         *output = get_value();
613         return 1;
614 }
615
616