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