📄 pnmtoy4m.c
字号:
/* * pnmtoy4m.c: Generate a YUV4MPEG2 stream from one or more PNM/PAM images. * * Converts R'G'B' to ITU-Rec.601 Y'CbCr colorspace, and/or * converts [0,255] grayscale to Rec.601 Y' luma. * * * Copyright (C) 2004 Matthew J. Marjanovic <maddog@mir.com> * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */#include <config.h>#include <stdio.h>#include <unistd.h>#include <string.h>#include <ctype.h>#include <errno.h>#include <fcntl.h>#include <assert.h>#include <yuv4mpeg.h>#include <mpegconsts.h>#include "colorspace.h"#ifndef O_BINARY# define O_BINARY 0#endif/* command-line parameters */typedef struct _cl_info { y4m_ratio_t output_aspect; y4m_ratio_t output_framerate; int output_interlace; int input_interlace; int deinterleave; int offset; int framecount; int repeatlast; int verbosity; int fdin; int bgr;} cl_info_t;staticvoid usage(const char *progname){ fprintf(stdout, "\n"); fprintf(stdout, "usage: %s [options] [pnm-file]\n", progname); fprintf(stdout, "\n"); fprintf(stdout, "Reads RAW PNM/PAM image(s), and produces YUV4MPEG2 stream on stdout.\n"); fprintf(stdout, "Converts computer graphics R'G'B' colorspace to digital video Y'CbCr.\n"); fprintf(stdout, "\n"); fprintf(stdout, "If 'pnm-file' is not specified, reads from stdin.\n"); fprintf(stdout, "\n"); fprintf(stdout, " options: (defaults specified in [])\n"); fprintf(stdout, "\n"); fprintf(stdout, " -o n frame offset (skip n input frames) [0]\n"); fprintf(stdout, " -n n frame count (output n frames; 0 == all of them) [0]\n"); fprintf(stdout, " -r repeat last input frame\n"); fprintf(stdout, " -D x treat PNM images as de-interleaved fields, with:\n"); fprintf(stdout, " t = first-image-is-top-field\n"); fprintf(stdout, " b = first-image-is-bottom-field\n"); fprintf(stdout, " -F n:d output framerate [30000:1001 = NTSC]\n"); fprintf(stdout, " -A w:h output pixel aspect ratio [1:1]\n"); fprintf(stdout, " -I x output interlacing [from -D, or p]\n"); fprintf(stdout, " p = none/progressive\n"); fprintf(stdout, " t = top-field-first\n"); fprintf(stdout, " b = bottom-field-first\n"); fprintf(stdout, " -v n verbosity (0,1,2) [1]\n"); fprintf(stdout, " -B pixels are packed in BGR(A) format [RGB(A)]\n");}staticvoid parse_args(cl_info_t *cl, int argc, char **argv){ int c; cl->offset = 0; cl->framecount = 0; cl->output_aspect = y4m_sar_SQUARE; cl->output_interlace = Y4M_UNKNOWN; cl->output_framerate = y4m_fps_NTSC; cl->deinterleave = 0; cl->input_interlace = Y4M_ILACE_NONE; cl->repeatlast = 0; cl->verbosity = 1; cl->fdin = 0; /* default to stdin */ cl->bgr = 0; while ((c = getopt(argc, argv, "A:F:I:D:o:n:rv:Bh")) != -1) { switch (c) { case 'A': if (y4m_parse_ratio(&(cl->output_aspect), optarg) != Y4M_OK) { mjpeg_error("Could not parse ratio: '%s'", optarg); goto ERROR_EXIT; } break; case 'F': if (y4m_parse_ratio(&(cl->output_framerate), optarg) != Y4M_OK) { mjpeg_error("Could not parse ratio: '%s'", optarg); goto ERROR_EXIT; } break; case 'I': switch (optarg[0]) { case 'p': cl->output_interlace = Y4M_ILACE_NONE; break; case 't': cl->output_interlace = Y4M_ILACE_TOP_FIRST; break; case 'b': cl->output_interlace = Y4M_ILACE_BOTTOM_FIRST; break; default: mjpeg_error("Unknown value for output interlace: '%c'", optarg[0]); goto ERROR_EXIT; break; } break; case 'D': cl->deinterleave = 1; switch (optarg[0]) { case 't': cl->input_interlace = Y4M_ILACE_TOP_FIRST; break; case 'b': cl->input_interlace = Y4M_ILACE_BOTTOM_FIRST; break; default: mjpeg_error("Unknown value for input interlace: '%c'", optarg[0]); goto ERROR_EXIT; break; } break; case 'o': if ((cl->offset = atoi(optarg)) < 0) mjpeg_error_exit1("Offset must be >= 0: '%s'", optarg); break; case 'n': if ((cl->framecount = atoi(optarg)) < 0) mjpeg_error_exit1("Frame count must be >= 0: '%s'", optarg); break; case 'r': cl->repeatlast = 1; break; case 'v': cl->verbosity = atoi(optarg); if ((cl->verbosity < 0) || (cl->verbosity > 2)) mjpeg_error("Verbosity must be 0, 1, or 2: '%s'", optarg); break; case 'h': usage(argv[0]); exit(0); break; case 'B': cl->bgr = 1; break; case '?': default: goto ERROR_EXIT; break; } } /* optional remaining argument is a filename */ if (optind == (argc - 1)) { if ((cl->fdin = open(argv[optind], O_RDONLY | O_BINARY)) == -1) mjpeg_error_exit1("Failed to open '%s': %s", argv[optind], strerror(errno)); } else if (optind != argc) goto ERROR_EXIT; mjpeg_default_handler_verbosity(cl->verbosity); /* output ilace defaults to match input (or none if input is interleaved) */ if (cl->output_interlace == Y4M_UNKNOWN) { if (cl->deinterleave) cl->output_interlace = cl->input_interlace; else cl->output_interlace = Y4M_ILACE_NONE; } mjpeg_info("Command-line Parameters:"); if (!cl->deinterleave) { mjpeg_info(" input format: interleaved"); } else { mjpeg_info(" input format: field-sequential, %s", (cl->input_interlace == Y4M_ILACE_TOP_FIRST) ? "first-image-is-top" : "first-image-is-bottom"); } mjpeg_info(" pixel packing: %s", (cl->bgr?"BGR(A)":"RGB(A)")); mjpeg_info(" output framerate: %d:%d", cl->output_framerate.n, cl->output_framerate.d); mjpeg_info(" output SAR: %d:%d", cl->output_aspect.n, cl->output_aspect.d); mjpeg_info(" output interlace: %s", mpeg_interlace_code_definition(cl->output_interlace)); mjpeg_info(" starting frame: %d", cl->offset); if (cl->framecount == 0) mjpeg_info(" # of frames: all%s", (cl->repeatlast) ? ", repeat last frame forever" : ", until input exhausted"); else mjpeg_info(" # of frames: %d%s", cl->framecount, (cl->repeatlast) ? ", repeat last frame until done" : ", or until input exhausted"); /* DONE! */ return; ERROR_EXIT: mjpeg_error("For usage hints, use option '-h'. Please take a hint."); exit(1);}/* the various PNM formats we handle */typedef enum { FMT_PAM = 0, FMT_PPM_RAW, FMT_PGM_RAW, FMT_PBM_RAW, FMT_PPM_PLAIN, FMT_PGM_PLAIN, FMT_PBM_PLAIN, FMT_UNKNOWN} pnm_format_t;#define FMT_FIRST FMT_PAM#define FMT_COUNT FMT_UNKNOWN/* 'magic numbers' of the PNM formats (corresponding to enum above) */const char *magics[FMT_COUNT+1] = { "P7", "P6", "P5", "P4", "P3", "P2", "P1", "*UNKNOWN*"};/* the various PAM "tupls" we handle */typedef enum { TUPL_RGB = 0, TUPL_GRAY, TUPL_RGB_ALPHA, TUPL_GRAY_ALPHA, TUPL_BW, TUPL_BW_ALPHA, TUPL_UNKNOWN} pam_tupl_t;#define TUPL_FIRST TUPL_RGB#define TUPL_COUNT TUPL_UNKNOWNstruct tupl_info { const char *tag; int depth;};/* descriptions of the PAM tupls (corresponding to enum above) */const struct tupl_info tupls[TUPL_COUNT+1] = { { "RGB", 3 }, { "GRAYSCALE", 1 }, { "RGB_ALPHA", 4 }, { "GRAYSCALE_ALPHA", 2 }, { "BLACKANDWHITE", 1 }, { "BLACKANDWHITE_ALPHA", 2 }, { "*UNKNOWN*", 0 }};/* PNM image information */typedef struct _pnm_info { pnm_format_t format; int width; int height; int depth; pam_tupl_t tupl;} pnm_info_t;staticint pnm_info_equal(pnm_info_t *a, pnm_info_t *b){ return ((a->format == b->format) && (a->width == b->width) && (a->height == b->height) && (a->depth == b->depth) && (a->tupl == b->tupl));}staticvoid pnm_info_copy(pnm_info_t *dst, pnm_info_t *src){ dst->format = src->format; dst->width = src->width; dst->height = src->height; dst->depth = src->depth; dst->tupl = src->tupl;}staticchar *read_tag_and_value(int fd, char *line, int maxlen){ int found_end_of_tag = 0; char *v = NULL; int n; while ( (maxlen > 0) && ((n = read(fd, line, 1)) == 1) && (*line != '\n') ) { if (!found_end_of_tag) { if (isspace(*line)) { *line = '\0'; found_end_of_tag = 1; } } else if (v == NULL) { if (!isspace(*line)) v = line; } line++; } if (maxlen <= 0) return NULL; if (n != 1) return NULL; *line = '\0'; if (v == NULL) return line; return v;}staticint read_pam_header(int fd, pnm_info_t *pnm){ char line[128]; int done = 0; int maxval = 0; char *val; pnm->width = 0; pnm->height = 0; pnm->depth = 0; pnm->tupl = TUPL_UNKNOWN; while (!done && ((val = read_tag_and_value(fd, line, 128)) != NULL)) { if (!strcmp(line, "ENDHDR")) { done = 1; } else if (!strcmp(line, "HEIGHT")) { pnm->height = atoi(val); } else if (!strcmp(line, "WIDTH")) { pnm->width = atoi(val); } else if (!strcmp(line, "DEPTH")) { pnm->depth = atoi(val); } else if (!strcmp(line, "MAXVAL")) { maxval = atoi(val); } else if (!strcmp(line, "TUPLTYPE")) { if (pnm->tupl != TUPL_UNKNOWN) mjpeg_error_exit1("Too many PAM TUPLTYPE's: %s", val); for (pnm->tupl = TUPL_FIRST; pnm->tupl < TUPL_UNKNOWN; (pnm->tupl)++) { if (!(strcmp(val, tupls[pnm->tupl].tag))) break; } if (pnm->tupl == TUPL_UNKNOWN) mjpeg_error_exit1("Unknown PAM TUPLTYPE: %s", val); } } if ( (pnm->width == 0) || (pnm->height == 0) || (pnm->depth == 0) || (maxval == 0) ) mjpeg_error_exit1("Bad PAM header!\n"); if (pnm->tupl == TUPL_UNKNOWN) { for (pnm->tupl = TUPL_FIRST; pnm->tupl < TUPL_UNKNOWN; (pnm->tupl)++) { if (pnm->depth == tupls[pnm->tupl].depth) break; } if (pnm->tupl == TUPL_UNKNOWN) mjpeg_error_exit1("No PAM TUPL for depth %d!", pnm->depth); } else { if (pnm->depth != tupls[pnm->tupl].depth) mjpeg_error_exit1("PAM depth mismatch: %d != %d for %s.", pnm->depth, tupls[pnm->tupl].depth, tupls[pnm->tupl].tag); } if (maxval != 255) mjpeg_error_exit1("Expecting maxval == 255, not %d!", maxval); return 0;}#define DO_READ_NUMBER(var) \ do { \ if (!isdigit(s[0])) \ mjpeg_error_exit1("PNM read error: bad char %d", s[0]); \ (var) = ((var) * 10) + (s[0] - '0'); \ } while (((n = read(fd, s, 1)) == 1) && (!isspace(s[0]))); \ if (n <= 0) return -1; #define DO_SKIP_WHITESPACE() \ incomment = 0; \ while ( ((n = read(fd, s, 1)) == 1) && \ ((isspace(s[0])) || (s[0] == '#') || (incomment)) ) { \ if (s[0] == '#') incomment = 1; \ if (s[0] == '\n') incomment = 0; \ } \ if (n <= 0) return -1; /* * returns: 0 - success, got header * 1 - EOF, no new frame * -1 - failure (actually, just errors out...) */staticint read_pnm_header(int fd, pnm_info_t *pnm){ char s[6]; int incomment; int n; int maxval = 0; pnm->width = 0; pnm->height = 0; pnm->depth = 0; /* look for MAGIC */ n = y4m_read(fd, s, 3); if (n > 0) return 1; /* EOF */ if (n < 0) mjpeg_error_exit1("Bad PNM header magic!"); for (pnm->format = FMT_FIRST; pnm->format < FMT_UNKNOWN; (pnm->format)++) { if (!(strncmp(s, magics[pnm->format], 2))) break; } if ( (pnm->format == FMT_UNKNOWN) || (!isspace(s[2])) ) mjpeg_error_exit1("Bad PNM magic!"); if (pnm->format == FMT_PAM) { if (s[2] != '\n') mjpeg_error_exit1("Bad PAM magic!"); return read_pam_header(fd, pnm); } else { pnm->tupl = TUPL_UNKNOWN; incomment = 0; DO_SKIP_WHITESPACE(); DO_READ_NUMBER(pnm->width); DO_SKIP_WHITESPACE(); DO_READ_NUMBER(pnm->height); if ((pnm->format != FMT_PBM_RAW) && (pnm->format != FMT_PBM_PLAIN)) { DO_SKIP_WHITESPACE(); DO_READ_NUMBER(maxval); if (maxval != 255) mjpeg_error_exit1("Expecting maxval == 255, not %d!", maxval); } switch (pnm->format) { case FMT_PPM_RAW: case FMT_PPM_PLAIN: pnm->depth = 3; break; case FMT_PGM_RAW: case FMT_PBM_RAW: case FMT_PGM_PLAIN: case FMT_PBM_PLAIN: pnm->depth = 1; break; default: assert(0); break; } } return 0;}staticvoid alloc_buffers(uint8_t *buffers[], int width, int height, int depth){ mjpeg_debug("Alloc'ing buffers"); buffers[0] = malloc(width * height * sizeof(buffers[0][0])); if (depth > 1) { buffers[1] = malloc(width * height * sizeof(buffers[1][0])); buffers[2] = malloc(width * height * sizeof(buffers[2][0])); } if (depth > 3) buffers[3] = malloc(width * height * sizeof(buffers[3][0]));}staticvoid read_rgba_raw(int fd, uint8_t *buffers[], uint8_t *rowbuffer, int width, int height, int bgra) { int x, y; uint8_t *pixels; uint8_t *R = buffers[0]; uint8_t *G = buffers[1]; uint8_t *B = buffers[2];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -