Please consider a donation to the Higher Intellect project. See https://preterhuman.net/donate.php or the Donate to Higher Intellect page for more info.

Vitc.c

From Higher Intellect Vintage Wiki
Jump to navigation Jump to search
/*
 * vitc.c - example of using dmVITC
 *
 * compile with -ldmedia and -lvl
 *
 * parses and prints out VITC codewords from video input.
 *
 * note comments below about device dependency.
 */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>

#include <dmedia/dmedia.h>
#include <sys/dmcommon.h>
#include <dmedia/dm_params.h>
#include <dmedia/dm_timecode.h>
#include <dmedia/dm_vitc.h>

#include <dmedia/vl.h>

#include <math.h>

/* Utility Routines ---------------------------------------------------- */

void error_exit(char *format, ...)
{
  va_list ap;
  
  va_start(ap, format);
  vfprintf( stdout, format, ap );
  va_end(ap);

  fprintf( stdout, "\n" );
  exit(2);
}

void error(char *format, ...)
{
  va_list ap;
  
  va_start(ap, format);
  vfprintf( stdout, format, ap );
  va_end(ap);

  fprintf( stdout, "\n" );
}

/*
** DC -- "DM Params Check".  Wraps a call to a dmParams function that
** returns DM_FAILURE on error.
*/

