AJA NTV2 SDK  17.1.1.1245
NTV2 SDK 17.1.1.1245
ancillarydata_timecode_vitc.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: MIT */
9 #include <ios>
10 #include <iomanip>
11 
12 using namespace std;
13 
14 
16 {
17  Init();
18 }
19 
20 
22 {
23  Init();
24  *this = clone;
25 }
26 
27 
29 {
30  Init();
31  if (pClone)
32  *this = *pClone;
33 }
34 
35 
37 {
38  Init();
39 }
40 
41 
43 {
48 
49  m_vitcType = AJAAncillaryData_Timecode_VITC_Type_Unknown; // note: "Unknown" defaults to "Timecode" for transmit
50  SetLocationLineNumber(14); // Assume F1 otherwise SetLocationLineNumber(277);
51 }
52 
53 
55 {
56  if (this != &inRHS) // ignore self-assignment
57  {
58  AJAAncillaryData_Timecode::operator= (inRHS); // copy the base class stuff
59  // copy the local stuff
60  m_vitcType = inRHS.m_vitcType;
61  }
62  return *this;
63 }
64 
65 
67 {
69  Init ();
70 }
71 
72 
74 {
76 
77  // reality check...
79  {
80  Init(); // load default values
81  status = AJA_STATUS_FAIL;
82  m_rcvDataValid = false;
83  }
84  else
85  {
86  // we have some kind of payload data - try to parse it
88  }
89  return status;
90 }
91 
92 
94 {
97 
99  if (AJA_FAILURE(status))
100  return status;
101 
102  status = EncodeLine(&m_payload[0]);
103  if (AJA_FAILURE(status))
104  return status;
105 
106  // round-trip: TEST ONLY!
107  //bool bResult = DecodeLine (m_pPayload);
108 
110  return AJA_STATUS_SUCCESS;
111 }
112 
113 
115 {
119  {
120  m_vitcType = inType;
121  return AJA_STATUS_SUCCESS;
122  }
123  return AJA_STATUS_RANGE;
124 }
125 
126 
127 
129 {
130  // note: BIG ASSUMPTION! If the user deliberately captured analog line 19 on either field,
131  // we're assuming it was for the sake of getting captioning data (NTSC/525-line).
132  // The only way we could know "for sure" would be to run ParsePayloadData() on
133  // the payload data, but that's not a static method so you'd have to recast the
134  // AJAAncillaryData object anyway!
135  if (pInAncData->GetDataCoding() == AJAAncDataCoding_Raw)
136  if (pInAncData->GetLocationLineNumber() == 14 || pInAncData->GetLocationLineNumber() == 277)
138 
139  return AJAAncDataType_Unknown;
140 }
141 
142 
143 
144 //-------------------------------------------------------------
145 // VITC Decode code (ported/stolen from ntv2vitc.cpp)
146 
147 
148  // According to SMPTE-12M, in NTSC the VITC waveform should begin no EARLIER than 10.0 usecs
149  // from the leading edge of sync, which translates to approx 13 pixels from the beginning of
150  // active video. In PAL, the spec says the VITC waveform should start no earlier than 11.2 usecs
151  // from the leading edge of sync, or 19 pixels from the beginning of active video. Just to be
152  // on the safe side, we're going to start looking for the first leading edge at pixel 10, and
153  // stop looking (if we haven't found it) at pixel 30.
154 const uint32_t VITC_DECODE_START_WINDOW = 10;
155 const uint32_t VITC_DECODE_END_WINDOW = 30;
156 
157 const uint8_t VITC_Y_CLIP = 102; // 8-bit YCbCr threshold (assumes black = 16)
158 
159  // levels
160 const uint8_t VITC_YUV8_LO = 0x10; // '0'
161 const uint8_t VITC_YUV8_HI = 0xC0; // '1'
162 
163 
164 
165 // return the VITC bit level (true or false) at pixelNum. pLine points to the beginning
166 // of the line (i.e. the first byte in active video).
167 static bool getVITCLevel (uint32_t pixelNum, const uint8_t * pLine)
168 {
169  uint8_t luma = pLine[pixelNum];
170  return luma > VITC_Y_CLIP;
171 }
172 
173 
174 // Implements one bit of the SMPTE-12M CRC polynomial: x^8 + 1
175 // Pretty simple: if the current ms bit of the shift register is 0, shift left, adding the new bit to the ls position.
176 // if the current ms bit of the shift register is 1, shift left, adding the inverse of the new bit to the ls position.
177 static void addToCRC (const bool inBit, uint8_t & inOutCRC)
178 {
179  uint8_t newBit = 0;
180 
181  // a more concise way to say this is: newBit = (inOutCRC >> 7) ^ inBit;
182  if (inOutCRC & 0x80)
183  newBit = (inBit ? 0 : 1);
184  else
185  newBit = (inBit ? 1 : 0);
186 
187  inOutCRC = (inOutCRC << 1) + newBit;
188 }
189 
190 
203 {
204  bool bResult = false;
205 
206  uint8_t CRC = 0; // accumulate the CRC here
207  uint8_t tcData[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; // accumulate the data bits here
208 
224  uint32_t currIndex = 0; // our running pixel index
225  uint32_t i;
226 
227  // First look for the beginning of the first start bit.
228  bool lastPixel = getVITCLevel(VITC_DECODE_START_WINDOW, pLine);
229  for (currIndex = VITC_DECODE_START_WINDOW+1; currIndex < VITC_DECODE_END_WINDOW; currIndex++)
230  {
231  bool thisPixel = getVITCLevel(currIndex, pLine);
232 
233  // look for a 0 -> 1 edge
234  if (!lastPixel && thisPixel)
235  break;
236  }
237 
238  // if we didn't find a 0 -> 1 edge within the window, bail
239  if (currIndex == VITC_DECODE_END_WINDOW)
240  return false;
241 
242  // we found an edge - now make sure the next 3 pixels are '1' (this will put us in the
243  // middle of the first start bit cell)
244  if ( !getVITCLevel(currIndex+1, pLine)
245  || !getVITCLevel(currIndex+2, pLine)
246  || !getVITCLevel(currIndex+3, pLine) )
247  return false;
248  else
249  currIndex += 3;
250 
251  // repeat for 9 groups...
252  for (uint8_t group = 0; group < 9; group++)
253  {
254  uint8_t data = 0;
255 
256  // assuming our index is sitting in the middle of the leading ('1') start bit of the group,
257  // look for the 1 -> 0 start bit transition. This will become our reference for the rest of
258  // the group. Ideally, the transition should happen 4.5 pixels from now, but we'll look as
259  // far as 8 pixels out.
260  for (i = 1; i < 8; i++)
261  {
262  if ( !getVITCLevel(currIndex+i, pLine) )
263  break;
264  }
265 
266  // if we couldn't find a 1->0 transition, assume we're broken and bail
267  if (i == 8)
268  break;
269  else
270  currIndex += i; // currIndex now sits at the beginning of the 2nd ('0' start bit) of the group
271 
272  // all ten bits are used to calculate the CRC, so add the two start bits now
273  addToCRC(true, CRC);
274  addToCRC(false, CRC);
275 
276  // position index in the middle of the first data bit (i.e. 1.5 bitcells from here)
277  currIndex += 11;
278 
279  // add the 8 data bits, assuming 7.5 pixels per VITC bit cell.
280  for (i = 0; i < 8; i++)
281  {
282  bool bit = getVITCLevel(currIndex, pLine);
283  addToCRC(bit, CRC);
284  currIndex += (i%2 ? 8 : 7); // alternate between 7 and 8 pixels to maintain 7.5 average
285 
286  // the data bits are transmit ls-bit first, so shift in from the right
287  data = (bit ? 0x80 : 0) + (data >> 1);
288  }
289 
290  // put the collected data byte where it belongs
291  if (group < 8) // this is a data group
292  tcData[group] = data;
293 
294  else // this is the last (CRC) group: after it's finished, the CRC register should be
295  { // one of the magic numbers or we had an error
296  if (CRC == 0x00)
297  {
298  m_vitcType = AJAAncillaryData_Timecode_VITC_Type_Timecode; // we've got valid "timecode" data
299  bResult = true;
300  }
301  else if (CRC == 0xFF)
302  {
303  m_vitcType = AJAAncillaryData_Timecode_VITC_Type_FilmData; // we've got a valid RP-201 "Film Data Block"
304  bResult = true;
305  }
306 
307  else if (CRC == 0x0F)
308  {
309  m_vitcType = AJAAncillaryData_Timecode_VITC_Type_ProdData; // we've got a valid RP-201 "Production Data Block"
310  bResult = true;
311  }
312 
313  else
314  {
315  m_vitcType = AJAAncillaryData_Timecode_VITC_Type_Unknown; // unrecognized CRC? Assume it's bad data...
316  bResult = false;
317  }
318  }
319 
320  // At this point, currIndex should now be positioned in the middle of the NEXT group's leading ('1') start bit
321 
322  } // for (int group...
323 
324 
325  // finished with all of the groups! If we think we've been successful, transfer the data bytes to the RP188_STRUCT
326  if (bResult)
327  {
328  // the "time" digits are in the ls nibbles of each received byte
329  // (note: SetTimeHexValue() performs the needed masking)
330  SetTimeHexValue(kTcFrameUnits, tcData[0]); // frame units);
331  SetTimeHexValue(kTcFrameTens, tcData[1]); // frame tens
332  SetTimeHexValue(kTcSecondUnits, tcData[2]); // second units
333  SetTimeHexValue(kTcSecondTens, tcData[3]); // second tens
334  SetTimeHexValue(kTcMinuteUnits, tcData[4]); // minute units
335  SetTimeHexValue(kTcMinuteTens, tcData[5]); // minute tens
336  SetTimeHexValue(kTcHourUnits, tcData[6]); // hour units
337  SetTimeHexValue(kTcHourTens, tcData[7]); // hour tens
338 
339  // the Binary Group "digits" are in the ms nibbles of each received byte
340  // (note: SetBinaryGroupHexValue() performs the needed masking)
341  SetBinaryGroupHexValue(kBg1, (tcData[0] >> 4)); // BG 1
342  SetBinaryGroupHexValue(kBg2, (tcData[1] >> 4)); // BG 2
343  SetBinaryGroupHexValue(kBg3, (tcData[2] >> 4)); // BG 3
344  SetBinaryGroupHexValue(kBg4, (tcData[3] >> 4)); // BG 4
345  SetBinaryGroupHexValue(kBg5, (tcData[4] >> 4)); // BG 5
346  SetBinaryGroupHexValue(kBg6, (tcData[5] >> 4)); // BG 6
347  SetBinaryGroupHexValue(kBg7, (tcData[6] >> 4)); // BG 7
348  SetBinaryGroupHexValue(kBg8, (tcData[7] >> 4)); // BG 8
349  }
350 
351  return bResult;
352 }
353 
354 
355 //-------------------------------------------------------------
356 // VITC Encode code (ported/stolen from ntv2vitc.cpp)
357 
358 #ifdef USE_SMPTE_266M
359 
360 // output one pixel in the line at the designated index. <level> is the 8-bit (Y) video level for this pixel.
361 static inline void DoVITCPixel (uint8_t * pOutLine, const uint32_t inPixelNum, const uint8_t inLevel)
362 {
363  pOutLine[inPixelNum] = inLevel;
364 }
365 
366 
367 // Output a four-pixel transition that crosses the 50% point exactly on a pixel.
368 // SMPTE-266 refers to this as "Curve B"
369 static void DoNormalTransition (uint8_t *pLine, uint32_t& pixelIndex, bool bBit0, bool bBit1)
370 {
371  if (!bBit0 && !bBit1) // 0 -> 0 transition:
372  {
373  // Output 4 pixels of "low" level...
374  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_LO);
375  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_LO);
376  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_LO);
377  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_LO);
378  }
379  else if (!bBit0 && bBit1) // 0 -> 1 transition:
380  {
381  DoVITCPixel (pLine, pixelIndex++, 0x2A);
382  DoVITCPixel (pLine, pixelIndex++, 0x68); // 50%
383  DoVITCPixel (pLine, pixelIndex++, 0xA6);
384  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_HI);
385  }
386  else if (bBit0 && !bBit1) // 1 -> 0 transition:
387  {
388  DoVITCPixel (pLine, pixelIndex++, 0xA6);
389  DoVITCPixel (pLine, pixelIndex++, 0x68); // 50%
390  DoVITCPixel (pLine, pixelIndex++, 0x2A);
391  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_LO);
392  }
393  else // 1 -> 1 transition:
394  {
395  // Output 4 pixels of "high" level...
396  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_HI);
397  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_HI);
398  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_HI);
399  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_HI);
400  }
401 }
402 
403 
404 // Output a four-pixel transition that crosses the 50% point exactly between two pixels. This transition is
405 // used between two bits of a "pair", making each bit a "half" pixel in length (e.g. 7.5 pixels each).
406 static void DoHalfTransition (uint8_t *pLine, uint32_t& pixelIndex, bool bBit0, bool bBit1)
407 {
408  if (!bBit0 && !bBit1) // 0 -> 0 transition:
409  {
410  // Output 3 pixels of "low" level...
411  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_LO);
412  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_LO);
413  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_LO);
414  }
415  else if (!bBit0 && bBit1) // 0 -> 1 transition:
416  {
417  DoVITCPixel (pLine, pixelIndex++, 0x3C);
418  DoVITCPixel (pLine, pixelIndex++, 0x94);
419  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_HI);
420  }
421  else if (bBit0 && !bBit1) // 1 -> 0 transition:
422  {
423  DoVITCPixel (pLine, pixelIndex++, 0x94);
424  DoVITCPixel (pLine, pixelIndex++, 0x3C);
425  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_LO);
426  }
427  else // 1 -> 1 transition:
428  {
429  // Output 3 pixels of "high" level...
430  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_HI);
431  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_HI);
432  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_HI);
433  }
434 }
435 
436 
437 // Encodes 2 VITC bits (15 pixels), assuming the previous bit ended on <bPrevBit>.
438 static void DoVITCBitPair (uint8_t *pLine, uint32_t& pixelIndex, bool bPrevBit, bool bBit0, bool bBit1)
439 {
440  uint8_t level;
441 
442  // 1st: do normal (SMPTE-266 calls it "Curve B") transition (50% point falls on exact pixel) from previous bit to Bit0 (pixels #0 - #3)
443  DoNormalTransition(pLine, pixelIndex, bPrevBit, bBit0);
444 
445  // 2nd: do 3 pixels of "Bit0" level
446  level = (bBit0 ? VITC_YUV8_HI : VITC_YUV8_LO);
447  DoVITCPixel (pLine, pixelIndex++, level); // pixel #4
448  DoVITCPixel (pLine, pixelIndex++, level); // pixel #5
449  DoVITCPixel (pLine, pixelIndex++, level); // pixel #6
450  DoVITCPixel (pLine, pixelIndex++, level); // pixel #7
451 
452  // 3rd: do "half" (SMPTE-266 calls it "Curve A") transition (50% point falls between two pixels) from Bit0 to Bit 1 (pixels #8 - #10)
453  DoHalfTransition(pLine, pixelIndex, bBit0, bBit1);
454 
455  // 4th: do remaining pixels of "Bit 1" level this is the place to drop a pixel if desired)
456  level = (bBit1 ? VITC_YUV8_HI : VITC_YUV8_LO);
457  DoVITCPixel (pLine, pixelIndex++, level); // pixel #11
458  DoVITCPixel (pLine, pixelIndex++, level); // pixel #12
459  DoVITCPixel (pLine, pixelIndex++, level); // pixel #13
460  DoVITCPixel (pLine, pixelIndex++, level); // pixel #14
461 }
462 
463 
464 // Encodes the supplied timecode data to VITC and inserts it in the designated video line.
465 // If this is RP-201 "Film" or "Production" data, we jigger the CRC per RP-201 specs.
467 {
468  uint32_t i;
469  uint32_t pixelIndex = 0; // running pixel count
470  uint32_t bitPair;
471  bool bBit0, bBit1;
472  bool bPrevBit = false; // the last bit of the previous group
473  uint8_t CRC = 0;
474 
475  // 1st: do 26 pixels of black before 1st start bit
476  for (i = 0; i < 26; i++)
477  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_LO);
478 
479  // VITC consists of 9 "groups" of 10 bits each (or 5 bit-pairs). Each group starts with a '1' bit and a '0' bit,
480  // followed by 8 data bits. The first 8 groups carry the 64 bits of VITC data, and the last group carries an 8-bit
481  // CRC calculated from the preceding 82 bits.
482  for (uint8_t group(0); group < kNumTimeDigits; group++)
483  {
484  uint8_t tcData, bgData;
485  GetTimeHexValue(group, tcData);
486  GetBinaryGroupHexValue(group, bgData);
487  uint8_t data = (bgData << 4) + tcData;
488 
489  // Note: we do VITC bit "pairs" because the timing works out to be 7.5 pixels per VITC bit.
490  // A bit pair is exactly 15 pixels, so it's more convenient to work with
491  DoVITCBitPair(pLine, pixelIndex, bPrevBit, 1, 0); // Start bits: 1, 0
492  addToCRC (1, CRC);
493  addToCRC (0, CRC); // note: start bits are included in CRC
494  bPrevBit = false;
495 
496  for (bitPair = 0; bitPair < 4; bitPair++)
497  {
498  bBit0 = (data & 0x01) > 0;
499  bBit1 = (data & 0x02) > 0;
500 
501  DoVITCBitPair(pLine, pixelIndex, bPrevBit, bBit0, bBit1); // 8 Data bits per group (ls bit first)
502 
503  // add bits to CRC
504  addToCRC (bBit0, CRC);
505  addToCRC (bBit1, CRC);
506 
507  bPrevBit = bBit1;
508  data = (data >> 2);
509  }
510  }
511 
512 
513  // do the final "CRC" group
514  DoVITCBitPair(pLine, pixelIndex, bPrevBit, 1, 0); // Start bits: 1, 0
515 
516  // the CRC also includes the start bits for the CRC group, so add those now
517  addToCRC (1, CRC);
518  addToCRC (0, CRC);
519  bPrevBit = false;
520 
521  // if we're transmitting standard VITC timecode, the CRC is sent as-is
522  // if this is RP-201 "Film Data", the CRC needs to be inverted
523  // if this is RP-201 "Production Data", the only the "upper" nibble of the CRC is inverted
525  CRC = ~CRC;
527  CRC = CRC ^ 0x0F;
528 
529  for (i = 0; i < 4; i++)
530  {
531  bBit0 = (CRC & 0x80) > 0;
532  bBit1 = (CRC & 0x40) > 0;
533 
534  DoVITCBitPair(pLine, pixelIndex, bPrevBit, bBit0, bBit1); // 8 Data bits per group (ms bit first)
535 
536  bPrevBit = bBit1;
537  CRC = (CRC << 2);
538  }
539 
540  // just in case we ended on a '1' data bit, do a final transition back to black
541  DoNormalTransition(pLine, pixelIndex, bPrevBit, 0);
542 
543  // fill the remainder of the line with Black (note that we're assuming 720 active pixels!)
544  const uint32_t remainingPixels (GetDC() > pixelIndex ? GetDC() - pixelIndex : 0);
545  if (remainingPixels)
546  for (i = 0; i < (uint32_t)remainingPixels; i++)
547  DoVITCPixel (pLine, pixelIndex++, VITC_YUV8_LO);
548 
549  return AJA_STATUS_SUCCESS;
550 }
551 
552 //------------------------------------------------------------------------------
553 
554 #else // use original NTV2 VITC algorithm
555 
556 // output one pixel in the line at the designated index. <level> is a relative pixel value,
557 // where "0.0" represents the logic low level, and "1.0" represents the logic high level.
558 static void DoVITCPixel (uint8_t *pLine, uint32_t pixelNum, float level)
559 {
560  uint8_t luma;
561  if (level <= 0.0) luma = VITC_YUV8_LO;
562  else if (level >= 1.0) luma = VITC_YUV8_HI;
563  else luma = (uint8_t)((float)(VITC_YUV8_HI - VITC_YUV8_LO) * level) + VITC_YUV8_LO;
564 
565  pLine[pixelNum] = luma;
566 }
567 
568 
569 // Output a five-pixel transition that crosses the 50% point exactly on a pixel.
570 static void DoNormalTransition (uint8_t *pLine, uint32_t& pixelIndex, bool bBit0, bool bBit1)
571 {
572  if (!bBit0 && !bBit1)
573  {
574  // 0 -> 0 transition: output 5 pixels of "low" level
575  DoVITCPixel (pLine, pixelIndex++, (float)0.0);
576  DoVITCPixel (pLine, pixelIndex++, (float)0.0);
577  DoVITCPixel (pLine, pixelIndex++, (float)0.0);
578  DoVITCPixel (pLine, pixelIndex++, (float)0.0);
579  DoVITCPixel (pLine, pixelIndex++, (float)0.0);
580  }
581  else if (!bBit0 && bBit1)
582  {
583  // 0 -> 1 transition
584  DoVITCPixel (pLine, pixelIndex++, (float)0.01);
585  DoVITCPixel (pLine, pixelIndex++, (float)0.18);
586  DoVITCPixel (pLine, pixelIndex++, (float)0.50);
587  DoVITCPixel (pLine, pixelIndex++, (float)0.82);
588  DoVITCPixel (pLine, pixelIndex++, (float)0.99);
589  }
590  else if (bBit0 && !bBit1)
591  {
592  // 1 -> 0 transition
593  DoVITCPixel (pLine, pixelIndex++, (float)0.99);
594  DoVITCPixel (pLine, pixelIndex++, (float)0.82);
595  DoVITCPixel (pLine, pixelIndex++, (float)0.50);
596  DoVITCPixel (pLine, pixelIndex++, (float)0.18);
597  DoVITCPixel (pLine, pixelIndex++, (float)0.01);
598  }
599  else
600  {
601  // 1 -> 1 transition: output 5 pixels of "high" level
602  DoVITCPixel (pLine, pixelIndex++, (float)1.0);
603  DoVITCPixel (pLine, pixelIndex++, (float)1.0);
604  DoVITCPixel (pLine, pixelIndex++, (float)1.0);
605  DoVITCPixel (pLine, pixelIndex++, (float)1.0);
606  DoVITCPixel (pLine, pixelIndex++, (float)1.0);
607  }
608 }
609 
610 
611 // Output a four-pixel transition that crosses the 50% point exactly between two pixels. This transition is
612 // used between two bits of a "pair", making each bit a "half" pixel in length (e.g. 7.5 pixels each).
613 static void DoHalfTransition (uint8_t *pLine, uint32_t& pixelIndex, bool bBit0, bool bBit1)
614 {
615  if (!bBit0 && !bBit1)
616  {
617  // 0 -> 0 transition: output 4 pixels of "low" level
618  DoVITCPixel (pLine, pixelIndex++, (float)0.0);
619  DoVITCPixel (pLine, pixelIndex++, (float)0.0);
620  DoVITCPixel (pLine, pixelIndex++, (float)0.0);
621  DoVITCPixel (pLine, pixelIndex++, (float)0.0);
622  }
623  else if (!bBit0 && bBit1)
624  {
625  // 0 -> 1 transition
626  DoVITCPixel (pLine, pixelIndex++, (float)0.00);
627  DoVITCPixel (pLine, pixelIndex++, (float)0.33);
628  DoVITCPixel (pLine, pixelIndex++, (float)0.67);
629  DoVITCPixel (pLine, pixelIndex++, (float)1.00);
630  }
631  else if (bBit0 && !bBit1)
632  {
633  // 1 -> 0 transition
634  DoVITCPixel (pLine, pixelIndex++, (float)1.00);
635  DoVITCPixel (pLine, pixelIndex++, (float)0.67);
636  DoVITCPixel (pLine, pixelIndex++, (float)0.33);
637  DoVITCPixel (pLine, pixelIndex++, (float)0.00);
638  }
639  else
640  {
641  // 1 -> 1 transition: output 4 pixels of "high" level
642  DoVITCPixel (pLine, pixelIndex++, (float)1.0);
643  DoVITCPixel (pLine, pixelIndex++, (float)1.0);
644  DoVITCPixel (pLine, pixelIndex++, (float)1.0);
645  DoVITCPixel (pLine, pixelIndex++, (float)1.0);
646  }
647 }
648 
649 // Encodes 2 VITC bits (15 pixels), assuming the previous bit ended on <bPrevBit>.
650 // bDropBit can be used to make a 14-pixel pair to try and keep the VITC waveform in time with the spec
651 static void DoVITCBitPair (uint8_t *pLine, uint32_t& pixelIndex, bool bPrevBit, bool bBit0, bool bBit1, bool bDropBit)
652 {
653  float level; // 0.0 -> 1.0 relative level (0.0 = logic low, 1.0 = logic high)
654 
655  // 1st: do normal transition (50% point falls on exact pixel) from previous bit to Bit0 (pixels #0 - #4)
656  DoNormalTransition(pLine, pixelIndex, bPrevBit, bBit0);
657 
658  // 2nd: do 3 pixels of "Bit0" level
659  level = (bBit0 ? (float)1.0 : (float)0.0);
660  DoVITCPixel (pLine, pixelIndex++, level); // pixel #5
661  DoVITCPixel (pLine, pixelIndex++, level); // pixel #6
662  DoVITCPixel (pLine, pixelIndex++, level); // pixel #7
663 
664  // 3rd: do "half" transition (50% point falls between two pixels) from Bit0 to Bit 1 (pixels #8 - #11)
665  DoHalfTransition(pLine, pixelIndex, bBit0, bBit1);
666 
667  // 4th: do remaining pixels of "Bit 1" level this is the place to drop a pixel if desired)
668  level = (bBit1 ? (float)1.0 : (float)0.0);
669  DoVITCPixel (pLine, pixelIndex++, level); // pixel #12
670  DoVITCPixel (pLine, pixelIndex++, level); // pixel #13
671  if (!bDropBit)
672  DoVITCPixel (pLine, pixelIndex++, level); // pixel #14 (optional)
673 }
674 
675 
676 // Encodes the supplied timecode data to VITC and inserts it in the designated video line.
677 // If this is RP-201 "Film" or "Production" data, we jigger the CRC per RP-201 specs.
678 bool AJAAncillaryData_Timecode_VITC::EncodeLine (uint8_t *pLine)
679 {
680  bool bResult = true;
681  uint32_t i;
682  uint32_t pixelIndex = 0; // running pixel count
683  uint32_t group;
684  uint32_t bitPair;
685  bool bBit0, bBit1;
686  bool bPrevBit = false; // the last bit of the previous group
687  bool bDropBit = false;
688  uint8_t CRC = 0;
689 
690  // 1st: do 24 pixels of black before 1st start bit
691  for (i = 0; i < 24; i++)
692  DoVITCPixel (pLine, pixelIndex++, 0.0);
693 
694  // VITC consists of 9 "groups" of 10 bits each (or 5 bit-pairs). Each group starts with a '1' bit and a '0' bit,
695  // followed by 8 data bits. The first 8 groups carry the 64 bits of VITC data, and the last group carries an 8-bit
696  // CRC calculated from the preceding 82 bits.
697  for (group = 0; group < kNumTimeDigits; group++)
698  {
699  uint8_t tcData, bgData;
700  GetTimeHexValue(group, tcData);
701  GetBinaryGroupHexValue(group, bgData);
702  uint8_t data = (bgData << 4) + tcData;
703 
704  // Note: we do VITC bit "pairs" because the timing works out to be 7.5 pixels per VITC bit.
705  // A bit pair is exactly 15 pixels, so it's more convenient to work with
706  DoVITCBitPair(pLine, pixelIndex, bPrevBit, 1, 0, false); // Start bits: 1, 0
707  addToCRC (1, CRC);
708  addToCRC (0, CRC); // note: start bits are included in CRC
709  bPrevBit = false;
710 
711  for (bitPair = 0; bitPair < 4; bitPair++)
712  {
713  bBit0 = (data & 0x01) > 0;
714  bBit1 = (data & 0x02) > 0;
715 
716  // in every other group, drop a pixel in the last pixel pair to help keep time
717  bDropBit = (group % 2 && bitPair == 3);
718 
719  DoVITCBitPair(pLine, pixelIndex, bPrevBit, bBit0, bBit1, bDropBit); // 8 Data bits per group (ls bit first)
720 
721  // add bits to CRC
722  addToCRC (bBit0, CRC);
723  addToCRC (bBit1, CRC);
724 
725  bPrevBit = bBit1;
726  data = (data >> 2);
727  }
728  }
729 
730 
731  // do the final "CRC" group
732  DoVITCBitPair(pLine, pixelIndex, bPrevBit, 1, 0, false); // Start bits: 1, 0
733 
734  // the CRC also includes the start bits for the CRC group, so add those now
735  addToCRC (1, CRC);
736  addToCRC (0, CRC);
737  bPrevBit = false;
738 
739  // if we're transmitting standard VITC timecode, the CRC is sent as-is
740  // if this is RP-201 "Film Data", the CRC needs to be inverted
741  // if this is RP-201 "Production Data", the only the "upper" nibble of the CRC is inverted
743  CRC = ~CRC;
745  CRC = CRC ^ 0x0F;
746 
747  for (i = 0; i < 4; i++)
748  {
749  bBit0 = (CRC & 0x80) > 0;
750  bBit1 = (CRC & 0x40) > 0;
751 
752  DoVITCBitPair(pLine, pixelIndex, bPrevBit, bBit0, bBit1, bDropBit); // 8 Data bits per group (ms bit first)
753 
754  bPrevBit = bBit1;
755  CRC = (CRC << 2);
756  }
757 
758  // just in case we ended on a '1' data bit, do a final transition back to black
759  DoNormalTransition(pLine, pixelIndex, bPrevBit, 0);
760 
761 
762  // fill the remainder of the line with Black (note that we're assuming 720 active pixels!)
763  int32_t remainingPixels = m_DC - pixelIndex;
764 
765  if (remainingPixels > 0)
766  {
767  for (i = 0; i < (uint32_t)remainingPixels; i++)
768  DoVITCPixel (pLine, pixelIndex++, 0.0);
769  }
770 
771  return bResult;
772 }
773 
774 #endif // !USE_SMPTE_266M
775 
777 {
778  switch (inType)
779  {
780  case AJAAncillaryData_Timecode_VITC_Type_Timecode: return "timecode (CRC=0x00)";
781  case AJAAncillaryData_Timecode_VITC_Type_FilmData: return "RP-201 Film Data (CRC=0xFF)";
782  case AJAAncillaryData_Timecode_VITC_Type_ProdData: return "RP-201 Prod Data (CRC=0x0F)";
783  default: break;
784  }
785  return "??";
786 }
787 
788 
789 ostream & AJAAncillaryData_Timecode_VITC::Print (ostream & debugStream, const bool bShowDetail) const
790 {
791  debugStream << IDAsString() << "(" << ::AJAAncDataCodingToString(m_coding) << ")" << endl;
792  //debugStream << "SMPTE 12M VITC Analog Data (" << ((m_coding == AJAAncillaryDataCoding_Digital) ? "Digital" : ((m_coding == AJAAncillaryDataCoding_Analog) ? "Analog" : "???????")) << ")" << endl;
793  AJAAncillaryData_Timecode::Print (debugStream, bShowDetail); // print the generic stuff
794  debugStream << endl
795  << "VITC Type: " << VITCTypeToString (m_vitcType);
796  return debugStream;
797 }
VITC_YUV8_HI
const uint8_t VITC_YUV8_HI
Definition: ancillarydata_timecode_vitc.cpp:161
AJAAncillaryData_Timecode::kBg5
@ kBg5
Definition: ancillarydata_timecode.h:290
AJAAncillaryData_Timecode
This is the base class for the AJAAncillaryData_Timecode_ATC and AJAAncillaryData_Timecode_VITC class...
Definition: ancillarydata_timecode.h:36
AJAAncillaryData_Timecode::kTcFrameTens
@ kTcFrameTens
Definition: ancillarydata_timecode.h:272
AJAAncillaryData_Timecode::kTcSecondTens
@ kTcSecondTens
Definition: ancillarydata_timecode.h:274
getVITCLevel
static bool getVITCLevel(uint32_t pixelNum, const uint8_t *pLine)
Definition: ancillarydata_timecode_vitc.cpp:167
AJAAncillaryData_Timecode_VITC_Type_Unknown
@ AJAAncillaryData_Timecode_VITC_Type_Unknown
Definition: ancillarydata_timecode_vitc.h:30
AJAAncillaryData::m_DID
uint8_t m_DID
Official SMPTE ancillary packet ID (w/o parity)
Definition: ancillarydata.h:1152
AJAAncDataType_Unknown
@ AJAAncDataType_Unknown
Includes data that is valid, but we don't recognize.
Definition: ancillarydata.h:46
AJAAncillaryData_Timecode::kBg8
@ kBg8
Definition: ancillarydata_timecode.h:293
DoHalfTransition
static void DoHalfTransition(uint8_t *pLine, uint32_t &pixelIndex, bool bBit0, bool bBit1)
Definition: ancillarydata_timecode_vitc.cpp:406
AJAAncillaryData_Timecode::kNumTimeDigits
@ kNumTimeDigits
Definition: ancillarydata_timecode.h:279
AJA_STATUS_SUCCESS
@ AJA_STATUS_SUCCESS
Definition: types.h:381
AJAAncillaryData_Timecode_VITC_Type_ProdData
@ AJAAncillaryData_Timecode_VITC_Type_ProdData
Definition: ancillarydata_timecode_vitc.h:33
AJAAncillaryData_Timecode::kBg4
@ kBg4
Definition: ancillarydata_timecode.h:289
ancillarydata_timecode_vitc.h
Declares the AJAAncillaryData_Timecode_VITC class.
AJAAncillaryData_Timecode_VITC::ParsePayloadData
virtual AJAStatus ParsePayloadData(void)
Parses out (interprets) the "local" ancillary data from my payload data.
Definition: ancillarydata_timecode_vitc.cpp:73
AJAAncDataType
AJAAncDataType
Identifies the ancillary data types that are known to this module.
Definition: ancillarydata.h:44
AJAAncDataType_Timecode_VITC
@ AJAAncDataType_Timecode_VITC
SMPTE 12-M Vertical Interval Timecode (aka "VITC")
Definition: ancillarydata.h:49
AJAAncillaryData_Timecode::kBg7
@ kBg7
Definition: ancillarydata_timecode.h:292
AJAAncillaryData_Timecode_VITC::m_vitcType
AJAAncillaryData_Timecode_VITC_Type m_vitcType
The "type" of VITC received or to be transmitted.
Definition: ancillarydata_timecode_vitc.h:120
AJAAncillaryData_VITC_SID
const uint8_t AJAAncillaryData_VITC_SID
Definition: ancillarydata_timecode_vitc.h:23
DoVITCPixel
static void DoVITCPixel(uint8_t *pOutLine, const uint32_t inPixelNum, const uint8_t inLevel)
Definition: ancillarydata_timecode_vitc.cpp:361
AJAAncillaryData::AllocDataMemory
AJAStatus AllocDataMemory(const uint32_t inNumBytes)
Definition: ancillarydata.cpp:171
AJAAncillaryData_Timecode_VITC_Type
AJAAncillaryData_Timecode_VITC_Type
Definition: ancillarydata_timecode_vitc.h:28
AJAAncillaryData_Timecode_VITC::RecognizeThisAncillaryData
static AJAAncDataType RecognizeThisAncillaryData(const AJAAncillaryData *pInAncData)
Definition: ancillarydata_timecode_vitc.cpp:128
AJAAncillaryData_Timecode::SetBinaryGroupHexValue
virtual AJAStatus SetBinaryGroupHexValue(uint8_t digitNum, uint8_t hexValue, uint8_t mask=0x0f)
Sets my raw "Binary Group" hex values.
Definition: ancillarydata_timecode.cpp:255
AJAAncillaryData_Timecode::kTcFrameUnits
@ kTcFrameUnits
Definition: ancillarydata_timecode.h:271
AJAAncDataCoding_Raw
@ AJAAncDataCoding_Raw
The ancillary data is in the form of a digitized waveform (e.g. CEA-608 captions, VITC,...
Definition: ancillarydata.h:477
AJAAncillaryData_Timecode::GetBinaryGroupHexValue
virtual AJAStatus GetBinaryGroupHexValue(uint8_t digitNum, uint8_t &hexValue, uint8_t mask=0x0f) const
Definition: ancillarydata_timecode.cpp:267
AJAAncillaryData_Timecode::kTcSecondUnits
@ kTcSecondUnits
Definition: ancillarydata_timecode.h:273
AJAAncillaryData_Timecode_VITC_Type_Timecode
@ AJAAncillaryData_Timecode_VITC_Type_Timecode
Definition: ancillarydata_timecode_vitc.h:31
AJAAncillaryData_Timecode_VITC::AJAAncillaryData_Timecode_VITC
AJAAncillaryData_Timecode_VITC()
Definition: ancillarydata_timecode_vitc.cpp:15
AJAAncillaryData_Timecode::SetTimeHexValue
virtual AJAStatus SetTimeHexValue(const uint8_t inDigitNum, const uint8_t inHexValue, const uint8_t inMask=0x0f)
Sets my raw "time" hex values.
Definition: ancillarydata_timecode.cpp:103
AJAStatus
AJAStatus
Definition: types.h:378
AJAAncillaryData_Timecode::kTcMinuteUnits
@ kTcMinuteUnits
Definition: ancillarydata_timecode.h:275
AJAAncillaryData_Timecode_VITC::GeneratePayloadData
virtual AJAStatus GeneratePayloadData(void)
Generate the payload data from the "local" ancillary data.
Definition: ancillarydata_timecode_vitc.cpp:93
AJAAncillaryData_Timecode::Clear
virtual void Clear(void)
Frees my allocated memory, if any, and resets my members to their default values.
Definition: ancillarydata_timecode.cpp:76
AJA_STATUS_FAIL
@ AJA_STATUS_FAIL
Definition: types.h:382
AJAAncillaryData_Timecode_VITC::DecodeLine
bool DecodeLine(const uint8_t *pInLine)
Definition: ancillarydata_timecode_vitc.cpp:202
AJAAncillaryData
I am the principal class that stores a single SMPTE-291 SDI ancillary data packet OR the digitized co...
Definition: ancillarydata.h:552
AJAAncillaryData_Timecode_VITC
This is the VITC-specific subclass of the AJAAncillaryData_Timecode class.
Definition: ancillarydata_timecode_vitc.h:41
AJAAncillaryData::GetPayloadData
virtual const uint8_t * GetPayloadData(void) const
Definition: ancillarydata.h:801
AJAAncillaryData_Timecode_VITC::EncodeLine
AJAStatus EncodeLine(uint8_t *pOutLine) const
Definition: ancillarydata_timecode_vitc.cpp:466
VITC_YUV8_LO
const uint8_t VITC_YUV8_LO
Definition: ancillarydata_timecode_vitc.cpp:160
VITC_DECODE_START_WINDOW
const uint32_t VITC_DECODE_START_WINDOW
Definition: ancillarydata_timecode_vitc.cpp:154
AJAAncillaryData_Timecode_VITC::Init
void Init(void)
Definition: ancillarydata_timecode_vitc.cpp:42
VITC_DECODE_END_WINDOW
const uint32_t VITC_DECODE_END_WINDOW
Definition: ancillarydata_timecode_vitc.cpp:155
AJAAncillaryData_Timecode_VITC::Clear
virtual void Clear(void)
Frees my allocated memory, if any, and resets my members to their default values.
Definition: ancillarydata_timecode_vitc.cpp:66
AJAAncillaryData::m_coding
AJAAncDataCoding m_coding
Analog or digital data.
Definition: ancillarydata.h:1156
DoVITCBitPair
static void DoVITCBitPair(uint8_t *pLine, uint32_t &pixelIndex, bool bPrevBit, bool bBit0, bool bBit1)
Definition: ancillarydata_timecode_vitc.cpp:438
AJAAncillaryData_Timecode_VITC::VITCTypeToString
static std::string VITCTypeToString(const AJAAncillaryData_Timecode_VITC_Type inType)
Definition: ancillarydata_timecode_vitc.cpp:776
AJAAncillaryData_Timecode::operator=
AJAAncillaryData_Timecode & operator=(const AJAAncillaryData_Timecode &inRHS)
Assignment operator – replaces my contents with the right-hand-side value.
Definition: ancillarydata_timecode.cpp:58
AJA_STATUS_RANGE
@ AJA_STATUS_RANGE
Definition: types.h:385
AJAAncillaryData_Timecode::Print
virtual std::ostream & Print(std::ostream &inOutStream, const bool inDetailed=false) const
Streams a human-readable representation of me to the given output stream.
Definition: ancillarydata_timecode.cpp:646
AJAAncillaryData::m_rcvDataValid
bool m_rcvDataValid
This is set true (or not) by ParsePayloadData()
Definition: ancillarydata.h:1158
AJAAncillaryData::m_SID
uint8_t m_SID
Official SMPTE secondary ID (or DBN - w/o parity)
Definition: ancillarydata.h:1153
AJAAncillaryData::Calculate8BitChecksum
virtual uint8_t Calculate8BitChecksum(void) const
Generates an 8-bit checksum from the DID + SID + DC + payload data.
Definition: ancillarydata.cpp:233
AJAAncillaryData_Timecode::kBg1
@ kBg1
Definition: ancillarydata_timecode.h:286
AJAAncillaryData::IDAsString
virtual std::string IDAsString(void) const
Definition: ancillarydata.cpp:1428
addToCRC
static void addToCRC(const bool inBit, uint8_t &inOutCRC)
Definition: ancillarydata_timecode_vitc.cpp:177
AJAAncillaryData_Timecode::GetTimeHexValue
virtual AJAStatus GetTimeHexValue(uint8_t inDigitNum, uint8_t &outHexValue, uint8_t inMask=0x0f) const
Answers with my current raw "time" hex values.
Definition: ancillarydata_timecode.cpp:115
VITC_Y_CLIP
const uint8_t VITC_Y_CLIP
Definition: ancillarydata_timecode_vitc.cpp:157
AJAAncillaryData::SetLocationLineNumber
virtual AJAStatus SetLocationLineNumber(const uint16_t inLineNum)
Sets my ancillary data "location" frame line number.
Definition: ancillarydata.cpp:321
AJAAncillaryData::m_payload
ByteVector m_payload
My payload data (DC = size)
Definition: ancillarydata.h:1157
AJAAncillaryData_Timecode_VITC::SetVITCDataType
virtual AJAStatus SetVITCDataType(const AJAAncillaryData_Timecode_VITC_Type inType)
Sets my VITC data type.
Definition: ancillarydata_timecode_vitc.cpp:114
AJAAncillaryData_Timecode::kTcHourUnits
@ kTcHourUnits
Definition: ancillarydata_timecode.h:277
std
Definition: json.hpp:5362
AJAAncDataCodingToString
const std::string & AJAAncDataCodingToString(const AJAAncDataCoding inValue, const bool inCompact=true)
Definition: ancillarydata.cpp:1372
AJAAncillaryData_VITC_PayloadSize
const uint32_t AJAAncillaryData_VITC_PayloadSize
Definition: ancillarydata_timecode_vitc.h:25
AJAAncillaryData_Timecode_VITC_Type_FilmData
@ AJAAncillaryData_Timecode_VITC_Type_FilmData
Definition: ancillarydata_timecode_vitc.h:32
AJAAncillaryData_Timecode_VITC::operator=
virtual AJAAncillaryData_Timecode_VITC & operator=(const AJAAncillaryData_Timecode_VITC &inRHS)
Assignment operator – replaces my contents with the right-hand-side value.
Definition: ancillarydata_timecode_vitc.cpp:54
AJAAncillaryData_Timecode::kTcHourTens
@ kTcHourTens
Definition: ancillarydata_timecode.h:278
AJAAncillaryData_Timecode_VITC::Print
virtual std::ostream & Print(std::ostream &inOutStream, const bool inDetailed=false) const
Streams a human-readable representation of me to the given output stream.
Definition: ancillarydata_timecode_vitc.cpp:789
AJAAncillaryData_VITC_DID
const uint8_t AJAAncillaryData_VITC_DID
Definition: ancillarydata_timecode_vitc.h:22
AJAAncillaryData_Timecode::kTcMinuteTens
@ kTcMinuteTens
Definition: ancillarydata_timecode.h:276
AJAAncillaryData_Timecode::kBg6
@ kBg6
Definition: ancillarydata_timecode.h:291
AJAAncillaryData::m_ancType
AJAAncDataType m_ancType
One of a known set of ancillary data types (or "Custom" if not identified)
Definition: ancillarydata.h:1159
AJAAncillaryData_Timecode::kBg3
@ kBg3
Definition: ancillarydata_timecode.h:288
AJA_FAILURE
#define AJA_FAILURE(_status_)
Definition: types.h:371
AJAAncillaryData::m_checksum
uint8_t m_checksum
My 8-bit checksum: DID + SID + DC + payload (w/o parity) [note: NOT the same as the 9-bit checksum in...
Definition: ancillarydata.h:1154
AJAAncillaryData_Timecode::kBg2
@ kBg2
Definition: ancillarydata_timecode.h:287
DoNormalTransition
static void DoNormalTransition(uint8_t *pLine, uint32_t &pixelIndex, bool bBit0, bool bBit1)
Definition: ancillarydata_timecode_vitc.cpp:369