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

smiltime.cc

Go to the documentation of this file.
00001 /*
00002 * smiltime.cc -- W3C SMIL2 Time value parser
00003 * Copyright (C) 2003-2007 Dan Dennedy <dan@dennedy.org>
00004 *
00005 * This program is free software; you can redistribute it and/or modify
00006 * it under the terms of the GNU General Public License as published by
00007 * the Free Software Foundation; either version 2 of the License, or
00008 * (at your option) any later version.
00009 *
00010 * This program is distributed in the hope that it will be useful,
00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013 * GNU General Public License for more details.
00014 *
00015 * You should have received a copy of the GNU General Public License
00016 * along with this program; if not, write to the Free Software Foundation,
00017 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00018 *
00019 */
00020 
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024 
00025 #include "smiltime.h"
00026 
00027 #include <iostream>
00028 #include <iomanip>
00029 #include <sstream>
00030 
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033 #include <math.h>
00034 
00035 #include "stringutils.h"
00036 
00037 namespace SMIL
00038 {
00039 
00040 Time::Time()
00041 {
00042     Time( 0L );
00043     indefinite = true;
00044     timeType = SMIL_TIME_INDEFINITE;
00045     resolved = true;
00046 }
00047 
00048 
00049 Time::Time( long time ) :
00050     timeValue( time ), 
00051     offset( 0 ),
00052     indefinite( false ), 
00053     resolved( true ), 
00054     syncbaseBegin( false ),
00055     timeType( SMIL_TIME_OFFSET )
00056 {
00057 }
00058 
00059 
00060 Time::Time( string time )
00061 {
00062     Time( 0L );
00063     parseTimeValue( time );
00064 }
00065 
00066 void Time::parseTimeValue( string time )
00067 {
00068     time = StringUtils::stripWhite( time );
00069 
00070     resolved = false;
00071 
00072     if ( StringUtils::begins ( time, "indefinite" ) || time.empty() || time.size() == 0 )
00073     {
00074         indefinite = true;
00075         timeType = SMIL_TIME_INDEFINITE;
00076         resolved = true;
00077         //cerr << "smil: this is an indefinite time value" << endl;
00078     }
00079     else if ( time.at( 0 ) == '+' || time.at( 0 ) == '-' )
00080     {
00081         //cerr << "smil: this is an offset time value" << endl;
00082         timeValue = parseClockValue( time.substr( 1 ) );
00083         if ( time.at( 0 ) == '-' )
00084             timeValue *= -1;
00085         timeType = SMIL_TIME_OFFSET;
00086         resolved = true;
00087         indefinite = false;
00088     }
00089     else if ( StringUtils::begins ( time, "wallclock(" ) )
00090     {
00091         //parseWallclockValue(time);
00092         timeType = SMIL_TIME_WALLCLOCK;
00093         resolved = true;
00094         indefinite = false;
00095         //cerr << "smil: this is a wallclock time value" << endl;
00096     }
00097     else if ( StringUtils::begins ( time, "accesskey(" ) )
00098     {
00099         timeType = SMIL_TIME_ACCESSKEY;
00100         //cerr << "smil: this is an accesskey   time value" << endl;
00101     }
00102     else
00103     {
00104         std::ostringstream token;
00105         char c;
00106         string::size_type pos = 0;
00107         string base;
00108         for ( ; pos < time.size(); ++pos )
00109         {
00110             c = time.at( pos );
00111             if ( c == '+' || c == '-' )
00112             {
00113                 string symbol = token.str();
00114                 token.str( string() );
00115                 //cerr << "smil: parsed symbol token '" << symbol << "'" << endl;
00116 
00117                 if ( symbol == string( "begin" ) )
00118                 {
00119                     //cerr << "smil: this is a sync based time value" << endl;
00120                     syncbaseBegin = true;
00121                     timeType = SMIL_TIME_SYNC_BASED;
00122                 }
00123                 else if ( symbol == string( "end" ) )
00124                 {
00125                     //cerr << "smil: this is a sync based time value" << endl;
00126                     syncbaseBegin = false;
00127                     timeType = SMIL_TIME_SYNC_BASED;
00128                 }
00129                 else if ( StringUtils::begins( symbol, "marker(" ) )
00130                 {
00131                     //cerr << "smil: this is a media marker time value" << endl;
00132                     //parseMediaMarkerValue(base, token); // base must not be empty
00133                     timeType = SMIL_TIME_MEDIA_MARKER;
00134                 }
00135                 else if ( StringUtils::begins( symbol, "repeat(" ) )
00136                 {
00137                     //cerr << "smil: this is an event repeat time value" << endl;
00138                     //parseRepeatValue(base, token); // base can be empty := current element
00139                     timeType = SMIL_TIME_REPEAT;
00140                 }
00141                 else
00142                 {
00143                     //cerr << "smil: this is an event based time value" << endl;
00144                     //parseEventValue( base, token ); // base can be empty := current element
00145                     timeType = SMIL_TIME_EVENT_BASED;
00146                 }
00147                 offset = parseClockValue( time.substr( pos + 1 ) );
00148                 if ( c == '-' )
00149                     offset *= -1;
00150                 break;
00151             }
00152             else if ( c == '.' && ( pos == 0 || time.at( pos - 1 ) != '\\' ) )
00153             {
00154                 base = token.str();
00155                 token.str( string() );
00156                 //cerr << "smil: parsed base token '" << base << "'" << endl;
00157             }
00158             else
00159             {
00160                 token << c;
00161             }
00162         }
00163         string remaining = token.str();
00164         //cerr << "smil: parsed remaining token '" << remaining << "'" << endl;
00165         if ( !remaining.empty() )
00166         {
00167             //cerr << "smil: this is an offset time value" << endl;
00168             offset = parseClockValue( time );
00169             timeType = SMIL_TIME_OFFSET;
00170             resolved = true;
00171             indefinite = false;
00172         }
00173     }
00174 }
00175 
00176 
00177 static inline
00178 string getFraction( string& time )
00179 {
00180     string::size_type pos;
00181     string fraction;
00182     if ( ( pos = time.find( '.' ) ) != string::npos )
00183     {
00184         fraction = time.substr( pos + 1 );
00185         //cerr << "smil: fraction = " << fraction << endl;
00186         time = time.substr( 0, pos );
00187     }
00188     return fraction;
00189 }
00190 
00191 
00192 long Time::parseClockValue( string time )
00193 {
00194     long total = 0;
00195     string hours;
00196     string minutes;
00197     string seconds;
00198     string milliseconds;
00199     string::size_type pos1, pos2;
00200 
00201     if ( ( pos1 = time.find( ':' ) ) != string::npos )
00202     {
00203         if ( ( pos2 = time.find( ':', pos1 + 1 ) ) != string::npos )
00204         {
00205             //parse Full-clock-value
00206             //cerr << "smil: parsing Full clock value " << time << endl;
00207             hours = time.substr( 0, pos1 );
00208             time = time.substr( pos1 + 1 );
00209             pos1 = pos2 - pos1 - 1;
00210         }
00211         //parse Partial-clock-value
00212         //cerr << "smil: parsing Partial clock value " << time << endl;
00213         if ( ( pos2 = time.find( '.' ) ) != string::npos )
00214         {
00215             milliseconds = time.substr( pos2 + 1, 3 );
00216             time = time.substr( 0, pos2 );
00217         }
00218         minutes = time.substr( 0, pos1 );
00219         seconds = time.substr( pos1 + 1 );
00220     }
00221     else
00222     {
00223         //parse Timecount-value
00224         //cerr << "smil: parsing Timecount value " << time << endl;
00225         if ( StringUtils::ends( time, "h" ) )
00226         {
00227             total = ( long ) ( atof( getFraction( time ).c_str() ) * 3600 );
00228             hours = time;
00229         }
00230         else if ( StringUtils::ends( time, "min" ) )
00231         {
00232             total = ( long ) ( atof( getFraction( time ).c_str() ) * 60 );
00233             minutes = time;
00234         }
00235         else if ( StringUtils::ends( time, "ms" ) )
00236         {
00237             total = ( long ) ( atof( time.c_str() ) + 0.5 );
00238         }
00239         else
00240         {
00241             total = atol( getFraction( time ).c_str() );
00242             seconds = time;
00243         }
00244     }
00245     //cerr << "smil: subtotal = " << total << ", h = " << atol(hours.c_str()) <<
00246     //  ", m = " << atol(minutes.c_str()) << ", s = " << atol(seconds.c_str()) <<
00247     //  ", ms = " << (long) (atof(milliseconds.c_str()) * 1000) << endl;
00248     total += ( atol( hours.c_str() ) * 3600 + atol( minutes.c_str() ) * 60 +
00249                atol( seconds.c_str() ) ) * 1000 + atol( milliseconds.c_str() );
00250     return total;
00251 }
00252 
00253 
00254 long Time::getResolvedOffset()
00255 {
00256     return ( resolved ? ( timeValue + offset ) : 0 );
00257 }
00258 
00259 bool Time::isNegative()
00260 {
00261     return ( resolved && !indefinite && ( getResolvedOffset() < 0 ) );
00262 }
00263 
00264 
00265 bool Time::operator< ( Time& time )
00266 {
00267     return !( ( *this > time ) || ( *this == time ) );
00268 }
00269 
00270 
00271 bool Time::operator==( Time& time )
00272 {
00273     return (
00274                ( this->isIndefinite() && time.isIndefinite() ) ||
00275                ( this->getResolvedOffset() == time.getResolvedOffset() )
00276            );
00277 }
00278 
00279 
00280 bool Time::operator> ( Time& time )
00281 {
00282     return (
00283                ( !resolved ) ||
00284                ( indefinite && time.isResolved() && !time.isIndefinite() ) ||
00285                ( resolved && time.isResolved() && this->getResolvedOffset() > time.getResolvedOffset() )
00286            );
00287 }
00288 
00289 
00290 void Time::setTimeValue( Time& source )
00291 {
00292     resolved = source.isResolved();
00293     indefinite = source.isIndefinite();
00294     timeValue = source.getTimeValue();
00295 }
00296 
00297 
00298 string Time::toString( TimeFormat format )
00299 {
00300     long ms = getResolvedOffset();
00301     ostringstream str;
00302     
00303     if ( indefinite )
00304     {
00305         str << "indefinite";
00306     }
00307     else if ( !resolved )
00308     {
00309         str << "unresolved";
00310     }
00311     else switch( format )
00312     {
00313         case TIME_FORMAT_CLOCK:
00314         {
00315             int hh = ( ms / 3600000 );
00316             ms -= hh * 3600000;
00317             int mm = ( ms / 60000 );
00318             ms -= mm * 60000;
00319             int ss = ms / 1000;
00320             ms -= ss * 1000;
00321     
00322             str << std::setfill( '0' ) << std::setw( 2 ) << hh << 
00323             ":" << std::setfill( '0' ) << std::setw( 2 ) << mm << 
00324             ":" << std::setfill( '0' ) << std::setw( 2 ) << ss << "." <<
00325             std::setfill( '0' ) << std::setw( 3 ) << ms;
00326             break;
00327         }
00328         case TIME_FORMAT_MS:
00329             str << ms << "ms";
00330             break;
00331         
00332         case TIME_FORMAT_S:
00333             str << ( ms / 1000 ) << "." << 
00334             std::setfill( '0' ) << std::setw( 3 ) << ( ms % 1000 );
00335             break;
00336         
00337         case TIME_FORMAT_MIN:
00338             str << ( ms / 60000 ) << "." << 
00339                 std::setfill( '0' ) << std::setw( 4 ) << 
00340                 floor( ( float )( ms % 60000 ) / 6 + 0.5 ) << "min";
00341             break;
00342         
00343         case TIME_FORMAT_H:
00344             str << ( ms / 3600000 ) << "." << 
00345                 std::setfill( '0' ) << std::setw( 5 ) << 
00346                 floor( ( float )( ms % 3600000 ) / 36 + 0.5 ) << "h";
00347             break;
00348         
00349         default:
00350             break;
00351     }
00352     return str.str();
00353 
00354 }
00355 
00356 string Time::serialise()
00357 {
00358     return toString();
00359 }
00360 
00361 
00362 MediaClippingTime::MediaClippingTime( ) :
00363     Time( 0L ),
00364     m_framerate( 0.0 ), 
00365     m_isSmpteValue( false ), 
00366     m_subframe( SMIL_SUBFRAME_NONE )
00367 {
00368 }
00369 
00370 MediaClippingTime::MediaClippingTime( float framerate ) :
00371     Time( 0L ),
00372     m_framerate( framerate ),
00373     m_isSmpteValue( false ), 
00374     m_subframe( SMIL_SUBFRAME_NONE )
00375 {
00376 }
00377 
00378 MediaClippingTime::MediaClippingTime( string time, float framerate ) :
00379     Time( 0L ),
00380     m_framerate( framerate ),
00381     m_isSmpteValue( false ), 
00382     m_subframe( SMIL_SUBFRAME_NONE )
00383 {
00384     parseValue( time );
00385 }
00386 
00387 
00388 void MediaClippingTime::setFramerate( float framerate )
00389 {
00390     m_framerate = framerate;
00391 }
00392 
00393 
00394 void MediaClippingTime::parseValue( string time )
00395 {
00396     time = StringUtils::stripWhite( time );
00397     //cerr << "smil: parsing media clipping time " << time << endl;
00398     if ( StringUtils::begins( time, "smpte=" ) ||
00399             StringUtils::begins( time, "smpte-30-drop=" ) ||
00400             StringUtils::begins( time, "smpte-25=" ) )
00401     {
00402         parseSmpteValue( time.substr( time.find( '=' ) + 1 ) );
00403     }
00404     else if ( time.find( '=' ) != string::npos )    // discard npt=
00405     {
00406         parseTimeValue( time.substr( time.find( '=' ) + 1 ) );
00407     } else
00408     {
00409         parseTimeValue( time );
00410     }
00411 }
00412 
00413 void MediaClippingTime::parseSmpteValue( string time )
00414 {
00415     string hours;
00416     string minutes;
00417     string seconds;
00418     string frames;
00419     string::size_type pos;
00420 
00421     if ( m_framerate == 0 )
00422         return;
00423     
00424     m_isSmpteValue = true;
00425 
00426     if ( ( pos = time.find( ':' ) ) != string::npos || ( pos = time.find( ';' ) ) != string::npos )
00427     {
00428         //cerr << "smil: parsing HH SMPTE value " << time << endl;
00429         hours = time.substr( 0, pos );
00430         time = time.substr( pos + 1 );
00431 
00432         if ( ( pos = time.find( ':' ) ) != string::npos || ( pos = time.find( ';' ) ) != string::npos )
00433         {
00434             //cerr << "smil: parsing MM SMPTE value " << time << endl;
00435             minutes = time.substr( 0, pos );
00436             time = time.substr( pos + 1 );
00437 
00438             if ( ( pos = time.find( ':' ) ) != string::npos || ( pos = time.find( ';' ) ) != string::npos )
00439             {
00440                 //cerr << "smil: parsing SS SMPTE value " << time << endl;
00441                 seconds = time.substr( 0, pos );
00442                 time = time.substr( pos + 1 );
00443 
00444                 if ( ( pos = time.find( '.' ) ) != string::npos )
00445                 {
00446                     //cerr << "smil: parsing FF SMPTE value " << time << endl;
00447                     frames = time.substr( 0, pos );
00448                     switch ( time.at( pos + 1 ) )
00449                     {
00450                     case '0' :
00451                         m_subframe = SMIL_SUBFRAME_0;
00452                         break;
00453                     case '1' :
00454                         m_subframe = SMIL_SUBFRAME_1;
00455                         break;
00456                     default:
00457                         m_subframe = SMIL_SUBFRAME_NONE;
00458                         break;
00459                     }
00460                 }
00461                 else
00462                 {
00463                     frames = time;
00464                 }
00465             }
00466             else
00467             {
00468                 // minutes, seconds, and frames only
00469                 frames = time;
00470                 seconds = minutes;
00471                 minutes = hours;
00472                 hours = "";
00473             }
00474         }
00475         else
00476         {
00477             // frames and seconds only
00478             frames = time;
00479             seconds = hours;
00480             hours = "";
00481         }
00482     }
00483     else
00484     {
00485         // frames only
00486         frames = time;
00487     }
00488     
00489     //cerr << "hh = " << hours << ", mm = " << minutes << ", ss = " << seconds << ", ff = " << frames << endl;
00490     timeValue = ( atol( hours.c_str() ) * 3600 + atol( minutes.c_str() ) * 60 +
00491                   atol( seconds.c_str() ) ) * 1000 + 
00492                   ( long )( atof( frames.c_str() ) / m_framerate * 1000 + 0.5 );
00493 
00494     resolved = true;
00495     indefinite = false;
00496 }
00497 
00498 string MediaClippingTime::toString( TimeFormat format )
00499 {
00500     if ( format == TIME_FORMAT_SMPTE )
00501     {
00502         if ( indefinite )
00503             return "indefinite";
00504         else if ( !resolved )
00505             return "unresolved";
00506         else
00507         {
00508             long ms = getResolvedOffset();
00509             int hh = ( ms / 3600000 );
00510             ms -= hh * 3600000;
00511             int mm = ( ms / 60000 );
00512             ms -= mm * 60000;
00513             int ss = ms / 1000;
00514             ms -= ss * 1000;
00515 
00516             ostringstream str;
00517             str << hh << ":" << std::setfill( '0' ) << std::setw( 2 ) << 
00518                 mm << ":" << std::setfill( '0' ) << std::setw( 2 ) << ss << 
00519                 ( m_framerate == 25.0 ? ":" : ";" ) << 
00520                 std::setfill( '0' ) << std::setw( 2 ) << 
00521                 floor( m_framerate * ms / 1000.0 + 0.5 );
00522             if ( m_subframe == SMIL_SUBFRAME_0 )
00523                 str << ".0";
00524             else if ( m_subframe == SMIL_SUBFRAME_1 )
00525                 str << ".1";
00526 
00527             return str.str();
00528         }
00529     }
00530     else if ( format == TIME_FORMAT_FRAMES )
00531     {
00532         std::ostringstream str;
00533         str << getFrames();
00534         return str.str();
00535     }
00536     else
00537     {
00538         return Time::toString( format );
00539     }
00540 }
00541 
00542 string MediaClippingTime::serialise()
00543 {
00544     std::string s;
00545     if ( m_isSmpteValue )
00546     {
00547         if ( m_framerate == 25.0 )
00548             s = "smpte-25=";
00549         else
00550             s = "smpte=";
00551         
00552         return s + toString();
00553     }
00554     return Time::toString();
00555 }
00556 
00557 
00558 string framesToSmpte( int frames, int fps )
00559 {
00560     char s[ 12 ];
00561     int hours, mins, secs;
00562     int cur = frames;
00563 
00564     if ( fps == 29 )
00565         fps = 30;
00566 
00567     if ( frames == 0 )
00568     {
00569         hours = 0;
00570         mins = 0;
00571         secs = 0;
00572     }
00573     else
00574     {
00575         /* NTSC drop-frame */
00576         if ( fps == 30 )
00577         {
00578             int max_frames = cur;
00579             for ( int j = 1800; j <= max_frames; j += 1800 )
00580             {
00581                 if ( j % 18000 )
00582                 {
00583                     max_frames += 2;
00584                     cur += 2;
00585                 }
00586             }
00587         }
00588         hours = cur / ( fps * 3600 );
00589         cur -= hours * ( fps * 3600 );
00590         mins = cur / ( fps * 60 );
00591         cur -= mins * ( fps * 60 );
00592         secs = cur / fps;
00593         cur -= secs * fps;
00594     }
00595 
00596     snprintf( s, 12, "%2.2d:%2.2d:%2.2d%s%2.2d", hours, mins, secs, ( fps == 30 ) ? ";" : ":", cur );
00597     return string( s );
00598 }
00599 
00600 
00601 string MediaClippingTime::parseValueToString( string time, TimeFormat format )
00602 {
00603     offset = 0;
00604     timeValue = 0;
00605     if ( format == TIME_FORMAT_NONE || format == TIME_FORMAT_FRAMES || format == TIME_FORMAT_SMPTE )
00606         parseSmpteValue( time );
00607     else
00608         parseValue( time );
00609     return toString( format );
00610 }
00611 
00612 
00613 string MediaClippingTime::parseFramesToString( int frames, TimeFormat format )
00614 {
00615     if ( m_framerate == 0 )
00616         return "";
00617     offset = 0;
00618     timeValue = ( long )( 1000.0 * frames / m_framerate + 0.5 );
00619     resolved = true;
00620     indefinite = false;
00621     
00622     switch ( format )
00623     {
00624         case TIME_FORMAT_NONE:
00625             return "";
00626         
00627         case TIME_FORMAT_FRAMES:
00628         {
00629             std::ostringstream str;
00630             str << frames;
00631             return str.str();
00632         }
00633         
00634         case TIME_FORMAT_SMPTE:
00635             return framesToSmpte( frames, ( int )m_framerate );
00636 
00637         default:
00638             return toString( format );
00639     }
00640 }
00641 
00642 int MediaClippingTime::getFrames()
00643 {
00644     return ( int )( m_framerate * getResolvedOffset() / 1000.0 + 0.5 );
00645 }
00646 
00647 } // namespace

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