00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024
00025 #include <iostream>
00026 #include <vector>
00027 using std::cerr;
00028 using std::endl;
00029
00030 #include <math.h>
00031 #include <unistd.h>
00032
00033 #include "export.h"
00034 #include "page_editor.h"
00035
00036 static void on_tool_change( GtkOptionMenu *optionmenu, gpointer user_data )
00037 {
00038 if ( gtk_option_menu_get_history( optionmenu ) == 2 )
00039 ( ( Export * ) user_data ) ->selectScene( common->getCurrentScene( ) );
00040 else
00041 ( ( Export * ) user_data ) ->selectSection( gtk_option_menu_get_history( optionmenu ) );
00042 }
00043
00048 Export::Export( PageExport *_exportPage, KinoCommon *_common ) :
00049 actions( EXPORT_SCENE_LIST ), exportPage( _exportPage ), common( _common )
00050 {
00051 menuRange = GTK_OPTION_MENU( lookup_widget( exportPage->getWidget(), "optionmenu_export_range" ) );
00052
00053 startSpin = GTK_SPIN_BUTTON( lookup_widget( exportPage->getWidget(), "spinbutton_export_range_start" ) );
00054 startEntry = GTK_ENTRY( lookup_widget( exportPage->getWidget(), "entry_export_start" ) );
00055 endSpin = GTK_SPIN_BUTTON( lookup_widget( exportPage->getWidget(), "spinbutton_export_range_end" ) );
00056 endEntry = GTK_ENTRY( lookup_widget( exportPage->getWidget(), "entry_export_end" ) );
00057 everySpin = GTK_SPIN_BUTTON( lookup_widget( exportPage->getWidget(), "spinbutton_export_range_every" ) );
00058
00059 gtk_option_menu_set_history( menuRange, 0 );
00060 selectSection( 0 );
00061 g_signal_connect( G_OBJECT( menuRange ), "changed", G_CALLBACK( on_tool_change ), this );
00062
00063
00064
00065 previewButton
00066 = GTK_TOGGLE_BUTTON( lookup_widget( common->getWidget(),
00067 "togglebutton_export_preview" ) );
00068 exportButton
00069 = GTK_TOGGLE_BUTTON( lookup_widget( common->getWidget(),
00070 "togglebutton_export_export" ) );
00071 stopButton
00072 = GTK_TOGGLE_BUTTON( lookup_widget( common->getWidget(),
00073 "togglebutton_export_stop" ) );
00074 pauseButton
00075 = GTK_TOGGLE_BUTTON( lookup_widget( common->getWidget(),
00076 "togglebutton_export_pause" ) );
00077
00078 }
00079
00088 void Export::startExport( bool preview )
00089 {
00090 bool supports_preview = actions & EXPORT_PREVIEW;
00091 bool supports_export = actions & EXPORT_EXPORT;
00092 bool supports_pause = actions & EXPORT_PAUSE;
00093
00094
00095
00096
00097 exportPage->exportMutex = true;
00098
00099
00100 gtk_toggle_button_set_active( previewButton, FALSE );
00101 gtk_widget_set_sensitive( GTK_WIDGET( previewButton ), FALSE );
00102
00103 gtk_toggle_button_set_active( exportButton, TRUE );
00104 gtk_widget_set_sensitive( GTK_WIDGET( exportButton ), FALSE );
00105
00106 gtk_toggle_button_set_active( stopButton, FALSE );
00107 gtk_widget_set_sensitive( GTK_WIDGET( stopButton ), TRUE );
00108
00109 gtk_toggle_button_set_active( pauseButton, FALSE );
00110 gtk_widget_set_sensitive( GTK_WIDGET( pauseButton ), supports_pause );
00111 exportPage->exportMutex = false;
00112
00113
00114 GtkNotebook * tmp =
00115 GTK_NOTEBOOK( lookup_widget( common->getWidget(), "notebook_export" ) );
00116 if ( !tmp )
00117 {
00118 cerr << "Export::startExport. Unable to access the notebook_export widget!"
00119 << endl;
00120 }
00121 else
00122 {
00123 gtk_widget_set_sensitive ( GTK_WIDGET( tmp ), FALSE );
00124 }
00125
00126 GtkWidget * tmpframe
00127 = GTK_WIDGET ( lookup_widget ( common->getWidget(), "vbox_export_range" ) );
00128 if ( !tmpframe )
00129 {
00130 cerr << "Export::startExport. Unable to acces the frame_export_range widget!"
00131 << endl;
00132 }
00133 else
00134 {
00135 gtk_widget_set_sensitive ( GTK_WIDGET( tmpframe ), FALSE );
00136 }
00137
00138
00139
00140
00141
00142 while ( gtk_events_pending() )
00143 {
00144 gtk_main_iteration();
00145 }
00146
00147
00148 PlayList playlist( *( common->getPlayList() ) );
00149
00150
00151 int begin = 0;
00152 int end = 0;
00153 int every = 1;
00154
00155 switch ( gtk_option_menu_get_history( menuRange ) )
00156 {
00157 case 0:
00158 begin = 0;
00159 end = common->getPlayList() ->GetNumFrames() - 1;
00160 break;
00161 case 1:
00162 begin = end = common->g_currentFrame;
00163 break;
00164 case 2:
00165 begin = gtk_spin_button_get_value_as_int( startSpin );
00166 end = gtk_spin_button_get_value_as_int( endSpin );
00167 break;
00168 }
00169 every = gtk_spin_button_get_value_as_int( everySpin );
00170
00171
00172 exportPage->resetProgress();
00173
00174
00175 startTime = 0.0;
00176 pauseTime = 0.0;
00177
00178
00179 common->setStatusBar( _( "Starting export" ) );
00180
00181
00182 enum export_result status = doExport( &playlist, begin, end, every, preview );
00183
00184 struct timeval tv;
00185 if ( 0 != gettimeofday( &tv, NULL ) )
00186 {
00187 cerr << ">>> Export::startExport - error calling gettimeofday?" << endl;
00188 }
00189
00190
00191 double now = tv.tv_sec + tv.tv_usec / 1000000.0;
00192 char buf[ 17 ];
00193 string message;
00194
00195
00196 switch ( status )
00197 {
00198 case EXPORT_RESULT_SUCCESS:
00199 message = _( "Export finished - time: " );
00200 break;
00201 case EXPORT_RESULT_FAILURE:
00202 message = _( "Export failed - time: " );
00203 break;
00204 case EXPORT_RESULT_ABORT:
00205 message = _( "Export stopped - time: " );
00206 break;
00207 }
00208 message += formatSecs( buf, 16, now - startTime );
00209 common->setStatusBar( message.c_str() );
00210
00211
00212
00213 if ( tmp )
00214 {
00215 gtk_widget_set_sensitive ( GTK_WIDGET( tmp ), TRUE );
00216 }
00217 if ( tmpframe )
00218 {
00219 gtk_widget_set_sensitive ( GTK_WIDGET( tmpframe ), TRUE );
00220 }
00221
00222
00223
00224 exportPage->updateProgress( ( gfloat ) 1.0 );
00225
00226
00227
00228
00229
00230
00231 exportPage->exportMutex = true;
00232
00233 gtk_toggle_button_set_active( previewButton, FALSE );
00234 gtk_widget_set_sensitive( GTK_WIDGET( previewButton ), supports_preview );
00235
00236 gtk_toggle_button_set_active( exportButton, FALSE );
00237 gtk_widget_set_sensitive( GTK_WIDGET( exportButton ), supports_export );
00238
00239 gtk_toggle_button_set_active( stopButton, TRUE );
00240 gtk_widget_set_sensitive( GTK_WIDGET( stopButton ), FALSE );
00241
00242 gtk_toggle_button_set_active( pauseButton, FALSE );
00243 gtk_widget_set_sensitive( GTK_WIDGET( pauseButton ), FALSE );
00244 exportPage->exportMutex = false;
00245
00246
00247 exportPage->isExporting = false;
00248
00249 while ( gtk_events_pending() )
00250 {
00251 gtk_main_iteration();
00252 }
00253
00254 }
00255
00256
00265 gulong Export::onActivate()
00266 {
00267 return EXPORT_SCENE_LIST | EXPORT_EXPORT | EXPORT_PAUSE;
00268 }
00269
00270
00271 gulong Export::onDeactivate()
00272 {
00273 return 0;
00274 }
00275
00283 gulong Export::activate()
00284 {
00285 actions = onActivate();
00286
00287 selectSection( gtk_option_menu_get_history( menuRange ) );
00288
00289
00290 GtkAdjustment *adjust = gtk_spin_button_get_adjustment( GTK_SPIN_BUTTON( startSpin ) );
00291 adjust->lower = 0;
00292 adjust->upper = common->getPlayList() ->GetNumFrames() - 1;
00293 g_signal_emit_by_name( adjust, "changed" );
00294 if ( adjust->value > adjust->upper )
00295 {
00296 gtk_adjustment_set_value ( adjust, adjust->upper );
00297 }
00298
00299 adjust
00300 = gtk_spin_button_get_adjustment( GTK_SPIN_BUTTON( endSpin ) );
00301 adjust->lower = 0;
00302 adjust->upper = common->getPlayList() ->GetNumFrames() - 1;
00303 g_signal_emit_by_name( adjust, "changed" );
00304 if ( adjust->value > adjust->upper )
00305 {
00306 gtk_adjustment_set_value ( adjust, adjust->upper );
00307 }
00308
00309
00310 bool supports_preview = actions & EXPORT_PREVIEW;
00311 bool supports_export = actions & EXPORT_EXPORT;
00312
00313
00314 exportPage->exportMutex = true;
00315 if ( exportPage->isExporting )
00316 {
00317
00318 cerr << ">>> Export::activate() internal error: "
00319 << "called with exportPage->isExporting!" << endl;
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341 }
00342 else
00343 {
00344
00345 gtk_toggle_button_set_active( previewButton, FALSE );
00346 gtk_widget_set_sensitive( GTK_WIDGET( previewButton ), supports_preview );
00347
00348 gtk_toggle_button_set_active( exportButton, FALSE );
00349 gtk_widget_set_sensitive( GTK_WIDGET( exportButton ), supports_export );
00350
00351 gtk_toggle_button_set_active( stopButton, TRUE );
00352 gtk_widget_set_sensitive( GTK_WIDGET( stopButton ), FALSE );
00353
00354 gtk_toggle_button_set_active( pauseButton, FALSE );
00355 gtk_widget_set_sensitive( GTK_WIDGET( pauseButton ), FALSE );
00356 }
00357 exportPage->exportMutex = false;
00358
00359
00360
00361 cerr << ">> Export::activate()" << endl;
00362 if ( EXPORT_SCENE_LIST & actions )
00363 {
00364
00365 return SCENE_LIST;
00366 }
00367 else
00368 {
00369
00370 return 0;
00371 }
00372 }
00373 gulong Export::deactivate()
00374 {
00375 cerr << "Export::deactivate()" << endl;
00376 if ( EXPORT_SCENE_LIST & onDeactivate() )
00377 {
00378
00379 return SCENE_LIST;
00380 }
00381 else
00382 {
00383
00384 return 0;
00385 }
00386 }
00387
00390 void Export::start()
00391 {
00392 cerr << ">>> Export::start()" << endl;
00393 }
00394 void Export::clean()
00395 {
00396 cerr << ">>> Export::clean()" << endl;
00397 }
00398
00408 void Export::selectScene( int i )
00409 {
00410 cerr << ">>> Export::selectScene() " << i << endl;
00411
00412 if ( !( EXPORT_SCENE_LIST & actions ) )
00413 {
00414 cerr << ">>> Export::selectScene: internal consistency error. "
00415 << "selectScene, even though page does not support scene list " << actions << endl;
00416 return ;
00417 }
00418 if ( exportPage->isExporting )
00419 return;
00420
00421
00422 GtkAdjustment *adjust = gtk_spin_button_get_adjustment( GTK_SPIN_BUTTON( startSpin ) );
00423
00424 vector <int> scene = common->getPageEditor() ->GetScene();
00425
00426 if ( scene.size( ) != 0 )
00427 {
00428 int begin = 0;
00429 int end = 0;
00430
00431 begin = i == 0 ? 0 : scene[ i - 1 ];
00432 adjust->lower = 0;
00433 adjust->upper = scene[ scene.size() - 1 ] - 1;
00434 gtk_spin_button_set_value( GTK_SPIN_BUTTON( startSpin ), begin );
00435 g_signal_emit_by_name( adjust, "changed" );
00436
00437
00438 adjust = gtk_spin_button_get_adjustment( GTK_SPIN_BUTTON( endSpin ) );
00439 adjust->lower = 0;
00440 adjust->upper = scene[ scene.size() - 1 ] - 1;
00441 end = scene[ i ] - 1;
00442 gtk_spin_button_set_value( GTK_SPIN_BUTTON( endSpin ), end );
00443 g_signal_emit_by_name( adjust, "changed" );
00444 Frame &frame = *( GetFramePool( ) ->GetFrame( ) );
00445 FileHandler *media;
00446 common->getPlayList() ->GetMediaObject( begin, &media );
00447 common->getPlayList() ->GetFrame( begin, frame );
00448 GetFramePool( ) ->DoneWithFrame( &frame );
00449 common->moveToFrame( begin );
00450 common->showFrameMoreInfo( frame, media );
00451 common->setCurrentScene( begin );
00452 }
00453
00454
00455 if ( gtk_option_menu_get_history( menuRange ) != 2 )
00456 gtk_option_menu_set_history( menuRange, 2 );
00457 else
00458 selectSection( 2 );
00459 }
00460
00461 void Export::selectSection( int i )
00462 {
00463 GtkWidget * label = lookup_widget( exportPage->getWidget(), "label177" );
00464 switch ( i )
00465 {
00466 case 2:
00467 gtk_widget_show( GTK_WIDGET( startSpin ) );
00468 gtk_widget_show( GTK_WIDGET( startEntry ) );
00469 gtk_widget_show( GTK_WIDGET( endSpin ) );
00470 gtk_widget_show( GTK_WIDGET( endEntry ) );
00471 gtk_widget_show( GTK_WIDGET( label ) );
00472 common->getPageExport()->timeFormatChanged();
00473 break;
00474
00475 default:
00476 gtk_widget_hide( GTK_WIDGET( startSpin ) );
00477 gtk_widget_hide( GTK_WIDGET( startEntry ) );
00478 gtk_widget_hide( GTK_WIDGET( endSpin ) );
00479 gtk_widget_hide( GTK_WIDGET( endEntry ) );
00480 gtk_widget_hide( GTK_WIDGET( label ) );
00481 break;
00482 }
00483 }
00484
00487 void Export::stopExport()
00488 {
00489 cerr << ">>> Export::stopExport()" << endl;
00490 }
00491 void Export::pauseExport()
00492 {
00493 cerr << ">>> Export::pauseExport()" << endl;
00494 }
00495
00503 void Export::onRangeChange( GtkSpinButton *widget )
00504 {
00505
00506 GtkAdjustment * startAdjust
00507 = gtk_spin_button_get_adjustment( GTK_SPIN_BUTTON( startSpin ) );
00508 GtkAdjustment *endAdjust
00509 = gtk_spin_button_get_adjustment( GTK_SPIN_BUTTON( endSpin ) );
00510
00511 if ( widget == startSpin )
00512 {
00513 if ( endAdjust->value < startAdjust->value )
00514 {
00515 gtk_adjustment_set_value ( endAdjust, startAdjust->value );
00516 }
00517 }
00518 else
00519 {
00520 if ( widget == endSpin )
00521 {
00522 if ( startAdjust->value > endAdjust->value )
00523 {
00524 gtk_adjustment_set_value ( startAdjust, endAdjust->value );
00525 }
00526 }
00527 else
00528 {
00529 cerr << ">>> Export::onRangeChange: internal error: widget passed is unknown"
00530 << endl;
00531 }
00532 }
00533 }
00534
00535
00539 void Export::doPause()
00540 {
00541 struct timespec ts;
00542 ts.tv_sec = 0;
00543 ts.tv_nsec = 1000 * 1000 * 20;
00544 while ( exportPage->isPausing )
00545 {
00546
00547 while ( gtk_events_pending() )
00548 {
00549 gtk_main_iteration();
00550 }
00551
00552 nanosleep( &ts, NULL );
00553 }
00554 }
00555
00557 char * Export::formatSecs( char * buf, int size, double seconds )
00558 {
00559 int _seconds = ( int ) rint( seconds );
00560 int secs = _seconds % 60;
00561 int mins = ( _seconds / 60 ) % 60;
00562 int hours = ( _seconds / 3600 );
00563 if ( hours > 0 )
00564 {
00565 snprintf( buf, size, "%i:%02i:%02i", hours, mins, secs );
00566 }
00567 else
00568 {
00569 if ( mins > 0 )
00570 {
00571 snprintf( buf, size, "%i:%02i", mins, secs );
00572 }
00573 else
00574 {
00575 snprintf( buf, size, "%i", secs );
00576 }
00577 }
00578 return buf;
00579 }
00580
00582 void Export::innerLoopUpdate( int progress, int begin, int end, int every,
00583 int currentFrame, const char* activity )
00584 {
00585 char buf[ 512 ];
00586 struct timeval tv;
00587 if ( 0 != gettimeofday( &tv, NULL ) )
00588 {
00589 cerr << ">>> Export::innerLoopUpdate: Error calling gettimeofday?" << endl;
00590 }
00591 double now = tv.tv_sec + tv.tv_usec / 1000000.0;
00592 gfloat com_ratio = ( gfloat ) ( progress - begin ) / ( gfloat ) ( end + 1 - begin ) ;
00593 if ( currentFrame == -1 )
00594 currentFrame = progress;
00595
00596 if ( 0 == startTime )
00597 {
00598
00599 startTime = now;
00600 nextUpdateTime = now - 1;
00601 snprintf( buf, 512, _( "%s frame %i." ), activity, currentFrame );
00602 }
00603 else
00604 {
00605
00606
00607 double time_so_far = now - startTime - pauseTime;
00608 double total_est = time_so_far / com_ratio;
00609
00610 char buf1[ 16 ];
00611 char buf2[ 16 ];
00612 char buf3[ 16 ];
00613 snprintf( buf, 512, _( "%s frame %i. Time used: %s, estimated: %s, left: %s" ),
00614 activity,
00615 currentFrame + 1,
00616 formatSecs( buf1, 16, time_so_far ),
00617 formatSecs( buf2, 16, total_est ),
00618 formatSecs( buf3, 16, total_est - time_so_far ) );
00619 }
00620
00621
00622
00623
00624 if ( now > nextUpdateTime )
00625 {
00626 common->setStatusBar( buf );
00627 nextUpdateTime = now + 0.25;
00628 }
00629
00630
00631
00632 exportPage->updateProgress( com_ratio );
00633
00634
00635 if ( EXPORT_PAUSE & actions && exportPage->isPausing )
00636 {
00637 doPause();
00638 if ( 0 != gettimeofday( &tv, NULL ) )
00639 {
00640 cerr << ">>> Export::innerLoopUpdate: Error calling gettimeofday?" << endl;
00641 }
00642 double foo = tv.tv_sec + tv.tv_usec / 1000000.0;
00643 pauseTime += ( foo - now );
00644 }
00645 }
00646
00647 double Export::calculateAdjustedRate( PlayList* playlist, double outputRate, int begin, int end, int every )
00648 {
00649 Frame& frame = *GetFramePool()->GetFrame();
00650 playlist->GetFrame( begin, frame );
00651 double seconds = double( end - begin + 1 ) / frame.GetFrameRate();
00652 int64_t idealSamples = int64_t( seconds * outputRate + 0.5 );
00653 int64_t actualSamples = 0;
00654 double adjustedRate = 0.0;
00655 struct timeval tv;
00656 if ( 0 != gettimeofday( &tv, NULL ) )
00657 {
00658 cerr << ">>> Export::calculateAdjustedRate: Error calling gettimeofday?" << endl;
00659 }
00660 double now = tv.tv_sec + tv.tv_usec / 1000000.0;
00661 double nextUpdateTime = now - 1.0;
00662 char buf[ 512 ];
00663 int n = -1, frameNum = 0;
00664 AsyncAudioResample<int16_ne_t,int16_le_t>* resampler = new AsyncAudioResample<int16_ne_t,int16_le_t>(
00665 AUDIO_RESAMPLE_SRC_SINC_FASTEST, playlist, outputRate, begin, end, every );
00666
00667
00668 while ( exportPage->isExporting && n != 0 )
00669 {
00670 n = resampler->Process( outputRate, frame.CalculateNumberSamples( int(outputRate), frameNum++ ) );
00671 actualSamples += n;
00672
00673 if ( 0 == gettimeofday( &tv, NULL ) )
00674 {
00675 now = tv.tv_sec + tv.tv_usec / 1000000.0;
00676 if ( now > nextUpdateTime )
00677 {
00678 snprintf( buf, 512, "%s (%02d%%)", _("Locking audio"),
00679 int( 100.0 * frameNum / (end - begin + 1) ) );
00680 common->setStatusBar( buf );
00681 nextUpdateTime = now + 1.0;
00682 }
00683 }
00684 exportPage->updateProgress( -1 );
00685
00686
00687 if ( EXPORT_PAUSE & actions && exportPage->isPausing )
00688 {
00689 doPause();
00690 }
00691 }
00692 GetFramePool()->DoneWithFrame( &frame );
00693
00694 if ( exportPage->isExporting )
00695 {
00696
00697
00698
00699 adjustedRate = outputRate + ( idealSamples - actualSamples ) / seconds;
00700 }
00701
00702 if ( 0 != gettimeofday( &tv, NULL ) )
00703 cerr << ">>> Export::innerLoopUpdate: Error calling gettimeofday?" << endl;
00704 double foo = tv.tv_sec + tv.tv_usec / 1000000.0;
00705 pauseTime += ( foo - now );
00706
00707 cerr << ">>> output rate is " << outputRate << ", adjusted rate is " << adjustedRate << endl;
00708
00709 return adjustedRate;
00710 }