prevent popup deactivation while button_down
[goodguy/history.git] / cinelerra-5.0 / cinelerra / fileffmpeg.C
index 2ef7d625af60998cc8dd27af2f4f77f7abb53ceb..1972a9eca9634824856e1427aecf285b0860e038 100644 (file)
@@ -9,13 +9,17 @@
 
 #include "asset.h"
 #include "bcwindowbase.h"
+#include "bcprogressbox.h"
 #include "bitspopup.h"
 #include "ffmpeg.h"
 #include "filebase.h"
 #include "file.h"
 #include "fileffmpeg.h"
 #include "filesystem.h"
+#include "format.inc"
+#include "indexfile.h"
 #include "mutex.h"
+#include "preferences.h"
 #include "videodevice.inc"
 
 FileFFMPEG::FileFFMPEG(Asset *asset, File *file)
@@ -32,6 +36,73 @@ FileFFMPEG::~FileFFMPEG()
 }
 
 
+FFMpegConfigNum::FFMpegConfigNum(BC_Window *window,
+               int x, int y, char *title_text, int *output)
+ : BC_TumbleTextBox(window, (int64_t)*output,
+       (int64_t)-1, (int64_t)25000000, 100, y, 100)
+{
+       this->window = window;
+       this->x = x;  this->y = y;
+       this->title_text = title_text;
+       this->output = output;
+}
+
+FFMpegConfigNum::~FFMpegConfigNum()
+{
+}
+
+void FFMpegConfigNum::create_objects()
+{
+       window->add_subwindow(title = new BC_Title(x, y, title_text));
+       BC_TumbleTextBox::create_objects();
+}
+
+int FFMpegConfigNum::handle_event()
+{
+       *output = atol(get_text());
+       return 1;
+}
+
+FFMpegAudioNum::FFMpegAudioNum(BC_Window *window,
+               int x, int y, char *title_text, int *output)
+ : FFMpegConfigNum(window, x, y, title_text, output)
+{
+}
+
+int FFMpegAudioBitrate::handle_event()
+{
+       int ret = FFMpegAudioNum::handle_event();
+       return ret;
+}
+
+FFMpegVideoNum::FFMpegVideoNum(BC_Window *window,
+               int x, int y, char *title_text, int *output)
+ : FFMpegConfigNum(window, x, y, title_text, output)
+{
+}
+
+int FFMpegVideoBitrate::handle_event()
+{
+       int ret = FFMpegVideoNum::handle_event();
+       Asset *asset = window()->asset;
+       if( asset->ff_video_bitrate )
+               window()->quality->disable();
+       else
+               window()->quality->enable();
+       return ret;
+}
+
+int FFMpegVideoQuality::handle_event()
+{
+       int ret = FFMpegVideoNum::handle_event();
+       Asset *asset = window()->asset;
+       if( asset->ff_video_quality )
+               window()->bitrate->disable();
+       else
+               window()->bitrate->enable();
+       return ret;
+}
+
 void FileFFMPEG::get_parameters(BC_WindowBase *parent_window,
                Asset *asset, BC_WindowBase *&format_window,
                int audio_options, int video_options)
@@ -65,28 +136,28 @@ int FileFFMPEG::check_sig(Asset *asset)
        return ret;
 }
 
