//////////////////////////////////////////////////////////////////////
// SMPTETimecode.cpp
//////////////////////////////////////////////////////////////////////
// Demonstration of SMPTE Timecode Conversions
//
// Brooks Harris
// 2015-04-04
// http://www.edlmax.com/services.htm
//
// If this code works, I wrote it. If not, I've never heard of it.
//////////////////////////////////////////////////////////////////////
#ifdef SMPTE_TIMECODE_INTRODUCTION
/*///////////////////////////////////////////////////////////
// Conversion between SMPTE hh:mm:ss:ff Time Code and Frames
/////////////////////////////////////////////////////////////
Brooks Harris, EdlMax, LLC
Version V4 2014-07-26
SMPTE time code (sometimes called "SMPTE/EBU time code",
"EBU time code", or simply "timecode") is specified in the
SMPTE standard SMPTE ST 12-1 Time and Control Code,
(formerly called "SMPTE 12M"). SMPTE ST 12-1 describes the
structure of time code data and its numbering schemes
together with digital encoding formats. SMTPE ST 12-1 is
the foundational document for "SMPTE time code" from which
many other time code-related SMPTE standards are derived.
The behavior of the time and frames data in SMPTE time code
derives from its original purpose for monitoring tape
position by frames. When it was invented the best way to
encode the position of a frame on tape was in the form of
hours:minutes:seconds:frames (hh:mm:ss:ff). The binary form
used in ST 12-1 is encoded using Binary Coded Decimal (BCD)
together with other operational metadata to enable real-
time signal detection of values, direction, and speed on
tape.
SMPTE time code can be thought of as a "position", or
"index"; an absolute integer number of frames from a zero
starting point encoded as an hh:mm:ss:ff number from a
00:00:00:00 "origin". This encoding form also has the
advantage of being human readable as a time.
SMPTE time code can also be represented in character (text)
format. The official form of character representation is
specified in SMPTE ST 258, Transfer of Edit Decision Lists.
The data elements of the text format follow the numbering
and counting schemes defined in SMPTE ST-12-1.
Both binary and text forms encode the frame position value
as hh:mm:ss:ff. Conversion between them is the subject of
this article.
--------------
Binary Formats
--------------
SMPTE ST-12-1 details information about three related
topics:
1) The time and frames data itself and its counting
methods. This aspect is often of most interest and
relevance to many software implementations.
2) Specification of how the binary versions of time code
are to be phase-aligned to the video signal they are
applied to.
3) The "transport layer", that is, the bit layout of time
code binary data and operational metadata. Two transports
are defined in SMPTE ST 12-1 - "Linear Time code (LTC)" is
used for magnetic tape position tracking and transporting
time code over (analog) wires, and Vertical Interval Time
code (VITC)" is used to transport time code data in the
vertical interval of analog video signals. One additional
transport layer, defined in a companion document SMPTE ST
12-2 - "Ancillary Time Code (ATC)", is used to transport
time code data in digital video signals.
--------------
Text Formats
--------------
Time code can also be represented as text, the familiar
form seen on many system displays, in edit decision lists,
application parameter settings, etc. SMPTE ST 258, section
8 Time code specifies text-formatting rules for time code
character representation:
[hour]":"[minute]":"[second][":" | ";" | "." | ","][frames]
where the last separator (the character between seconds and
frames) indicates the count mode (Non-drop Frame or Drop
Frame) and "field 1" or "field 2" of interlace video.
"00:00:00.00" (period - field 1 non-drop-frame)
"00:00:00:00" (colon - field 2 non-drop-frame)
"00:00:00,00" (comma - field 1 drop-frame)
"00:00:00;00" (semicolon - field 2 drop-frame)
SMPTE Time code and text formatting does not explicitly
address "progressive" video formats. In this case it is
convention to default to ":" (non-drop-frame) or ";" (drop-
frame).
In systems and applications supporting conventional
interlace video formats the "field 1" and "field 2"
designations have significance. In many other applications
the field data is not important or may be ignored.
Typically in these cases ":" or ";" are used as default.
Implementers and users should exercise caution regarding
this aspect of time code.
--------------
Encoding of Frames as hh:mm:ss:ff
--------------
The SMPTE time code numbering schemes, or "count mode(s)",
label frames as hh:mm:ss:ff starting at an arbitrary zero
origin (00:00:00:00) through approximately 24 hours,
depending on the rate and count mode in use.
SMPTE time code supports four frame rates:
- 30/1 (30/1 HDTV)
- 30000/1001 second (NTSC and 30000/1001 digital video)
- 25/1 second (PAL and 25Hz digital video)
- 24/1 second (Film and 24p HDTV)
(60/1, 60000/1001, 50/1, and 48/1 can be supported by
treating frames in pairs. This is beyond the scope of this
article)
Its important to note that the NTSC frame rate is *not*
29.97, as is often stated. The true and correct rate is
30000/1001, which is 29.97002997002997... (a repeating
decimal fraction). Rounded to 2 decimal points it is 29.97
and this how the 30000/1001 rate gets the name "29.97". But
29.97 is not accurate enough for many purposes, especially
maintaining video/audio sync.
The "count modulus", that is, the number of unit counts per
time, is referred to as "frames per (nominal) second":
30fps (0-29 frames)
25fps (0-24 frames)
24fps (0-23 frames).
Here, "nominal" (name of) is used to indicate that a
"second" is not a true second in the case of 30000/1001
rate video.
--------------
Non-drop Frame and Drop Frame Count Modes
--------------
The "Non-drop Frame" count mode labels frames with an
uninterrupted incrementing count of hh:mm:ss:ff. It is
applicable to all rates. In the case of 30000/1001 (NTSC),
the hh:mm:ss portions of a Non-drop Frame count does not
indicate accurate running time because the frame rate is
not exactly "30 frames per second".
The "Drop Frame" count mode was developed to compensate for
the slightly slow frame rate of NTSC. 30000/1001 is
approximately 29.97002997.... , that is, 30 frames last for
slightly longer than true 30 frames-per-second. Put another
way, an "NTSC second" is slightly longer than a true second
- (1 / (30000 / 1001)) * 30 = 1.001 seconds. Thus there is
a "drift" between the true time and uncompensated (Non-drop
Frame) hh:mm:ss:ff representation. To compensate, the Drop
Frame count mode skips, or "drops", certain numbers from
the count. The rule is:
"Drop" (omit) the frame numbers 00 and 01 from the count
every minute except each tenth minute; that is, except each
minute that is divisible by 10.
To quote SMTPE ST 12-1, section 5.2.2 Drop frame - NTSC
time compensated mode:
To minimize the NTSC time deviation from real time, the
first two frame numbers (00 and 01) shall be omitted from
the count at the start of each minute except minutes 00,
10, 20, 30, 40, and 50.
So, at the end of the first minute the count "skips" from
00:00:59;29 to 00:01:00:00;02. The numbers 00:01:00:00;00
and 00:01:00:00;01 are "dropped", or omitted, from the
count. At the end of the tenth minute the count does not
skip - the count goes smoothly from 00:09:59;29 to
00:10:00:00;00 to 00:10:00:00;01.
The NTSC video signal is always the same; the count modulus
(30fps, 0-29) and frame rate (30000/1001) do not change
whether Non-drop or Drop-frame count mode is used. Drop
Frame is only a numbering scheme - actual video frames are
not dropped! Where would they go?
--------------
Drop Frame Accuracy and Broadcast "Daily Jam"
--------------
The Drop Frame count mode does a good job of distributing
the drift, or error, between true "clock time" and the
30000/1001 frame rate to produce usable "time-of-day"
hh:mm:ss:ff values. But it is not exactly accurate; there
is approximately a -2.6 frame error at the end of the 24-
hour day.
The Drop Frame value 24:00:00;00 = 2589408 frames, but 24 *
60 * 60 * (30000/1001) = 2589410.589410594. frames. The
Drop Frame count mode under-counts by approximately
-2.6 frames: 2589408 - 2589410.589410594. = -2.589410594.
frames. (There's that repeating decimal fraction again,
this time in the total frames in 24 hours, an unavoidable
consequence of the 30000/1001 rate no matter how you cut
it).
In production and post-production this inaccuracy can
generally be ignored, but in broadcast synchronization it
is traditional to reset the time code value once a day to
keep the indicated time-of-day as close as possible to true
clock time. This is sometimes called "daily jam", where the
time code value is "jammed", or reset, to an accurate
clock. Thus, time code values are very nearly the true
time-of-day and the error does not accumulate each day.
//////////////////////////////////////////////////////////////////////
// SMPTETimecode.h
#include <stdio.h>
#include <stdlib.h> // for atol()
typedef enum SMPTETCBASERATE
{
SMPTETCBASERATE_NOT_SET = 0,
SMPTETCBASERATE_30FPS = 1, // NTSC
SMPTETCBASERATE_25FPS = 2, // PAL
SMPTETCBASERATE_24FPS = 3, // Film or 24p
} SMPTETCBASERATE;
typedef enum SMPTETCTYPE
{
SMPTETCTYPE_NOT_SET = 0,
SMPTETCTYPE_FIELD1_NON_DROP_FRAME = 1, // '.' period
SMPTETCTYPE_FIELD2_NON_DROP_FRAME = 2, // ':' colon
SMPTETCTYPE_FIELD1_DROP_FRAME = 3, // ',' comma
SMPTETCTYPE_FIELD2_DROP_FRAME = 4, // ';' semi colon
} SMPTETCTYPE;
// convert hh:mm:ss:ff to frames
int SMPTE_HMSF_To_Frames( long lHr,
long lMn,
long lSc,
long lFr,
SMPTETCBASERATE SMPTETCBASERATEx,
SMPTETCTYPE SMPTETCTYPEx,
long* plFramesResult);
// convert timecode character representation to frames
int SMPTE_TextTC_To_Frames( char* sTimecode,
SMPTETCBASERATE SMPTETCBASERATEx,
long* plFramesResult);
// convert frames to hh MM ss ff
int SMPTE_Frames_To_HMSF( long lFrames,
SMPTETCBASERATE SMPTETCBASERATEx,
SMPTETCTYPE SMPTETCTYPEx,
long* plHrResult,
long* plMnResult,
long* plScResult,
long* plFrResult);
// convert frames to timecode character representation
int SMPTE_Frames_To_TextTC( long lFrames,
SMPTETCBASERATE SMPTETCBASERATEx,
SMPTETCTYPE SMPTETCTYPEx,
char* sTimecodeResult);
//////////////////////////////////////////////////////////////////////
*/////////////////////////////////////////////////////////////////////
#endif // #ifdef SMPTE_TIMECODE_INTRODUCTION
#include "SMPTETimecode.h"
int SMPTE_HMSF_To_Frames( long lHr,
long lMn,
long lSc,
long lFr,
SMPTETCBASERATE SMPTETCBASERATEx,
SMPTETCTYPE SMPTETCTYPEx,
long* plFramesResult)
{
///////////////////////////////////////////////////////
// convert hh:mm:ss:ff to frames
// params -
//
// long lHours - input hours
// long lMins - input minutes
// long lSecs - input seconds
// long lFrames - input frames
// SMPTETCBASERATEx one of (from "SMPTETimecode.h") -
// typedef enum SMPTETCBASERATE
// {
// SMPTETCBASERATE_NOT_SET = 0, // error
// SMPTETCBASERATE_30FPS = 1, // NTSC
// SMPTETCBASERATE_25FPS = 2, // PAL
// SMPTETCBASERATE_24FPS = 3, // Film or 24p
// } SMPTETCBASERATE;
//
// SMPTETCTYPEx one of (from "SMPTETimecode.h")
// typedef enum SMPTETCTYPE
// {
// SMPTETCTYPE_NOT_SET = 0,
// SMPTETCTYPE_FIELD1_NON_DROP_FRAME = 1, // '.' period
// SMPTETCTYPE_FIELD2_NON_DROP_FRAME = 2, // ':' colon
// SMPTETCTYPE_FIELD1_DROP_FRAME = 3, // ',' comma
// SMPTETCTYPE_FIELD2_DROP_FRAME = 4, // ';' semi colon
// } SMPTETCTYPE;
//
// plFramesResult - pointer to caller supplied long varible to receive result
//
// Return -
// Returns 0; // no error handling provided
//
///////////////////////////////////////////////////////
int iRet = 0; // no error handling provided
long lFramesCalc = 0;
if(SMPTETCBASERATEx == SMPTETCBASERATE_30FPS)
{
if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_NON_DROP_FRAME
|| SMPTETCTYPEx == SMPTETCTYPE_FIELD2_NON_DROP_FRAME)
{
// convert as 30 fps non-drop frame
lFramesCalc = lHr * 108000; // 30 * 60 * 60 non-drop frames in 1 hour
lFramesCalc += lMn * 1800; // 30 * 60 non-drop frames in one minute
lFramesCalc += lSc * 30; // 30 non-drop frames in one second
lFramesCalc += lFr; // frames
}
else if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_DROP_FRAME
|| SMPTETCTYPEx == SMPTETCTYPE_FIELD2_DROP_FRAME)
{
// convert as 30 fps drop-frame
lFramesCalc = lHr * 107892; // ((30 * 60 - 2) * 10 + 2) * 6 drop-frame frames in 1 hour
lFramesCalc += lMn * 1798; // 30 * 60 - 2 drop-frame frames in one minute
lFramesCalc += (lMn / 10) * 2; // for each minute except each 10th minute add 2 frames
lFramesCalc += lSc * 30; // 30 drop-frame frames in one second
lFramesCalc += lFr; // frames
}
}
else if(SMPTETCBASERATEx == SMPTETCBASERATE_25FPS)
{
if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_NON_DROP_FRAME
|| SMPTETCTYPEx == SMPTETCTYPE_FIELD2_NON_DROP_FRAME)
{
// convert as 25 fps non-drop frame
lFramesCalc = lHr * 90000; // 25 * 60 * 60 non-drop frames in 1 hour
lFramesCalc += lMn * 1500; // 25 * 60 non-drop frames in one minute
lFramesCalc += lSc * 25; // 25 non-drop frames in one second
lFramesCalc += lFr; // frames
}
else
{
// drop-frame couting mode is not supported for 25 fps
}
}
else if(SMPTETCBASERATEx == SMPTETCBASERATE_24FPS)
{
if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_NON_DROP_FRAME
|| SMPTETCTYPEx == SMPTETCTYPE_FIELD2_NON_DROP_FRAME)
{
// convert as 24 fps non-drop frame
lFramesCalc = lHr * 86400; // 24 * 60 * 60 non-drop frames in 1 hour
lFramesCalc += lMn * 1440; // 24 * 60 non-drop frames in one minute
lFramesCalc += lSc * 24; // 24 non-drop frames in one second
lFramesCalc += lFr; // frames
}
else
{
// drop-frame couting mode is not supported for 24 fps
}
}
*plFramesResult = lFramesCalc;
return iRet;
} // SMPTE_TextTC_To_Frames()
int SMPTE_TextTC_To_Frames( char* sTimecode,
SMPTETCBASERATE SMPTETCBASERATEx,
long* plFramesResult)
{
///////////////////////////////////////////////////////
// convert timecode character representation to frames
// params -
//
// sTimecode - input timecode character string (null terminated char string)
// SMPTETCBASERATEx one of (from "SMPTETimecode.h") -
// typedef enum SMPTETCBASERATE
// {
// SMPTETCBASERATE_NOT_SET = 0, // error
// SMPTETCBASERATE_30FPS = 1, // NTSC
// SMPTETCBASERATE_25FPS = 2, // PAL
// SMPTETCBASERATE_24FPS = 3, // Film or 24p
// } SMPTETCBASERATE;
// plFramesResult - pointer to called supplied long varible to receive result
// Return -
// Returns 0; // no error handling provided
//
///////////////////////////////////////////////////////
int iRet = 0; // no error handling provided
// parse the timecode string to obtain hh:mm:ss:ff values as integers
// atol() stops reading the string at any non-numeric character
// so the separators in 00:00:00:00 conveniently terminate each field
long lHours = atol(sTimecode + 0); // xx:00:00:00
long lMins = atol(sTimecode + 3); // 00:xx:00:00
long lSecs = atol(sTimecode + 6); // 00:00:xx:00
long lFrames = atol(sTimecode + 9); // 00:00:00:xx
// get the SMPTETCTYPE from the string
SMPTETCTYPE SMPTETCTYPEx = SMPTETCTYPE_NOT_SET;
char cTcType = sTimecode[8]; // last separator
if(cTcType == '.') // period
SMPTETCTYPEx = SMPTETCTYPE_FIELD1_NON_DROP_FRAME;
else if(cTcType == ':') // colon
SMPTETCTYPEx = SMPTETCTYPE_FIELD2_NON_DROP_FRAME;
else if(cTcType == ',') // comma
SMPTETCTYPEx = SMPTETCTYPE_FIELD1_DROP_FRAME;
else if(cTcType == ';') // semicolon
SMPTETCTYPEx = SMPTETCTYPE_FIELD2_DROP_FRAME;
long lFramesResult = 0;
SMPTE_HMSF_To_Frames( lHours,
lMins,
lSecs,
lFrames,
SMPTETCBASERATEx,
SMPTETCTYPEx,
&lFramesResult);
*plFramesResult = lFramesResult;
return iRet;
} // SMPTE_TextTC_To_Frames()
int SMPTE_Frames_To_HMSF( long lFrames,
SMPTETCBASERATE SMPTETCBASERATEx,
SMPTETCTYPE SMPTETCTYPEx,
long* plHrResult,
long* plMnResult,
long* plScResult,
long* plFrResult)
{
///////////////////////////////////////////////////////
// convert frames to hh mm ss ff
//
// params -
// lFrames - input frames value
//
// SMPTETCBASERATEx one of (from "SMPTETimecode.h")
// typedef enum SMPTETCBASERATE
// {
// SMPTETCBASERATE_NOT_SET = 0, // error
// SMPTETCBASERATE_30FPS = 1, // NTSC
// SMPTETCBASERATE_25FPS = 2, // PAL
// SMPTETCBASERATE_24FPS = 3, // Film or 24p
// } SMPTETCBASERATE;
//
// SMPTETCTYPEx one of (from "SMPTETimecode.h")
// typedef enum SMPTETCTYPE
// {
// SMPTETCTYPE_NOT_SET = 0,
// SMPTETCTYPE_FIELD1_NON_DROP_FRAME = 1, // '.' period
// SMPTETCTYPE_FIELD2_NON_DROP_FRAME = 2, // ':' colon
// SMPTETCTYPE_FIELD1_DROP_FRAME = 3, // ',' comma
// SMPTETCTYPE_FIELD2_DROP_FRAME = 4, // ';' semi colon
// } SMPTETCTYPE;
//
// long* plHrResult - pointer to caller supplied long to receive hours result
// long* plMnResult - pointer to caller supplied long to receive minutes result
// long* plScResult - pointer to caller supplied long to receive seconds result
// long* plFrResult - pointer to caller supplied long to receive frames result
//
// Return -
// Returns 0; // no error handling provided
//
///////////////////////////////////////////////////////
int iRet = 0; // no error handling provided
long lHr = 0;
long lMn = 0;
long lSc = 0;
long lFr = 0;
long lQuot = 0;
long lRem = 0;
if(SMPTETCBASERATEx == SMPTETCBASERATE_30FPS)
{
if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_NON_DROP_FRAME
|| SMPTETCTYPEx == SMPTETCTYPE_FIELD2_NON_DROP_FRAME)
{
// convert as 30 fps non-drop frame
#ifdef USE_24HOUR_ROLLOVER
if(lFrames >= 2592000) // 24 hour rollover
lFrames -= 2592000;
#endif // #ifdef USE_24HOUR_ROLLOVER
// HOURS /////////////////
lQuot = lFrames / 108000; // frames in non-drop-frame hour
lRem = lFrames % 108000;
lHr = lQuot;
// MINUTES /////////////////
lQuot = lRem / 1800; // frames in non-drop-frame minute
lRem = lRem % 1800;
lMn = lQuot;
// SECONDS //////////////
lQuot = lRem / 30; // frames in non-drop-frame second
lRem = lRem % 30;
lSc = lQuot;
// FRAMES ///////////////
lFr = lRem;
// return results
*plHrResult = lHr;
*plMnResult = lMn;
*plScResult = lSc;
*plFrResult = lFr;
}
else if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_DROP_FRAME
|| SMPTETCTYPEx == SMPTETCTYPE_FIELD2_DROP_FRAME)
{
// convert as 30 fps drop-frame
#ifdef USE_24HOUR_ROLLOVER
if(lFrames >= 2589408) // 24 hour rollover
lFrames -= 2589408;
#endif // #ifdef USE_24HOUR_ROLLOVER
long lCross10x2 = 0; // crossed 10 minutes
long lEven10 = 0; // is even 10 minutes
// HOURS ///////////////////
lQuot = lFrames / 107892; // frames in drop-frame hour
lRem = lFrames % 107892;
lHr = lQuot;
// MINUTES /////////////////
lQuot = lRem / 1798; // frames in drop-frame minute
lRem = lRem % 1798;
lCross10x2 = (lQuot / 10) * 2; // calc two frames for each 10 minute crossing
lEven10 = lQuot % 10; // calc is even 10th minute
if(lRem < (lCross10x2) && lEven10 == 0)
{
lQuot -= 1;
lRem += (1798 + 2) - (lCross10x2);
}
else
{
if(lRem < ((lCross10x2) + 2) && lEven10 != 0)
{
lQuot -= 1;
lRem += 1798 - (lCross10x2);
}
else
{
//lQuot = lQuot;
lRem -= (lCross10x2);
}
}
lMn = lQuot; // minute
// SECONDS //////////////
lQuot = lRem / 30;
lRem = lRem % 30;
lSc = lQuot;
// FRAMES ///////////////
lFr = lRem;
// return results
*plHrResult = lHr;
*plMnResult = lMn;
*plScResult = lSc;
*plFrResult = lFr;
}
}
else if(SMPTETCBASERATEx == SMPTETCBASERATE_25FPS)
{
if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_NON_DROP_FRAME
|| SMPTETCTYPEx == SMPTETCTYPE_FIELD2_NON_DROP_FRAME)
{
// convert as 25 fps non-drop frame
#ifdef USE_24HOUR_ROLLOVER
if(lFrames >= 2160000) // 24 hour rollover
lFrames -= 2160000;
#endif // #ifdef USE_24HOUR_ROLLOVER
// HOURS /////////////////
lQuot = lFrames / 90000; // frames in non-drop-frame hour
lRem = lFrames % 90000;
lHr = lQuot;
// MINUTES /////////////////
lQuot = lRem / 1500; // frames in non-drop-frame minute
lRem = lRem % 1500;
lMn = lQuot;
// SECONDS //////////////
lQuot = lRem / 25; // frames in non-drop-frame second
lRem = lRem % 25;
lSc = lQuot;
// FRAMES ///////////////
lFr = lRem;
// return results
*plHrResult = lHr;
*plMnResult = lMn;
*plScResult = lSc;
*plFrResult = lFr;
}
else
{
// drop-frame count mode is not supported for 25 fps
}
}
else if(SMPTETCBASERATEx == SMPTETCBASERATE_24FPS)
{
if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_NON_DROP_FRAME
|| SMPTETCTYPEx == SMPTETCTYPE_FIELD2_NON_DROP_FRAME)
{
// convert as 24 fps non-drop frame
#ifdef USE_24HOUR_ROLLOVER
if(lFrames >= 2073600) // 24 hour rollover
lFrames -= 2073600;
#endif // #ifdef USE_24HOUR_ROLLOVER
// HOURS /////////////////
lQuot = lFrames / 86400; // frames in non-drop-frame hour
lRem = lFrames % 86400;
lHr = lQuot;
// MINUTES /////////////////
lQuot = lRem / 1440; // frames in non-drop-frame minute
lRem = lRem % 1440;
lMn = lQuot;
// SECONDS //////////////
lQuot = lRem / 24; // frames in non-drop-frame second
lRem = lRem % 24;
lSc = lQuot;
// FRAMES ///////////////
lFr = lRem;
// return results
*plHrResult = lHr;
*plMnResult = lMn;
*plScResult = lSc;
*plFrResult = lFr;
}
else
{
// drop-frame count mode is not supported for 24 fps
}
}
return iRet;
} // SMPTE_Frames_To_HMSF()
int SMPTE_Frames_To_TextTC( long lFrames,
SMPTETCBASERATE SMPTETCBASERATEx,
SMPTETCTYPE SMPTETCTYPEx,
char* sTimecodeResult)
{
///////////////////////////////////////////////////////
// convert frames to timecode character representation
// params -
// lFrames - input frames value
//
// SMPTETCBASERATEx one of (from "SMPTETimecode.h")
// typedef enum SMPTETCBASERATE
// {
// SMPTETCBASERATE_NOT_SET = 0, // error
// SMPTETCBASERATE_30FPS = 1, // NTSC
// SMPTETCBASERATE_25FPS = 2, // PAL
// SMPTETCBASERATE_24FPS = 3, // Film or 24p
// } SMPTETCBASERATE;
//
// SMPTETCTYPEx one of (from "SMPTETimecode.h")
// typedef enum SMPTETCTYPE
// {
// SMPTETCTYPE_NOT_SET = 0,
// SMPTETCTYPE_FIELD1_NON_DROP_FRAME = 1, // '.' period
// SMPTETCTYPE_FIELD2_NON_DROP_FRAME = 2, // ':' colon
// SMPTETCTYPE_FIELD1_DROP_FRAME = 3, // ',' comma
// SMPTETCTYPE_FIELD2_DROP_FRAME = 4, // ';' semi colon
// } SMPTETCTYPE;
//
// sTimecodeResult - output timecode character string (null terminated char string)
//
// Return -
// Returns 0; // no error handling provided
//
///////////////////////////////////////////////////////
int iRet = 0; // no error handling provided
long lHr = 0;
long lMn = 0;
long lSc = 0;
long lFr = 0;
SMPTE_Frames_To_HMSF( lFrames,
SMPTETCBASERATEx,
SMPTETCTYPEx,
&lHr,
&lMn,
&lSc,
&lFr);
// HMSF to ascii text /////////////
// "00:00:00:00"
sprintf(sTimecodeResult, "%02d:%02d:%02d:%02d",
lHr,
lMn,
lSc,
lFr
);
// Set the last separator to timecode type character
if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_NON_DROP_FRAME)
sTimecodeResult[8] = '.'; // period
else if(SMPTETCTYPEx == SMPTETCTYPE_FIELD2_NON_DROP_FRAME)
sTimecodeResult[8] = ':'; // colon
else if(SMPTETCTYPEx == SMPTETCTYPE_FIELD1_DROP_FRAME)
sTimecodeResult[8] = ','; // comma
else if(SMPTETCTYPEx == SMPTETCTYPE_FIELD2_DROP_FRAME)
sTimecodeResult[8] = ';'; // semicolon
return iRet;
} // SMPTE_Frames_To_TextTC()