7 // work around for __STDC_CONSTANT_MACROS
11 #include "bcwindowbase.h"
12 #include "bitspopup.h"
16 #include "fileffmpeg.h"
17 #include "filesystem.h"
18 #include "indexfile.h"
19 #include "mainprogress.h"
21 #include "preferences.h"
22 #include "videodevice.inc"
24 FileFFMPEG::FileFFMPEG(Asset *asset, File *file)
25 : FileBase(asset, file)
28 if(asset->format == FILE_UNKNOWN)
29 asset->format = FILE_FFMPEG;
32 FileFFMPEG::~FileFFMPEG()
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)
43 this->window = window;
44 this->x = x; this->y = y;
45 this->title_text = title_text;
46 this->output = output;
49 FFMpegConfigNum::~FFMpegConfigNum()
53 void FFMpegConfigNum::create_objects()
55 window->add_subwindow(title = new BC_Title(x, y, title_text));
56 BC_TumbleTextBox::create_objects();
59 int FFMpegConfigNum::handle_event()
61 *output = atol(get_text());
65 FFMpegAudioNum::FFMpegAudioNum(BC_Window *window,
66 int x, int y, char *title_text, int *output)
67 : FFMpegConfigNum(window, x, y, title_text, output)
71 int FFMpegAudioBitrate::handle_event()
73 int ret = FFMpegAudioNum::handle_event();
77 FFMpegVideoNum::FFMpegVideoNum(BC_Window *window,
78 int x, int y, char *title_text, int *output)
79 : FFMpegConfigNum(window, x, y, title_text, output)
83 int FFMpegVideoBitrate::handle_event()
85 int ret = FFMpegVideoNum::handle_event();
86 Asset *asset = window()->asset;
87 if( asset->ff_video_bitrate )
88 window()->quality->disable();
90 window()->quality->enable();
94 int FFMpegVideoQuality::handle_event()
96 int ret = FFMpegVideoNum::handle_event();
97 Asset *asset = window()->asset;
98 if( asset->ff_video_quality )
99 window()->bitrate->disable();
101 window()->bitrate->enable();
105 void FileFFMPEG::get_parameters(BC_WindowBase *parent_window,
106 Asset *asset, BC_WindowBase *&format_window,
107 int audio_options, int video_options)
110 FFMPEGConfigAudio *window = new FFMPEGConfigAudio(parent_window, asset);
111 format_window = window;
112 window->create_objects();
113 window->run_window();
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();
125 int FileFFMPEG::check_sig(Asset *asset)
127 char *ptr = strstr(asset->path, ".pcm");
129 ptr = strstr(asset->path, ".raw");
133 int ret = !ffmpeg.init_decoder(asset->path) &&
134 !ffmpeg.open_decoder() ? 1 : 0;
138 void FileFFMPEG::get_info(char *path, char *text, int len)
142 cp += sprintf(cp, _("file path: %s\n"), path);
145 if( stat(path, &st) < 0 ) {
146 cp += sprintf(cp, _(" err: %s\n"), strerror(errno));
150 cp += sprintf(cp, _(" %jd bytes\n"), st.st_size);
152 if( !ret ) ret = ffmpeg.init_decoder(path);
153 if( !ret ) ret = ffmpeg.open_decoder();
155 cp += sprintf(cp, _("info:\n"));
156 ffmpeg.info(cp, len-(cp-text));
159 sprintf(cp, _("== open failed\n"));
162 int FileFFMPEG::get_video_info(int track, int &pid, double &framerate,
163 int &width, int &height, char *title)
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;
174 int FileFFMPEG::get_audio_for_video(int vstream, int astream, int64_t &channel_mask)
177 return ff->ff_audio_for_video(vstream, astream, channel_mask);
180 int FileFFMPEG::select_video_stream(Asset *asset, int vstream)
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);
192 int FileFFMPEG::select_audio_stream(Asset *asset, int astream)
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);
200 int FileFFMPEG::open_file(int rd, int wr)
204 ff = new FFMPEG(this);
207 result = ff->init_decoder(asset->path);
208 if( !result ) result = ff->open_decoder();
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);
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);
230 IndexState *index_state = asset->index_state;
231 index_state->read_markers(file->preferences->index_directory, asset->path);
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);
245 int FileFFMPEG::close_file()
253 int FileFFMPEG::write_samples(double **buffer, int64_t len)
255 if( !ff || len < 0 ) return -1;
257 int ret = ff->encode(stream, buffer, len);
261 int FileFFMPEG::write_frames(VFrame ***frames, int len)
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);
275 int FileFFMPEG::read_samples(double *buffer, int64_t len)
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));
286 int FileFFMPEG::read_frame(VFrame *frame)
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();
299 int64_t FileFFMPEG::get_memory_usage()
304 int FileFFMPEG::colormodel_supported(int colormodel)
309 int FileFFMPEG::get_best_colormodel(Asset *asset, int driver)
312 case PLAYBACK_X11: return BC_RGB888;
313 case PLAYBACK_X11_GL: return BC_YUV888;
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),
326 this->parent_window = parent_window;
334 FFMPEGConfigAudio::~FFMPEGConfigAudio()
336 lock_window("FFMPEGConfigAudio::~FFMPEGConfigAudio");
337 if(preset_popup) delete preset_popup;
338 presets.remove_all_objects();
342 void FFMPEGConfigAudio::create_objects()
345 lock_window("FFMPEGConfigAudio::create_objects");
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,'.');
357 if( strcmp(asset->fformat, ++ext) ) continue;
359 presets.append(new BC_ListBoxItem(name));
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;
368 if( !asset->acodec[0] && presets.size() > 0 )
369 strcpy(asset->acodec, presets[0]->get_text());
371 add_tool(new BC_Title(x, y, _("Preset:")));
373 preset_popup = new FFMPEGConfigAudioPopup(this, x, y);
374 preset_popup->create_objects();
377 bitrate = new FFMpegAudioBitrate(this, x, y, _("Bitrate:"), &asset->ff_audio_bitrate);
378 bitrate->create_objects();
379 bitrate->set_increment(1000);
381 y += bitrate->get_h() + 10;
382 add_subwindow(new BC_Title(x, y, _("Audio Options:")));
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));
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));
393 bitrate->handle_event();
397 int FFMPEGConfigAudio::close_event()
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)
408 this->audio_popup = audio_popup;
411 int FFAudioOptions::handle_event()
413 strcpy(audio_popup->asset->ff_audio_options, get_text());
418 FFMPEGConfigAudioPopup::FFMPEGConfigAudioPopup(FFMPEGConfigAudio *popup, int x, int y)
419 : BC_PopupTextBox(popup, &popup->presets, popup->asset->acodec, x, y, 300, 300)
424 int FFMPEGConfigAudioPopup::handle_event()
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);
437 FFMPEGConfigAudioToggle::FFMPEGConfigAudioToggle(FFMPEGConfigAudio *popup,
438 char *title_text, int x, int y, int *output)
439 : BC_CheckBox(x, y, *output, title_text)
442 this->output = output;
444 int FFMPEGConfigAudioToggle::handle_event()
446 *output = get_value();
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),
459 this->parent_window = parent_window;
468 FFMPEGConfigVideo::~FFMPEGConfigVideo()
470 lock_window("FFMPEGConfigVideo::~FFMPEGConfigVideo");
471 if(preset_popup) delete preset_popup;
472 presets.remove_all_objects();
476 void FFMPEGConfigVideo::create_objects()
479 lock_window("FFMPEGConfigVideo::create_objects");
481 add_subwindow(new BC_Title(x, y, _("Compression:")));
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,'.');
494 if( strcmp(asset->fformat, ++ext) ) continue;
496 presets.append(new BC_ListBoxItem(name));
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;
505 if( !asset->vcodec[0] && presets.size() > 0 )
506 strcpy(asset->vcodec, presets[0]->get_text());
508 preset_popup = new FFMPEGConfigVideoPopup(this, x, y);
509 preset_popup->create_objects();
511 if( asset->ff_video_bitrate && asset->ff_video_quality ) {
512 asset->ff_video_bitrate = 0;
513 asset->ff_video_quality = 0;
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);
526 y += quality->get_h() + 10;
527 add_subwindow(new BC_Title(x, y, _("Video Options:")));
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));
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));
537 add_subwindow(new BC_OKButton(this));
539 if( asset->ff_video_bitrate )
541 if( asset->ff_video_quality )
546 int FFMPEGConfigVideo::close_event()
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)
557 this->video_popup = video_popup;
560 int FFVideoOptions::handle_event()
562 strcpy(video_popup->asset->ff_video_options, get_text());
567 FFMPEGConfigVideoPopup::FFMPEGConfigVideoPopup(FFMPEGConfigVideo *popup, int x, int y)
568 : BC_PopupTextBox(popup, &popup->presets, popup->asset->vcodec, x, y, 300, 300)
573 int FFMPEGConfigVideoPopup::handle_event()
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);
586 FFMPEGConfigVideoToggle::FFMPEGConfigVideoToggle(FFMPEGConfigVideo *popup,
587 char *title_text, int x, int y, int *output)
588 : BC_CheckBox(x, y, *output, title_text)
591 this->output = output;
593 int FFMPEGConfigVideoToggle::handle_event()
595 *output = get_value();
599 FFMPEGScanProgress::FFMPEGScanProgress(IndexFile *index_file, MainProgressBar *progress_bar,
600 const char *title, int64_t length, int64_t *position, int *canceled)
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;
613 FFMPEGScanProgress::~FFMPEGScanProgress()
620 void FFMPEGScanProgress::run()
623 if( progress_bar->update(*position) ) {
624 *canceled = done = 1;
627 index_file->redraw_edits(0);
632 int FileFFMPEG::get_index(IndexFile *index_file, MainProgressBar *progress_bar)
635 if( !file->preferences->ffmpeg_marker_indexes ) return 1;
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;
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);
649 int64_t file_bytes = fs.get_size(ff->fmt_ctx->filename);
650 char *index_path = index_file->index_filename;
653 int64_t scan_position = 0;
654 FFMPEGScanProgress *scan_progress = 0;
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);
665 index_state->index_bytes = file_bytes;
666 index_state->init_scan(file->preferences->index_size);
668 if( ff->scan(index_state, &scan_position, &canceled) || canceled ) {
669 index_state->reset_index();
670 index_state->reset_markers();
674 delete scan_progress;
675 if( canceled ) return 1;
677 index_state->marker_status = MARKERS_READY;
678 return index_state->create_index(index_path, asset);