-void FileFFMPEG::get_info(char *path, char *text)
+void FileFFMPEG::get_info(char *path, char *text, int len)
 {
        char *cp = text;
        FFMPEG ffmpeg(0);
-       cp += sprintf(cp, "file path: %s\n", path);
+       cp += sprintf(cp, _("file path: %s\n"), path);
        struct stat st;
        int ret = 0;
        if( stat(path, &st) < 0 ) {
-               cp += sprintf(cp, " err: %s\n", strerror(errno));
+               cp += sprintf(cp, _(" err: %s\n"), strerror(errno));
                ret = 1;
        }
        else {
-               cp += sprintf(cp, "  %jd bytes\n", st.st_size);
+               cp += sprintf(cp, _("  %jd bytes\n"), st.st_size);
        }
        if( !ret ) ret = ffmpeg.init_decoder(path);
        if( !ret ) ret = ffmpeg.open_decoder();
        if( !ret ) {
-               cp += sprintf(cp, "info:\n");
-               ffmpeg.info(cp, BCTEXTLEN-(cp-text));
+               cp += sprintf(cp, _("info:\n"));
+               ffmpeg.info(cp, len-(cp-text));
        }
        else
-               sprintf(cp, "== open failed\n");
+               sprintf(cp, _("== open failed\n"));
 }
 
 int FileFFMPEG::get_video_info(int track, int &pid, double &framerate,
@@ -120,7 +191,6 @@ int FileFFMPEG::select_video_stream(Asset *asset, int vstream)
 int FileFFMPEG::select_audio_stream(Asset *asset, int astream)
 {
        if( !ff || !asset->audio_data ) return 1;
-       asset->channels = ff->ff_audio_channels(astream);
        asset->sample_rate = ff->ff_sample_rate(astream);
                asset->audio_length = ff->ff_audio_samples(astream);
        return 0;
@@ -154,6 +224,8 @@ int FileFFMPEG::open_file(int rd, int wr)
                                if( !asset->video_length ) asset->video_length = ff->ff_video_frames(0);
                                if( !asset->frame_rate ) asset->frame_rate = ff->ff_frame_rate(0);
                        }
+                       IndexState *index_state = asset->index_state;
+                       index_state->read_markers(file->preferences->index_directory, asset->path);
                }
        }
        else if( wr ) {
@@ -175,22 +247,6 @@ int FileFFMPEG::close_file()
 }
 
 
-int FileFFMPEG::set_video_position(int64_t pos)
-{
-        if( !ff || pos < 0 || pos >= asset->video_length )
-               return 1;
-       return 0;
-}
-
-
-int FileFFMPEG::set_audio_position(int64_t pos)
-{
-        if( !ff || pos < 0 || pos >= asset->audio_length )
-               return 1;
-       return 0;
-}
-
-
 int FileFFMPEG::write_samples(double **buffer, int64_t len)
 {
         if( !ff || len < 0 ) return -1;
@@ -218,8 +274,10 @@ int FileFFMPEG::read_samples(double *buffer, int64_t len)
         if( !ff || len < 0 ) return -1;
        int ch = file->current_channel;
        int64_t pos = file->current_sample;
-       ff->decode(ch, pos, buffer, len);
-        return 0;
+       int ret = ff->decode(ch, pos, buffer, len);
+       if( ret > 0 ) return 0;
+       memset(buffer,0,len*sizeof(*buffer));
+        return -1;
 }
 
 int FileFFMPEG::read_frame(VFrame *frame)
@@ -227,8 +285,11 @@ int FileFFMPEG::read_frame(VFrame *frame)
         if( !ff ) return -1;
        int layer = file->current_layer;
        int64_t pos = file->current_frame;
-       ff->decode(layer, pos, frame);
-       return 0;
+       int ret = ff->decode(layer, pos, frame);
+       frame->set_status(ret);
+       if( ret >= 0 ) return 0;
+       frame->clear_frame();
+       return -1;
 }
 
 
@@ -251,12 +312,10 @@ int FileFFMPEG::get_best_colormodel(Asset *asset, int driver)
        return BC_YUV420P;
 }
 
-
 //======
-extern void get_exe_path(char *result); // from main.C
 
 FFMPEGConfigAudio::FFMPEGConfigAudio(BC_WindowBase *parent_window, Asset *asset)
- : BC_Window(PROGRAM_NAME ": Audio Preset",
+ : BC_Window(_(PROGRAM_NAME ": Audio Preset"),
        parent_window->get_abs_cursor_x(1),
        parent_window->get_abs_cursor_y(1),
        420, 420)
@@ -264,6 +323,9 @@ FFMPEGConfigAudio::FFMPEGConfigAudio(BC_WindowBase *parent_window, Asset *asset)
        this->parent_window = parent_window;
        this->asset = asset;
        preset_popup = 0;
+
+       bitrate = 0;
+       audio_options = 0;
 }
 
 FFMPEGConfigAudio::~FFMPEGConfigAudio()
