ivfenc: support reading/writing from a pipe

Use - for the filename to use stdin/stdout. Update to avoid opening
the file multiple times.

Change-Id: I356356fa16bb334d4b22abc531dc03c0d95917a3
This commit is contained in:
John Koleszar 2010-06-10 17:10:12 -04:00
Родитель db0f26c0e2
Коммит 3245d463c4
1 изменённых файлов: 170 добавлений и 164 удалений

334
ivfenc.c
Просмотреть файл

@ -1,10 +1,10 @@
/* /*
* Copyright (c) 2010 The VP8 project authors. All Rights Reserved. * Copyright (c) 2010 The VP8 project authors. All Rights Reserved.
* *
* Use of this source code is governed by a BSD-style license * Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source * that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found * tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may * in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree. * be found in the AUTHORS file in the root of the source tree.
*/ */
@ -55,8 +55,8 @@ void die(const char *fmt, ...)
{ {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
vprintf(fmt, ap); vfprintf(stderr, fmt, ap);
printf("\n"); fprintf(stderr, "\n");
usage_exit(); usage_exit();
} }
@ -66,10 +66,10 @@ static void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s)
{ {
const char *detail = vpx_codec_error_detail(ctx); const char *detail = vpx_codec_error_detail(ctx);
printf("%s: %s\n", s, vpx_codec_error(ctx)); fprintf(stderr, "%s: %s\n", s, vpx_codec_error(ctx));
if (detail) if (detail)
printf(" %s\n", detail); fprintf(stderr, " %s\n", detail);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -226,9 +226,15 @@ enum video_file_type
FILE_TYPE_Y4M FILE_TYPE_Y4M
}; };
struct detect_buffer {
char buf[4];
int valid;
};
#define IVF_FRAME_HDR_SZ (4+8) /* 4 byte size + 8 byte timestamp */ #define IVF_FRAME_HDR_SZ (4+8) /* 4 byte size + 8 byte timestamp */
static int read_frame(FILE *f, vpx_image_t *img, unsigned int file_type, static int read_frame(FILE *f, vpx_image_t *img, unsigned int file_type,
y4m_input *y4m) y4m_input *y4m, struct detect_buffer *detect)
{ {
int plane = 0; int plane = 0;
@ -275,7 +281,15 @@ static int read_frame(FILE *f, vpx_image_t *img, unsigned int file_type,
for (r = 0; r < h; r++) for (r = 0; r < h; r++)
{ {
fread(ptr, 1, w, f); if (detect->valid)
{
memcpy(ptr, detect->buf, 4);
fread(ptr+4, 1, w-4, f);
detect->valid = 0;
}
else
fread(ptr, 1, w, f);
ptr += img->stride[plane]; ptr += img->stride[plane];
} }
} }
@ -286,16 +300,14 @@ static int read_frame(FILE *f, vpx_image_t *img, unsigned int file_type,
unsigned int file_is_y4m(FILE *infile, unsigned int file_is_y4m(FILE *infile,
y4m_input *y4m) y4m_input *y4m,
char detect[4])
{ {
char raw_hdr[4]; if(memcmp(detect, "YUV4", 4) == 0 &&
if (fread(raw_hdr, 1, 4, infile) == 4 && y4m_input_open(y4m, infile, detect, 4) >= 0)
memcmp(raw_hdr, "YUV4", 4) == 0 &&
y4m_input_open(y4m, infile, raw_hdr, 4) >= 0)
{ {
return 1; return 1;
} }
rewind(infile);
return 0; return 0;
} }
@ -303,18 +315,21 @@ unsigned int file_is_y4m(FILE *infile,
unsigned int file_is_ivf(FILE *infile, unsigned int file_is_ivf(FILE *infile,
unsigned int *fourcc, unsigned int *fourcc,
unsigned int *width, unsigned int *width,
unsigned int *height) unsigned int *height,
char detect[4])
{ {
char raw_hdr[IVF_FILE_HDR_SZ]; char raw_hdr[IVF_FILE_HDR_SZ];
int is_ivf = 0; int is_ivf = 0;
if(memcmp(detect, "DKIF", 4) != 0)
return 0;
/* See write_ivf_file_header() for more documentation on the file header /* See write_ivf_file_header() for more documentation on the file header
* layout. * layout.
*/ */
if (fread(raw_hdr, 1, IVF_FILE_HDR_SZ, infile) == IVF_FILE_HDR_SZ) if (fread(raw_hdr + 4, 1, IVF_FILE_HDR_SZ - 4, infile)
== IVF_FILE_HDR_SZ - 4)
{ {
if (raw_hdr[0] == 'D' && raw_hdr[1] == 'K'
&& raw_hdr[2] == 'I' && raw_hdr[3] == 'F')
{ {
is_ivf = 1; is_ivf = 1;
@ -331,8 +346,6 @@ unsigned int file_is_ivf(FILE *infile,
*width = mem_get_le16(raw_hdr + 12); *width = mem_get_le16(raw_hdr + 12);
*height = mem_get_le16(raw_hdr + 14); *height = mem_get_le16(raw_hdr + 14);
} }
else
rewind(infile);
return is_ivf; return is_ivf;
} }
@ -546,28 +559,28 @@ static void usage_exit()
{ {
int i; int i;
printf("Usage: %s <options> src_filename dst_filename\n", exec_name); fprintf(stderr, "Usage: %s <options> src_filename dst_filename\n", exec_name);
printf("\n_options:\n"); fprintf(stderr, "\n_options:\n");
arg_show_usage(stdout, main_args); arg_show_usage(stdout, main_args);
printf("\n_encoder Global Options:\n"); fprintf(stderr, "\n_encoder Global Options:\n");
arg_show_usage(stdout, global_args); arg_show_usage(stdout, global_args);
printf("\n_rate Control Options:\n"); fprintf(stderr, "\n_rate Control Options:\n");
arg_show_usage(stdout, rc_args); arg_show_usage(stdout, rc_args);
printf("\n_twopass Rate Control Options:\n"); fprintf(stderr, "\n_twopass Rate Control Options:\n");
arg_show_usage(stdout, rc_twopass_args); arg_show_usage(stdout, rc_twopass_args);
printf("\n_keyframe Placement Options:\n"); fprintf(stderr, "\n_keyframe Placement Options:\n");
arg_show_usage(stdout, kf_args); arg_show_usage(stdout, kf_args);
#if CONFIG_VP8_ENCODER #if CONFIG_VP8_ENCODER
printf("\n_vp8 Specific Options:\n"); fprintf(stderr, "\n_vp8 Specific Options:\n");
arg_show_usage(stdout, vp8_args); arg_show_usage(stdout, vp8_args);
#endif #endif
printf("\n" fprintf(stderr, "\n"
"Included encoders:\n" "Included encoders:\n"
"\n"); "\n");
for (i = 0; i < sizeof(codecs) / sizeof(codecs[0]); i++) for (i = 0; i < sizeof(codecs) / sizeof(codecs[0]); i++)
printf(" %-6s - %s\n", fprintf(stderr, " %-6s - %s\n",
codecs[i].name, codecs[i].name,
vpx_codec_iface_name(codecs[i].iface)); vpx_codec_iface_name(codecs[i].iface));
@ -687,7 +700,7 @@ int main(int argc, const char **argv_)
/* DWIM: Assume the user meant passes=2 if pass=2 is specified */ /* DWIM: Assume the user meant passes=2 if pass=2 is specified */
if (one_pass_only > arg_passes) if (one_pass_only > arg_passes)
{ {
printf("Warning: Assuming --pass=%d implies --passes=%d\n", fprintf(stderr, "Warning: Assuming --pass=%d implies --passes=%d\n",
one_pass_only, one_pass_only); one_pass_only, one_pass_only);
arg_passes = one_pass_only; arg_passes = one_pass_only;
} }
@ -701,7 +714,8 @@ int main(int argc, const char **argv_)
if (res) if (res)
{ {
printf("Failed to get config: %s\n", vpx_codec_err_to_string(res)); fprintf(stderr, "Failed to get config: %s\n",
vpx_codec_err_to_string(res));
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -761,24 +775,27 @@ int main(int argc, const char **argv_)
cfg.rc_2pass_vbr_bias_pct = arg_parse_uint(&arg); cfg.rc_2pass_vbr_bias_pct = arg_parse_uint(&arg);
if (arg_passes < 2) if (arg_passes < 2)
printf("Warning: option %s ignored in one-pass mode.\n", fprintf(stderr,
arg.name); "Warning: option %s ignored in one-pass mode.\n",
arg.name);
} }
else if (arg_match(&arg, &minsection_pct, argi)) else if (arg_match(&arg, &minsection_pct, argi))
{ {
cfg.rc_2pass_vbr_minsection_pct = arg_parse_uint(&arg); cfg.rc_2pass_vbr_minsection_pct = arg_parse_uint(&arg);
if (arg_passes < 2) if (arg_passes < 2)
printf("Warning: option %s ignored in one-pass mode.\n", fprintf(stderr,
arg.name); "Warning: option %s ignored in one-pass mode.\n",
arg.name);
} }
else if (arg_match(&arg, &maxsection_pct, argi)) else if (arg_match(&arg, &maxsection_pct, argi))
{ {
cfg.rc_2pass_vbr_maxsection_pct = arg_parse_uint(&arg); cfg.rc_2pass_vbr_maxsection_pct = arg_parse_uint(&arg);
if (arg_passes < 2) if (arg_passes < 2)
printf("Warning: option %s ignored in one-pass mode.\n", fprintf(stderr,
arg.name); "Warning: option %s ignored in one-pass mode.\n",
arg.name);
} }
else if (arg_match(&arg, &kf_min_dist, argi)) else if (arg_match(&arg, &kf_min_dist, argi))
cfg.kf_min_dist = arg_parse_uint(&arg); cfg.kf_min_dist = arg_parse_uint(&arg);
@ -826,7 +843,7 @@ int main(int argc, const char **argv_)
/* Check for unrecognized options */ /* Check for unrecognized options */
for (argi = argv; *argi; argi++) for (argi = argv; *argi; argi++)
if (argi[0][0] == '-') if (argi[0][0] == '-' && argi[0][1])
die("Error: Unrecognized option %s\n", *argi); die("Error: Unrecognized option %s\n", *argi);
/* Handle non-option arguments */ /* Handle non-option arguments */
@ -836,140 +853,126 @@ int main(int argc, const char **argv_)
if (!in_fn || !out_fn) if (!in_fn || !out_fn)
usage_exit(); usage_exit();
/* Parse certain options from the input file, if possible */
infile = fopen(in_fn, "rb");
if (!infile)
{
printf("Failed to open input file");
return EXIT_FAILURE;
}
if (file_is_y4m(infile, &y4m))
{
file_type = FILE_TYPE_Y4M;
cfg.g_w = y4m.pic_w;
cfg.g_h = y4m.pic_h;
/* Use the frame rate from the file only if none was specified on the
* command-line.
*/
if (!arg_have_timebase)
{
cfg.g_timebase.num = y4m.fps_d;
cfg.g_timebase.den = y4m.fps_n;
}
arg_use_i420 = 0;
}
else if (file_is_ivf(infile, &fourcc, &cfg.g_w, &cfg.g_h))
{
file_type = FILE_TYPE_IVF;
switch (fourcc)
{
case 0x32315659:
arg_use_i420 = 0;
break;
case 0x30323449:
arg_use_i420 = 1;
break;
default:
printf("Unsupported fourcc (%08x) in IVF\n", fourcc);
return EXIT_FAILURE;
}
}
else
file_type = FILE_TYPE_RAW;
fclose(infile);
#define SHOW(field) printf(" %-28s = %d\n", #field, cfg.field)
if (verbose)
{
printf("Codec: %s\n", vpx_codec_iface_name(codec->iface));
printf("Source file: %s Format: %s\n", in_fn, arg_use_i420 ? "I420" : "YV12");
printf("Destination file: %s\n", out_fn);
printf("Encoder parameters:\n");
SHOW(g_usage);
SHOW(g_threads);
SHOW(g_profile);
SHOW(g_w);
SHOW(g_h);
SHOW(g_timebase.num);
SHOW(g_timebase.den);
SHOW(g_error_resilient);
SHOW(g_pass);
SHOW(g_lag_in_frames);
SHOW(rc_dropframe_thresh);
SHOW(rc_resize_allowed);
SHOW(rc_resize_up_thresh);
SHOW(rc_resize_down_thresh);
SHOW(rc_end_usage);
SHOW(rc_target_bitrate);
SHOW(rc_min_quantizer);
SHOW(rc_max_quantizer);
SHOW(rc_undershoot_pct);
SHOW(rc_overshoot_pct);
SHOW(rc_buf_sz);
SHOW(rc_buf_initial_sz);
SHOW(rc_buf_optimal_sz);
SHOW(rc_2pass_vbr_bias_pct);
SHOW(rc_2pass_vbr_minsection_pct);
SHOW(rc_2pass_vbr_maxsection_pct);
SHOW(kf_mode);
SHOW(kf_min_dist);
SHOW(kf_max_dist);
}
if (file_type == FILE_TYPE_Y4M)
/*The Y4M reader does its own allocation.
Just initialize this here to avoid problems if we never read any
frames.*/
memset(&raw, 0, sizeof(raw));
else
vpx_img_alloc(&raw, arg_use_i420 ? VPX_IMG_FMT_I420 : VPX_IMG_FMT_YV12,
cfg.g_w, cfg.g_h, 1);
// This was added so that ivfenc will create monotically increasing
// timestamps. Since we create new timestamps for alt-reference frames
// we need to make room in the series of timestamps. Since there can
// only be 1 alt-ref frame ( current bitstream) multiplying by 2
// gives us enough room.
cfg.g_timebase.den *= 2;
memset(&stats, 0, sizeof(stats)); memset(&stats, 0, sizeof(stats));
for (pass = one_pass_only ? one_pass_only - 1 : 0; pass < arg_passes; pass++) for (pass = one_pass_only ? one_pass_only - 1 : 0; pass < arg_passes; pass++)
{ {
int frames_in = 0, frames_out = 0; int frames_in = 0, frames_out = 0;
unsigned long nbytes = 0; unsigned long nbytes = 0;
struct detect_buffer detect;
infile = fopen(in_fn, "rb"); /* Parse certain options from the input file, if possible */
infile = strcmp(in_fn, "-") ? fopen(in_fn, "rb") : stdin;
if (!infile) if (!infile)
{ {
printf("Failed to open input file"); fprintf(stderr, "Failed to open input file\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
/*Skip the file header.*/ fread(detect.buf, 1, 4, infile);
if (file_type == FILE_TYPE_IVF) detect.valid = 0;
if (file_is_y4m(infile, &y4m, detect.buf))
{ {
char raw_hdr[IVF_FILE_HDR_SZ]; file_type = FILE_TYPE_Y4M;
(void)fread(raw_hdr, 1, IVF_FILE_HDR_SZ, infile); cfg.g_w = y4m.pic_w;
cfg.g_h = y4m.pic_h;
/* Use the frame rate from the file only if none was specified on the
* command-line.
*/
if (!arg_have_timebase)
{
cfg.g_timebase.num = y4m.fps_d;
cfg.g_timebase.den = y4m.fps_n;
}
arg_use_i420 = 0;
} }
else if(file_type == FILE_TYPE_Y4M) else if (file_is_ivf(infile, &fourcc, &cfg.g_w, &cfg.g_h, detect.buf))
{ {
char buffer[80]; file_type = FILE_TYPE_IVF;
(void)fgets(buffer, sizeof(buffer)/sizeof(*buffer) - 1, infile); switch (fourcc)
{
case 0x32315659:
arg_use_i420 = 0;
break;
case 0x30323449:
arg_use_i420 = 1;
break;
default:
fprintf(stderr, "Unsupported fourcc (%08x) in IVF\n", fourcc);
return EXIT_FAILURE;
}
}
else
{
file_type = FILE_TYPE_RAW;
detect.valid = 1;
}
#define SHOW(field) fprintf(stderr, " %-28s = %d\n", #field, cfg.field)
if (verbose && pass == 0)
{
fprintf(stderr, "Codec: %s\n", vpx_codec_iface_name(codec->iface));
fprintf(stderr, "Source file: %s Format: %s\n", in_fn,
arg_use_i420 ? "I420" : "YV12");
fprintf(stderr, "Destination file: %s\n", out_fn);
fprintf(stderr, "Encoder parameters:\n");
SHOW(g_usage);
SHOW(g_threads);
SHOW(g_profile);
SHOW(g_w);
SHOW(g_h);
SHOW(g_timebase.num);
SHOW(g_timebase.den);
SHOW(g_error_resilient);
SHOW(g_pass);
SHOW(g_lag_in_frames);
SHOW(rc_dropframe_thresh);
SHOW(rc_resize_allowed);
SHOW(rc_resize_up_thresh);
SHOW(rc_resize_down_thresh);
SHOW(rc_end_usage);
SHOW(rc_target_bitrate);
SHOW(rc_min_quantizer);
SHOW(rc_max_quantizer);
SHOW(rc_undershoot_pct);
SHOW(rc_overshoot_pct);
SHOW(rc_buf_sz);
SHOW(rc_buf_initial_sz);
SHOW(rc_buf_optimal_sz);
SHOW(rc_2pass_vbr_bias_pct);
SHOW(rc_2pass_vbr_minsection_pct);
SHOW(rc_2pass_vbr_maxsection_pct);
SHOW(kf_mode);
SHOW(kf_min_dist);
SHOW(kf_max_dist);
} }
outfile = fopen(out_fn, "wb"); if(pass == 0) {
if (file_type == FILE_TYPE_Y4M)
/*The Y4M reader does its own allocation.
Just initialize this here to avoid problems if we never read any
frames.*/
memset(&raw, 0, sizeof(raw));
else
vpx_img_alloc(&raw, arg_use_i420 ? VPX_IMG_FMT_I420 : VPX_IMG_FMT_YV12,
cfg.g_w, cfg.g_h, 1);
// This was added so that ivfenc will create monotically increasing
// timestamps. Since we create new timestamps for alt-reference frames
// we need to make room in the series of timestamps. Since there can
// only be 1 alt-ref frame ( current bitstream) multiplying by 2
// gives us enough room.
cfg.g_timebase.den *= 2;
}
outfile = strcmp(out_fn, "-") ? fopen(out_fn, "wb") : stdout;
if (!outfile) if (!outfile)
{ {
printf("Failed to open output file"); fprintf(stderr, "Failed to open output file\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -977,7 +980,7 @@ int main(int argc, const char **argv_)
{ {
if (!stats_open_file(&stats, stats_fn, pass)) if (!stats_open_file(&stats, stats_fn, pass))
{ {
printf("Failed to open statistics store\n"); fprintf(stderr, "Failed to open statistics store\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
@ -985,7 +988,7 @@ int main(int argc, const char **argv_)
{ {
if (!stats_open_mem(&stats, pass)) if (!stats_open_mem(&stats, pass))
{ {
printf("Failed to open statistics store\n"); fprintf(stderr, "Failed to open statistics store\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
@ -1020,8 +1023,8 @@ int main(int argc, const char **argv_)
for (i = 0; i < arg_ctrl_cnt; i++) for (i = 0; i < arg_ctrl_cnt; i++)
{ {
if (vpx_codec_control_(&encoder, arg_ctrls[i][0], arg_ctrls[i][1])) if (vpx_codec_control_(&encoder, arg_ctrls[i][0], arg_ctrls[i][1]))
printf("Error: Tried to set control %d = %d\n", fprintf(stderr, "Error: Tried to set control %d = %d\n",
arg_ctrls[i][0], arg_ctrls[i][1]); arg_ctrls[i][0], arg_ctrls[i][1]);
ctx_exit_on_error(&encoder, "Failed to control codec"); ctx_exit_on_error(&encoder, "Failed to control codec");
} }
@ -1037,13 +1040,15 @@ int main(int argc, const char **argv_)
if (!arg_limit || frames_in < arg_limit) if (!arg_limit || frames_in < arg_limit)
{ {
frame_avail = read_frame(infile, &raw, file_type, &y4m); frame_avail = read_frame(infile, &raw, file_type, &y4m,
&detect);
if (frame_avail) if (frame_avail)
frames_in++; frames_in++;
printf("\rPass %d/%d frame %4d/%-4d %7ldB \033[K", pass + 1, fprintf(stderr,
arg_passes, frames_in, frames_out, nbytes); "\rPass %d/%d frame %4d/%-4d %7ldB \033[K", pass + 1,
arg_passes, frames_in, frames_out, nbytes);
} }
else else
frame_avail = 0; frame_avail = 0;
@ -1067,15 +1072,15 @@ int main(int argc, const char **argv_)
{ {
case VPX_CODEC_CX_FRAME_PKT: case VPX_CODEC_CX_FRAME_PKT:
frames_out++; frames_out++;
printf(" %6luF", fprintf(stderr, " %6luF",
(unsigned long)pkt->data.frame.sz); (unsigned long)pkt->data.frame.sz);
write_ivf_frame_header(outfile, pkt); write_ivf_frame_header(outfile, pkt);
fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile); fwrite(pkt->data.frame.buf, 1, pkt->data.frame.sz, outfile);
nbytes += pkt->data.raw.sz; nbytes += pkt->data.raw.sz;
break; break;
case VPX_CODEC_STATS_PKT: case VPX_CODEC_STATS_PKT:
frames_out++; frames_out++;
printf(" %6luS", fprintf(stderr, " %6luS",
(unsigned long)pkt->data.twopass_stats.sz); (unsigned long)pkt->data.twopass_stats.sz);
stats_write(&stats, stats_write(&stats,
pkt->data.twopass_stats.buf, pkt->data.twopass_stats.buf,
@ -1089,7 +1094,7 @@ int main(int argc, const char **argv_)
int i; int i;
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
printf("%.3lf ", pkt->data.psnr.psnr[i]); fprintf(stderr, "%.3lf ", pkt->data.psnr.psnr[i]);
} }
break; break;
@ -1104,7 +1109,8 @@ int main(int argc, const char **argv_)
/* this bitrate calc is simplified and relies on the fact that this /* this bitrate calc is simplified and relies on the fact that this
* application uses 1/timebase for framerate. * application uses 1/timebase for framerate.
*/ */
printf("\rPass %d/%d frame %4d/%-4d %7ldB %7ldb/f %7"PRId64"b/s" fprintf(stderr,
"\rPass %d/%d frame %4d/%-4d %7ldB %7ldb/f %7"PRId64"b/s"
" %7lu %s (%.2f fps)\033[K", pass + 1, " %7lu %s (%.2f fps)\033[K", pass + 1,
arg_passes, frames_in, frames_out, nbytes, nbytes * 8 / frames_in, arg_passes, frames_in, frames_out, nbytes, nbytes * 8 / frames_in,
nbytes * 8 *(int64_t)cfg.g_timebase.den/2/ cfg.g_timebase.num / frames_in, nbytes * 8 *(int64_t)cfg.g_timebase.den/2/ cfg.g_timebase.num / frames_in,
@ -1121,7 +1127,7 @@ int main(int argc, const char **argv_)
fclose(outfile); fclose(outfile);
stats_close(&stats); stats_close(&stats);
printf("\n"); fprintf(stderr, "\n");
if (one_pass_only) if (one_pass_only)
break; break;