📄 yuv4mpeg.c
字号:
y4m_xtag_list_t *y4m_si_xtags(y4m_stream_info_t *si){ return &(si->x_tags); }void y4m_init_frame_info(y4m_frame_info_t *info){ if (info == NULL) return; /* init substructures */ y4m_init_xtag_list(&(info->x_tags)); /* set defaults */ y4m_clear_frame_info(info);}void y4m_clear_frame_info(y4m_frame_info_t *info){ if (info == NULL) return; /* clear/initialize info */ info->spatial = Y4M_UNKNOWN; info->temporal = Y4M_UNKNOWN; info->presentation = Y4M_UNKNOWN; y4m_xtag_clearlist(&(info->x_tags));}void y4m_copy_frame_info(y4m_frame_info_t *dest, const y4m_frame_info_t *src){ if ((dest == NULL) || (src == NULL)) return; /* copy info */ dest->spatial = src->spatial; dest->temporal = src->temporal; dest->presentation = src->presentation; y4m_copy_xtag_list(&(dest->x_tags), &(src->x_tags));}void y4m_fini_frame_info(y4m_frame_info_t *info){ if (info == NULL) return; y4m_fini_xtag_list(&(info->x_tags));}void y4m_fi_set_presentation(y4m_frame_info_t *fi, int pres){ fi->presentation = pres; }int y4m_fi_get_presentation(const y4m_frame_info_t *fi){ return fi->presentation; }void y4m_fi_set_temporal(y4m_frame_info_t *fi, int sampling){ fi->temporal = sampling; }int y4m_fi_get_temporal(const y4m_frame_info_t *fi){ return fi->temporal; }void y4m_fi_set_spatial(y4m_frame_info_t *fi, int sampling){ fi->spatial = sampling; }int y4m_fi_get_spatial(const y4m_frame_info_t *fi){ return fi->spatial; }y4m_xtag_list_t *y4m_fi_xtags(y4m_frame_info_t *fi){ return &(fi->x_tags); }/************************************************************************* * * Tag parsing * *************************************************************************//* Parse (the first) old, unofficial X-tag chroma specification, and then remove that tag from the X-tag list. */static inthandle_old_chroma_xtag(y4m_stream_info_t *si){ y4m_xtag_list_t *xtags = y4m_si_xtags(si); const char *tag = NULL; int n, chroma; for (n = y4m_xtag_count(xtags) - 1; n >= 0; n--) { tag = y4m_xtag_get(xtags, n); if (!strncmp("XYSCSS=", tag, 7)) break; } if ((tag == NULL) || (n < 0)) return Y4M_UNKNOWN; mjpeg_warn("Deprecated X-tag for chroma found in a stream header..."); mjpeg_warn("...pester someone to upgrade the source's program!"); /* parse the tag */ tag += 7; if (!strcmp("411", tag)) chroma = Y4M_CHROMA_411; else if (!strcmp(tag, "420")) chroma = Y4M_CHROMA_420JPEG; else if (!strcmp(tag, "420MPEG2")) chroma = Y4M_CHROMA_420MPEG2; else if (!strcmp(tag, "420PALDV")) chroma = Y4M_CHROMA_420PALDV; else if (!strcmp(tag, "420JPEG")) chroma = Y4M_CHROMA_420JPEG; else if (!strcmp(tag, "444")) chroma = Y4M_CHROMA_444; else chroma = Y4M_UNKNOWN; /* Remove the 'X' tag so that no one has to worry about it any more. */ y4m_xtag_remove(xtags, n); /* Hmm... what if there are more XYSCSS tags? Broken is as broken does; thank goodness this is temporary code. */ return chroma;}int y4m_parse_stream_tags(char *s, y4m_stream_info_t *i){ char *token, *value; char tag; int err; /* parse fields */ for (token = strtok(s, Y4M_DELIM); token != NULL; token = strtok(NULL, Y4M_DELIM)) { if (token[0] == '\0') continue; /* skip empty strings */ tag = token[0]; value = token + 1; switch (tag) { case 'W': /* width */ i->width = atoi(value); if (i->width <= 0) return Y4M_ERR_RANGE; break; case 'H': /* height */ i->height = atoi(value); if (i->height <= 0) return Y4M_ERR_RANGE; break; case 'F': /* frame rate (fps) */ if ((err = y4m_parse_ratio(&(i->framerate), value)) != Y4M_OK) return err; if (i->framerate.n < 0) return Y4M_ERR_RANGE; break; case 'I': /* interlacing */ switch (value[0]) { case 'p': i->interlace = Y4M_ILACE_NONE; break; case 't': i->interlace = Y4M_ILACE_TOP_FIRST; break; case 'b': i->interlace = Y4M_ILACE_BOTTOM_FIRST; break; case 'm': i->interlace = Y4M_ILACE_MIXED; break; case '?': default: i->interlace = Y4M_UNKNOWN; break; } break; case 'A': /* sample (pixel) aspect ratio */ if ((err = y4m_parse_ratio(&(i->sampleaspect), value)) != Y4M_OK) return err; if (i->sampleaspect.n < 0) return Y4M_ERR_RANGE; break; case 'C': i->chroma = y4m_chroma_parse_keyword(value); if (i->chroma == Y4M_UNKNOWN) return Y4M_ERR_HEADER; break; case 'X': /* 'X' meta-tag */ if ((err = y4m_xtag_add(&(i->x_tags), token)) != Y4M_OK) return err; break; default: /* possible error on unknown options */ if (_y4mparam_allow_unknown_tags) { /* unknown tags ok: store in xtag list and warn... */ if ((err = y4m_xtag_add(&(i->x_tags), token)) != Y4M_OK) return err; mjpeg_warn("Unknown stream tag encountered: '%s'", token); } else { /* unknown tags are *not* ok */ return Y4M_ERR_BADTAG; } break; } } /* If feature_level > 0, then handle and/or remove any old-style XYSCSS chroma tags. The new-style 'C' tag takes precedence, however. */ if (_y4mparam_feature_level > 0) { int xt_chroma = handle_old_chroma_xtag(i); if (i->chroma == Y4M_UNKNOWN) i->chroma = xt_chroma; else if ((xt_chroma != Y4M_UNKNOWN) && (xt_chroma != i->chroma)) mjpeg_warn("Old chroma X-tag (ignored) does not match new chroma tag."); } /* Without 'C' tag or any other chroma spec, default to 420jpeg */ if (i->chroma == Y4M_UNKNOWN) i->chroma = Y4M_CHROMA_420JPEG; /* Error checking... */ /* - Width and Height are required. */ if ((i->width == Y4M_UNKNOWN) || (i->height == Y4M_UNKNOWN)) return Y4M_ERR_HEADER; /* - Non-420 chroma and mixed interlace require level >= 1 */ if (_y4mparam_feature_level < 1) { if ((i->chroma != Y4M_CHROMA_420JPEG) && (i->chroma != Y4M_CHROMA_420MPEG2) && (i->chroma != Y4M_CHROMA_420PALDV)) return Y4M_ERR_FEATURE; if (i->interlace == Y4M_ILACE_MIXED) return Y4M_ERR_FEATURE; } /* ta da! done. */ return Y4M_OK;}static int y4m_parse_frame_tags(char *s, const y4m_stream_info_t *si, y4m_frame_info_t *fi){ char *token, *value; char tag; int err; /* parse fields */ for (token = strtok(s, Y4M_DELIM); token != NULL; token = strtok(NULL, Y4M_DELIM)) { if (token[0] == '\0') continue; /* skip empty strings */ tag = token[0]; value = token + 1; switch (tag) { case 'I': /* frame 'I' tag requires feature level >= 1 */ if (_y4mparam_feature_level < 1) return Y4M_ERR_FEATURE; if (si->interlace != Y4M_ILACE_MIXED) return Y4M_ERR_BADTAG; switch (value[0]) { case 't': fi->presentation = Y4M_PRESENT_TOP_FIRST; break; case 'T': fi->presentation = Y4M_PRESENT_TOP_FIRST_RPT; break; case 'b': fi->presentation = Y4M_PRESENT_BOTTOM_FIRST; break; case 'B': fi->presentation = Y4M_PRESENT_BOTTOM_FIRST_RPT; break; case '1': fi->presentation = Y4M_PRESENT_PROG_SINGLE; break; case '2': fi->presentation = Y4M_PRESENT_PROG_DOUBLE; break; case '3': fi->presentation = Y4M_PRESENT_PROG_TRIPLE; break; default: return Y4M_ERR_BADTAG; } switch (value[1]) { case 'p': fi->temporal = Y4M_SAMPLING_PROGRESSIVE; break; case 'i': fi->temporal = Y4M_SAMPLING_INTERLACED; break; default: return Y4M_ERR_BADTAG; } switch (value[2]) { case 'p': fi->spatial = Y4M_SAMPLING_PROGRESSIVE; break; case 'i': fi->spatial = Y4M_SAMPLING_INTERLACED; break; case '?': fi->spatial = Y4M_UNKNOWN; break; default: return Y4M_ERR_BADTAG; } break; case 'X': /* 'X' meta-tag */ if ((err = y4m_xtag_add(&(fi->x_tags), token)) != Y4M_OK) return err; break; default: /* possible error on unknown options */ if (_y4mparam_allow_unknown_tags) { /* unknown tags ok: store in xtag list and warn... */ if ((err = y4m_xtag_add(&(fi->x_tags), token)) != Y4M_OK) return err; mjpeg_warn("Unknown frame tag encountered: '%s'", token); } else { /* unknown tags are *not* ok */ return Y4M_ERR_BADTAG; } break; } } /* error-checking and/or non-mixed defaults */ switch (si->interlace) { case Y4M_ILACE_MIXED: /* T and P are required if stream "Im" */ if ((fi->presentation == Y4M_UNKNOWN) || (fi->temporal == Y4M_UNKNOWN)) return Y4M_ERR_HEADER; /* and S is required if stream is also 4:2:0 */ if ( ((si->chroma == Y4M_CHROMA_420JPEG) || (si->chroma == Y4M_CHROMA_420MPEG2) || (si->chroma == Y4M_CHROMA_420PALDV)) && (fi->spatial == Y4M_UNKNOWN) ) return Y4M_ERR_HEADER; break; case Y4M_ILACE_NONE: /* stream "Ip" --> equivalent to frame "I1pp" */ fi->spatial = Y4M_SAMPLING_PROGRESSIVE; fi->temporal = Y4M_SAMPLING_PROGRESSIVE; fi->presentation = Y4M_PRESENT_PROG_SINGLE; break; case Y4M_ILACE_TOP_FIRST: /* stream "It" --> equivalent to frame "Itii" */ fi->spatial = Y4M_SAMPLING_INTERLACED; fi->temporal = Y4M_SAMPLING_INTERLACED; fi->presentation = Y4M_PRESENT_TOP_FIRST; break; case Y4M_ILACE_BOTTOM_FIRST: /* stream "Ib" --> equivalent to frame "Ibii" */ fi->spatial = Y4M_SAMPLING_INTERLACED; fi->temporal = Y4M_SAMPLING_INTERLACED; fi->presentation = Y4M_PRESENT_BOTTOM_FIRST; break; default: /* stream unknown: then, whatever */ break; } /* ta da! done. */ return Y4M_OK;}/************************************************************************* * * Read/Write stream header * *************************************************************************/int y4m_read_stream_header(int fd, y4m_stream_info_t *i){ char line[Y4M_LINE_MAX]; char *p; int n; int err; /* start with a clean slate */ y4m_clear_stream_info(i); /* read the header line */ for (n = 0, p = line; n < Y4M_LINE_MAX; n++, p++) { if (read(fd, p, 1) < 1) return Y4M_ERR_SYSTEM; if (*p == '\n') { *p = '\0'; /* Replace linefeed by end of string */ break; } } if (n >= Y4M_LINE_MAX) return Y4M_ERR_HEADER; /* look for keyword in header */ if (strncmp(line, Y4M_MAGIC, strlen(Y4M_MAGIC))) return Y4M_ERR_MAGIC; if ((err = y4m_parse_stream_tags(line + strlen(Y4M_MAGIC), i)) != Y4M_OK) return err; return Y4M_OK;}int y4m_write_stream_header(int fd, const y4m_stream_info_t *i){ char s[Y4M_LINE_MAX+1]; int n; int err; y4m_ratio_t rate = i->framerate; y4m_ratio_t aspect = i->sampleaspect; const char *chroma_keyword = y4m_chroma_keyword(i->chroma); if ((i->chroma == Y4M_UNKNOWN) || (chroma_keyword == NULL)) return Y4M_ERR_HEADER; if (_y4mparam_feature_level < 1) { if ((i->chroma != Y4M_CHROMA_420JPEG) && (i->chroma != Y4M_CHROMA_420MPEG2) && (i->chroma != Y4M_CHROMA_420PALDV)) return Y4M_ERR_FEATURE; if (i->interlace == Y4M_ILACE_MIXED) return Y4M_ERR_FEATURE; } y4m_ratio_reduce(&rate); y4m_ratio_reduce(&aspect); n = snprintf(s, sizeof(s), "%s W%d H%d F%d:%d I%s A%d:%d C%s", Y4M_MAGIC, i->width, i->height, rate.n, rate.d, (i->interlace == Y4M_ILACE_NONE) ? "p" : (i->interlace == Y4M_ILACE_TOP_FIRST) ? "t" : (i->interlace == Y4M_ILACE_BOTTOM_FIRST) ? "b" : (i->interlace == Y4M_ILACE_MIXED) ? "m" : "?", aspect.n, aspect.d, chroma_keyword ); if ((n < 0) || (n > Y4M_LINE_MAX)) return Y4M_ERR_HEADER; if ((err = y4m_snprint_xtags(s + n, sizeof(s) - n - 1, &(i->x_tags))) != Y4M_OK) return err; /* non-zero on error */ return (y4m_write(fd, s, strlen(s)) ? Y4M_ERR_SYSTEM : Y4M_OK);}/************************************************************************* * * Read/Write frame header * *************************************************************************/int y4m_read_frame_header(int fd, const y4m_stream_info_t *si, y4m_frame_info_t *fi){ char line[Y4M_LINE_MAX]; char *p; int n; ssize_t remain; /* start with a clean slate */ y4m_clear_frame_info(fi); /* This is more clever than read_stream_header... Try to read "FRAME\n" all at once, and don't try to parse if nothing else is there... */ remain = y4m_read(fd, line, sizeof(Y4M_FRAME_MAGIC)-1+1); /* -'\0', +'\n' */ if (remain < 0) return Y4M_ERR_SYSTEM; if (remain > 0) { /* A clean EOF should end exactly at a frame-boundary */ if (remain == sizeof(Y4M_FRAME_MAGIC)) return Y4M_ERR_EOF; else return Y4M_ERR_BADEOF; } if (strncmp(line, Y4M_FRAME_MAGIC, sizeof(Y4M_FRAME_MAGIC)-1)) return Y4M_ERR_MAGIC; if (line[sizeof(Y4M_FRAME_MAGIC)-1] == '\n') return Y4M_OK; /* done -- no tags: that was the end-of-line. */ if (line[sizeof(Y4M_FRAME_MAGIC)-1] != Y4M_DELIM[0]) { return Y4M_ERR_MAGIC; /* wasn't a space -- what was it? */ } /* proceed to get the tags... (overwrite the magic) */ for (n = 0, p = line; n < Y4M_LINE_MAX; n++, p++) { if (y4m_read(fd, p, 1)) return Y4M_ERR_SYSTEM; if (*p == '\n') { *p = '\0'; /* Replace linefeed by end of string */ break; } } if (n >= Y4M_LINE_MAX) return Y4M_ERR_HEADER;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -