1 /*********************************************************************** 2 * AUTHOR: David McPaul 3 * FILE: TimeCode.cpp 4 * DESCR: Handles all TimeCode functions. 5 ***********************************************************************/ 6 #include <TimeCode.h> 7 #include "MediaDebug.h" 8 #include <string.h> 9 10 status_t us_to_timecode(bigtime_t micros, int * hours, int * minutes, int * seconds, int * frames, const timecode_info * code) 11 { 12 int32 l_frames; 13 14 CALLED(); 15 16 if (code) { 17 // We have a valid timecode_info 18 switch (code->type) { 19 case B_TIMECODE_DEFAULT: // NTSC 20 case B_TIMECODE_30_DROP_2: // NTSC 21 l_frames = int32((((micros % 1000000) * 29.97) / 1000000) + (micros / 1000000 * 29.97)); 22 break; 23 case B_TIMECODE_30_DROP_4: // Brazil 24 l_frames = int32((((micros % 1000000) * 29.95) / 1000000) + (micros / 1000000 * 29.95)); 25 break; 26 default: 27 l_frames = (((micros % 1000000) * code->fps_div) / 1000000) + (micros / 1000000 * code->fps_div); 28 break; 29 }; 30 } else { 31 // Convert us to frames 32 l_frames = int32((((micros % 1000000) * 29.97) / 1000000) + (micros / 1000000 * 29.97)); 33 } 34 35 return frames_to_timecode(l_frames, hours, minutes, seconds, frames, code); 36 } 37 38 status_t timecode_to_us(int hours, int minutes, int seconds, int frames, bigtime_t * micros, const timecode_info * code) 39 { 40 int32 l_frames; 41 42 CALLED(); 43 44 if (timecode_to_frames(hours, minutes, seconds, frames, &l_frames, code) == B_OK) { 45 46 if (code) { 47 // We have a valid timecode_info 48 switch (code->type) { 49 case B_TIMECODE_DEFAULT: // NTSC 50 case B_TIMECODE_30_DROP_2: // NTSC 51 *micros = bigtime_t(l_frames * ((1000000 / 29.97) + 0.5)); 52 break; 53 case B_TIMECODE_30_DROP_4: // Brazil 54 *micros = bigtime_t(l_frames * ((1000000 / 29.95) + 0.5)); 55 break; 56 default: 57 *micros = l_frames * 1000000 / code->fps_div; 58 break; 59 }; 60 61 } else { 62 *micros = bigtime_t(l_frames * ((1000000 / 29.97) + 0.5)); 63 } 64 65 return B_OK; 66 } 67 return B_ERROR; 68 } 69 70 status_t frames_to_timecode(int32 l_frames, int * hours, int * minutes, int * seconds, int * frames, const timecode_info * code) 71 { 72 int fps_div; 73 int total_mins; 74 int32 extra_frames; 75 int32 extra_frames2; 76 77 CALLED(); 78 79 if (code) { 80 // We have a valid timecode_info so use it 81 fps_div = code->fps_div; 82 83 if (code->every_nth > 0) { 84 // Handle Drop Frames format 85 86 total_mins = l_frames / fps_div / 60; 87 88 // "every_nth" minute we gain "drop_frames" "except_nth" minute 89 extra_frames = code->drop_frames*(total_mins/code->every_nth) - code->drop_frames*(total_mins/code->except_nth); 90 l_frames += extra_frames; 91 92 total_mins = l_frames / fps_div / 60; 93 extra_frames2 = code->drop_frames*(total_mins/code->every_nth) - code->drop_frames*(total_mins/code->except_nth); 94 95 // Gaining frames may mean that we gain more frames so we keep adjusting until no more to adjust 96 while (extra_frames != extra_frames2) { 97 l_frames += extra_frames2 - extra_frames; 98 extra_frames = extra_frames2; 99 100 total_mins = l_frames / fps_div / 60; 101 extra_frames2 = code->drop_frames*(total_mins/code->every_nth) - code->drop_frames*(total_mins/code->except_nth); 102 } 103 104 // l_frames should now include all adjusted frames 105 } 106 } else { 107 // no timecode_info so set default NTSC :-( 108 fps_div = 30; // NTSC Default 109 total_mins = l_frames / fps_div / 60; 110 111 // "every_nth" minute we gain "drop_frames" "except_nth" minute 112 extra_frames = 2*(total_mins) - 2*(total_mins/10); 113 l_frames += extra_frames; 114 115 total_mins = l_frames / fps_div / 60; 116 extra_frames2 = 2*(total_mins) - 2*(total_mins/10); 117 118 // Gaining frames may mean that we gain more frames so we keep adjusting until no more to adjust 119 while (extra_frames != extra_frames2) { 120 l_frames += extra_frames2 - extra_frames; 121 extra_frames = extra_frames2; 122 123 total_mins = l_frames / fps_div / 60; 124 extra_frames2 = 2*(total_mins) - 2*(total_mins/10); 125 } 126 } 127 128 // convert frames to seconds leaving the remainder as frames 129 *seconds = l_frames / fps_div; // DIV 130 *frames = l_frames % fps_div; // MOD 131 132 // Normalise to Hours Minutes Seconds Frames 133 *minutes = *seconds / 60; 134 *seconds = *seconds % 60; 135 136 *hours = *minutes / 60; 137 *minutes = *minutes % 60; 138 139 return B_OK; 140 } 141 142 status_t timecode_to_frames(int hours, int minutes, int seconds, int frames, int32 * l_frames, const timecode_info * code) 143 { 144 int fps_div; 145 int total_mins; 146 int32 extra_frames; 147 148 CALLED(); 149 150 if (code) { 151 // We have a valid timecode_info 152 fps_div = code->fps_div; 153 154 total_mins = (hours * 60) + minutes; 155 *l_frames = (total_mins * 60) + seconds; 156 *l_frames = (*l_frames * fps_div) + frames; 157 158 // Adjust "every_nth" minute we lose "drop_frames" "except_nth" minute 159 if (code->every_nth > 0) { 160 extra_frames = code->drop_frames*(total_mins/code->every_nth) - code->drop_frames*(total_mins/code->except_nth); 161 162 *l_frames = *l_frames - extra_frames; 163 } 164 } else { 165 166 total_mins = (hours * 60) + minutes; 167 *l_frames = (total_mins * 60) + seconds; 168 *l_frames = (*l_frames * 30) + frames; 169 170 // Adjust "every_nth" minute we lose "drop_frames" "except_nth" minute 171 extra_frames = 2*(total_mins) - 2*(total_mins/10); 172 173 *l_frames = *l_frames - extra_frames; 174 } 175 176 return B_OK; 177 } 178 179 status_t get_timecode_description(timecode_type type, timecode_info * out_timecode) 180 { 181 CALLED(); 182 183 out_timecode->type = type; 184 strncpy(out_timecode->format,"%.2ih:%.2im:%.2is.%.2i",31); 185 out_timecode->every_nth = 0; 186 out_timecode->except_nth = 0; 187 188 switch (type) { 189 case B_TIMECODE_100: 190 strncpy(out_timecode->name,"100 FPS",31); 191 out_timecode->fps_div = 100; 192 break; 193 case B_TIMECODE_75: // CD 194 strncpy(out_timecode->name,"CD",31); 195 out_timecode->fps_div = 75; 196 break; 197 case B_TIMECODE_30: // MIDI 198 strncpy(out_timecode->name,"MIDI",31); 199 out_timecode->fps_div = 30; 200 break; 201 case B_TIMECODE_30_DROP_2: // NTSC 202 strncpy(out_timecode->name,"NTSC",31); 203 out_timecode->fps_div = 30; // This is supposed to be 29.97fps but can be simulated using the drop frame format below 204 out_timecode->drop_frames = 2; // Drop 2 frames 205 out_timecode->every_nth = 1; // every 1 minutes 206 out_timecode->except_nth = 10; // except every 10 minutes 207 break; 208 case B_TIMECODE_30_DROP_4: // Brazil 209 strncpy(out_timecode->name,"NTSC Brazil",31); 210 out_timecode->fps_div = 30; 211 out_timecode->drop_frames = 4; // Drop 4 frames 212 out_timecode->every_nth = 1; // every 1 minutes 213 out_timecode->except_nth = 10; // except every 10 minutes 214 break; 215 case B_TIMECODE_25: // PAL 216 strncpy(out_timecode->name,"PAL",31); 217 out_timecode->fps_div = 25; 218 break; 219 case B_TIMECODE_24: // Film 220 strncpy(out_timecode->name,"FILM",31); 221 out_timecode->fps_div = 24; 222 break; 223 case B_TIMECODE_18: // Super8 224 strncpy(out_timecode->name,"Super 8",31); 225 out_timecode->fps_div = 18; 226 break; 227 default: 228 strncpy(out_timecode->name,"NTSC",31); 229 out_timecode->fps_div = 30; // This is supposed to be 29.97fps but can be simulated using the drop frame format below 230 out_timecode->drop_frames = 2; // Drop 2 frames 231 out_timecode->every_nth = 1; // every 1 minutes 232 out_timecode->except_nth = 10; // except every 10 minutes 233 break; 234 } 235 236 return B_OK; 237 } 238 239 status_t count_timecodes() 240 { 241 CALLED(); 242 return 8; // Is this right? 243 } 244 245 /************************************************************* 246 * public BTimeCode 247 *************************************************************/ 248 249 250 BTimeCode::BTimeCode() 251 { 252 CALLED(); 253 } 254 255 256 BTimeCode::BTimeCode(bigtime_t us, 257 timecode_type type) 258 { 259 CALLED(); 260 if (SetType(type) == B_OK) { 261 SetMicroseconds(us); 262 } 263 } 264 265 266 BTimeCode::BTimeCode(const BTimeCode &clone) 267 { 268 CALLED(); 269 if (SetType(clone.Type()) == B_OK) { 270 SetData(clone.Hours(),clone.Minutes(),clone.Seconds(),clone.Frames()); 271 } 272 } 273 274 275 BTimeCode::BTimeCode(int hours, 276 int minutes, 277 int seconds, 278 int frames, 279 timecode_type type) 280 { 281 CALLED(); 282 if (SetType(type) == B_OK) { 283 SetData(hours,minutes,seconds,frames); 284 } 285 } 286 287 288 BTimeCode::~BTimeCode() 289 { 290 CALLED(); 291 } 292 293 294 void 295 BTimeCode::SetData(int hours, 296 int minutes, 297 int seconds, 298 int frames) 299 { 300 CALLED(); 301 fHours = hours; 302 fMinutes = minutes; 303 fSeconds = seconds; 304 fFrames = frames; 305 } 306 307 308 status_t 309 BTimeCode::SetType(timecode_type type) 310 { 311 CALLED(); 312 313 return get_timecode_description(type, &fInfo); 314 } 315 316 317 void 318 BTimeCode::SetMicroseconds(bigtime_t us) 319 { 320 CALLED(); 321 322 us_to_timecode(us, &fHours, &fMinutes, &fSeconds, &fFrames, &fInfo); 323 } 324 325 326 void 327 BTimeCode::SetLinearFrames(int32 linear_frames) 328 { 329 CALLED(); 330 331 frames_to_timecode(linear_frames, &fHours, &fMinutes, &fSeconds, &fFrames, &fInfo); 332 } 333 334 335 BTimeCode & 336 BTimeCode::operator=(const BTimeCode &clone) 337 { 338 CALLED(); 339 fHours = clone.Hours(); 340 fMinutes = clone.Minutes(); 341 fSeconds = clone.Seconds(); 342 fFrames = clone.Frames(); 343 SetType(clone.Type()); 344 345 return *this; 346 } 347 348 349 bool 350 BTimeCode::operator==(const BTimeCode &other) const 351 { 352 CALLED(); 353 354 return ((this->LinearFrames()) == (other.LinearFrames())); 355 } 356 357 358 bool 359 BTimeCode::operator<(const BTimeCode &other) const 360 { 361 CALLED(); 362 363 return ((this->LinearFrames()) < (other.LinearFrames())); 364 } 365 366 367 BTimeCode & 368 BTimeCode::operator+=(const BTimeCode &other) 369 { 370 CALLED(); 371 372 this->SetLinearFrames(this->LinearFrames() + other.LinearFrames()); 373 374 return *this; 375 } 376 377 378 BTimeCode & 379 BTimeCode::operator-=(const BTimeCode &other) 380 { 381 CALLED(); 382 383 this->SetLinearFrames(this->LinearFrames() - other.LinearFrames()); 384 385 return *this; 386 } 387 388 389 BTimeCode 390 BTimeCode::operator+(const BTimeCode &other) const 391 { 392 CALLED(); 393 BTimeCode tc(*this); 394 tc += other; 395 return tc; 396 } 397 398 399 BTimeCode 400 BTimeCode::operator-(const BTimeCode &other) const 401 { 402 CALLED(); 403 BTimeCode tc(*this); 404 tc -= other; 405 return tc; 406 } 407 408 409 int 410 BTimeCode::Hours() const 411 { 412 CALLED(); 413 414 return fHours; 415 } 416 417 418 int 419 BTimeCode::Minutes() const 420 { 421 CALLED(); 422 423 return fMinutes; 424 } 425 426 427 int 428 BTimeCode::Seconds() const 429 { 430 CALLED(); 431 432 return fSeconds; 433 } 434 435 436 int 437 BTimeCode::Frames() const 438 { 439 CALLED(); 440 441 return fFrames; 442 } 443 444 445 timecode_type 446 BTimeCode::Type() const 447 { 448 CALLED(); 449 450 return fInfo.type; 451 } 452 453 454 void 455 BTimeCode::GetData(int *out_hours, 456 int *out_minutes, 457 int *out_seconds, 458 int *out_frames, 459 timecode_type *out_type) const 460 { 461 CALLED(); 462 *out_hours = fHours; 463 *out_minutes = fMinutes; 464 *out_seconds = fSeconds; 465 *out_frames = fFrames; 466 *out_type = fInfo.type; 467 } 468 469 470 bigtime_t 471 BTimeCode::Microseconds() const 472 { 473 bigtime_t us; 474 475 CALLED(); 476 477 if (timecode_to_us(fHours,fMinutes,fSeconds,fFrames, &us, &fInfo) == B_OK) { 478 return us; 479 } 480 481 return 0; 482 } 483 484 485 int32 486 BTimeCode::LinearFrames() const 487 { 488 int32 linear_frames; 489 490 CALLED(); 491 492 if (timecode_to_frames(fHours,fMinutes,fSeconds,fFrames,&linear_frames,&fInfo) == B_OK) { 493 return linear_frames; 494 } 495 496 return 0; 497 } 498 499 500 void 501 BTimeCode::GetString(char *str) const 502 { 503 CALLED(); 504 sprintf(str,fInfo.format, fHours, fMinutes, fSeconds, fFrames); 505 } 506