/*************************************************************************** makemeta.c Laserdisc metadata generator. Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. Visit http://mamedev.org for licensing and usage restrictions. **************************************************************************** Metadata format (12 bytes/field): Offset Description ------ ------------------------------------------ 0 = version (currently 2) 1 = internal flags: bit 0 = previous field is the same frame bit 1 = next field is the same frame 2 = white flag 3-5 = line 16 Philips code 6-8 = line 17 Philips code 9-11 = line 18 Philips code ***************************************************************************/ #include #include #include "aviio.h" #include "bitmap.h" /*************************************************************************** CONSTANTS ***************************************************************************/ #define INSERT_FRAME_CODE 0xff000000 #define INSERT_FRAME_CODE_INC 0xff000001 #define INVALID_CODE 0xffffffff /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ typedef struct _pattern_data pattern_data; struct _pattern_data { pattern_data *next; UINT32 line16, line17, line18; int white; }; /*************************************************************************** IMPLEMENTATION ***************************************************************************/ /*------------------------------------------------- parse_line - parse a Philips code from a line of video data -------------------------------------------------*/ static int parse_line(bitmap_t *bitmap, int line, int expected_bits, UINT8 *result) { const UINT16 *source = BITMAP_ADDR16(bitmap, line, 0); int x, edges = 0, bits = 0; int minwidth = 1000; UINT8 bit[720]; int edge[720]; int error = 0; /* clamp expected bits */ expected_bits *= 2; if (expected_bits > ARRAY_LENGTH(edge) - 1) expected_bits = ARRAY_LENGTH(edge) - 1; /* find the edges in the line */ for (x = 1; x < bitmap->width && edges < ARRAY_LENGTH(edge); x++) if (source[x] >= 0xc000 && source[x - 1] < 0xc000) edge[edges++] = x; else if (source[x] <= 0x4000 && source[x - 1] > 0x4000) edge[edges++] = x; /* find the minimum width */ for (x = 1; x < edges; x++) { int width = edge[x] - edge[x - 1]; if (width > 3 && width < minwidth) minwidth = edge[x] - edge[x - 1]; } /* now generate the bits */ for (x = 1; x < edges; x++) { int width = edge[x] - edge[x - 1]; if (width > 3) { int count = (width > 3 * minwidth / 2) ? 2 : 1; while (count--) bit[bits++] = (source[edge[x - 1]] >= 0x8000) ? 1 : 0; } } /* look for improperly paired bits in the sequence */ if (bits < expected_bits) { /* look for two bits in a row of the same type on an even boundary */ for (x = 0; x < bits; x += 2) if (bit[x] == bit[x + 1]) break; /* if we got something wrong, assume we're missing an opening 0 bit */ if (x < bits) { memmove(&bit[1], &bit[0], bits); bit[0] = 0; bits++; } } /* trailing bits are 0 */ while (bits < expected_bits) bit[bits++] = 0; /* output */ for (x = 0; x < MIN(bits, expected_bits); x += 2) { static const UINT8 trans[4] = { 0x80, 1, 0, 0xff }; result[x/2] = trans[(bit[x] << 1) | bit[x + 1]]; if (result[x/2] > 1) error++; } return error ? -(bits / 2) : (bits / 2); } /*------------------------------------------------- output_meta - output a line of metadata -------------------------------------------------*/ static void output_meta(UINT8 flags, UINT8 white, UINT32 line12, UINT32 line13, UINT32 line14, UINT32 framenum, UINT32 chapternum) { /* start with the raw metadata, followed by a comment */ printf("02%02X%02X%06X%06X%06X ; ", flags, white, line12, line13, line14); /* separate comments for leadin/leadout */ if (line13 == 0x88ffff) printf("leadin\n"); else if (line13 == 0x80eeee) printf("leadout\n"); /* otherwise, display the frame and chapter, and indicate white flag/stop code */ else { printf("frame %05x ch %02x", framenum, chapternum); if (white) printf(" (white)"); if (line12 == 0x82cfff) printf(" (stop)"); printf("\n"); } } /*------------------------------------------------- generate_from_avi - generate the data from an AVI file -------------------------------------------------*/ static int generate_from_avi(const char *aviname) { UINT32 line12 = 0, line13 = 0, line14 = 0, framenum = 0, chapternum = 0; const avi_movie_info *info; bitmap_t *bitmap; avi_error avierr; avi_file *avi; int white = 0; int frame; /* open the file */ avierr = avi_open(aviname, &avi); if (avierr != AVIERR_NONE) { fprintf(stderr, "Error opening AVI file: %s\n", avi_error_string(avierr)); return 1; } /* extract movie info */ info = avi_get_movie_info(avi); fprintf(stderr, "%dx%d - %d frames total\n", info->video_width, info->video_height, info->video_numsamples); if (info->video_height != 39) { fprintf(stderr, "Unknown VANC capture format: expected 39 rows\n"); return 1; } /* allocate a bitmap to hold it */ bitmap = bitmap_alloc(info->video_width, info->video_height, BITMAP_FORMAT_YUY16); if (bitmap == NULL) { fprintf(stderr, "Out of memory allocating %dx%d bitmap\n", info->video_width, info->video_height); return 1; } /* loop over frames */ for (frame = 0; frame < info->video_numsamples; frame++) { int field; UINT8 bits[24]; /* read the frame */ avierr = avi_read_video_frame_yuy16(avi, frame, bitmap); if (avierr != AVIERR_NONE) { fprintf(stderr, "Error reading AVI frame %d: %s\n", frame, avi_error_string(avierr)); break; } /* loop over two fields */ for (field = 0; field < 2; field++) { int prevwhite = white; int i; /* line 7 contains the white flag */ white = 0; if (*BITMAP_ADDR16(bitmap, 20 * field + 7, bitmap->width / 2) > 0x8000) white = 1; /* output metadata for *previous* field */ if (frame > 0 || field > 0) { int flags = 0; if (!prevwhite) flags |= 0x01; if (!white) flags |= 0x02; output_meta(flags, prevwhite, line12, line13, line14, framenum, chapternum); } /* line 12 contains stop code and other interesting bits */ line12 = 0; if (parse_line(bitmap, 20 * field + 12, 24, bits) == 24) for (i = 0; i < 24; i++) line12 = (line12 << 1) | bits[i]; /* line 13 and 14 contain frame/chapter/lead in/out encodings */ line13 = 0; if (parse_line(bitmap, 20 * field + 13, 24, bits) == 24) for (i = 0; i < 24; i++) line13 = (line13 << 1) | bits[i]; line14 = 0; if (parse_line(bitmap, 20 * field + 14, 24, bits) == 24) for (i = 0; i < 24; i++) line14 = (line14 << 1) | bits[i]; /* the two lines must match */ // if (line13 != 0 && line14 != 0 && line13 != line14) // line13 = line14 = 0; /* is this a frame number? */ if ((line13 & 0xf00000) == 0xf00000) framenum = line13 & 0x7ffff; if ((line13 & 0xf00fff) == 0x800ddd) chapternum = (line13 >> 12) & 0x7f; } } /* output metadata for *previous* field */ { int flags = 0; if (!white) flags |= 0x01; output_meta(flags, white, line12, line13, line14, framenum, chapternum); } bitmap_free(bitmap); return 0; } /*------------------------------------------------- parse_philips_code - parse a single Philips code from a string, stopping at the given end characters -------------------------------------------------*/ static UINT32 parse_philips_code(char **argptr, const char *endchars) { char *arg = *argptr; UINT32 value = 0; /* look for special chars first */ if (*arg == '+') { *argptr = arg + 1; return INSERT_FRAME_CODE_INC; } else if (*arg == '@') { *argptr = arg + 1; return INSERT_FRAME_CODE; } /* parse the rest as hex digits */ for ( ; *arg != 0 && strchr(endchars, *arg) == NULL; arg++) { if (*arg >= '0' && *arg <= '9') value = (value << 4) + (*arg - '0'); else if (*arg >= 'a' && *arg <= 'f') value = (value << 4) + 10 + (*arg - 'a'); else if (*arg >= 'A' && *arg <= 'F') value = (value << 4) + 10 + (*arg - 'A'); else return INVALID_CODE; } /* if we're too big, we're invalid */ if (value > 0xffffff) return INVALID_CODE; *argptr = arg; return value; } /*------------------------------------------------- parse_pattern - parse a pattern into a series of pattern_data structs -------------------------------------------------*/ static pattern_data *parse_pattern(char *arg, int *countptr) { pattern_data *head = NULL; pattern_data **tailptr = &head; int count = 0; /* first parse the count */ for ( ; *arg != 0 && *arg != '*'; arg++) { if (!isdigit(*arg)) return NULL; count = count * 10 + (*arg - '0'); } if (*arg == 0) return NULL; arg++; *countptr = count; /* loop until we hit the end */ while (*arg != 0) { pattern_data *pat; /* allocate a new structure */ pat = malloc(sizeof(*pat)); if (pat == NULL) return NULL; memset(pat, 0, sizeof(*pat)); /* bang at the beginning means white flag */ if (*arg == '!') { arg++; pat->white = 1; } /* parse line16 until we hit a period or comma */ pat->line16 = parse_philips_code(&arg, ".,"); if (pat->line16 == INVALID_CODE) return NULL; if (*arg != '.' && *arg != ',' && *arg != 0) return NULL; if (*arg == '.') arg++; /* parse line17 until we hit a period */ pat->line17 = parse_philips_code(&arg, ".,"); if (pat->line17 == INVALID_CODE) return NULL; if (*arg != '.' && *arg != ',' && *arg != 0) return NULL; if (*arg == '.') arg++; /* parse line18 until we hit a comma */ pat->line18 = parse_philips_code(&arg, ","); if (pat->line18 == INVALID_CODE) return NULL; if (*arg != ',' && *arg != 0) return NULL; if (*arg == ',') arg++; /* append to the end */ *tailptr = pat; tailptr = &pat->next; } return head; } /*------------------------------------------------- generate_from_pattern - generate metadata from a pattern -------------------------------------------------*/ static int generate_from_pattern(pattern_data *pattern, int patcount) { pattern_data *curpat = pattern; int framenum = 0, bcdframenum = 0; /* loop until we exceed the pattern frame count */ while (1) { UINT32 line16, line17, line18; int flags = 0; /* handle special codes for line 16 */ line16 = curpat->line16; if (line16 == INSERT_FRAME_CODE_INC) { framenum++; bcdframenum = (((framenum / 10000) % 10) << 16) | (((framenum / 1000) % 10) << 12) | (((framenum / 100) % 10) << 8) | (((framenum / 10) % 10) << 4) | (framenum % 10); } if (line16 == INSERT_FRAME_CODE || line16 == INSERT_FRAME_CODE_INC) line16 = 0xf80000 | bcdframenum; /* handle special codes for line 17 */ line17 = curpat->line17; if (line17 == INSERT_FRAME_CODE_INC) { framenum++; bcdframenum = (((framenum / 10000) % 10) << 16) | (((framenum / 1000) % 10) << 12) | (((framenum / 100) % 10) << 8) | (((framenum / 10) % 10) << 4) | (framenum % 10); } if (line17 == INSERT_FRAME_CODE || line17 == INSERT_FRAME_CODE_INC) line17 = 0xf80000 | bcdframenum; /* handle special codes for line 18 */ line18 = curpat->line18; if (line18 == INSERT_FRAME_CODE_INC) { framenum++; bcdframenum = (((framenum / 10000) % 10) << 16) | (((framenum / 1000) % 10) << 12) | (((framenum / 100) % 10) << 8) | (((framenum / 10) % 10) << 4) | (framenum % 10); } if (line18 == INSERT_FRAME_CODE || line18 == INSERT_FRAME_CODE_INC) line18 = 0xf80000 | bcdframenum; /* bail if we passed the end */ if (framenum > patcount) return 0; /* if we don't have a white flag, the previous frame must match us */ if (!curpat->white) flags |= 0x01; /* advance to the next pattern piece */ curpat = curpat->next; if (curpat == NULL) curpat = pattern; /* if the new field doesn't have a white flag, it must match our current frame */ if (!curpat->white) flags |= 0x02; /* output the result */ output_meta(flags, curpat->white, line16, line17, line18, bcdframenum, 0); } return 0; } /*------------------------------------------------- usage - display program usage -------------------------------------------------*/ static int usage(void) { fprintf(stderr, "Usage: \n"); fprintf(stderr, " makemeta [avifile.avi] [