File indexing completed on 2024-12-08 04:27:14
0001 /* 0002 SPDX-FileCopyrightText: 2003 Jason Wood <jasonwood@blueyonder.co.uk> 0003 SPDX-FileCopyrightText: 2010 Jean-Baptiste Mardelle <jb@kdenlive.org> 0004 0005 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 /* 0009 0010 Timecode calculation code for reference 0011 If we ever use Quicktime timecode with 50.94 Drop frame, keep in mind that there is a bug in the Quicktime code 0012 0013 //CONVERT A FRAME NUMBER TO DROP FRAME TIMECODE 0014 //Code by David Heidelberger, adapted from Andrew Duncan 0015 //Given an int called framenumber and a double called framerate 0016 //Framerate should be 29.97, 59.94, or 23.976, otherwise the calculations will be off. 0017 0018 int d; 0019 int m; 0020 0021 int dropFrames = round(framerate * .066666); //Number of frames to drop on the minute marks is the nearest integer to 6% of the framerate 0022 int framesPerHour = round(framerate*60*60); //Number of frames in an hour 0023 int framesPer24Hours = framesPerHour*24; //Number of frames in a day - timecode rolls over after 24 hours 0024 int framesPer10Minutes = round(framerate * 60 * 10); //Number of frames per ten minutes 0025 int framesPerMinute = round(framerate)*60)- dropFrames; //Number of frames per minute is the round of the framerate * 60 minus the number of dropped frames 0026 0027 if (framenumber<0) //Negative time. Add 24 hours. 0028 { 0029 framenumber=framesPer24Hours+framenumber; 0030 } 0031 0032 //If framenumber is greater than 24 hrs, next operation will rollover clock 0033 framenumber = framenumber % framesPer24Hours; //% is the modulus operator, which returns a remainder. a % b = the remainder of a/b 0034 0035 d = framenumber\framesPer10Minutes; // \ means integer division, which is a/b without a remainder. Some languages you could use floor(a/b) 0036 m = framenumber % framesPer10Minutes; 0037 0038 if (m>1) 0039 { 0040 framenumber=framenumber + (dropFrames*9*d) + dropFrames*((m-dropFrames)\framesPerMinute); 0041 } 0042 else 0043 { 0044 framenumber = framenumber + dropFrames*9*d; 0045 } 0046 0047 int frRound = round(framerate); 0048 int frames = framenumber % frRound; 0049 int seconds = (framenumber \ frRound) % 60; 0050 int minutes = ((framenumber \ frRound) \ 60) % 60; 0051 int hours = (((framenumber \ frRound) \ 60) \ 60); 0052 0053 ------------------------------------------------------------------------------------ 0054 0055 //CONVERT DROP FRAME TIMECODE TO A FRAME NUMBER 0056 //Code by David Heidelberger, adapted from Andrew Duncan 0057 //Given ints called hours, minutes, seconds, frames, and a double called framerate 0058 0059 int dropFrames = round(framerate*.066666); //Number of drop frames is 6% of framerate rounded to nearest integer 0060 int timeBase = round(framerate); //We don’t need the exact framerate anymore, we just need it rounded to nearest integer 0061 0062 int hourFrames = timeBase*60*60; //Number of frames per hour (non-drop) 0063 int minuteFrames = timeBase*60; //Number of frames per minute (non-drop) 0064 int totalMinutes = (60*hours) + minutes; //Total number of minutes 0065 int frameNumber = ((hourFrames * hours) + (minuteFrames * minutes) + (timeBase * seconds) + frames) - (dropFrames * (totalMinutes - (totalMinutes \ 10))); 0066 return frameNumber; 0067 0068 */ 0069 0070 #include "timecode.h" 0071 0072 #include "kdenlive_debug.h" 0073 0074 Timecode::Timecode(Formats format, double framesPerSecond) 0075 { 0076 setFormat(framesPerSecond, format); 0077 } 0078 0079 Timecode::~Timecode() = default; 0080 0081 void Timecode::setFormat(double framesPerSecond, Formats format) 0082 { 0083 m_displayedFramesPerSecond = qRound(framesPerSecond); 0084 m_dropFrameTimecode = qFuzzyCompare(framesPerSecond, 30000.0 / 1001.0); 0085 m_format = format; 0086 m_realFps = framesPerSecond; 0087 if (m_dropFrameTimecode) { 0088 m_dropFrames = round(m_realFps * .066666); // Number of frames to drop on the minute marks is the nearest integer to 6% of the framerate 0089 m_framesPer10Minutes = round(m_realFps * 600); // Number of frames per ten minutes 0090 } 0091 } 0092 0093 Timecode::Formats Timecode::format() const 0094 { 0095 return m_format; 0096 } 0097 0098 double Timecode::fps() const 0099 { 0100 return m_realFps; 0101 } 0102 0103 const QString Timecode::mask(const GenTime &t) const 0104 { 0105 if (m_realFps > 100) { 0106 if (t < GenTime()) { 0107 if (m_dropFrameTimecode) { 0108 return QStringLiteral("#99:99:99,999"); 0109 } 0110 return QStringLiteral("#99:99:99:999"); 0111 } 0112 if (m_dropFrameTimecode) { 0113 return QStringLiteral("99:99:99,999"); 0114 } 0115 return QStringLiteral("99:99:99:999"); 0116 } 0117 if (t < GenTime()) { 0118 if (m_dropFrameTimecode) { 0119 return QStringLiteral("#99:99:99,99"); 0120 } 0121 return QStringLiteral("#99:99:99:99"); 0122 } 0123 if (m_dropFrameTimecode) { 0124 return QStringLiteral("99:99:99,99"); 0125 } 0126 return QStringLiteral("99:99:99:99"); 0127 } 0128 0129 QString Timecode::reformatSeparators(QString duration) const 0130 { 0131 if (m_dropFrameTimecode) { 0132 return duration.replace(8, 1, ';'); 0133 } 0134 return duration.replace(8, 1, ':'); 0135 } 0136 0137 int Timecode::getFrameCount(const QString &duration) const 0138 { 0139 if (duration.isEmpty()) { 0140 return 0; 0141 } 0142 int hours, minutes, seconds, frames; 0143 int offset = 0; 0144 if (duration.at(0) == '-') { 0145 offset = 1; 0146 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0147 hours = duration.midRef(1, 2).toInt(); 0148 #else 0149 hours = QStringView(duration).mid(1, 2).toInt(); 0150 #endif 0151 } else { 0152 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0153 hours = duration.leftRef(2).toInt(); 0154 #else 0155 hours = QStringView(duration).left(2).toInt(); 0156 #endif 0157 } 0158 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0159 minutes = duration.midRef(3 + offset, 2).toInt(); 0160 seconds = duration.midRef(6 + offset, 2).toInt(); 0161 frames = duration.rightRef(duration.length() - 9 - offset).toInt(); 0162 #else 0163 minutes = QStringView(duration).mid(3 + offset, 2).toInt(); 0164 seconds = QStringView(duration).mid(6 + offset, 2).toInt(); 0165 frames = QStringView(duration).right(duration.length() - 9 - offset).toInt(); 0166 #endif 0167 if (m_dropFrameTimecode) { 0168 // CONVERT DROP FRAME TIMECODE TO A FRAME NUMBER 0169 // Code by David Heidelberger, adapted from Andrew Duncan 0170 // Given ints called hours, minutes, seconds, frames, and a double called framerate 0171 0172 int totalMinutes = (60 * hours) + minutes; // Total number of minutes 0173 int frameNumber = 0174 ((m_displayedFramesPerSecond * 3600 * hours) + (m_displayedFramesPerSecond * 60 * minutes) + (m_displayedFramesPerSecond * seconds) + frames) - 0175 (m_dropFrames * (totalMinutes - floor(totalMinutes / 10))); 0176 return frameNumber; 0177 } 0178 return qRound((hours * 3600.0 + minutes * 60.0 + seconds) * m_realFps + frames); 0179 } 0180 0181 QString Timecode::getDisplayTimecode(const GenTime &time, bool frameDisplay) const 0182 { 0183 if (frameDisplay) { 0184 return QString::number((int)time.frames(m_realFps)); 0185 } 0186 return getTimecode(time); 0187 } 0188 0189 QString Timecode::getTimecode(const GenTime &time) const 0190 { 0191 switch (m_format) { 0192 case HH_MM_SS_FF: 0193 return getTimecodeHH_MM_SS_FF(time); 0194 break; 0195 case HH_MM_SS_HH: 0196 return getTimecodeHH_MM_SS_HH(time); 0197 break; 0198 case Frames: 0199 return getTimecodeFrames(time); 0200 break; 0201 case Seconds: 0202 return getTimecodeSeconds(time); 0203 break; 0204 default: 0205 qCWarning(KDENLIVE_LOG) << "Unknown timecode format specified, defaulting to HH_MM_SS_FF" << '\n'; 0206 return getTimecodeHH_MM_SS_FF(time); 0207 } 0208 } 0209 0210 const QString Timecode::getDisplayTimecodeFromFrames(int frames, bool frameDisplay) const 0211 { 0212 if (frameDisplay) { 0213 return QString::number(frames); 0214 } 0215 return getTimecodeHH_MM_SS_FF(frames); 0216 } 0217 0218 const QString Timecode::getTimecodeFromFrames(int frames) const 0219 { 0220 return getTimecodeHH_MM_SS_FF(frames); 0221 } 0222 0223 // static 0224 QString Timecode::getStringTimecode(int frames, const double &fps, bool showFrames) 0225 { 0226 // Returns the timecode in an hh:mm:ss format 0227 0228 bool negative = false; 0229 if (frames < 0) { 0230 negative = true; 0231 frames = qAbs(frames); 0232 } 0233 0234 auto seconds = (int)(frames / fps); 0235 int frms = frames % qRound(fps); 0236 int minutes = seconds / 60; 0237 seconds = seconds % 60; 0238 int hours = minutes / 60; 0239 minutes = minutes % 60; 0240 QString text = showFrames 0241 ? QString("%1:%2:%3.%4") 0242 .arg(hours, 2, 10, QLatin1Char('0')) 0243 .arg(minutes, 2, 10, QLatin1Char('0')) 0244 .arg(seconds, 2, 10, QLatin1Char('0')) 0245 .arg(frms, fps > 100 ? 3 : 2, 10, QLatin1Char('0')) 0246 : QString("%1:%2:%3").arg(hours, 2, 10, QLatin1Char('0')).arg(minutes, 2, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0')); 0247 if (negative) { 0248 text.prepend('-'); 0249 } 0250 return text; 0251 } 0252 0253 const QString Timecode::getTimecodeHH_MM_SS_FF(const GenTime &time) const 0254 { 0255 if (m_dropFrameTimecode) { 0256 return getTimecodeDropFrame(time); 0257 } 0258 return getTimecodeHH_MM_SS_FF((int)time.frames(m_realFps)); 0259 } 0260 0261 const QString Timecode::getTimecodeHH_MM_SS_FF(int frames) const 0262 { 0263 if (m_dropFrameTimecode) { 0264 return getTimecodeDropFrame(frames); 0265 } 0266 0267 bool negative = false; 0268 if (frames < 0) { 0269 negative = true; 0270 frames = qAbs(frames); 0271 } 0272 0273 int hours = frames / (m_realFps * 3600); 0274 frames -= floor(hours * 3600 * m_realFps); 0275 0276 int minutes = frames / (m_realFps * 60); 0277 frames -= floor(minutes * 60 * m_realFps); 0278 0279 int seconds = frames / m_realFps; 0280 frames -= ceil(seconds * m_realFps); 0281 QString text = QString("%1:%2:%3:%4") 0282 .arg(hours, 2, 10, QLatin1Char('0')) 0283 .arg(minutes, 2, 10, QLatin1Char('0')) 0284 .arg(seconds, 2, 10, QLatin1Char('0')) 0285 .arg(frames, m_realFps > 100 ? 3 : 2, 10, QLatin1Char('0')); 0286 if (negative) { 0287 text.prepend('-'); 0288 } 0289 return text; 0290 } 0291 0292 const QString Timecode::getTimecodeHH_MM_SS_HH(const GenTime &time) const 0293 { 0294 auto hundredths = (int)(time.seconds() * 100); 0295 0296 bool negative = false; 0297 if (hundredths < 0) { 0298 negative = true; 0299 hundredths = qAbs(hundredths); 0300 } 0301 0302 int seconds = hundredths / 100; 0303 hundredths = hundredths % 100; 0304 int minutes = seconds / 60; 0305 seconds = seconds % 60; 0306 int hours = minutes / 60; 0307 minutes = minutes % 60; 0308 0309 QString text = QString("%1:%2:%3%5%4") 0310 .arg(hours, 2, 10, QLatin1Char('0')) 0311 .arg(minutes, 2, 10, QLatin1Char('0')) 0312 .arg(seconds, 2, 10, QLatin1Char('0')) 0313 .arg(hundredths, 2, 10, QLatin1Char('0')) 0314 .arg(m_dropFrameTimecode ? QLatin1Char(',') : QLatin1Char(':')); 0315 if (negative) { 0316 text.prepend('-'); 0317 } 0318 return text; 0319 } 0320 0321 const QString Timecode::getTimecodeFrames(const GenTime &time) const 0322 { 0323 return QString::number((int)time.frames(m_realFps)); 0324 } 0325 0326 const QString Timecode::getTimecodeSeconds(const GenTime &time) const 0327 { 0328 return QString::number(time.seconds(), 'f'); 0329 } 0330 0331 const QString Timecode::getTimecodeDropFrame(const GenTime &time) const 0332 { 0333 return getTimecodeDropFrame((int)time.frames(m_realFps)); 0334 } 0335 0336 const QString Timecode::getTimecodeDropFrame(int framenumber) const 0337 { 0338 // CONVERT A FRAME NUMBER TO DROP FRAME TIMECODE 0339 // Based on code by David Heidelberger, adapted from Andrew Duncan 0340 // Given an int called framenumber and a double called framerate 0341 // Framerate should be 29.97, 59.94, or 23.976, otherwise the calculations will be off. 0342 0343 bool negative = false; 0344 if (framenumber < 0) { 0345 negative = true; 0346 framenumber = qAbs(framenumber); 0347 } 0348 0349 int d = floor(framenumber / m_framesPer10Minutes); 0350 int m = framenumber % m_framesPer10Minutes; 0351 0352 if (m > m_dropFrames) { 0353 framenumber += (m_dropFrames * 9 * d) + m_dropFrames * (floor((m - m_dropFrames) / (round(m_realFps * 60) - m_dropFrames))); 0354 } else { 0355 framenumber += m_dropFrames * 9 * d; 0356 } 0357 0358 int frames = framenumber % m_displayedFramesPerSecond; 0359 int seconds = (int)floor(framenumber / m_displayedFramesPerSecond) % 60; 0360 int minutes = (int)floor(floor(framenumber / m_displayedFramesPerSecond) / 60) % 60; 0361 int hours = floor(floor(floor(framenumber / m_displayedFramesPerSecond) / 60) / 60); 0362 0363 QString text = QString("%1:%2:%3,%4") 0364 .arg(hours, 2, 10, QLatin1Char('0')) 0365 .arg(minutes, 2, 10, QLatin1Char('0')) 0366 .arg(seconds, 2, 10, QLatin1Char('0')) 0367 .arg(frames, m_realFps > 100 ? 3 : 2, 10, QLatin1Char('0')); 0368 if (negative) { 0369 text.prepend('-'); 0370 } 0371 return text; 0372 }