7 // work around for __STDC_CONSTANT_MACROS
11 #include "bcwindowbase.h"
12 #include "bcprogressbox.h"
13 #include "bitspopup.h"
17 #include "fileffmpeg.h"
18 #include "filesystem.h"
20 #include "indexfile.h"
22 #include "preferences.h"
23 #include "videodevice.inc"
25 FileFFMPEG::FileFFMPEG(Asset *asset, File *file)
26 : FileBase(asset, file)
29 if(asset->format == FILE_UNKNOWN)
30 asset->format = FILE_FFMPEG;
33 FileFFMPEG::~FileFFMPEG()
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)
44 this->window = window;
45 this->x = x; this->y = y;
46 this->title_text = title_text;
47 this->output = output;
50 FFMpegConfigNum::~FFMpegConfigNum()
54 void FFMpegConfigNum::create_objects()
56 window->add_subwindow(title = new BC_Title(x, y, title_text));
57 BC_TumbleTextBox::create_objects();
60 int FFMpegConfigNum::handle_event()
62 *output = atol(get_text());
66 FFMpegAudioNum::FFMpegAudioNum(BC_Window *window,
67 int x, int y, char *title_text, int *output)
68 : FFMpegConfigNum(window, x, y, title_text, output)
72 int FFMpegAudioBitrate::handle_event()
74 int ret = FFMpegAudioNum::handle_event();
78 FFMpegVideoNum::FFMpegVideoNum(BC_Window *window,
79 int x, int y, char *title_text, int *output)
80 : FFMpegConfigNum(window, x, y, title_text, output)
84 int FFMpegVideoBitrate::handle_event()
86 int ret = FFMpegVideoNum::handle_event();
87 Asset *asset = window()->asset;
88 if( asset->ff_video_bitrate )
89 window()->quality->disable();
91 window()->quality->enable();
95 int FFMpegVideoQuality::handle_event()
97 int ret = FFMpegVideoNum::handle_event();
98 Asset *asset = window()->asset;
99 if( asset->ff_video_quality )
100 window()->bitrate->disable();
102 window()->bitrate->enable();
106 void FileFFMPEG::get_parameters(BC_WindowBase *parent_window,
107 Asset *asset, BC_WindowBase *&format_window,
108 int audio_options, int video_options)
111 FFMPEGConfigAudio *window = new FFMPEGConfigAudio(parent_window, asset);
112 format_window = window;
113 window->create_objects();
114 window->run_window();
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();
126 int FileFFMPEG::check_sig(Asset *asset)
128 char *ptr = strstr(asset->path, ".pcm");
130 ptr = strstr(asset->path, ".raw");
134 int ret = !ffmpeg.init_decoder(asset->path) &&
135 !ffmpeg.open_decoder() ? 1 : 0;
139 void FileFFMPEG::get_info(char *path, char *text, int len)
143 cp += sprintf(cp, _("file path: %s\n"), path);
146 if( stat(path, &st) < 0 ) {
147 cp += sprintf(cp, _(" err: %s\n"), strerror(errno));
151 cp += sprintf(cp, _(" %jd bytes\n"), st.st_size);
153 if( !ret ) ret = ffmpeg.init_decoder(path);
154 if( !ret ) ret = ffmpeg.open_decoder();
156 cp += sprintf(cp, _("info:\n"));
157 ffmpeg.info(cp, len-(cp-text));
160 sprintf(cp, _("== open failed\n"));
163 int FileFFMPEG::get_video_info(int track, int &pid, double &framerate,
164 int &width, int &height, char *title)
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;
175 int FileFFMPEG::get_audio_for_video(int vstream, int astream, int64_t &channel_mask)
178 return ff->ff_audio_for_video(vstream, astream, channel_mask);
181 int FileFFMPEG::select_video_stream(Asset *asset, int vstream)
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);
191 int FileFFMPEG::select_audio_stream(Asset *asset, int astream)
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);
199 int FileFFMPEG::open_file(int rd, int wr)
203 ff = new FFMPEG(this);
206 result = ff->init_decoder(asset->path);
207 if( !result ) result = ff->open_decoder();
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);
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);
227 IndexState *index_state = asset->index_state;
228 index_state->read_markers(file->preferences->index_directory, asset->path);
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);
242 int FileFFMPEG::close_file()
250 int FileFFMPEG::set_video_position(int64_t pos)
252 if( !ff || pos < 0 || pos >= asset->video_length )
258 int FileFFMPEG::set_audio_position(int64_t pos)
260 if( !ff || pos < 0 || pos >= asset->audio_length )
266 int FileFFMPEG::write_samples(double **buffer, int64_t len)
268 if( !ff || len < 0 ) return -1;
270 int ret = ff->encode(stream, buffer, len);
274 int FileFFMPEG::write_frames(VFrame ***frames, int len)
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);
288 int FileFFMPEG::read_samples(double *buffer, int64_t len)
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));
299 int FileFFMPEG::read_frame(VFrame *frame)
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();
312 int64_t FileFFMPEG::get_memory_usage()
317 int FileFFMPEG::colormodel_supported(int colormodel)
322 int FileFFMPEG::get_best_colormodel(Asset *asset, int driver)
325 case PLAYBACK_X11: return BC_RGB888;
326 case PLAYBACK_X11_GL: return BC_YUV888;
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),
339 this->parent_window = parent_window;
347 FFMPEGConfigAudio::~FFMPEGConfigAudio()
349 lock_window("FFMPEGConfigAudio::~FFMPEGConfigAudio");
350 if(preset_popup) delete preset_popup;
351 presets.remove_all_objects();
355 void FFMPEGConfigAudio::create_objects()
358 lock_window("FFMPEGConfigAudio::create_objects");
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,'.');
370 if( strcmp(asset->fformat, ++ext) ) continue;
372 presets.append(new BC_ListBoxItem(name));
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;
381 if( !asset->acodec[0] && presets.size() > 0 )
382 strcpy(asset->acodec, presets[0]->get_text());
384 add_tool(new BC_Title(x, y, _("Preset:")));
386 preset_popup = new FFMPEGConfigAudioPopup(this, x, y);
387 preset_popup->create_objects();
390 bitrate = new FFMpegAudioBitrate(this, x, y, _("Bitrate:"), &asset->ff_audio_bitrate);
391 bitrate->create_objects();
392 bitrate->set_increment(1000);
394 y += bitrate->get_h() + 10;
395 add_subwindow(new BC_Title(x, y, _("Audio Options:")));
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));
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));
406 bitrate->handle_event();
410 int FFMPEGConfigAudio::close_event()
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)
421 this->audio_popup = audio_popup;
424 int FFAudioOptions::handle_event()
426 strcpy(audio_popup->asset->ff_audio_options, get_text());
431 FFMPEGConfigAudioPopup::FFMPEGConfigAudioPopup(FFMPEGConfigAudio *popup, int x, int y)
432 : BC_PopupTextBox(popup, &popup->presets, popup->asset->acodec, x, y, 300, 300)
437 int FFMPEGConfigAudioPopup::handle_event()
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);
450 FFMPEGConfigAudioToggle::FFMPEGConfigAudioToggle(FFMPEGConfigAudio *popup,
451 char *title_text, int x, int y, int *output)
452 : BC_CheckBox(x, y, *output, title_text)
455 this->output = output;
457 int FFMPEGConfigAudioToggle::handle_event()
459 *output = get_value();
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),
472 this->parent_window = parent_window;
481 FFMPEGConfigVideo::~FFMPEGConfigVideo()
483 lock_window("FFMPEGConfigVideo::~FFMPEGConfigVideo");
484 if(preset_popup) delete preset_popup;
485 presets.remove_all_objects();
489 void FFMPEGConfigVideo::create_objects()
492 lock_window("FFMPEGConfigVideo::create_objects");
494 add_subwindow(new BC_Title(x, y, _("Compression:")));
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,'.');
507 if( strcmp(asset->fformat, ++ext) ) continue;
509 presets.append(new BC_ListBoxItem(name));
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;
518 if( !asset->vcodec[0] && presets.size() > 0 )
519 strcpy(asset->vcodec, presets[0]->get_text());
521 preset_popup = new FFMPEGConfigVideoPopup(this, x, y);
522 preset_popup->create_objects();
524 if( asset->ff_video_bitrate && asset->ff_video_quality ) {
525 asset->ff_video_bitrate = 0;
526 asset->ff_video_quality = 0;
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);
539 y += quality->get_h() + 10;
540 add_subwindow(new BC_Title(x, y, _("Video Options:")));
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));
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));
550 add_subwindow(new BC_OKButton(this));
552 if( asset->ff_video_bitrate )
554 if( asset->ff_video_quality )
559 int FFMPEGConfigVideo::close_event()
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)
570 this->video_popup = video_popup;
573 int FFVideoOptions::handle_event()
575 strcpy(video_popup->asset->ff_video_options, get_text());
580 FFMPEGConfigVideoPopup::FFMPEGConfigVideoPopup(FFMPEGConfigVideo *popup, int x, int y)
581 : BC_PopupTextBox(popup, &popup->presets, popup->asset->vcodec, x, y, 300, 300)
586 int FFMPEGConfigVideoPopup::handle_event()
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);
599 FFMPEGConfigVideoToggle::FFMPEGConfigVideoToggle(FFMPEGConfigVideo *popup,
600 char *title_text, int x, int y, int *output)
601 : BC_CheckBox(x, y, *output, title_text)
604 this->output = output;
606 int FFMPEGConfigVideoToggle::handle_event()
608 *output = get_value();
612 FFMPEGScanProgress::FFMPEGScanProgress(const char *title, int64_t length, int64_t *position, int *canceled)
615 strcpy(this->progress_title, title);
616 this->length = length;
617 this->position = position;
618 this->canceled = canceled;
624 FFMPEGScanProgress::~FFMPEGScanProgress()
631 void FFMPEGScanProgress::run()
633 BC_ProgressBox *progress = new BC_ProgressBox(-1, -1, progress_title, length);
636 struct timeval start_time, now, then;
637 gettimeofday(&start_time, 0);
641 if( progress->update(*position, 1) ) {
642 *canceled = done = 1;
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);
659 progress->stop_progress();
663 int FileFFMPEG::get_index(char *index_path)
666 if( !file->preferences->ffmpeg_marker_indecies ) return 1;
668 IndexState *index_state = asset->index_state;
669 if( index_state->index_status != INDEX_NOTTESTED ) return 0;
670 index_state->index_status = INDEX_BUILDING;
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);
677 char progress_title[BCTEXTLEN];
678 sprintf(progress_title, _("Creating %s\n"), index_path);
680 int64_t file_bytes = fs.get_size(ff->fmt_ctx->filename);
681 int64_t scan_position = 0;
683 FFMPEGScanProgress scan_progress(progress_title, file_bytes, &scan_position, &canceled);
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();
692 index_state->marker_status = MARKERS_READY;
693 return index_state->create_index(index_path, asset);