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 <sstream>
00027 #include <string>
00028 #include <algorithm>
00029 using std::cerr;
00030 using std::endl;
00031
00032 #include <gtk/gtk.h>
00033 #include <stdio.h>
00034 #include <stdlib.h>
00035 #include <assert.h>
00036
00037 #include "page_export_pipe.h"
00038 #include "preferences.h"
00039 #include "message.h"
00040 #include "frame.h"
00041 #include "rwpipe.h"
00042 #include "stringutils.h"
00043
00044 #include <sys/types.h>
00045 #include <dirent.h>
00046 #include <sys/wait.h>
00047
00048 class DVPipeTool : protected RWPipe
00049 {
00050 public:
00051 string m_command;
00052 string m_description;
00053 bool m_active;
00054 string m_flags;
00055 vector < string > m_profiles;
00056
00057 DVPipeTool( string command ) :
00058 m_command( command ),
00059 m_description( "" ),
00060 m_active( false )
00061 {
00062 load( command );
00063 }
00064
00065 void load( string command )
00066 {
00067 if ( run( command ) )
00068 {
00069 char temp[ 10240 ];
00070 while ( readLine( temp, 10240 ) > 0 )
00071 {
00072 char * ptr = strchr( temp, ':' );
00073 if ( ptr != NULL )
00074 {
00075 *ptr = '\0';
00076 ptr += 2;
00077
00078 if ( !strcmp( temp, "Title" ) )
00079 m_description = ptr;
00080 else if ( !strcmp( temp, "Status" ) )
00081 m_active = !strcmp( ptr, "Active" );
00082 else if ( !strcmp( temp, "Flags" ) )
00083 m_flags = ptr;
00084 else if ( !strcmp( temp, "Profile" ) )
00085 m_profiles.push_back( ptr );
00086 else
00087 fprintf( stderr, "Unrecognised %s: %s\n", temp, ptr );
00088 }
00089 }
00090 stop( );
00091 }
00092 }
00093
00094 bool execute( const string& normalisation, double length, int profile,
00095 const string& file, const string& docfile, int pass, const string& aspect )
00096 {
00097 gchar *command;
00098 bool result;
00099
00100
00101 command = g_strdup_printf( "\"%s\" %s %f %d \"%s\" \"%s\" %d \"%s\"", m_command.c_str( ),
00102 normalisation.c_str( ),
00103 length,
00104 profile,
00105 StringUtils::replaceAll( file, "\"", "\\\"" ).c_str( ),
00106 docfile.c_str(),
00107 pass,
00108 aspect.c_str() );
00109
00110 result = run( command );
00111 g_free( command );
00112 return result;
00113 }
00114
00115 bool output( Frame &frame )
00116 {
00117 int size = frame.IsPAL( ) ? 144000 : 120000;
00118 return writeData( frame.data, size ) == size ;
00119 }
00120
00121 void close( )
00122 {
00123 stop( );
00124 }
00125
00126 static bool compare( const DVPipeTool* x, const DVPipeTool* y )
00127 {
00128 return ( x->m_description < y->m_description );
00129 }
00130 };
00131
00132 static void on_tool_change( GtkOptionMenu *optionmenu, gpointer user_data )
00133 {
00134 int index = gtk_option_menu_get_history( optionmenu );
00135 ( ( ExportPipe * ) user_data ) ->selectTool( index );
00136 }
00137
00143 ExportPipe::ExportPipe( PageExport *_exportPage, KinoCommon *_common ) :
00144 Export( _exportPage, _common )
00145 {
00146 cerr << "> Creating ExportPipe Page" << endl;
00147
00148 loadTools( DATADIR "/kino/scripts/exports" );
00149 string alternate_dir = string( getenv( "HOME" ) ) + string( "/kino/exports" );
00150 loadTools( alternate_dir );
00151
00152 char *kinoHome=getenv("KINO_HOME");
00153
00154 if(kinoHome)
00155 {
00156 string alternate_dir = string( kinoHome ) + string( "/exports" );
00157 loadTools( alternate_dir );
00158 }
00159
00160 GtkWidget *window = common->getWidget();
00161 GtkOptionMenu *option_menu = GTK_OPTION_MENU( lookup_widget( window, "optionmenu_pipe_tools" ) );
00162 g_signal_connect( G_OBJECT( option_menu ), "changed", G_CALLBACK( on_tool_change ), this );
00163
00164 activateTool( 0 );
00165 }
00166
00170 ExportPipe::~ExportPipe()
00171 {
00172 cerr << "> Destroying ExportPipe Page" << endl;
00173 }
00174
00178 void ExportPipe::loadTools( string directory )
00179 {
00180 char * filename;
00181 struct dirent *entry;
00182
00183 DIR *dir = opendir( directory.c_str( ) );
00184
00185 if ( dir )
00186 {
00187 while ( ( entry = readdir( dir ) ) != NULL )
00188 {
00189 filename = g_strdup_printf( "%s/%s", directory.c_str( ), entry->d_name );
00190 if ( entry->d_name[ 0 ] != '.' )
00191 {
00192 fprintf( stderr, "%s\n", filename );
00193 DVPipeTool *tool = new DVPipeTool( filename );
00194 if ( tool->m_active )
00195 m_tools.push_back( tool );
00196 else
00197 delete tool;
00198 }
00199 g_free( filename );
00200 }
00201 closedir( dir );
00202 }
00203 std::sort( m_tools.begin(), m_tools.end(), DVPipeTool::compare );
00204 }
00205
00206 void ExportPipe::activateTool( int index )
00207 {
00208
00209 GtkWidget * window = common->getWidget();
00210
00211
00212 GtkOptionMenu *option_menu = GTK_OPTION_MENU( lookup_widget( window, "optionmenu_pipe_tools" ) );
00213 GtkMenu *menu = GTK_MENU( gtk_option_menu_get_menu( option_menu ) );
00214
00215
00216 if ( menu == NULL )
00217 {
00218 menu = GTK_MENU( gtk_menu_new( ) );
00219 for ( unsigned int i = 0; i < m_tools.size( ); i ++ )
00220 {
00221 GtkWidget *item = gtk_menu_item_new_with_label( m_tools[ i ] ->m_description.c_str( ) );
00222 gtk_widget_show( item );
00223 gtk_menu_append( menu, item );
00224 }
00225
00226 gtk_option_menu_set_menu( option_menu, GTK_WIDGET( menu ) );
00227 }
00228
00229
00230 gtk_option_menu_set_history( option_menu, index );
00231 }
00232
00233 void ExportPipe::selectTool( int index )
00234 {
00235
00236 GtkWidget * window = common->getWidget();
00237
00238
00239 GtkOptionMenu *option_menu = GTK_OPTION_MENU( lookup_widget( window, "optionmenu_pipe_profile" ) );
00240 GtkMenu *menu = GTK_MENU( gtk_option_menu_get_menu( option_menu ) );
00241
00242
00243 if ( menu == NULL )
00244 gtk_option_menu_remove_menu( option_menu );
00245
00246
00247 menu = GTK_MENU( gtk_menu_new( ) );
00248
00249
00250 if ( m_tools[ index ] ->m_profiles.size( ) )
00251 {
00252 for ( unsigned int i = 0; i < m_tools[ index ] ->m_profiles.size( ); i ++ )
00253 {
00254 GtkWidget *item = gtk_menu_item_new_with_label( m_tools[ index ] ->m_profiles[ i ].c_str( ) );
00255 gtk_widget_show( item );
00256 gtk_menu_append( menu, item );
00257 }
00258 }
00259 else
00260 {
00261 GtkWidget *item = gtk_menu_item_new_with_label( "[No Profile Available]" );
00262 gtk_widget_show( item );
00263 gtk_menu_append( menu, item );
00264 }
00265
00266
00267 gtk_option_menu_set_menu( option_menu, GTK_WIDGET( menu ) );
00268 }
00269
00273 void ExportPipe::start()
00274 {
00275 cerr << ">> Entering ExportPipe" << endl;
00276 playlistTempName[ 0 ] = '\0';
00277 }
00278
00284 gulong ExportPipe::onActivate()
00285 {
00286 return EXPORT_EXPORT | EXPORT_SCENE_LIST | EXPORT_PAUSE;
00287 }
00288
00292 void ExportPipe::clean()
00293 {
00294 cerr << ">> Leaving ExportPipe" << endl;
00295 if ( strcmp( playlistTempName, "" ) )
00296 unlink( playlistTempName );
00297 }
00298
00301 enum export_result ExportPipe::doExport( PlayList *playlist, int begin, int end, int every, bool preview )
00302 {
00303 enum export_result status = EXPORT_RESULT_SUCCESS;
00304 Frame& frame = *GetFramePool()->GetFrame();
00305 int i = -1, j;
00306
00307
00308 GtkWidget *window = common->getWidget();
00309
00310
00311 GtkOptionMenu *option_menu = GTK_OPTION_MENU( lookup_widget( window, "optionmenu_pipe_tools" ) );
00312 DVPipeTool *tool = m_tools[ gtk_option_menu_get_history( option_menu ) ];
00313
00314 if ( tool != NULL )
00315 {
00316
00317 int passes = 1;
00318 if ( tool->m_flags.find("double-pass") != string::npos )
00319 passes = 2;
00320
00321
00322 playlist->GetFrame( begin, frame );
00323
00324
00325 int16_le_t *audio_buffers[ 4 ];
00326 for ( int c = 0; c < 4; c++ )
00327 audio_buffers[ c ] = new int16_le_t[ 2 * DV_AUDIO_MAX_SAMPLES ];
00328
00329
00330 AudioInfo info;
00331 frame.GetAudioInfo( info );
00332
00333
00334 if ( info.frequency == 0 )
00335 info.frequency = 48000;
00336
00337
00338 string normalisation = frame.IsPAL( ) ? "pal" : "ntsc";
00339 string aspect = frame.IsWide() ? "16:9" : "4:3";
00340 double length = ( double ) ( end - begin ) / ( frame.IsPAL( ) ? 25.0 : 29.97 );
00341
00342
00343 GtkOptionMenu *option_menu = GTK_OPTION_MENU( lookup_widget( window, "optionmenu_pipe_profile" ) );
00344 int profile = gtk_option_menu_get_history( option_menu );
00345
00346
00347 string filename = ( char* ) gtk_entry_get_text( GTK_ENTRY( lookup_widget( window, "entry_export_pipe_file" ) ) );
00348
00349
00350
00351 innerLoopUpdate( begin, begin, end + (end - begin + 1) * (passes - 1), every );
00352
00353
00354 double adjustedRate = calculateAdjustedRate( playlist, info.frequency, begin, end, every );
00355 if ( !adjustedRate )
00356 status = EXPORT_RESULT_ABORT;
00357
00358 for (int pass = 1; pass <= passes && exportPage->isExporting; pass++ )
00359 {
00360
00361 bool success = false;
00362 if ( common->getPlayList()->GetDocName() == "" )
00363 {
00364 if ( strcmp( playlistTempName, "" ) == 0 )
00365 {
00366 PlayList* copy = new PlayList( *( common->getPlayList() ) );
00367 strcpy( playlistTempName, "/tmp/kino.XXXXXX" );
00368 mkstemp( playlistTempName );
00369 copy->SavePlayList( playlistTempName );
00370 delete copy;
00371 }
00372 success = tool->execute( normalisation, length, profile, filename, playlistTempName, pass, aspect );
00373 }
00374 else
00375 {
00376 success = tool->execute( normalisation, length, profile, filename, common->getPlayList()->GetDocName(), pass, aspect );
00377 }
00378
00379
00380 AsyncAudioResample<int16_ne_t,int16_le_t>* resampler = new AsyncAudioResample<int16_ne_t,int16_le_t>(
00381 AUDIO_RESAMPLE_SRC_SINC_BEST_QUALITY, playlist, info.frequency, begin, end, every );
00382 if ( resampler->IsError() )
00383 {
00384 std::cerr << ">>> Resampler error: " << resampler->GetError() << std::endl;
00385 exportPage->isExporting = false;
00386 status = EXPORT_RESULT_FAILURE;
00387 }
00388
00389
00390 for ( i = begin, j = 0; success && i <= end && exportPage->isExporting; i += every, j++ )
00391 {
00392
00393 if ( passes > 1 )
00394 innerLoopUpdate( i + (end - begin + 1) / every * (pass - 1),
00395 begin, end + (end - begin + 1), every, i,
00396 pass == 1 ? _("Exporting (pass 1)") : _("Exporting (pass 2)") );
00397 else
00398 innerLoopUpdate( i, begin, end, every );
00399
00400
00401 int requestedSamples = frame.CalculateNumberSamples( info.frequency, j );
00402 info.samples = resampler->Process( adjustedRate, requestedSamples );
00403 int16_le_t *p = resampler->GetOutput();
00404 for ( int s = 0; s < info.samples; s++ )
00405 for ( int c = 0; c < info.channels; c++ )
00406 audio_buffers[ c ][ s ] = *p++;
00407
00408
00409 playlist->GetFrame( i, frame );
00410
00411 if ( info.samples )
00412 frame.EncodeAudio( info, audio_buffers );
00413
00414
00415 success = tool->output( frame );
00416 }
00417 tool->close( );
00418 delete resampler;
00419 }
00420 for ( int c = 0; c < 4; c++ )
00421 delete[] audio_buffers[ c ];
00422 }
00423
00424 GetFramePool()->DoneWithFrame( &frame );
00425
00426
00427 if ( status == EXPORT_RESULT_SUCCESS && !exportPage->isExporting )
00428 return EXPORT_RESULT_ABORT;
00429
00430 return status;
00431 }
00432
00433 extern "C"
00434 {
00435 void
00436 on_button_export_pipe_file_clicked (GtkButton *button,
00437 gpointer user_data)
00438 {
00439 const char *filename = common->getFileToSave( _("Enter a File Name to supply to Tool") );
00440 gtk_widget_grab_focus( lookup_widget( GTK_WIDGET( button ), "entry_export_pipe_file" ) );
00441 if ( strcmp( filename, "" ) )
00442 gtk_entry_set_text( GTK_ENTRY( lookup_widget( GTK_WIDGET( button ), "entry_export_pipe_file" ) ), filename );
00443 }
00444 }