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
us_to_timecode(bigtime_t micros,int * hours,int * minutes,int * seconds,int * frames,const timecode_info * code)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
timecode_to_us(int hours,int minutes,int seconds,int frames,bigtime_t * micros,const timecode_info * code)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
frames_to_timecode(int32 l_frames,int * hours,int * minutes,int * seconds,int * frames,const timecode_info * code)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
timecode_to_frames(int hours,int minutes,int seconds,int frames,int32 * l_frames,const timecode_info * code)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
get_timecode_description(timecode_type type,timecode_info * out_timecode)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
count_timecodes()239 status_t count_timecodes()
240 {
241 CALLED();
242 return 8; // Is this right?
243 }
244
245 /*************************************************************
246 * public BTimeCode
247 *************************************************************/
248
249
BTimeCode()250 BTimeCode::BTimeCode()
251 {
252 CALLED();
253 }
254
255
BTimeCode(bigtime_t us,timecode_type type)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
BTimeCode(const BTimeCode & clone)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
BTimeCode(int hours,int minutes,int seconds,int frames,timecode_type type)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
~BTimeCode()288 BTimeCode::~BTimeCode()
289 {
290 CALLED();
291 }
292
293
294 void
SetData(int hours,int minutes,int seconds,int frames)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
SetType(timecode_type type)309 BTimeCode::SetType(timecode_type type)
310 {
311 CALLED();
312
313 return get_timecode_description(type, &fInfo);
314 }
315
316
317 void
SetMicroseconds(bigtime_t us)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
SetLinearFrames(int32 linear_frames)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 &
operator =(const BTimeCode & clone)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
operator ==(const BTimeCode & other) const350 BTimeCode::operator==(const BTimeCode &other) const
351 {
352 CALLED();
353
354 return ((this->LinearFrames()) == (other.LinearFrames()));
355 }
356
357
358 bool
operator <(const BTimeCode & other) const359 BTimeCode::operator<(const BTimeCode &other) const
360 {
361 CALLED();
362
363 return ((this->LinearFrames()) < (other.LinearFrames()));
364 }
365
366
367 BTimeCode &
operator +=(const BTimeCode & other)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 &
operator -=(const BTimeCode & other)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
operator +(const BTimeCode & other) const390 BTimeCode::operator+(const BTimeCode &other) const
391 {
392 CALLED();
393 BTimeCode tc(*this);
394 tc += other;
395 return tc;
396 }
397
398
399 BTimeCode
operator -(const BTimeCode & other) const400 BTimeCode::operator-(const BTimeCode &other) const
401 {
402 CALLED();
403 BTimeCode tc(*this);
404 tc -= other;
405 return tc;
406 }
407
408
409 int
Hours() const410 BTimeCode::Hours() const
411 {
412 CALLED();
413
414 return fHours;
415 }
416
417
418 int
Minutes() const419 BTimeCode::Minutes() const
420 {
421 CALLED();
422
423 return fMinutes;
424 }
425
426
427 int
Seconds() const428 BTimeCode::Seconds() const
429 {
430 CALLED();
431
432 return fSeconds;
433 }
434
435
436 int
Frames() const437 BTimeCode::Frames() const
438 {
439 CALLED();
440
441 return fFrames;
442 }
443
444
445 timecode_type
Type() const446 BTimeCode::Type() const
447 {
448 CALLED();
449
450 return fInfo.type;
451 }
452
453
454 void
GetData(int * out_hours,int * out_minutes,int * out_seconds,int * out_frames,timecode_type * out_type) const455 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
Microseconds() const471 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
LinearFrames() const486 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
GetString(char * str) const501 BTimeCode::GetString(char *str) const
502 {
503 CALLED();
504 sprintf(str,fInfo.format, fHours, fMinutes, fSeconds, fFrames);
505 }
506