save_video_frame_as_pnm: use pam_write

use current version of pam.h from GPUJPEG that supports writing PNM
This commit is contained in:
Martin Pulec
2022-11-28 09:18:18 +01:00
parent 15af296d27
commit 12d818ea19
3 changed files with 136 additions and 71 deletions

View File

@@ -67,23 +67,23 @@ static int init(struct module *parent, const char *cfg, void **state);
static void done(void *state);
static struct video_frame *filter(void *state, struct video_frame *in);
using namespace std;
static bool load_logo_data_from_file(struct state_capture_filter_logo *s, const char *filename) {
if (strcasecmp(filename + (MAX(strlen(filename), 4) - 4), ".pam") == 0) {
int depth = 0;
bool rgb;
unsigned char *data;
if (!pam_read(filename, &s->width, &s->height, &depth, NULL, &data, malloc)) {
struct pam_metadata info;
if (!pam_read(filename, &info, &data, malloc)) {
return false;
}
if (depth != 3 && depth != 4) {
s->width = info.width;
s->height = info.height;
if (info.depth != 3 && info.depth != 4) {
cerr << "Unsupported depth passed.";
free(data);
return false;
}
rgb = depth == 3;
int datalen = depth * s->width * s->height;
rgb = info.depth == 3;
int datalen = info.depth * s->width * s->height;
if (rgb) {
datalen = 4 * s->width * s->height;
auto tmp = (unsigned char *) malloc(datalen);

View File

@@ -2,6 +2,9 @@
* @file utils/pam.h
* @author Martin Pulec <pulec@cesnet.cz>
*
* Very simple library to read and write PAM/PPM files. Only binary formats
* (P5, P6) for PNM are processed, P4 is also not used.
*
* This file is part of GPUJPEG.
*/
/*
@@ -36,11 +39,13 @@
#define PAM_H_7E23A609_963A_45A8_88E2_ED4D3FDFF69F
#ifdef __cplusplus
#include <cctype>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#else
#include <ctype.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
@@ -54,27 +59,19 @@
#define PAM_ATTRIBUTE_UNUSED
#endif
/**
* @pram maxval may be NULL in which case equality to 255 is checked
*/
static inline bool pam_read(const char *filename, unsigned int *width, unsigned int *height, int *depth, int *maxval, unsigned char **data, void *(*allocator)(size_t)) PAM_ATTRIBUTE_UNUSED;
static inline bool pam_read(const char *filename, unsigned int *width, unsigned int *height, int *depth, int *maxval, unsigned char **data, void *(*allocator)(size_t)) {
struct pam_metadata {
int width;
int height;
int depth; // == channel count
int maxval;
};
static inline bool pam_read(const char *filename, struct pam_metadata *info, unsigned char **data, void *(*allocator)(size_t)) PAM_ATTRIBUTE_UNUSED;
static bool pam_write(const char *filename, unsigned int width, unsigned int height, int depth, int maxval, const unsigned char *data, bool pnm) PAM_ATTRIBUTE_UNUSED;
static inline void parse_pam(FILE *file, struct pam_metadata *info) {
char line[128];
errno = 0;
FILE *file = fopen(filename, "rb");
if (!file) {
fprintf(stderr, "Failed to open %s: %s", filename, strerror(errno));
return false;
}
fgets(line, sizeof line - 1, file);
if (feof(file) || ferror(file) || strcmp(line, "P7\n") != 0) {
fprintf(stderr, "File '%s' doesn't seem to be valid PAM.\n", filename);
fclose(file);
return false;
}
fgets(line, sizeof line - 1, file);
*width = 0, *height = 0, *depth = 0;
int maxv = 0;
while (!feof(file) && !ferror(file)) {
if (strcmp(line, "ENDHDR\n") == 0) {
break;
@@ -87,21 +84,13 @@ static inline bool pam_read(const char *filename, unsigned int *width, unsigned
*spc = '\0';
const char *val = spc + 1;
if (strcmp(key, "WIDTH") == 0) {
*width = atoi(val);
info->width = atoi(val);
} else if (strcmp(key, "HEIGHT") == 0) {
*height = atoi(val);
info->height = atoi(val);
} else if (strcmp(key, "DEPTH") == 0) {
*depth = atoi(val);
info->depth = atoi(val);
} else if (strcmp(key, "MAXVAL") == 0) {
maxv = atoi(val);
if (maxval == NULL && maxv != 255) {
fprintf(stderr, "Maxval 255 is assumed but %d presented.\n", maxv);
fclose(file);
return false;
}
if (maxval != NULL) {
*maxval = maxv;
}
info->maxval = atoi(val);
} else if (strcmp(key, "TUPLTYPE") == 0) {
// ignored - assuming MAXVAL == 255, value of DEPTH is sufficient
// to determine pixel format
@@ -110,17 +99,89 @@ static inline bool pam_read(const char *filename, unsigned int *width, unsigned
}
fgets(line, sizeof line - 1, file);
}
if (*width * *height == 0) {
}
static inline bool parse_pnm(FILE *file, char pnm_id, struct pam_metadata *info) {
switch (pnm_id) {
case '1':
case '2':
case '3':
case '4':
fprintf(stderr, "Unsupported PNM P%c\n", pnm_id);
return false;
case '5':
info->depth = 1;
break;
case '6':
info->depth = 3;
break;
default:
fprintf(stderr, "Wrong PNM P%c\n", pnm_id);
return false;
}
int item_nr = 0;
while (!feof(file) && !ferror(file)) {
int val = 0;
if (fscanf(file, "%d", &val) == 1) {
switch (item_nr++) {
case 0:
info->width = val;
break;
case 1:
info->height = val;
break;
case 2:
info->maxval = val;
getc(file); // skip whitespace following header
return true;
}
} else {
if (getc(file) == '#') {
int ch;
while ((ch = getc(file)) != '\n' && ch != EOF)
;
} else {
break;
}
}
}
fprintf(stderr, "Problem parsing PNM header, number of hdr items successfully read: %d\n", item_nr);
return false;
}
static inline bool pam_read(const char *filename, struct pam_metadata *info, unsigned char **data, void *(*allocator)(size_t)) {
char line[128];
errno = 0;
FILE *file = fopen(filename, "rb");
if (!file) {
fprintf(stderr, "Failed to open %s: %s", filename, strerror(errno));
return false;
}
memset(info, 0, sizeof *info);
fgets(line, 4, file);
if (feof(file) || ferror(file)) {
fprintf(stderr, "File '%s' read error: %s\n", filename, strerror(errno));
}
if (strcmp(line, "P7\n") == 0) {
parse_pam(file, info);
} else if (strlen(line) == 3 && line[0] == 'P' && isspace(line[2])) {
parse_pnm(file, line[1], info);
} else {
fprintf(stderr, "File '%s' doesn't seem to be valid PAM or PNM.\n", filename);
fclose(file);
return false;
}
if (info->width * info->height == 0) {
fprintf(stderr, "Unspecified size header field!");
fclose(file);
return false;
}
if (*depth == 0) {
if (info->depth == 0) {
fprintf(stderr, "Unspecified depth header field!");
fclose(file);
return false;
}
if (maxv == 0) {
if (info->maxval == 0) {
fprintf(stderr, "Unspecified maximal value field!");
fclose(file);
return false;
@@ -129,7 +190,7 @@ static inline bool pam_read(const char *filename, unsigned int *width, unsigned
fclose(file);
return true;
}
int datalen = *depth * *width * *height * (maxv <= 255 ? 1 : 2);
size_t datalen = (size_t) info->depth * info->width * info->height * (info->maxval <= 255 ? 1 : 2);
*data = (unsigned char *) allocator(datalen);
if (!*data) {
fprintf(stderr, "Unspecified depth header field!");
@@ -138,7 +199,7 @@ static inline bool pam_read(const char *filename, unsigned int *width, unsigned
}
fread((char *) *data, datalen, 1, file);
if (feof(file) || ferror(file)) {
perror("Unable to load PAM data from file");
perror("Unable to load PAM/PNM data from file");
fclose(file);
return false;
}
@@ -146,34 +207,45 @@ static inline bool pam_read(const char *filename, unsigned int *width, unsigned
return true;
}
static bool pam_write(const char *filename, unsigned int width, unsigned int height, int depth, int maxval, const unsigned char *data) PAM_ATTRIBUTE_UNUSED;
static bool pam_write(const char *filename, unsigned int width, unsigned int height, int depth, int maxval, const unsigned char *data) {
static bool pam_write(const char *filename, unsigned int width, unsigned int height, int depth, int maxval, const unsigned char *data, bool pnm) {
errno = 0;
FILE *file = fopen(filename, "wb");
if (!file) {
fprintf(stderr, "Failed to open %s for writing: %s", filename, strerror(errno));
return false;
}
const char *tuple_type = "INVALID";
switch (depth) {
case 4: tuple_type = "RGB_ALPHA"; break;
case 3: tuple_type = "RGB"; break;
case 2: tuple_type = "GRAYSCALE_ALPHA"; break;
case 1: tuple_type = "GRAYSCALE"; break;
default: fprintf(stderr, "Wrong depth: %d\n", depth);
if (pnm) {
if (depth != 1 && depth != 3) {
fprintf(stderr, "Only 1 or 3 channels supported for PNM!\n");
return false;
}
fprintf(file, "P%d\n"
"%u %u\n"
"%d\n",
depth == 1 ? 5 : 6,
width, height, maxval);
} else {
const char *tuple_type = "INVALID";
switch (depth) {
case 4: tuple_type = "RGB_ALPHA"; break;
case 3: tuple_type = "RGB"; break;
case 2: tuple_type = "GRAYSCALE_ALPHA"; break;
case 1: tuple_type = "GRAYSCALE"; break;
default: fprintf(stderr, "Wrong depth: %d\n", depth);
}
fprintf(file, "P7\n"
"WIDTH %u\n"
"HEIGHT %u\n"
"DEPTH %d\n"
"MAXVAL %d\n"
"TUPLTYPE %s\n"
"ENDHDR\n",
width, height, depth, maxval, tuple_type);
}
fprintf(file, "P7\n"
"WIDTH %u\n"
"HEIGHT %u\n"
"DEPTH %d\n"
"MAXVAL %d\n"
"TUPLTYPE %s\n"
"ENDHDR\n",
width, height, depth, maxval, tuple_type);
fwrite((const char *) data, width * height * depth, maxval <= 255 ? 1 : 2, file);
bool ret = !ferror(file);
if (!ret) {
perror("Unable to write PAM data");
perror("Unable to write PAM/PNM data");
}
fclose(file);
return ret;

View File

@@ -59,6 +59,7 @@
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "utils/pam.h"
#include "utils/y4m.h"
#include "video_codec.h"
#include "video_frame.h"
@@ -432,7 +433,6 @@ bool save_video_frame_as_pnm(struct video_frame *frame, const char *name)
{
unsigned char *data = NULL, *tmp_data = NULL;
struct tile *tile = &frame->tiles[0];
int len = tile->width * tile->height * 3;
if (frame->color_spec == RGB) {
data = (unsigned char *) tile->data;
} else if (get_bits_per_component(frame->color_spec) <= 8 ||
@@ -443,6 +443,7 @@ bool save_video_frame_as_pnm(struct video_frame *frame, const char *name)
get_codec_name(frame->color_spec));
return false;
}
int len = tile->width * tile->height * 3;
data = tmp_data = (unsigned char *) malloc(len);
dec (data, (const unsigned char *) tile->data, len, 0, 0, 0);
} else {
@@ -450,21 +451,13 @@ bool save_video_frame_as_pnm(struct video_frame *frame, const char *name)
if (!data) {
return false;
}
len *= 2;
}
if (!data) {
return false;
}
FILE *out = fopen(name, "w");
if(out) {
fprintf(out, "P6\n%d %d\n%d\n", tile->width, tile->height, (1<<get_bits_per_component(frame->color_spec)) - 1);
if (fwrite(data, len, 1, out) != 1) {
perror("fwrite");
}
fclose(out);
}
pam_write(name, tile->width, tile->height, 3, (1<<get_bits_per_component(frame->color_spec)) - 1, data, true);
free(tmp_data);
return true;