mirror of
https://github.com/outbackdingo/UltraGrid.git
synced 2026-03-22 04:40:30 +00:00
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:
@@ -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);
|
||||
|
||||
180
src/utils/pam.h
180
src/utils/pam.h
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user