@@ -281,21 +343,51 @@ void FFMPEGConfigAudio::create_objects()
 
         FileSystem fs;
        char option_path[BCTEXTLEN];
-       FFMPEG::set_option_path(option_path, "/audio");
+       FFMPEG::set_option_path(option_path, "audio");
         fs.update(option_path);
         int total_files = fs.total_files();
         for(int i = 0; i < total_files; i++) {
                 const char *name = fs.get_entry(i)->get_name();
+               if( asset->fformat[0] != 0 ) {
+                       const char *ext = strrchr(name,'.');
+                       if( !ext ) continue;
+                       if( strcmp(asset->fformat, ++ext) ) continue;
+               }
                 presets.append(new BC_ListBoxItem(name));
         }
 
+       if( asset->acodec[0] ) {
+               int k = presets.size();
+               while( --k >= 0 && strcmp(asset->acodec, presets[k]->get_text()) );
+               if( k < 0 ) asset->acodec[0] = 0;
+       }
+
+       if( !asset->acodec[0] && presets.size() > 0 )
+               strcpy(asset->acodec, presets[0]->get_text());
+
        add_tool(new BC_Title(x, y, _("Preset:")));
        y += 25;
        preset_popup = new FFMPEGConfigAudioPopup(this, x, y);
        preset_popup->create_objects();
 
+       y += 50;
+       bitrate = new FFMpegAudioBitrate(this, x, y, _("Bitrate:"), &asset->ff_audio_bitrate);
+       bitrate->create_objects();
+       bitrate->set_increment(1000);
+
+       y += bitrate->get_h() + 10;
+       add_subwindow(new BC_Title(x, y, _("Audio Options:")));
+       y += 25;
+       if( !asset->ff_audio_options[0] && asset->acodec[0] ) {
+               FFMPEG::set_option_path(option_path, "audio/%s", asset->acodec);
+               FFMPEG::load_options(option_path, asset->ff_audio_options,
+                        sizeof(asset->ff_audio_options));
+       }
+       add_subwindow(audio_options = new FFAudioOptions(this, x, y, get_w()-x-20, 10,
+                sizeof(asset->ff_audio_options)-1, asset->ff_audio_options));
        add_subwindow(new BC_OKButton(this));
        show_window(1);
+       bitrate->handle_event();
        unlock_window();
 }
 
@@ -306,6 +398,20 @@ int FFMPEGConfigAudio::close_event()
 }
 
 
+FFAudioOptions::FFAudioOptions(FFMPEGConfigAudio *audio_popup,
+       int x, int y, int w, int rows, int size, char *text)
+ : BC_TextBox(x, y, w, rows, size, text)
+{
+       this->audio_popup = audio_popup;
+}
+
+int FFAudioOptions::handle_event()
+{
+       strcpy(audio_popup->asset->ff_audio_options, get_text());
+       return 1;
+}
+
+
 FFMPEGConfigAudioPopup::FFMPEGConfigAudioPopup(FFMPEGConfigAudio *popup, int x, int y)
  : BC_PopupTextBox(popup, &popup->presets, popup->asset->acodec, x, y, 300, 300)
 {
@@ -315,6 +421,12 @@ FFMPEGConfigAudioPopup::FFMPEGConfigAudioPopup(FFMPEGConfigAudio *popup, int x,
 int FFMPEGConfigAudioPopup::handle_event()
 {
        strcpy(popup->asset->acodec, get_text());
+       Asset *asset = popup->asset;
+       char option_path[BCTEXTLEN];
+       FFMPEG::set_option_path(option_path, "audio/%s", asset->acodec);
+       FFMPEG::load_options(option_path, asset->ff_audio_options,
+                        sizeof(asset->ff_audio_options));
+       popup->audio_options->update(asset->ff_audio_options);
        return 1;
 }
 
@@ -336,7 +448,7 @@ int FFMPEGConfigAudioToggle::handle_event()
 //======
 
 FFMPEGConfigVideo::FFMPEGConfigVideo(BC_WindowBase *parent_window, Asset *asset)
- : BC_Window(PROGRAM_NAME ": Video Preset",
+ : BC_Window(_(PROGRAM_NAME ": Video Preset"),
        parent_window->get_abs_cursor_x(1),
        parent_window->get_abs_cursor_y(1),
        420, 420)
@@ -344,6 +456,10 @@ FFMPEGConfigVideo::FFMPEGConfigVideo(BC_WindowBase *parent_window, Asset *asset)
        this->parent_window = parent_window;
        this->asset = asset;
        preset_popup = 0;
+
+       bitrate = 0;
+       quality = 0;
+       video_options = 0;
 }
 
 FFMPEGConfigVideo::~FFMPEGConfigVideo()
@@ -369,14 +485,58 @@ void FFMPEGConfigVideo::create_objects()
         int total_files = fs.total_files();
         for(int i = 0; i < total_files; i++) {
                 const char *name = fs.get_entry(i)->get_name();
+               if( asset->fformat[0] != 0 ) {
+                       const char *ext = strrchr(name,'.');
+                       if( !ext ) continue;
+                       if( strcmp(asset->fformat, ++ext) ) continue;
+               }
                 presets.append(new BC_ListBoxItem(name));
         }
 
+       if( asset->vcodec[0] ) {
+               int k = presets.size();
+               while( --k >= 0 && strcmp(asset->vcodec, presets[k]->get_text()) );
+               if( k < 0 ) asset->vcodec[0] = 0;
+       }
+
+       if( !asset->vcodec[0] && presets.size() > 0 )
+               strcpy(asset->vcodec, presets[0]->get_text());
+
        preset_popup = new FFMPEGConfigVideoPopup(this, x, y);
        preset_popup->create_objects();
 
+       if( asset->ff_video_bitrate && asset->ff_video_quality ) {
+               asset->ff_video_bitrate = 0;
+               asset->ff_video_quality = 0;
+       }
+
+       y += 50;
+       bitrate = new FFMpegVideoBitrate(this, x, y, _("Bitrate:"), &asset->ff_video_bitrate);
+       bitrate->create_objects();
+       bitrate->set_increment(100000);
+       y += bitrate->get_h() + 5;
+       quality = new FFMpegVideoQuality(this, x, y, _("Quality:"), &asset->ff_video_quality);
+       quality->create_objects();
+       quality->set_increment(1);
+       quality->set_boundaries((int64_t)0, (int64_t)31);
+
+       y += quality->get_h() + 10;
+       add_subwindow(new BC_Title(x, y, _("Video Options:")));
+       y += 25;
+       if( !asset->ff_video_options[0] && asset->vcodec[0] ) {
+               FFMPEG::set_option_path(option_path, "video/%s", asset->vcodec);
+               FFMPEG::load_options(option_path, asset->ff_video_options,
+                        sizeof(asset->ff_video_options));
+       }
+       add_subwindow(video_options = new FFVideoOptions(this, x, y, get_w()-x-20, 10,
+                sizeof(asset->ff_video_options)-1, asset->ff_video_options));
+
        add_subwindow(new BC_OKButton(this));
        show_window(1);
+       if( asset->ff_video_bitrate )
+               quality->disable();
+       if( asset->ff_video_quality )
+               bitrate->disable();
        unlock_window();
 }
 
@@ -387,6 +547,20 @@ int FFMPEGConfigVideo::close_event()
 }
 
 
+FFVideoOptions::FFVideoOptions(FFMPEGConfigVideo *video_popup,
+       int x, int y, int w, int rows, int size, char *text)
+ : BC_TextBox(x, y, w, rows, size, text)
+{
+       this->video_popup = video_popup;
+}
+
+int FFVideoOptions::handle_event()
+{
+       strcpy(video_popup->asset->ff_video_options, get_text());
+       return 1;
+}
+
+
 FFMPEGConfigVideoPopup::FFMPEGConfigVideoPopup(FFMPEGConfigVideo *popup, int x, int y)
  : BC_PopupTextBox(popup, &popup->presets, popup->asset->vcodec, x, y, 300, 300)
 {
@@ -396,6 +570,12 @@ FFMPEGConfigVideoPopup::FFMPEGConfigVideoPopup(FFMPEGConfigVideo *popup, int x,
 int FFMPEGConfigVideoPopup::handle_event()
 {
        strcpy(popup->asset->vcodec, get_text());
+       Asset *asset = popup->asset;
+       char option_path[BCTEXTLEN];
+       FFMPEG::set_option_path(option_path, "video/%s", asset->vcodec);
+       FFMPEG::load_options(option_path, asset->ff_video_options,
+                        sizeof(asset->ff_video_options));
+       popup->video_options->update(asset->ff_video_options);
        return 1;
 }
 
@@ -413,4 +593,87 @@ int FFMPEGConfigVideoToggle::handle_event()
        return 1;
 }
 
+FFMPEGScanProgress::FFMPEGScanProgress(const char *title, int64_t length, int64_t *position, int *canceled)
+ : Thread(1, 0, 0)
+{
+       strcpy(this->progress_title, title);
+       this->length = length;
+       this->position = position;
+       this->canceled = canceled;
+       done = 0;
+       progress = 0;
+       start();
+}
+
+FFMPEGScanProgress::~FFMPEGScanProgress()
+{
+       done = 1;
+       cancel();
+       join();
+}
+
+void FFMPEGScanProgress::run()
+{
+       BC_ProgressBox *progress = new BC_ProgressBox(-1, -1, progress_title, length);
+       progress->start();
+
+       struct timeval start_time, now, then;
+       gettimeofday(&start_time, 0);
+       then = start_time;
+
+       while( !done ) {
+               if( progress->update(*position, 1) ) {
+                       *canceled = done = 1;
+                       break;
+               }
+               gettimeofday(&now, 0);
+               if(now.tv_sec - then.tv_sec >= 1) {
+                       int64_t elapsed = now.tv_sec - start_time.tv_sec;
+                       int64_t byte_rate = *position / elapsed;
+                       int64_t eta = !byte_rate ? 0 : (length - *position) / byte_rate;
+                       char string[BCTEXTLEN];
+                       sprintf(string, "%s\nETA: " _LD "m" _LD "s",
+                               progress_title, eta / 60, eta % 60);
+                       progress->update_title(string, 1);
+                       then = now;
+               }
+               usleep(500000);
+       }
+
+       progress->stop_progress();
+       delete progress;
+}
+
+int FileFFMPEG::get_index(char *index_path)
+{
+       if( !ff ) return -1;
+       if( !file->preferences->ffmpeg_marker_indecies ) return 1;
+
+       IndexState *index_state = asset->index_state;
+       if( index_state->index_status != INDEX_NOTTESTED ) return 0;
+       index_state->index_status = INDEX_BUILDING;
+
+       for( int aidx=0; aidx<ff->ffaudio.size(); ++aidx ) {
+               FFAudioStream *aud = ff->ffaudio[aidx];
+               index_state->add_audio_stream(aud->channels, aud->length);
+       }
+
+       char progress_title[BCTEXTLEN];
+       sprintf(progress_title, _("Creating %s\n"), index_path);
+        FileSystem fs;
+       int64_t file_bytes = fs.get_size(ff->fmt_ctx->filename);
+       int64_t scan_position = 0;
+       int canceled = 0;
+       FFMPEGScanProgress scan_progress(progress_title, file_bytes, &scan_position, &canceled);
+
+       index_state->index_bytes = file_bytes;
+       index_state->init_scan(file->preferences->index_size);
+       if( ff->scan(index_state, &scan_position, &canceled) || canceled ) {
+               index_state->reset_index();
+               index_state->reset_markers();
+               return 1;
+       }
+       index_state->marker_status = MARKERS_READY;
+       return index_state->create_index(index_path, asset);
+}