y4m start header parsing ready

This commit is contained in:
Sami Ahovainio 2020-09-07 14:04:48 +03:00
parent c10b841e7c
commit cbcee67821
4 changed files with 147 additions and 2 deletions

View file

@ -163,6 +163,8 @@ int kvz_config_init(kvz_config *cfg)
cfg->intra_bit_allocation = false; cfg->intra_bit_allocation = false;
cfg->clip_neighbour = true; cfg->clip_neighbour = true;
cfg->file_format = KVZ_FORMAT_AUTO;
return 1; return 1;
} }
@ -448,8 +450,10 @@ int kvz_config_parse(kvz_config *cfg, const char *name, const char *value)
static const char * const scaling_list_names[] = { "off", "custom", "default", NULL }; static const char * const scaling_list_names[] = { "off", "custom", "default", NULL };
static const char * const rc_algorithm_names[] = { "no-rc", "lambda", "oba", NULL }; static const char * const rc_algorithm_names[] = { "no-rc", "lambda", "oba", NULL };
static const char * const preset_values[11][25*2] = {
static const char * const file_format_names[] = {"y4m", NULL};
static const char * const preset_values[11][25*2] = {
{ {
"ultrafast", "ultrafast",
"rd", "0", "rd", "0",
@ -1359,6 +1363,15 @@ int kvz_config_parse(kvz_config *cfg, const char *name, const char *value)
else if OPT("clip-neighbour") { else if OPT("clip-neighbour") {
cfg->clip_neighbour = atobool(value); cfg->clip_neighbour = atobool(value);
} }
else if OPT("input-file-format") {
int8_t file_format = 0;
if (!parse_enum(value, file_format_names, &file_format)) {
fprintf(stderr, "Invalid input file format %s. Valid values include %s\n", value,
file_format_names[0]);
return 0;
}
cfg->file_format = file_format;
}
else { else {
return 0; return 0;
} }

View file

@ -149,6 +149,7 @@ static const struct option long_options[] = {
{ "no-intra-bits", no_argument, NULL, 0 }, { "no-intra-bits", no_argument, NULL, 0 },
{ "clip-neighbour", no_argument, NULL, 0 }, { "clip-neighbour", no_argument, NULL, 0 },
{ "no-clip-neighbour", no_argument, NULL, 0 }, { "no-clip-neighbour", no_argument, NULL, 0 },
{ "input-file-format", required_argument, NULL, 0 },
{0, 0, 0, 0} {0, 0, 0, 0}
}; };
@ -184,6 +185,26 @@ static int select_input_res_auto(const char *file_name, int32_t *out_width, int3
return success; return success;
} }
/**
* \brief Try to detect file format from file name automatically
*
* \param file_name file name to get format from
* \return 0 (auto) if no format is detected, or id of the format
*/
static int detect_file_format(const char *file_name) {
if (!file_name) return 0;
// Find the last delimiter char ( / or \ ). Hope the other kind is not used in the name.
// If delim is not found, set pointer to the beginning.
unsigned char* sub_str = (unsigned char*)strrchr(file_name, '.');
if (!sub_str) return 0;
// KVZ_FILE_FORMAT
if (strcmp(sub_str, ".y4m") == 0) return 1;
return 0;
}
/** /**
* \brief Parse command line arguments. * \brief Parse command line arguments.
* \param argc Number of arguments * \param argc Number of arguments
@ -289,8 +310,13 @@ cmdline_opts_t* cmdline_opts_parse(const kvz_api *const api, int argc, char *arg
goto done; goto done;
} }
// Check the file name for format
if (opts->config->file_format == KVZ_FORMAT_AUTO) {
opts->config->file_format = detect_file_format(opts->input);
}
// Set resolution automatically if necessary // Set resolution automatically if necessary
if (opts->config->width == 0 && opts->config->height == 0) { if (opts->config->file_format == KVZ_FORMAT_AUTO && opts->config->width == 0 && opts->config->height == 0) {
ok = select_input_res_auto(opts->input, &opts->config->width, &opts->config->height); ok = select_input_res_auto(opts->input, &opts->config->width, &opts->config->height);
goto done; goto done;
} }
@ -370,6 +396,9 @@ void print_help(void)
" --input-format <string> : P420 or P400 [P420]\n" " --input-format <string> : P420 or P400 [P420]\n"
" --input-bitdepth <int> : 8-16 [8]\n" " --input-bitdepth <int> : 8-16 [8]\n"
" --loop-input : Re-read input file forever.\n" " --loop-input : Re-read input file forever.\n"
" --input-file-format <string> : Input file format if other than RAW [auto]\n"
" - auto: Check the file ending for format\n"
" - y4m\n"
"\n" "\n"
/* Word wrap to this width to stay under 80 characters (including ") *************/ /* Word wrap to this width to stay under 80 characters (including ") *************/
"Options:\n" "Options:\n"

View file

@ -310,6 +310,92 @@ static double calc_avg_qp(uint64_t qp_sum, uint32_t frames_done)
return (double)qp_sum / (double)frames_done; return (double)qp_sum / (double)frames_done;
} }
/**
* \brief Reads the information in y4m header
*
* \param input Pointer to the input file
* \param config Pointer to the config struct
*/
static bool read_header(FILE* input, kvz_config* config) {
char buffer[256];
bool end_of_header = false;
while(!end_of_header) {
for (int i = 0; i < 256; i++) {
buffer[i] = getc(input);
// Start code of frame data
if (buffer[i] == 0x0A) {
for (; i > 0; i--) {
ungetc(buffer[i], input);
}
end_of_header = true;
break;
}
// Header sections are separated by space (ascii 0x20)
if (buffer[i] == 0x20) {
// Header start sequence does not hold any addition information, so it can be skipped
if ((i == 9) && strncmp(buffer, "YUV4MPEG2 ", 10) == 0) {
break;
}
switch (buffer[0]) {
// Width
case 'W':
// Exclude starting 'W' and the space at the end with substr
config->width = atoi(&buffer[1]);
break;
// Height
case 'H':
// Exclude starting 'H' and the space at the end with substr
config->height = atoi(&buffer[1]);
break;
// Framerate (or start code of frame)
case 'F':
// The header has no ending signature other than the start code of a frame
if (i > 5 && strncmp(buffer, "FRAME", 5) == 0) {
for (; i > 0; i--) {
ungetc(buffer[i], input);
}
end_of_header = true;
break;
}
else {
config->framerate_num = atoi(&buffer[1]);
for (int j = 0; j < i; j++) {
if (buffer[j] == ':') {
config->framerate_denom = atoi(&buffer[j + 1]);
}
}
break;
}
// Interlacing
case 'I':
break;
// Aspect ratio
case 'A':
break;
// Colour space
case 'C':
break;
// Comment
case 'X':
break;
default:
fprintf(stderr, "Unknown header argument starting with '%i'\n", buffer[0]);
break;
}
break;
}
}
}
if (config->width == 0 || config->height == 0 || config->framerate_num == 0 || config->framerate_denom == 0) {
fprintf(stderr, "Failed to read necessary info from y4m headers. Width, height and frame rate must be present in the headers.\n");
return false;
}
return true;
}
/** /**
* \brief Program main function. * \brief Program main function.
* \param argc Argument count from commandline * \param argc Argument count from commandline
@ -409,6 +495,13 @@ int main(int argc, char *argv[])
} }
} }
// Parse headers if input data is in y4m container
if (opts->config->file_format == KVZ_FORMAT_Y4M) {
if (!read_header(input, opts->config)) {
goto exit_failure;
}
}
enc = api->encoder_open(opts->config); enc = api->encoder_open(opts->config);
if (!enc) { if (!enc) {
fprintf(stderr, "Failed to open encoder.\n"); fprintf(stderr, "Failed to open encoder.\n");

View file

@ -227,6 +227,14 @@ enum kvz_rc_algorithm
KVZ_LAMBDA = 1, KVZ_LAMBDA = 1,
KVZ_OBA = 2, KVZ_OBA = 2,
}; };
enum kvz_file_format
{
KVZ_FORMAT_AUTO = 0,
KVZ_FORMAT_Y4M = 1
};
// Map from input format to chroma format. // Map from input format to chroma format.
#define KVZ_FORMAT2CSP(format) ((enum kvz_chroma_format)"\0\1\2\3"[format]) #define KVZ_FORMAT2CSP(format) ((enum kvz_chroma_format)"\0\1\2\3"[format])
@ -437,6 +445,8 @@ typedef struct kvz_config
uint8_t intra_bit_allocation; uint8_t intra_bit_allocation;
uint8_t clip_neighbour; uint8_t clip_neighbour;
enum kvz_file_format file_format;
} kvz_config; } kvz_config;
/** /**