/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include /* 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); } }