2 * BitMap OVerLay video filter for MPlayer
4 * (C) 2002 Per Wigren <wigren@home.se>
6 * This file is part of MPlayer.
8 * MPlayer is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * MPlayer is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * Use MPlayer as a framebuffer to read bitmaps and commands from a FIFO
25 * and display them in the window.
29 * RGBA32 width height xpos ypos alpha clear
30 * * Followed by width*height*4 bytes of raw RGBA32 data.
31 * ABGR32 width height xpos ypos alpha clear
32 * * Followed by width*height*4 bytes of raw ABGR32 data.
33 * RGB24 width height xpos ypos alpha clear
34 * * Followed by width*height*3 bytes of raw RGB32 data.
35 * BGR24 width height xpos ypos alpha clear
36 * * Followed by width*height*3 bytes of raw BGR32 data.
38 * ALPHA width height xpos ypos alpha
39 * * Change alpha for area
40 * CLEAR width height xpos ypos
43 * * Disable all alpha transparency!
44 * Send "ALPHA 0 0 0 0 0" to enable again!
51 * width, height Size of image/area
52 * xpos, ypos Start blitting at X/Y position
53 * alpha Set alpha difference. 0 means same as original.
54 * 255 makes everything opaque
55 * -255 makes everything transparent
56 * If you set this to -255 you can then send a sequence of
57 * ALPHA-commands to set the area to -225, -200, -175 etc
58 * for a nice fade-in-effect! ;)
59 * clear Clear the framebuffer before blitting. 1 means clear.
60 * If 0, the image will just be blitted on top of the old
61 * one, so you don't need to send 1,8MB of RGBA32 data
62 * everytime a small part of the screen is updated.
64 * Arguments for the filter are hidden:opaque:fifo
65 * For example 1:0:/tmp/myfifo.fifo will start the filter hidden, transparent
66 * and use /tmp/myfifo.fifo as the fifo.
68 * If you find bugs, please send me patches! ;)
70 * This filter was developed for use in Freevo (http://freevo.sf.net), but
71 * anyone is free to use it! ;)
81 #include <sys/types.h>
87 #include "img_format.h"
90 #include "libavutil/common.h"
92 #include "libvo/fastmemcpy.h"
94 #define IS_RAWIMG 0x100
98 #define IMG_RGBA32 0x101
99 #define IMG_ABGR32 0x102
100 #define IMG_RGB24 0x103
101 #define IMG_BGR24 0x104
102 #define IMG_PNG 0x201
103 #define CMD_CLEAR 0x001
104 #define CMD_ALPHA 0x002
109 #define INRANGE(a,b,c) ( ((a) < (b)) ? (b) : ( ((a) > (c)) ? (c) : (a) ) )
111 #define rgb2y(R,G,B) ( (( 263*R + 516*G + 100*B) >> 10) + 16 )
112 #define rgb2u(R,G,B) ( ((-152*R - 298*G + 450*B) >> 10) + 128 )
113 #define rgb2v(R,G,B) ( (( 450*R - 376*G - 73*B) >> 10) + 128 )
115 #define DBG(a) (mp_msg(MSGT_VFILTER, MSGL_DBG2, "DEBUG: %d\n", a))
118 int w
, h
, x1
, y1
, x2
, y2
;
120 unsigned char *y
, *u
, *v
, *a
, *oa
;
128 query_format(struct vf_instance
*vf
, unsigned int fmt
){
129 if(fmt
==IMGFMT_YV12
) return VFCAP_CSP_SUPPORTED
;
135 config(struct vf_instance
*vf
,
136 int width
, int height
, int d_width
, int d_height
,
137 unsigned int flags
, unsigned int outfmt
)
139 vf
->priv
->bitmap
.y
= malloc( width
*height
);
140 vf
->priv
->bitmap
.u
= malloc( width
*height
/4 );
141 vf
->priv
->bitmap
.v
= malloc( width
*height
/4 );
142 vf
->priv
->bitmap
.a
= malloc( width
*height
);
143 vf
->priv
->bitmap
.oa
= malloc( width
*height
);
144 if(!( vf
->priv
->bitmap
.y
&&
145 vf
->priv
->bitmap
.u
&&
146 vf
->priv
->bitmap
.v
&&
147 vf
->priv
->bitmap
.a
&&
148 vf
->priv
->bitmap
.oa
)) {
149 mp_msg(MSGT_VFILTER
, MSGL_ERR
, "vf_bmovl: Could not allocate memory for bitmap buffer: %s\n", strerror(errno
) );
153 // Set default to black...
154 memset( vf
->priv
->bitmap
.u
, 128, width
*height
/4 );
155 memset( vf
->priv
->bitmap
.v
, 128, width
*height
/4 );
157 vf
->priv
->w
= vf
->priv
->x1
= width
;
158 vf
->priv
->h
= vf
->priv
->y1
= height
;
159 vf
->priv
->y2
= vf
->priv
->x2
= 0;
161 return vf_next_config(vf
, width
, height
, d_width
, d_height
, flags
, outfmt
);
165 uninit(struct vf_instance
*vf
)
168 free(vf
->priv
->bitmap
.y
);
169 free(vf
->priv
->bitmap
.u
);
170 free(vf
->priv
->bitmap
.v
);
171 free(vf
->priv
->bitmap
.a
);
172 free(vf
->priv
->bitmap
.oa
);
173 if (vf
->priv
->stream_fd
>= 0)
174 close(vf
->priv
->stream_fd
);
180 _read_cmd(int fd
, char *cmd
, char *args
) {
181 int done
=FALSE
, pos
=0;
185 if(! read( fd
, &tmp
, 1 ) ) return FALSE
;
186 if( (tmp
>='A' && tmp
<='Z') || (tmp
>='0' && tmp
<='9') )
188 else if(tmp
== ' ') {
192 else if(tmp
== '\n') {
204 if(! read( fd
, &tmp
, 1 ) ) return FALSE
;
205 if( (tmp
>= ' ') && (pos
<100) ) args
[pos
]=tmp
;
217 put_image(struct vf_instance
*vf
, mp_image_t
* mpi
, double pts
){
218 int buf_x
=0, buf_y
=0, buf_pos
=0;
220 int xpos
=0, ypos
=0, pos
=0;
221 unsigned char red
=0, green
=0, blue
=0;
225 dmpi
= vf_get_image(vf
->next
, mpi
->imgfmt
, MP_IMGTYPE_TEMP
,
226 MP_IMGFLAG_ACCEPT_STRIDE
| MP_IMGFLAG_PREFER_ALIGNED_STRIDE
,
229 memcpy_pic( dmpi
->planes
[0], mpi
->planes
[0], mpi
->width
, mpi
->height
, dmpi
->stride
[0], mpi
->stride
[0] );
230 memcpy_pic( dmpi
->planes
[1], mpi
->planes
[1], mpi
->chroma_width
, mpi
->chroma_height
, dmpi
->stride
[1], mpi
->stride
[1] );
231 memcpy_pic( dmpi
->planes
[2], mpi
->planes
[2], mpi
->chroma_width
, mpi
->chroma_height
, dmpi
->stride
[2], mpi
->stride
[2] );
233 if(vf
->priv
->stream_fd
>= 0) {
237 FD_SET( vf
->priv
->stream_fd
, &vf
->priv
->stream_fdset
);
238 tv
.tv_sec
=0; tv
.tv_usec
=0;
240 ready
= select( vf
->priv
->stream_fd
+1, &vf
->priv
->stream_fdset
, NULL
, NULL
, &tv
);
242 // We've got new data from the FIFO
244 char cmd
[20], args
[100];
245 int imgw
,imgh
,imgx
,imgy
,clear
,imgalpha
,pxsz
=1,command
;
246 unsigned char *buffer
= NULL
;
248 if(! _read_cmd( vf
->priv
->stream_fd
, cmd
, args
) ) {
249 mp_msg(MSGT_VFILTER
, MSGL_ERR
, "\nvf_bmovl: Error reading commands: %s\n\n", strerror(errno
));
252 mp_msg(MSGT_VFILTER
, MSGL_DBG2
, "\nDEBUG: Got: %s+%s\n", cmd
, args
);
255 if ( strncmp(cmd
,"RGBA32",6)==0 ) { pxsz
=4; command
= IMG_RGBA32
; }
256 else if( strncmp(cmd
,"ABGR32",6)==0 ) { pxsz
=4; command
= IMG_ABGR32
; }
257 else if( strncmp(cmd
,"RGB24" ,5)==0 ) { pxsz
=3; command
= IMG_RGB24
; }
258 else if( strncmp(cmd
,"BGR24" ,5)==0 ) { pxsz
=3; command
= IMG_BGR24
; }
259 else if( strncmp(cmd
,"CLEAR" ,5)==0 ) { pxsz
=1; command
= CMD_CLEAR
; }
260 else if( strncmp(cmd
,"ALPHA" ,5)==0 ) { pxsz
=1; command
= CMD_ALPHA
; }
261 else if( strncmp(cmd
,"OPAQUE",6)==0 ) vf
->priv
->opaque
=TRUE
;
262 else if( strncmp(cmd
,"SHOW", 4)==0 ) vf
->priv
->hidden
=FALSE
;
263 else if( strncmp(cmd
,"HIDE", 4)==0 ) vf
->priv
->hidden
=TRUE
;
264 else if( strncmp(cmd
,"FLUSH" ,5)==0 ) return vf_next_put_image(vf
, dmpi
, MP_NOPTS_VALUE
);
266 mp_msg(MSGT_VFILTER
, MSGL_WARN
, "\nvf_bmovl: Unknown command: '%s'. Ignoring.\n", cmd
);
267 return vf_next_put_image(vf
, dmpi
, MP_NOPTS_VALUE
);
270 if(command
== CMD_ALPHA
) {
271 sscanf( args
, "%d %d %d %d %d", &imgw
, &imgh
, &imgx
, &imgy
, &imgalpha
);
272 mp_msg(MSGT_VFILTER
, MSGL_DBG2
, "\nDEBUG: ALPHA: %d %d %d %d %d\n\n",
273 imgw
, imgh
, imgx
, imgy
, imgalpha
);
274 if(imgw
==0 && imgh
==0) vf
->priv
->opaque
=FALSE
;
277 if(command
& IS_RAWIMG
) {
278 sscanf( args
, "%d %d %d %d %d %d",
279 &imgw
, &imgh
, &imgx
, &imgy
, &imgalpha
, &clear
);
280 mp_msg(MSGT_VFILTER
, MSGL_DBG2
, "\nDEBUG: RAWIMG: %d %d %d %d %d %d\n\n",
281 imgw
, imgh
, imgx
, imgy
, imgalpha
, clear
);
283 buffer
= malloc(imgw
*imgh
*pxsz
);
285 mp_msg(MSGT_VFILTER
, MSGL_WARN
, "\nvf_bmovl: Couldn't allocate temporary buffer! Skipping...\n\n");
286 return vf_next_put_image(vf
, dmpi
, MP_NOPTS_VALUE
);
288 /* pipes/sockets might need multiple calls to read(): */
289 want
= (imgw
*imgh
*pxsz
);
291 while (have
< want
) {
292 got
= read( vf
->priv
->stream_fd
, buffer
+have
, want
-have
);
294 mp_msg(MSGT_VFILTER
, MSGL_WARN
, "\nvf_bmovl: premature EOF...\n\n");
298 mp_msg(MSGT_VFILTER
, MSGL_WARN
, "\nvf_bmovl: read error: %s\n\n", strerror(errno
));
303 mp_msg(MSGT_VFILTER
, MSGL_DBG2
, "Got %d bytes... (wanted %d)\n", have
, want
);
306 memset( vf
->priv
->bitmap
.y
, 0, vf
->priv
->w
*vf
->priv
->h
);
307 memset( vf
->priv
->bitmap
.u
, 128, vf
->priv
->w
*vf
->priv
->h
/4 );
308 memset( vf
->priv
->bitmap
.v
, 128, vf
->priv
->w
*vf
->priv
->h
/4 );
309 memset( vf
->priv
->bitmap
.a
, 0, vf
->priv
->w
*vf
->priv
->h
);
310 memset( vf
->priv
->bitmap
.oa
, 0, vf
->priv
->w
*vf
->priv
->h
);
311 vf
->priv
->x1
= dmpi
->width
;
312 vf
->priv
->y1
= dmpi
->height
;
313 vf
->priv
->x2
= vf
->priv
->y2
= 0;
315 // Define how much of our bitmap that contains graphics!
316 vf
->priv
->x1
= av_clip(imgx
, 0, vf
->priv
->x1
);
317 vf
->priv
->y1
= av_clip(imgy
, 0, vf
->priv
->y1
);
318 vf
->priv
->x2
= av_clip(imgx
+ imgw
, vf
->priv
->x2
, vf
->priv
->w
);
319 vf
->priv
->y2
= av_clip(imgy
+ imgh
, vf
->priv
->y2
, vf
->priv
->h
);
322 if( command
== CMD_CLEAR
) {
323 sscanf( args
, "%d %d %d %d", &imgw
, &imgh
, &imgx
, &imgy
);
324 mp_msg(MSGT_VFILTER
, MSGL_DBG2
, "\nDEBUG: CLEAR: %d %d %d %d\n\n", imgw
, imgh
, imgx
, imgy
);
326 for( ypos
=imgy
; (ypos
< (imgy
+imgh
)) && (ypos
< vf
->priv
->y2
) ; ypos
++ ) {
327 memset( vf
->priv
->bitmap
.y
+ (ypos
*vf
->priv
->w
) + imgx
, 0, imgw
);
328 memset( vf
->priv
->bitmap
.a
+ (ypos
*vf
->priv
->w
) + imgx
, 0, imgw
);
329 memset( vf
->priv
->bitmap
.oa
+ (ypos
*vf
->priv
->w
) + imgx
, 0, imgw
);
331 memset( vf
->priv
->bitmap
.u
+ ((ypos
/2)*dmpi
->stride
[1]) + (imgx
/2), 128, imgw
/2 );
332 memset( vf
->priv
->bitmap
.v
+ ((ypos
/2)*dmpi
->stride
[2]) + (imgx
/2), 128, imgw
/2 );
334 } // Recalculate area that contains graphics
335 if( (imgx
<= vf
->priv
->x1
) && ( (imgw
+imgx
) >= vf
->priv
->x2
) ) {
336 if( (imgy
<= vf
->priv
->y1
) && ( (imgy
+imgh
) >= vf
->priv
->y1
) )
337 vf
->priv
->y1
= imgy
+imgh
;
338 if( (imgy
<= vf
->priv
->y2
) && ( (imgy
+imgh
) >= vf
->priv
->y2
) )
341 if( (imgy
<= vf
->priv
->y1
) && ( (imgy
+imgh
) >= vf
->priv
->y2
) ) {
342 if( (imgx
<= vf
->priv
->x1
) && ( (imgx
+imgw
) >= vf
->priv
->x1
) )
343 vf
->priv
->x1
= imgx
+imgw
;
344 if( (imgx
<= vf
->priv
->x2
) && ( (imgx
+imgw
) >= vf
->priv
->x2
) )
347 return vf_next_put_image(vf
, dmpi
, MP_NOPTS_VALUE
);
350 for( buf_y
=0 ; (buf_y
< imgh
) && (buf_y
< (vf
->priv
->h
-imgy
)) ; buf_y
++ ) {
351 for( buf_x
=0 ; (buf_x
< (imgw
*pxsz
)) && (buf_x
< ((vf
->priv
->w
+imgx
)*pxsz
)) ; buf_x
+= pxsz
) {
352 if(command
& IS_RAWIMG
) buf_pos
= (buf_y
* imgw
* pxsz
) + buf_x
;
353 pos
= ((buf_y
+imgy
) * vf
->priv
->w
) + ((buf_x
/pxsz
)+imgx
);
357 red
= buffer
[buf_pos
+0];
358 green
= buffer
[buf_pos
+1];
359 blue
= buffer
[buf_pos
+2];
360 alpha
= buffer
[buf_pos
+3];
363 alpha
= buffer
[buf_pos
+0];
364 blue
= buffer
[buf_pos
+1];
365 green
= buffer
[buf_pos
+2];
366 red
= buffer
[buf_pos
+3];
369 red
= buffer
[buf_pos
+0];
370 green
= buffer
[buf_pos
+1];
371 blue
= buffer
[buf_pos
+2];
375 blue
= buffer
[buf_pos
+0];
376 green
= buffer
[buf_pos
+1];
377 red
= buffer
[buf_pos
+2];
381 vf
->priv
->bitmap
.a
[pos
] = INRANGE((vf
->priv
->bitmap
.oa
[pos
]+imgalpha
),0,255);
384 mp_msg(MSGT_VFILTER
, MSGL_ERR
, "vf_bmovl: Internal error!\n");
387 if( command
& IS_RAWIMG
) {
388 vf
->priv
->bitmap
.y
[pos
] = rgb2y(red
,green
,blue
);
389 vf
->priv
->bitmap
.oa
[pos
] = alpha
;
390 vf
->priv
->bitmap
.a
[pos
] = INRANGE((alpha
+imgalpha
),0,255);
391 if((buf_y
%2) && ((buf_x
/pxsz
)%2)) {
392 pos
= ( ((buf_y
+imgy
)/2) * dmpi
->stride
[1] ) + (((buf_x
/pxsz
)+imgx
)/2);
393 vf
->priv
->bitmap
.u
[pos
] = rgb2u(red
,green
,blue
);
394 vf
->priv
->bitmap
.v
[pos
] = rgb2v(red
,green
,blue
);
400 } else if(ready
< 0) {
401 mp_msg(MSGT_VFILTER
, MSGL_WARN
, "\nvf_bmovl: Error %d in fifo: %s\n\n", errno
, strerror(errno
));
405 if(vf
->priv
->hidden
) return vf_next_put_image(vf
, dmpi
, MP_NOPTS_VALUE
);
407 if(vf
->priv
->opaque
) { // Just copy buffer memory to screen
408 for( ypos
=vf
->priv
->y1
; ypos
< vf
->priv
->y2
; ypos
++ ) {
409 fast_memcpy( dmpi
->planes
[0] + (ypos
*dmpi
->stride
[0]) + vf
->priv
->x1
,
410 vf
->priv
->bitmap
.y
+ (ypos
*vf
->priv
->w
) + vf
->priv
->x1
,
411 vf
->priv
->x2
- vf
->priv
->x1
);
413 fast_memcpy( dmpi
->planes
[1] + ((ypos
/2)*dmpi
->stride
[1]) + (vf
->priv
->x1
/2),
414 vf
->priv
->bitmap
.u
+ (((ypos
/2)*(vf
->priv
->w
)/2)) + (vf
->priv
->x1
/2),
415 (vf
->priv
->x2
- vf
->priv
->x1
)/2 );
416 fast_memcpy( dmpi
->planes
[2] + ((ypos
/2)*dmpi
->stride
[2]) + (vf
->priv
->x1
/2),
417 vf
->priv
->bitmap
.v
+ (((ypos
/2)*(vf
->priv
->w
)/2)) + (vf
->priv
->x1
/2),
418 (vf
->priv
->x2
- vf
->priv
->x1
)/2 );
421 } else { // Blit the bitmap to the videoscreen, pixel for pixel
422 for( ypos
=vf
->priv
->y1
; ypos
< vf
->priv
->y2
; ypos
++ ) {
423 for ( xpos
=vf
->priv
->x1
; xpos
< vf
->priv
->x2
; xpos
++ ) {
424 pos
= (ypos
* dmpi
->stride
[0]) + xpos
;
426 alpha
= vf
->priv
->bitmap
.a
[pos
];
428 if (alpha
== 0) continue; // Completly transparent pixel
430 if (alpha
== 255) { // Opaque pixel
431 dmpi
->planes
[0][pos
] = vf
->priv
->bitmap
.y
[pos
];
432 if ((ypos
%2) && (xpos
%2)) {
433 pos
= ( (ypos
/2) * dmpi
->stride
[1] ) + (xpos
/2);
434 dmpi
->planes
[1][pos
] = vf
->priv
->bitmap
.u
[pos
];
435 dmpi
->planes
[2][pos
] = vf
->priv
->bitmap
.v
[pos
];
437 } else { // Alphablended pixel
438 dmpi
->planes
[0][pos
] =
439 ((255 - alpha
) * (int)dmpi
->planes
[0][pos
] +
440 alpha
* (int)vf
->priv
->bitmap
.y
[pos
]) >> 8;
442 if ((ypos
%2) && (xpos
%2)) {
443 pos
= ( (ypos
/2) * dmpi
->stride
[1] ) + (xpos
/2);
445 dmpi
->planes
[1][pos
] =
446 ((255 - alpha
) * (int)dmpi
->planes
[1][pos
] +
447 alpha
* (int)vf
->priv
->bitmap
.u
[pos
]) >> 8;
449 dmpi
->planes
[2][pos
] =
450 ((255 - alpha
) * (int)dmpi
->planes
[2][pos
] +
451 alpha
* (int)vf
->priv
->bitmap
.v
[pos
]) >> 8;
457 return vf_next_put_image(vf
, dmpi
, MP_NOPTS_VALUE
);
461 vf_open(vf_instance_t
*vf
, char *args
)
466 vf
->put_image
= put_image
;
467 vf
->query_format
= query_format
;
470 vf
->priv
= malloc(sizeof(struct vf_priv_s
));
472 if(!args
|| sscanf(args
, "%d:%d:%s", &vf
->priv
->hidden
, &vf
->priv
->opaque
, filename
) < 3 ) {
473 mp_msg(MSGT_VFILTER
, MSGL_ERR
, "vf_bmovl: Bad arguments!\n");
474 mp_msg(MSGT_VFILTER
, MSGL_ERR
, "vf_bmovl: Arguments are 'bool hidden:bool opaque:string fifo'\n");
478 vf
->priv
->stream_fd
= open(filename
, O_RDWR
);
479 if(vf
->priv
->stream_fd
>= 0) {
480 FD_ZERO( &vf
->priv
->stream_fdset
);
481 mp_msg(MSGT_VFILTER
, MSGL_INFO
, "vf_bmovl: Opened fifo %s as FD %d\n", filename
, vf
->priv
->stream_fd
);
483 mp_msg(MSGT_VFILTER
, MSGL_WARN
, "vf_bmovl: Error! Couldn't open FIFO %s: %s\n", filename
, strerror(errno
));
484 vf
->priv
->stream_fd
= -1;
490 const vf_info_t vf_info_bmovl
= {
491 "Read bitmaps from a FIFO and display them in window",