1 /*****************************************************************************
2 * win32.c: Screen capture module.
3 *****************************************************************************
4 * Copyright (C) 2004-2011 VLC authors and VideoLAN
7 * Authors: Gildas Bazin <gbazin@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_common.h>
42 POINT ptl
; /* Coordinates of the primary display's top left, when the origin
43 * is taken to be the top left of the entire virtual screen */
51 * In screen coordinates the origin is the upper-left corner of the primary
52 * display, and points can have negative x/y when other displays are located
53 * to the left/top of the primary.
55 * Windows may supply these coordinates in physical or logical units
56 * depending on the version of Windows and the DPI awareness of the application.
57 * I have noticed that even different interfaces of VLC (qt, rc...) can lead
58 * to differences in DPI awareness. The choice of physical vs logical seems
59 * to be universal though (it applies to everything we use, from GetCursorPos
60 * to GetSystemMetrics and BitBlt) so we don't have to worry about anything.
62 * The only issue here is that it can be confusing to users when setting e.g.
63 * subscreen position and dimensions. This however can be controlled by
64 * disabling display scaling in the compatibility settings of the VLC executable.
66 static inline void FromScreenCoordinates( demux_t
*p_demux
, POINT
*p_point
)
68 demux_sys_t
*p_sys
= p_demux
->p_sys
;
69 screen_data_t
*p_data
= p_sys
->p_data
;
70 p_point
->x
+= p_data
->ptl
.x
;
71 p_point
->y
+= p_data
->ptl
.y
;
74 static inline void ToScreenCoordinates( demux_t
*p_demux
, POINT
*p_point
)
76 demux_sys_t
*p_sys
= p_demux
->p_sys
;
77 screen_data_t
*p_data
= p_sys
->p_data
;
78 p_point
->x
-= p_data
->ptl
.x
;
79 p_point
->y
-= p_data
->ptl
.y
;
82 int screen_InitCapture( demux_t
*p_demux
)
84 demux_sys_t
*p_sys
= p_demux
->p_sys
;
85 screen_data_t
*p_data
;
86 int i_chroma
, i_bits_per_pixel
;
88 p_sys
->p_data
= p_data
= calloc( 1, sizeof( screen_data_t
) );
92 /* Get the device context for the whole screen */
93 p_data
->hdc_src
= CreateDC( TEXT("DISPLAY"), NULL
, NULL
, NULL
);
94 if( !p_data
->hdc_src
)
96 msg_Err( p_demux
, "cannot get device context" );
101 p_data
->hdc_dst
= CreateCompatibleDC( p_data
->hdc_src
);
102 if( !p_data
->hdc_dst
)
104 msg_Err( p_demux
, "cannot get compat device context" );
105 ReleaseDC( 0, p_data
->hdc_src
);
110 i_bits_per_pixel
= GetDeviceCaps( p_data
->hdc_src
, BITSPIXEL
);
111 switch( i_bits_per_pixel
)
113 case 8: /* FIXME: set the palette */
114 i_chroma
= VLC_CODEC_RGB8
; break;
116 case 16: /* Yes it is really 15 bits (when using BI_RGB) */
117 i_chroma
= VLC_CODEC_RGB15
; break;
119 i_chroma
= VLC_CODEC_RGB24
; break;
121 i_chroma
= VLC_CODEC_RGB32
; break;
123 msg_Err( p_demux
, "unknown screen depth %i", i_bits_per_pixel
);
124 DeleteDC( p_data
->hdc_dst
);
125 ReleaseDC( 0, p_data
->hdc_src
);
130 es_format_Init( &p_sys
->fmt
, VIDEO_ES
, i_chroma
);
131 p_sys
->fmt
.video
.i_visible_width
=
132 p_sys
->fmt
.video
.i_width
= GetSystemMetrics( SM_CXVIRTUALSCREEN
);
133 p_sys
->fmt
.video
.i_visible_height
=
134 p_sys
->fmt
.video
.i_height
= GetSystemMetrics( SM_CYVIRTUALSCREEN
);
135 p_sys
->fmt
.video
.i_bits_per_pixel
= i_bits_per_pixel
;
136 p_sys
->fmt
.video
.i_sar_num
= p_sys
->fmt
.video
.i_sar_den
= 1;
137 p_sys
->fmt
.video
.i_chroma
= i_chroma
;
138 p_sys
->fmt
.video
.transfer
= TRANSFER_FUNC_SRGB
;
139 p_sys
->fmt
.video
.b_color_range_full
= true;
143 case VLC_CODEC_RGB15
:
144 p_sys
->fmt
.video
.i_rmask
= 0x7c00;
145 p_sys
->fmt
.video
.i_gmask
= 0x03e0;
146 p_sys
->fmt
.video
.i_bmask
= 0x001f;
148 case VLC_CODEC_RGB24
:
149 p_sys
->fmt
.video
.i_rmask
= 0x00ff0000;
150 p_sys
->fmt
.video
.i_gmask
= 0x0000ff00;
151 p_sys
->fmt
.video
.i_bmask
= 0x000000ff;
153 case VLC_CODEC_RGB32
:
154 p_sys
->fmt
.video
.i_rmask
= 0x00ff0000;
155 p_sys
->fmt
.video
.i_gmask
= 0x0000ff00;
156 p_sys
->fmt
.video
.i_bmask
= 0x000000ff;
159 msg_Warn( p_demux
, "Unknown RGB masks" );
163 p_data
->ptl
.x
= - GetSystemMetrics( SM_XVIRTUALSCREEN
);
164 p_data
->ptl
.y
= - GetSystemMetrics( SM_YVIRTUALSCREEN
);
169 int screen_CloseCapture( demux_t
*p_demux
)
171 demux_sys_t
*p_sys
= p_demux
->p_sys
;
172 screen_data_t
*p_data
= p_sys
->p_data
;
174 if( p_data
->p_block
) block_Release( p_data
->p_block
);
176 if( p_data
->hgdi_backup
)
177 SelectObject( p_data
->hdc_dst
, p_data
->hgdi_backup
);
179 DeleteDC( p_data
->hdc_dst
);
180 ReleaseDC( 0, p_data
->hdc_src
);
192 static void CaptureBlockRelease( block_t
*p_block
)
194 DeleteObject( ((struct block_sys_t
*)p_block
)->hbmp
);
198 static const struct vlc_block_callbacks CaptureBlockCallbacks
=
203 static block_t
*CaptureBlockNew( demux_t
*p_demux
)
205 demux_sys_t
*p_sys
= p_demux
->p_sys
;
206 screen_data_t
*p_data
= p_sys
->p_data
;
207 struct block_sys_t
*p_block
;
212 if( p_data
->bmi
.bmiHeader
.biSize
== 0 )
215 /* Create the bitmap info header */
216 p_data
->bmi
.bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
217 p_data
->bmi
.bmiHeader
.biWidth
= p_sys
->fmt
.video
.i_width
;
218 p_data
->bmi
.bmiHeader
.biHeight
= - p_sys
->fmt
.video
.i_height
;
219 p_data
->bmi
.bmiHeader
.biPlanes
= 1;
220 p_data
->bmi
.bmiHeader
.biBitCount
= p_sys
->fmt
.video
.i_bits_per_pixel
;
221 p_data
->bmi
.bmiHeader
.biCompression
= BI_RGB
;
222 p_data
->bmi
.bmiHeader
.biSizeImage
= 0;
223 p_data
->bmi
.bmiHeader
.biXPelsPerMeter
= 0;
224 p_data
->bmi
.bmiHeader
.biYPelsPerMeter
= 0;
225 p_data
->bmi
.bmiHeader
.biClrUsed
= 0;
226 p_data
->bmi
.bmiHeader
.biClrImportant
= 0;
228 i_val
= var_CreateGetInteger( p_demux
, "screen-fragment-size" );
229 p_data
->i_fragment_size
= i_val
> 0 ? i_val
: (int)p_sys
->fmt
.video
.i_height
;
230 p_data
->i_fragment_size
= i_val
> (int)p_sys
->fmt
.video
.i_height
?
231 (int)p_sys
->fmt
.video
.i_height
:
232 p_data
->i_fragment_size
;
233 p_sys
->f_fps
*= (p_sys
->fmt
.video
.i_height
/p_data
->i_fragment_size
);
234 p_sys
->i_incr
= vlc_tick_rate_duration( p_sys
->f_fps
);
235 p_data
->i_fragment
= 0;
240 /* Create the bitmap storage space */
241 hbmp
= CreateDIBSection( p_data
->hdc_dst
, &p_data
->bmi
, DIB_RGB_COLORS
,
242 &p_buffer
, NULL
, 0 );
243 if( !hbmp
|| !p_buffer
)
245 msg_Err( p_demux
, "cannot create bitmap" );
249 /* Select the bitmap into the compatible DC */
250 if( !p_data
->hgdi_backup
)
251 p_data
->hgdi_backup
= SelectObject( p_data
->hdc_dst
, hbmp
);
253 SelectObject( p_data
->hdc_dst
, hbmp
);
255 if( !p_data
->hgdi_backup
)
257 msg_Err( p_demux
, "cannot select bitmap" );
262 if( !(p_block
= malloc( sizeof( struct block_sys_t
) )) )
265 /* Fill all fields */
267 ( ( ( ( p_sys
->fmt
.video
.i_width
* p_sys
->fmt
.video
.i_bits_per_pixel
) + 31 ) & ~31 ) >> 3 );
268 i_buffer
= i_stride
* p_sys
->fmt
.video
.i_height
;
269 block_Init( &p_block
->self
, &CaptureBlockCallbacks
, p_buffer
, i_buffer
);
270 p_block
->hbmp
= hbmp
;
272 return &p_block
->self
;
275 if( hbmp
) DeleteObject( hbmp
);
279 block_t
*screen_Capture( demux_t
*p_demux
)
281 demux_sys_t
*p_sys
= p_demux
->p_sys
;
282 screen_data_t
*p_data
= p_sys
->p_data
;
284 if( !p_data
->i_fragment
)
286 if( !( p_data
->p_block
= CaptureBlockNew( p_demux
) ) )
288 msg_Warn( p_demux
, "cannot get block" );
293 if( p_sys
->b_follow_mouse
)
296 GetCursorPos( &pos
);
297 FromScreenCoordinates( p_demux
, &pos
);
298 FollowMouse( p_sys
, pos
.x
, pos
.y
);
301 POINT top_left
= { p_sys
->i_left
, p_sys
->i_top
};
302 ToScreenCoordinates( p_demux
, &top_left
);
304 if( !BitBlt( p_data
->hdc_dst
, 0,
305 p_data
->i_fragment
* p_data
->i_fragment_size
,
306 p_sys
->fmt
.video
.i_width
, p_data
->i_fragment_size
,
307 p_data
->hdc_src
, top_left
.x
, top_left
.y
+
308 p_data
->i_fragment
* p_data
->i_fragment_size
,
309 SRCCOPY
| CAPTUREBLT
) )
311 msg_Err( p_demux
, "error during BitBlt()" );
315 p_data
->i_fragment
++;
317 if( !( p_data
->i_fragment
%
318 (p_sys
->fmt
.video
.i_height
/p_data
->i_fragment_size
) ) )
320 block_t
*p_block
= p_data
->p_block
;
321 p_data
->i_fragment
= 0;
328 GetCursorPos( &pos
);
329 FromScreenCoordinates( p_demux
, &pos
);
330 RenderCursor( p_demux
, pos
.x
, pos
.y
,