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

playlist.cc

Go to the documentation of this file.
00001 /*
00002 * playlist.cc -- SMIL implementation
00003 * Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de>
00004 * Copyright (C) 2001-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 // C++ includes
00026 #include <iostream>
00027 #include <fstream>
00028 #include <sstream>
00029 #include <string>
00030 #include <list>
00031 #include <map>
00032 
00033 using std::cerr;
00034 using std::endl;
00035 using std::ends;
00036 using std::ofstream;
00037 using std::ostringstream;
00038 using std::list;
00039 using std::map;
00040 
00041 // C includes
00042 #include <libxml/xmlmemory.h>
00043 #include <libxml/parser.h>
00044 #include <libxml/tree.h>
00045 #include <pthread.h>
00046 #include <unistd.h>
00047 #include <sys/stat.h>
00048 #include <sys/types.h>
00049 #include <dirent.h>
00050 
00051 // local includes
00052 #include "playlist.h"
00053 #include "error.h"
00054 #include "filehandler.h"
00055 #include "frame.h"
00056 #include "stringutils.h"
00057 
00058 const xmlChar* SMIL20_NAMESPACE_HREF = reinterpret_cast< const xmlChar* >( "http://www.w3.org/2001/SMIL20/Language" );
00059 const string KINO_AUTOSAVE_DIR = string( getenv("HOME") ) + string( "/.kino-history" );
00060 
00065 class KinoFileMap : public FileMap
00066 {
00067 private:
00068     map < string, FileHandler* > filemap;
00069 
00070 public:
00071     virtual ~KinoFileMap()
00072     {}
00073 
00074     map<string, FileHandler*> &GetMap()
00075     {
00076         return filemap;
00077     }
00078 
00079     void Clear( )
00080     {
00081         map<string, FileHandler*>::iterator n;
00082         for ( n = filemap.begin(); n != filemap.end(); ++n )
00083             delete ( *n ).second;
00084         filemap.erase( filemap.begin(), filemap.end() );
00085     }
00086 
00087     void GetUnusedFxFiles( PlayList &list, vector< string > &unused )
00088     {
00089         unused.erase( unused.begin(), unused.end() );
00090         map<string, FileHandler*>::iterator n;
00091         for ( n = filemap.begin(); n != filemap.end(); ++n )
00092         {
00093             if ( n->first.find( ".kinofx.dv" ) != string::npos && !list.IsFileUsed( n->first ) )
00094             {
00095                 unused.push_back( n->first );
00096             }
00097         }
00098     }
00099 };
00100 
00104 FileMap *GetFileMap( )
00105 {
00106     static FileMap * thismap = new KinoFileMap( );
00107     return thismap;
00108 }
00109 
00121 string directory_utils::join_file_to_directory( const string directory, const string &file )
00122 {
00123     vector <string> items;
00124 
00125     // Determine if the file is a full file spec or not
00126     if ( file[ 0 ] != '/' && directory[ 0 ] != '/' )
00127     {
00128         char path[ PATH_MAX ];
00129         getcwd( path, PATH_MAX );
00130         StringUtils::split( path, "/", items );
00131     }
00132 
00133     // Now add the directory if file is not absolute
00134     if ( file[ 0 ] != '/' )
00135         StringUtils::split( directory, "/", items );
00136 
00137     // Split the file and append to the directory info
00138     StringUtils::split( file, "/", items );
00139 
00140     // iterate through the items vector
00141     for ( vector< string >::iterator item = items.begin( ); item != items.end( ); )
00142     {
00143         if ( *item == ".." )
00144         {
00145             if ( item == items.begin( ) )
00146             {
00147                 items.erase( item );
00148                 item = items.begin();
00149             }
00150             else
00151             {
00152                 items.erase( -- item + 1 );
00153                 items.erase( -- item + 1 );
00154                 item ++;
00155             }
00156         }
00157         else
00158         {
00159             item ++;
00160         }
00161     }
00162 
00163     return "/" + StringUtils::join( items, "/" );
00164 }
00165 
00173 string directory_utils::get_directory_from_file( const string &file )
00174 {
00175     return join_file_to_directory( "", file + "/.." );
00176 }
00177 
00190 string directory_utils::get_absolute_path_to_file( const string &directory, const string &file )
00191 {
00192     return join_file_to_directory( directory, file );
00193 }
00194 
00201 string directory_utils::get_relative_path_to_file( const string &directory, const string &file )
00202 {
00203     string output = "";
00204     string absolute = join_file_to_directory( directory, file );
00205     vector < string > directory_items;
00206     vector < string > absolute_items;
00207     StringUtils::split( absolute, "/", absolute_items );
00208     StringUtils::split( directory, "/", directory_items );
00209 
00210     vector < string >::iterator directory_item;
00211     vector < string >::iterator absolute_item;
00212 
00213     // While they're both the same, remove from both
00214     for ( directory_item = directory_items.begin(), absolute_item = absolute_items.begin();
00215             directory_item != directory_items.end() && absolute_item != absolute_items.end() && *directory_item == *absolute_item; )
00216     {
00217         directory_items.erase( directory_item );
00218         absolute_items.erase( absolute_item );
00219         directory_item = directory_items.begin();
00220         absolute_item = absolute_items.begin();
00221     }
00222 
00223     // For each item left in the directory_items, output a ../
00224     for ( directory_item = directory_items.begin(); directory_item != directory_items.end() ; directory_item ++ )
00225         output += "../";
00226 
00227     // Now simply join what's left in absolute to output and return
00228     output += StringUtils::join( absolute_items, "/" );
00229     return output;
00230 }
00231 
00235 string directory_utils::expand_directory( const string directory )
00236 {
00237     string output;
00238     vector <string> items;
00239     StringUtils::split( directory, "/", items );
00240     vector< string >::iterator item = items.begin( );
00241 
00242     if ( item != items.end( ) && *item == "~" )
00243     {
00244         output = getenv( "HOME" );
00245         item ++;
00246     }
00247 
00248     for ( ; item != items.end( ); item ++ )
00249         output += "/" + *item;
00250 
00251     return output;
00252 }
00253 
00254 typedef bool ( *callback ) ( xmlNodePtr node, void *p, bool *freed );
00255 
00256 typedef struct
00257 {
00258     int absFrame;
00259     int absBegin;
00260     int absEnd;
00261     int clipFrame;
00262     int clipBegin;
00263     int clipEnd;
00264     int clipNumber;
00265     int clipLength;
00266     char    fileName[ 1024 ];
00267     xmlNodePtr  sequence;
00268     xmlNodePtr  video;
00269 }
00270 MovieInfo;
00271 
00293 static bool findFile( xmlNodePtr node, void *p, bool *freed )
00294 {
00295     MovieInfo * data = ( MovieInfo* ) p;
00296 
00297     if ( xmlStrcmp( node->name, ( const xmlChar* ) "seq" ) == 0 )
00298     {
00299         data->sequence = node;
00300         data->clipNumber++;
00301     }
00302 
00303     // if this is a <video> node, calculate its absolute begin and end positions
00304 
00305     else if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00306     {
00307 
00308         data->video = node;
00309 
00310         // Check whether the required properties exist
00311 
00312         xmlChar *src = xmlGetProp( node, ( const xmlChar* ) "src" );
00313         xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00314         xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00315 
00316         if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) )
00317         {
00318 
00319             data->clipBegin = atoi( ( const char* ) clipBegin );
00320             data->clipEnd = atoi( ( const char* ) clipEnd );
00321 
00322             data->absBegin += data->clipLength; // add length of previous clip
00323             data->clipLength = data->clipEnd - data->clipBegin + 1;
00324             data->absEnd = data->absBegin + data->clipLength - 1;
00325 
00326             // cerr << "Number: " << data->clipNumber << " starts " << data->absBegin << " ends " << data->absEnd << endl;
00327 
00328             // if absFrame is within this scene, we have found the corresponding file.
00329             // Otherwise, add frame count of this scene to absBegin
00330 
00331             if ( data->absFrame <= data->absEnd )
00332             {
00333                 strcpy( data->fileName, ( char * ) src );
00334                 data->clipFrame = data->absFrame - data->absBegin + data->clipBegin;
00335 
00336                 // Free memory used
00337                 xmlFree( src );
00338                 xmlFree( clipEnd );
00339                 xmlFree( clipBegin );
00340 
00341                 // cerr << "Obtaining frame " << data->clipFrame << " from " << data->clipNumber << endl;
00342 
00343                 return true; // true means done traversing xml tree
00344             }
00345         }
00346 
00347         if ( src )
00348             xmlFree( src );
00349         if ( clipEnd )
00350             xmlFree( clipEnd );
00351         if ( clipBegin )
00352             xmlFree( clipBegin );
00353 
00354     }
00355     return false;
00356 }
00357 
00358 
00365 static bool fillMap( xmlNodePtr node, void *p, bool *freed )
00366 {
00367     if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00368     {
00369 
00370         // Check whether the required properties exist
00371 
00372         xmlChar * src = xmlGetProp( node, ( const xmlChar* ) "src" );
00373         xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00374         xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00375 
00376         if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) )
00377         {
00378 
00379             // Determine the absolute path to the file indicated by src
00380             string & directory = *( string * ) p;
00381             string index = directory_utils::get_absolute_path_to_file( directory, ( char * ) src );
00382 
00383             // Internally, we always use absolute paths so convert now, just to be on the safe side
00384             xmlSetProp( node, ( const xmlChar* ) "src", ( xmlChar * ) index.c_str() );
00385 
00386             // Check if the file actually exists in the file map
00387             if ( GetFileMap() ->GetMap().find( index ) == GetFileMap() ->GetMap().end() )
00388             {
00389 
00390                 FileHandler * mediaFile;
00391                 /* determine file type */
00392                 if ( strncasecmp( strrchr( ( char* ) src, '.' ), ".avi", 4 ) == 0 )
00393                     mediaFile = new AVIHandler();
00394                 else if ( strncasecmp( strrchr( ( char* ) src, '.' ), ".dv", 3 ) == 0 ||
00395                           strncasecmp( strrchr( ( char* ) src, '.' ), ".dif", 4 ) == 0 )
00396                     mediaFile = new RawHandler();
00397 #ifdef HAVE_LIBQUICKTIME
00398 
00399                 else if ( strncasecmp( strrchr( ( char* ) src, '.' ), ".mov", 4 ) == 0 )
00400                     mediaFile = new QtHandler();
00401 #endif
00402 
00403                 else
00404                 {
00405                     xmlFree( src );
00406                     xmlFree( clipEnd );
00407                     xmlFree( clipBegin );
00408                     return false;
00409                 }
00410                 /* construct appropriate filehandler */
00411                 if ( mediaFile->Open( index.c_str() ) )
00412                 {
00413                     GetFileMap() ->GetMap() [ index ] = mediaFile;
00414                 }
00415                 else
00416                 {
00417                     cerr << "Unable to open " << ( char * ) src
00418                     << " - removing from list" << endl;
00419                     xmlUnlinkNode( node );
00420                     xmlFreeNode( node );
00421                     *freed = true;
00422                 }
00423             }
00424         }
00425 
00426         xmlFree( src );
00427         xmlFree( clipEnd );
00428         xmlFree( clipBegin );
00429     }
00430     return false;
00431 }
00432 
00433 
00443 static bool findSceneStart( xmlNodePtr node, void *p, bool *freed )
00444 {
00445     int fileCount = 0;
00446     MovieInfo *data = ( MovieInfo* ) p;
00447     int begin = data->absBegin;
00448 
00449     // if this is a <seq> node process all of its <video> child nodes
00450 
00451     if ( xmlStrcmp( node->name, ( const xmlChar* ) "seq" ) == 0 )
00452     {
00453 
00454         data->sequence = node;
00455 
00456         node = node->children;
00457         while ( node != NULL )
00458         {
00459             if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00460             {
00461 
00462                 data->video = node;
00463 
00464                 xmlChar *src = xmlGetProp( node, ( const xmlChar* ) "src" );
00465                 xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00466                 xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00467 
00468                 if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) )
00469                 {
00470 
00471                     data->clipBegin = atoi( ( const char* ) clipBegin );
00472                     data->clipEnd = atoi( ( const char* ) clipEnd );
00473 
00474                     // if this is the first file remember its name and start
00475 
00476                     if ( fileCount == 0 )
00477                     {
00478                         data->clipFrame = data->clipBegin;
00479                         strcpy( data->fileName, ( char * ) src );
00480                     }
00481 
00482                     // if absFrame is within current scene we are done.
00483                     // fine name and relative frame number have been already found (see above)
00484                     // otherwise update absBegin to hold abs frame num of next file
00485 
00486                     if ( data->absFrame <= begin + data->clipEnd - data->clipBegin )
00487                     {
00488                         xmlFree( clipBegin );
00489                         xmlFree( clipEnd );
00490                         xmlFree( src );
00491                         return true;
00492                     }
00493                     else
00494                     {
00495                         begin += ( data->clipEnd - data->clipBegin + 1 );
00496                     }
00497                     fileCount++;
00498                 }
00499                 if ( src )
00500                     xmlFree( src );
00501                 if ( clipEnd )
00502                     xmlFree( clipEnd );
00503                 if ( clipBegin )
00504                     xmlFree( clipBegin );
00505             }
00506             node = node->next;
00507         }
00508     }
00509     data->absBegin = begin;
00510     data->clipFrame = 0;
00511     strcpy( data->fileName, "" );
00512     return false;
00513 }
00514 
00515 
00516 static bool findSceneEnd( xmlNodePtr node, void *p, bool *freed )
00517 {
00518     bool found = false;
00519     xmlChar *src = NULL;
00520     MovieInfo *data = ( MovieInfo* ) p;
00521 
00522     // if this is a <seq> node process all of its <video> child nodes
00523 
00524     if ( xmlStrcmp( node->name, ( const xmlChar* ) "seq" ) == 0 )
00525     {
00526 
00527         data->sequence = node;
00528 
00529         node = node->children;
00530         while ( node != NULL )
00531         {
00532             if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00533             {
00534 
00535                 data->video = node;
00536 
00537                 if ( src )
00538                     xmlFree( src );
00539 
00540                 src = xmlGetProp( node, ( const xmlChar* ) "src" );
00541                 xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00542                 xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00543 
00544                 if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) )
00545                 {
00546 
00547                     data->clipBegin = atoi( ( const char* ) clipBegin );
00548                     data->clipEnd = atoi( ( const char* ) clipEnd );
00549                     data->clipFrame = data->clipEnd;
00550 
00551                     if ( data->absFrame <= data->absBegin + data->clipEnd - data->clipBegin )
00552                         found = true;
00553                     data->absBegin += ( data->clipEnd - data->clipBegin + 1 );
00554                 }
00555 
00556                 if ( clipEnd )
00557                     xmlFree( clipEnd );
00558                 if ( clipBegin )
00559                     xmlFree( clipBegin );
00560             }
00561             node = node->next;
00562         }
00563 
00564         if ( found )
00565         {
00566             strcpy( data->fileName, ( char * ) src );
00567             xmlFree( src );
00568             data->absEnd = data->absBegin - 1;
00569             return true;
00570         }
00571 
00572         if ( src )
00573             xmlFree( src );
00574     }
00575     data->clipFrame = 0;
00576     strcpy( data->fileName, "" );
00577     return false;
00578 }
00579 
00594 static bool countFrames( xmlNodePtr node, void *p, bool *freed )
00595 {
00596     if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00597     {
00598 
00599         xmlChar * src = xmlGetProp( node, ( const xmlChar* ) "src" );
00600         xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00601         xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00602 
00603         if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) )
00604             * ( ( int* ) p ) += atoi( ( const char* ) clipEnd ) - atoi( ( const char* ) clipBegin ) + 1;
00605 
00606         if ( clipEnd )
00607             xmlFree( clipEnd );
00608         if ( clipBegin )
00609             xmlFree( clipBegin );
00610         if ( src )
00611             xmlFree( src );
00612     }
00613     return false;
00614 }
00615 
00617 typedef struct
00618 {
00619     string file;   /* Contains filename */
00620     string clipBegin; /* Contains begin counts */
00621     string clipEnd;   /* Continas end count */
00622 }
00623 EliInfo;
00624 
00625 typedef list<EliInfo> EliInfos;
00626 typedef EliInfos::iterator EliInfosIterator;
00627 
00640 static bool convertEli( xmlNodePtr node, void *p, bool *freed )
00641 {
00642     if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00643     {
00644 
00645         xmlChar * src = xmlGetProp( node, ( const xmlChar* ) "src" );
00646         xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00647         xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00648 
00649         if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) )
00650         {
00651             /*
00652             cout << "convertEli : " << src << ", " << clipBegin << ", " << clipEnd
00653             << endl; 
00654             */
00655             EliInfos * Eli = ( EliInfos * ) p;
00656             EliInfo tmp;
00657             tmp.file = ( const char* ) src;
00658             tmp.clipBegin = ( const char* ) clipBegin;
00659             tmp.clipEnd = ( const char* ) clipEnd;
00660             Eli->push_back( tmp );
00661         }
00662 
00663         if ( clipEnd )
00664             xmlFree( clipEnd );
00665         if ( clipBegin )
00666             xmlFree( clipBegin );
00667         if ( src )
00668             xmlFree( src );
00669     }
00670     return false;
00671 }
00672 
00673 class SrtContext
00674 {
00675 public:
00676     ofstream file;
00677     unsigned counter;
00678     xmlChar* title;
00679     xmlChar* abstract;
00680     xmlChar* src;
00681     unsigned begin;
00682     unsigned duration;
00683 
00684     SrtContext( const char* filename ) :
00685         file(filename), counter(0), title(NULL), abstract(NULL), src(NULL),
00686         begin(0), duration(0)
00687     {
00688     }
00689 
00690     ~SrtContext()
00691     {
00692         printEntry();
00693         file.close();
00694     }
00695 
00696     void printEntry(void)
00697     {
00698         if ( title || abstract )
00699         {
00700             Frame *frame = GetFramePool( )->GetFrame( );
00701             FileHandler *mediaFile = GetFileMap()->GetMap() [ string( ( const char* ) src ) ];
00702             SMIL::MediaClippingTime time;
00703 
00704             mediaFile->GetFrame( *frame, 0 );
00705             time.setFramerate( frame->GetFrameRate() );
00706             GetFramePool( )->DoneWithFrame( frame );
00707             string beginString = time.parseFramesToString( begin, SMIL::Time::TIME_FORMAT_CLOCK );
00708             string durationString = time.parseFramesToString( duration - begin, SMIL::Time::TIME_FORMAT_CLOCK );
00709             begin = 0;
00710 
00711             file << ++counter << endl;
00712             file << StringUtils::replaceAll( beginString, ".", "," );
00713             file << " --> " << StringUtils::replaceAll( durationString, ".", "," ) << endl;
00714             if ( title )
00715                 file << title << endl;
00716             if ( abstract )
00717                 file << abstract << endl;
00718             file << endl;
00719         }
00720         if ( src )
00721         {
00722             xmlFree( src );
00723             src = NULL;
00724         }
00725         if ( title )
00726         {
00727             xmlFree( title );
00728             title = NULL;
00729         }
00730         if ( abstract )
00731         {
00732             xmlFree( abstract );
00733             abstract = NULL;
00734         }
00735     }
00736 };
00737 
00738 static bool convertSrt( xmlNodePtr node, void *data, bool *freed )
00739 {
00740     SrtContext* srt = static_cast< SrtContext* >( data );
00741     
00742     if ( xmlStrcmp( node->name, ( const xmlChar* ) "seq" ) == 0 )
00743     {
00744         srt->printEntry();
00745         srt->title = xmlGetProp( node, ( const xmlChar * ) "title" );
00746         srt->abstract = xmlGetProp( node, ( const xmlChar * ) "abstract" );
00747     }
00748     else if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00749     {
00750         xmlChar* clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00751         xmlChar* clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00752 
00753         srt->src = xmlGetProp( node, ( const xmlChar* ) "src" );
00754         if ( !srt->begin && ( srt->title || srt->abstract ) )
00755             srt->begin = srt->duration;
00756         srt->duration += atoi( ( const char* ) clipEnd ) - atoi( ( const char* ) clipBegin ) + 1;
00757 
00758         xmlFree( clipBegin );
00759         xmlFree( clipEnd );
00760     }
00761 
00762     return false;
00763 }
00764 
00765 static bool convertFramesToSmilTime( xmlNodePtr node, void *data, bool *freed )
00766 {
00767     if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00768     {
00769         Frame *frame = GetFramePool( )->GetFrame( );
00770         xmlChar *src = xmlGetProp( node, ( const xmlChar* ) "src" );
00771         string index( ( char* ) src );
00772         xmlFree( src );
00773         FileHandler *mediaFile = GetFileMap()->GetMap() [ index ];
00774         mediaFile->GetFrame( *frame, 0 );
00775         SMIL::MediaClippingTime time;
00776         time.setFramerate( frame->GetFrameRate() );
00777         GetFramePool( )->DoneWithFrame( frame );
00778 
00779         xmlChar *prop = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00780         if ( prop )
00781         {
00782             std::string newValue = time.parseFramesToString( atoi( ( const char* ) prop ), SMIL::Time::TIME_FORMAT_CLOCK );
00783             xmlFree( prop );
00784             xmlSetProp( node, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) newValue.c_str() );
00785         }
00786         prop = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00787         if ( prop )
00788         {
00789             std::string newValue = time.parseFramesToString( atoi( ( const char* ) prop ), SMIL::Time::TIME_FORMAT_CLOCK );
00790             xmlFree( prop );
00791             xmlSetProp( node, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) newValue.c_str() );
00792         }
00793     }
00794     return false;
00795 }
00796 
00797 static bool convertSmilTimeToFrames( xmlNodePtr node, void *data, bool *freed )
00798 {
00799     if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 )
00800     {
00801         Frame *frame = GetFramePool( )->GetFrame( );
00802         xmlChar *src = xmlGetProp( node, ( const xmlChar* ) "src" );
00803         string index( ( char* ) src );
00804         xmlFree( src );
00805         FileHandler *mediaFile = GetFileMap()->GetMap() [ index ];
00806         mediaFile->GetFrame( *frame, 0 );
00807         SMIL::MediaClippingTime time;
00808         time.setFramerate( frame->GetFrameRate() );
00809         GetFramePool( )->DoneWithFrame( frame );
00810 
00811         xmlChar *prop = xmlGetProp( node, ( const xmlChar* ) "clipBegin" );
00812         if ( prop )
00813         {
00814             time.parseValue( ( const char* ) prop );
00815             xmlFree( prop );
00816             std::string newValue = time.toString( SMIL::Time::TIME_FORMAT_FRAMES );
00817             xmlSetProp( node, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) newValue.c_str() );
00818         }
00819         prop = xmlGetProp( node, ( const xmlChar* ) "clipEnd" );
00820         if ( prop )
00821         {
00822             time.parseValue( ( const char* ) prop );
00823             xmlFree( prop );
00824             std::string newValue = time.toString( SMIL::Time::TIME_FORMAT_FRAMES );
00825             xmlSetProp( node, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) newValue.c_str() );
00826         }
00827     }
00828     return false;
00829 }
00830 
00831 static bool clone( xmlNodePtr node, void *data, bool *freed )
00832 {
00833     xmlNodePtr* parent = (xmlNodePtr*) data;
00834 
00835     xmlNodePtr child = xmlNewNode( NULL, node->name );
00836     xmlAddChild( *parent, child );
00837     for ( xmlAttr* attr = node->properties; attr; attr = attr->next )
00838         xmlNewProp( child, attr->name, xmlGetProp( attr->parent, attr->name) );
00839     if ( node->children ) // next iteration in depth-first traversal will be a child
00840         *parent = child;
00841     else if ( node == node->parent->last ) // last sibling
00842         *parent = (*parent)->parent;
00843     
00844     return false;
00845 }
00846 
00861 static bool parse( xmlNodePtr node, callback func, void *p )
00862 {
00863     bool done = false;
00864 
00865     while ( node != NULL && done == false )
00866     {
00867         bool freed = false;
00868         xmlNodePtr next = node->next;
00869         done = ( *func ) ( node, p, &freed );
00870         if ( !done && !freed && node->children )
00871             done = parse( node->children, func, p );
00872         node = next;
00873     }
00874     return done;
00875 }
00876 
00877 
00880 PlayList::PlayList() : dirty( false ), doc_name( "" ), count( 0 )
00881 {
00882     xmlNsPtr    ns;
00883     xmlNodePtr  root;
00884 
00885     // cerr << "*PlayList::PlayList()" << endl;
00886 
00887     doc = xmlNewDoc( ( const xmlChar* ) "1.0" );
00888     root = xmlNewNode( NULL, ( const xmlChar* ) "smil" );
00889     ns = xmlNewNs( root, SMIL20_NAMESPACE_HREF, ( const xmlChar* ) NULL );
00890     xmlDocSetRootElement( doc, root );
00891     xmlAddChild( root, xmlNewNode( NULL, ( const xmlChar* ) "body" ) );
00892 }
00893 
00894 
00897 PlayList::PlayList( const PlayList& playList )
00898 {
00899     xmlNsPtr    ns;
00900     xmlNodePtr  root;
00901 
00902     doc = xmlNewDoc( ( const xmlChar* ) "1.0" );
00903     root = xmlNewNode( NULL, ( const xmlChar* ) "smil" );
00904     ns = xmlNewNs( root, ( const xmlChar* ) SMIL20_NAMESPACE_HREF, ( const xmlChar* ) NULL );
00905     xmlDocSetRootElement( doc, root );
00906     parse( playList.GetBody(), clone, &root );
00907     dirty = playList.dirty;
00908     doc_name = playList.GetDocName( );
00909     RefreshCount( );
00910 }
00911 
00912 
00915 PlayList& PlayList::operator=( const PlayList& playList )
00916 {
00917     // cerr << "*PlayList::operator=(const PlayList& playList)" << endl;
00918 
00919     if ( doc != playList.doc )
00920     {
00921         xmlNsPtr    ns;
00922         xmlNodePtr  root;
00923 
00924         xmlFreeDoc( doc );
00925         doc = xmlNewDoc( ( const xmlChar* ) "1.0" );
00926         root = xmlNewNode( NULL, ( const xmlChar* ) "smil" );
00927         ns = xmlNewNs( root, ( const xmlChar* ) SMIL20_NAMESPACE_HREF, ( const xmlChar* ) NULL );
00928         xmlDocSetRootElement( doc, root );
00929         parse( playList.GetBody(), clone, &root );
00930         dirty = playList.dirty;
00931         doc_name = playList.GetDocName( );
00932         RefreshCount( );
00933     }
00934     return *this;
00935 }
00936 
00937 
00941 PlayList::~PlayList()
00942 {
00943     // cerr << "*PlayList::~PlayList()" << endl;
00944     if ( doc != NULL )
00945     {
00946         xmlFreeDoc( doc );
00947         doc = NULL;
00948     }
00949 }
00950 
00955 xmlNodePtr PlayList::GetBody( ) const
00956 {
00957     return xmlDocGetRootElement( doc )->children;
00958 }
00959 
00960 
00961 void PlayList::RefreshCount( )
00962 {
00963     count = 0;
00964     if ( doc != NULL )
00965         parse( GetBody(), countFrames, &count );
00966 }
00967 
00972 int PlayList::GetNumFrames() const
00973 {
00974     return count;
00975 }
00976 
00977 
00978 char* PlayList::GetFileNameOfFrame( int frameNum ) const
00979 {
00980     // cerr << "char* PlayList::GetFileNameOfFrame(int frameNum)" << endl;
00981     MovieInfo data;
00982 
00983     memset( &data, 0, sizeof( MovieInfo ) );
00984     data.absBegin = 0;
00985     data.absEnd = 0;
00986     data.absFrame = frameNum;
00987 
00988     parse( GetBody(), findFile, &data );
00989     return strdup( data.fileName );
00990 }
00991 
00992 
01002 bool PlayList::GetFrame( int frameNum, Frame &frame )
01003 {
01004     MovieInfo data;
01005 
01006     // cerr << "bool PlayList::GetFrame(" << frameNum << ", Frame &frame)" << endl;
01007 
01008     memset( &data, 0, sizeof( MovieInfo ) );
01009     data.absBegin = 0;
01010     data.absEnd = 0;
01011     data.absFrame = frameNum;
01012 
01013     parse( GetBody(), findFile, &data );
01014 
01015     if ( strcmp( data.fileName, "" ) )
01016     {
01017         // NB: In our playlist, we enforce an internal absolute path - there is no need
01018         // to convert the file name here
01019         string index( ( char * ) data.fileName );
01020         FileHandler *mediaFile = GetFileMap() ->GetMap() [ index ];
01021         if ( data.clipFrame >= mediaFile->GetTotalFrames() )
01022             data.clipFrame = mediaFile->GetTotalFrames() - 1;
01023         return ( mediaFile->GetFrame( frame, data.clipFrame ) >= 0 );
01024     }
01025 
01026     return false;
01027 }
01028 
01029 
01036 bool PlayList::GetMediaObject( int frameNum, FileHandler **media )
01037 {
01038     MovieInfo data;
01039 
01040     memset( &data, 0, sizeof( MovieInfo ) );
01041     data.absBegin = 0;
01042     data.absEnd = 0;
01043     data.absFrame = frameNum;
01044 
01045     parse( GetBody(), findFile, &data );
01046 
01047     if ( strcmp( data.fileName, "" ) )
01048     {
01049         // Again, the absolute path is ensure internally - no need to convert
01050         string index( ( char * ) data.fileName );
01051         *media = GetFileMap() ->GetMap() [ index ];
01052         return true;
01053     }
01054 
01055     return false;
01056 }
01057 
01058 
01059 int PlayList::GetClipBegin( int frameNum ) const
01060 {
01061     MovieInfo data;
01062 
01063     memset( &data, 0, sizeof( MovieInfo ) );
01064     data.absBegin = 0;
01065     data.absEnd = 0;
01066     data.absFrame = frameNum;
01067 
01068     if ( parse( GetBody(), findSceneStart, &data ) )
01069         return data.clipBegin;
01070     else
01071         return 0;
01072 }
01073 
01074 
01075 int PlayList::GetClipEnd( int frameNum ) const
01076 {
01077     MovieInfo data;
01078 
01079     memset( &data, 0, sizeof( MovieInfo ) );
01080     data.absBegin = 0;
01081     data.absEnd = 0;
01082     data.absFrame = frameNum;
01083 
01084     if ( parse( GetBody(), findSceneEnd, &data ) )
01085         return data.clipEnd;
01086     else
01087         return 0;
01088 }
01089 
01090 
01091 bool PlayList::SetClipBegin( int frameNum, const char* value )
01092 {
01093     MovieInfo data;
01094 
01095     memset( &data, 0, sizeof( MovieInfo ) );
01096     data.absBegin = 0;
01097     data.absEnd = 0;
01098     data.absFrame = frameNum;
01099 
01100     if ( parse( GetBody(), findSceneStart, &data ) )
01101     {
01102         xmlSetProp( data.video, ( const xmlChar * ) "clipBegin", ( const xmlChar * ) value );
01103         RefreshCount( );
01104         return true;
01105     }
01106     else
01107         return false;
01108 }
01109 
01110 
01111 bool PlayList::SetClipEnd( int frameNum, const char* value )
01112 {
01113     MovieInfo data;
01114 
01115     memset( &data, 0, sizeof( MovieInfo ) );
01116     data.absBegin = 0;
01117     data.absEnd = 0;
01118     data.absFrame = frameNum;
01119 
01120     if ( parse( GetBody(), findSceneEnd, &data ) )
01121     {
01122         xmlSetProp( data.video, ( const xmlChar * ) "clipEnd", ( const xmlChar * ) value );
01123         RefreshCount( );
01124         return true;
01125     }
01126     else
01127         return false;
01128 }
01129 
01130 
01131 int PlayList::FindStartOfScene( int frameNum ) const
01132 {
01133     MovieInfo data;
01134 
01135     // cerr << "int PlayList::FindStartOfScene(int frameNum)" << endl;
01136 
01137     memset( &data, 0, sizeof( MovieInfo ) );
01138     data.absBegin = 0;
01139     data.absEnd = 0;
01140     data.absFrame = frameNum;
01141 
01142     parse( GetBody(), findSceneStart, &data );
01143 
01144     if ( strcmp( data.fileName, "" ) )
01145         return data.absBegin;
01146     else
01147         return 0;
01148 }
01149 
01150 
01151 int PlayList::FindEndOfScene( int frameNum ) const
01152 {
01153     MovieInfo data;
01154 
01155     // cerr << "int PlayList::FindEndOfScene(int frameNum)" << endl;
01156 
01157     data.absBegin = 0;
01158     data.absEnd = 0;
01159     data.absFrame = frameNum;
01160 
01161     parse( GetBody(), findSceneEnd, &data );
01162 
01163     if ( strcmp( data.fileName, "" ) )
01164         return data.absEnd;
01165     else
01166         return 999999;
01167 }
01168 
01169 
01170 void PlayList::AutoSplit( int start, int end )
01171 {
01172     MovieInfo firstFile;
01173     MovieInfo lastFile;
01174     Frame *frame = GetFramePool( ) ->GetFrame( );
01175     struct tm   recDate;
01176     time_t  startTime;
01177     time_t  endTime;
01178 
01179     // cerr << "PlayList::AutoSplit(int start=" << start << ", int end=" << end << ")" ;
01180     // cerr << endl;
01181 
01182     memset( &firstFile, 0, sizeof( MovieInfo ) );
01183     firstFile.absBegin = 0;
01184     firstFile.absEnd = 0;
01185     firstFile.absFrame = start;
01186 
01187     parse( GetBody(), findFile, &firstFile );
01188     string index1( ( char* ) firstFile.fileName );
01189     FileHandler *mediaFile1 = GetFileMap() ->GetMap() [ index1 ];
01190     mediaFile1->GetFrame( *frame, firstFile.clipFrame );
01191     frame->GetRecordingDate( recDate );
01192     startTime = mktime( &recDate );
01193 
01194     memset( &lastFile, 0, sizeof( MovieInfo ) );
01195     lastFile.absBegin = 0;
01196     lastFile.absEnd = 0;
01197     lastFile.absFrame = end;
01198 
01199     parse( GetBody(), findFile, &lastFile );
01200 
01201     string index2( ( char* ) lastFile.fileName );
01202     FileHandler *mediaFile2 = GetFileMap() ->GetMap() [ index2 ];
01203     mediaFile2->GetFrame( *frame, lastFile.clipFrame );
01204     frame->GetRecordingDate( recDate );
01205     endTime = mktime( &recDate );
01206 
01207     int fps = frame->IsPAL() ? 25 : 30;
01208 
01209     GetFramePool( ) ->DoneWithFrame( frame );
01210 
01211     // bail out on invalid recording date/time
01212     if ( startTime < 0 || endTime < 0 )
01213         return ;
01214 
01215     AutoSplit ( start, startTime, end, endTime, fps );
01216 }
01217 
01218 
01219 void PlayList::AutoSplit( int start, time_t startTime, int end, time_t endTime, int fps )
01220 {
01221     time_t diffTime = static_cast<time_t>( difftime( endTime, startTime ) );
01222     if ( ( ( diffTime * fps ) - ( end - start ) ) > fps || diffTime < 0.0 )
01223     {
01224         if ( ( end - start ) > 1 )
01225         {
01226             time_t mid = start + ( end - start ) / 2;
01227             time_t midTime;
01228             // reduce the scope of temporary varables here to reduce memory usage when recurring:
01229             {
01230                 MovieInfo midFile;
01231                 struct tm recDate;
01232                 Frame *frame = GetFramePool( ) ->GetFrame( );
01233 
01234                 memset( &midFile, 0, sizeof( MovieInfo ) );
01235                 midFile.absFrame = mid;
01236 
01237                 parse( GetBody(), findFile, &midFile );
01238 
01239                 string index( ( char* ) midFile.fileName );
01240                 FileHandler *mediaFile = GetFileMap() ->GetMap() [ index ];
01241                 mediaFile->GetFrame( *frame, midFile.clipFrame );
01242                 frame->GetRecordingDate( recDate );
01243                 midTime = mktime( &recDate );
01244 
01245                 GetFramePool( ) ->DoneWithFrame( frame );
01246             }
01247 
01248             // bail out on invalid recording date/time
01249             if ( midTime < 0 )
01250                 return ;
01251 
01252             AutoSplit ( start, startTime, mid, midTime, fps );
01253             AutoSplit ( mid, midTime, end, endTime, fps );
01254         }
01255         else
01256         {
01257             SplitSceneBefore( end );
01258         }
01259     }
01260 }
01261 
01262 
01263 bool PlayList::SplitSceneBefore( int frameNum )
01264 {
01265     MovieInfo data;
01266 
01267     // cerr << "PlayList::SplitSceneBefore(int frameNum=" << frameNum << ")" << endl;
01268 
01269     if ( GetNumFrames() == 0 )
01270         return false;
01271 
01272     memset( &data, 0, sizeof( MovieInfo ) );
01273     data.absBegin = 0;
01274     data.absEnd = 0;
01275     data.absFrame = frameNum;
01276     parse( GetBody(), findSceneStart, &data );
01277     int begin = data.absBegin;
01278 
01279     memset( &data, 0, sizeof( MovieInfo ) );
01280     data.absBegin = 0;
01281     data.absEnd = 0;
01282     data.absFrame = frameNum;
01283     parse( GetBody(), findSceneEnd, &data );
01284     int end = data.absEnd;
01285 
01286     if ( strcmp( data.fileName, "" ) && begin != frameNum )
01287     {
01288 
01289         dirty = true;
01290 
01291         // Copy the right hand side of the current scene
01292         xmlNode *firstSequence = data.sequence;
01293         PlayList playlist;
01294         GetPlayList( frameNum, end, playlist );
01295 
01296         // Paste it after the current scene
01297         xmlAddNextSibling( firstSequence, playlist.GetBody()->children );
01298 
01299         // in the first sequence, delete from frameNum to end of scene
01300         Delete( frameNum, end );
01301 
01302         return true;
01303     }
01304     else
01305     {
01306         return false;
01307     }
01308 }
01309 
01310 bool PlayList::JoinScenesAt( int frameNum )
01311 {
01312     MovieInfo scene1;
01313     MovieInfo scene2;
01314     MovieInfo scene2end;
01315 
01316     if ( GetNumFrames() == 0 )
01317         return false;
01318 
01319     // cerr << "PlayList::JoinScenesAt(int frameNum=" << frameNum << ")" << endl;
01320 
01321     memset( &scene1, 0, sizeof( MovieInfo ) );
01322     scene1.absBegin = 0;
01323     scene1.absEnd = 0;
01324     scene1.absFrame = frameNum;
01325     parse( GetBody(), findSceneStart, &scene1 );
01326 
01327     memset( &scene2, 0, sizeof( MovieInfo ) );
01328     scene2.absBegin = 0;
01329     scene2.absEnd = 0;
01330     scene2.absFrame = frameNum;
01331     parse( GetBody(), findSceneEnd, &scene2 );
01332     int end = scene2.absEnd + 1;
01333 
01334     memset( &scene2end, 0, sizeof( MovieInfo ) );
01335     scene2end.absBegin = 0;
01336     scene2end.absEnd = 0;
01337     scene2end.absFrame = end;
01338     parse( GetBody(), findSceneEnd, &scene2end );
01339 
01340     if ( scene1.sequence != scene2end.sequence )
01341     {
01342 
01343         dirty = true;
01344 
01345         // cerr << ">>>> Joining scene at " << scene1.absBegin << " with scene at "
01346         // << scene2.absBegin << " which ends at " << scene2end.absEnd << endl;
01347 
01348         // concatenate the contents of scene2 into scene1
01349         xmlNode *lastchild = xmlGetLastChild( scene1.sequence );
01350         xmlNodePtr next = NULL;
01351         for ( xmlNodePtr ptr = scene2end.sequence->children; ptr != NULL; ptr = next )
01352         {
01353             next = ptr->next;
01354             lastchild = xmlAddNextSibling( lastchild, ptr );
01355             ptr = next;
01356         }
01357         // Merge the metadata properties
01358         for ( xmlAttr* attr = scene2end.sequence->properties; attr; attr = attr->next )
01359         {
01360             const char *valueB = ( const char* ) xmlGetProp( attr->parent, ( const xmlChar * ) attr->name );
01361             // Only if the value is meaningful
01362             if ( valueB && strcmp( valueB, "" ) )
01363             {
01364                 // See if the property exists on clip A
01365                 const char *valueA = ( const char* ) xmlGetProp( scene1.sequence, ( const xmlChar * ) attr->name );
01366                 if ( valueA && strcmp( valueA, "" ) )
01367                 {
01368                     xmlChar* value = new xmlChar[ strlen(valueA) + strlen(valueB) + 1 ];
01369                     strcpy( (char*) value, (const char*) valueA );
01370                     strcat( (char*) value, " " );
01371                     strcat( (char*) value, (const char*) valueB );
01372                     xmlSetProp( scene1.sequence, attr->name, ( const xmlChar * ) value );
01373                     delete[] value;
01374                 }
01375                 else
01376                 {
01377                     xmlSetProp( scene1.sequence, attr->name, ( const xmlChar * ) valueB );
01378                 }
01379             }
01380         }
01381         xmlUnlinkNode( scene2end.sequence );
01382         xmlFreeNode( scene2end.sequence );
01383         RefreshCount( );
01384 
01385         return true;
01386     }
01387     else
01388     {
01389         return false;
01390     }
01391 }
01392 
01393 
01405 bool PlayList::GetPlayList( int first, int last, PlayList &playlist ) const
01406 {
01407     MovieInfo firstFile, lastFile;
01408     bool copyFlag = false;
01409 
01410     if ( GetNumFrames() == 0 )
01411         return false;
01412 
01413     playlist.dirty = false;
01414 
01415     // cerr << " bool PlayList::Copy(int first, int last, PlayList &playlist) " << endl;
01416 
01417     memset( &firstFile, 0, sizeof( MovieInfo ) );
01418     firstFile.absBegin = 0;
01419     firstFile.absEnd = 0;
01420     firstFile.absFrame = first;
01421 
01422     parse( GetBody(), findFile, &firstFile );
01423 
01424     memset( &lastFile, 0, sizeof( MovieInfo ) );
01425     lastFile.absBegin = 0;
01426     lastFile.absEnd = 0;
01427     lastFile.absFrame = last;
01428 
01429     parse( GetBody(), findFile, &lastFile );
01430 
01431     if ( strcmp( firstFile.fileName, "" ) && strcmp( lastFile.fileName, "" ) )
01432     {
01433 
01434         xmlNodePtr srcNode = GetBody();
01435         xmlNodePtr dstNode = playlist.GetBody();
01436         xmlNodePtr nextSeq = NULL;
01437 
01438         for ( xmlNodePtr srcSeq = srcNode->children; srcSeq != NULL; srcSeq = nextSeq )
01439         {
01440             nextSeq = srcSeq->next;
01441             if ( xmlStrcmp( srcSeq->name, ( const xmlChar* ) "seq" ) == 0 )
01442             {
01443                 xmlNodePtr seq = xmlNewNode( NULL, ( const xmlChar* ) "seq" );
01444                 xmlAddChild( dstNode, seq );
01445                 xmlNodePtr nextVideo = NULL;
01446 
01447                 for ( xmlNodePtr srcVideo = srcSeq->children; srcVideo != NULL; srcVideo = nextVideo )
01448                 {
01449                     nextVideo = srcVideo->next;
01450                     if ( xmlStrcmp( srcVideo->name, ( const xmlChar* ) "video" ) == 0 )
01451                     {
01452 
01453                         // case 1: selection contains more than one file. This one is neither the first nor the last.
01454 
01455                         if ( copyFlag && srcVideo != firstFile.video && srcVideo != lastFile.video )
01456                         {
01457                             xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" );
01458                             xmlAddChild( seq, video );
01459                             for ( xmlAttr* attr = srcVideo->properties; attr; attr = attr->next )
01460                                 xmlNewProp( video, attr->name, xmlGetProp( attr->parent, attr->name) );
01461                         }
01462 
01463                         // case 2: selection contains more than one file and this is the first file
01464 
01465                         else if ( srcVideo == firstFile.video && srcVideo != lastFile.video )
01466                         {
01467 
01468                             ostringstream sb1, sb2;
01469 
01470                             xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" );
01471                             xmlNewProp( video, ( const xmlChar* ) "src", ( const xmlChar* ) firstFile.fileName );
01472                             sb1 << firstFile.clipFrame << ends;
01473                             xmlNewProp( video, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb1.str().c_str() );
01474                             sb2 << firstFile.clipEnd << ends;
01475                             xmlNewProp( video, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb2.str().c_str() );
01476                             xmlAddChild( seq, video );
01477                             copyFlag = true;
01478                         }
01479 
01480                         // case 3: selection contains more than one file and this is the last file
01481 
01482                         else if ( srcVideo != firstFile.video && srcVideo == lastFile.video )
01483                         {
01484 
01485                             ostringstream sb1, sb2;
01486 
01487                             xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" );
01488                             xmlNewProp( video, ( const xmlChar* ) "src", ( const xmlChar* ) lastFile.fileName );
01489                             sb1 << lastFile.clipBegin << ends;
01490                             xmlNewProp( video, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb1.str().c_str() );
01491                             sb2 << lastFile.clipFrame << ends;
01492                             xmlNewProp( video, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb2.str().c_str() );
01493                             xmlAddChild( seq, video );
01494                             copyFlag = false;
01495                         }
01496 
01497                         // case 4: selection contains exactly one file
01498 
01499                         else if ( srcVideo == firstFile.video && srcVideo == lastFile.video )
01500                         {
01501 
01502                             ostringstream sb1, sb2;
01503 
01504                             xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" );
01505                             xmlNewProp( video, ( const xmlChar* ) "src", ( const xmlChar* ) firstFile.fileName );
01506                             sb1 << firstFile.clipFrame << ends;
01507                             xmlNewProp( video, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb1.str().c_str() );
01508                             sb2 << lastFile.clipFrame << ends;
01509                             xmlNewProp( video, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb2.str().c_str() );
01510                             xmlAddChild( seq, video );
01511                         }
01512                     }
01513                 }
01514 
01515                 // if this sequence does not have any video clips, remove it
01516 
01517                 if ( seq->children == NULL )
01518                 {
01519                     xmlUnlinkNode( seq );
01520                     xmlFreeNode( seq );
01521                 }
01522                 else
01523                 // copy the seq attributes
01524                 {
01525                     for ( xmlAttr* attr = srcSeq->properties; attr; attr = attr->next )
01526                         xmlNewProp( seq, attr->name, xmlGetProp( attr->parent, attr->name) );
01527                 }
01528             }
01529         }
01530         // PASS PATH
01531         string path = directory_utils::get_directory_from_file( GetDocName() );
01532         parse( playlist.GetBody(), fillMap, &path );
01533     }
01534     playlist.RefreshCount( );
01535     return true;
01536 }
01537 
01538 
01550 bool PlayList::InsertPlayList( PlayList &playlist, int before )
01551 {
01552     // cerr << "bool PlayList::Paste(PlayList &playlist, int before(=" << before << "))" << endl;
01553 
01554     if ( playlist.GetNumFrames() == 0 )
01555         return false;
01556 
01557     // PASS PATH
01558     string path = directory_utils::get_directory_from_file( GetDocName() );
01559     parse( playlist.GetBody(), fillMap, &path );
01560 
01561     MovieInfo file;
01562 
01563     memset( &file, 0, sizeof( MovieInfo ) );
01564     file.absBegin = 0;
01565     file.absEnd = 0;
01566     file.absFrame = before;
01567     file.sequence = NULL;
01568     file.video = NULL;
01569 
01570     // Fill the map with any new files now, before we change the doc
01571     parse( GetBody(), findFile, &file );
01572 
01573     xmlNodePtr node = playlist.GetBody();
01574     bool first = true;
01575     xmlNodePtr next = NULL;
01576     xmlNodePtr sequence = file.sequence;
01577 
01578     if ( GetNumFrames() > 0 )
01579     {
01580         dirty = true;
01581     }
01582     else
01583     {
01584         dirty = playlist.dirty;
01585 
01586         if ( doc_name == "" )
01587             doc_name = playlist.GetDocName( );
01588 
01589     }
01590 
01591     for ( xmlNodePtr ptr = node->children; ptr != NULL; ptr = next )
01592     {
01593 
01594         //cerr << endl << "Sibling" << endl;
01595         //xmlElemDump(stderr, NULL, ptr);
01596         //cerr << endl;
01597 
01598         // Get the next sibling before adding
01599         next = ptr->next;
01600 
01601         // If first and at start of scene insert, otherwise append
01602         // cerr << "Scene i'm pasting into starts at " << file.absBegin << " [" << file.absEnd << "]" << endl;
01603 
01604         if ( first && sequence == NULL )
01605         {
01606             // This strange approach avoids using xmlCopyNode, which adds extra namespace declarations
01607             xmlNodePtr tmp = xmlNewNode( NULL, ( const xmlChar* ) "seq" );
01608             xmlAddChild( GetBody(), tmp );
01609             sequence = xmlAddNextSibling( tmp, ptr );
01610             xmlUnlinkNode( tmp );
01611             xmlFreeNode( tmp );
01612         }
01613         else if ( first && before == file.absBegin && before != ( file.absEnd + 1 ) )
01614         {
01615             // cerr << "Inserting before " << before << endl;
01616             sequence = xmlAddPrevSibling( sequence, ptr );
01617         }
01618         else if ( first && before != ( file.absEnd + 1 ) )
01619         {
01620             // cerr << "Splitting scene that start at " << file.absBegin << " and ends at " << file.absEnd << " at " << before << endl;
01621             // cerr << endl << "Before Split" << endl;
01622             // xmlElemDump(stderr, NULL, sequence);
01623             // cerr << endl;
01624 
01625             // Split the current scene
01626             SplitSceneBefore( before );
01627 
01628             // Find our new position
01629             memset( &file, 0, sizeof( MovieInfo ) );
01630             file.absBegin = 0;
01631             file.absFrame = before;
01632             file.sequence = NULL;
01633             file.video = NULL;
01634 
01635             parse( GetBody(), findFile, &file );
01636 
01637             // cerr << endl << "After Split" << endl;
01638             // xmlElemDump(stderr, NULL, sequence);
01639             // cerr << endl;
01640 
01641             // Add before the scene returned
01642             sequence = xmlAddPrevSibling( file.sequence, ptr );
01643         }
01644         else
01645         {
01646             // cerr << "Inserting after " << before << endl;
01647             sequence = xmlAddNextSibling( sequence, ptr );
01648         }
01649 
01650         // We're definitely no longer first
01651         first = false;
01652     }
01653 
01654     RefreshCount( );
01655     return true;
01656 }
01657 
01658 
01659 bool PlayList::Delete( int first, int last )
01660 {
01661     int absClipBegin;
01662     int clipBegin;
01663     int clipEnd;
01664     static int firstCall = 0;
01665 
01666     // cerr << "bool PlayList::Delete(int first=" << first << ", int last=" << last << ")" << endl;
01667 
01668     // SplitSceneBefore calls Delete, avoid recursion
01669 
01670     if ( GetNumFrames() == 0 )
01671         return false;
01672 
01673     if ( firstCall == 0 )
01674     {
01675         firstCall = 1;
01676         SplitSceneBefore( first );
01677         firstCall = 0;
01678     }
01679 
01680     xmlNodePtr srcNode = GetBody();
01681     absClipBegin = 0;
01682     xmlNodePtr nextSequence = NULL;
01683     for ( xmlNodePtr srcSeq = srcNode->children; srcSeq != NULL; srcSeq = nextSequence )
01684     {
01685 
01686         dirty = true;
01687 
01688         // In case we need to delete this node, get the next pointer before starting
01689         nextSequence = srcSeq->next;
01690 
01691         if ( xmlStrcmp( srcSeq->name, ( const xmlChar* ) "seq" ) == 0 )
01692         {
01693             xmlNodePtr nextVideo = NULL;
01694 
01695             for ( xmlNodePtr srcVideo = srcSeq->children; srcVideo != NULL; srcVideo = nextVideo )
01696             {
01697 
01698                 // In case we have to delete this node
01699                 nextVideo = srcVideo->next;
01700 
01701                 if ( xmlStrcmp( srcVideo->name, ( const xmlChar* ) "video" ) == 0 )
01702                 {
01703 
01704                     ostringstream sb1, sb2;
01705                     xmlChar *s;
01706 
01707                     sb1 << ( s = xmlGetProp( srcVideo, ( const xmlChar* ) "clipBegin" ) ) << ends;
01708                     clipBegin = atoi( sb1.str().c_str() );
01709                     if ( s )
01710                         xmlFree( s );
01711                     s = xmlGetProp( srcVideo, ( const xmlChar* ) "clipEnd" );
01712                     clipEnd = atoi( ( char * ) s );
01713                     sb2 << ( s = xmlGetProp( srcVideo, ( const xmlChar* ) "clipEnd" ) ) << ends;
01714                     clipEnd = atoi( sb2.str().c_str() );
01715                     if ( s )
01716                         xmlFree( s );
01717 
01718                     // case 1: selection covers this file completely. Remove this file from playlist.
01719 
01720                     if ( first <= absClipBegin && last >= absClipBegin + clipEnd - clipBegin )
01721                     {
01722                         xmlUnlinkNode( srcVideo );
01723                         xmlFreeNode( srcVideo );
01724                         // cerr << "case 1 " << endl;
01725                     }
01726 
01727                     // case 2: selection starts before or at start of file and ends somewhere in the file.
01728                     // New start of file is now end of selection + 1
01729 
01730                     else if ( first <= absClipBegin && last >= absClipBegin && last <= absClipBegin + clipEnd - clipBegin )
01731                     {
01732 
01733                         ostringstream sb;
01734 
01735                         sb << last - absClipBegin + clipBegin + 1 << ends;
01736                         xmlSetProp( srcVideo, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb.str().c_str() );
01737                         // cerr << "case 2 " << endl;
01738                     }
01739 
01740                     // case 3: selection starts somewhere in the file and ends at or after the file
01741                     // New end of file is now start of selection - 1
01742 
01743                     else if ( first > absClipBegin && first <= absClipBegin + clipEnd - clipBegin && last >= absClipBegin + clipEnd - clipBegin )
01744                     {
01745 
01746                         ostringstream sb;
01747 
01748                         sb << first - absClipBegin + clipBegin - 1 << ends;
01749                         xmlSetProp( srcVideo, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb.str().c_str() );
01750                         // cerr << "case 3 " << endl;
01751                     }
01752 
01753                     // case 4: selection starts somewhere in the file and ends in the file.
01754                     // We must split this node such that end of first file is start of selection - 1
01755                     // and start of second file is end of selection + 1
01756 
01757                     else if ( first > absClipBegin && last < absClipBegin + clipEnd - clipBegin )
01758                     {
01759 
01760                         ostringstream sb1, sb2;
01761                         xmlChar *s;
01762 
01763                         xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" );
01764                         xmlNewProp( video, ( const xmlChar* ) "src", ( s = xmlGetProp( srcVideo, ( const xmlChar* ) "src" ) ) );
01765                         if ( s )
01766                             xmlFree( s );
01767                         sb1 << last - absClipBegin + clipBegin + 1 << ends;
01768                         xmlNewProp( video, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb1.str().c_str() );
01769                         xmlNewProp( video, ( const xmlChar* ) "clipEnd", ( s = xmlGetProp( srcVideo, ( const xmlChar* ) "clipEnd" ) ) );
01770                         if ( s )
01771                             xmlFree( s );
01772                         xmlAddNextSibling( srcVideo, video );
01773                         sb2 << first - absClipBegin + clipBegin - 1 << ends;
01774                         xmlSetProp( srcVideo, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb2.str().c_str() );
01775                         // cerr << "case 4 " << endl;
01776                     }
01777 
01778                     absClipBegin += clipEnd - clipBegin + 1;
01779                 }
01780             }
01781 
01782             // if the node is now empty, delete it (can delete - see nextSequence above)
01783 
01784             if ( srcSeq->children == NULL )
01785             {
01786                 xmlUnlinkNode( srcSeq );
01787                 xmlFreeNode( srcSeq );
01788             }
01789         }
01790     }
01791 
01792     RefreshCount( );
01793 
01794     return true;
01795 }
01796 
01797 
01798 bool PlayList::LoadMediaObject( char *filename )
01799 {
01800     // cerr << "bool PlayList::LoadAVI(" << filename << ")" << endl;
01801 
01802     xmlNodePtr  seq;
01803     xmlNodePtr  node;
01804     ostringstream   sb;
01805     FileHandler *mediaFile = NULL;
01806     int existingFrames;
01807     int framesInFile;
01808 
01809     dirty = true;
01810 
01811     // This object should be located in a directory relative to cwd or absolute
01812     string index = directory_utils::get_abs