2 * This file is part of MPlayer.
4 * MPlayer 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 * MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <libavutil/common.h>
32 #include "img_format.h"
36 #include "libvo/fastmemcpy.h"
40 #include "libvo/osd.h"
46 static struct vf_priv_s
{
47 // These four values are a backup of the values parsed from the command line.
48 // This is necessary so that we do not get a mess upon filter reinit due to
49 // e.g. aspect changes and with only aspect specified on the command line,
50 // where we would otherwise use the values calculated for a different aspect
51 // instead of recalculating them again.
52 int cfg_exp_w
, cfg_exp_h
;
53 int cfg_exp_x
, cfg_exp_y
;
61 struct osd_state
*osd
;
62 } const vf_priv_dflt
= {
74 //===========================================================================//
77 static void draw_func(void *ctx
, int x0
,int y0
, int w
,int h
,unsigned char* src
, unsigned char *srca
, int stride
){
78 struct vf_instance
*vf
= ctx
;
80 if(w
<=0 || h
<=0) return; // nothing to do...
81 // printf("OSD redraw: %d;%d %dx%d \n",x0,y0,w,h);
82 dst
=vf
->dmpi
->planes
[0]+
83 vf
->dmpi
->stride
[0]*y0
+
84 (vf
->dmpi
->bpp
>>3)*x0
;
85 switch(vf
->dmpi
->imgfmt
){
88 vo_draw_alpha_rgb12(w
, h
, src
, srca
, stride
, dst
, vf
->dmpi
->stride
[0]);
92 vo_draw_alpha_rgb15(w
,h
,src
,srca
,stride
,dst
,vf
->dmpi
->stride
[0]);
96 vo_draw_alpha_rgb16(w
,h
,src
,srca
,stride
,dst
,vf
->dmpi
->stride
[0]);
100 vo_draw_alpha_rgb24(w
,h
,src
,srca
,stride
,dst
,vf
->dmpi
->stride
[0]);
104 vo_draw_alpha_rgb32(w
,h
,src
,srca
,stride
,dst
,vf
->dmpi
->stride
[0]);
113 vo_draw_alpha_yv12(w
,h
,src
,srca
,stride
,dst
,vf
->dmpi
->stride
[0]);
116 vo_draw_alpha_yuy2(w
,h
,src
,srca
,stride
,dst
,vf
->dmpi
->stride
[0]);
119 vo_draw_alpha_yuy2(w
,h
,src
,srca
,stride
,dst
+1,vf
->dmpi
->stride
[0]);
124 static void draw_osd(struct vf_instance
*vf
,int w
,int h
){
125 osd_draw_text(vf
->priv
->osd
, vf
->priv
->exp_w
,vf
->priv
->exp_h
,draw_func
,vf
);
129 //===========================================================================//
131 static int config(struct vf_instance
*vf
,
132 int width
, int height
, int d_width
, int d_height
,
133 unsigned int flags
, unsigned int outfmt
)
135 struct MPOpts
*opts
= vf
->opts
;
136 if(outfmt
== IMGFMT_MPEGPES
) {
137 vf
->priv
->passthrough
= 1;
138 return vf_next_config(vf
,width
,height
,d_width
,d_height
,flags
,outfmt
);
140 if (outfmt
== IMGFMT_IF09
) return 0;
141 vf
->priv
->exp_x
= vf
->priv
->cfg_exp_x
;
142 vf
->priv
->exp_y
= vf
->priv
->cfg_exp_y
;
143 vf
->priv
->exp_w
= vf
->priv
->cfg_exp_w
;
144 vf
->priv
->exp_h
= vf
->priv
->cfg_exp_h
;
145 // calculate the missing parameters:
147 if(vf
->priv
->exp_w
<width
) vf
->priv
->exp_w
=width
;
148 if(vf
->priv
->exp_h
<height
) vf
->priv
->exp_h
=height
;
150 if ( vf
->priv
->exp_w
== -1 ) vf
->priv
->exp_w
=width
;
151 else if (vf
->priv
->exp_w
< -1 ) vf
->priv
->exp_w
=width
- vf
->priv
->exp_w
;
152 else if ( vf
->priv
->exp_w
<width
) vf
->priv
->exp_w
=width
;
153 if ( vf
->priv
->exp_h
== -1 ) vf
->priv
->exp_h
=height
;
154 else if ( vf
->priv
->exp_h
< -1 ) vf
->priv
->exp_h
=height
- vf
->priv
->exp_h
;
155 else if( vf
->priv
->exp_h
<height
) vf
->priv
->exp_h
=height
;
157 if (vf
->priv
->aspect
) {
158 float adjusted_aspect
= vf
->priv
->aspect
;
159 adjusted_aspect
*= ((double)width
/height
) / ((double)d_width
/d_height
);
160 if (vf
->priv
->exp_h
< vf
->priv
->exp_w
/ adjusted_aspect
) {
161 vf
->priv
->exp_h
= vf
->priv
->exp_w
/ adjusted_aspect
+ 0.5;
163 vf
->priv
->exp_w
= vf
->priv
->exp_h
* adjusted_aspect
+ 0.5;
166 if (vf
->priv
->round
> 1) { // round up.
167 vf
->priv
->exp_w
= (1 + (vf
->priv
->exp_w
- 1) / vf
->priv
->round
) * vf
->priv
->round
;
168 vf
->priv
->exp_h
= (1 + (vf
->priv
->exp_h
- 1) / vf
->priv
->round
) * vf
->priv
->round
;
171 if(vf
->priv
->exp_x
<0 || vf
->priv
->exp_x
+width
>vf
->priv
->exp_w
) vf
->priv
->exp_x
=(vf
->priv
->exp_w
-width
)/2;
172 if(vf
->priv
->exp_y
<0 || vf
->priv
->exp_y
+height
>vf
->priv
->exp_h
) vf
->priv
->exp_y
=(vf
->priv
->exp_h
-height
)/2;
174 if(!opts
->screen_size_x
&& !opts
->screen_size_y
){
175 d_width
=d_width
*vf
->priv
->exp_w
/width
;
176 d_height
=d_height
*vf
->priv
->exp_h
/height
;
178 return vf_next_config(vf
,vf
->priv
->exp_w
,vf
->priv
->exp_h
,d_width
,d_height
,flags
,outfmt
);
181 // there are 4 cases:
182 // codec --DR--> expand --DR--> vo
183 // codec --DR--> expand -copy-> vo
184 // codec -copy-> expand --DR--> vo
185 // codec -copy-> expand -copy-> vo (worst case)
187 static void get_image(struct vf_instance
*vf
, mp_image_t
*mpi
){
188 // if(mpi->type==MP_IMGTYPE_IPB) return; // not yet working
190 if(vf
->priv
->osd_enabled
&& (mpi
->flags
&MP_IMGFLAG_PRESERVE
)){
191 // check if we have to render osd!
192 osd_update(vf
->priv
->osd
, vf
->priv
->exp_w
, vf
->priv
->exp_h
);
193 if(vo_osd_check_range_update(vf
->priv
->exp_x
,vf
->priv
->exp_y
,
194 vf
->priv
->exp_x
+mpi
->w
,vf
->priv
->exp_y
+mpi
->h
)) return;
197 if(vf
->priv
->exp_w
==mpi
->width
||
198 (mpi
->flags
&(MP_IMGFLAG_ACCEPT_STRIDE
|MP_IMGFLAG_ACCEPT_WIDTH
)) ){
200 mpi
->priv
=vf
->dmpi
=vf_get_image(vf
->next
,mpi
->imgfmt
,
201 mpi
->type
, mpi
->flags
,
202 FFMAX(vf
->priv
->exp_w
, mpi
->width
+vf
->priv
->exp_x
),
203 FFMAX(vf
->priv
->exp_h
, mpi
->height
+vf
->priv
->exp_y
));
204 if((vf
->dmpi
->flags
& MP_IMGFLAG_DRAW_CALLBACK
) &&
205 !(vf
->dmpi
->flags
& MP_IMGFLAG_DIRECT
)){
206 mp_tmsg(MSGT_VFILTER
, MSGL_INFO
, "Full DR not possible, trying SLICES instead!\n");
209 // set up mpi as a cropped-down image of dmpi:
210 if(mpi
->flags
&MP_IMGFLAG_PLANAR
){
211 mpi
->planes
[0]=vf
->dmpi
->planes
[0]+
212 vf
->priv
->exp_y
*vf
->dmpi
->stride
[0]+vf
->priv
->exp_x
;
213 mpi
->planes
[1]=vf
->dmpi
->planes
[1]+
214 (vf
->priv
->exp_y
>>mpi
->chroma_y_shift
)*vf
->dmpi
->stride
[1]+(vf
->priv
->exp_x
>>mpi
->chroma_x_shift
);
215 mpi
->planes
[2]=vf
->dmpi
->planes
[2]+
216 (vf
->priv
->exp_y
>>mpi
->chroma_y_shift
)*vf
->dmpi
->stride
[2]+(vf
->priv
->exp_x
>>mpi
->chroma_x_shift
);
217 mpi
->stride
[1]=vf
->dmpi
->stride
[1];
218 mpi
->stride
[2]=vf
->dmpi
->stride
[2];
220 mpi
->planes
[0]=vf
->dmpi
->planes
[0]+
221 vf
->priv
->exp_y
*vf
->dmpi
->stride
[0]+
222 vf
->priv
->exp_x
*(vf
->dmpi
->bpp
/8);
224 mpi
->stride
[0]=vf
->dmpi
->stride
[0];
225 mpi
->width
=vf
->dmpi
->width
;
226 mpi
->flags
|=MP_IMGFLAG_DIRECT
;
227 mpi
->flags
&=~MP_IMGFLAG_DRAW_CALLBACK
;
228 // vf->dmpi->flags&=~MP_IMGFLAG_DRAW_CALLBACK;
232 static void start_slice(struct vf_instance
*vf
, mp_image_t
*mpi
){
233 // printf("start_slice called! flag=%d\n",mpi->flags&MP_IMGFLAG_DRAW_CALLBACK);
234 if(!vf
->next
->draw_slice
){
235 mpi
->flags
&=~MP_IMGFLAG_DRAW_CALLBACK
;
238 // they want slices!!! allocate the buffer.
240 mpi
->priv
=vf
->dmpi
=vf_get_image(vf
->next
,mpi
->imgfmt
,
241 // MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_PREFER_ALIGNED_STRIDE,
242 MP_IMGTYPE_TEMP
, mpi
->flags
,
243 FFMAX(vf
->priv
->exp_w
, mpi
->width
+vf
->priv
->exp_x
),
244 FFMAX(vf
->priv
->exp_h
, mpi
->height
+vf
->priv
->exp_y
));
245 if(!(vf
->dmpi
->flags
&MP_IMGFLAG_DRAW_CALLBACK
))
246 mp_tmsg(MSGT_VFILTER
, MSGL_WARN
, "WARNING! Next filter doesn't support SLICES, get ready for sig11...\n"); // shouldn't happen.
247 vf
->priv
->first_slice
= 1;
250 static void draw_top_blackbar_slice(struct vf_instance
*vf
,
251 unsigned char** src
, int* stride
, int w
,int h
, int x
, int y
){
252 if(vf
->priv
->exp_y
>0 && y
== 0) {
253 vf_next_draw_slice(vf
, vf
->dmpi
->planes
, vf
->dmpi
->stride
,
254 vf
->dmpi
->w
,vf
->priv
->exp_y
,0,0);
259 static void draw_bottom_blackbar_slice(struct vf_instance
*vf
,
260 unsigned char** src
, int* stride
, int w
,int h
, int x
, int y
){
261 if(vf
->priv
->exp_y
+vf
->h
<vf
->dmpi
->h
&& y
+h
== vf
->h
) {
262 unsigned char *src2
[MP_MAX_PLANES
];
263 src2
[0] = vf
->dmpi
->planes
[0]
264 + (vf
->priv
->exp_y
+vf
->h
)*vf
->dmpi
->stride
[0];
265 if(vf
->dmpi
->flags
&MP_IMGFLAG_PLANAR
){
266 src2
[1] = vf
->dmpi
->planes
[1]
267 + ((vf
->priv
->exp_y
+vf
->h
)>>vf
->dmpi
->chroma_y_shift
)*vf
->dmpi
->stride
[1];
268 src2
[2] = vf
->dmpi
->planes
[2]
269 + ((vf
->priv
->exp_y
+vf
->h
)>>vf
->dmpi
->chroma_y_shift
)*vf
->dmpi
->stride
[2];
271 src2
[1] = vf
->dmpi
->planes
[1]; // passthrough rgb8 palette
273 vf_next_draw_slice(vf
, src2
, vf
->dmpi
->stride
,
274 vf
->dmpi
->w
,vf
->dmpi
->h
-(vf
->priv
->exp_y
+vf
->h
),
275 0,vf
->priv
->exp_y
+vf
->h
);
279 static void draw_slice(struct vf_instance
*vf
,
280 unsigned char** src
, int* stride
, int w
,int h
, int x
, int y
){
281 // printf("draw_slice() called %d at %d\n",h,y);
283 if (y
== 0 && y
+h
== vf
->h
) {
284 // special case - only one slice
285 draw_top_blackbar_slice(vf
, src
, stride
, w
, h
, x
, y
);
286 vf_next_draw_slice(vf
,src
,stride
,w
,h
,x
+vf
->priv
->exp_x
,y
+vf
->priv
->exp_y
);
287 draw_bottom_blackbar_slice(vf
, src
, stride
, w
, h
, x
, y
);
290 if (vf
->priv
->first_slice
) {
291 draw_top_blackbar_slice(vf
, src
, stride
, w
, h
, x
, y
);
292 draw_bottom_blackbar_slice(vf
, src
, stride
, w
, h
, x
, y
);
294 vf_next_draw_slice(vf
,src
,stride
,w
,h
,x
+vf
->priv
->exp_x
,y
+vf
->priv
->exp_y
);
295 if (!vf
->priv
->first_slice
) {
296 draw_top_blackbar_slice(vf
, src
, stride
, w
, h
, x
, y
);
297 draw_bottom_blackbar_slice(vf
, src
, stride
, w
, h
, x
, y
);
299 vf
->priv
->first_slice
= 0;
302 // w, h = width and height of the actual video frame (located at exp_x/exp_y)
303 static void clear_borders(struct vf_instance
*vf
, int w
, int h
)
305 // upper border (over the full width)
306 vf_mpi_clear(vf
->dmpi
, 0, 0, vf
->priv
->exp_w
, vf
->priv
->exp_y
);
308 vf_mpi_clear(vf
->dmpi
, 0, vf
->priv
->exp_y
+ h
, vf
->priv
->exp_w
,
309 vf
->priv
->exp_h
- (vf
->priv
->exp_y
+ h
));
311 vf_mpi_clear(vf
->dmpi
, 0, vf
->priv
->exp_y
, vf
->priv
->exp_x
, h
);
313 vf_mpi_clear(vf
->dmpi
, vf
->priv
->exp_x
+ w
, vf
->priv
->exp_y
,
314 vf
->priv
->exp_w
- (vf
->priv
->exp_x
+ w
), h
);
317 static int put_image(struct vf_instance
*vf
, mp_image_t
*mpi
, double pts
){
318 if (vf
->priv
->passthrough
) {
319 mp_image_t
*dmpi
= vf_get_image(vf
->next
, IMGFMT_MPEGPES
,
320 MP_IMGTYPE_EXPORT
, 0, mpi
->w
, mpi
->h
);
321 dmpi
->planes
[0]=mpi
->planes
[0];
322 return vf_next_put_image(vf
,dmpi
, pts
);
325 if(mpi
->flags
&MP_IMGFLAG_DIRECT
|| mpi
->flags
&MP_IMGFLAG_DRAW_CALLBACK
){
327 if(!vf
->dmpi
) { mp_tmsg(MSGT_VFILTER
, MSGL_WARN
, "Why do we get NULL??\n"); return 0; }
329 clear_borders(vf
,mpi
->w
,mpi
->h
);
331 if(vf
->priv
->osd_enabled
) draw_osd(vf
,mpi
->w
,mpi
->h
);
333 // we've used DR, so we're ready...
334 if(!(mpi
->flags
&MP_IMGFLAG_PLANAR
))
335 vf
->dmpi
->planes
[1] = mpi
->planes
[1]; // passthrough rgb8 palette
336 return vf_next_put_image(vf
,vf
->dmpi
, pts
);
339 // hope we'll get DR buffer:
340 vf
->dmpi
=vf_get_image(vf
->next
,mpi
->imgfmt
,
341 MP_IMGTYPE_TEMP
, MP_IMGFLAG_ACCEPT_STRIDE
,
342 vf
->priv
->exp_w
, vf
->priv
->exp_h
);
345 if(mpi
->flags
&MP_IMGFLAG_PLANAR
){
346 memcpy_pic(vf
->dmpi
->planes
[0]+
347 vf
->priv
->exp_y
*vf
->dmpi
->stride
[0]+vf
->priv
->exp_x
,
348 mpi
->planes
[0], mpi
->w
, mpi
->h
,
349 vf
->dmpi
->stride
[0],mpi
->stride
[0]);
350 memcpy_pic(vf
->dmpi
->planes
[1]+
351 (vf
->priv
->exp_y
>>mpi
->chroma_y_shift
)*vf
->dmpi
->stride
[1]+(vf
->priv
->exp_x
>>mpi
->chroma_x_shift
),
352 mpi
->planes
[1], (mpi
->w
>>mpi
->chroma_x_shift
), (mpi
->h
>>mpi
->chroma_y_shift
),
353 vf
->dmpi
->stride
[1],mpi
->stride
[1]);
354 memcpy_pic(vf
->dmpi
->planes
[2]+
355 (vf
->priv
->exp_y
>>mpi
->chroma_y_shift
)*vf
->dmpi
->stride
[2]+(vf
->priv
->exp_x
>>mpi
->chroma_x_shift
),
356 mpi
->planes
[2], (mpi
->w
>>mpi
->chroma_x_shift
), (mpi
->h
>>mpi
->chroma_y_shift
),
357 vf
->dmpi
->stride
[2],mpi
->stride
[2]);
359 memcpy_pic(vf
->dmpi
->planes
[0]+
360 vf
->priv
->exp_y
*vf
->dmpi
->stride
[0]+vf
->priv
->exp_x
*(vf
->dmpi
->bpp
/8),
361 mpi
->planes
[0], mpi
->w
*(vf
->dmpi
->bpp
/8), mpi
->h
,
362 vf
->dmpi
->stride
[0],mpi
->stride
[0]);
363 vf
->dmpi
->planes
[1] = mpi
->planes
[1]; // passthrough rgb8 palette
365 clear_borders(vf
,mpi
->w
,mpi
->h
);
367 if(vf
->priv
->osd_enabled
) draw_osd(vf
,mpi
->w
,mpi
->h
);
369 return vf_next_put_image(vf
,vf
->dmpi
, pts
);
372 //===========================================================================//
374 static int control(struct vf_instance
*vf
, int request
, void* data
){
377 case VFCTRL_SET_OSD_OBJ
:
378 vf
->priv
->osd
= data
;
380 case VFCTRL_DRAW_OSD
:
381 if(vf
->priv
->osd_enabled
) return CONTROL_TRUE
;
385 return vf_next_control(vf
,request
,data
);
388 static int query_format(struct vf_instance
*vf
, unsigned int fmt
){
389 return vf_next_query_format(vf
,fmt
);
392 static int vf_open(vf_instance_t
*vf
, char *args
){
395 vf
->query_format
=query_format
;
396 vf
->start_slice
=start_slice
;
397 vf
->draw_slice
=draw_slice
;
398 vf
->get_image
=get_image
;
399 vf
->put_image
=put_image
;
400 mp_msg(MSGT_VFILTER
, MSGL_INFO
, "Expand: %d x %d, %d ; %d, osd: %d, aspect: %f, round: %d\n",
405 vf
->priv
->osd_enabled
,
408 if (vf
->priv
->osd_enabled
)
409 vf
->default_caps
= VFCAP_OSD_FILTER
;
413 #define ST_OFF(f) M_ST_OFF(struct vf_priv_s,f)
414 static m_option_t vf_opts_fields
[] = {
415 {"w", ST_OFF(cfg_exp_w
), CONF_TYPE_INT
, 0, 0 ,0, NULL
},
416 {"h", ST_OFF(cfg_exp_h
), CONF_TYPE_INT
, 0, 0 ,0, NULL
},
417 {"x", ST_OFF(cfg_exp_x
), CONF_TYPE_INT
, M_OPT_MIN
, -1, 0, NULL
},
418 {"y", ST_OFF(cfg_exp_y
), CONF_TYPE_INT
, M_OPT_MIN
, -1, 0, NULL
},
419 {"osd", ST_OFF(osd_enabled
), CONF_TYPE_FLAG
, 0 , 0, 1, NULL
},
420 {"aspect", ST_OFF(aspect
), CONF_TYPE_DOUBLE
, M_OPT_MIN
, 0, 0, NULL
},
421 {"round", ST_OFF(round
), CONF_TYPE_INT
, M_OPT_MIN
, 1, 0, NULL
},
422 { NULL
, NULL
, 0, 0, 0, 0, NULL
}
425 static const m_struct_t vf_opts
= {
427 sizeof(struct vf_priv_s
),
434 const vf_info_t vf_info_expand
= {
447 //===========================================================================//