Commit 9e50ed7f authored by John Koleszar's avatar John Koleszar

vpxenc: initial implementation of multistream support

Add the ability to specify multiple output streams on the command line.
Streams are delimited by --, and most parameters inherit from previous
streams.

In this implementation, resizing streams is still not supported. It
does not make use of the new multistream support in the encoder either.
Two pass support runs all streams independently, though it's
theoretically possible that we could combine firstpass runs in the
future. The logic required for this is too tricky to do as part of this
initial implementation. This is mostly an effort to get the parameter
passing and independent streams working from the application's
perspective, and a later commit will add the rescaling and
multiresolution support.

Change-Id: Ibf18c2355f54189fc91952c734c899e5c072b3e0
parent 732cb9a6
......@@ -116,13 +116,17 @@ void warn(const char *fmt, ...)
}
static void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s)
static void ctx_exit_on_error(vpx_codec_ctx_t *ctx, const char *s, ...)
{
va_list ap;
va_start(ap, s);
if (ctx->err)
{
const char *detail = vpx_codec_error_detail(ctx);
fprintf(stderr, "%s: %s\n", s, vpx_codec_error(ctx));
vfprintf(stderr, s, ap);
fprintf(stderr, ": %s\n", vpx_codec_error(ctx));
if (detail)
fprintf(stderr, " %s\n", detail);
......@@ -1480,8 +1484,6 @@ struct global_config
const struct codec_item *codec;
int passes;
int pass;
const char *stats_fn;
const char *out_fn;
int usage;
int deadline;
int use_i420;
......@@ -1490,13 +1492,47 @@ struct global_config
int show_psnr;
int have_framerate;
struct vpx_rational framerate;
int write_webm;
int debug;
int show_q_hist_buckets;
int show_rate_hist_buckets;
};
/* Per-stream configuration */
struct stream_config
{
struct vpx_codec_enc_cfg cfg;
const char *out_fn;
const char *stats_fn;
stereo_format_t stereo_fmt;
int arg_ctrls[ARG_CTRL_CNT_MAX][2];
int arg_ctrl_cnt;
int write_webm;
};
struct stream_state
{
int index;
struct stream_state *next;
struct stream_config config;
FILE *file;
struct rate_hist rate_hist;
EbmlGlobal ebml;
uint32_t hash;
uint64_t psnr_sse_total;
uint64_t psnr_samples_total;
double psnr_totals[4];
int psnr_count;
int counts[64];
vpx_codec_ctx_t encoder;
unsigned int frames_out;
uint64_t cx_time;
size_t nbytes;
stats_io_t stats;
};
static void parse_global_config(struct global_config *global, char **argv)
{
char **argi, **argj;
......@@ -1507,7 +1543,6 @@ static void parse_global_config(struct global_config *global, char **argv)
global->codec = codecs;
global->passes = 1;
global->use_i420 = 1;
global->write_webm = 1;
for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step)
{
......@@ -1543,8 +1578,6 @@ static void parse_global_config(struct global_config *global, char **argv)
die("Error: Invalid pass selected (%d)\n",
global->pass);
}
else if (arg_match(&arg, &fpf_name, argi))
global->stats_fn = arg.val;
else if (arg_match(&arg, &usage, argi))
global->usage = arg_parse_uint(&arg);
else if (arg_match(&arg, &deadline, argi))
......@@ -1570,10 +1603,6 @@ static void parse_global_config(struct global_config *global, char **argv)
global->framerate = arg_parse_rational(&arg);
global->have_framerate = 1;
}
else if (arg_match(&arg, &use_ivf, argi))
global->write_webm = 0;
else if (arg_match(&arg, &outputfile, argi))
global->out_fn = arg.val;
else if (arg_match(&arg, &debugmode, argi))
global->debug = 1;
else if (arg_match(&arg, &q_hist_n, argi))
......@@ -1586,9 +1615,6 @@ static void parse_global_config(struct global_config *global, char **argv)
/* Validate global config */
/* Ensure that --passes and --pass are consistent. If --pass is set and
* --passes=2, ensure --fpf was set.
*/
if (global->pass)
{
/* DWIM: Assume the user meant passes=2 if pass=2 is specified */
......@@ -1598,10 +1624,6 @@ static void parse_global_config(struct global_config *global, char **argv)
global->pass, global->pass);
global->passes = global->pass;
}
if (global->passes == 2 && !global->stats_fn)
die("Must specify --fpf when --pass=%d and --passes=2\n",
global->pass);
}
}
......@@ -1667,199 +1689,599 @@ static void close_input_file(struct input_state *input)
y4m_input_close(&input->y4m);
}
int main(int argc, const char **argv_)
static struct stream_state *new_stream(struct global_config *global,
struct stream_state *prev)
{
vpx_codec_ctx_t encoder;
int i;
FILE *outfile;
vpx_codec_enc_cfg_t cfg;
vpx_codec_err_t res;
int pass;
stats_io_t stats;
vpx_image_t raw;
int frame_avail, got_data;
struct stream_state *stream;
struct input_state input = {0};
struct global_config global;
struct arg arg;
char **argv, **argi, **argj;
int arg_ctrls[ARG_CTRL_CNT_MAX][2], arg_ctrl_cnt = 0;
static const arg_def_t **ctrl_args = no_args;
static const int *ctrl_args_map = NULL;
unsigned long cx_time = 0;
stream = calloc(1, sizeof(*stream));
if(!stream)
fatal("Failed to allocate new stream.");
if(prev)
{
memcpy(stream, prev, sizeof(*stream));
stream->index++;
prev->next = stream;
}
else
{
vpx_codec_err_t res;
EbmlGlobal ebml = {0};
uint32_t hash = 0;
uint64_t psnr_sse_total = 0;
uint64_t psnr_samples_total = 0;
double psnr_totals[4] = {0, 0, 0, 0};
int psnr_count = 0;
stereo_format_t stereo_fmt = STEREO_FORMAT_MONO;
int counts[64]={0};
struct rate_hist rate_hist={0};
/* Populate encoder configuration */
res = vpx_codec_enc_config_default(global->codec->iface,
&stream->config.cfg,
global->usage);
if (res)
fatal("Failed to get config: %s\n", vpx_codec_err_to_string(res));
exec_name = argv_[0];
ebml.last_pts_ms = -1;
/* Change the default timebase to a high enough value so that the
* encoder will always create strictly increasing timestamps.
*/
stream->config.cfg.g_timebase.den = 1000;
if (argc < 3)
usage_exit();
/* Never use the library's default resolution, require it be parsed
* from the file or set on the command line.
*/
stream->config.cfg.g_w = 0;
stream->config.cfg.g_h = 0;
/* First parse the global configuration values, because we want to apply
* other parameters on top of the default configuration provided by the
* codec.
*/
argv = argv_dup(argc - 1, argv_ + 1);
parse_global_config(&global, argv);
/* Initialize remaining stream parameters */
stream->config.stereo_fmt = STEREO_FORMAT_MONO;
stream->config.write_webm = 1;
stream->ebml.last_pts_ms = -1;
}
/* Populate encoder configuration */
res = vpx_codec_enc_config_default(global.codec->iface, &cfg,
global.usage);
/* Output files must be specified for each stream */
stream->config.out_fn = NULL;
if (res)
fatal("Failed to get config: %s", vpx_codec_err_to_string(res));
stream->next = NULL;
return stream;
}
/* Change the default timebase to a high enough value so that the encoder
* will always create strictly increasing timestamps.
*/
cfg.g_timebase.den = 1000;
/* Never use the library's default resolution, require it be parsed
* from the file or set on the command line.
*/
cfg.g_w = 0;
cfg.g_h = 0;
static int parse_stream_params(struct global_config *global,
struct stream_state *stream,
char **argv)
{
char **argi, **argj;
struct arg arg;
static const arg_def_t **ctrl_args = no_args;
static const int *ctrl_args_map = NULL;
struct stream_config *config = &stream->config;
int eos_mark_found = 0;
/* Setup default input stream settings */
input.framerate.num = 30;
input.framerate.den = 1;
input.use_i420 = 1;
/* Handle codec specific options */
if (global->codec->iface == &vpx_codec_vp8_cx_algo)
{
ctrl_args = vp8_args;
ctrl_args_map = vp8_arg_ctrl_map;
}
/* Now parse the remainder of the parameters. */
for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step)
{
arg.argv_step = 1;
/* Once we've found an end-of-stream marker (--) we want to continue
* shifting arguments but not consuming them.
*/
if (eos_mark_found)
{
argj++;
continue;
}
else if (!strcmp(*argj, "--"))
{
eos_mark_found = 1;
continue;
}
if (0);
else if (arg_match(&arg, &outputfile, argi))
config->out_fn = arg.val;
else if (arg_match(&arg, &fpf_name, argi))
config->stats_fn = arg.val;
else if (arg_match(&arg, &use_ivf, argi))
config->write_webm = 0;
else if (arg_match(&arg, &threads, argi))
cfg.g_threads = arg_parse_uint(&arg);
config->cfg.g_threads = arg_parse_uint(&arg);
else if (arg_match(&arg, &profile, argi))
cfg.g_profile = arg_parse_uint(&arg);
config->cfg.g_profile = arg_parse_uint(&arg);
else if (arg_match(&arg, &width, argi))
cfg.g_w = arg_parse_uint(&arg);
config->cfg.g_w = arg_parse_uint(&arg);
else if (arg_match(&arg, &height, argi))
cfg.g_h = arg_parse_uint(&arg);
config->cfg.g_h = arg_parse_uint(&arg);
else if (arg_match(&arg, &stereo_mode, argi))
stereo_fmt = arg_parse_enum_or_int(&arg);
config->stereo_fmt = arg_parse_enum_or_int(&arg);
else if (arg_match(&arg, &timebase, argi))
cfg.g_timebase = arg_parse_rational(&arg);
config->cfg.g_timebase = arg_parse_rational(&arg);
else if (arg_match(&arg, &error_resilient, argi))
cfg.g_error_resilient = arg_parse_uint(&arg);
config->cfg.g_error_resilient = arg_parse_uint(&arg);
else if (arg_match(&arg, &lag_in_frames, argi))
cfg.g_lag_in_frames = arg_parse_uint(&arg);
config->cfg.g_lag_in_frames = arg_parse_uint(&arg);
else if (arg_match(&arg, &dropframe_thresh, argi))
cfg.rc_dropframe_thresh = arg_parse_uint(&arg);
config->cfg.rc_dropframe_thresh = arg_parse_uint(&arg);
else if (arg_match(&arg, &resize_allowed, argi))
cfg.rc_resize_allowed = arg_parse_uint(&arg);
config->cfg.rc_resize_allowed = arg_parse_uint(&arg);
else if (arg_match(&arg, &resize_up_thresh, argi))
cfg.rc_resize_up_thresh = arg_parse_uint(&arg);
config->cfg.rc_resize_up_thresh = arg_parse_uint(&arg);
else if (arg_match(&arg, &resize_down_thresh, argi))
cfg.rc_resize_down_thresh = arg_parse_uint(&arg);
config->cfg.rc_resize_down_thresh = arg_parse_uint(&arg);
else if (arg_match(&arg, &end_usage, argi))
cfg.rc_end_usage = arg_parse_enum_or_int(&arg);
config->cfg.rc_end_usage = arg_parse_enum_or_int(&arg);
else if (arg_match(&arg, &target_bitrate, argi))
cfg.rc_target_bitrate = arg_parse_uint(&arg);
config->cfg.rc_target_bitrate = arg_parse_uint(&arg);
else if (arg_match(&arg, &min_quantizer, argi))
cfg.rc_min_quantizer = arg_parse_uint(&arg);
config->cfg.rc_min_quantizer = arg_parse_uint(&arg);
else if (arg_match(&arg, &max_quantizer, argi))
cfg.rc_max_quantizer = arg_parse_uint(&arg);
config->cfg.rc_max_quantizer = arg_parse_uint(&arg);
else if (arg_match(&arg, &undershoot_pct, argi))
cfg.rc_undershoot_pct = arg_parse_uint(&arg);
config->cfg.rc_undershoot_pct = arg_parse_uint(&arg);
else if (arg_match(&arg, &overshoot_pct, argi))
cfg.rc_overshoot_pct = arg_parse_uint(&arg);
config->cfg.rc_overshoot_pct = arg_parse_uint(&arg);
else if (arg_match(&arg, &buf_sz, argi))
cfg.rc_buf_sz = arg_parse_uint(&arg);
config->cfg.rc_buf_sz = arg_parse_uint(&arg);
else if (arg_match(&arg, &buf_initial_sz, argi))
cfg.rc_buf_initial_sz = arg_parse_uint(&arg);
config->cfg.rc_buf_initial_sz = arg_parse_uint(&arg);
else if (arg_match(&arg, &buf_optimal_sz, argi))
cfg.rc_buf_optimal_sz = arg_parse_uint(&arg);
config->cfg.rc_buf_optimal_sz = arg_parse_uint(&arg);
else if (arg_match(&arg, &bias_pct, argi))
{
cfg.rc_2pass_vbr_bias_pct = arg_parse_uint(&arg);
config->cfg.rc_2pass_vbr_bias_pct = arg_parse_uint(&arg);
if (global.passes < 2)
if (global->passes < 2)
warn("option %s ignored in one-pass mode.\n", arg.name);
}
else if (arg_match(&arg, &minsection_pct, argi))
{
cfg.rc_2pass_vbr_minsection_pct = arg_parse_uint(&arg);
config->cfg.rc_2pass_vbr_minsection_pct = arg_parse_uint(&arg);
if (global.passes < 2)
if (global->passes < 2)
warn("option %s ignored in one-pass mode.\n", arg.name);
}
else if (arg_match(&arg, &maxsection_pct, argi))
{
cfg.rc_2pass_vbr_maxsection_pct = arg_parse_uint(&arg);
config->cfg.rc_2pass_vbr_maxsection_pct = arg_parse_uint(&arg);
if (global.passes < 2)
if (global->passes < 2)
warn("option %s ignored in one-pass mode.\n", arg.name);
}
else if (arg_match(&arg, &kf_min_dist, argi))
cfg.kf_min_dist = arg_parse_uint(&arg);
config->cfg.kf_min_dist = arg_parse_uint(&arg);
else if (arg_match(&arg, &kf_max_dist, argi))
cfg.kf_max_dist = arg_parse_uint(&arg);
config->cfg.kf_max_dist = arg_parse_uint(&arg);
else if (arg_match(&arg, &kf_disabled, argi))
cfg.kf_mode = VPX_KF_DISABLED;
config->cfg.kf_mode = VPX_KF_DISABLED;
else
argj++;
{
int i, match = 0;
for (i = 0; ctrl_args[i]; i++)
{
if (arg_match(&arg, ctrl_args[i], argi))
{
int j;
match = 1;
/* Point either to the next free element or the first
* instance of this control.
*/
for(j=0; j<config->arg_ctrl_cnt; j++)
if(config->arg_ctrls[j][0] == ctrl_args_map[i])
break;
/* Update/insert */
assert(j < ARG_CTRL_CNT_MAX);
if (j < ARG_CTRL_CNT_MAX)
{
config->arg_ctrls[j][0] = ctrl_args_map[i];
config->arg_ctrls[j][1] = arg_parse_enum_or_int(&arg);
if(j == config->arg_ctrl_cnt)
config->arg_ctrl_cnt++;
}
}
}
if (!match)
argj++;
}
}
/* Handle codec specific options */
#if CONFIG_VP8_ENCODER
return eos_mark_found;
}
if (global.codec->iface == &vpx_codec_vp8_cx_algo)
#define FOREACH_STREAM(func)\
do\
{\
struct stream_state *stream;\
\
for(stream = streams; stream; stream = stream->next)\
func;\
}while(0)
static void validate_stream_config(struct stream_state *stream)
{
struct stream_state *streami;
if(!stream->config.cfg.g_w || !stream->config.cfg.g_h)
fatal("Stream %d: Specify stream dimensions with --width (-w) "
" and --height (-h)", stream->index);
for(streami = stream; streami; streami = streami->next)
{
ctrl_args = vp8_args;
ctrl_args_map = vp8_arg_ctrl_map;
/* All streams require output files */
if(!streami->config.out_fn)
fatal("Stream %d: Output file is required (specify with -o)",
streami->index);
/* Check for two streams outputting to the same file */
if(streami != stream)
{
const char *a = stream->config.out_fn;
const char *b = streami->config.out_fn;
if(!strcmp(a,b) && strcmp(a, "/dev/null") && strcmp(a, ":nul"))
fatal("Stream %d: duplicate output file (from stream %d)",
streami->index, stream->index);
}
/* Check for two streams sharing a stats file. */
if(streami != stream)
{
const char *a = stream->config.stats_fn;
const char *b = streami->config.stats_fn;
if(a && b && !strcmp(a,b))
fatal("Stream %d: duplicate stats file (from stream %d)",
streami->index, stream->index);
}
}
}
#endif
for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step)
static void set_stream_dimensions(struct stream_state *stream,
unsigned int w,
unsigned int h)
{
if ((stream->config.cfg.g_w && stream->config.cfg.g_w != w)
||(stream->config.cfg.g_h && stream->config.cfg.g_h != h))
fatal("Stream %d: Resizing not yet supported", stream->index);
stream->config.cfg.g_w = w;
stream->config.cfg.g_h = h;
}
static void show_stream_config(struct stream_state *stream,
struct global_config *global,
struct input_state *input)
{
#define SHOW(field) \
fprintf(stderr, " %-28s = %d\n", #field, stream->config.cfg.field)
if(stream->index == 0)
{
int match = 0;
fprintf(stderr, "Codec: %s\n",
vpx_codec_iface_name(global->codec->iface));
fprintf(stderr, "Source file: %s Format: %s\n", input->fn,
input->use_i420 ? "I420" : "YV12");
}
if(stream->next || stream->index)
fprintf(stderr, "\nStream Index: %d\n", stream->index);
fprintf(stderr, "Destination file: %s\n", stream->config.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);
}
static void open_output_file(struct stream_state *stream,
struct global_config *global)
{
const char *fn = stream->config.out_fn;
stream->file = strcmp(fn, "-") ? fopen(fn, "wb") : set_binary_mode(stdout);
if (!stream->file)
fatal("Failed to open output file");
if(stream->config.write_webm && fseek(stream->file, 0, SEEK_CUR))
fatal("WebM output to pipes not supported.");
if(stream->config.write_webm)
{
stream->ebml.stream = stream->file;
write_webm_file_header(&stream->ebml, &stream->config.cfg,
&global->framerate,
stream->config.stereo_fmt);
}
else
write_ivf_file_header(stream->file, &stream->config.cfg,
global->codec->fourcc, 0);
}
static void close_output_file(struct stream_state *stream,
unsigned int fourcc)
{
if(stream->config.write_webm)
{
write_webm_file_footer(&stream->ebml, stream->hash);
free(stream->ebml.cue_list);
stream->ebml.cue_list = NULL;
}
else
{
if (!fseek(stream->file, 0, SEEK_SET))
write_ivf_file_header(stream->file, &stream->config.cfg,
fourcc,
stream->frames_out);
}
fclose(stream->file);
}
static void setup_pass(struct stream_state *stream,
struct global_config *global,
int pass)
{
if (stream->config.stats_fn)
{
if (!stats_open_file(&stream->stats, stream->config.stats_fn,
pass))
fatal("Failed to open statistics store");
}
else
{
if (!stats_open_mem(&stream->stats, pass))
fatal("Failed to open statistics store");
}
stream->config.cfg.g_pass = global->passes == 2
? pass ? VPX_RC_LAST_PASS : VPX_RC_FIRST_PASS
: VPX_RC_ONE_PASS;
if (pass)
stream->config.cfg.rc_twopass_stats_in = stats_get(&stream->stats);
}
static void initialize_encoder(struct stream_state *stream,
struct global_config *global)
{
int i;
/* Construct Encoder Context */
vpx_codec_enc_init(&stream->encoder, global->codec->iface,
&stream->config.cfg,
global->show_psnr ? VPX_CODEC_USE_PSNR : 0);
ctx_exit_on_error(&stream->encoder, "Failed to initialize encoder");
/* Note that we bypass the vpx_codec_control wrapper macro because
* we're being clever to store the control IDs in an array. Real
* applications will want to make use of the enumerations directly
*/
for (i = 0; i < stream->config.arg_ctrl_cnt; i++)
{
int ctrl = stream->config.arg_ctrls[i][0];
int value = stream->config.arg_ctrls[i][1];
if (vpx_codec_control_(&stream->encoder, ctrl, value))
fprintf(stderr, "Error: Tried to set control %d = %d\n",
ctrl, value);
ctx_exit_on_error(&stream->encoder, "Failed to control codec");
}
}
arg.argv_step = 1;
for (i = 0; ctrl_args[i]; i++)
static void encode_frame(struct stream_state *stream,
struct global_config *global,
struct vpx_image *img,
unsigned int frames_in)
{
vpx_codec_pts_t frame_start, next_frame_start;
struct vpx_codec_enc_cfg *cfg = &stream->config.cfg;
struct vpx_usec_timer timer;
frame_start = (cfg->g_timebase.den * (int64_t)(frames_in - 1)
* global->framerate.den)
/ cfg->g_timebase.num / global->framerate.num;
next_frame_start = (cfg->g_timebase.den * (int64_t)(frames_in)
* global->framerate.den)
/ cfg->g_timebase.num / global->framerate.num;
vpx_usec_timer_start(&timer);
vpx_codec_encode(&stream->encoder, img, frame_start,
next_frame_start - frame_start,
0, global->deadline);
vpx_usec_timer_mark(&timer);
stream->cx_time += vpx_usec_timer_elapsed(&timer);
ctx_exit_on_error(&stream->encoder, "Stream %d: Failed to encode frame",
stream->index);
}
static void update_quantizer_histogram(struct stream_state *stream)
{
if(stream->config.cfg.g_pass != VPX_RC_FIRST_PASS)
{
int q;
vpx_codec_control(&stream->encoder, VP8E_GET_LAST_QUANTIZER_64, &q);
ctx_exit_on_error(&stream->encoder, "Failed to read quantizer");
stream->counts[q]++;
}
}
static void get_cx_data(struct stream_state *stream,
struct global_config *global,
int *got_data)
{
const vpx_codec_cx_pkt_t *pkt;
const struct vpx_codec_enc_cfg *cfg = &stream->config.cfg;
vpx_codec_iter_t iter = NULL;
while ((pkt = vpx_codec_get_cx_data(&stream->encoder, &iter)))
{
*got_data = 1;
switch (pkt->kind)
{
if (arg_match(&arg, ctrl_args[i], argi))
case VPX_CODEC_CX_FRAME_PKT:
stream->frames_out++;
fprintf(stderr, " %6luF",
(unsigned long)pkt->data.frame.sz);
update_rate_histogram(&stream->rate_hist, cfg, pkt);
if(stream->config.write_webm)
{
/* Update the hash */
if(!stream->ebml.debug)
stream->hash = murmur(pkt->data.frame.buf,
pkt->data.frame.sz, stream->hash);
write_webm_block(&stream->ebml, cfg, pkt);
}
else
{
int j;
match = 1;
/* Point either to the next free element or the first
* instance of this control.
*/
for(j=0; j<arg_ctrl_cnt; j++)
if(arg_ctrls[j][0] == ctrl_args_map[i])
break;
/* Update/insert */
assert(j < ARG_CTRL_CNT_MAX);
if (j < ARG_CTRL_CNT_MAX)
write_ivf_frame_header(stream->file, pkt);
if(fwrite(pkt->data.frame.buf, 1,
pkt->data.frame.sz, stream->file));
}