xref: /haiku/src/kits/media/TimeCode.cpp (revision b84955d416f92e89c9d73999f545a2dec353d988)
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