mpv/video/out/vo_image.c

184 lines
4.9 KiB
C
Raw Normal View History

/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <libswscale/swscale.h>
#include "config.h"
#include "misc/bstr.h"
#include "osdep/io.h"
#include "options/m_config.h"
#include "options/path.h"
#include "mpv_talloc.h"
#include "common/common.h"
#include "common/msg.h"
#include "video/out/vo.h"
#include "video/csputils.h"
#include "video/mp_image.h"
#include "video/fmt-conversion.h"
#include "video/image_writer.h"
#include "video/sws_utils.h"
#include "sub/osd.h"
#include "options/m_option.h"
static const struct m_sub_options image_writer_conf = {
.opts = image_writer_opts,
.size = sizeof(struct image_writer_opts),
.defaults = &image_writer_opts_defaults,
};
struct vo_image_opts {
struct image_writer_opts *opts;
char *outdir;
};
#define OPT_BASE_STRUCT struct vo_image_opts
static const struct m_sub_options vo_image_conf = {
.opts = (const struct m_option[]) {
OPT_SUBSTRUCT("vo-image", opts, image_writer_conf, 0),
OPT_STRING("vo-image-outdir", outdir, 0),
{0},
},
.size = sizeof(struct vo_image_opts),
};
struct priv {
struct vo_image_opts *opts;
struct mp_image *current;
int frame;
};
static bool checked_mkdir(struct vo *vo, const char *buf)
{
MP_INFO(vo, "Creating output directory '%s'...\n", buf);
if (mkdir(buf, 0755) < 0) {
char *errstr = mp_strerror(errno);
if (errno == EEXIST) {
struct stat stat_p;
if (stat(buf, &stat_p ) == 0 && S_ISDIR(stat_p.st_mode))
return true;
}
MP_ERR(vo, "Error creating output directory: %s\n", errstr);
return false;
}
return true;
}
static int reconfig(struct vo *vo, struct mp_image_params *params)
{
struct priv *p = vo->priv;
mp_image_unrefp(&p->current);
return 0;
}
static void draw_image(struct vo *vo, mp_image_t *mpi)
{
struct priv *p = vo->priv;
p->current = mpi;
struct mp_osd_res dim = osd_res_from_image_params(vo->params);
osd_draw_on_image(vo->osd, dim, mpi->pts, OSD_DRAW_SUB_ONLY, p->current);
}
static void flip_page(struct vo *vo)
{
struct priv *p = vo->priv;
if (!p->current)
return;
(p->frame)++;
void *t = talloc_new(NULL);
char *filename = talloc_asprintf(t, "%08d.%s", p->frame,
image_writer_file_ext(p->opts->opts));
if (p->opts->outdir && strlen(p->opts->outdir))
filename = mp_path_join(t, p->opts->outdir, filename);
MP_INFO(vo, "Saving %s\n", filename);
write_image(p->current, p->opts->opts, filename, vo->log);
talloc_free(t);
mp_image_unrefp(&p->current);
}
static int query_format(struct vo *vo, int fmt)
{
if (mp_sws_supported_format(fmt))
return 1;
return 0;
}
static void uninit(struct vo *vo)
{
struct priv *p = vo->priv;
mp_image_unrefp(&p->current);
}
static int preinit(struct vo *vo)
{
struct priv *p = vo->priv;
p->opts = mp_get_config_group(vo, vo->global, &vo_image_conf);
if (p->opts->outdir && !checked_mkdir(vo, p->opts->outdir))
return -1;
return 0;
}
static int control(struct vo *vo, uint32_t request, void *data)
{
return VO_NOTIMPL;
}
const struct vo_driver video_out_image =
{
.description = "Write video frames to image files",
.name = "image",
video: move display and timing to a separate thread The VO is run inside its own thread. It also does most of video timing. The playloop hands the image data and a realtime timestamp to the VO, and the VO does the rest. In particular, this allows the playloop to do other things, instead of blocking for video redraw. But if anything accesses the VO during video timing, it will block. This also fixes vo_sdl.c event handling; but that is only a side-effect, since reimplementing the broken way would require more effort. Also drop --softsleep. In theory, this option helps if the kernel's sleeping mechanism is too inaccurate for video timing. In practice, I haven't ever encountered a situation where it helps, and it just burns CPU cycles. On the other hand it's probably actively harmful, because it prevents the libavcodec decoder threads from doing real work. Side note: Originally, I intended that multiple frames can be queued to the VO. But this is not done, due to problems with OSD and other certain features. OSD in particular is simply designed in a way that it can be neither timed nor copied, so you do have to render it into the video frame before you can draw the next frame. (Subtitles have no such restriction. sd_lavc was even updated to fix this.) It seems the right solution to queuing multiple VO frames is rendering on VO-backed framebuffers, like vo_vdpau.c does. This requires VO driver support, and is out of scope of this commit. As consequence, the VO has a queue size of 1. The existing video queue is just needed to compute frame duration, and will be moved out in the next commit.
2014-08-12 21:02:08 +00:00
.untimed = true,
.priv_size = sizeof(struct priv),
.options = (const struct m_option[]) {
OPT_SUBOPT_LEGACY("jpeg-quality", "vo-image-jpeg-quality"),
OPT_SUBOPT_LEGACY("jpeg-smooth", "vo-image-jpeg-smooth"),
OPT_SUBOPT_LEGACY("jpeg-source-chroma", "vo-image-jpeg-source-chroma"),
OPT_SUBOPT_LEGACY("png-compression", "vo-image-png-compression"),
OPT_SUBOPT_LEGACY("png-filter", "vo-image-png-filter"),
OPT_SUBOPT_LEGACY("format", "vo-image-format"),
OPT_SUBOPT_LEGACY("high-bit-depth", "vo-image-high-bit-depth"),
OPT_SUBOPT_LEGACY("tag-colorspace", "vo-image-tag-colorspace"),
OPT_SUBOPT_LEGACY("outdir", "vo-image-outdir"),
{0},
},
.preinit = preinit,
.query_format = query_format,
2013-08-24 15:01:42 +00:00
.reconfig = reconfig,
.control = control,
.draw_image = draw_image,
.flip_page = flip_page,
.uninit = uninit,
.global_opts = &vo_image_conf,
};