Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

373 lines
9.7 KiB

  1. /**********************************************************************
  2. Copyright (c) 1996 - 1998 Microsoft Corporation. All Rights Reserved.
  3. TimeCode
  4. A class to describe and manipulate SMPTE timecodes.
  5. If we extend this class, fix its bugs, and use it in all applications,
  6. then we will have consistent and error free code!
  7. NOTE: I have provided the dropframe math derivations in the code along
  8. with references to the appropriate texts.
  9. TODO: PAL Dropframe math. (fairly consistent, base it on the NTSC drop code)
  10. Check values and generate EXCEPTIONS on errs!
  11. Extend timecode class to other video types/rates
  12. Flesh out the operator overloads (+=,-=,+,- all make good sense)
  13. Ken Greenebaum, November 1996
  14. **********************************************************************/
  15. #include <wtypes.h>
  16. #include <stdio.h>
  17. #include "timecode.h"
  18. // Initialize constants!
  19. const char TimeCode::NTSC_MODE = (char)0x01;
  20. const char TimeCode::PAL_MODE = (char)0x00;
  21. const char TimeCode::FORMAT_MASK = (char)0xFE;
  22. const char TimeCode::DROPFRAME_MODE = (char)0x02;
  23. const char TimeCode::NO_DROPFRAME_MODE = (char)0x00;
  24. const char TimeCode::DROPFRAME_MASK = (char)0xFD;
  25. const char TimeCode::NTSC_NODROP = NTSC_MODE | NO_DROPFRAME_MODE;
  26. const char TimeCode::NTSC_DROP = NTSC_MODE | DROPFRAME_MODE;
  27. const char TimeCode::PAL_NODROP = PAL_MODE | NO_DROPFRAME_MODE;
  28. const char TimeCode::PAL_DROP = PAL_MODE | DROPFRAME_MODE;
  29. TimeCode::TimeCode()
  30. {
  31. SetFormat(TRUE, FALSE); // NTSC, no dropframe
  32. SetTime(0, 0, 0, 0); // 00:00:00:00
  33. }
  34. TimeCode::TimeCode(char *string, BOOL ntsc, BOOL drop)
  35. {
  36. SetFormat(ntsc, drop);
  37. SetTime(string);
  38. }
  39. TimeCode::TimeCode(int h, int m, int s, int f, BOOL ntsc, BOOL d)
  40. {
  41. SetFormat(ntsc, d);
  42. SetTime(h, m, s, f);
  43. }
  44. void
  45. TimeCode::operator++(int unusedPostfixThingy) // postfix
  46. {
  47. operator++(); // call the prefix operator for the sake of consistency
  48. }
  49. void
  50. TimeCode::operator++() // prefix
  51. {
  52. if(_frames.dirty)
  53. _FramesFromHMSF();
  54. _frames.frames++;
  55. _frames.dirty = FALSE;
  56. _hmsf.dirty = TRUE;
  57. }
  58. void
  59. TimeCode::operator--(int unusedPostfixThingy) // postfix
  60. {
  61. operator--(); // call the prefix operator for the sake of consistency
  62. }
  63. void
  64. TimeCode::operator--() // prefix
  65. {
  66. if(_frames.dirty)
  67. _FramesFromHMSF();
  68. _frames.frames--; // XXX shouldn't we check for wrap?
  69. _frames.dirty = FALSE;
  70. _hmsf.dirty = TRUE;
  71. }
  72. void
  73. TimeCode::SetFormat(BOOL ntsc, BOOL drop)
  74. {
  75. _timeCodeFormat = (ntsc ? NTSC_MODE : PAL_MODE) |
  76. (drop ? DROPFRAME_MODE : NO_DROPFRAME_MODE);
  77. }
  78. void
  79. TimeCode::SetTime(int hours, int minutes, int seconds, int frames)
  80. {
  81. _hmsf.hours = hours;
  82. _hmsf.minutes = minutes;
  83. _hmsf.seconds = seconds;
  84. _hmsf.frames = frames;
  85. _hmsf.dirty = FALSE;
  86. _frames.dirty = TRUE; // invalidate the frames cache
  87. // XXX should we sanity check the timecode value?
  88. }
  89. void
  90. TimeCode::SetTime(char *string)
  91. {
  92. sscanf(string, "%d:%d:%d:%d",
  93. &_hmsf.hours, &_hmsf.minutes, &_hmsf.seconds, &_hmsf.frames);
  94. _hmsf.dirty = FALSE;
  95. _frames.dirty = TRUE; // invalidate the frames cache
  96. // XXX should we sanity check the timecode value?
  97. }
  98. void
  99. TimeCode::GetTime(int *hours, int *minutes, int *seconds, int *frames)
  100. {
  101. if(_hmsf.dirty)
  102. _HMSFfromFrames();
  103. *hours = _hmsf.hours;
  104. *minutes = _hmsf.minutes;
  105. *seconds = _hmsf.seconds;
  106. *frames = _hmsf.frames;
  107. }
  108. LONGLONG
  109. TimeCode::GetTime()
  110. {
  111. if(_frames.dirty)
  112. _FramesFromHMSF();
  113. return(_frames.frames);
  114. }
  115. void
  116. TimeCode::GetString(char *string) {
  117. if(_hmsf.dirty)
  118. _HMSFfromFrames();
  119. wsprintf(string, "%02d:%02d:%02d:%02d",
  120. _hmsf.hours, _hmsf.minutes, _hmsf.seconds, _hmsf.frames);
  121. }
  122. /**********************************************************************
  123. Dropframe math derivation:
  124. NTSC dropframe Pg208 Video Demystified:
  125. To resolve the color timing error, the first two frame numbers
  126. (0, and 1) at the start of each minute, except for minutes 0, 10, 20, 30,
  127. 40, and 50, are omitted from the count.
  128. So:
  129. The number of frames of video in an NTSC dropframe hour =
  130. 30 frames/second * 60 seconds/minute * 60 minutes/hour
  131. - 60 minutes * 2 frames dropped/minute
  132. + 6 excepted minutes * 2 frames not dropped
  133. = 107892
  134. The number of frames of video in an NTSC dropframe minute =
  135. 30 frames/second * 60 seconds/minute - 2 if minute != 0,10,20,30,40 or 50!
  136. There are 6 10 minute cycles in an hour.
  137. Each cycle has 17982 frames:
  138. 9 dropframe min * ((30 frames/sec * 60 sec/min) - 2 dropframes/min) frames/min
  139. 1 nondrop min * 30 frames/second * 60 sec/min
  140. **********************************************************************/
  141. void
  142. TimeCode::_HMSFfromFrames()
  143. {
  144. LONGLONG frames = _frames.frames;
  145. int units;
  146. int tmp;
  147. switch(_timeCodeFormat) {
  148. case NTSC_NODROP:
  149. _hmsf.hours = (int)(frames / 108000.0);
  150. frames-= _hmsf.hours * 108000;
  151. _hmsf.minutes = (int)(frames / 1800.0);
  152. frames-= _hmsf.minutes * 1800;
  153. _hmsf.seconds = (int)(frames / 30.0);
  154. frames-= _hmsf.seconds * 30;
  155. _hmsf.frames = (int)frames;
  156. break;
  157. case NTSC_DROP:
  158. _hmsf.hours = (int)(frames / 107892.0);
  159. frames-= _hmsf.hours * 107892;
  160. // remove 10 minute cycles
  161. tmp = (int)(frames / 17982.0);
  162. _hmsf.minutes = 10 * tmp;
  163. frames-= tmp * 17982;
  164. // remaining minutes < complete 10 minute cycle
  165. // first minute (0) would not drop frames
  166. if(frames >= 1800) {
  167. _hmsf.minutes++;
  168. frames-=1800;
  169. tmp = (int)(frames / 1798.0);
  170. _hmsf.minutes+= tmp;
  171. frames-= tmp * 1798;
  172. }
  173. // remaining seconds <= 60
  174. _hmsf.seconds = 0;
  175. units = _hmsf.minutes - 10*(_hmsf.minutes/10); // remove 10s
  176. if(!units) { // the 0, 10, 20, 30, 40, 50 minute
  177. _hmsf.seconds = (int)(frames / 30.0);
  178. frames-= _hmsf.seconds * 30;
  179. }
  180. else { // not an exception min(the 1st secof this min has 28frames)
  181. if(frames>=28) {
  182. frames-=28;
  183. _hmsf.seconds = 1;
  184. // the rest are 30 frame seconds
  185. _hmsf.seconds+= (int)(frames / 30.0);
  186. frames-= (_hmsf.seconds-1) * 30;
  187. }
  188. }
  189. _hmsf.frames = (int)frames;
  190. _hmsf.frames += ((units)&&(_hmsf.seconds==0))?2:0; // a drop second
  191. break;
  192. case PAL_NODROP:
  193. _hmsf.hours = (int)(frames / 90000.0);
  194. frames-= _hmsf.hours * 90000;
  195. _hmsf.minutes = (int)(frames / 1500.0);
  196. frames-= _hmsf.minutes * 1500;
  197. _hmsf.seconds = (int)(frames / 25.0);
  198. frames-= _hmsf.seconds * 25;
  199. _hmsf.frames = (int)frames;
  200. break;
  201. case PAL_DROP: fprintf(stderr,
  202. "TimeCode::_HMSfromFrames: PAL_DROP not implemented!\n");
  203. break;
  204. default:
  205. // XXX really should throw an exception!
  206. fprintf(stderr,
  207. "TimeCode::_HMSfromFrames: unknown _timeCodeFormat %d\n",
  208. _timeCodeFormat);
  209. break;
  210. }
  211. _hmsf.dirty = FALSE;
  212. }
  213. void
  214. TimeCode::_FramesFromHMSF()
  215. {
  216. LONGLONG frames;
  217. //int exceptionalMinutes;
  218. switch(_timeCodeFormat) {
  219. case NTSC_NODROP:
  220. frames = _hmsf.frames;
  221. frames+= _hmsf.seconds * 30;
  222. frames+= _hmsf.minutes * 1800; // 30f/s * 60s/min
  223. frames+= _hmsf.hours * 108000; //30f/s*60s/min*60min/hr
  224. break;
  225. case NTSC_DROP: // see derivation for details!
  226. frames = _hmsf.hours * 107892; // for every hour
  227. frames+= _hmsf.minutes * 1800; // for every minute, except...
  228. // now take away 2 frames per non-exempted minute!
  229. frames-= ((_hmsf.minutes - _hmsf.minutes/10) * 2);
  230. frames+= _hmsf.seconds * 30;
  231. frames+= _hmsf.frames;
  232. break;
  233. case PAL_NODROP:
  234. frames = _hmsf.frames;
  235. frames+= _hmsf.seconds * 25;
  236. frames+= _hmsf.minutes * 1500; // 25f/s * 60s/min
  237. frames+= _hmsf.hours * 90000; //25f/s*60s/min*60min/hr
  238. break;
  239. case PAL_DROP: fprintf(stderr,
  240. "TimeCode::_HMSfromFrames: PAL_DROP not implemented!\n");
  241. break;
  242. default:
  243. // XXX really should throw an exception!
  244. fprintf(stderr,
  245. "TimeCode::_HMSfromFrames: unknown _timeCodeFormat %d\n",
  246. _timeCodeFormat);
  247. break;
  248. }
  249. _frames.frames = frames; // ;)
  250. _frames.dirty = FALSE;
  251. }
  252. #ifdef TEST
  253. #include <string.h>
  254. #include <stdio.h>
  255. __cdecl
  256. main()
  257. {
  258. // TimeCode tc; // NTSC nondrop
  259. // TimeCode tc("0:0:0:0", TRUE, FALSE); // NTSC nondrop
  260. // TimeCode tc("0:0:0:0", FALSE, FALSE); // PAL nondrop
  261. TimeCode tc1("0:0:0:0", TRUE, TRUE); // NTSC drop
  262. TimeCode tc2("0:0:0:0", TRUE, TRUE); // NTSC drop
  263. char string[TIMECODE_STRING_LENGTH];
  264. /* strcpy(string, "1:2:3:4"); */
  265. strcpy(string, "0:0:0:0");
  266. tc1.SetTime(string);
  267. tc1.GetString(string);
  268. printf("timecode = <%s>\n", string);
  269. int x;
  270. for (x= 0; x < 200000; x++) {
  271. tc1++;
  272. tc1.GetString(string);
  273. tc2.SetTime(string);
  274. printf("timecode = <%s> %s (%d)\n",
  275. string, tc1==tc2?"ok":"BAD!!!", (int)tc1.GetTime());
  276. }
  277. return(0);
  278. }
  279. #endif /* TEST */