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