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