add refresh rate to vicons + a bunch of bug 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 "bcprogressbox.h"
13 #include "bitspopup.h"
14 #include "ffmpeg.h"
15 #include "filebase.h"
16 #include "file.h"
17 #include "fileffmpeg.h"
18 #include "filesystem.h"
19 #include "format.inc"
20 #include "indexfile.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         asset->frame_rate = ff->ff_frame_rate(vstream);
188         return 0;
189 }
190
191 int FileFFMPEG::select_audio_stream(Asset *asset, int astream)
192 {
193         if( !ff || !asset->audio_data ) return 1;
194         asset->sample_rate = ff->ff_sample_rate(astream);
195         asset->audio_length = ff->ff_audio_samples(astream);
196         return 0;
197 }
198
199 int FileFFMPEG::open_file(int rd, int wr)
200 {
201         int result = 0;
202         if( ff ) return 1;
203         ff = new FFMPEG(this);
204
205         if( rd ) {
206                 result = ff->init_decoder(asset->path);
207                 if( !result ) result = ff->open_decoder();
208                 if( !result ) {
209                         int audio_channels = ff->ff_total_audio_channels();
210                         if( audio_channels > 0 ) {
211                                 asset->audio_data = 1;
212                                 asset->channels = audio_channels;
213                                 asset->sample_rate = ff->ff_sample_rate(0);
214                                 asset->audio_length = ff->ff_audio_samples(0);
215                         }
216                         int video_layers = ff->ff_total_video_layers();
217                         if( video_layers > 0 ) {
218                                 asset->video_data = 1;
219                                 if( !asset->layers ) asset->layers = video_layers;
220                                 asset->actual_width = ff->ff_video_width(0);
221                                 asset->actual_height = ff->ff_video_height(0);
222                                 if( !asset->width ) asset->width = asset->actual_width;
223                                 if( !asset->height ) asset->height = asset->actual_height;
224                                 if( !asset->video_length ) asset->video_length = ff->ff_video_frames(0);
225                                 if( !asset->frame_rate ) asset->frame_rate = ff->ff_frame_rate(0);
226                         }
227                         IndexState *index_state = asset->index_state;
228                         index_state->read_markers(file->preferences->index_directory, asset->path);
229                 }
230         }
231         else if( wr ) {
232                 result = ff->init_encoder(asset->path);
233                 // must be in this order or dvdauthor will fail
234                 if( !result && asset->video_data )
235                         result = ff->open_encoder("video", asset->vcodec);
236                 if( !result && asset->audio_data )
237                         result = ff->open_encoder("audio", asset->acodec);
238         }
239         return result;
240 }
241
242 int FileFFMPEG::close_file()
243 {
244         delete ff;
245         ff = 0;
246         return 0;
247 }
248
249
250 int FileFFMPEG::set_video_position(int64_t pos)
251 {
252         if( !ff || pos < 0 || pos >= asset->video_length )
253                 return 1;
254         return 0;
255 }
256
257
258 int FileFFMPEG::set_audio_position(int64_t pos)
259 {
260         if( !ff || pos < 0 || pos >= asset->audio_length )
261                 return 1;
262         return 0;
263 }
264
265
266 int FileFFMPEG::write_samples(double **buffer, int64_t len)
267 {
268         if( !ff || len < 0 ) return -1;
269         int stream = 0;
270         int ret = ff->encode(stream, buffer, len);
271         return ret;
272 }
273
274 int FileFFMPEG::write_frames(VFrame ***frames, int len)
275 {
276         if( !ff ) return -1;
277         int ret = 0, layer = 0;
278         for(int i = 0; i < 1; i++) {
279                 for(int j = 0; j < len && !ret; j++) {
280                         VFrame *frame = frames[i][j];
281                         ret = ff->encode(layer, frame);
282                 }
283         }
284         return ret;
285 }
286
287
288 int FileFFMPEG::read_samples(double *buffer, int64_t len)
289 {
290         if( !ff || len < 0 ) return -1;
291         int ch = file->current_channel;
292         int64_t pos = file->current_sample;
293         int ret = ff->decode(ch, pos, buffer, len);
294         if( ret > 0 ) return 0;
295         memset(buffer,0,len*sizeof(*buffer));
296         return -1;
297 }
298
299 int FileFFMPEG::read_frame(VFrame *frame)
300 {
301         if( !ff ) return -1;
302         int layer = file->current_layer;
303         int64_t pos = file->current_frame;
304         int ret = ff->decode(layer, pos, frame);
305         frame->set_status(ret);
306         if( ret >= 0 ) return 0;
307         frame->clear_frame();
308         return -1;
309 }
310
311
312 int64_t FileFFMPEG::get_memory_usage()
313 {
314         return 0;
315 }
316
317 int FileFFMPEG::colormodel_supported(int colormodel)
318 {
319         return colormodel;
320 }
321
322 int FileFFMPEG::get_best_colormodel(Asset *asset, int driver)
323 {
324         switch(driver) {
325         case PLAYBACK_X11:      return BC_RGB888;
326         case PLAYBACK_X11_GL:   return BC_YUV888;
327         }
328         return BC_YUV420P;
329 }
330
331 //======
332
333 FFMPEGConfigAudio::FFMPEGConfigAudio(BC_WindowBase *parent_window, Asset *asset)
334  : BC_Window(_(PROGRAM_NAME ": Audio Preset"),
335         parent_window->get_abs_cursor_x(1),
336         parent_window->get_abs_cursor_y(1),
337         420, 420)
338 {
339         this->parent_window = parent_window;
340         this->asset = asset;
341         preset_popup = 0;
342
343         bitrate = 0;
344         audio_options = 0;
345 }
346
347 FFMPEGConfigAudio::~FFMPEGConfigAudio()
348 {
349         lock_window("FFMPEGConfigAudio::~FFMPEGConfigAudio");
350         if(preset_popup) delete preset_popup;
351         presets.remove_all_objects();
352         unlock_window();
353 }
354
355 void FFMPEGConfigAudio::create_objects()
356 {
357         int x = 10, y = 10;
358         lock_window("FFMPEGConfigAudio::create_objects");
359
360         FileSystem fs;
361         char option_path[BCTEXTLEN];
362         FFMPEG::set_option_path(option_path, "audio");
363         fs.update(option_path);
364         int total_files = fs.total_files();
365         for(int i = 0; i < total_files; i++) {
366                 const char *name = fs.get_entry(i)->get_name();
367                 if( asset->fformat[0] != 0 ) {
368                         const char *ext = strrchr(name,'.');
369                         if( !ext ) continue;
370                         if( strcmp(asset->fformat, ++ext) ) continue;
371                 }
372                 presets.append(new BC_ListBoxItem(name));
373         }
374
375         if( asset->acodec[0] ) {
376                 int k = presets.size();
377                 while( --k >= 0 && strcmp(asset->acodec, presets[k]->get_text()) );
378                 if( k < 0 ) asset->acodec[0] = 0;
379         }
380
381         if( !asset->acodec[0] && presets.size() > 0 )
382                 strcpy(asset->acodec, presets[0]->get_text());
383
384         add_tool(new BC_Title(x, y, _("Preset:")));
385         y += 25;
386         preset_popup = new FFMPEGConfigAudioPopup(this, x, y);
387         preset_popup->create_objects();
388
389         y += 50;
390         bitrate = new FFMpegAudioBitrate(this, x, y, _("Bitrate:"), &asset->ff_audio_bitrate);
391         bitrate->create_objects();
392         bitrate->set_increment(1000);
393
394         y += bitrate->get_h() + 10;
395         add_subwindow(new BC_Title(x, y, _("Audio Options:")));
396         y += 25;
397         if( !asset->ff_audio_options[0] && asset->acodec[0] ) {
398                 FFMPEG::set_option_path(option_path, "audio/%s", asset->acodec);
399                 FFMPEG::load_options(option_path, asset->ff_audio_options,
400                          sizeof(asset->ff_audio_options));
401         }
402         add_subwindow(audio_options = new FFAudioOptions(this, x, y, get_w()-x-20, 10,
403                  sizeof(asset->ff_audio_options)-1, asset->ff_audio_options));
404         add_subwindow(new BC_OKButton(this));
405         show_window(1);
406         bitrate->handle_event();
407         unlock_window();
408 }
409
410 int FFMPEGConfigAudio::close_event()
411 {
412         set_done(0);
413         return 1;
414 }
415
416
417 FFAudioOptions::FFAudioOptions(FFMPEGConfigAudio *audio_popup,
418         int x, int y, int w, int rows, int size, char *text)
419  : BC_TextBox(x, y, w, rows, size, text)
420 {
421         this->audio_popup = audio_popup;
422 }
423
424 int FFAudioOptions::handle_event()
425 {
426         strcpy(audio_popup->asset->ff_audio_options, get_text());
427         return 1;
428 }
429
430
431 FFMPEGConfigAudioPopup::FFMPEGConfigAudioPopup(FFMPEGConfigAudio *popup, int x, int y)
432  : BC_PopupTextBox(popup, &popup->presets, popup->asset->acodec, x, y, 300, 300)
433 {
434         this->popup = popup;
435 }
436
437 int FFMPEGConfigAudioPopup::handle_event()
438 {
439         strcpy(popup->asset->acodec, get_text());
440         Asset *asset = popup->asset;
441         char option_path[BCTEXTLEN];
442         FFMPEG::set_option_path(option_path, "audio/%s", asset->acodec);
443         FFMPEG::load_options(option_path, asset->ff_audio_options,
444                          sizeof(asset->ff_audio_options));
445         popup->audio_options->update(asset->ff_audio_options);
446         return 1;
447 }
448
449
450 FFMPEGConfigAudioToggle::FFMPEGConfigAudioToggle(FFMPEGConfigAudio *popup,
451         char *title_text, int x, int y, int *output)
452  : BC_CheckBox(x, y, *output, title_text)
453 {
454         this->popup = popup;
455         this->output = output;
456 }
457 int FFMPEGConfigAudioToggle::handle_event()
458 {
459         *output = get_value();
460         return 1;
461 }
462
463
464 //======
465
466 FFMPEGConfigVideo::FFMPEGConfigVideo(BC_WindowBase *parent_window, Asset *asset)
467  : BC_Window(_(PROGRAM_NAME ": Video Preset"),
468         parent_window->get_abs_cursor_x(1),
469         parent_window->get_abs_cursor_y(1),
470         420, 420)
471 {
472         this->parent_window = parent_window;
473         this->asset = asset;
474         preset_popup = 0;
475
476         bitrate = 0;
477         quality = 0;
478         video_options = 0;
479 }
480
481 FFMPEGConfigVideo::~FFMPEGConfigVideo()
482 {
483         lock_window("FFMPEGConfigVideo::~FFMPEGConfigVideo");
484         if(preset_popup) delete preset_popup;
485         presets.remove_all_objects();
486         unlock_window();
487 }
488
489 void FFMPEGConfigVideo::create_objects()
490 {
491         int x = 10, y = 10;
492         lock_window("FFMPEGConfigVideo::create_objects");
493
494         add_subwindow(new BC_Title(x, y, _("Compression:")));
495         y += 25;
496
497         FileSystem fs;
498         char option_path[BCTEXTLEN];
499         FFMPEG::set_option_path(option_path, "video");
500         fs.update(option_path);
501         int total_files = fs.total_files();
502         for(int i = 0; i < total_files; i++) {
503                 const char *name = fs.get_entry(i)->get_name();
504                 if( asset->fformat[0] != 0 ) {
505                         const char *ext = strrchr(name,'.');
506                         if( !ext ) continue;
507                         if( strcmp(asset->fformat, ++ext) ) continue;
508                 }
509                 presets.append(new BC_ListBoxItem(name));
510         }
511
512         if( asset->vcodec[0] ) {
513                 int k = presets.size();
514                 while( --k >= 0 && strcmp(asset->vcodec, presets[k]->get_text()) );
515                 if( k < 0 ) asset->vcodec[0] = 0;
516         }
517
518         if( !asset->vcodec[0] && presets.size() > 0 )
519                 strcpy(asset->vcodec, presets[0]->get_text());
520
521         preset_popup = new FFMPEGConfigVideoPopup(this, x, y);
522         preset_popup->create_objects();
523
524         if( asset->ff_video_bitrate && asset->ff_video_quality ) {
525                 asset->ff_video_bitrate = 0;
526                 asset->ff_video_quality = 0;
527         }
528
529         y += 50;
530         bitrate = new FFMpegVideoBitrate(this, x, y, _("Bitrate:"), &asset->ff_video_bitrate);
531         bitrate->create_objects();
532         bitrate->set_increment(100000);
533         y += bitrate->get_h() + 5;
534         quality = new FFMpegVideoQuality(this, x, y, _("Quality:"), &asset->ff_video_quality);
535         quality->create_objects();
536         quality->set_increment(1);
537         quality->set_boundaries((int64_t)0, (int64_t)31);
538
539         y += quality->get_h() + 10;
540         add_subwindow(new BC_Title(x, y, _("Video Options:")));
541         y += 25;
542         if( !asset->ff_video_options[0] && asset->vcodec[0] ) {
543                 FFMPEG::set_option_path(option_path, "video/%s", asset->vcodec);
544                 FFMPEG::load_options(option_path, asset->ff_video_options,
545                          sizeof(asset->ff_video_options));
546         }
547         add_subwindow(video_options = new FFVideoOptions(this, x, y, get_w()-x-20, 10,
548                  sizeof(asset->ff_video_options)-1, asset->ff_video_options));
549
550         add_subwindow(new BC_OKButton(this));
551         show_window(1);
552         if( asset->ff_video_bitrate )
553                 quality->disable();
554         if( asset->ff_video_quality )
555                 bitrate->disable();
556         unlock_window();
557 }
558
559 int FFMPEGConfigVideo::close_event()
560 {
561         set_done(0);
562         return 1;
563 }
564
565
566 FFVideoOptions::FFVideoOptions(FFMPEGConfigVideo *video_popup,
567         int x, int y, int w, int rows, int size, char *text)
568  : BC_TextBox(x, y, w, rows, size, text)
569 {
570         this->video_popup = video_popup;
571 }
572
573 int FFVideoOptions::handle_event()
574 {
575         strcpy(video_popup->asset->ff_video_options, get_text());
576         return 1;
577 }
578
579
580 FFMPEGConfigVideoPopup::FFMPEGConfigVideoPopup(FFMPEGConfigVideo *popup, int x, int y)
581  : BC_PopupTextBox(popup, &popup->presets, popup->asset->vcodec, x, y, 300, 300)
582 {
583         this->popup = popup;
584 }
585
586 int FFMPEGConfigVideoPopup::handle_event()
587 {
588         strcpy(popup->asset->vcodec, get_text());
589         Asset *asset = popup->asset;
590         char option_path[BCTEXTLEN];
591         FFMPEG::set_option_path(option_path, "video/%s", asset->vcodec);
592         FFMPEG::load_options(option_path, asset->ff_video_options,
593                          sizeof(asset->ff_video_options));
594         popup->video_options->update(asset->ff_video_options);
595         return 1;
596 }
597
598
599 FFMPEGConfigVideoToggle::FFMPEGConfigVideoToggle(FFMPEGConfigVideo *popup,
600         char *title_text, int x, int y, int *output)
601  : BC_CheckBox(x, y, *output, title_text)
602 {
603         this->popup = popup;
604         this->output = output;
605 }
606 int FFMPEGConfigVideoToggle::handle_event()
607 {
608         *output = get_value();
609         return 1;
610 }
611
612 FFMPEGScanProgress::FFMPEGScanProgress(const char *title, int64_t length, int64_t *position, int *canceled)
613  : Thread(1, 0, 0)
614 {
615         strcpy(this->progress_title, title);
616         this->length = length;
617         this->position = position;
618         this->canceled = canceled;
619         done = 0;
620         progress = 0;
621         start();
622 }
623
624 FFMPEGScanProgress::~FFMPEGScanProgress()
625 {
626         done = 1;
627         cancel();
628         join();
629 }
630
631 void FFMPEGScanProgress::run()
632 {
633         BC_ProgressBox *progress = new BC_ProgressBox(-1, -1, progress_title, length);
634         progress->start();
635
636         struct timeval start_time, now, then;
637         gettimeofday(&start_time, 0);
638         then = start_time;
639
640         while( !done ) {
641                 if( progress->update(*position, 1) ) {
642                         *canceled = done = 1;
643                         break;
644                 }
645                 gettimeofday(&now, 0);
646                 if(now.tv_sec - then.tv_sec >= 1) {
647                         int64_t elapsed = now.tv_sec - start_time.tv_sec;
648                         int64_t byte_rate = *position / elapsed;
649                         int64_t eta = !byte_rate ? 0 : (length - *position) / byte_rate;
650                         char string[BCTEXTLEN];
651                         sprintf(string, "%s\nETA: " _LD "m" _LD "s",
652                                 progress_title, eta / 60, eta % 60);
653                         progress->update_title(string, 1);
654                         then = now;
655                 }
656                 usleep(500000);
657         }
658
659         progress->stop_progress();
660         delete progress;
661 }
662
663 int FileFFMPEG::get_index(char *index_path)
664 {
665         if( !ff ) return -1;
666         if( !file->preferences->ffmpeg_marker_indecies ) return 1;
667
668         IndexState *index_state = asset->index_state;
669         if( index_state->index_status != INDEX_NOTTESTED ) return 0;
670         index_state->index_status = INDEX_BUILDING;
671
672         for( int aidx=0; aidx<ff->ffaudio.size(); ++aidx ) {
673                 FFAudioStream *aud = ff->ffaudio[aidx];
674                 index_state->add_audio_stream(aud->channels, aud->length);
675         }
676
677         char progress_title[BCTEXTLEN];
678         sprintf(progress_title, _("Creating %s\n"), index_path);
679         FileSystem fs;
680         int64_t file_bytes = fs.get_size(ff->fmt_ctx->filename);
681         int64_t scan_position = 0;
682         int canceled = 0;
683         FFMPEGScanProgress scan_progress(progress_title, file_bytes, &scan_position, &canceled);
684
685         index_state->index_bytes = file_bytes;
686         index_state->init_scan(file->preferences->index_size);
687         if( ff->scan(index_state, &scan_position, &canceled) || canceled ) {
688                 index_state->reset_index();
689                 index_state->reset_markers();
690                 return 1;
691         }
692         index_state->marker_status = MARKERS_READY;
693         return index_state->create_index(index_path, asset);
694 }
695