#define DC(call) \
{ \
  if ( (call) != DM_SUCCESS ) \
    error_exit("DM_FAILURE for call \"%s\"", #call); \
}

char *timing_name(int timingtype)
{
  switch (timingtype) 
    {
    case VL_TIMING_525_SQ_PIX:
      return "NTSC square pix analog (525 lines) 646x486";
      
    case VL_TIMING_625_SQ_PIX:
      return "PAL square pix analog (625 lines) 768x576";
      
    case VL_TIMING_525_CCIR601:
      return "525-line CCIR601 digital component 720x486";
      
    case VL_TIMING_625_CCIR601:
      return "625-line CCIR601 digital component 720x576";
      
    case VL_TIMING_525_4FSC:
      return "525-line 4x ntsc subcarrier 768x486";
      
    case VL_TIMING_625_4FSC:
      return "625-line 4x pal subcarrier 948x576";
      
    default:
      return "???";
    }
}

char *packing_name(int packtype)
{
  switch (packtype) 
    {
    case VL_PACKING_RGB_8:
      return "RGB";

    case VL_PACKING_RGBA_8:
      return "RGBA";
	
    case VL_PACKING_RBG_323:   
      return "Starter Video RGB8";
	
    case VL_PACKING_RGB_332_P:	
      return "RGB332";
	
    case VL_PACKING_Y_8_P: 
      return "8 Bit Greyscale";

    case VL_PACKING_YVYU_422_8:
      return "YVYU_422_8";
	
    default:		
      return "???";
    }
}

void print_tc_type_name(int tc_type)
{
  switch (tc_type & DM_TC_FORMAT_MASK)
    {
    case DM_TC_FORMAT_NTSC: printf("NTSC "); break;
    case DM_TC_FORMAT_PAL:  printf("PAL ");  break;
    case DM_TC_FORMAT_FILM: printf("FILM "); break;
    }
  
  if ((tc_type & DM_TC_FORMAT_MASK) == DM_TC_FORMAT_NTSC)
    {
      switch (tc_type & DM_TC_DROP_MASK)
        {
        case DM_TC_NODROP:    printf("non-drop "); break;
        case DM_TC_DROPFRAME: printf("drop "); break;
        }
    }
  
  switch (tc_type & DM_TC_RATE_MASK)
    {
    case DM_TC_RATE_2997: printf("29.97 frame/sec"); break;
    case DM_TC_RATE_30:   printf("30 frame/sec"); break;
    case DM_TC_RATE_24:   printf("24 frame/sec"); break;
    case DM_TC_RATE_25:   printf("25 frame/sec"); break;
    }
  
  printf(" (0x%x)", tc_type);
}

char *tc_type_speed_string(int tc_type)
{
  switch (tc_type & DM_TC_RATE_MASK)
    {
    case DM_TC_RATE_2997:
      return "29.97fps";
    case DM_TC_RATE_30:
      return "30fps";
    case DM_TC_RATE_24:
      return "24fps";
    case DM_TC_RATE_25:
      return "25fps";
    default:
      return "???";
    }
}

char *tc_type_drop_string(int tc_type)
{
  if(tc_type & DM_TC_DROP_MASK)
    switch(tc_type & DM_TC_FORMAT_MASK)
      {
      case DM_TC_FORMAT_NTSC: return "fourfd";
      case DM_TC_FORMAT_PAL:  return "eightfd";
      default:           return "???";
      }
  else
    return "nd";
}

void printvitc(DMVITCcode vitcCodeword)
{
  printf("SYN VITC %x %s %s %dh %dm %ds %df %s %s %s "
         "ub%02x:%02x:%02x:%02x\n",
         vitcCodeword.tc.tc_type,
         tc_type_speed_string(vitcCodeword.tc.tc_type),
         tc_type_drop_string(vitcCodeword.tc.tc_type),
         vitcCodeword.tc.hour,
         vitcCodeword.tc.minute,
         vitcCodeword.tc.second,
         vitcCodeword.tc.frame,
         vitcCodeword.evenField ? "even" : "odd",
         vitcCodeword.dropFrame ? "drop" : "non-drop",
         ((vitcCodeword.tc.tc_type & DM_TC_FORMAT_MASK) ==
          DM_TC_FORMAT_PAL) ? 
         (vitcCodeword.colorLock ? "lock" : "nolock") : /* 625 */
         (vitcCodeword.colorLock ? "A" : "B"),   /* 525 */
         vitcCodeword.userData[3],
         vitcCodeword.userData[2],
         vitcCodeword.userData[1],
         vitcCodeword.userData[0]
         );
}

/* main ------------------------------------------------------------ */

int main(int argc, char **argv)
{
  /* VL stuff */

  VLServer server = vlOpenVideo("");
  VLNode vid = vlGetNode(server, VL_SRC, VL_VIDEO, VL_ANY);
  VLNode mem = vlGetNode(server, VL_DRN, VL_MEM, VL_ANY);
  VLPath path = vlCreatePath(server, VL_ANY, vid, mem);
  VLBuffer buffer;
  VLControlValue val;
  int xsize, ysize;
  int packing;
  int timing;

  /* dmVITC stuff */

  DMVITCdecoder vitcdecoder;
  int tc_type;

  vlSetupPaths(server, (VLPathList)&path, 1, VL_SHARE, VL_SHARE);

#define BUFSIZE 10
  printf("VL buffer size is %d entries\n", BUFSIZE);

  /* get video timing */
  
  vlGetControl (server, path, vid, VL_TIMING, &val);
  timing = val.intVal;
  printf("using %s timing\n", timing_name(timing));

  /* each buffer has a field */

  val.intVal = VL_CAPTURE_NONINTERLEAVED;
  vlSetControl (server, path, mem, VL_CAP_TYPE, &val);

  /* yuv packing (can also use RGB with dmVITC) */

  val.intVal = VL_PACKING_YVYU_422_8;
  packing = val.intVal;
  vlSetControl (server, path, mem, VL_PACKING, &val);
  printf("using %s packing\n", packing_name(packing));
  
  /* size: we only need a few lines */

  vlGetControl (server, path, mem, VL_SIZE, &val);
  val.xyVal.y = 16;
  vlSetControl (server, path, mem, VL_SIZE, &val);
  vlGetControl (server, path, mem, VL_SIZE, &val);
  printf("size is %d,%d\n", val.xyVal.x, val.xyVal.y);
  xsize = val.xyVal.x; ysize = val.xyVal.y;
  
  /* offset: set negative offset so we can get at VITC
   *
   * note this is device dependent (see vid2mem.html in
   * the Lurker's Guide).
   *
   * this code uses the mvp/vino/ev1 values for 525-line video
   * haven't tried it on ev3/cosmo2/divo
   * will not work on sirius -- sirius doesn't do VL_OFFSET
   */
  if (timing == VL_TIMING_525_SQ_PIX || timing == VL_TIMING_525_CCIR601)
    {
      /* 525-line values for mvp/vino/ev1 */
      val.xyVal.x = 0;
      val.xyVal.y = -10;
    }
  else
    {
      /* 625-line values for mvp/vino/ev1 */
      val.xyVal.x = 0;
      val.xyVal.y = -15;
    }
  vlSetControl (server, path, mem, VL_OFFSET, &val);
  vlGetControl (server, path, mem, VL_OFFSET, &val);
  printf("offset is %d,%d\n", val.xyVal.x, val.xyVal.y);

  /* set up VITC decoder - choose tc_type based on video timing */

  if (timing == VL_TIMING_525_SQ_PIX || timing == VL_TIMING_525_CCIR601)
    tc_type = DM_TC_30_ND;
  else
    tc_type = DM_TC_25_ND;

  printf("VITC timecode is ");
  print_tc_type_name(tc_type);
  printf("\n");
  
  DC( dmVITCDecoderCreate(&vitcdecoder, tc_type) );

  /* choose VITC stride based on pixel packing */

  switch (packing)
    {
    case VL_PACKING_RGBA_8:
    case VL_PACKING_RGB_8:
      DC( dmVITCDecoderSetStride(vitcdecoder, 4 /*RGBX*/, 2 /*G*/ ) );
      break;
    case VL_PACKING_Y_8_P:
      DC( dmVITCDecoderSetStride(vitcdecoder, 1 /*mono*/, 0 ) );
      break;
    case VL_PACKING_YVYU_422_8:
      DC( dmVITCDecoderSetStride(vitcdecoder, 2 /*YUV*/, 1 /*Y*/ ) );
      break;
    default:
      error_exit("don't know how do VITC on data with %s packing",
                 packing_name(packing));
    }

  /* choose VITC pixel timing based on video timing */

  DC( dmVITCDecoderSetPixelTiming(vitcdecoder, timing)  );
  
  /* start up video */

  buffer = vlCreateBuffer(server, path, mem, BUFSIZE);

  vlRegisterBuffer (server, path, mem, buffer);
  vlBeginTransfer (server, path, 0, NULL);

  while (1)
    {
      VLInfoPtr info = vlGetNextValid(server, buffer);
      unsigned char *data;
      DMVITCcode vitcCodeword;
      
      if (!info)
        {
          sginap(5);
          continue;
        }

      data = vlGetActiveRegion(server, buffer, info);

      if (DM_SUCCESS != dmVITCDecode(vitcdecoder,
                                     data,
                                     xsize,
                                     ysize,
                                     &vitcCodeword))
        {
          printf("baaaaaaad VITC\n");
        }
      else
        {
          printvitc(vitcCodeword);
        }
      
      vlPutFree(server, buffer);
    }
}