2 * This file is part of mplayer2.
4 * mplayer2 is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * mplayer2 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 General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with mplayer2; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include <libswscale/swscale.h>
25 #include <libavcodec/avcodec.h>
29 #include "screenshot.h"
33 #include "libmpcodecs/img_format.h"
34 #include "libmpcodecs/mp_image.h"
35 #include "libmpcodecs/dec_video.h"
36 #include "libmpcodecs/vf.h"
37 #include "libvo/video_out.h"
39 #include "fmt-conversion.h"
41 //for sws_getContextFromCmdLine and mp_sws_set_colorspace
42 #include "libmpcodecs/vf_scale.h"
43 #include "libvo/csputils.h"
45 typedef struct screenshot_ctx
{
48 int using_vf_screenshot
;
54 static screenshot_ctx
*screenshot_get_ctx(MPContext
*mpctx
)
56 if (!mpctx
->screenshot_ctx
)
57 mpctx
->screenshot_ctx
= talloc_zero(mpctx
, screenshot_ctx
);
58 return mpctx
->screenshot_ctx
;
61 static int write_png(screenshot_ctx
*ctx
, struct mp_image
*image
)
63 char *fname
= ctx
->fname
;
65 void *outbuffer
= NULL
;
68 struct AVCodec
*png_codec
= avcodec_find_encoder(CODEC_ID_PNG
);
69 AVCodecContext
*avctx
= NULL
;
72 avctx
= avcodec_alloc_context3(png_codec
);
76 avctx
->time_base
= AV_TIME_BASE_Q
;
77 avctx
->width
= image
->width
;
78 avctx
->height
= image
->height
;
79 avctx
->pix_fmt
= PIX_FMT_RGB24
;
80 avctx
->compression_level
= 0;
82 if (avcodec_open2(avctx
, png_codec
, NULL
) < 0) {
84 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "Could not open libavcodec PNG encoder"
85 " for saving screenshot\n");
89 size_t outbuffer_size
= image
->width
* image
->height
* 3 * 2;
90 outbuffer
= malloc(outbuffer_size
);
95 avcodec_get_frame_defaults(&pic
);
96 for (int n
= 0; n
< 4; n
++) {
97 pic
.data
[n
] = image
->planes
[n
];
98 pic
.linesize
[n
] = image
->stride
[n
];
100 int size
= avcodec_encode_video(avctx
, outbuffer
, outbuffer_size
, &pic
);
104 fp
= fopen(fname
, "wb");
106 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "\nPNG Error opening %s for writing!\n",
111 fwrite(outbuffer
, size
, 1, fp
);
120 avcodec_close(avctx
);
128 static int fexists(char *fname
)
130 return mp_path_exists(fname
);
133 static void gen_fname(screenshot_ctx
*ctx
)
136 snprintf(ctx
->fname
, 100, "shot%04d.png", ++ctx
->frameno
);
137 } while (fexists(ctx
->fname
) && ctx
->frameno
< 100000);
138 if (fexists(ctx
->fname
)) {
139 ctx
->fname
[0] = '\0';
143 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "*** screenshot '%s' ***\n", ctx
->fname
);
147 void screenshot_save(struct MPContext
*mpctx
, struct mp_image
*image
)
149 screenshot_ctx
*ctx
= screenshot_get_ctx(mpctx
);
150 struct mp_image
*dst
= alloc_mpi(image
->w
, image
->h
, IMGFMT_RGB24
);
152 struct SwsContext
*sws
= sws_getContextFromCmdLine(image
->width
,
159 struct mp_csp_details colorspace
;
160 get_detected_video_colorspace(mpctx
->sh_video
, &colorspace
);
161 // this is a property of the output device; images always use full-range RGB
162 colorspace
.levels_out
= MP_CSP_LEVELS_PC
;
163 mp_sws_set_colorspace(sws
, &colorspace
);
165 sws_scale(sws
, (const uint8_t **)image
->planes
, image
->stride
, 0,
166 image
->height
, dst
->planes
, dst
->stride
);
171 sws_freeContext(sws
);
175 static void vf_screenshot_callback(void *pctx
, struct mp_image
*image
)
177 struct MPContext
*mpctx
= (struct MPContext
*)pctx
;
178 screenshot_ctx
*ctx
= screenshot_get_ctx(mpctx
);
179 screenshot_save(mpctx
, image
);
181 screenshot_request(mpctx
, 0, ctx
->full_window
);
184 void screenshot_request(struct MPContext
*mpctx
, bool each_frame
,
187 if (mpctx
->video_out
&& mpctx
->video_out
->config_ok
) {
188 screenshot_ctx
*ctx
= screenshot_get_ctx(mpctx
);
190 ctx
->using_vf_screenshot
= 0;
193 ctx
->each_frame
= !ctx
->each_frame
;
194 ctx
->full_window
= full_window
;
195 if (!ctx
->each_frame
)
199 struct voctrl_screenshot_args args
= { .full_window
= full_window
};
200 if (vo_control(mpctx
->video_out
, VOCTRL_SCREENSHOT
, &args
) == true) {
201 screenshot_save(mpctx
, args
.out_image
);
202 free_mp_image(args
.out_image
);
204 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "No VO support for taking"
205 " screenshots, trying VFCTRL_SCREENSHOT!\n");
206 ctx
->using_vf_screenshot
= 1;
207 struct vf_ctrl_screenshot cmd
= {
208 .image_callback
= vf_screenshot_callback
,
209 .image_callback_ctx
= mpctx
,
211 struct vf_instance
*vfilter
= mpctx
->sh_video
->vfilter
;
212 if (vfilter
->control(vfilter
, VFCTRL_SCREENSHOT
, &cmd
) !=
214 mp_msg(MSGT_CPLAYER
, MSGL_INFO
,
215 "...failed (need --vf=screenshot?)\n");
220 void screenshot_flip(struct MPContext
*mpctx
)
222 screenshot_ctx
*ctx
= screenshot_get_ctx(mpctx
);
224 if (!ctx
->each_frame
)
227 // screenshot_flip is called when the VO presents a new frame. vf_screenshot
228 // can behave completely different (consider filters inserted between
229 // vf_screenshot and vf_vo, that add or remove frames), so handle this case
231 if (ctx
->using_vf_screenshot
)
234 screenshot_request(mpctx
, 0, ctx
->full_window
);