2012-08-06 17:52:17 +02:00
|
|
|
/*
|
|
|
|
* This file is part of mplayer.
|
|
|
|
*
|
|
|
|
* mplayer 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.
|
|
|
|
*
|
|
|
|
* mplayer 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 mplayer. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <stdbool.h>
|
2012-08-06 17:52:47 +02:00
|
|
|
#include <sys/stat.h>
|
2012-08-06 17:52:17 +02:00
|
|
|
|
|
|
|
#include <libswscale/swscale.h>
|
|
|
|
|
|
|
|
#include "config.h"
|
2014-08-29 12:09:04 +02:00
|
|
|
#include "misc/bstr.h"
|
2012-08-06 17:52:47 +02:00
|
|
|
#include "osdep/io.h"
|
2013-12-17 02:02:25 +01:00
|
|
|
#include "options/path.h"
|
2012-08-06 17:52:17 +02:00
|
|
|
#include "talloc.h"
|
2013-12-17 02:39:45 +01:00
|
|
|
#include "common/msg.h"
|
2012-11-09 01:06:43 +01:00
|
|
|
#include "video/out/vo.h"
|
|
|
|
#include "video/csputils.h"
|
|
|
|
#include "video/vfcap.h"
|
|
|
|
#include "video/mp_image.h"
|
|
|
|
#include "video/fmt-conversion.h"
|
|
|
|
#include "video/image_writer.h"
|
2012-12-22 19:11:53 +01:00
|
|
|
#include "video/sws_utils.h"
|
2013-11-24 12:58:06 +01:00
|
|
|
#include "sub/osd.h"
|
2013-12-17 02:02:25 +01:00
|
|
|
#include "options/m_option.h"
|
2012-08-06 17:52:17 +02:00
|
|
|
|
|
|
|
struct priv {
|
|
|
|
struct image_writer_opts *opts;
|
2012-08-06 17:52:47 +02:00
|
|
|
char *outdir;
|
2012-08-06 17:52:17 +02:00
|
|
|
|
2012-12-22 19:11:53 +01:00
|
|
|
struct mp_image *current;
|
2012-08-06 17:52:17 +02:00
|
|
|
int frame;
|
|
|
|
};
|
|
|
|
|
2013-08-23 23:30:09 +02:00
|
|
|
static bool checked_mkdir(struct vo *vo, const char *buf)
|
2012-08-06 17:52:47 +02:00
|
|
|
{
|
2013-08-23 23:30:09 +02:00
|
|
|
MP_INFO(vo, "Creating output directory '%s'...\n", buf);
|
2012-08-06 17:52:47 +02:00
|
|
|
if (mkdir(buf, 0755) < 0) {
|
|
|
|
char *errstr = strerror(errno);
|
|
|
|
if (errno == EEXIST) {
|
|
|
|
struct stat stat_p;
|
2014-10-17 21:46:08 +02:00
|
|
|
if (stat(buf, &stat_p ) == 0 && S_ISDIR(stat_p.st_mode))
|
2012-08-06 17:52:47 +02:00
|
|
|
return true;
|
|
|
|
}
|
2013-08-23 23:30:09 +02:00
|
|
|
MP_ERR(vo, "Error creating output directory: %s\n", errstr);
|
2012-08-06 17:52:47 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-08-24 17:01:42 +02:00
|
|
|
static int reconfig(struct vo *vo, struct mp_image_params *params, int flags)
|
2012-08-06 17:52:17 +02:00
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
2012-12-22 19:11:53 +01:00
|
|
|
mp_image_unrefp(&p->current);
|
|
|
|
|
2012-08-06 17:52:17 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-22 19:11:53 +01:00
|
|
|
static void draw_image(struct vo *vo, mp_image_t *mpi)
|
2012-08-06 17:52:17 +02:00
|
|
|
{
|
2012-12-22 19:11:53 +01:00
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
2014-06-17 23:05:50 +02:00
|
|
|
p->current = mpi;
|
2012-12-22 19:11:53 +01:00
|
|
|
|
2014-01-21 23:43:54 +01:00
|
|
|
struct mp_osd_res dim = osd_res_from_image_params(vo->params);
|
2014-06-15 20:46:57 +02:00
|
|
|
osd_draw_on_image(vo->osd, dim, mpi->pts, OSD_DRAW_SUB_ONLY, p->current);
|
2012-08-06 17:52:17 +02:00
|
|
|
}
|
|
|
|
|
2012-12-22 19:11:53 +01:00
|
|
|
static void flip_page(struct vo *vo)
|
2012-08-06 17:52:17 +02:00
|
|
|
{
|
|
|
|
struct priv *p = vo->priv;
|
video: introduce failure path for image allocations
Until now, failure to allocate image data resulted in a crash (i.e.
abort() was called). This was intentional, because it's pretty silly to
degrade playback, and in almost all situations, the OOM will probably
kill you anyway. (And then there's the standard Linux overcommit
behavior, which also will kill you at some point.)
But I changed my opinion, so here we go. This change does not affect
_all_ memory allocations, just image data. Now in most failure cases,
the output will just be skipped. For video filters, this coincidentally
means that failure is treated as EOF (because the playback core assumes
EOF if nothing comes out of the video filter chain). In other
situations, output might be in some way degraded, like skipping frames,
not scaling OSD, and such.
Functions whose return values changed semantics:
mp_image_alloc
mp_image_new_copy
mp_image_new_ref
mp_image_make_writeable
mp_image_setrefp
mp_image_to_av_frame_and_unref
mp_image_from_av_frame
mp_image_new_external_ref
mp_image_new_custom_ref
mp_image_pool_make_writeable
mp_image_pool_get
mp_image_pool_new_copy
mp_vdpau_mixed_frame_create
vf_alloc_out_image
vf_make_out_image_writeable
glGetWindowScreenshot
2014-06-17 22:43:43 +02:00
|
|
|
if (!p->current)
|
|
|
|
return;
|
2012-08-06 17:52:17 +02:00
|
|
|
|
2013-05-18 13:19:22 +02:00
|
|
|
(p->frame)++;
|
|
|
|
|
2012-08-06 17:52:47 +02:00
|
|
|
void *t = talloc_new(NULL);
|
|
|
|
char *filename = talloc_asprintf(t, "%08d.%s", p->frame,
|
|
|
|
image_writer_file_ext(p->opts));
|
|
|
|
|
|
|
|
if (p->outdir && strlen(p->outdir))
|
|
|
|
filename = mp_path_join(t, bstr0(p->outdir), bstr0(filename));
|
|
|
|
|
2013-08-23 23:30:09 +02:00
|
|
|
MP_INFO(vo, "Saving %s\n", filename);
|
2013-12-21 17:59:38 +01:00
|
|
|
write_image(p->current, p->opts, filename, vo->log);
|
2012-08-06 17:52:17 +02:00
|
|
|
|
2012-08-06 17:52:47 +02:00
|
|
|
talloc_free(t);
|
2012-12-22 19:11:53 +01:00
|
|
|
mp_image_unrefp(&p->current);
|
2012-08-06 17:52:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int query_format(struct vo *vo, uint32_t fmt)
|
|
|
|
{
|
2012-12-22 19:11:53 +01:00
|
|
|
if (mp_sws_supported_format(fmt))
|
2013-03-01 11:16:01 +01:00
|
|
|
return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW;
|
2012-08-06 17:52:17 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void uninit(struct vo *vo)
|
|
|
|
{
|
2012-12-22 19:11:53 +01:00
|
|
|
struct priv *p = vo->priv;
|
|
|
|
|
|
|
|
mp_image_unrefp(&p->current);
|
2012-08-06 17:52:17 +02:00
|
|
|
}
|
|
|
|
|
2013-07-22 22:52:42 +02:00
|
|
|
static int preinit(struct vo *vo)
|
2012-08-06 17:52:17 +02:00
|
|
|
{
|
2014-05-06 00:21:15 +02:00
|
|
|
struct priv *p = vo->priv;
|
|
|
|
if (p->outdir && !checked_mkdir(vo, p->outdir))
|
|
|
|
return -1;
|
2012-08-06 17:52:17 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int control(struct vo *vo, uint32_t request, void *data)
|
|
|
|
{
|
|
|
|
return VO_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define OPT_BASE_STRUCT struct priv
|
|
|
|
|
|
|
|
const struct vo_driver video_out_image =
|
|
|
|
{
|
2013-10-23 19:06:14 +02:00
|
|
|
.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 23:02:08 +02:00
|
|
|
.untimed = true,
|
2012-08-06 17:52:17 +02:00
|
|
|
.priv_size = sizeof(struct priv),
|
|
|
|
.options = (const struct m_option[]) {
|
options: drop --opt:subopt option names
For all suboptions, "flat" options were available by separating the
parent option and the sub option with ":", e.g. "--rawvideo:w=123". Drop
this syntax and use "-" as separator. This means even suboptions are
available as normal options now, e.g. "--rawvideo-w=123". The old syntax
doesn't work anymore.
Note that this is completely separate from actual suboptions. For
example, "-rawvideo w=123:h=123" still works. (Not that this syntax is
worth supporting, but it's needed anyway, for for other things like vf
and vo suboptions.)
As a consequence of this change, we also have to add new "no-" prefixed
options for flag suboptions, so that "--no-input-default-bindings"
works. ("--input-no-default-bindings" also works as a consequence of
allowing "-input no-default-bindings" - they are handled by the same
underlying option.)
For --input, always use the full syntax in the manpage. There exist
suboptions other than --input (like --tv, --rawvideo, etc.), but since
they might be handled differently in the future, don't touch these yet.
M_OPT_PREFIXED becomes the default, so remove it. As a minor unrelated
cleanup, get rid of M_OPT_MERGE too and use the OPT_SUBSTRUCT() macro in
some places.
Unrelated: remove the duplicated --tv:buffersize option, fix a typo in
changes.rst.
2013-02-21 22:10:21 +01:00
|
|
|
OPT_SUBSTRUCT("", opts, image_writer_conf, 0),
|
2012-08-06 17:52:47 +02:00
|
|
|
OPT_STRING("outdir", outdir, 0),
|
2012-08-06 17:52:17 +02:00
|
|
|
{0},
|
|
|
|
},
|
|
|
|
.preinit = preinit,
|
2012-11-04 16:24:18 +01:00
|
|
|
.query_format = query_format,
|
2013-08-24 17:01:42 +02:00
|
|
|
.reconfig = reconfig,
|
2012-08-06 17:52:17 +02:00
|
|
|
.control = control,
|
2012-11-04 15:56:04 +01:00
|
|
|
.draw_image = draw_image,
|
2012-08-06 17:52:17 +02:00
|
|
|
.flip_page = flip_page,
|
|
|
|
.uninit = uninit,
|
|
|
|
};
|