Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
DISCONTINUED:openSUSE:11.1
smilutils
kino-1.1.0-playlistsupport.diff
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File kino-1.1.0-playlistsupport.diff of Package smilutils
--- smilutils-0.3.0/libkino/Makefile.am +++ smilutils-0.3.0/libkino/Makefile.am @@ -8,6 +8,8 @@ avi.cc avi.h \ filehandler.cc filehandler.h \ frame.cc frame.h \ + stringutils.cc stringutils.h \ + smiltime.cc smiltime.h \ playlist.cc playlist.h \ riff.cc riff.h \ error.cc error.h \ --- smilutils-0.3.0/libkino/Makefile.in +++ smilutils-0.3.0/libkino/Makefile.in @@ -109,6 +109,8 @@ avi.cc avi.h \ filehandler.cc filehandler.h \ frame.cc frame.h \ + stringutils.cc stringutils.h \ + smiltime.cc smiltime.h \ playlist.cc playlist.h \ riff.cc riff.h \ error.cc error.h \ @@ -128,6 +130,7 @@ libkinolegacy_la_LDFLAGS = libkinolegacy_la_DEPENDENCIES = am_libkinolegacy_la_OBJECTS = avi.lo filehandler.lo frame.lo playlist.lo \ + stringutils.lo smiltime.lo \ riff.lo error.lo preferences.lo gnome-stubs.lo libkinolegacy_la_OBJECTS = $(am_libkinolegacy_la_OBJECTS) @@ -142,6 +145,7 @@ @AMDEP_TRUE@ ./$(DEPDIR)/filehandler.Plo ./$(DEPDIR)/frame.Plo \ @AMDEP_TRUE@ ./$(DEPDIR)/gnome-stubs.Plo \ @AMDEP_TRUE@ ./$(DEPDIR)/playlist.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/smiltime.Plo ./$(DEPDIR)/stringutils.Plo \ @AMDEP_TRUE@ ./$(DEPDIR)/preferences.Plo ./$(DEPDIR)/riff.Plo CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) @@ -218,6 +222,8 @@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/playlist.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/preferences.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/riff.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smiltime.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stringutils.Plo@am__quote@ distclean-depend: -rm -rf ./$(DEPDIR) --- smilutils-0.3.0/libkino/playlist.cc +++ smilutils-0.3.0/libkino/playlist.cc @@ -1,21 +1,22 @@ /* - * playlist.cc - * Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ +* playlist.cc -- SMIL implementation +* Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de> +* Copyright (C) 2001-2007 Dan Dennedy <dan@dennedy.org> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ #ifdef HAVE_CONFIG_H #include <config.h> @@ -24,7 +25,7 @@ // C++ includes #include <iostream> #include <fstream> -#include <strstream> +#include <sstream> #include <string> #include <list> #include <map> @@ -33,7 +34,7 @@ using std::endl; using std::ends; using std::ofstream; -using std::strstream; +using std::ostringstream; using std::list; using std::map; @@ -43,52 +44,58 @@ #include <libxml/tree.h> #include <pthread.h> #include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> // local includes #include "playlist.h" #include "error.h" #include "filehandler.h" #include "frame.h" +#include "stringutils.h" + +const xmlChar* SMIL20_NAMESPACE_HREF = reinterpret_cast< const xmlChar* >( "http://www.w3.org/2001/SMIL20/Language" ); +const string KINO_AUTOSAVE_DIR = string( getenv("HOME") ) + string( "/.kino-history" ); -/** The file map implementation - this class is responsible for maintaining the list of +/** The file map implementation - this class is responsible for maintaining the list of open files associated to a project. */ class KinoFileMap : public FileMap { - private: - map < string, FileHandler* > filemap; +private: + map < string, FileHandler* > filemap; - public: - virtual ~KinoFileMap() - { - } +public: + virtual ~KinoFileMap() + {} - map<string, FileHandler*> &GetMap() - { - return filemap; - } + map<string, FileHandler*> &GetMap() + { + return filemap; + } - void Clear( ) - { - map<string, FileHandler*>::iterator n; - for (n = filemap.begin(); n != filemap.end(); ++n) - delete (*n).second; - filemap.erase( filemap.begin(), filemap.end() ); - } + void Clear( ) + { + map<string, FileHandler*>::iterator n; + for ( n = filemap.begin(); n != filemap.end(); ++n ) + delete ( *n ).second; + filemap.erase( filemap.begin(), filemap.end() ); + } - void GetUnusedFxFiles( PlayList &list, vector< string > &unused ) + void GetUnusedFxFiles( PlayList &list, vector< string > &unused ) + { + unused.erase( unused.begin(), unused.end() ); + map<string, FileHandler*>::iterator n; + for ( n = filemap.begin(); n != filemap.end(); ++n ) { - unused.erase( unused.begin(), unused.end() ); - map<string, FileHandler*>::iterator n; - for (n = filemap.begin(); n != filemap.end(); ++n) + if ( n->first.find( ".kinofx.dv" ) != string::npos && !list.IsFileUsed( n->first ) ) { - if ( n->first.find( ".kinofx.dv" ) != string::npos && !list.IsFileUsed( n->first ) ) - { - unused.push_back( n->first ); - } + unused.push_back( n->first ); } } + } }; /** Singleton file map object. @@ -96,82 +103,16 @@ FileMap *GetFileMap( ) { - static FileMap *thismap = new KinoFileMap( ); + static FileMap * thismap = new KinoFileMap( ); return thismap; } -/** Split and Join string utility methods. -*/ - -class string_utils -{ - public: - static int split( const string &input, const string &delimiter, vector< string > &items, const bool clean = true ); - static string join( vector< string >&items, const string &delimiter ); -}; - -/** Split the string by delimiter and return each resultant string in items. - - Note that the items vector is not cleaned betweeen iterations. -*/ - -int string_utils::split( const string &input, const string &delimiter, vector< string > &items, const bool clean ) -{ - int delimiter_size = delimiter.size(); - int input_size = input.size(); - - // Find the first delimiter - int position = 0; - int end_position = input.find( delimiter, 0 ); - - // While we have a valid position - while( end_position >= position ) - { - // Obtain the substr and push if valid - string s = input.substr( position, end_position - position ); - if( !clean || s.size() > 0 ) items.push_back(s); - - // Find the next delimiter - position = end_position + delimiter_size; - end_position = input.find( delimiter, position ); - } - - // Obtain the substr of what's left and push if valid - string s = input.substr( position, input_size - position ); - if( !clean || s.size() > 0 ) items.push_back(s); - - // Return the number of items found - return items.size(); -} - -/** Join the contents of items with the given delimiter. - - Note that the no leading or trailing delimters are output. -*/ - -string string_utils::join( vector< string >&items, const string &delimiter ) -{ - string output( "" ); - - // Loop through the items - for ( vector< string >::iterator item = items.begin( ); item != items.end( ); item ++ ) - { - if ( item == items.begin() ) - output += *item; - else - output += delimiter + *item; - } - - return string( output ); -} - - /** Join the file to the directory. - + It is assumed that if the input file is not an absolute path (ie: starting with a /) then the full path is relative to the current working directory. So 'file' would return /'cwd' and 'relative/file' would be /'cwd + relative'. - + Note that .. directory specs in the file input are normalised to the full path minus the .. and erroneously formed paths (containing double slashes for example) are also corrected in the output. @@ -186,15 +127,15 @@ { char path[ PATH_MAX ]; getcwd( path, PATH_MAX ); - string_utils::split( path, "/", items ); + StringUtils::split( path, "/", items ); } // Now add the directory if file is not absolute if ( file[ 0 ] != '/' ) - string_utils::split( directory, "/", items ); + StringUtils::split( directory, "/", items ); // Split the file and append to the directory info - string_utils::split( file, "/", items ); + StringUtils::split( file, "/", items ); // iterate through the items vector for ( vector< string >::iterator item = items.begin( ); item != items.end( ); ) @@ -206,7 +147,7 @@ items.erase( item ); item = items.begin(); } - else + else { items.erase( -- item + 1 ); items.erase( -- item + 1 ); @@ -219,11 +160,11 @@ } } - return "/" + string_utils::join( items, "/" ); + return "/" + StringUtils::join( items, "/" ); } /** Obtain the directory from the file. - + A value of 'file' that actually corresponds to a directory will result in the parent directory being returned ie: /'parent'/'directory' returns /'parent' which is probably not what you want... @@ -235,13 +176,13 @@ } /** Get absolute path to file. - + If file has an absolute path, then you will get a cleaned up version of that path and file returned (ie: /'directory'/'sub'/..///'file' becomes /'directory'/'file'). - + If file is relative (ie: not starting with a /) and directory is absolute (ie: starting with a /) then you will receive the appended output. - + If neither is absolute, then cwd is pre-pended to directory and file is appended and a cleaned up file spec is returned. */ @@ -252,7 +193,7 @@ } /** Get relative path to file. - + Given a directory of /same-path/different-path and file of /same-path/blah, the output should be ../blah. */ @@ -263,15 +204,15 @@ string absolute = join_file_to_directory( directory, file ); vector < string > directory_items; vector < string > absolute_items; - string_utils::split( absolute, "/", absolute_items ); - string_utils::split( directory, "/", directory_items ); + StringUtils::split( absolute, "/", absolute_items ); + StringUtils::split( directory, "/", directory_items ); vector < string >::iterator directory_item; vector < string >::iterator absolute_item; // While they're both the same, remove from both for ( directory_item = directory_items.begin(), absolute_item = absolute_items.begin(); - directory_item != directory_items.end() && absolute_item != absolute_items.end() && *directory_item == *absolute_item; ) + directory_item != directory_items.end() && absolute_item != absolute_items.end() && *directory_item == *absolute_item; ) { directory_items.erase( directory_item ); absolute_items.erase( absolute_item ); @@ -284,7 +225,7 @@ output += "../"; // Now simply join what's left in absolute to output and return - output += string_utils::join( absolute_items, "/" ); + output += StringUtils::join( absolute_items, "/" ); return output; } @@ -295,10 +236,10 @@ { string output; vector <string> items; - string_utils::split( directory, "/", items ); + StringUtils::split( directory, "/", items ); vector< string >::iterator item = items.begin( ); - if ( *item == "~" ) + if ( item != items.end( ) && *item == "~" ) { output = getenv( "HOME" ); item ++; @@ -310,21 +251,21 @@ return output; } -typedef bool (*callback)(xmlNodePtr node, void *p); +typedef bool ( *callback ) ( xmlNodePtr node, void *p, bool *freed ); typedef struct { - int absFrame; - int absBegin; - int absEnd; - int clipFrame; - int clipBegin; - int clipEnd; - int clipNumber; - int clipLength; - char fileName[ 1024 ]; - xmlNodePtr sequence; - xmlNodePtr video; + int absFrame; + int absBegin; + int absEnd; + int clipFrame; + int clipBegin; + int clipEnd; + int clipNumber; + int clipLength; + char fileName[ 1024 ]; + xmlNodePtr sequence; + xmlNodePtr video; } MovieInfo; @@ -349,65 +290,69 @@ \param p pointer to MovieInfo struct \result true if file has been found and xml tree walk is done */ -static bool findFile(xmlNodePtr node, void *p) +static bool findFile( xmlNodePtr node, void *p, bool *freed ) { - MovieInfo *data = (MovieInfo*)p; + MovieInfo * data = ( MovieInfo* ) p; - if (xmlStrcmp(node->name, (const xmlChar*)"seq") == 0) { - data->sequence = node; - data->clipNumber++; - } + if ( xmlStrcmp( node->name, ( const xmlChar* ) "seq" ) == 0 ) + { + data->sequence = node; + data->clipNumber++; + } - // if this is a <video> node, calculate its absolute begin and end positions + // if this is a <video> node, calculate its absolute begin and end positions - else if (xmlStrcmp(node->name, (const xmlChar*)"video") == 0) { + else if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 ) + { - data->video = node; + data->video = node; - // Check whether the required properties exist + // Check whether the required properties exist - xmlChar *src = xmlGetProp(node, (const xmlChar*)"src"); - xmlChar *clipBegin = xmlGetProp(node, (const xmlChar*)"clipBegin"); - xmlChar *clipEnd = xmlGetProp(node, (const xmlChar*)"clipEnd"); + xmlChar *src = xmlGetProp( node, ( const xmlChar* ) "src" ); + xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" ); + xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" ); - if ((src != NULL) && (clipBegin != NULL) && (clipEnd != NULL)) { + if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) ) + { - data->clipBegin = atoi((const char*)clipBegin); - data->clipEnd = atoi((const char*)clipEnd); + data->clipBegin = atoi( ( const char* ) clipBegin ); + data->clipEnd = atoi( ( const char* ) clipEnd ); - data->absBegin += data->clipLength; // add length of previous clip - data->clipLength = data->clipEnd - data->clipBegin + 1; - data->absEnd = data->absBegin + data->clipLength - 1; + data->absBegin += data->clipLength; // add length of previous clip + data->clipLength = data->clipEnd - data->clipBegin + 1; + data->absEnd = data->absBegin + data->clipLength - 1; - // cerr << "Number: " << data->clipNumber << " starts " << data->absBegin << " ends " << data->absEnd << endl; + // cerr << "Number: " << data->clipNumber << " starts " << data->absBegin << " ends " << data->absEnd << endl; - // if absFrame is within this scene, we have found the corresponding file. - // Otherwise, add frame count of this scene to absBegin + // if absFrame is within this scene, we have found the corresponding file. + // Otherwise, add frame count of this scene to absBegin - if (data->absFrame <= data->absEnd) { - strcpy( data->fileName, (char *)src ); - data->clipFrame = data->absFrame - data->absBegin + data->clipBegin; + if ( data->absFrame <= data->absEnd ) + { + strcpy( data->fileName, ( char * ) src ); + data->clipFrame = data->absFrame - data->absBegin + data->clipBegin; - // Free memory used - xmlFree(src); - xmlFree(clipEnd); - xmlFree(clipBegin); + // Free memory used + xmlFree( src ); + xmlFree( clipEnd ); + xmlFree( clipBegin ); - // cerr << "Obtaining frame " << data->clipFrame << " from " << data->clipNumber << endl; + // cerr << "Obtaining frame " << data->clipFrame << " from " << data->clipNumber << endl; - return true; // true means done traversing xml tree - } - } + return true; // true means done traversing xml tree + } + } - if (src) - xmlFree(src); - if (clipEnd) - xmlFree(clipEnd); - if (clipBegin) - xmlFree(clipBegin); + if ( src ) + xmlFree( src ); + if ( clipEnd ) + xmlFree( clipEnd ); + if ( clipBegin ) + xmlFree( clipBegin ); - } - return false; + } + return false; } @@ -417,63 +362,72 @@ \param p pointer to a string containing the project directory \result true if file has been found and xml tree walk is done */ -static bool fillMap(xmlNodePtr node, void *p) +static bool fillMap( xmlNodePtr node, void *p, bool *freed ) { - if (xmlStrcmp(node->name, (const xmlChar*)"video") == 0) { + if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 ) + { - // Check whether the required properties exist + // Check whether the required properties exist - xmlChar *src = xmlGetProp(node, (const xmlChar*)"src"); - xmlChar *clipBegin = xmlGetProp(node, (const xmlChar*)"clipBegin"); - xmlChar *clipEnd = xmlGetProp(node, (const xmlChar*)"clipEnd"); + xmlChar * src = xmlGetProp( node, ( const xmlChar* ) "src" ); + xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" ); + xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" ); - if ((src != NULL) && (clipBegin != NULL) && (clipEnd != NULL)) { + if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) ) + { - // Determine the absolute path to the file indicated by src - string &directory = *(string *)p; - string index = directory_utils::get_absolute_path_to_file( directory, (char *)src ); + // Determine the absolute path to the file indicated by src + string & directory = *( string * ) p; + string index = directory_utils::get_absolute_path_to_file( directory, ( char * ) src ); // Internally, we always use absolute paths so convert now, just to be on the safe side - xmlSetProp(node, (const xmlChar*)"src", (xmlChar *)index.c_str() ); + xmlSetProp( node, ( const xmlChar* ) "src", ( xmlChar * ) index.c_str() ); // Check if the file actually exists in the file map - if ( GetFileMap()->GetMap().find( index ) == GetFileMap()->GetMap().end()) { + if ( GetFileMap() ->GetMap().find( index ) == GetFileMap() ->GetMap().end() ) + { - FileHandler *mediaFile; + FileHandler * mediaFile; /* determine file type */ - if (strncasecmp(strrchr((char*)src,'.'), ".avi", 4) == 0) + if ( strncasecmp( strrchr( ( char* ) src, '.' ), ".avi", 4 ) == 0 ) mediaFile = new AVIHandler(); - else if (strncasecmp(strrchr((char*)src,'.'), ".dv", 3) == 0 || - strncasecmp(strrchr((char*)src,'.'), ".dif", 4) == 0) + else if ( strncasecmp( strrchr( ( char* ) src, '.' ), ".dv", 3 ) == 0 || + strncasecmp( strrchr( ( char* ) src, '.' ), ".dif", 4 ) == 0 ) mediaFile = new RawHandler(); #ifdef HAVE_LIBQUICKTIME - else if (strncasecmp(strrchr((char*)src,'.'), ".mov", 4) == 0) + + else if ( strncasecmp( strrchr( ( char* ) src, '.' ), ".mov", 4 ) == 0 ) mediaFile = new QtHandler(); #endif - else { + + else + { xmlFree( src ); - xmlFree(clipEnd); - xmlFree(clipBegin); + xmlFree( clipEnd ); + xmlFree( clipBegin ); return false; } /* construct appropriate filehandler */ - if ( mediaFile->Open( index.c_str() ) ) { - GetFileMap()->GetMap()[ index ] = mediaFile; + if ( mediaFile->Open( index.c_str() ) ) + { + GetFileMap() ->GetMap() [ index ] = mediaFile; } - else { - cerr << "Unable to open " << (char *)src - << " - removing from list" << endl; + else + { + cerr << "Unable to open " << ( char * ) src + << " - removing from list" << endl; xmlUnlinkNode( node ); xmlFreeNode( node ); + *freed = true; } - } - } + } + } xmlFree( src ); - xmlFree(clipEnd); - xmlFree(clipBegin); - } - return false; + xmlFree( clipEnd ); + xmlFree( clipBegin ); + } + return false; } @@ -486,129 +440,141 @@ If the scene has been found in the playlist, the file name and first frame number are returned in the private data. */ -static bool findSceneStart(xmlNodePtr node, void *p) +static bool findSceneStart( xmlNodePtr node, void *p, bool *freed ) { - int fileCount = 0; - MovieInfo *data = (MovieInfo*)p; + int fileCount = 0; + MovieInfo *data = ( MovieInfo* ) p; int begin = data->absBegin; - // if this is a <seq> node process all of its <video> child nodes - - if (xmlStrcmp(node->name, (const xmlChar*)"seq") == 0) { + // if this is a <seq> node process all of its <video> child nodes - data->sequence = node; + if ( xmlStrcmp( node->name, ( const xmlChar* ) "seq" ) == 0 ) + { - node = node->xmlChildrenNode; - while (node != NULL) { - if (xmlStrcmp(node->name, (const xmlChar*)"video") == 0) { + data->sequence = node; - data->video = node; + node = node->children; + while ( node != NULL ) + { + if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 ) + { - xmlChar *src = xmlGetProp(node, (const xmlChar*)"src"); - xmlChar *clipBegin = xmlGetProp(node, (const xmlChar*)"clipBegin"); - xmlChar *clipEnd = xmlGetProp(node, (const xmlChar*)"clipEnd"); + data->video = node; - if ((src != NULL) && (clipBegin != NULL) && (clipEnd != NULL)) { + xmlChar *src = xmlGetProp( node, ( const xmlChar* ) "src" ); + xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" ); + xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" ); - data->clipBegin = atoi((const char*)clipBegin); - data->clipEnd = atoi((const char*)clipEnd); + if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) ) + { - // if this is the first file remember its name and start + data->clipBegin = atoi( ( const char* ) clipBegin ); + data->clipEnd = atoi( ( const char* ) clipEnd ); - if (fileCount == 0) { - data->clipFrame = data->clipBegin; - strcpy( data->fileName, (char *)src ); - } + // if this is the first file remember its name and start - // if absFrame is within current scene we are done. - // fine name and relative frame number have been already found (see above) - // otherwise update absBegin to hold abs frame num of next file + if ( fileCount == 0 ) + { + data->clipFrame = data->clipBegin; + strcpy( data->fileName, ( char * ) src ); + } - if (data->absFrame <= begin + data->clipEnd - data->clipBegin) { - xmlFree(clipBegin); - xmlFree(clipEnd); - xmlFree(src); - return true; + // if absFrame is within current scene we are done. + // fine name and relative frame number have been already found (see above) + // otherwise update absBegin to hold abs frame num of next file + + if ( data->absFrame <= begin + data->clipEnd - data->clipBegin ) + { + xmlFree( clipBegin ); + xmlFree( clipEnd ); + xmlFree( src ); + return true; } - else { - begin += (data->clipEnd - data->clipBegin + 1); + else + { + begin += ( data->clipEnd - data->clipBegin + 1 ); } - fileCount++; - } - if (src) - xmlFree(src); - if (clipEnd) - xmlFree(clipEnd); - if (clipBegin) - xmlFree(clipBegin); - } - node = node->next; - } - } + fileCount++; + } + if ( src ) + xmlFree( src ); + if ( clipEnd ) + xmlFree( clipEnd ); + if ( clipBegin ) + xmlFree( clipBegin ); + } + node = node->next; + } + } data->absBegin = begin; - data->clipFrame = 0; + data->clipFrame = 0; strcpy( data->fileName, "" ); - return false; + return false; } -static bool findSceneEnd(xmlNodePtr node, void *p) +static bool findSceneEnd( xmlNodePtr node, void *p, bool *freed ) { - bool found = false; - xmlChar *src = NULL; - MovieInfo *data = (MovieInfo*)p; + bool found = false; + xmlChar *src = NULL; + MovieInfo *data = ( MovieInfo* ) p; - // if this is a <seq> node process all of its <video> child nodes + // if this is a <seq> node process all of its <video> child nodes - if (xmlStrcmp(node->name, (const xmlChar*)"seq") == 0) { - - data->sequence = node; + if ( xmlStrcmp( node->name, ( const xmlChar* ) "seq" ) == 0 ) + { - node = node->xmlChildrenNode; - while (node != NULL) { - if (xmlStrcmp(node->name, (const xmlChar*)"video") == 0) { + data->sequence = node; - data->video = node; + node = node->children; + while ( node != NULL ) + { + if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 ) + { - if (src) - xmlFree(src); + data->video = node; - src = xmlGetProp(node, (const xmlChar*)"src"); - xmlChar *clipBegin = xmlGetProp(node, (const xmlChar*)"clipBegin"); - xmlChar *clipEnd = xmlGetProp(node, (const xmlChar*)"clipEnd"); + if ( src ) + xmlFree( src ); - if ((src != NULL) && (clipBegin != NULL) && (clipEnd != NULL)) { + src = xmlGetProp( node, ( const xmlChar* ) "src" ); + xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" ); + xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" ); - data->clipBegin = atoi((const char*)clipBegin); - data->clipEnd = atoi((const char*)clipEnd); - data->clipFrame = data->clipEnd; + if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) ) + { - if (data->absFrame <= data->absBegin + data->clipEnd - data->clipBegin) - found = true; - data->absBegin += (data->clipEnd - data->clipBegin + 1); - } + data->clipBegin = atoi( ( const char* ) clipBegin ); + data->clipEnd = atoi( ( const char* ) clipEnd ); + data->clipFrame = data->clipEnd; + + if ( data->absFrame <= data->absBegin + data->clipEnd - data->clipBegin ) + found = true; + data->absBegin += ( data->clipEnd - data->clipBegin + 1 ); + } - if (clipEnd) - xmlFree(clipEnd); - if (clipBegin) - xmlFree(clipBegin); - } - node = node->next; - } + if ( clipEnd ) + xmlFree( clipEnd ); + if ( clipBegin ) + xmlFree( clipBegin ); + } + node = node->next; + } - if (found) { - strcpy( data->fileName, (char *)src ); + if ( found ) + { + strcpy( data->fileName, ( char * ) src ); xmlFree( src ); - data->absEnd = data->absBegin - 1; - return true; + data->absEnd = data->absBegin - 1; + return true; } - if (src) - xmlFree(src); - } - data->clipFrame = 0; - strcpy( data->fileName, "" ); - return false; + if ( src ) + xmlFree( src ); + } + data->clipFrame = 0; + strcpy( data->fileName, "" ); + return false; } /** Count number of frames in one node @@ -625,33 +591,36 @@ \note This code requires a <video> node format that is not defined in smil. */ -static bool countFrames(xmlNodePtr node, void *p) +static bool countFrames( xmlNodePtr node, void *p, bool *freed ) { - if (xmlStrcmp(node->name, (const xmlChar*)"video") == 0) { + if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 ) + { - xmlChar *src = xmlGetProp(node, (const xmlChar*)"src"); - xmlChar *clipBegin = xmlGetProp(node, (const xmlChar*)"clipBegin"); - xmlChar *clipEnd = xmlGetProp(node, (const xmlChar*)"clipEnd"); - - if ((src != NULL) && (clipBegin != NULL) && (clipEnd != NULL)) - *((int*)p) += atoi((const char*)clipEnd) - atoi((const char*)clipBegin) + 1; - - if (clipEnd) - xmlFree(clipEnd); - if (clipBegin) - xmlFree(clipBegin); - if (src) - xmlFree(src); - } - return false; + xmlChar * src = xmlGetProp( node, ( const xmlChar* ) "src" ); + xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" ); + xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" ); + + if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) ) + * ( ( int* ) p ) += atoi( ( const char* ) clipEnd ) - atoi( ( const char* ) clipBegin ) + 1; + + if ( clipEnd ) + xmlFree( clipEnd ); + if ( clipBegin ) + xmlFree( clipBegin ); + if ( src ) + xmlFree( src ); + } + return false; } /** A type needed for ELI format saving */ -typedef struct { - string file; /* Contains filename */ - string clipBegin; /* Contains begin counts */ - string clipEnd; /* Continas end count */ -} EliInfo; +typedef struct +{ + string file; /* Contains filename */ + string clipBegin; /* Contains begin counts */ + string clipEnd; /* Continas end count */ +} +EliInfo; typedef list<EliInfo> EliInfos; typedef EliInfos::iterator EliInfosIterator; @@ -663,43 +632,217 @@ If this is a <video> node, then add the info to p. p is a ptr to an EliInfos struct. - + The node must have this format: <video src="file.avi" clipBegin="200" clipEnd="800" \> \note This code requires a <video> node format that is not defined in smil. */ -static bool convertEli(xmlNodePtr node, void *p) +static bool convertEli( xmlNodePtr node, void *p, bool *freed ) +{ + if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 ) + { + + xmlChar * src = xmlGetProp( node, ( const xmlChar* ) "src" ); + xmlChar *clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" ); + xmlChar *clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" ); + + if ( ( src != NULL ) && ( clipBegin != NULL ) && ( clipEnd != NULL ) ) + { + /* + cout << "convertEli : " << src << ", " << clipBegin << ", " << clipEnd + << endl; + */ + EliInfos * Eli = ( EliInfos * ) p; + EliInfo tmp; + tmp.file = ( const char* ) src; + tmp.clipBegin = ( const char* ) clipBegin; + tmp.clipEnd = ( const char* ) clipEnd; + Eli->push_back( tmp ); + } + + if ( clipEnd ) + xmlFree( clipEnd ); + if ( clipBegin ) + xmlFree( clipBegin ); + if ( src ) + xmlFree( src ); + } + return false; +} + +class SrtContext +{ +public: + ofstream file; + unsigned counter; + xmlChar* title; + xmlChar* abstract; + xmlChar* src; + unsigned begin; + unsigned duration; + + SrtContext( const char* filename ) : + file(filename), counter(0), title(NULL), abstract(NULL), src(NULL), + begin(0), duration(0) + { + } + + ~SrtContext() + { + printEntry(); + file.close(); + } + + void printEntry(void) + { + if ( title || abstract ) + { + Frame *frame = GetFramePool( )->GetFrame( ); + FileHandler *mediaFile = GetFileMap()->GetMap() [ string( ( const char* ) src ) ]; + SMIL::MediaClippingTime time; + + mediaFile->GetFrame( *frame, 0 ); + time.setFramerate( frame->GetFrameRate() ); + GetFramePool( )->DoneWithFrame( frame ); + string beginString = time.parseFramesToString( begin, SMIL::Time::TIME_FORMAT_CLOCK ); + string durationString = time.parseFramesToString( duration - begin, SMIL::Time::TIME_FORMAT_CLOCK ); + begin = 0; + + file << ++counter << endl; + file << StringUtils::replaceAll( beginString, ".", "," ); + file << " --> " << StringUtils::replaceAll( durationString, ".", "," ) << endl; + if ( title ) + file << title << endl; + if ( abstract ) + file << abstract << endl; + file << endl; + } + if ( src ) + { + xmlFree( src ); + src = NULL; + } + if ( title ) + { + xmlFree( title ); + title = NULL; + } + if ( abstract ) + { + xmlFree( abstract ); + abstract = NULL; + } + } +}; + +static bool convertSrt( xmlNodePtr node, void *data, bool *freed ) +{ + SrtContext* srt = static_cast< SrtContext* >( data ); + + if ( xmlStrcmp( node->name, ( const xmlChar* ) "seq" ) == 0 ) + { + srt->printEntry(); + srt->title = xmlGetProp( node, ( const xmlChar * ) "title" ); + srt->abstract = xmlGetProp( node, ( const xmlChar * ) "abstract" ); + } + else if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 ) + { + xmlChar* clipBegin = xmlGetProp( node, ( const xmlChar* ) "clipBegin" ); + xmlChar* clipEnd = xmlGetProp( node, ( const xmlChar* ) "clipEnd" ); + + srt->src = xmlGetProp( node, ( const xmlChar* ) "src" ); + if ( !srt->begin && ( srt->title || srt->abstract ) ) + srt->begin = srt->duration; + srt->duration += atoi( ( const char* ) clipEnd ) - atoi( ( const char* ) clipBegin ) + 1; + + xmlFree( clipBegin ); + xmlFree( clipEnd ); + } + + return false; +} + +static bool convertFramesToSmilTime( xmlNodePtr node, void *data, bool *freed ) +{ + if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 ) + { + Frame *frame = GetFramePool( )->GetFrame( ); + xmlChar *src = xmlGetProp( node, ( const xmlChar* ) "src" ); + string index( ( char* ) src ); + xmlFree( src ); + FileHandler *mediaFile = GetFileMap()->GetMap() [ index ]; + mediaFile->GetFrame( *frame, 0 ); + SMIL::MediaClippingTime time; + time.setFramerate( frame->GetFrameRate() ); + GetFramePool( )->DoneWithFrame( frame ); + + xmlChar *prop = xmlGetProp( node, ( const xmlChar* ) "clipBegin" ); + if ( prop ) + { + std::string newValue = time.parseFramesToString( atoi( ( const char* ) prop ), SMIL::Time::TIME_FORMAT_CLOCK ); + xmlFree( prop ); + xmlSetProp( node, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) newValue.c_str() ); + } + prop = xmlGetProp( node, ( const xmlChar* ) "clipEnd" ); + if ( prop ) + { + std::string newValue = time.parseFramesToString( atoi( ( const char* ) prop ), SMIL::Time::TIME_FORMAT_CLOCK ); + xmlFree( prop ); + xmlSetProp( node, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) newValue.c_str() ); + } + } + return false; +} + +static bool convertSmilTimeToFrames( xmlNodePtr node, void *data, bool *freed ) { - if (xmlStrcmp(node->name, (const xmlChar*)"video") == 0) { + if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 ) + { + Frame *frame = GetFramePool( )->GetFrame( ); + xmlChar *src = xmlGetProp( node, ( const xmlChar* ) "src" ); + string index( ( char* ) src ); + xmlFree( src ); + FileHandler *mediaFile = GetFileMap()->GetMap() [ index ]; + mediaFile->GetFrame( *frame, 0 ); + SMIL::MediaClippingTime time; + time.setFramerate( frame->GetFrameRate() ); + GetFramePool( )->DoneWithFrame( frame ); - xmlChar *src = xmlGetProp(node, (const xmlChar*)"src"); - xmlChar *clipBegin = xmlGetProp(node, (const xmlChar*)"clipBegin"); - xmlChar *clipEnd = xmlGetProp(node, (const xmlChar*)"clipEnd"); - - if ((src != NULL) && (clipBegin != NULL) && (clipEnd != NULL)) { - /* - cout << "convertEli : " << src << ", " << clipBegin << ", " << clipEnd - << endl; - */ - EliInfos * Eli = (EliInfos *) p; - EliInfo tmp; - tmp.file = (const char*) src; - tmp.clipBegin = (const char*) clipBegin; - tmp.clipEnd = (const char*) clipEnd; - Eli->push_back(tmp); - } - - if (clipEnd) - xmlFree(clipEnd); - if (clipBegin) - xmlFree(clipBegin); - if (src) - xmlFree(src); - } - return false; + xmlChar *prop = xmlGetProp( node, ( const xmlChar* ) "clipBegin" ); + if ( prop ) + { + time.parseValue( ( const char* ) prop ); + xmlFree( prop ); + std::string newValue = time.toString( SMIL::Time::TIME_FORMAT_FRAMES ); + xmlSetProp( node, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) newValue.c_str() ); + } + prop = xmlGetProp( node, ( const xmlChar* ) "clipEnd" ); + if ( prop ) + { + time.parseValue( ( const char* ) prop ); + xmlFree( prop ); + std::string newValue = time.toString( SMIL::Time::TIME_FORMAT_FRAMES ); + xmlSetProp( node, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) newValue.c_str() ); + } + } + return false; } +static bool clone( xmlNodePtr node, void *data, bool *freed ) +{ + xmlNodePtr* parent = (xmlNodePtr*) data; + xmlNodePtr child = xmlNewNode( NULL, node->name ); + xmlAddChild( *parent, child ); + for ( xmlAttr* attr = node->properties; attr; attr = attr->next ) + xmlNewProp( child, attr->name, xmlGetProp( attr->parent, attr->name) ); + if ( node->children ) // next iteration in depth-first traversal will be a child + *parent = child; + else if ( node == node->parent->last ) // last sibling + *parent = (*parent)->parent; + + return false; +} /** Walk the xml tree @@ -715,17 +858,20 @@ If the callback function returns true the xml tree walk is aborted. */ -static bool parse(xmlNodePtr node, callback func, void *p) +static bool parse( xmlNodePtr node, callback func, void *p ) { - bool done = false; + bool done = false; - while (node != NULL && done == false) { - done = (*func)(node, p); - if ( done == false && node->xmlChildrenNode != NULL ) - done = parse(node->xmlChildrenNode, func, p); - node = node->next; - } - return done; + while ( node != NULL && done == false ) + { + bool freed = false; + xmlNodePtr next = node->next; + done = ( *func ) ( node, p, &freed ); + if ( !done && !freed && node->children ) + done = parse( node->children, func, p ); + node = next; + } + return done; } @@ -733,24 +879,31 @@ PlayList::PlayList() : dirty( false ), doc_name( "" ), count( 0 ) { - xmlNsPtr ns; - xmlNodePtr root; + xmlNsPtr ns; + xmlNodePtr root; - // cerr << "*PlayList::PlayList()" << endl; + // cerr << "*PlayList::PlayList()" << endl; - doc = xmlNewDoc((const xmlChar*)"1.0"); - root = xmlNewNode( NULL, (const xmlChar*)"smil"); - ns = xmlNewNs(root,(const xmlChar*)"http://www.w3.org/2001/SMIL20/Language", (const xmlChar*)"smil2"); - xmlDocSetRootElement(doc, root); + doc = xmlNewDoc( ( const xmlChar* ) "1.0" ); + root = xmlNewNode( NULL, ( const xmlChar* ) "smil" ); + ns = xmlNewNs( root, SMIL20_NAMESPACE_HREF, ( const xmlChar* ) NULL ); + xmlDocSetRootElement( doc, root ); + xmlAddChild( root, xmlNewNode( NULL, ( const xmlChar* ) "body" ) ); } /** Copy Constructor */ -PlayList::PlayList(const PlayList& playList) +PlayList::PlayList( const PlayList& playList ) { - // cerr << "*PlayList::PlayList(const PlayList& playList)" << endl; - doc = xmlCopyDoc(playList.doc, true); + xmlNsPtr ns; + xmlNodePtr root; + + doc = xmlNewDoc( ( const xmlChar* ) "1.0" ); + root = xmlNewNode( NULL, ( const xmlChar* ) "smil" ); + ns = xmlNewNs( root, ( const xmlChar* ) SMIL20_NAMESPACE_HREF, ( const xmlChar* ) NULL ); + xmlDocSetRootElement( doc, root ); + parse( playList.GetBody(), clone, &root ); dirty = playList.dirty; doc_name = playList.GetDocName( ); RefreshCount( ); @@ -759,18 +912,26 @@ /** Assignment Operator */ -PlayList& PlayList::operator=(const PlayList& playList) +PlayList& PlayList::operator=( const PlayList& playList ) { - // cerr << "*PlayList::operator=(const PlayList& playList)" << endl; + // cerr << "*PlayList::operator=(const PlayList& playList)" << endl; - if (doc != playList.doc) { - xmlFreeDoc(doc); - doc = xmlCopyDoc(playList.doc, true); + if ( doc != playList.doc ) + { + xmlNsPtr ns; + xmlNodePtr root; + + xmlFreeDoc( doc ); + doc = xmlNewDoc( ( const xmlChar* ) "1.0" ); + root = xmlNewNode( NULL, ( const xmlChar* ) "smil" ); + ns = xmlNewNs( root, ( const xmlChar* ) SMIL20_NAMESPACE_HREF, ( const xmlChar* ) NULL ); + xmlDocSetRootElement( doc, root ); + parse( playList.GetBody(), clone, &root ); dirty = playList.dirty; doc_name = playList.GetDocName( ); RefreshCount( ); - } - return *this; + } + return *this; } @@ -779,18 +940,29 @@ PlayList::~PlayList() { - // cerr << "*PlayList::~PlayList()" << endl; - if (doc != NULL) { - xmlFreeDoc(doc); - doc = NULL; - } + // cerr << "*PlayList::~PlayList()" << endl; + if ( doc != NULL ) + { + xmlFreeDoc( doc ); + doc = NULL; + } +} + +/** Get the SMIL body element + + \return a node pointer to the body element */ + +xmlNodePtr PlayList::GetBody( ) const +{ + return xmlDocGetRootElement( doc )->children; } + void PlayList::RefreshCount( ) { count = 0; - if (doc != NULL) - parse(xmlDocGetRootElement(doc), countFrames, &count); + if ( doc != NULL ) + parse( GetBody(), countFrames, &count ); } /** Counts the number of frames in the playlist @@ -799,22 +971,22 @@ int PlayList::GetNumFrames() const { - return count; + return count; } -char* PlayList::GetFileNameOfFrame(int frameNum) const +char* PlayList::GetFileNameOfFrame( int frameNum ) const { - // cerr << "char* PlayList::GetFileNameOfFrame(int frameNum)" << endl; - MovieInfo data; + // cerr << "char* PlayList::GetFileNameOfFrame(int frameNum)" << endl; + MovieInfo data; - memset(&data, 0, sizeof(MovieInfo)); - data.absBegin = 0; - data.absEnd = 0; - data.absFrame = frameNum; + memset( &data, 0, sizeof( MovieInfo ) ); + data.absBegin = 0; + data.absEnd = 0; + data.absFrame = frameNum; - parse(xmlDocGetRootElement(doc), findFile, &data); - return data.fileName; + parse( GetBody(), findFile, &data ); + return strdup( data.fileName ); } @@ -827,32 +999,31 @@ \return true if a frame could be copied, false otherwise */ -bool PlayList::GetFrame(int frameNum, Frame &frame) +bool PlayList::GetFrame( int frameNum, Frame &frame ) { - MovieInfo data; + MovieInfo data; - // cerr << "bool PlayList::GetFrame(" << frameNum << ", Frame &frame)" << endl; + // cerr << "bool PlayList::GetFrame(" << frameNum << ", Frame &frame)" << endl; - memset(&data, 0, sizeof(MovieInfo)); - data.absBegin = 0; - data.absEnd = 0; - data.absFrame = frameNum; + memset( &data, 0, sizeof( MovieInfo ) ); + data.absBegin = 0; + data.absEnd = 0; + data.absFrame = frameNum; - parse(xmlDocGetRootElement(doc), findFile, &data); + parse( GetBody(), findFile, &data ); - if ( strcmp( data.fileName, "" ) ) { + if ( strcmp( data.fileName, "" ) ) + { // NB: In our playlist, we enforce an internal absolute path - there is no need // to convert the file name here - string index( (char *)data.fileName ); - FileHandler *mediaFile = GetFileMap()->GetMap()[ index ]; - if (data.clipFrame >= mediaFile->GetTotalFrames()) - data.clipFrame = mediaFile->GetTotalFrames() -1; - mediaFile->GetFrame(frame, data.clipFrame); - frame.playlist_position = frameNum; - return true; - } + string index( ( char * ) data.fileName ); + FileHandler *mediaFile = GetFileMap() ->GetMap() [ index ]; + if ( data.clipFrame >= mediaFile->GetTotalFrames() ) + data.clipFrame = mediaFile->GetTotalFrames() - 1; + return ( mediaFile->GetFrame( frame, data.clipFrame ) >= 0 ); + } - return false; + return false; } @@ -862,299 +1033,359 @@ \param media a pointer to a FileHandler to be set \return true if an FileHandler could be copied, false otherwise */ -bool PlayList::GetMediaObject(int frameNum, FileHandler **media) +bool PlayList::GetMediaObject( int frameNum, FileHandler **media ) { - MovieInfo data; + MovieInfo data; - memset(&data, 0, sizeof(MovieInfo)); - data.absBegin = 0; - data.absEnd = 0; - data.absFrame = frameNum; + memset( &data, 0, sizeof( MovieInfo ) ); + data.absBegin = 0; + data.absEnd = 0; + data.absFrame = frameNum; - parse(xmlDocGetRootElement(doc), findFile, &data); + parse( GetBody(), findFile, &data ); - if ( strcmp( data.fileName, "" ) ) { + if ( strcmp( data.fileName, "" ) ) + { // Again, the absolute path is ensure internally - no need to convert - string index( (char *)data.fileName ); - *media = GetFileMap()->GetMap()[ index ]; - return true; - } + string index( ( char * ) data.fileName ); + *media = GetFileMap() ->GetMap() [ index ]; + return true; + } - return false; + return false; } -int PlayList::GetClipBegin(int frameNum) const +int PlayList::GetClipBegin( int frameNum ) const { - MovieInfo data; + MovieInfo data; - memset(&data, 0, sizeof(MovieInfo)); - data.absBegin = 0; - data.absEnd = 0; - data.absFrame = frameNum; + memset( &data, 0, sizeof( MovieInfo ) ); + data.absBegin = 0; + data.absEnd = 0; + data.absFrame = frameNum; - if (parse(xmlDocGetRootElement(doc), findSceneStart, &data)) - return data.clipBegin; - else - return 0; + if ( parse( GetBody(), findSceneStart, &data ) ) + return data.clipBegin; + else + return 0; } -int PlayList::GetClipEnd(int frameNum) const +int PlayList::GetClipEnd( int frameNum ) const { - MovieInfo data; + MovieInfo data; - memset(&data, 0, sizeof(MovieInfo)); - data.absBegin = 0; - data.absEnd = 0; - data.absFrame = frameNum; + memset( &data, 0, sizeof( MovieInfo ) ); + data.absBegin = 0; + data.absEnd = 0; + data.absFrame = frameNum; - if (parse(xmlDocGetRootElement(doc), findSceneEnd, &data)) - return data.clipEnd; - else - return 0; + if ( parse( GetBody(), findSceneEnd, &data ) ) + return data.clipEnd; + else + return 0; } -bool PlayList::SetClipBegin(int frameNum, const char* value) +bool PlayList::SetClipBegin( int frameNum, const char* value ) { - MovieInfo data; + MovieInfo data; - memset(&data, 0, sizeof(MovieInfo)); - data.absBegin = 0; - data.absEnd = 0; - data.absFrame = frameNum; + memset( &data, 0, sizeof( MovieInfo ) ); + data.absBegin = 0; + data.absEnd = 0; + data.absFrame = frameNum; - if (parse(xmlDocGetRootElement(doc), findSceneStart, &data)) { - xmlSetProp(data.video, (const xmlChar *) "clipBegin", (const xmlChar *) value); + if ( parse( GetBody(), findSceneStart, &data ) ) + { + xmlSetProp( data.video, ( const xmlChar * ) "clipBegin", ( const xmlChar * ) value ); RefreshCount( ); return true; - } else - return false; + } + else + return false; } -bool PlayList::SetClipEnd(int frameNum, const char* value) +bool PlayList::SetClipEnd( int frameNum, const char* value ) { - MovieInfo data; + MovieInfo data; - memset(&data, 0, sizeof(MovieInfo)); - data.absBegin = 0; - data.absEnd = 0; - data.absFrame = frameNum; + memset( &data, 0, sizeof( MovieInfo ) ); + data.absBegin = 0; + data.absEnd = 0; + data.absFrame = frameNum; - if (parse(xmlDocGetRootElement(doc), findSceneEnd, &data)) { - xmlSetProp(data.video, (const xmlChar *) "clipEnd", (const xmlChar *) value); + if ( parse( GetBody(), findSceneEnd, &data ) ) + { + xmlSetProp( data.video, ( const xmlChar * ) "clipEnd", ( const xmlChar * ) value ); RefreshCount( ); return true; - } else - return false; + } + else + return false; } -int PlayList::FindStartOfScene(int frameNum) const +int PlayList::FindStartOfScene( int frameNum ) const { - MovieInfo data; + MovieInfo data; - // cerr << "int PlayList::FindStartOfScene(int frameNum)" << endl; + // cerr << "int PlayList::FindStartOfScene(int frameNum)" << endl; - memset(&data, 0, sizeof(MovieInfo)); - data.absBegin = 0; - data.absEnd = 0; - data.absFrame = frameNum; + memset( &data, 0, sizeof( MovieInfo ) ); + data.absBegin = 0; + data.absEnd = 0; + data.absFrame = frameNum; - parse(xmlDocGetRootElement(doc), findSceneStart, &data); + parse( GetBody(), findSceneStart, &data ); - if ( strcmp( data.fileName, "" ) ) - return data.absBegin; - else - return 0; + if ( strcmp( data.fileName, "" ) ) + return data.absBegin; + else + return 0; } -int PlayList::FindEndOfScene(int frameNum) const +int PlayList::FindEndOfScene( int frameNum ) const { - MovieInfo data; + MovieInfo data; - // cerr << "int PlayList::FindEndOfScene(int frameNum)" << endl; + // cerr << "int PlayList::FindEndOfScene(int frameNum)" << endl; - data.absBegin = 0; - data.absEnd = 0; - data.absFrame = frameNum; + data.absBegin = 0; + data.absEnd = 0; + data.absFrame = frameNum; - parse(xmlDocGetRootElement(doc), findSceneEnd, &data); + parse( GetBody(), findSceneEnd, &data ); - if ( strcmp( data.fileName, "" ) ) - return data.absEnd; - else - return 999999; + if ( strcmp( data.fileName, "" ) ) + return data.absEnd; + else + return 999999; } -void PlayList::AutoSplit(int start, int end) +void PlayList::AutoSplit( int start, int end ) { - MovieInfo firstFile; - MovieInfo lastFile; - Frame *frame = GetFramePool( )->GetFrame( ); - struct tm recDate; - time_t startTime; - time_t endTime; + MovieInfo firstFile; + MovieInfo lastFile; + Frame *frame = GetFramePool( ) ->GetFrame( ); + struct tm recDate; + time_t startTime; + time_t endTime; - // cerr << "PlayList::AutoSplit(int start=" << start << ", int end=" << end << ")" ; - // cerr << endl; + // cerr << "PlayList::AutoSplit(int start=" << start << ", int end=" << end << ")" ; + // cerr << endl; - memset(&firstFile, 0, sizeof(MovieInfo)); - firstFile.absBegin = 0; - firstFile.absEnd = 0; - firstFile.absFrame = start; + memset( &firstFile, 0, sizeof( MovieInfo ) ); + firstFile.absBegin = 0; + firstFile.absEnd = 0; + firstFile.absFrame = start; - parse(xmlDocGetRootElement(doc), findFile, &firstFile); - string index1( (char*)firstFile.fileName ); - FileHandler *mediaFile1 = GetFileMap()->GetMap()[ index1 ]; - mediaFile1->GetFrame(*frame, firstFile.clipFrame); - frame->GetRecordingDate(recDate); - startTime = mktime(&recDate); + parse( GetBody(), findFile, &firstFile ); + string index1( ( char* ) firstFile.fileName ); + FileHandler *mediaFile1 = GetFileMap() ->GetMap() [ index1 ]; + mediaFile1->GetFrame( *frame, firstFile.clipFrame ); + frame->GetRecordingDate( recDate ); + startTime = mktime( &recDate ); - memset(&lastFile, 0, sizeof(MovieInfo)); - lastFile.absBegin = 0; - lastFile.absEnd = 0; - lastFile.absFrame = end; + memset( &lastFile, 0, sizeof( MovieInfo ) ); + lastFile.absBegin = 0; + lastFile.absEnd = 0; + lastFile.absFrame = end; - parse(xmlDocGetRootElement(doc), findFile, &lastFile); + parse( GetBody(), findFile, &lastFile ); - string index2( (char*)lastFile.fileName ); - FileHandler *mediaFile2 = GetFileMap()->GetMap()[ index2 ]; - mediaFile2->GetFrame(*frame, lastFile.clipFrame); - frame->GetRecordingDate(recDate); - endTime = mktime(&recDate); + string index2( ( char* ) lastFile.fileName ); + FileHandler *mediaFile2 = GetFileMap() ->GetMap() [ index2 ]; + mediaFile2->GetFrame( *frame, lastFile.clipFrame ); + frame->GetRecordingDate( recDate ); + endTime = mktime( &recDate ); - int fps = frame->IsPAL() ? 25 : 30; + int fps = frame->IsPAL() ? 25 : 30; - GetFramePool( )->DoneWithFrame( frame ); + GetFramePool( ) ->DoneWithFrame( frame ); - // bail out on invalid recording date/time - if (startTime < 0 || endTime < 0) return; + // bail out on invalid recording date/time + if ( startTime < 0 || endTime < 0 ) + return ; - if (((difftime(endTime, startTime) * fps) - (end - start)) > fps - || difftime(endTime, startTime) < 0.0 ) { - if ((end - start) > 1) { - // cerr << " ! " << endl; - AutoSplit (start, start + (end - start) / 2); - AutoSplit (start + (end - start) / 2, end); - } else { - // cerr << endl << " *** split before " << end << endl; - SplitSceneBefore(end); - } - } else { - // cerr << " ok" << endl; - } + AutoSplit ( start, startTime, end, endTime, fps ); } -bool PlayList::SplitSceneBefore(int frameNum) +void PlayList::AutoSplit( int start, time_t startTime, int end, time_t endTime, int fps ) { - MovieInfo data; + time_t diffTime = static_cast<time_t>( difftime( endTime, startTime ) ); + if ( ( ( diffTime * fps ) - ( end - start ) ) > fps || diffTime < 0.0 ) + { + if ( ( end - start ) > 1 ) + { + time_t mid = start + ( end - start ) / 2; + time_t midTime; + // reduce the scope of temporary varables here to reduce memory usage when recurring: + { + MovieInfo midFile; + struct tm recDate; + Frame *frame = GetFramePool( ) ->GetFrame( ); + + memset( &midFile, 0, sizeof( MovieInfo ) ); + midFile.absFrame = mid; + + parse( GetBody(), findFile, &midFile ); + + string index( ( char* ) midFile.fileName ); + FileHandler *mediaFile = GetFileMap() ->GetMap() [ index ]; + mediaFile->GetFrame( *frame, midFile.clipFrame ); + frame->GetRecordingDate( recDate ); + midTime = mktime( &recDate ); + + GetFramePool( ) ->DoneWithFrame( frame ); + } - // cerr << "PlayList::SplitSceneBefore(int frameNum=" << frameNum << ")" << endl; + // bail out on invalid recording date/time + if ( midTime < 0 ) + return ; + + AutoSplit ( start, startTime, mid, midTime, fps ); + AutoSplit ( mid, midTime, end, endTime, fps ); + } + else + { + SplitSceneBefore( end ); + } + } +} - if ( GetNumFrames() == 0 ) - return false; - memset(&data, 0, sizeof(MovieInfo)); - data.absBegin = 0; - data.absEnd = 0; - data.absFrame = frameNum; - parse(xmlDocGetRootElement(doc), findSceneStart, &data); - int begin = data.absBegin; - - memset(&data, 0, sizeof(MovieInfo)); - data.absBegin = 0; - data.absEnd = 0; - data.absFrame = frameNum; - parse(xmlDocGetRootElement(doc), findSceneEnd, &data); - int end = data.absEnd; +bool PlayList::SplitSceneBefore( int frameNum ) +{ + MovieInfo data; - if ( strcmp( data.fileName, "" ) && begin != frameNum ) { + // cerr << "PlayList::SplitSceneBefore(int frameNum=" << frameNum << ")" << endl; - dirty = true; + if ( GetNumFrames() == 0 ) + return false; - // duplicate the whole scene. + memset( &data, 0, sizeof( MovieInfo ) ); + data.absBegin = 0; + data.absEnd = 0; + data.absFrame = frameNum; + parse( GetBody(), findSceneStart, &data ); + int begin = data.absBegin; + + memset( &data, 0, sizeof( MovieInfo ) ); + data.absBegin = 0; + data.absEnd = 0; + data.absFrame = frameNum; + parse( GetBody(), findSceneEnd, &data ); + int end = data.absEnd; - xmlNode *firstSequence = data.sequence; - xmlNode *secondSequence = xmlCopyNodeList(firstSequence); - xmlAddNextSibling(firstSequence, secondSequence); + if ( strcmp( data.fileName, "" ) && begin != frameNum ) + { - // in the first sequence, delete from frameNum to end of scene + dirty = true; - Delete(frameNum, end); + // Copy the right hand side of the current scene + xmlNode *firstSequence = data.sequence; + PlayList playlist; + GetPlayList( frameNum, end, playlist ); - // in the second sequence, delete from start of scene to frameNum - 1 + // Paste it after the current scene + xmlAddNextSibling( firstSequence, playlist.GetBody()->children ); - Delete(frameNum, frameNum - begin + frameNum - 1); + // in the first sequence, delete from frameNum to end of scene + Delete( frameNum, end ); return true; - } - else { + } + else + { return false; } } -bool PlayList::JoinScenesAt(int frameNum) +bool PlayList::JoinScenesAt( int frameNum ) { - MovieInfo scene1; - MovieInfo scene2; - MovieInfo scene2end; + MovieInfo scene1; + MovieInfo scene2; + MovieInfo scene2end; if ( GetNumFrames() == 0 ) return false; - // cerr << "PlayList::JoinScenesAt(int frameNum=" << frameNum << ")" << endl; + // cerr << "PlayList::JoinScenesAt(int frameNum=" << frameNum << ")" << endl; - memset(&scene1, 0, sizeof(MovieInfo)); - scene1.absBegin = 0; - scene1.absEnd = 0; - scene1.absFrame = frameNum; - parse(xmlDocGetRootElement(doc), findSceneStart, &scene1); - - memset(&scene2, 0, sizeof(MovieInfo)); - scene2.absBegin = 0; - scene2.absEnd = 0; - scene2.absFrame = frameNum; - parse(xmlDocGetRootElement(doc), findSceneEnd, &scene2); - int end = scene2.absEnd + 1; - - memset(&scene2end, 0, sizeof(MovieInfo)); - scene2end.absBegin = 0; - scene2end.absEnd = 0; - scene2end.absFrame = end; - parse(xmlDocGetRootElement(doc), findSceneEnd, &scene2end); + memset( &scene1, 0, sizeof( MovieInfo ) ); + scene1.absBegin = 0; + scene1.absEnd = 0; + scene1.absFrame = frameNum; + parse( GetBody(), findSceneStart, &scene1 ); + + memset( &scene2, 0, sizeof( MovieInfo ) ); + scene2.absBegin = 0; + scene2.absEnd = 0; + scene2.absFrame = frameNum; + parse( GetBody(), findSceneEnd, &scene2 ); + int end = scene2.absEnd + 1; + + memset( &scene2end, 0, sizeof( MovieInfo ) ); + scene2end.absBegin = 0; + scene2end.absEnd = 0; + scene2end.absFrame = end; + parse( GetBody(), findSceneEnd, &scene2end ); - if ( scene1.sequence != scene2end.sequence ) { + if ( scene1.sequence != scene2end.sequence ) + { dirty = true; // cerr << ">>>> Joining scene at " << scene1.absBegin << " with scene at " - // << scene2.absBegin << " which ends at " << scene2end.absEnd << endl; + // << scene2.absBegin << " which ends at " << scene2end.absEnd << endl; - // concatenate the contents of scene2 into scene1 - xmlNode *lastchild = xmlGetLastChild( scene1.sequence ); - xmlNode *secondSequence = xmlCopyNodeList( scene2end.sequence->children ); - - // Delete scene2 - Delete(scene2.absBegin, scene2end.absEnd ); - - while ( secondSequence != NULL ) { - xmlNode *backup = secondSequence->next; - lastchild = xmlAddNextSibling(lastchild, secondSequence); - secondSequence = backup; + // concatenate the contents of scene2 into scene1 + xmlNode *lastchild = xmlGetLastChild( scene1.sequence ); + xmlNodePtr next = NULL; + for ( xmlNodePtr ptr = scene2end.sequence->children; ptr != NULL; ptr = next ) + { + next = ptr->next; + lastchild = xmlAddNextSibling( lastchild, ptr ); + ptr = next; } - - xmlFreeNode( secondSequence ); + // Merge the metadata properties + for ( xmlAttr* attr = scene2end.sequence->properties; attr; attr = attr->next ) + { + const char *valueB = ( const char* ) xmlGetProp( attr->parent, ( const xmlChar * ) attr->name ); + // Only if the value is meaningful + if ( valueB && strcmp( valueB, "" ) ) + { + // See if the property exists on clip A + const char *valueA = ( const char* ) xmlGetProp( scene1.sequence, ( const xmlChar * ) attr->name ); + if ( valueA && strcmp( valueA, "" ) ) + { + xmlChar* value = new xmlChar[ strlen(valueA) + strlen(valueB) + 1 ]; + strcpy( (char*) value, (const char*) valueA ); + strcat( (char*) value, " " ); + strcat( (char*) value, (const char*) valueB ); + xmlSetProp( scene1.sequence, attr->name, ( const xmlChar * ) value ); + delete[] value; + } + else + { + xmlSetProp( scene1.sequence, attr->name, ( const xmlChar * ) valueB ); + } + } + } + xmlUnlinkNode( scene2end.sequence ); + xmlFreeNode( scene2end.sequence ); RefreshCount( ); return true; - } - else { + } + else + { return false; } } @@ -1167,116 +1398,141 @@ \param first number of first frame \param last number of last frame + \param playlist the playlist object to be filled \return true if the frames could be copied, false otherwise */ -bool PlayList::GetPlayList(int first, int last, PlayList &playlist) const +bool PlayList::GetPlayList( int first, int last, PlayList &playlist ) const { - MovieInfo firstFile, lastFile; - bool copyFlag = false; + MovieInfo firstFile, lastFile; + bool copyFlag = false; if ( GetNumFrames() == 0 ) return false; playlist.dirty = false; - // cerr << " bool PlayList::Copy(int first, int last, PlayList &playlist) " << endl; - - memset(&firstFile, 0, sizeof(MovieInfo)); - firstFile.absBegin = 0; - firstFile.absEnd = 0; - firstFile.absFrame = first; + // cerr << " bool PlayList::Copy(int first, int last, PlayList &playlist) " << endl; - parse(xmlDocGetRootElement(doc), findFile, &firstFile); + memset( &firstFile, 0, sizeof( MovieInfo ) ); + firstFile.absBegin = 0; + firstFile.absEnd = 0; + firstFile.absFrame = first; - memset(&lastFile, 0, sizeof(MovieInfo)); - lastFile.absBegin = 0; - lastFile.absEnd = 0; - lastFile.absFrame = last; + parse( GetBody(), findFile, &firstFile ); - parse(xmlDocGetRootElement(doc), findFile, &lastFile); + memset( &lastFile, 0, sizeof( MovieInfo ) ); + lastFile.absBegin = 0; + lastFile.absEnd = 0; + lastFile.absFrame = last; - if ( strcmp( firstFile.fileName, "" ) && strcmp(lastFile.fileName, "" )) { + parse( GetBody(), findFile, &lastFile ); - xmlNodePtr srcNode = xmlDocGetRootElement(doc); - xmlNodePtr dstNode = xmlDocGetRootElement(playlist.doc); - - for (xmlNodePtr srcSeq = srcNode->xmlChildrenNode; srcSeq != NULL; srcSeq = srcSeq->next) { - if (xmlStrcmp(srcSeq->name, (const xmlChar*)"seq") == 0) { - xmlNodePtr seq = xmlNewNode(NULL, (const xmlChar*)"seq"); - xmlAddChild(dstNode, seq); - for (xmlNodePtr srcVideo = srcSeq->xmlChildrenNode; srcVideo != NULL; srcVideo = srcVideo->next) { - if (xmlStrcmp(srcVideo->name, (const xmlChar*)"video") == 0) { - - // case 1: selection contains more than one file. This one is neither the first nor the last. + if ( strcmp( firstFile.fileName, "" ) && strcmp( lastFile.fileName, "" ) ) + { - if (copyFlag && srcVideo != firstFile.video && srcVideo != lastFile.video) { - xmlAddChild(seq, xmlCopyNode(srcVideo,1)); - } + xmlNodePtr srcNode = GetBody(); + xmlNodePtr dstNode = playlist.GetBody(); + xmlNodePtr nextSeq = NULL; - // case 2: selection contains more than one file and this is the first file + for ( xmlNodePtr srcSeq = srcNode->children; srcSeq != NULL; srcSeq = nextSeq ) + { + nextSeq = srcSeq->next; + if ( xmlStrcmp( srcSeq->name, ( const xmlChar* ) "seq" ) == 0 ) + { + xmlNodePtr seq = xmlNewNode( NULL, ( const xmlChar* ) "seq" ); + xmlAddChild( dstNode, seq ); + xmlNodePtr nextVideo = NULL; - else if (srcVideo == firstFile.video && srcVideo != lastFile.video) { + for ( xmlNodePtr srcVideo = srcSeq->children; srcVideo != NULL; srcVideo = nextVideo ) + { + nextVideo = srcVideo->next; + if ( xmlStrcmp( srcVideo->name, ( const xmlChar* ) "video" ) == 0 ) + { + + // case 1: selection contains more than one file. This one is neither the first nor the last. + + if ( copyFlag && srcVideo != firstFile.video && srcVideo != lastFile.video ) + { + xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" ); + xmlAddChild( seq, video ); + for ( xmlAttr* attr = srcVideo->properties; attr; attr = attr->next ) + xmlNewProp( video, attr->name, xmlGetProp( attr->parent, attr->name) ); + } + + // case 2: selection contains more than one file and this is the first file + + else if ( srcVideo == firstFile.video && srcVideo != lastFile.video ) + { + + ostringstream sb1, sb2; + + xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" ); + xmlNewProp( video, ( const xmlChar* ) "src", ( const xmlChar* ) firstFile.fileName ); + sb1 << firstFile.clipFrame << ends; + xmlNewProp( video, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb1.str().c_str() ); + sb2 << firstFile.clipEnd << ends; + xmlNewProp( video, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb2.str().c_str() ); + xmlAddChild( seq, video ); + copyFlag = true; + } + + // case 3: selection contains more than one file and this is the last file + + else if ( srcVideo != firstFile.video && srcVideo == lastFile.video ) + { + + ostringstream sb1, sb2; + + xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" ); + xmlNewProp( video, ( const xmlChar* ) "src", ( const xmlChar* ) lastFile.fileName ); + sb1 << lastFile.clipBegin << ends; + xmlNewProp( video, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb1.str().c_str() ); + sb2 << lastFile.clipFrame << ends; + xmlNewProp( video, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb2.str().c_str() ); + xmlAddChild( seq, video ); + copyFlag = false; + } + + // case 4: selection contains exactly one file + + else if ( srcVideo == firstFile.video && srcVideo == lastFile.video ) + { + + ostringstream sb1, sb2; + + xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" ); + xmlNewProp( video, ( const xmlChar* ) "src", ( const xmlChar* ) firstFile.fileName ); + sb1 << firstFile.clipFrame << ends; + xmlNewProp( video, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb1.str().c_str() ); + sb2 << lastFile.clipFrame << ends; + xmlNewProp( video, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb2.str().c_str() ); + xmlAddChild( seq, video ); + } + } + } - strstream sb1, sb2; + // if this sequence does not have any video clips, remove it - xmlNodePtr video = xmlNewNode(NULL, (const xmlChar*)"video"); - xmlNewProp(video, (const xmlChar*)"src", (const xmlChar*)firstFile.fileName); - sb1 << firstFile.clipFrame << ends; - xmlNewProp(video, (const xmlChar*)"clipBegin", (const xmlChar*)sb1.str()); - sb2 << firstFile.clipEnd << ends; - xmlNewProp(video, (const xmlChar*)"clipEnd", (const xmlChar*)sb2.str()); - xmlAddChild(seq, video); - copyFlag = true; - } - - // case 3: selection contains more than one file and this is the last file - - else if (srcVideo != firstFile.video && srcVideo == lastFile.video) { - - strstream sb1, sb2; - - xmlNodePtr video = xmlNewNode(NULL, (const xmlChar*)"video"); - xmlNewProp(video, (const xmlChar*)"src", (const xmlChar*)lastFile.fileName); - sb1 << lastFile.clipBegin << ends; - xmlNewProp(video, (const xmlChar*)"clipBegin", (const xmlChar*)sb1.str()); - sb2 << lastFile.clipFrame << ends; - xmlNewProp(video, (const xmlChar*)"clipEnd", (const xmlChar*)sb2.str()); - xmlAddChild(seq, video); - copyFlag = false; - } - - // case 4: selection contains exactly one file - - else if (srcVideo == firstFile.video && srcVideo == lastFile.video) { - - strstream sb1, sb2; - - xmlNodePtr video = xmlNewNode(NULL, (const xmlChar*)"video"); - xmlNewProp(video, (const xmlChar*)"src", (const xmlChar*)firstFile.fileName); - sb1 << firstFile.clipFrame << ends; - xmlNewProp(video, (const xmlChar*)"clipBegin", (const xmlChar*)sb1.str()); - sb2 << lastFile.clipFrame << ends; - xmlNewProp(video, (const xmlChar*)"clipEnd", (const xmlChar*)sb2.str()); - xmlAddChild(seq, video); - } - } - } - - // if this sequence does not have any video clips, remove it - - if (seq->xmlChildrenNode == NULL) { - xmlUnlinkNode(seq); - xmlFreeNode(seq); - } - } - } + if ( seq->children == NULL ) + { + xmlUnlinkNode( seq ); + xmlFreeNode( seq ); + } + else + // copy the seq attributes + { + for ( xmlAttr* attr = srcSeq->properties; attr; attr = attr->next ) + xmlNewProp( seq, attr->name, xmlGetProp( attr->parent, attr->name) ); + } + } + } // PASS PATH string path = directory_utils::get_directory_from_file( GetDocName() ); - parse(xmlDocGetRootElement(playlist.doc), fillMap, &path ); - } + parse( playlist.GetBody(), fillMap, &path ); + } playlist.RefreshCount( ); - return true; + return true; } @@ -1291,34 +1547,33 @@ \param before insert playlist before this frame */ -bool PlayList::InsertPlayList(PlayList &playlist, int before) +bool PlayList::InsertPlayList( PlayList &playlist, int before ) { - // cerr << "bool PlayList::Paste(PlayList &playlist, int before(=" << before << "))" << endl; + // cerr << "bool PlayList::Paste(PlayList &playlist, int before(=" << before << "))" << endl; if ( playlist.GetNumFrames() == 0 ) return false; // PASS PATH string path = directory_utils::get_directory_from_file( GetDocName() ); - parse(xmlDocGetRootElement(playlist.doc), fillMap, &path ); + parse( playlist.GetBody(), fillMap, &path ); - MovieInfo file; + MovieInfo file; - memset(&file, 0, sizeof(MovieInfo)); - file.absBegin = 0; - file.absEnd = 0; - file.absFrame = before; - file.sequence = NULL; - file.video = NULL; + memset( &file, 0, sizeof( MovieInfo ) ); + file.absBegin = 0; + file.absEnd = 0; + file.absFrame = before; + file.sequence = NULL; + file.video = NULL; // Fill the map with any new files now, before we change the doc - parse(xmlDocGetRootElement(doc), findFile, &file); - - xmlNodePtr node = xmlDocGetRootElement(playlist.doc); + parse( GetBody(), findFile, &file ); - bool first = true; - xmlNodePtr next = NULL; - xmlNodePtr sequence = file.sequence; + xmlNodePtr node = playlist.GetBody(); + bool first = true; + xmlNodePtr next = NULL; + xmlNodePtr sequence = file.sequence; if ( GetNumFrames() > 0 ) { @@ -1333,342 +1588,425 @@ } - for (xmlNodePtr ptr = node->children; ptr != NULL; ptr = next) { + for ( xmlNodePtr ptr = node->children; ptr != NULL; ptr = next ) + { + + //cerr << endl << "Sibling" << endl; + //xmlElemDump(stderr, NULL, ptr); + //cerr << endl; + + // Get the next sibling before adding + next = ptr->next; + + // If first and at start of scene insert, otherwise append + // cerr << "Scene i'm pasting into starts at " << file.absBegin << " [" << file.absEnd << "]" << endl; + + if ( first && sequence == NULL ) + { + // This strange approach avoids using xmlCopyNode, which adds extra namespace declarations + xmlNodePtr tmp = xmlNewNode( NULL, ( const xmlChar* ) "seq" ); + xmlAddChild( GetBody(), tmp ); + sequence = xmlAddNextSibling( tmp, ptr ); + xmlUnlinkNode( tmp ); + xmlFreeNode( tmp ); + } + else if ( first && before == file.absBegin && before != ( file.absEnd + 1 ) ) + { + // cerr << "Inserting before " << before << endl; + sequence = xmlAddPrevSibling( sequence, ptr ); + } + else if ( first && before != ( file.absEnd + 1 ) ) + { + // cerr << "Splitting scene that start at " << file.absBegin << " and ends at " << file.absEnd << " at " << before << endl; + // cerr << endl << "Before Split" << endl; + // xmlElemDump(stderr, NULL, sequence); + // cerr << endl; + + // Split the current scene + SplitSceneBefore( before ); + + // Find our new position + memset( &file, 0, sizeof( MovieInfo ) ); + file.absBegin = 0; + file.absFrame = before; + file.sequence = NULL; + file.video = NULL; + + parse( GetBody(), findFile, &file ); + + // cerr << endl << "After Split" << endl; + // xmlElemDump(stderr, NULL, sequence); + // cerr << endl; + + // Add before the scene returned + sequence = xmlAddPrevSibling( file.sequence, ptr ); + } + else + { + // cerr << "Inserting after " << before << endl; + sequence = xmlAddNextSibling( sequence, ptr ); + } - //cerr << endl << "Sibling" << endl; - //xmlElemDump(stderr, NULL, ptr); - //cerr << endl; - - // Get the next sibling before adding - next = ptr->next; - - // If first and at start of scene insert, otherwise append - // cerr << "Scene i'm pasting into starts at " << file.absBegin << " [" << file.absEnd << "]" << endl; - - if (first && sequence == NULL) { - ptr = xmlCopyNode(ptr, -1); - sequence = xmlAddChild(xmlDocGetRootElement(doc), ptr); - } else if (first && before == file.absBegin && before != (file.absEnd + 1)) { - // cerr << "Inserting before " << before << endl; - sequence = xmlAddPrevSibling(sequence, ptr); - } else if (first && before != (file.absEnd + 1)) { - // cerr << "Splitting scene that start at " << file.absBegin << " and ends at " << file.absEnd << " at " << before << endl; - // cerr << endl << "Before Split" << endl; - // xmlElemDump(stderr, NULL, sequence); - // cerr << endl; - - // Split the current scene - SplitSceneBefore(before); - - // Find our new position - memset(&file, 0, sizeof(MovieInfo)); - file.absBegin = 0; - file.absFrame = before; - file.sequence = NULL; - file.video = NULL; - - parse(xmlDocGetRootElement(doc), findFile, &file); - - // cerr << endl << "After Split" << endl; - // xmlElemDump(stderr, NULL, sequence); - // cerr << endl; - - // Add before the scene returned - sequence = xmlAddPrevSibling(file.sequence, ptr); - } else { - // cerr << "Inserting after " << before << endl; - sequence = xmlAddNextSibling(sequence, ptr); - } - - // We're definitely no longer first - first = false; - } + // We're definitely no longer first + first = false; + } RefreshCount( ); - return true; + return true; } -bool PlayList::Delete(int first, int last) +bool PlayList::Delete( int first, int last ) { - int absClipBegin; - int clipBegin; - int clipEnd; - static int firstCall = 0; + int absClipBegin; + int clipBegin; + int clipEnd; + static int firstCall = 0; - // cerr << "bool PlayList::Delete(int first=" << first << ", int last=" << last << ")" << endl; + // cerr << "bool PlayList::Delete(int first=" << first << ", int last=" << last << ")" << endl; - // SplitSceneBefore calls Delete, avoid recursion + // SplitSceneBefore calls Delete, avoid recursion if ( GetNumFrames() == 0 ) return false; - if (firstCall == 0) { - firstCall = 1; - SplitSceneBefore(first); - firstCall = 0; - } - - xmlNodePtr srcNode = xmlDocGetRootElement(doc); - absClipBegin = 0; - xmlNodePtr nextSequence = NULL; - for (xmlNodePtr srcSeq = srcNode->xmlChildrenNode; srcSeq != NULL; srcSeq = nextSequence ) { + if ( firstCall == 0 ) + { + firstCall = 1; + SplitSceneBefore( first ); + firstCall = 0; + } + + xmlNodePtr srcNode = GetBody(); + absClipBegin = 0; + xmlNodePtr nextSequence = NULL; + for ( xmlNodePtr srcSeq = srcNode->children; srcSeq != NULL; srcSeq = nextSequence ) + { dirty = true; // In case we need to delete this node, get the next pointer before starting nextSequence = srcSeq->next; - if (xmlStrcmp(srcSeq->name, (const xmlChar*)"seq") == 0) { - xmlNodePtr nextVideo = NULL; + if ( xmlStrcmp( srcSeq->name, ( const xmlChar* ) "seq" ) == 0 ) + { + xmlNodePtr nextVideo = NULL; - for (xmlNodePtr srcVideo = srcSeq->xmlChildrenNode; srcVideo != NULL; srcVideo = nextVideo ) { + for ( xmlNodePtr srcVideo = srcSeq->children; srcVideo != NULL; srcVideo = nextVideo ) + { // In case we have to delete this node nextVideo = srcVideo->next; - if (xmlStrcmp(srcVideo->name, (const xmlChar*)"video") == 0) { + if ( xmlStrcmp( srcVideo->name, ( const xmlChar* ) "video" ) == 0 ) + { + + ostringstream sb1, sb2; + xmlChar *s; + + sb1 << ( s = xmlGetProp( srcVideo, ( const xmlChar* ) "clipBegin" ) ) << ends; + clipBegin = atoi( sb1.str().c_str() ); + if ( s ) + xmlFree( s ); + s = xmlGetProp( srcVideo, ( const xmlChar* ) "clipEnd" ); + clipEnd = atoi( ( char * ) s ); + sb2 << ( s = xmlGetProp( srcVideo, ( const xmlChar* ) "clipEnd" ) ) << ends; + clipEnd = atoi( sb2.str().c_str() ); + if ( s ) + xmlFree( s ); + + // case 1: selection covers this file completely. Remove this file from playlist. + + if ( first <= absClipBegin && last >= absClipBegin + clipEnd - clipBegin ) + { + xmlUnlinkNode( srcVideo ); + xmlFreeNode( srcVideo ); + // cerr << "case 1 " << endl; + } + + // case 2: selection starts before or at start of file and ends somewhere in the file. + // New start of file is now end of selection + 1 + + else if ( first <= absClipBegin && last >= absClipBegin && last <= absClipBegin + clipEnd - clipBegin ) + { + + ostringstream sb; + + sb << last - absClipBegin + clipBegin + 1 << ends; + xmlSetProp( srcVideo, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb.str().c_str() ); + // cerr << "case 2 " << endl; + } + + // case 3: selection starts somewhere in the file and ends at or after the file + // New end of file is now start of selection - 1 + + else if ( first > absClipBegin && first <= absClipBegin + clipEnd - clipBegin && last >= absClipBegin + clipEnd - clipBegin ) + { + + ostringstream sb; + + sb << first - absClipBegin + clipBegin - 1 << ends; + xmlSetProp( srcVideo, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb.str().c_str() ); + // cerr << "case 3 " << endl; + } - strstream sb1, sb2; - xmlChar *s; + // case 4: selection starts somewhere in the file and ends in the file. + // We must split this node such that end of first file is start of selection - 1 + // and start of second file is end of selection + 1 + + else if ( first > absClipBegin && last < absClipBegin + clipEnd - clipBegin ) + { + + ostringstream sb1, sb2; + xmlChar *s; + + xmlNodePtr video = xmlNewNode( NULL, ( const xmlChar* ) "video" ); + xmlNewProp( video, ( const xmlChar* ) "src", ( s = xmlGetProp( srcVideo, ( const xmlChar* ) "src" ) ) ); + if ( s ) + xmlFree( s ); + sb1 << last - absClipBegin + clipBegin + 1 << ends; + xmlNewProp( video, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) sb1.str().c_str() ); + xmlNewProp( video, ( const xmlChar* ) "clipEnd", ( s = xmlGetProp( srcVideo, ( const xmlChar* ) "clipEnd" ) ) ); + if ( s ) + xmlFree( s ); + xmlAddNextSibling( srcVideo, video ); + sb2 << first - absClipBegin + clipBegin - 1 << ends; + xmlSetProp( srcVideo, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb2.str().c_str() ); + // cerr << "case 4 " << endl; + } + + absClipBegin += clipEnd - clipBegin + 1; + } + } - sb1 << (s = xmlGetProp(srcVideo, (const xmlChar*)"clipBegin")) << ends; - sb1 >> clipBegin; - if (s) - xmlFree(s); - s = xmlGetProp(srcVideo, (const xmlChar*)"clipEnd"); - clipEnd = atoi( (char *)s ); - sb2 << (s = xmlGetProp(srcVideo, (const xmlChar*)"clipEnd")) << ends; - sb2 >> clipEnd; - if (s) - xmlFree(s); - - // case 1: selection covers this file completely. Remove this file from playlist. - - if (first <= absClipBegin && last >= absClipBegin + clipEnd - clipBegin) { - xmlUnlinkNode(srcVideo); - xmlFreeNode(srcVideo); - // cerr << "case 1 " << endl; - } - - // case 2: selection starts before or at start of file and ends somewhere in the file. - // New start of file is now end of selection + 1 - - else if (first <= absClipBegin && last >= absClipBegin && last <= absClipBegin + clipEnd - clipBegin) { - - strstream sb; - - sb << last - absClipBegin + clipBegin + 1 << ends; - xmlSetProp(srcVideo,(const xmlChar*)"clipBegin", (const xmlChar*)sb.str()); - // cerr << "case 2 " << endl; - } - - // case 3: selection starts somewhere in the file and ends at or after the file - // New end of file is now start of selection - 1 - - else if (first > absClipBegin && first <= absClipBegin + clipEnd - clipBegin && last >= absClipBegin + clipEnd - clipBegin) { - - strstream sb; - - sb << first - absClipBegin + clipBegin - 1 << ends; - xmlSetProp(srcVideo,(const xmlChar*)"clipEnd", (const xmlChar*)sb.str()); - // cerr << "case 3 " << endl; - } - - // case 4: selection starts somewhere in the file and ends in the file. - // We must split this node such that end of first file is start of selection - 1 - // and start of second file is end of selection + 1 - - else if (first > absClipBegin && last < absClipBegin + clipEnd - clipBegin) { - - strstream sb1,sb2; - xmlChar *s; - - xmlNodePtr video = xmlNewNode(NULL, (const xmlChar*)"video"); - xmlNewProp(video, (const xmlChar*)"src", (s = xmlGetProp(srcVideo, (const xmlChar*)"src"))); - if (s) - xmlFree(s); - sb1 << last - absClipBegin + clipBegin + 1 << ends; - xmlNewProp(video, (const xmlChar*)"clipBegin", (const xmlChar*)sb1.str()); - xmlNewProp(video, (const xmlChar*)"clipEnd", (s = xmlGetProp(srcVideo, (const xmlChar*)"clipEnd"))); - if (s) - xmlFree(s); - xmlAddNextSibling(srcVideo, video); - sb2 << first - absClipBegin + clipBegin - 1 << ends; - xmlSetProp(srcVideo,(const xmlChar*)"clipEnd", (const xmlChar*)sb2.str()); - // cerr << "case 4 " << endl; - } - - absClipBegin += clipEnd - clipBegin + 1; - } - } - - // if the node is now empty, delete it (can delete - see nextSequence above) - - if (srcSeq->xmlChildrenNode == NULL) { - xmlUnlinkNode( srcSeq ); - xmlFreeNode( srcSeq ); - } - } - } + // if the node is now empty, delete it (can delete - see nextSequence above) + + if ( srcSeq->children == NULL ) + { + xmlUnlinkNode( srcSeq ); + xmlFreeNode( srcSeq ); + } + } + } RefreshCount( ); - return true; + return true; } -bool PlayList::LoadMediaObject(char *filename) +bool PlayList::LoadMediaObject( char *filename ) { - // cerr << "bool PlayList::LoadAVI(" << filename << ")" << endl; + // cerr << "bool PlayList::LoadAVI(" << filename << ")" << endl; - xmlNodePtr seq; - xmlNodePtr node; - strstream sb; - FileHandler *mediaFile = NULL; - int existingFrames; - int framesInFile; + xmlNodePtr seq; + xmlNodePtr node; + ostringstream sb; + FileHandler *mediaFile = NULL; + int existingFrames; + int framesInFile; dirty = true; // This object should be located in a directory relative to cwd or absolute - string index = directory_utils::get_absolute_path_to_file( "", (char *) filename ); - if ( GetFileMap()->GetMap().find( index ) == GetFileMap()->GetMap().end( ) ) + string index = directory_utils::get_absolute_path_to_file( "", ( char * ) filename ); + if ( GetFileMap() ->GetMap().find( index ) == GetFileMap() ->GetMap().end( ) ) { - if (strncasecmp(strrchr(filename, '.'), ".avi", 4) == 0) - mediaFile = new AVIHandler(); - else if (strncasecmp(strrchr(filename, '.'), ".dv", 3) == 0 || - strncasecmp(strrchr(filename,'.'), ".dif", 4) == 0) - mediaFile = new RawHandler(); + if ( strrchr( filename, '.' ) ) + { + if ( strncasecmp( strrchr( filename, '.' ), ".avi", 4 ) == 0 ) + mediaFile = new AVIHandler(); + else if ( strncasecmp( strrchr( filename, '.' ), ".dv", 3 ) == 0 || + strncasecmp( strrchr( filename, '.' ), ".dif", 4 ) == 0 ) + mediaFile = new RawHandler(); #ifdef HAVE_LIBQUICKTIME - else if (strncasecmp(strrchr(filename, '.'), ".mov", 4) == 0) - mediaFile = new QtHandler(); + else if ( strncasecmp( strrchr( filename, '.' ), ".mov", 4 ) == 0 ) + mediaFile = new QtHandler(); #endif + } + if ( mediaFile == NULL ) return false; - if ( mediaFile->Open(filename) == false ) + if ( mediaFile->Open( filename ) == false ) return false; - GetFileMap()->GetMap()[ index ] = mediaFile; + GetFileMap() ->GetMap() [ index ] = mediaFile; } else { - mediaFile = GetFileMap()->GetMap()[ index ]; + mediaFile = GetFileMap() ->GetMap() [ index ]; } - framesInFile = mediaFile->GetTotalFrames(); - existingFrames = GetNumFrames(); + framesInFile = mediaFile->GetTotalFrames(); + existingFrames = GetNumFrames(); - seq = xmlNewNode(NULL, (const xmlChar*)"seq"); - node = xmlDocGetRootElement(doc); - xmlAddChild(node,seq); - node = xmlNewNode(NULL, (const xmlChar*)"video"); - xmlNewProp(node, (const xmlChar*)"src", (const xmlChar*)filename); - xmlNewProp(node, (const xmlChar*)"clipBegin", (const xmlChar*)"0"); - sb << framesInFile - 1 << ends; - xmlNewProp(node, (const xmlChar*)"clipEnd", (const xmlChar*)sb.str()); - xmlAddChild(seq,node); + seq = xmlNewNode( NULL, ( const xmlChar* ) "seq" ); + xmlAddChild( GetBody(), seq ); + node = xmlNewNode( NULL, ( const xmlChar* ) "video" ); + xmlNewProp( node, ( const xmlChar* ) "src", ( const xmlChar* ) index.c_str() ); + xmlNewProp( node, ( const xmlChar* ) "clipBegin", ( const xmlChar* ) "0" ); + sb << framesInFile - 1 << ends; + xmlNewProp( node, ( const xmlChar* ) "clipEnd", ( const xmlChar* ) sb.str().c_str() ); + xmlAddChild( seq, node ); - if (framesInFile > 0) + if ( framesInFile > 0 ) { RefreshCount( ); - AutoSplit(existingFrames, existingFrames + framesInFile - 1); + AutoSplit( existingFrames, existingFrames + framesInFile - 1 ); } return true; } -bool PlayList::LoadPlayList(char *filename) +bool PlayList::LoadPlayList( char *filename ) { - // cerr << "bool PlayList::LoadPlayList(" << filename << ")" << endl; + // cerr << "bool PlayList::LoadPlayList(" << filename << ")" << endl; dirty = false; - - xmlNsPtr ns; - xmlNodePtr node; - - xmlFreeDoc(doc); - fail_null(doc = xmlParseFile(filename)); - - node = xmlDocGetRootElement(doc); - if (node == NULL) { - cerr << "empty document" << endl; - xmlFreeDoc(doc); - doc = NULL; - return false; - } - ns = xmlSearchNsByHref(doc, node, (const xmlChar *) "http://www.w3.org/2001/SMIL20/Language"); - if (ns == NULL) { - cerr << "document of the wrong type, Namespace not found" << endl; - xmlFreeDoc(doc); - doc = NULL; - return false; - } - if (xmlStrcmp(node->name, (const xmlChar *) "smil")) { - cerr << "document of the wrong type, root node != smil" << endl; - xmlFreeDoc(doc); - doc = NULL; - return false; - } - CleanPlayList(node); + + xmlNsPtr ns; + xmlNodePtr node; + + xmlFreeDoc( doc ); + doc = xmlParseFile( filename ); + if ( !doc ) + { + cerr << "file does not exist or failed to parse XML" << endl; + return false; + } + + node = xmlDocGetRootElement( doc ); + if ( node == NULL ) + { + cerr << "empty document" << endl; + xmlFreeDoc( doc ); + doc = NULL; + return false; + } + ns = xmlSearchNsByHref( doc, node, SMIL20_NAMESPACE_HREF ); + if ( ns == NULL ) + { + cerr << "document of the wrong type, Namespace not found" << endl; + xmlFreeDoc( doc ); + doc = NULL; + return false; + } + if ( xmlStrcmp( node->name, ( const xmlChar * ) "smil" ) ) + { + cerr << "document of the wrong type, root node != smil" << endl; + xmlFreeDoc( doc ); + doc = NULL; + return false; + } + CleanPlayList( node ); // PASS PATH string path = directory_utils::get_directory_from_file( filename ); - parse(xmlDocGetRootElement(doc), fillMap, &path ); + parse( GetBody(), fillMap, &path ); dirty = false; + + // Legacy documents have smil2 namespace prefix declaration + if ( xmlSearchNs( doc, node, ( const xmlChar * ) "smil2" ) == NULL ) + { + // New age compliant documents use SMIL time values, but we continue + // to use frame numbers internally. + parse( node, convertSmilTimeToFrames, NULL ); + } + else + { + // We need to insert a body element into the legacy document + xmlNodePtr root = xmlDocGetRootElement( doc ); + xmlNodePtr seq = root->children; + if ( !seq ) + return false; + if ( xmlStrcmp( seq->name, ( const xmlChar* ) "seq" ) == 0 ) + { + xmlNodePtr body = xmlNewNode( NULL, ( const xmlChar * ) "body" ); + while ( seq ) + { + xmlNodePtr next = seq->next; + xmlUnlinkNode( seq ); + xmlAddChild( body, seq ); + seq = next; + } + xmlAddChild( root, body ); + } + dirty = true; + } + RefreshCount( ); - return true; + return true; } -static bool relativeMap(xmlNodePtr node, void *p) +static bool relativeMap( xmlNodePtr node, void *p, bool *freed ) { - if (xmlStrcmp(node->name, (const xmlChar*)"video") == 0) + if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 ) { - // Check whether the required properties exist - xmlChar *src = xmlGetProp(node, (const xmlChar*)"src"); + // Check whether the required properties exist + xmlChar * src = xmlGetProp( node, ( const xmlChar* ) "src" ); - if ( src != NULL ) + if ( src != NULL ) { - // Determine the absolute path to the file indicated by src - string &directory = *(string *)p; - string index = directory_utils::get_relative_path_to_file( directory, (char *)src ); + // Determine the absolute path to the file indicated by src + string & directory = *( string * ) p; + string index = directory_utils::get_relative_path_to_file( directory, ( char * ) src ); // Save to relative file now - xmlSetProp(node, (const xmlChar*)"src", (xmlChar *)index.c_str() ); - } + xmlSetProp( node, ( const xmlChar* ) "src", ( xmlChar * ) index.c_str() ); + } xmlFree( src ); - } - return false; + } + return false; } -bool PlayList::SavePlayList(char *filename) +bool PlayList::SavePlayList( char *filename, bool isLegacyFormat ) { // Try to save file bool ret = false; - + // Copy the xml doc + xmlDocPtr copy_doc = xmlNewDoc( ( const xmlChar* ) "1.0" ); + xmlNodePtr root = xmlNewNode( NULL, ( const xmlChar* ) "smil" ); + xmlNewNs( root, ( const xmlChar* ) SMIL20_NAMESPACE_HREF, ( const xmlChar* ) NULL ); + xmlDocSetRootElement( copy_doc, root ); + + if ( isLegacyFormat ) + { + // Clone without body + parse( this->GetBody()->children, clone, &root ); + + // Add legacy namespace declaration + xmlNewNs( xmlDocGetRootElement( copy_doc ), SMIL20_NAMESPACE_HREF, ( const xmlChar* ) "smil2" ); + } + else + { + // Clone including body + parse( this->GetBody(), clone, &root ); + + // Convert frame numbers used internally to SMIL time values. + parse( copy_doc->children, convertFramesToSmilTime, NULL ); + } + if ( Preferences::getInstance().relativeSave ) { // Obtain path relative to filenames directory string path = directory_utils::get_directory_from_file( filename ); - // Copy the xml doc - xmlDocPtr copy_doc = xmlCopyDoc( doc, true ); // Convert the copy to relative - parse( xmlDocGetRootElement( copy_doc ), relativeMap, &path ); + parse( copy_doc->children, relativeMap, &path ); // Save the copy ret = xmlSaveFormatFile( filename, copy_doc, 1 ) != -1; - // Delete the copy - xmlFreeDoc( copy_doc ); } else { // Can save directly - ret = xmlSaveFormatFile(filename,doc,1) != -1; + ret = xmlSaveFormatFile( filename, copy_doc, 1 ) != -1; } + // Delete the copy + xmlFreeDoc( copy_doc ); // If saved... - if ( ret ) + if ( !isLegacyFormat && ret ) { // ... and doc name is unspecified, we have now and we're not dirty any more // though all undos are dirty @@ -1676,19 +2014,19 @@ { doc_name = string( filename ); dirty = false; - GetEditorBackup( )->SetAllDirty( ); + GetEditorBackup( ) ->SetAllDirty( ); } // ... and doc name is the same as file name then we're not dirty any more any more // though all undos are dirty else if ( !strcmp( filename, doc_name.c_str() ) ) { dirty = false; - GetEditorBackup( )->SetAllDirty( ); + GetEditorBackup( ) ->SetAllDirty( ); } // ... otherwise we're still dirty - undos stay the same } - return ret; + return ret; } /** Save a playlist en mjpegtools ELI format. @@ -1701,53 +2039,75 @@ \return true if the frames could be copied, false otherwise */ -bool PlayList::SavePlayListEli(char * filename, bool isPAL) +bool PlayList::SavePlayListEli( char * filename, bool isPAL ) { - EliInfos eli; - if (doc != NULL) { - parse(xmlDocGetRootElement(doc), convertEli, &eli); - } - - /* Open a file */ - ofstream eli_file(filename); - if (!eli_file) { - return false; - } - - eli_file << "LAV Edit List" << endl; - eli_file << (isPAL? "PAL" : "NTSC") << endl; - - /* The number of clips */ - eli_file << eli.size() << endl; + EliInfos eli; + if ( doc != NULL ) + { + parse( GetBody(), convertEli, &eli ); + } - /* Now, all the clips, without numbers */ - EliInfosIterator End = eli.end(); - EliInfosIterator i; - for (i = eli.begin(); i != End; i++) { - eli_file << (*i).file << endl; - } - - /* Now, number \s begin \s end\n */ - int count = 0; - for (i = eli.begin(); i != End; i++) { - eli_file << count - << " " << (*i).clipBegin - << " " << (*i).clipEnd << endl; - ++count; - } - - /* Check the final status */ - if (eli_file.bad()) { - return false; - } - - /* We are done */ - eli_file.close(); + /* Open a file */ + ofstream eli_file( filename ); + if ( !eli_file ) + { + return false; + } + + eli_file << "LAV Edit List" << endl; + eli_file << ( isPAL ? "PAL" : "NTSC" ) << endl; + + /* The number of clips */ + eli_file << eli.size() << endl; + + /* Now, all the clips, without numbers */ + EliInfosIterator End = eli.end(); + EliInfosIterator i; + for ( i = eli.begin(); i != End; i++ ) + { + eli_file << ( *i ).file << endl; + } + + /* Now, number \s begin \s end\n */ + int count = 0; + for ( i = eli.begin(); i != End; i++ ) + { + eli_file << count + << " " << ( *i ).clipBegin + << " " << ( *i ).clipEnd << endl; + ++count; + } + + /* Check the final status */ + if ( eli_file.bad() ) + { + return false; + } + + /* We are done */ + eli_file.close(); - return true; + return true; } +bool PlayList::SavePlayListSrt( const char* filename ) +{ + SrtContext srt( filename ); + + /* Open a file */ + if ( !srt.file ) + { + return false; + } + if ( doc ) + { + parse( GetBody(), convertSrt, &srt ); + } + + return !srt.file.bad(); +} + /** Recursively deletes unnecessary items from our XML tree. @@ -1758,32 +2118,43 @@ */ -void PlayList::CleanPlayList(xmlNodePtr node) +void PlayList::CleanPlayList( xmlNodePtr node ) { - while (node != NULL) { + while ( node != NULL ) + { + + xmlNodePtr nodeToDelete = NULL; - xmlNodePtr nodeToDelete = NULL; + CleanPlayList( node->children ); + if ( xmlStrcmp( node->name, ( const xmlChar* ) "smil" ) == 0 ) + { + //do nothing + } + else if ( xmlStrcmp( node->name, ( const xmlChar* ) "body" ) == 0 ) + { + //do nothing + } + else if ( xmlStrcmp( node->name, ( const xmlChar* ) "seq" ) == 0 ) + { + if ( node->children == NULL ) + { + nodeToDelete = node; + } + } + else if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 ) + { + // do nothing + } + else + nodeToDelete = node; + node = node->next; - CleanPlayList(node->xmlChildrenNode); - if (xmlStrcmp(node->name, (const xmlChar*)"smil") == 0) { - //do nothing - } - else if (xmlStrcmp(node->name, (const xmlChar*)"seq") == 0) { - if (node->xmlChildrenNode == NULL) { - nodeToDelete = node; - } - } else if (xmlStrcmp(node->name, (const xmlChar*)"video") == 0) { - // do nothing - } - else - nodeToDelete = node; - node = node->next; - - if (nodeToDelete != NULL) { - xmlUnlinkNode(nodeToDelete); - xmlFreeNode(nodeToDelete); - } - } + if ( nodeToDelete != NULL ) + { + xmlUnlinkNode( nodeToDelete ); + xmlFreeNode( nodeToDelete ); + } + } RefreshCount( ); } @@ -1806,31 +2177,32 @@ \result true if file has been found and xml tree walk is done */ -static bool checkIfFileUsed(xmlNodePtr node, void *p) +static bool checkIfFileUsed( xmlNodePtr node, void *p, bool *freed ) { - if (xmlStrcmp(node->name, (const xmlChar*)"video") == 0) { - // Check whether the required properties exist - xmlChar *src = xmlGetProp(node, (const xmlChar*)"src"); - string index( (char *)src ); + if ( xmlStrcmp( node->name, ( const xmlChar* ) "video" ) == 0 ) + { + // Check whether the required properties exist + xmlChar * src = xmlGetProp( node, ( const xmlChar* ) "src" ); + string index( ( char * ) src ); xmlFree( src ); - return *(string *)p == index; - } - return false; + return *( string * ) p == index; + } + return false; } /** Checks if the filename exists in the current playlist. - + \param filename file name to search for \result true if file exists */ bool PlayList::IsFileUsed( string filename ) const { - return parse( xmlDocGetRootElement( doc ), checkIfFileUsed, &filename ); + return parse( GetBody(), checkIfFileUsed, &filename ); } /** Returns the current setting of the dirty flag - + \result true if the playlist is dirty */ @@ -1840,7 +2212,7 @@ } /** Sets the dirty flag on the playlist. - + \param value value of dirty flag */ @@ -1850,10 +2222,10 @@ } /** Returns the doc name associated to the playlist. - + NB: This should reflect the first .smil file received via the Load or Save methods above. - + \result the file name of the playlist */ @@ -1863,10 +2235,10 @@ } /** Protected set doc name method. - + The playlist class uses the first file name of load or save, so operations like save as shouldn't trigger this method. - + \param m_doc_name doc name to use */ @@ -1882,11 +2254,11 @@ // If we have a file, load it, otherwise we have valid contents already... if ( doc_name != "" ) - playlist.LoadPlayList( (char *)doc_name.c_str() ); + playlist.LoadPlayList( ( char * ) doc_name.c_str() ); } /** Obtain the current project directory. - + \result the directory to place files containing this playlist */ @@ -1906,11 +2278,98 @@ return output; } +string PlayList::GetSeqAttribute( int frameNum, const char* name ) const +{ + MovieInfo data; + + memset( &data, 0, sizeof( MovieInfo ) ); + data.absBegin = 0; + data.absEnd = 0; + data.absFrame = frameNum; + + if ( parse( GetBody(), findSceneStart, &data ) && data.sequence ) + { + const char *value = ( const char* ) xmlGetProp( data.sequence, ( const xmlChar * ) name ); + if ( value ) + return value; + } + return ""; +} + + +bool PlayList::SetSeqAttribute( int frameNum, const char* name, const char* value ) +{ + MovieInfo data; + + memset( &data, 0, sizeof( MovieInfo ) ); + data.absBegin = 0; + data.absEnd = 0; + data.absFrame = frameNum; + + if ( parse( GetBody(), findSceneStart, &data ) && data.sequence ) + { + xmlSetProp( data.sequence, ( const xmlChar * ) name, ( const xmlChar * ) value ); + dirty = true; + return true; + } + return false; +} + + +bool PlayList::SetDocId( const char* value ) +{ + if ( GetBody() ) + { + xmlSetProp( GetBody(), ( const xmlChar * ) "id", ( const xmlChar * ) value ); + dirty = true; + return true; + } + return false; +} + + +string PlayList::GetDocId( ) const +{ + if ( GetBody() ) + { + const char *value = ( const char* ) xmlGetProp( GetBody(), ( const xmlChar * ) "id" ); + if ( value ) + return value; + } + return ""; +} + + +bool PlayList::SetDocTitle( const char* value ) +{ + if ( GetBody() ) + { + xmlSetProp( GetBody(), ( const xmlChar * ) "title", ( const xmlChar * ) value ); + dirty = true; + return true; + } + return false; +} + + +string PlayList::GetDocTitle( ) const +{ + if ( GetBody() ) + { + const char *value = ( const char* ) xmlGetProp( GetBody(), ( const xmlChar * ) "title" ); + if ( value ) + return value; + } + return ""; +} + + /** Editor backup - holds all the previous playlists (up to a the maxUndos preference value). */ -EditorBackup::EditorBackup() : position( -1 ) { +EditorBackup::EditorBackup() : position( -1 ) +{ cerr << ">> Creating undo/redo buffer" << endl; maxUndos = Preferences::getInstance().maxUndos; } @@ -1918,48 +2377,53 @@ /** Destructor for the Editor Backup object. */ -EditorBackup::~EditorBackup() { +EditorBackup::~EditorBackup() +{ cerr << ">> Destroying undo/redo buffer" << endl; - while( backups.size() ) + while ( backups.size() ) { delete backups[ backups.size() - 1 ]; backups.pop_back(); } } -void EditorBackup::Store( PlayList *playlist ) { +void EditorBackup::Store( PlayList *playlist, bool isPersisted ) +{ cerr << ">>> Received playlist to store at position " << position + 1 << endl; // Three conditions to check: - // + // // 1. The undo position is 1 less than the current size of the vector and less than the max size // in which case we dump the new playlist at the top of the vector and increment the position // - if ( ( position + 1 ) == (int)backups.size() && ( position < maxUndos || maxUndos == 0 ) ) { + if ( ( position + 1 ) == ( int ) backups.size() && ( position < maxUndos || maxUndos == 0 ) ) + { cerr << ">>>> Adding to end" << endl; position ++; PlayList *temp = new PlayList; - playlist->GetPlayList(0, playlist->GetNumFrames() - 1, *temp); + playlist->GetPlayList( 0, playlist->GetNumFrames() - 1, *temp ); temp->SetDirty( playlist->IsDirty( ) ); backups.push_back( temp ); } - // + // // 2. The undo position is not at the end, in which case we need to remove everything from position // to the end before pushing // - else if ( ( position + 1 ) < (int)backups.size() ) { + else if ( ( position + 1 ) < ( int ) backups.size() ) + { cerr << ">>>> Cleaning from " << position + 1 << " to " << backups.size() << endl; position ++; - while( position < (int)backups.size() ) { + while ( position < ( int ) backups.size() ) + { delete backups[ backups.size() - 1 ]; backups.pop_back(); } PlayList *temp = new PlayList; - playlist->GetPlayList(0, playlist->GetNumFrames() - 1, *temp); + playlist->GetPlayList( 0, playlist->GetNumFrames() - 1, *temp ); temp->SetDirty( playlist->IsDirty( ) ); backups.push_back( temp ); } @@ -1968,12 +2432,13 @@ // 3. We're at the top of the stack so we need to remove position 0 and push to the end // - else if ( position == maxUndos ) { + else if ( position == maxUndos ) + { cerr << ">>>> Removing the earliest playlist to make room" << endl; delete backups[ 0 ]; backups.erase( backups.begin() ); PlayList *temp = new PlayList; - playlist->GetPlayList(0, playlist->GetNumFrames() - 1, *temp); + playlist->GetPlayList( 0, playlist->GetNumFrames() - 1, *temp ); temp->SetDirty( playlist->IsDirty( ) ); backups.push_back( temp ); } @@ -1981,15 +2446,56 @@ // // Just in case we missed something... // - - else { + + else + { cerr << ">>>> Unknown condition - position = " << position << " size = " << backups.size() << endl; } + + // Update persistent storage + if ( isPersisted ) + { + // First, delete contents of private directory in $HOME + string directory = KINO_AUTOSAVE_DIR; + mkdir( directory.c_str(), 0700 ); + DIR* dir = opendir( directory.c_str( ) ); + struct dirent* entry; + if ( dir ) + { + while ( ( entry = readdir( dir ) ) != NULL ) + { + if ( entry->d_name[0] != '.' ) + { + ostringstream sb; + sb << directory << "/" << entry->d_name << ends; + unlink( sb.str().c_str() ); + } + } + closedir( dir ); + + // Second, write each dirty backup + // absolute filenames is faster and more appropriate here + bool isRelative = Preferences::getInstance().relativeSave; + Preferences::getInstance().relativeSave = false; + for ( size_t i = 0; i < backups.size(); ++i ) + { + if ( backups[i]->IsDirty() ) + { + ostringstream sb; + sb << directory << "/" << i << ".xml" << ends; + // save in legacy format for speed + backups[i]->SavePlayList( const_cast<char*>( sb.str().c_str() ), true ); + } + } + Preferences::getInstance().relativeSave = isRelative; + } + } } -void EditorBackup::Undo( PlayList *playlist ) { +void EditorBackup::Undo( PlayList *playlist ) +{ cerr << ">>> Received request to undo from position " << position - 1 << endl; - if ( position > 0 ) + if ( position > 0 ) { position --; playlist->Delete( 0, playlist->GetNumFrames() - 1 ); @@ -1997,15 +2503,16 @@ playlist->InsertPlayList( temp, 0 ); playlist->SetDirty( temp.IsDirty( ) ); } - else + else { cerr << ">>>> Unable to satisfy request." << endl; } } -void EditorBackup::Redo( PlayList *playlist ) { +void EditorBackup::Redo( PlayList *playlist ) +{ cerr << ">>> Received request to recover from position " << position + 1 << endl; - if ( ( position + 1 ) < (int)backups.size() ) + if ( ( position + 1 ) < ( int ) backups.size() ) { position ++; playlist->Delete( 0, playlist->GetNumFrames() - 1 ); @@ -2013,14 +2520,14 @@ playlist->InsertPlayList( temp, 0 ); playlist->SetDirty( temp.IsDirty( ) ); } - else + else { cerr << ">>>> Unable to satisfy request." << endl; } } /** Set all the stored backups as dirty other than the current one. - + When a file is saved, the previous copy (which may still be in the editor backup), will be dirty (relative to the current contents of the file) - this method ensures that only the most recent item will be registered as clean. @@ -2032,7 +2539,7 @@ for ( n = backups.begin(); n != backups.end(); ++n ) ( **n ).SetDirty( true ); if ( position >= 0 ) - backups[ position ]->SetDirty( false ); + backups[ position ] ->SetDirty( false ); } /** Clear the contents of the editor backup. @@ -2040,7 +2547,7 @@ void EditorBackup::Clear( ) { - while( backups.size() ) + while ( backups.size() ) { delete backups[ backups.size() - 1 ]; backups.pop_back(); @@ -2053,6 +2560,43 @@ EditorBackup *GetEditorBackup( ) { - static EditorBackup *backup = new EditorBackup( ); + static EditorBackup * backup = new EditorBackup( ); return backup; } + +bool EditorBackup::Restore( PlayList * playlist ) +{ + string directory = KINO_AUTOSAVE_DIR; + DIR* dir = opendir( directory.c_str( ) ); + struct dirent* entry; + bool result = false; + + if ( dir ) + { + std::vector<int> names; + while ( ( entry = readdir( dir ) ) != NULL ) + { + if ( entry->d_name[0] != '.' ) + names.push_back( atoi( entry->d_name ) ); + } + closedir( dir ); + + if ( !names.empty() ) + { + std::sort( names.begin(), names.end() ); + Clear(); + PlayList pl; + for ( size_t i = 0; i < names.size(); ++i ) + { + ostringstream sb; + sb << directory << "/" << names[i] << ".xml" << ends; + if ( pl.LoadPlayList( const_cast<char*>( sb.str().c_str() ) ) ) + Store( &pl, false ); + } + ++position; + Undo( playlist ); + result = true; + } + } + return result; +} --- smilutils-0.3.0/libkino/playlist.h +++ smilutils-0.3.0/libkino/playlist.h @@ -1,20 +1,22 @@ /* - * Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ +* playlist.h -- SMIL definition +* Copyright (C) 2000 Arne Schirmacher <arne@schirmacher.de> +* Copyright (C) 2001-2007 Dan Dennedy <dan@dennedy.org> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ #ifndef _PLAYLIST_H #define _PLAYLIST_H @@ -26,11 +28,13 @@ using std::string; #include <map> using std::map; +#include "smiltime.h" // C includes #include <libxml/xmlmemory.h> #include <libxml/parser.h> +#include <time.h> // forward declarations @@ -43,33 +47,35 @@ class PlayList { public: - PlayList(); - PlayList(const PlayList&); - PlayList& operator=(const PlayList&); - ~PlayList(); - - int GetNumFrames() const; - char* GetFileNameOfFrame(int frameNum) const; - bool GetFrame(int frameNum, Frame &frame); - bool GetMediaObject(int frameNum, FileHandler **media); - int GetClipBegin(int frameNum) const; - int GetClipEnd(int frameNum) const; - bool SetClipBegin(int frameNum, const char* value); - bool SetClipEnd(int frameNum, const char* value); - int FindStartOfScene(int frameNum) const; - int FindEndOfScene(int frameNum) const; - void AutoSplit(int first, int last); - bool SplitSceneBefore(int frameNum); - bool JoinScenesAt(int frameNum); - bool GetPlayList(int first, int last, PlayList &playlist) const; - bool InsertPlayList(PlayList &playlist, int before); - bool Delete(int first, int last); - bool LoadMediaObject(char *filename); - bool LoadPlayList(char *filename); - bool SavePlayList(char *filename); - bool SavePlayListEli(char *filename, bool isPAL); - void CleanPlayList(xmlNodePtr node); - void CleanPlayList( ); + PlayList(); + PlayList( const PlayList& ); + PlayList& operator=( const PlayList& ); + ~PlayList(); + + xmlNodePtr GetBody( ) const; + int GetNumFrames() const; + char* GetFileNameOfFrame( int frameNum ) const; + bool GetFrame( int frameNum, Frame &frame ); + bool GetMediaObject( int frameNum, FileHandler **media ); + int GetClipBegin( int frameNum ) const; + int GetClipEnd( int frameNum ) const; + bool SetClipBegin( int frameNum, const char* value ); + bool SetClipEnd( int frameNum, const char* value ); + int FindStartOfScene( int frameNum ) const; + int FindEndOfScene( int frameNum ) const; + void AutoSplit( int first, int last ); + bool SplitSceneBefore( int frameNum ); + bool JoinScenesAt( int frameNum ); + bool GetPlayList( int first, int last, PlayList &playlist ) const; + bool InsertPlayList( PlayList &playlist, int before ); + bool Delete( int first, int last ); + bool LoadMediaObject( char *filename ); + bool LoadPlayList( char *filename ); + bool SavePlayList( char *filename, bool isLegacyFormat = false ); + bool SavePlayListEli( char *filename, bool isPAL ); + bool SavePlayListSrt( const char *filename ); + void CleanPlayList( xmlNodePtr node ); + void CleanPlayList( ); bool IsFileUsed( string filename ) const; bool IsDirty( ) const; void SetDirty( bool value ); @@ -77,13 +83,23 @@ void GetLastCleanPlayList( PlayList &playlist ); void SetDocName( string ); string GetProjectDirectory( ); + string GetSeqAttribute( int frameNum, const char* ) const; + bool SetSeqAttribute( int frameNum, const char*, const char* ); + bool SetDocId( const char* value ); + string GetDocId( ) const; + bool SetDocTitle( const char* value ); + string GetDocTitle( ) const; + protected: bool dirty; + private: void RefreshCount(); + void AutoSplit( int first, time_t startTime, int last, time_t endTime, int fps ); string doc_name; - xmlDocPtr doc; + xmlDocPtr doc; int count; + SMIL::MediaClippingTime time; }; /** Directory and path utilities @@ -91,30 +107,32 @@ class directory_utils { - public: - static string join_file_to_directory( const string directory, const string &file ); - static string get_directory_from_file( const string &file ); - static string get_absolute_path_to_file( const string &directory, const string &file ); - static string get_relative_path_to_file( const string &directory, const string &file ); - static string expand_directory( const string directory ); +public: + static string join_file_to_directory( const string directory, const string &file ); + static string get_directory_from_file( const string &file ); + static string get_absolute_path_to_file( const string &directory, const string &file ); + static string get_relative_path_to_file( const string &directory, const string &file ); + static string expand_directory( const string directory ); }; /** The EditorBackup class holds the previous PlayLists for undo/redo functionality. */ -class EditorBackup { - private: - int maxUndos; - int position; - vector <PlayList *> backups; - public: - EditorBackup(); - ~EditorBackup(); - void Store( PlayList * ); - void Undo( PlayList * ); - void Redo( PlayList * ); - void SetAllDirty( ); - void Clear( ); +class EditorBackup +{ +private: + int maxUndos; + int position; + vector <PlayList *> backups; +public: + EditorBackup(); + ~EditorBackup(); + void Store( PlayList *, bool isPersisted = true ); + void Undo( PlayList * ); + void Redo( PlayList * ); + void SetAllDirty( ); + void Clear( ); + bool Restore( PlayList * ); }; /** The singleton method for obtaining the instance of the EditorBackup. @@ -122,18 +140,21 @@ extern EditorBackup *GetEditorBackup(); + /** The FileMap class holds the mappings between the file name and the loaded file objects. */ class FileMap { - public: - /** The map from file name to handler is here */ - virtual map<string, FileHandler*> &GetMap() = 0; - /** Clears the content of the file map. */ - virtual void Clear() = 0; - /** Obtains a list of unused fx rendered files. */ - virtual void GetUnusedFxFiles( PlayList &list, vector< string > &unused ) = 0; +public: + virtual ~FileMap() + { } + /** The map from file name to handler is here */ + virtual map<string, FileHandler*> &GetMap() = 0; + /** Clears the content of the file map. */ + virtual void Clear() = 0; + /** Obtains a list of unused fx rendered files. */ + virtual void GetUnusedFxFiles( PlayList &list, vector< string > &unused ) = 0; }; /** The singleton method for obtain the instance of the file map. @@ -141,4 +162,5 @@ extern FileMap *GetFileMap( ); + #endif --- smilutils-0.3.0/libkino/smiltime.cc +++ smilutils-0.3.0/libkino/smiltime.cc @@ -0,0 +1,647 @@ +/* +* smiltime.cc -- W3C SMIL2 Time value parser +* Copyright (C) 2003-2007 Dan Dennedy <dan@dennedy.org> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "smiltime.h" + +#include <iostream> +#include <iomanip> +#include <sstream> + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> + +#include "stringutils.h" + +namespace SMIL +{ + +Time::Time() +{ + Time( 0L ); + indefinite = true; + timeType = SMIL_TIME_INDEFINITE; + resolved = true; +} + + +Time::Time( long time ) : + timeValue( time ), + offset( 0 ), + indefinite( false ), + resolved( true ), + syncbaseBegin( false ), + timeType( SMIL_TIME_OFFSET ) +{ +} + + +Time::Time( string time ) +{ + Time( 0L ); + parseTimeValue( time ); +} + +void Time::parseTimeValue( string time ) +{ + time = StringUtils::stripWhite( time ); + + resolved = false; + + if ( StringUtils::begins ( time, "indefinite" ) || time.empty() || time.size() == 0 ) + { + indefinite = true; + timeType = SMIL_TIME_INDEFINITE; + resolved = true; + //cerr << "smil: this is an indefinite time value" << endl; + } + else if ( time.at( 0 ) == '+' || time.at( 0 ) == '-' ) + { + //cerr << "smil: this is an offset time value" << endl; + timeValue = parseClockValue( time.substr( 1 ) ); + if ( time.at( 0 ) == '-' ) + timeValue *= -1; + timeType = SMIL_TIME_OFFSET; + resolved = true; + indefinite = false; + } + else if ( StringUtils::begins ( time, "wallclock(" ) ) + { + //parseWallclockValue(time); + timeType = SMIL_TIME_WALLCLOCK; + resolved = true; + indefinite = false; + //cerr << "smil: this is a wallclock time value" << endl; + } + else if ( StringUtils::begins ( time, "accesskey(" ) ) + { + timeType = SMIL_TIME_ACCESSKEY; + //cerr << "smil: this is an accesskey time value" << endl; + } + else + { + std::ostringstream token; + char c; + string::size_type pos = 0; + string base; + for ( ; pos < time.size(); ++pos ) + { + c = time.at( pos ); + if ( c == '+' || c == '-' ) + { + string symbol = token.str(); + token.str( string() ); + //cerr << "smil: parsed symbol token '" << symbol << "'" << endl; + + if ( symbol == string( "begin" ) ) + { + //cerr << "smil: this is a sync based time value" << endl; + syncbaseBegin = true; + timeType = SMIL_TIME_SYNC_BASED; + } + else if ( symbol == string( "end" ) ) + { + //cerr << "smil: this is a sync based time value" << endl; + syncbaseBegin = false; + timeType = SMIL_TIME_SYNC_BASED; + } + else if ( StringUtils::begins( symbol, "marker(" ) ) + { + //cerr << "smil: this is a media marker time value" << endl; + //parseMediaMarkerValue(base, token); // base must not be empty + timeType = SMIL_TIME_MEDIA_MARKER; + } + else if ( StringUtils::begins( symbol, "repeat(" ) ) + { + //cerr << "smil: this is an event repeat time value" << endl; + //parseRepeatValue(base, token); // base can be empty := current element + timeType = SMIL_TIME_REPEAT; + } + else + { + //cerr << "smil: this is an event based time value" << endl; + //parseEventValue( base, token ); // base can be empty := current element + timeType = SMIL_TIME_EVENT_BASED; + } + offset = parseClockValue( time.substr( pos + 1 ) ); + if ( c == '-' ) + offset *= -1; + break; + } + else if ( c == '.' && ( pos == 0 || time.at( pos - 1 ) != '\\' ) ) + { + base = token.str(); + token.str( string() ); + //cerr << "smil: parsed base token '" << base << "'" << endl; + } + else + { + token << c; + } + } + string remaining = token.str(); + //cerr << "smil: parsed remaining token '" << remaining << "'" << endl; + if ( !remaining.empty() ) + { + //cerr << "smil: this is an offset time value" << endl; + offset = parseClockValue( time ); + timeType = SMIL_TIME_OFFSET; + resolved = true; + indefinite = false; + } + } +} + + +static inline +string getFraction( string& time ) +{ + string::size_type pos; + string fraction; + if ( ( pos = time.find( '.' ) ) != string::npos ) + { + fraction = time.substr( pos + 1 ); + //cerr << "smil: fraction = " << fraction << endl; + time = time.substr( 0, pos ); + } + return fraction; +} + + +long Time::parseClockValue( string time ) +{ + long total = 0; + string hours; + string minutes; + string seconds; + string milliseconds; + string::size_type pos1, pos2; + + if ( ( pos1 = time.find( ':' ) ) != string::npos ) + { + if ( ( pos2 = time.find( ':', pos1 + 1 ) ) != string::npos ) + { + //parse Full-clock-value + //cerr << "smil: parsing Full clock value " << time << endl; + hours = time.substr( 0, pos1 ); + time = time.substr( pos1 + 1 ); + pos1 = pos2 - pos1 - 1; + } + //parse Partial-clock-value + //cerr << "smil: parsing Partial clock value " << time << endl; + if ( ( pos2 = time.find( '.' ) ) != string::npos ) + { + milliseconds = time.substr( pos2 + 1, 3 ); + time = time.substr( 0, pos2 ); + } + minutes = time.substr( 0, pos1 ); + seconds = time.substr( pos1 + 1 ); + } + else + { + //parse Timecount-value + //cerr << "smil: parsing Timecount value " << time << endl; + if ( StringUtils::ends( time, "h" ) ) + { + total = ( long ) ( atof( getFraction( time ).c_str() ) * 3600 ); + hours = time; + } + else if ( StringUtils::ends( time, "min" ) ) + { + total = ( long ) ( atof( getFraction( time ).c_str() ) * 60 ); + minutes = time; + } + else if ( StringUtils::ends( time, "ms" ) ) + { + total = ( long ) ( atof( time.c_str() ) + 0.5 ); + } + else + { + total = atol( getFraction( time ).c_str() ); + seconds = time; + } + } + //cerr << "smil: subtotal = " << total << ", h = " << atol(hours.c_str()) << + // ", m = " << atol(minutes.c_str()) << ", s = " << atol(seconds.c_str()) << + // ", ms = " << (long) (atof(milliseconds.c_str()) * 1000) << endl; + total += ( atol( hours.c_str() ) * 3600 + atol( minutes.c_str() ) * 60 + + atol( seconds.c_str() ) ) * 1000 + atol( milliseconds.c_str() ); + return total; +} + + +long Time::getResolvedOffset() +{ + return ( resolved ? ( timeValue + offset ) : 0 ); +} + +bool Time::isNegative() +{ + return ( resolved && !indefinite && ( getResolvedOffset() < 0 ) ); +} + + +bool Time::operator< ( Time& time ) +{ + return !( ( *this > time ) || ( *this == time ) ); +} + + +bool Time::operator==( Time& time ) +{ + return ( + ( this->isIndefinite() && time.isIndefinite() ) || + ( this->getResolvedOffset() == time.getResolvedOffset() ) + ); +} + + +bool Time::operator> ( Time& time ) +{ + return ( + ( !resolved ) || + ( indefinite && time.isResolved() && !time.isIndefinite() ) || + ( resolved && time.isResolved() && this->getResolvedOffset() > time.getResolvedOffset() ) + ); +} + + +void Time::setTimeValue( Time& source ) +{ + resolved = source.isResolved(); + indefinite = source.isIndefinite(); + timeValue = source.getTimeValue(); +} + + +string Time::toString( TimeFormat format ) +{ + long ms = getResolvedOffset(); + ostringstream str; + + if ( indefinite ) + { + str << "indefinite"; + } + else if ( !resolved ) + { + str << "unresolved"; + } + else switch( format ) + { + case TIME_FORMAT_CLOCK: + { + int hh = ( ms / 3600000 ); + ms -= hh * 3600000; + int mm = ( ms / 60000 ); + ms -= mm * 60000; + int ss = ms / 1000; + ms -= ss * 1000; + + str << std::setfill( '0' ) << std::setw( 2 ) << hh << + ":" << std::setfill( '0' ) << std::setw( 2 ) << mm << + ":" << std::setfill( '0' ) << std::setw( 2 ) << ss << "." << + std::setfill( '0' ) << std::setw( 3 ) << ms; + break; + } + case TIME_FORMAT_MS: + str << ms << "ms"; + break; + + case TIME_FORMAT_S: + str << ( ms / 1000 ) << "." << + std::setfill( '0' ) << std::setw( 3 ) << ( ms % 1000 ); + break; + + case TIME_FORMAT_MIN: + str << ( ms / 60000 ) << "." << + std::setfill( '0' ) << std::setw( 4 ) << + floor( ( float )( ms % 60000 ) / 6 + 0.5 ) << "min"; + break; + + case TIME_FORMAT_H: + str << ( ms / 3600000 ) << "." << + std::setfill( '0' ) << std::setw( 5 ) << + floor( ( float )( ms % 3600000 ) / 36 + 0.5 ) << "h"; + break; + + default: + break; + } + return str.str(); + +} + +string Time::serialise() +{ + return toString(); +} + + +MediaClippingTime::MediaClippingTime( ) : + Time( 0L ), + m_framerate( 0.0 ), + m_isSmpteValue( false ), + m_subframe( SMIL_SUBFRAME_NONE ) +{ +} + +MediaClippingTime::MediaClippingTime( float framerate ) : + Time( 0L ), + m_framerate( framerate ), + m_isSmpteValue( false ), + m_subframe( SMIL_SUBFRAME_NONE ) +{ +} + +MediaClippingTime::MediaClippingTime( string time, float framerate ) : + Time( 0L ), + m_framerate( framerate ), + m_isSmpteValue( false ), + m_subframe( SMIL_SUBFRAME_NONE ) +{ + parseValue( time ); +} + + +void MediaClippingTime::setFramerate( float framerate ) +{ + m_framerate = framerate; +} + + +void MediaClippingTime::parseValue( string time ) +{ + time = StringUtils::stripWhite( time ); + //cerr << "smil: parsing media clipping time " << time << endl; + if ( StringUtils::begins( time, "smpte=" ) || + StringUtils::begins( time, "smpte-30-drop=" ) || + StringUtils::begins( time, "smpte-25=" ) ) + { + parseSmpteValue( time.substr( time.find( '=' ) + 1 ) ); + } + else if ( time.find( '=' ) != string::npos ) // discard npt= + { + parseTimeValue( time.substr( time.find( '=' ) + 1 ) ); + } else + { + parseTimeValue( time ); + } +} + +void MediaClippingTime::parseSmpteValue( string time ) +{ + string hours; + string minutes; + string seconds; + string frames; + string::size_type pos; + + if ( m_framerate == 0 ) + return; + + m_isSmpteValue = true; + + if ( ( pos = time.find( ':' ) ) != string::npos || ( pos = time.find( ';' ) ) != string::npos ) + { + //cerr << "smil: parsing HH SMPTE value " << time << endl; + hours = time.substr( 0, pos ); + time = time.substr( pos + 1 ); + + if ( ( pos = time.find( ':' ) ) != string::npos || ( pos = time.find( ';' ) ) != string::npos ) + { + //cerr << "smil: parsing MM SMPTE value " << time << endl; + minutes = time.substr( 0, pos ); + time = time.substr( pos + 1 ); + + if ( ( pos = time.find( ':' ) ) != string::npos || ( pos = time.find( ';' ) ) != string::npos ) + { + //cerr << "smil: parsing SS SMPTE value " << time << endl; + seconds = time.substr( 0, pos ); + time = time.substr( pos + 1 ); + + if ( ( pos = time.find( '.' ) ) != string::npos ) + { + //cerr << "smil: parsing FF SMPTE value " << time << endl; + frames = time.substr( 0, pos ); + switch ( time.at( pos + 1 ) ) + { + case '0' : + m_subframe = SMIL_SUBFRAME_0; + break; + case '1' : + m_subframe = SMIL_SUBFRAME_1; + break; + default: + m_subframe = SMIL_SUBFRAME_NONE; + break; + } + } + else + { + frames = time; + } + } + else + { + // minutes, seconds, and frames only + frames = time; + seconds = minutes; + minutes = hours; + hours = ""; + } + } + else + { + // frames and seconds only + frames = time; + seconds = hours; + hours = ""; + } + } + else + { + // frames only + frames = time; + } + + //cerr << "hh = " << hours << ", mm = " << minutes << ", ss = " << seconds << ", ff = " << frames << endl; + timeValue = ( atol( hours.c_str() ) * 3600 + atol( minutes.c_str() ) * 60 + + atol( seconds.c_str() ) ) * 1000 + + ( long )( atof( frames.c_str() ) / m_framerate * 1000 + 0.5 ); + + resolved = true; + indefinite = false; +} + +string MediaClippingTime::toString( TimeFormat format ) +{ + if ( format == TIME_FORMAT_SMPTE ) + { + if ( indefinite ) + return "indefinite"; + else if ( !resolved ) + return "unresolved"; + else + { + long ms = getResolvedOffset(); + int hh = ( ms / 3600000 ); + ms -= hh * 3600000; + int mm = ( ms / 60000 ); + ms -= mm * 60000; + int ss = ms / 1000; + ms -= ss * 1000; + + ostringstream str; + str << hh << ":" << std::setfill( '0' ) << std::setw( 2 ) << + mm << ":" << std::setfill( '0' ) << std::setw( 2 ) << ss << + ( m_framerate == 25.0 ? ":" : ";" ) << + std::setfill( '0' ) << std::setw( 2 ) << + floor( m_framerate * ms / 1000.0 + 0.5 ); + if ( m_subframe == SMIL_SUBFRAME_0 ) + str << ".0"; + else if ( m_subframe == SMIL_SUBFRAME_1 ) + str << ".1"; + + return str.str(); + } + } + else if ( format == TIME_FORMAT_FRAMES ) + { + std::ostringstream str; + str << getFrames(); + return str.str(); + } + else + { + return Time::toString( format ); + } +} + +string MediaClippingTime::serialise() +{ + std::string s; + if ( m_isSmpteValue ) + { + if ( m_framerate == 25.0 ) + s = "smpte-25="; + else + s = "smpte="; + + return s + toString(); + } + return Time::toString(); +} + + +string framesToSmpte( int frames, int fps ) +{ + char s[ 12 ]; + int hours, mins, secs; + int cur = frames; + + if ( fps == 29 ) + fps = 30; + + if ( frames == 0 ) + { + hours = 0; + mins = 0; + secs = 0; + } + else + { + /* NTSC drop-frame */ + if ( fps == 30 ) + { + int max_frames = cur; + for ( int j = 1800; j <= max_frames; j += 1800 ) + { + if ( j % 18000 ) + { + max_frames += 2; + cur += 2; + } + } + } + hours = cur / ( fps * 3600 ); + cur -= hours * ( fps * 3600 ); + mins = cur / ( fps * 60 ); + cur -= mins * ( fps * 60 ); + secs = cur / fps; + cur -= secs * fps; + } + + snprintf( s, 12, "%2.2d:%2.2d:%2.2d%s%2.2d", hours, mins, secs, ( fps == 30 ) ? ";" : ":", cur ); + return string( s ); +} + + +string MediaClippingTime::parseValueToString( string time, TimeFormat format ) +{ + offset = 0; + timeValue = 0; + if ( format == TIME_FORMAT_NONE || format == TIME_FORMAT_FRAMES || format == TIME_FORMAT_SMPTE ) + parseSmpteValue( time ); + else + parseValue( time ); + return toString( format ); +} + + +string MediaClippingTime::parseFramesToString( int frames, TimeFormat format ) +{ + if ( m_framerate == 0 ) + return ""; + offset = 0; + timeValue = ( long )( 1000.0 * frames / m_framerate + 0.5 ); + resolved = true; + indefinite = false; + + switch ( format ) + { + case TIME_FORMAT_NONE: + return ""; + + case TIME_FORMAT_FRAMES: + { + std::ostringstream str; + str << frames; + return str.str(); + } + + case TIME_FORMAT_SMPTE: + return framesToSmpte( frames, ( int )m_framerate ); + + default: + return toString( format ); + } +} + +int MediaClippingTime::getFrames() +{ + return ( int )( m_framerate * getResolvedOffset() / 1000.0 + 0.5 ); +} + +} // namespace --- smilutils-0.3.0/libkino/smiltime.h +++ smilutils-0.3.0/libkino/smiltime.h @@ -0,0 +1,149 @@ +/* +* smiltime.h -- W3C SMIL2 Time value parser +* Copyright (C) 2003-2007 Dan Dennedy <dan@dennedy.org> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +#ifndef _SMILTIME_H +#define _SMILTIME_H + +#include <string> + +using namespace std; + +namespace SMIL +{ + +class Time +{ +public: + typedef enum { + SMIL_TIME_INDEFINITE = 0, + SMIL_TIME_OFFSET, + SMIL_TIME_SYNC_BASED, // not implemented + SMIL_TIME_EVENT_BASED, // not implemented + SMIL_TIME_WALLCLOCK, // not implemented + SMIL_TIME_MEDIA_MARKER, // not implemented + SMIL_TIME_REPEAT, // not implemented + SMIL_TIME_ACCESSKEY, //not implemented + } TimeType; + + typedef enum { + TIME_FORMAT_NONE = 0, + TIME_FORMAT_FRAMES, + TIME_FORMAT_SMPTE, + TIME_FORMAT_CLOCK, + TIME_FORMAT_MS, + TIME_FORMAT_S, + TIME_FORMAT_MIN, + TIME_FORMAT_H + } TimeFormat; + + +protected: + // internally stored in milliseconds + long timeValue; + long offset; + bool indefinite; + bool resolved; + bool syncbaseBegin; + TimeType timeType; + +public: + Time(); + Time( long time ); + Time( string time ); + virtual ~Time() + {} + + virtual void parseTimeValue( string time ); + + long getTimeValue() + { + return timeValue; + } + TimeType getTimeType() + { + return timeType; + } + long getOffset() + { + return offset; + } + + bool operator< ( Time& time ); + bool operator==( Time& time ); + bool operator> ( Time& time ); + bool isResolved() + { + return resolved; + } + long getResolvedOffset(); + bool isNegative(); + bool isIndefinite() + { + return indefinite; + } + virtual string toString( TimeFormat format = TIME_FORMAT_CLOCK ); + virtual string serialise(); + +protected: + long parseClockValue( string time ); + + +private: + void setTimeValue( Time& source ); + +}; + + +class MediaClippingTime : public Time +{ +public: + typedef enum { + SMIL_SUBFRAME_NONE, + SMIL_SUBFRAME_0, + SMIL_SUBFRAME_1 + } SubframeType; + + MediaClippingTime(); + MediaClippingTime( float framerate ); + MediaClippingTime( string time, float framerate ); + virtual ~MediaClippingTime() + {} + void setFramerate( float framerate ); + virtual void parseValue( string time ); + void parseSmpteValue( string time ); + + virtual string toString( TimeFormat format = TIME_FORMAT_SMPTE ); + virtual string serialise(); + + string parseValueToString( string time, TimeFormat format ); + string parseFramesToString( int frames, TimeFormat format ); + int getFrames(); + +private: + float m_framerate; + bool m_isSmpteValue; + SubframeType m_subframe; +}; + +string framesToSmpte( int frames, int fps ); + +} // namespace + +#endif --- smilutils-0.3.0/libkino/stringutils.cc +++ smilutils-0.3.0/libkino/stringutils.cc @@ -0,0 +1,140 @@ +/* +* stringutils.cc -- C++ STL string functions +* Copyright (C) 2003-2007 Dan Dennedy <dan@dennedy.org> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sstream> +using std::ostringstream; +#include <stdio.h> + +#include "stringutils.h" + +string StringUtils::replaceAll ( string haystack, string needle, string s ) +{ + string::size_type pos = 0; + while ( ( pos = haystack.find ( needle, pos ) ) != string::npos ) + { + haystack.erase ( pos, needle.length() ); + haystack.insert ( pos, s ); + pos += s.length(); + } + return haystack; +} + +string StringUtils::stripWhite ( string s ) +{ + ostringstream dest; + char c; + for ( string::size_type pos = 0; pos < s.size(); ++pos ) + { + c = s.at( pos ); + if ( c != 0x20 && c != 0x09 && c != 0x0d && c != 0x0a ) + dest << c; + } + return dest.str(); +} + + +bool StringUtils::begins( string source, string sub ) +{ + return ( source.substr ( 0, sub.length() ) == sub ); +} + +bool StringUtils::ends( string source, string sub ) +{ + if ( sub.length() >= source.length() ) + return false; + return ( source.substr( source.length() - sub.length() ) == sub ); +} + +string StringUtils::ltos( long num ) +{ + char s[ 81 ]; + + sprintf ( s, "%ld", num ); + return string( s ); +} + +string StringUtils::itos( int num ) +{ + char s[ 81 ]; + + sprintf ( s, "%d", num ); + return string( s ); +} + +/** Split the string by delimiter and return each resultant string in items. + + Note that the items vector is not cleaned betweeen iterations. +*/ + +int StringUtils::split( const string &input, const string &delimiter, vector< string > &items, const bool clean ) +{ + int delimiter_size = delimiter.size(); + int input_size = input.size(); + + // Find the first delimiter + int position = 0; + int end_position = input.find( delimiter, 0 ); + + // While we have a valid position + while ( end_position >= position ) + { + // Obtain the substr and push if valid + string s = input.substr( position, end_position - position ); + if ( !clean || s.size() > 0 ) + items.push_back( s ); + + // Find the next delimiter + position = end_position + delimiter_size; + end_position = input.find( delimiter, position ); + } + + // Obtain the substr of what's left and push if valid + string s = input.substr( position, input_size - position ); + if ( !clean || s.size() > 0 ) + items.push_back( s ); + + // Return the number of items found + return items.size(); +} + +/** Join the contents of items with the given delimiter. + + Note that the no leading or trailing delimters are output. +*/ + +string StringUtils::join( vector< string >&items, const string &delimiter ) +{ + string output( "" ); + + // Loop through the items + for ( vector< string >::iterator item = items.begin( ); item != items.end( ); item ++ ) + { + if ( item == items.begin() ) + output += *item; + else + output += delimiter + *item; + } + + return string( output ); +} --- smilutils-0.3.0/libkino/stringutils.h +++ smilutils-0.3.0/libkino/stringutils.h @@ -0,0 +1,43 @@ +/* +* stringutils.h -- C++ STL string functions +* Copyright (C) 2003-2007 Dan Dennedy <dan@dennedy.org> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +*/ + +#ifndef _STRINGUTILS_H +#define _STRINGUTILS_H + +#include <string> +#include <vector> +using std::string; +using std::vector; + +class StringUtils +{ +public: + static string replaceAll ( string haystack, string needle, string s ); + static string stripWhite ( string s ); + static bool begins ( string source, string sub ); + static bool ends ( string source, string sub ); + static string itos ( int num ); + static string ltos ( long num ); + static int split( const string &input, const string &delimiter, vector< string > &items, const bool clean = true ); + static string join( vector< string >&items, const string &delimiter ); +}; + +#endif +
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor