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.
25 #include <libswscale/swscale.h>
26 #include <libavcodec/avcodec.h>
30 #include "screenshot.h"
34 #include "libmpcodecs/img_format.h"
35 #include "libmpcodecs/mp_image.h"
36 #include "libmpcodecs/dec_video.h"
37 #include "libmpcodecs/vf.h"
38 #include "libvo/video_out.h"
40 #include "fmt-conversion.h"
42 //for sws_getContextFromCmdLine_hq and mp_sws_set_colorspace
43 #include "libmpcodecs/vf_scale.h"
44 #include "libvo/csputils.h"
46 typedef struct screenshot_ctx
{
50 int using_vf_screenshot
;
56 static int destroy_ctx(void *ptr
)
58 struct screenshot_ctx
*ctx
= ptr
;
59 #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 28, 0)
60 avcodec_free_frame(&ctx
->pic
);
67 static screenshot_ctx
*screenshot_get_ctx(MPContext
*mpctx
)
69 if (!mpctx
->screenshot_ctx
) {
70 struct screenshot_ctx
*ctx
= talloc_zero(mpctx
, screenshot_ctx
);
71 talloc_set_destructor(ctx
, destroy_ctx
);
72 ctx
->pic
= avcodec_alloc_frame();
74 mpctx
->screenshot_ctx
= ctx
;
76 return mpctx
->screenshot_ctx
;
79 static int write_png(screenshot_ctx
*ctx
, struct mp_image
*image
)
81 char *fname
= ctx
->fname
;
83 void *outbuffer
= NULL
;
86 struct AVCodec
*png_codec
= avcodec_find_encoder(CODEC_ID_PNG
);
87 AVCodecContext
*avctx
= NULL
;
90 avctx
= avcodec_alloc_context3(png_codec
);
94 avctx
->time_base
= AV_TIME_BASE_Q
;
95 avctx
->width
= image
->width
;
96 avctx
->height
= image
->height
;
97 avctx
->pix_fmt
= PIX_FMT_RGB24
;
98 avctx
->compression_level
= 0;
100 if (avcodec_open2(avctx
, png_codec
, NULL
) < 0) {
102 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "Could not open libavcodec PNG encoder"
103 " for saving screenshot\n");
107 size_t outbuffer_size
= image
->width
* image
->height
* 3 * 2;
108 outbuffer
= malloc(outbuffer_size
);
112 AVFrame
*pic
= ctx
->pic
;
113 avcodec_get_frame_defaults(pic
);
114 for (int n
= 0; n
< 4; n
++) {
115 pic
->data
[n
] = image
->planes
[n
];
116 pic
->linesize
[n
] = image
->stride
[n
];
118 int size
= avcodec_encode_video(avctx
, outbuffer
, outbuffer_size
, pic
);
122 fp
= fopen(fname
, "wb");
124 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "\nPNG Error opening %s for writing!\n",
129 fwrite(outbuffer
, size
, 1, fp
);
138 avcodec_close(avctx
);
146 static int fexists(char *fname
)
148 return mp_path_exists(fname
);
151 static void gen_fname(screenshot_ctx
*ctx
)
154 snprintf(ctx
->fname
, 100, "shot%04d.png", ++ctx
->frameno
);
155 } while (fexists(ctx
->fname
) && ctx
->frameno
< 100000);
156 if (fexists(ctx
->fname
)) {
157 ctx
->fname
[0] = '\0';
161 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "*** screenshot '%s' ***\n", ctx
->fname
);
165 void screenshot_save(struct MPContext
*mpctx
, struct mp_image
*image
)
167 screenshot_ctx
*ctx
= screenshot_get_ctx(mpctx
);
168 struct mp_image
*dst
= alloc_mpi(image
->w
, image
->h
, IMGFMT_RGB24
);
170 struct SwsContext
*sws
= sws_getContextFromCmdLine_hq(image
->width
,
177 struct mp_csp_details colorspace
;
178 get_detected_video_colorspace(mpctx
->sh_video
, &colorspace
);
179 // this is a property of the output device; images always use full-range RGB
180 colorspace
.levels_out
= MP_CSP_LEVELS_PC
;
181 mp_sws_set_colorspace(sws
, &colorspace
);
183 sws_scale(sws
, (const uint8_t **)image
->planes
, image
->stride
, 0,
184 image
->height
, dst
->planes
, dst
->stride
);
189 sws_freeContext(sws
);
193 static void vf_screenshot_callback(void *pctx
, struct mp_image
*image
)
195 struct MPContext
*mpctx
= (struct MPContext
*)pctx
;
196 screenshot_ctx
*ctx
= screenshot_get_ctx(mpctx
);
197 screenshot_save(mpctx
, image
);
199 screenshot_request(mpctx
, 0, ctx
->full_window
);
202 void screenshot_request(struct MPContext
*mpctx
, bool each_frame
,
205 if (mpctx
->video_out
&& mpctx
->video_out
->config_ok
) {
206 screenshot_ctx
*ctx
= screenshot_get_ctx(mpctx
);
208 ctx
->using_vf_screenshot
= 0;
211 ctx
->each_frame
= !ctx
->each_frame
;
212 ctx
->full_window
= full_window
;
213 if (!ctx
->each_frame
)
217 struct voctrl_screenshot_args args
= { .full_window
= full_window
};
218 if (vo_control(mpctx
->video_out
, VOCTRL_SCREENSHOT
, &args
) == true) {
219 screenshot_save(mpctx
, args
.out_image
);
220 free_mp_image(args
.out_image
);
222 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "No VO support for taking"
223 " screenshots, trying VFCTRL_SCREENSHOT!\n");
224 ctx
->using_vf_screenshot
= 1;
225 struct vf_ctrl_screenshot cmd
= {
226 .image_callback
= vf_screenshot_callback
,
227 .image_callback_ctx
= mpctx
,
229 struct vf_instance
*vfilter
= mpctx
->sh_video
->vfilter
;
230 if (vfilter
->control(vfilter
, VFCTRL_SCREENSHOT
, &cmd
) !=
232 mp_msg(MSGT_CPLAYER
, MSGL_INFO
,
233 "...failed (need --vf=screenshot?)\n");
238 void screenshot_flip(struct MPContext
*mpctx
)
240 screenshot_ctx
*ctx
= screenshot_get_ctx(mpctx
);
242 if (!ctx
->each_frame
)
245 // screenshot_flip is called when the VO presents a new frame. vf_screenshot
246 // can behave completely different (consider filters inserted between
247 // vf_screenshot and vf_vo, that add or remove frames), so handle this case
249 if (ctx
->using_vf_screenshot
)
252 screenshot_request(mpctx
, 0, ctx
->full_window
);