windows-nt/Source/XPSP1/NT/multimedia/dshow/timecode/timecode.cpp

374 lines
9.7 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/**********************************************************************
Copyright (c) 1996 - 1998 Microsoft Corporation. All Rights Reserved.
TimeCode
A class to describe and manipulate SMPTE timecodes.
If we extend this class, fix its bugs, and use it in all applications,
then we will have consistent and error free code!
NOTE: I have provided the dropframe math derivations in the code along
with references to the appropriate texts.
TODO: PAL Dropframe math. (fairly consistent, base it on the NTSC drop code)
Check values and generate EXCEPTIONS on errs!
Extend timecode class to other video types/rates
Flesh out the operator overloads (+=,-=,+,- all make good sense)
Ken Greenebaum, November 1996
**********************************************************************/
#include <wtypes.h>
#include <stdio.h>
#include "timecode.h"
// Initialize constants!
const char TimeCode::NTSC_MODE = (char)0x01;
const char TimeCode::PAL_MODE = (char)0x00;
const char TimeCode::FORMAT_MASK = (char)0xFE;
const char TimeCode::DROPFRAME_MODE = (char)0x02;
const char TimeCode::NO_DROPFRAME_MODE = (char)0x00;
const char TimeCode::DROPFRAME_MASK = (char)0xFD;
const char TimeCode::NTSC_NODROP = NTSC_MODE | NO_DROPFRAME_MODE;
const char TimeCode::NTSC_DROP = NTSC_MODE | DROPFRAME_MODE;
const char TimeCode::PAL_NODROP = PAL_MODE | NO_DROPFRAME_MODE;
const char TimeCode::PAL_DROP = PAL_MODE | DROPFRAME_MODE;
TimeCode::TimeCode()
{
SetFormat(TRUE, FALSE); // NTSC, no dropframe
SetTime(0, 0, 0, 0); // 00:00:00:00
}
TimeCode::TimeCode(char *string, BOOL ntsc, BOOL drop)
{
SetFormat(ntsc, drop);
SetTime(string);
}
TimeCode::TimeCode(int h, int m, int s, int f, BOOL ntsc, BOOL d)
{
SetFormat(ntsc, d);
SetTime(h, m, s, f);
}
void
TimeCode::operator++(int unusedPostfixThingy) // postfix
{
operator++(); // call the prefix operator for the sake of consistency
}
void
TimeCode::operator++() // prefix
{
if(_frames.dirty)
_FramesFromHMSF();
_frames.frames++;
_frames.dirty = FALSE;
_hmsf.dirty = TRUE;
}
void
TimeCode::operator--(int unusedPostfixThingy) // postfix
{
operator--(); // call the prefix operator for the sake of consistency
}
void
TimeCode::operator--() // prefix
{
if(_frames.dirty)
_FramesFromHMSF();
_frames.frames--; // XXX shouldn't we check for wrap?
_frames.dirty = FALSE;
_hmsf.dirty = TRUE;
}
void
TimeCode::SetFormat(BOOL ntsc, BOOL drop)
{
_timeCodeFormat = (ntsc ? NTSC_MODE : PAL_MODE) |
(drop ? DROPFRAME_MODE : NO_DROPFRAME_MODE);
}
void
TimeCode::SetTime(int hours, int minutes, int seconds, int frames)
{
_hmsf.hours = hours;
_hmsf.minutes = minutes;
_hmsf.seconds = seconds;
_hmsf.frames = frames;
_hmsf.dirty = FALSE;
_frames.dirty = TRUE; // invalidate the frames cache
// XXX should we sanity check the timecode value?
}
void
TimeCode::SetTime(char *string)
{
sscanf(string, "%d:%d:%d:%d",
&_hmsf.hours, &_hmsf.minutes, &_hmsf.seconds, &_hmsf.frames);
_hmsf.dirty = FALSE;
_frames.dirty = TRUE; // invalidate the frames cache
// XXX should we sanity check the timecode value?
}
void
TimeCode::GetTime(int *hours, int *minutes, int *seconds, int *frames)
{
if(_hmsf.dirty)
_HMSFfromFrames();
*hours = _hmsf.hours;
*minutes = _hmsf.minutes;
*seconds = _hmsf.seconds;
*frames = _hmsf.frames;
}
LONGLONG
TimeCode::GetTime()
{
if(_frames.dirty)
_FramesFromHMSF();
return(_frames.frames);
}
void
TimeCode::GetString(char *string) {
if(_hmsf.dirty)
_HMSFfromFrames();
wsprintf(string, "%02d:%02d:%02d:%02d",
_hmsf.hours, _hmsf.minutes, _hmsf.seconds, _hmsf.frames);
}
/**********************************************************************
Dropframe math derivation:
NTSC dropframe Pg208 Video Demystified:
To resolve the color timing error, the first two frame numbers
(0, and 1) at the start of each minute, except for minutes 0, 10, 20, 30,
40, and 50, are omitted from the count.
So:
The number of frames of video in an NTSC dropframe hour =
30 frames/second * 60 seconds/minute * 60 minutes/hour
- 60 minutes * 2 frames dropped/minute
+ 6 excepted minutes * 2 frames not dropped
= 107892
The number of frames of video in an NTSC dropframe minute =
30 frames/second * 60 seconds/minute - 2 if minute != 0,10,20,30,40 or 50!
There are 6 10 minute cycles in an hour.
Each cycle has 17982 frames:
9 dropframe min * ((30 frames/sec * 60 sec/min) - 2 dropframes/min) frames/min
1 nondrop min * 30 frames/second * 60 sec/min
**********************************************************************/
void
TimeCode::_HMSFfromFrames()
{
LONGLONG frames = _frames.frames;
int units;
int tmp;
switch(_timeCodeFormat) {
case NTSC_NODROP:
_hmsf.hours = (int)(frames / 108000.0);
frames-= _hmsf.hours * 108000;
_hmsf.minutes = (int)(frames / 1800.0);
frames-= _hmsf.minutes * 1800;
_hmsf.seconds = (int)(frames / 30.0);
frames-= _hmsf.seconds * 30;
_hmsf.frames = (int)frames;
break;
case NTSC_DROP:
_hmsf.hours = (int)(frames / 107892.0);
frames-= _hmsf.hours * 107892;
// remove 10 minute cycles
tmp = (int)(frames / 17982.0);
_hmsf.minutes = 10 * tmp;
frames-= tmp * 17982;
// remaining minutes < complete 10 minute cycle
// first minute (0) would not drop frames
if(frames >= 1800) {
_hmsf.minutes++;
frames-=1800;
tmp = (int)(frames / 1798.0);
_hmsf.minutes+= tmp;
frames-= tmp * 1798;
}
// remaining seconds <= 60
_hmsf.seconds = 0;
units = _hmsf.minutes - 10*(_hmsf.minutes/10); // remove 10s
if(!units) { // the 0, 10, 20, 30, 40, 50 minute
_hmsf.seconds = (int)(frames / 30.0);
frames-= _hmsf.seconds * 30;
}
else { // not an exception min(the 1st secof this min has 28frames)
if(frames>=28) {
frames-=28;
_hmsf.seconds = 1;
// the rest are 30 frame seconds
_hmsf.seconds+= (int)(frames / 30.0);
frames-= (_hmsf.seconds-1) * 30;
}
}
_hmsf.frames = (int)frames;
_hmsf.frames += ((units)&&(_hmsf.seconds==0))?2:0; // a drop second
break;
case PAL_NODROP:
_hmsf.hours = (int)(frames / 90000.0);
frames-= _hmsf.hours * 90000;
_hmsf.minutes = (int)(frames / 1500.0);
frames-= _hmsf.minutes * 1500;
_hmsf.seconds = (int)(frames / 25.0);
frames-= _hmsf.seconds * 25;
_hmsf.frames = (int)frames;
break;
case PAL_DROP: fprintf(stderr,
"TimeCode::_HMSfromFrames: PAL_DROP not implemented!\n");
break;
default:
// XXX really should throw an exception!
fprintf(stderr,
"TimeCode::_HMSfromFrames: unknown _timeCodeFormat %d\n",
_timeCodeFormat);
break;
}
_hmsf.dirty = FALSE;
}
void
TimeCode::_FramesFromHMSF()
{
LONGLONG frames;
//int exceptionalMinutes;
switch(_timeCodeFormat) {
case NTSC_NODROP:
frames = _hmsf.frames;
frames+= _hmsf.seconds * 30;
frames+= _hmsf.minutes * 1800; // 30f/s * 60s/min
frames+= _hmsf.hours * 108000; //30f/s*60s/min*60min/hr
break;
case NTSC_DROP: // see derivation for details!
frames = _hmsf.hours * 107892; // for every hour
frames+= _hmsf.minutes * 1800; // for every minute, except...
// now take away 2 frames per non-exempted minute!
frames-= ((_hmsf.minutes - _hmsf.minutes/10) * 2);
frames+= _hmsf.seconds * 30;
frames+= _hmsf.frames;
break;
case PAL_NODROP:
frames = _hmsf.frames;
frames+= _hmsf.seconds * 25;
frames+= _hmsf.minutes * 1500; // 25f/s * 60s/min
frames+= _hmsf.hours * 90000; //25f/s*60s/min*60min/hr
break;
case PAL_DROP: fprintf(stderr,
"TimeCode::_HMSfromFrames: PAL_DROP not implemented!\n");
break;
default:
// XXX really should throw an exception!
fprintf(stderr,
"TimeCode::_HMSfromFrames: unknown _timeCodeFormat %d\n",
_timeCodeFormat);
break;
}
_frames.frames = frames; // ;)
_frames.dirty = FALSE;
}
#ifdef TEST
#include <string.h>
#include <stdio.h>
__cdecl
main()
{
// TimeCode tc; // NTSC nondrop
// TimeCode tc("0:0:0:0", TRUE, FALSE); // NTSC nondrop
// TimeCode tc("0:0:0:0", FALSE, FALSE); // PAL nondrop
TimeCode tc1("0:0:0:0", TRUE, TRUE); // NTSC drop
TimeCode tc2("0:0:0:0", TRUE, TRUE); // NTSC drop
char string[TIMECODE_STRING_LENGTH];
/* strcpy(string, "1:2:3:4"); */
strcpy(string, "0:0:0:0");
tc1.SetTime(string);
tc1.GetString(string);
printf("timecode = <%s>\n", string);
int x;
for (x= 0; x < 200000; x++) {
tc1++;
tc1.GetString(string);
tc2.SetTime(string);
printf("timecode = <%s> %s (%d)\n",
string, tc1==tc2?"ok":"BAD!!!", (int)tc1.GetTime());
}
return(0);
}
#endif /* TEST */