1 /*
2    libltc - en+decode linear timecode
3 
4    Copyright (C) 2006-2012 Robin Gareus <robin@gareus.org>
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU Lesser General Public License as
8    published by the Free Software Foundation, either version 3 of the
9    License, or (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU Lesser General Public License for more details.
15 
16    You should have received a copy of the GNU Lesser General Public
17    License along with this library.
18    If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "ltc.h"
26 
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30 
31 /**
32  * SMPTE Timezones
33  */
34 struct SMPTETimeZonesStruct {
35 	unsigned char code; //actually 6 bit!
36 	char timezone[6];
37 };
38 
39 /**
40  * SMPTE Timezone codes as per http://www.barney-wol.net/time/timecode.html
41  */
42 static const struct SMPTETimeZonesStruct smpte_time_zones[] =
43 {
44     /*  code,   timezone (UTC+)     //Standard time                 //Daylight saving   */
45     {   0x00,   "+0000"             /* Greenwich */                 /* - */             },
46     {   0x00,   "-0000"             /* Greenwich */                 /* - */             },
47     {   0x01,   "-0100"             /* Azores */                    /* - */             },
48     {   0x02,   "-0200"             /* Mid-Atlantic */              /* - */             },
49     {   0x03,   "-0300"             /* Buenos Aires */              /* Halifax */       },
50     {   0x04,   "-0400"             /* Halifax */                   /* New York */      },
51     {   0x05,   "-0500"             /* New York */                  /* Chicago */       },
52     {   0x06,   "-0600"             /* Chicago Denver */            /* - */             },
53     {   0x07,   "-0700"             /* Denver */                    /* Los Angeles */   },
54     {   0x08,   "-0800"             /* Los Angeles */               /* - */             },
55     {   0x09,   "-0900"             /* Alaska */                    /* - */             },
56     {   0x10,   "-1000"             /* Hawaii */                    /* - */             },
57     {   0x11,   "-1100"             /* Midway Island */             /* - */             },
58     {   0x12,   "-1200"             /* Kwaialein */                 /* - */             },
59     {   0x13,   "+1300"             /* - */                         /* New Zealand */   },
60     {   0x14,   "+1200"             /* New Zealand */               /* - */             },
61     {   0x15,   "+1100"             /* Solomon Islands */           /* - */             },
62     {   0x16,   "+1000"             /* Guam */                      /* - */             },
63     {   0x17,   "+0900"             /* Tokyo */                     /* - */             },
64     {   0x18,   "+0800"             /* Beijing */                   /* - */             },
65     {   0x19,   "+0700"             /* Bangkok */                   /* - */             },
66     {   0x20,   "+0600"             /* Dhaka */                     /* - */             },
67     {   0x21,   "+0500"             /* Islamabad */                 /* - */             },
68     {   0x22,   "+0400"             /* Abu Dhabi */                 /* - */             },
69     {   0x23,   "+0300"             /* Moscow */                    /* - */             },
70     {   0x24,   "+0200"             /* Eastern Europe */            /* - */             },
71     {   0x25,   "+0100"             /* Central Europe */            /* - */             },
72 /*  {   0x26,   "Undefined"         Reserved; do not use                                },*/
73 /*  {   0x27,   "Undefined"         Reserved; do not use                                },*/
74     {   0x28,   "TP-03"             /* Time precision class 3 */    /* - */             },
75     {   0x29,   "TP-02"             /* Time precision class 2 */    /* - */             },
76     {   0x30,   "TP-01"             /* Time precision class 1 */    /* - */             },
77     {   0x31,   "TP-00"             /* Time precision class 0 */    /* - */             },
78     {   0x0A,   "-0030"             /* - */                         /* - */             },
79     {   0x0B,   "-0130"             /* - */                         /* - */             },
80     {   0x0C,   "-0230"             /* - */                         /* Newfoundland */  },
81     {   0x0D,   "-0330"             /* Newfoundland */              /* - */             },
82     {   0x0E,   "-0430"             /* - */                         /* - */             },
83     {   0x0F,   "-0530"             /* - */                         /* - */             },
84     {   0x1A,   "-0630"             /* - */                         /* - */             },
85     {   0x1B,   "-0730"             /* - */                         /* - */             },
86     {   0x1C,   "-0830"             /* - */                         /* - */             },
87     {   0x1D,   "-0930"             /* Marquesa Islands */          /* - */             },
88     {   0x1E,   "-1030"             /* - */                         /* - */             },
89     {   0x1F,   "-1130"             /* - */                         /* - */             },
90     {   0x2A,   "+1130"             /* Norfolk Island */            /* - */             },
91     {   0x2B,   "+1030"             /* Lord Howe Is. */             /* - */             },
92     {   0x2C,   "+0930"             /* Darwin */                    /* - */             },
93     {   0x2D,   "+0830"             /* - */                         /* - */             },
94     {   0x2E,   "+0730"             /* - */                         /* - */             },
95     {   0x2F,   "+0630"             /* Rangoon */                   /* - */             },
96     {   0x3A,   "+0530"             /* Bombay */                    /* - */             },
97     {   0x3B,   "+0430"             /* Kabul */                     /* - */             },
98     {   0x3C,   "+0330"             /* Tehran */                    /* - */             },
99     {   0x3D,   "+0230"             /* - */                         /* - */             },
100     {   0x3E,   "+0130"             /* - */                         /* - */             },
101     {   0x3F,   "+0030"             /* - */                         /* - */             },
102     {   0x32,   "+1245"             /* Chatham Island */            /* - */             },
103 /*  {   0x33,   "Undefined"         Reserved; do not use                                },*/
104 /*  {   0x34,   "Undefined"         Reserved; do not use                                },*/
105 /*  {   0x35,   "Undefined"         Reserved; do not use                                },*/
106 /*  {   0x36,   "Undefined"         Reserved; do not use                                },*/
107 /*  {   0x37,   "Undefined"         Reserved; do not use                                },*/
108     {   0x38,   "+XXXX"             /* User defined time offset */  /* - */             },
109 /*  {   0x39,   "Undefined"         Unknown                         Unknown             },*/
110 /*  {   0x39,   "Undefined"         Unknown                         Unknown             },*/
111 
112     {   0xFF,   ""                  /* The End */                                       }
113 };
114 
smpte_set_timezone_string(LTCFrame * frame,SMPTETimecode * stime)115 static void smpte_set_timezone_string(LTCFrame *frame, SMPTETimecode *stime) {
116 	int i = 0;
117 
118 	const unsigned char code = frame->user7 + (frame->user8 << 4);
119 
120 	char timezone[6] = "+0000";
121 
122 	for (i = 0 ; smpte_time_zones[i].code != 0xFF ; i++) {
123 		if ( smpte_time_zones[i].code == code ) {
124 			strcpy(timezone, smpte_time_zones[i].timezone);
125 			break;
126 		}
127 	}
128 	strcpy(stime->timezone, timezone);
129 }
130 
smpte_set_timezone_code(SMPTETimecode * stime,LTCFrame * frame)131 static void smpte_set_timezone_code(SMPTETimecode *stime, LTCFrame *frame) {
132 	int i = 0;
133 	unsigned char code = 0x00;
134 
135 	// Find code for timezone string
136 	// Primitive search
137 	for (i=0; smpte_time_zones[i].code != 0xFF; i++) {
138 		if ( (strcmp(smpte_time_zones[i].timezone, stime->timezone)) == 0 ) {
139 			code = smpte_time_zones[i].code;
140 			break;
141 		}
142 	}
143 
144 	frame->user7 = code & 0x0F;
145 	frame->user8 = (code & 0xF0) >> 4;
146 }
147 
148 /** Drop-frame support function
149  * We skip the first two frame numbers (0 and 1) at the beginning of each minute,
150  * except for minutes 0, 10, 20, 30, 40, and 50
151  * (i.e. we skip frame numbers at the beginning of minutes for which mins_units is not 0).
152  */
skip_drop_frames(LTCFrame * frame)153 static void skip_drop_frames(LTCFrame* frame) {
154 	if ((frame->mins_units != 0)
155 		&& (frame->secs_units == 0)
156 		&& (frame->secs_tens == 0)
157 		&& (frame->frame_units == 0)
158 		&& (frame->frame_tens == 0)
159 		) {
160 		frame->frame_units += 2;
161 	}
162 }
163 
ltc_frame_to_time(SMPTETimecode * stime,LTCFrame * frame,int flags)164 void ltc_frame_to_time(SMPTETimecode *stime, LTCFrame *frame, int flags) {
165 	if (!stime) return;
166 
167 	if (flags & LTC_USE_DATE) {
168 		smpte_set_timezone_string(frame, stime);
169 
170 		stime->years  = frame->user5 + frame->user6*10;
171 		stime->months = frame->user3 + frame->user4*10;
172 		stime->days   = frame->user1 + frame->user2*10;
173 	} else {
174 		stime->years  = 0;
175 		stime->months = 0;
176 		stime->days   = 0;
177 		sprintf(stime->timezone,"+0000");
178 	}
179 
180 	stime->hours = frame->hours_units + frame->hours_tens*10;
181 	stime->mins  = frame->mins_units  + frame->mins_tens*10;
182 	stime->secs  = frame->secs_units  + frame->secs_tens*10;
183 	stime->frame = frame->frame_units + frame->frame_tens*10;
184 }
185 
ltc_time_to_frame(LTCFrame * frame,SMPTETimecode * stime,enum LTC_TV_STANDARD standard,int flags)186 void ltc_time_to_frame(LTCFrame* frame, SMPTETimecode* stime, enum LTC_TV_STANDARD standard, int flags) {
187 	if (flags & LTC_USE_DATE) {
188 		smpte_set_timezone_code(stime, frame);
189 		frame->user6 = stime->years/10;
190 		frame->user5 = stime->years - frame->user6*10;
191 		frame->user4 = stime->months/10;
192 		frame->user3 = stime->months - frame->user4*10;
193 		frame->user2 = stime->days/10;
194 		frame->user1 = stime->days - frame->user2*10;
195 	}
196 
197 	frame->hours_tens  = stime->hours/10;
198 	frame->hours_units = stime->hours - frame->hours_tens*10;
199 	frame->mins_tens   = stime->mins/10;
200 	frame->mins_units  = stime->mins - frame->mins_tens*10;
201 	frame->secs_tens   = stime->secs/10;
202 	frame->secs_units  = stime->secs - frame->secs_tens*10;
203 	frame->frame_tens  = stime->frame/10;
204 	frame->frame_units = stime->frame - frame->frame_tens*10;
205 
206 	// Prevent illegal SMPTE frames
207 	if (frame->dfbit) {
208 		skip_drop_frames(frame);
209 	}
210 
211 	if ((flags & LTC_NO_PARITY) == 0) {
212 		ltc_frame_set_parity(frame, standard);
213 	}
214 }
215 
ltc_frame_reset(LTCFrame * frame)216 void ltc_frame_reset(LTCFrame* frame) {
217 	memset(frame, 0, sizeof(LTCFrame));
218 	// syncword = 0x3FFD
219 #ifdef LTC_BIG_ENDIAN
220 	// mirrored BE bit order: FCBF
221 	frame->sync_word = 0xFCBF;
222 #else
223 	// mirrored LE bit order: BFFC
224 	frame->sync_word = 0xBFFC;
225 #endif
226 }
227 
ltc_frame_increment(LTCFrame * frame,int fps,enum LTC_TV_STANDARD standard,int flags)228 int ltc_frame_increment(LTCFrame* frame, int fps, enum LTC_TV_STANDARD standard, int flags) {
229 	int rv = 0;
230 
231 	frame->frame_units++;
232 
233 	if (frame->frame_units == 10)
234 	{
235 		frame->frame_units = 0;
236 		frame->frame_tens++;
237 	}
238 	if (fps == frame->frame_units+frame->frame_tens*10)
239 	{
240 		frame->frame_units = 0;
241 		frame->frame_tens = 0;
242 		frame->secs_units++;
243 		if (frame->secs_units == 10)
244 		{
245 			frame->secs_units = 0;
246 			frame->secs_tens++;
247 			if (frame->secs_tens == 6)
248 			{
249 				frame->secs_tens = 0;
250 				frame->mins_units++;
251 				if (frame->mins_units == 10)
252 				{
253 					frame->mins_units = 0;
254 					frame->mins_tens++;
255 					if (frame->mins_tens == 6)
256 					{
257 						frame->mins_tens = 0;
258 						frame->hours_units++;
259 						if (frame->hours_units == 10)
260 						{
261 							frame->hours_units = 0;
262 							frame->hours_tens++;
263 						}
264 						if (frame->hours_units == 4 && frame->hours_tens==2)
265 						{
266 							/* 24h wrap around */
267 							rv=1;
268 							frame->hours_tens=0;
269 							frame->hours_units = 0;
270 
271 							if (flags&1)
272 							{
273 								/* wrap date */
274 								SMPTETimecode stime;
275 								stime.years  = frame->user5 + frame->user6*10;
276 								stime.months = frame->user3 + frame->user4*10;
277 								stime.days   = frame->user1 + frame->user2*10;
278 
279 								if (stime.months > 0 && stime.months < 13)
280 								{
281 									unsigned char dpm[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
282 									/* proper leap-year calc:
283 									 * ((stime.years%4)==0 && ( (stime.years%100) != 0 || (stime.years%400) == 0) )
284 									 * simplified since year is 0..99
285 									 */
286 									if ((stime.years%4)==0 /* && stime.years!=0 */ ) /* year 2000 was a leap-year */
287 										dpm[1]=29;
288 									stime.days++;
289 									if (stime.days > dpm[stime.months-1])
290 									{
291 										stime.days=1;
292 										stime.months++;
293 										if (stime.months > 12) {
294 											stime.months=1;
295 											stime.years=(stime.years+1)%100;
296 										}
297 									}
298 									frame->user6 = stime.years/10;
299 									frame->user5 = stime.years%10;
300 									frame->user4 = stime.months/10;
301 									frame->user3 = stime.months%10;
302 									frame->user2 = stime.days/10;
303 									frame->user1 = stime.days%10;
304 								} else {
305 									rv=-1;
306 								}
307 							}
308 						}
309 					}
310 				}
311 			}
312 		}
313 	}
314 
315 	if (frame->dfbit) {
316 		skip_drop_frames(frame);
317 	}
318 
319 	if ((flags & LTC_NO_PARITY) == 0) {
320 		ltc_frame_set_parity(frame, standard);
321 	}
322 
323 	return rv;
324 }
325 
ltc_frame_decrement(LTCFrame * frame,int fps,enum LTC_TV_STANDARD standard,int flags)326 int ltc_frame_decrement(LTCFrame* frame, int fps, enum LTC_TV_STANDARD standard, int flags) {
327 	int rv = 0;
328 
329 	int frames = frame->frame_units + frame->frame_tens * 10;
330 	if (frames > 0) {
331 		frames--;
332 	} else {
333 		frames = fps -1;
334 	}
335 
336 	frame->frame_units = frames % 10;
337 	frame->frame_tens  = frames / 10;
338 
339 	if (frames == fps -1) {
340 		int secs = frame->secs_units + frame->secs_tens * 10;
341 		if (secs > 0) {
342 			secs--;
343 		} else {
344 			secs = 59;
345 		}
346 		frame->secs_units = secs % 10;
347 		frame->secs_tens  = secs / 10;
348 
349 		if (secs == 59) {
350 			int mins = frame->mins_units + frame->mins_tens * 10;
351 			if (mins > 0) {
352 				mins--;
353 			} else {
354 				mins = 59;
355 			}
356 			frame->mins_units = mins % 10;
357 			frame->mins_tens  = mins / 10;
358 
359 			if (mins == 59) {
360 				int hours = frame->hours_units + frame->hours_tens * 10;
361 				if (hours > 0) {
362 					hours--;
363 				} else {
364 					hours = 23;
365 				}
366 				frame->hours_units = hours % 10;
367 				frame->hours_tens  = hours / 10;
368 
369 				if (hours == 23) {
370 					/* 24h wrap around */
371 					rv=1;
372 					if (flags&LTC_USE_DATE)
373 					{
374 						/* wrap date */
375 						SMPTETimecode stime;
376 						stime.years  = frame->user5 + frame->user6*10;
377 						stime.months = frame->user3 + frame->user4*10;
378 						stime.days   = frame->user1 + frame->user2*10;
379 
380 						if (stime.months > 0 && stime.months < 13)
381 						{
382 							unsigned char dpm[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
383 							/* proper leap-year calc:
384 							 * ((stime.years%4)==0 && ( (stime.years%100) != 0 || (stime.years%400) == 0) )
385 							 * simplified since year is 0..99
386 							 */
387 							if ((stime.years%4)==0 /* && stime.years!=0 */ ) /* year 2000 was a leap-year */
388 								dpm[1]=29;
389 							//
390 							if (stime.days > 1) {
391 								stime.days--;
392 							} else {
393 								stime.months = 1 + (stime.months + 10)%12;
394 								stime.days = dpm[stime.months-1];
395 								if (stime.months == 12)  {
396 									stime.years=(stime.years+99)%100; // XXX
397 								}
398 							}
399 
400 							frame->user6 = stime.years/10;
401 							frame->user5 = stime.years%10;
402 							frame->user4 = stime.months/10;
403 							frame->user3 = stime.months%10;
404 							frame->user2 = stime.days/10;
405 							frame->user1 = stime.days%10;
406 						} else {
407 							rv=-1;
408 						}
409 					}
410 				}
411 			}
412 		}
413 	}
414 
415 	if (frame->dfbit && /* prevent endless recursion */ fps > 2) {
416 		if ((frame->mins_units != 0)
417 			&& (frame->secs_units == 0)
418 			&& (frame->secs_tens == 0)
419 			&& (frame->frame_units == 1)
420 			&& (frame->frame_tens == 0)
421 			) {
422 			ltc_frame_decrement(frame, fps, standard, flags&LTC_USE_DATE);
423 			ltc_frame_decrement(frame, fps, standard, flags&LTC_USE_DATE);
424 		}
425 	}
426 
427 	if ((flags & LTC_NO_PARITY) == 0) {
428 		ltc_frame_set_parity(frame, standard);
429 	}
430 
431 	return rv;
432 }
433 
parse_bcg_flags(LTCFrame * f,enum LTC_TV_STANDARD standard)434 int parse_bcg_flags(LTCFrame *f, enum LTC_TV_STANDARD standard) {
435 	switch (standard) {
436 		case LTC_TV_625_50: /* 25 fps mode */
437 			return (
438 					  ((f->binary_group_flag_bit0)?4:0)
439 					| ((f->binary_group_flag_bit1)?2:0)
440 					| ((f->biphase_mark_phase_correction)?1:0)
441 					);
442 			break;
443 		default: /* 24,30 fps mode */
444 			return (
445 					  ((f->binary_group_flag_bit2)?4:0)
446 					| ((f->binary_group_flag_bit1)?2:0)
447 					| ((f->binary_group_flag_bit0)?1:0)
448 					);
449 			break;
450 	}
451 }
452