Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

page_export_pipe.cc

Go to the documentation of this file.
00001 /*
00002 * page_export_pipe.cc DV Pipe exports
00003 * Copyright (C) 2003 Charles Yates <charles.yates@pandora.be>
00004 * Copyright (C) 2003-2007 Dan Dennedy <dan@dennedy.org>
00005 *
00006 * This program is free software; you can redistribute it and/or modify
00007 * it under the terms of the GNU General Public License as published by
00008 * the Free Software Foundation; either version 2 of the License, or
00009 * (at your option) any later version.
00010 *
00011 * This program is distributed in the hope that it will be useful,
00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 * GNU General Public License for more details.
00015 *
00016 * You should have received a copy of the GNU General Public License
00017 * along with this program; if not, write to the Free Software Foundation,
00018 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
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         // Construct the command
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     // Get the main widget
00209     GtkWidget * window = common->getWidget();
00210 
00211     // Start with the option menu
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     // Create a menu if we don't have one
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     // Set the active item
00230     gtk_option_menu_set_history( option_menu, index );
00231 }
00232 
00233 void ExportPipe::selectTool( int index )
00234 {
00235     // Get the main widget
00236     GtkWidget * window = common->getWidget();
00237 
00238     // Populate the profiles
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     // Remove the old menu if it exists
00243     if ( menu == NULL )
00244         gtk_option_menu_remove_menu( option_menu );
00245 
00246     // Create a new one
00247     menu = GTK_MENU( gtk_menu_new( ) );
00248 
00249     // Populate it...
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     // Set it
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     // Get the main widget
00308     GtkWidget *window = common->getWidget();
00309 
00310     // Obtain the selected tool
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         // Determine the number of passes to do
00317         int passes = 1;
00318         if ( tool->m_flags.find("double-pass") != string::npos )
00319             passes = 2;
00320 
00321         // Get the first frame to determine normalisation
00322         playlist->GetFrame( begin, frame );
00323 
00324         // Set up resampling
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         // Get audio info
00330         AudioInfo info;
00331         frame.GetAudioInfo( info );
00332 
00333         // Resample to 48000 if invalid rate detected
00334         if ( info.frequency == 0 )
00335             info.frequency = 48000;
00336 
00337         // Evaluate argument for tool
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         // Get the profile
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         // Get the file name
00347         string filename = ( char* ) gtk_entry_get_text( GTK_ENTRY( lookup_widget( window, "entry_export_pipe_file" ) ) );
00348     
00349         // first call to innerLoopUpdate initializes progress tracker, which
00350         // we want to do because calculateAdjustedRate generates paused time.
00351         innerLoopUpdate( begin, begin, end + (end - begin + 1) * (passes - 1), every );
00352 
00353         // Determine correct amount of audio for duration
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             // Execute the tool
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             // Create the resampler
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             // Iterate over all frames in selection
00390             for ( i = begin, j = 0; success && i <= end && exportPage->isExporting; i += every, j++ )
00391             {
00392                 // Call innerLoopUpdate
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                 // Resample
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                 // Get the frame
00409                 playlist->GetFrame( i, frame );
00410     
00411                 if ( info.samples )
00412                     frame.EncodeAudio( info, audio_buffers );
00413     
00414                 // Write the frame
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     // Return result
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 }

Generated on Sun Mar 11 22:11:46 2007 for Kino by  doxygen 1.4.2