1 /*
2 * This file is part of mpv.
3 *
4 * mpv is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * mpv is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <math.h>
22 #include <stdbool.h>
23 #include <sys/stat.h>
24
25 #include <libswscale/swscale.h>
26
27 #include "config.h"
28 #include "misc/bstr.h"
29 #include "osdep/io.h"
30 #include "options/m_config.h"
31 #include "options/path.h"
32 #include "mpv_talloc.h"
33 #include "common/common.h"
34 #include "common/msg.h"
35 #include "video/out/vo.h"
36 #include "video/csputils.h"
37 #include "video/mp_image.h"
38 #include "video/fmt-conversion.h"
39 #include "video/image_writer.h"
40 #include "video/sws_utils.h"
41 #include "sub/osd.h"
42 #include "options/m_option.h"
43
44 static const struct m_sub_options image_writer_conf = {
45 .opts = image_writer_opts,
46 .size = sizeof(struct image_writer_opts),
47 .defaults = &image_writer_opts_defaults,
48 };
49
50 struct vo_image_opts {
51 struct image_writer_opts *opts;
52 char *outdir;
53 };
54
55 #define OPT_BASE_STRUCT struct vo_image_opts
56
57 static const struct m_sub_options vo_image_conf = {
58 .opts = (const struct m_option[]) {
59 {"vo-image", OPT_SUBSTRUCT(opts, image_writer_conf)},
60 {"vo-image-outdir", OPT_STRING(outdir), .flags = M_OPT_FILE},
61 {0},
62 },
63 .size = sizeof(struct vo_image_opts),
64 };
65
66 struct priv {
67 struct vo_image_opts *opts;
68
69 struct mp_image *current;
70 int frame;
71 };
72
checked_mkdir(struct vo * vo,const char * buf)73 static bool checked_mkdir(struct vo *vo, const char *buf)
74 {
75 MP_INFO(vo, "Creating output directory '%s'...\n", buf);
76 if (mkdir(buf, 0755) < 0) {
77 char *errstr = mp_strerror(errno);
78 if (errno == EEXIST) {
79 struct stat stat_p;
80 if (stat(buf, &stat_p ) == 0 && S_ISDIR(stat_p.st_mode))
81 return true;
82 }
83 MP_ERR(vo, "Error creating output directory: %s\n", errstr);
84 return false;
85 }
86 return true;
87 }
88
reconfig(struct vo * vo,struct mp_image_params * params)89 static int reconfig(struct vo *vo, struct mp_image_params *params)
90 {
91 struct priv *p = vo->priv;
92 mp_image_unrefp(&p->current);
93
94 return 0;
95 }
96
draw_image(struct vo * vo,mp_image_t * mpi)97 static void draw_image(struct vo *vo, mp_image_t *mpi)
98 {
99 struct priv *p = vo->priv;
100
101 p->current = mpi;
102
103 struct mp_osd_res dim = osd_res_from_image_params(vo->params);
104 osd_draw_on_image(vo->osd, dim, mpi->pts, OSD_DRAW_SUB_ONLY, p->current);
105 }
106
flip_page(struct vo * vo)107 static void flip_page(struct vo *vo)
108 {
109 struct priv *p = vo->priv;
110 if (!p->current)
111 return;
112
113 (p->frame)++;
114
115 void *t = talloc_new(NULL);
116 char *filename = talloc_asprintf(t, "%08d.%s", p->frame,
117 image_writer_file_ext(p->opts->opts));
118
119 if (p->opts->outdir && strlen(p->opts->outdir))
120 filename = mp_path_join(t, p->opts->outdir, filename);
121
122 MP_INFO(vo, "Saving %s\n", filename);
123 write_image(p->current, p->opts->opts, filename, vo->global, vo->log);
124
125 talloc_free(t);
126 mp_image_unrefp(&p->current);
127 }
128
query_format(struct vo * vo,int fmt)129 static int query_format(struct vo *vo, int fmt)
130 {
131 if (mp_sws_supported_format(fmt))
132 return 1;
133 return 0;
134 }
135
uninit(struct vo * vo)136 static void uninit(struct vo *vo)
137 {
138 struct priv *p = vo->priv;
139
140 mp_image_unrefp(&p->current);
141 }
142
preinit(struct vo * vo)143 static int preinit(struct vo *vo)
144 {
145 struct priv *p = vo->priv;
146 p->opts = mp_get_config_group(vo, vo->global, &vo_image_conf);
147 if (p->opts->outdir && !checked_mkdir(vo, p->opts->outdir))
148 return -1;
149 return 0;
150 }
151
control(struct vo * vo,uint32_t request,void * data)152 static int control(struct vo *vo, uint32_t request, void *data)
153 {
154 return VO_NOTIMPL;
155 }
156
157 const struct vo_driver video_out_image =
158 {
159 .description = "Write video frames to image files",
160 .name = "image",
161 .untimed = true,
162 .priv_size = sizeof(struct priv),
163 .preinit = preinit,
164 .query_format = query_format,
165 .reconfig = reconfig,
166 .control = control,
167 .draw_image = draw_image,
168 .flip_page = flip_page,
169 .uninit = uninit,
170 .global_opts = &vo_image_conf,
171 };
172