--- main.orig/libmpcodecs/vf_bmovl2.c 1969-12-31 19:00:00.000000000 -0500 +++ main/libmpcodecs/vf_bmovl2.c 2004-01-13 20:20:12.000000000 -0500 @@ -0,0 +1,756 @@ +/* vf_bmovl2.c - BitMap OVerLay Video Filter for MPlayer - + * Extra Tasty Crispy Version + * (C) 2004 Jason Tackaberry + * Licenced under the GNU General Public License + * + * Portions of code taken from vf_bmovl.c which is + * (C) 2002 Per Wigren + * + * Use MPlayer as a framebuffer to read bitmaps and commands from a FIFO + * and display them in the window. This is an enhancement of the original + * bmovl. + * + * See DOCS/tech/vf_bmovl2.txt for details. + */ + +#ifndef HAVE_NO_POSIX_SELECT + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mp_image.h" +#include "vf.h" +#include "img_format.h" +#include "../config.h" + +#include "../mp_msg.h" +#include "../libvo/fastmemcpy.h" +#include "../libvo/video_out.h" +#include "../input/input.h" +#include "../osdep/timer.h" + +#define FALSE 0 +#define TRUE !FALSE + +#define rgb2y(R,G,B) ( (( 263*R + 516*G + 100*B) >> 10) + 16 ) +#define rgb2u(R,G,B) ( ((-152*R - 298*G + 450*B) >> 10) + 128 ) +#define rgb2v(R,G,B) ( (( 450*R - 376*G - 73*B) >> 10) + 128 ) + +#define CMD_RAWIMG 0x100 +#define CMD_ALPHA 0x101 +#define CMD_VISIBLE 0x102 +#define CMD_MOVE 0x103 +#define CMD_ZINDEX 0x104 +#define CMD_DELETE 0x105 + +struct bmovl_image { + unsigned char *y, *u, *v, *a, *uva; + int left, top, w, h, zindex, alpha, visible; + char id[100]; + struct bmovl_image *next; +}; + +struct vf_priv_s { + struct bmovl_image *image; + int stream_fd; + char fifo_fname[PATH_MAX]; + int mpi_width, mpi_height; +}; + +/* Keep track of bmovl2 instances for two reasons: + 1. Image layers should be able to survive a loadfile, so when the + bmovl2 filter is initialized, we first check to see if we have + an existing filter listening on the specified fifo and use that + instead. + 2. When the movie is paused, we still want to be able to update the + overlays, so vf_bmovl2_pause_update() needs to be able to find + the bmovl2 instances. +*/ +vf_instance_t** vf_bmovl2 = NULL; +static struct vf_priv_s **vf_bmovl2_priv = NULL; +static mp_image_t* pause_mpi = NULL; +static int pause_state = 0; + +static int cmd_filter(mp_cmd_t* cmd, int paused, struct vf_priv_s *priv); +static int put_image(struct vf_instance_s* vf, mp_image_t* mpi); +static int handle_commands(struct vf_priv_s* priv); +static int handle_commands_and_put_image(struct vf_instance_s* vf, mp_image_t* mpi); + +static struct bmovl_image * +find_image_by_id(struct bmovl_image *img, char *id) +{ + while (img) { + if (!strcmp(img->id, id)) + return img; + img = img->next; + } + return 0; +} + +static struct bmovl_image * +remove_image(struct bmovl_image *first, struct bmovl_image *img) +{ + struct bmovl_image *tmp = first, *prev = 0; + while (tmp) { + if (tmp == img) { + if (prev) prev->next = tmp->next; + else first = tmp->next; + break; + } + prev = tmp; + tmp = prev->next; + } + return first; +} + +static struct bmovl_image * +insert_image_by_zindex(struct bmovl_image *first, struct bmovl_image *img) +{ + struct bmovl_image *tmp = first, *prev = 0; + if (!first) { + img->next = 0; + return img; + } + while (tmp) { + if (img->zindex <= tmp->zindex) { + img->next = tmp; + if (prev) prev->next = img; + else first = img; + break; + } else if (tmp->next == NULL) { + img->next = 0; + tmp->next = img; + break; + } + prev = tmp; + tmp = prev->next; + } + return first; +} +void +free_image_data(struct bmovl_image *img) +{ + if (!img) + return; + + free(img->y); + free(img->u); + free(img->v); + free(img->a); + free(img->uva); +} + +static inline void +copy_mpi(mp_image_t *dmpi, mp_image_t *mpi) +{ + if (mpi->flags&MP_IMGFLAG_PLANAR) { + memcpy_pic(dmpi->planes[0], mpi->planes[0], mpi->w, mpi->h, + dmpi->stride[0],mpi->stride[0]); + memcpy_pic(dmpi->planes[1], mpi->planes[1], mpi->chroma_width, + mpi->chroma_height, dmpi->stride[1], mpi->stride[1]); + memcpy_pic(dmpi->planes[2], mpi->planes[2], mpi->chroma_width, + mpi->chroma_height, dmpi->stride[2], mpi->stride[2]); + } else { + memcpy_pic(dmpi->planes[0], mpi->planes[0], mpi->w*(dmpi->bpp/8), + mpi->h, dmpi->stride[0], mpi->stride[0]); + } +} + +void +vf_bmovl2_pause_update(vo_functions_t *video_out) +{ + int i, nfilters = 0, do_put_image = 0; + struct vf_instance_s *p; + + if (!vf_bmovl2) + return; + + for (i = 0, p = vf_bmovl2[i]; p != NULL; p = vf_bmovl2[++i]) { + if (handle_commands(p->priv)) + do_put_image = 1; + nfilters++; + } + + if (do_put_image) { + put_image(vf_bmovl2[nfilters-1], pause_mpi); + video_out->flip_page(); + } +} + + +static int +query_format(struct vf_instance_s* vf, unsigned int fmt) +{ + if (fmt == IMGFMT_YV12) + return VFCAP_CSP_SUPPORTED; + return 0; +} + + +static int +config(struct vf_instance_s* vf, int width, int height, + int d_width, int d_height, unsigned int flags, unsigned int outfmt) +{ + vf->priv->mpi_width = width; + vf->priv->mpi_height = height; + return vf_next_config(vf, width, height, d_width, d_height, flags, outfmt); +} + +static int +check_fifo(int fd) +{ + struct timeval tv; + fd_set fdset; + + FD_ZERO(&fdset); + FD_SET(fd, &fdset); + tv.tv_sec = tv.tv_usec = 0; + return select(fd + 1, &fdset, NULL, NULL, &tv); +} + + +static int +read_cmd(int fd, char *cmd, char *args) +{ + char *p = cmd, *start = cmd, maxlen = 20; + *args = 0; + while (read(fd, p, 1)) { + if (*p == ' ' && start == cmd) { + *p = 0; + p = start = args; + maxlen = 100; + } + else if (*p == '\n' || p - start > maxlen) { *p = 0; return TRUE; } + else p++; + } + return FALSE; +} + +static int +handle_commands(struct vf_priv_s* priv) +{ + int atom_level = 0, res, command; + char cmd[20], args[100]; + + do { + res = check_fifo(priv->stream_fd); + if (res == 0) { + if (atom_level > 0) + usec_sleep(1000); + else + return FALSE; + } else if(res < 0) { + mp_msg(MSGT_VFILTER, MSGL_WARN, "\nvf_bmovl2: Error %d in fifo: %s\n\n", errno, strerror(errno)); + return FALSE; + } + + // If we're here it means there's a command in the fifo + if(!read_cmd(priv->stream_fd, cmd, args)) { + mp_msg(MSGT_VFILTER, MSGL_ERR, "\nvf_bmovl2: Error reading commands: %s\n\n", strerror(errno)); + return FALSE; + } + + command = 0; + if (strncmp(cmd, "ZINDEX", 6) == 0) command = CMD_ZINDEX; + if (strncmp(cmd, "MOVE", 4) == 0) command = CMD_MOVE; + if (strncmp(cmd, "ALPHA", 5) == 0) command = CMD_ALPHA; + if (strncmp(cmd, "VISIBLE", 7) == 0) command = CMD_VISIBLE; + if (strncmp(cmd, "DELETE", 6) == 0) command = CMD_DELETE; + if (strncmp(cmd, "RAWIMG", 6) == 0) command = CMD_RAWIMG; + if (strncmp(cmd, "ATOM", 4) == 0) atom_level++; + if (strncmp(cmd, "ENDATOM", 7) == 0) { atom_level--; continue; } + + switch (command) { + + case CMD_ZINDEX: { // ZINDEX img_id zindex + int zindex; + char id[101]; + struct bmovl_image *img; + sscanf(args, "%100s %d", id, &zindex); + img = find_image_by_id(priv->image, id); + if (!img) + break; + img->zindex = zindex; + if (priv->image == NULL || priv->image->next == NULL) + break; + priv->image = remove_image(priv->image, img); + priv->image = insert_image_by_zindex(priv->image, img); + break; + } + + case CMD_MOVE: { // MOVE img_id x y + char id[101]; + int x, y; + struct bmovl_image *img; + sscanf(args, "%100s %d %d", id, &x, &y); + img = find_image_by_id(priv->image, id); + if (!img) + break; + img->left = x; + img->top = y; + break; + } + + case CMD_ALPHA: { // ALPHA img_id alpha + int alpha; + char id[101]; + struct bmovl_image *img; + sscanf(args, "%100s %d", id, &alpha); + img = find_image_by_id(priv->image, id); + if (!img) + break; + img->alpha = alpha; + break; + } + + case CMD_VISIBLE: { // VISIBLE img_id flag + char id[101]; + int visible; + struct bmovl_image *img; + sscanf(args, "%100s %d", id, &visible); + img = find_image_by_id(priv->image, id); + if (!img) + break; + img->visible = visible; + break; + } + + case CMD_DELETE: { // DELETE img_id + char id[101]; + struct bmovl_image *img; + sscanf(args, "%100s", id); + img = find_image_by_id(priv->image, id); + if (!img) + break; + priv->image = remove_image(priv->image, img); + free_image_data(img); + free(img); + break; + } + + case CMD_RAWIMG: { // RAWIMG img_id format w h reset + char format[5], *imgbuf, id[101]; + int w = 0, h = 0, reset = 0, bpp, i, + r_pos, g_pos, b_pos, a_pos, bytes; + struct bmovl_image *img; + int buf_x, buf_y; + unsigned char uva; + + sscanf(args, "%100s %4s %d %d %d", id, format, &w, &h, &reset); + bpp = strlen(format); + if (bpp != 3 && bpp != 4) { + mp_msg(MSGT_VFILTER, MSGL_ERR, "\nvf_bmovl2: CMD_RAWIMG: invalid image format (%s)\n", format); + break; + } + imgbuf = malloc(w * h * bpp); + if(!imgbuf) { + mp_msg(MSGT_VFILTER, MSGL_WARN, "\nvf_bmovl2: CMD_RAWIMG: couldn't allocate temp buffer (%d bytes)\n", w * h * bpp); + break; + } + bytes = read(priv->stream_fd, imgbuf, w * h * bpp); + if (bytes != w * h * bpp) { + mp_msg(MSGT_VFILTER, MSGL_WARN, "\nvf_bmovl2: CMD_RAWIMG: image too short (got %d bytes)\n", bytes); + free(imgbuf); + break; + } + + for (i = 0; i < bpp; i++) { + switch (format[i]) { + case 'R': r_pos = i; break; + case 'G': g_pos = i; break; + case 'B': b_pos = i; break; + case 'A': a_pos = i; break; + } + } + + img = find_image_by_id(priv->image, id); + if (img) { + // Free memory of an existing image + free_image_data(img); + } else { + img = (struct bmovl_image *)malloc(sizeof(struct bmovl_image)); + memset(img, 0, sizeof(struct bmovl_image)); + img->visible = TRUE; + img->alpha = 255; + strcpy(img->id, id); + // Insert this image in the list ordered by zindex + priv->image = insert_image_by_zindex(priv->image, img); + } + // Allocate memory for new image + img->y = malloc(w * h); + img->u = malloc(w * h / 4); + img->v = malloc(w * h / 4); + img->a = malloc(w * h); + img->uva = malloc(w * h / 4); + img->w = w; + img->h = h; + + // Convert the image to YUV + for (buf_y = 0; buf_y < h; buf_y++) { + for (buf_x = 0; buf_x < w * bpp; buf_x += bpp) { + int buf_pos = (buf_y * w * bpp) + buf_x, + img_pos = (buf_y * w) + (buf_x / bpp); + unsigned char r = imgbuf[buf_pos + r_pos], + g = imgbuf[buf_pos + g_pos], + b = imgbuf[buf_pos + b_pos], + a = (bpp == 4) ? imgbuf[buf_pos + a_pos] : 0xFF; + + img->y[img_pos] = rgb2y(r, g, b); + img->a[img_pos] = a; + + /* Given pixels: + * A B + * C D + * sample alpha for U/V between A and C. (Thanks Billy.) + */ + if (!(buf_y & 1) && !((buf_x / bpp) & 1)) + uva = a; + else if ((buf_y & 1) && !((buf_x / bpp) & 1)) + uva = (uva + a) >> 1; + else if ((buf_y & 1) && ((buf_x / bpp) & 1)) { + img_pos = ((buf_y >> 1) * (w >> 1)) + (buf_x / bpp >> 1); + img->u[img_pos] = rgb2u(r, g, b); + img->v[img_pos] = rgb2v(r, g, b); + img->uva[img_pos] = (bpp == 4) ? uva : 255; + } + } + } + if (reset) { + img->visible = TRUE; + img->alpha = 255; + img->left = img->top = 0; + if (img->zindex != 0) { + img->zindex = 0; + // Reinsert this image since z-index has been reset + priv->image = remove_image(priv->image, img); + priv->image = insert_image_by_zindex(priv->image, img); + } + } + free(imgbuf); + break; + } + } + } while (atom_level > 0); + return 1; +} + +static inline char +multiply_alpha(unsigned char r, unsigned char a) +{ + int temp = (r * a) + 0x80; + return ((temp + (temp >> 8)) >> 8); +} + +static inline char +blend_byte(unsigned char dst, unsigned char src, unsigned char alpha, + int layer_alpha) +{ + unsigned char a = (layer_alpha != 255) ? alpha * layer_alpha >> 8 : alpha; + return (multiply_alpha(dst, 255 - a) + multiply_alpha(src, a)); +} + +static unsigned char layer_alpha_array[8] = {0,0,0,0,0,0,0,0}; + +static inline void +blend_byte_8(unsigned char *dst, unsigned char *src, + unsigned char *alpha, int layer_alpha) +{ +#ifndef HAVE_MMX + int i; + for (i = 0; i < 8; i++) { + dst[i] = blend_byte(dst[i], src[i], alpha[i], layer_alpha); + } +#else + static unsigned char d[8] = {255,255,255,255, 255,255,255,255}; + static unsigned char round[8] = {0x80, 0, 0x80, 0, 0x80, 0, 0x80, 0}; + + // Here's proof that infinite monkeys really can write Shakespeare. + asm volatile( + "movq %0, %%mm0\n\t" // %mm0 = mpi + "movq %%mm0, %%mm1\n\t" // %mm1 = mpi + "movq %2, %%mm5\n\t" // %mm5 = alpha + "movq %3, %%mm2\n\t" // %mm2 = 255's + "movq %5, %%mm4\n\t" // %mm4 = round + + "pxor %%mm7, %%mm7\n\t" // zero out %mm7 + + // Modify alpha from image with layer alpha + "movl %6, %%eax\n\t" // %eax = layer alpha + "cmpl $255, %%eax\n\t" // don't apply layer alpha if it's 100% opaque + "je 42f\n\t" + "movq %%mm5, %%mm6\n\t" // %mm6 = %mm5 = alpha + "punpcklbw %%mm7, %%mm5\n\t" // %mm5 = low dword of alpha + "punpckhbw %%mm7, %%mm6\n\t" // %mm6 = hi dword of alpha + "movq %4, %%mm3\n\t" // %mm3 = layer alpha + "pmullw %%mm3, %%mm5\n\t" // alpha * layer_alpha + "pmullw %%mm3, %%mm6\n\t" + "psrlw $8, %%mm5\n\t" // Divide by 256 + "psrlw $8, %%mm6\n\t" + "packuswb %%mm6, %%mm5\n\t" // Pack back into %mm5 + "42: \n\t" + + // Do (255-alpha) * mpi + "psubw %%mm5, %%mm2\n\t" // %mm2 = 255 - alpha + "punpcklbw %%mm7, %%mm0\n\t" // %mm0 = low dword of mpi + "punpckhbw %%mm7, %%mm1\n\t" // %mm1 = hi dword of mpi + "movq %%mm2, %%mm3\n\t" + "punpcklbw %%mm7, %%mm2\n\t" // %mm0 = low dword of 255-a + "punpckhbw %%mm7, %%mm3\n\t" // %mm1 = hi dword of 255-a + "pmullw %%mm2, %%mm0\n\t" // (255-a) * mpi = (r*a) + "pmullw %%mm3, %%mm1\n\t" + // approximate division by 255 + "paddw %%mm4, %%mm0\n\t" // (r*a) + 0x80 + "paddw %%mm4, %%mm1\n\t" + "movq %%mm0, %%mm2\n\t" // temp = (r*a) + 0x80 + "movq %%mm1, %%mm3\n\t" + "psrlw $8, %%mm0\n\t" // temp >> 8 + "psrlw $8, %%mm1\n\t" + "paddw %%mm2, %%mm0\n\t" // temp + (temp >> 8) + "paddw %%mm3, %%mm1\n\t" + "psrlw $8, %%mm0\n\t" // (temp+(temp>>8))>>8 + "psrlw $8, %%mm1\n\t" + + // Now do alpha*img -- can't touch %mm0 and %mm1 + "movq %1, %%mm2\n\t" // %mm2 = src image + "movq %%mm2, %%mm3\n\t" // %mm3 = src image + "punpcklbw %%mm7, %%mm2\n\t" // %mm2 = low dword of srcimg + "punpckhbw %%mm7, %%mm3\n\t" // %mm3 = hi dword of srcimg + "movq %%mm5, %%mm6\n\t" + "punpcklbw %%mm7, %%mm5\n\t" // %mm5 = low dword of alpha + "punpckhbw %%mm7, %%mm6\n\t" // %mm6 = hi dword of alpha + "pmullw %%mm5, %%mm2\n\t" // alpha * srcimg = (r * a) + "pmullw %%mm6, %%mm3\n\t" + // approximate division by 255 + "paddw %%mm4, %%mm2\n\t" // (r*a) + 0x80 + "paddw %%mm4, %%mm3\n\t" + "movq %%mm2, %%mm4\n\t" // temp = (r*a) + 0x80 + "movq %%mm3, %%mm5\n\t" + "psrlw $8, %%mm2\n\t" // temp >> 8 + "psrlw $8, %%mm3\n\t" + "paddw %%mm4, %%mm2\n\t" // temp + (temp >> 8) + "paddw %%mm5, %%mm3\n\t" + "psrlw $8, %%mm2\n\t" // (temp+(temp>>8))>>8 + "psrlw $8, %%mm3\n\t" + + // Add the two together + "paddw %%mm2, %%mm0\n\t" + "paddw %%mm3, %%mm1\n\t" + // Pack into bytes + "packuswb %%mm1, %%mm0\n\t" + "movq %%mm0, %0\n\t" + "emms\n\t" + :: "m" (dst[0]), "m" (src[0]), "m" (alpha[0]), "m" (d[0]), + "m" (layer_alpha_array[0]), "m" (round[0]), "m" (layer_alpha) : "%eax"); +#endif +} + +#define check_bounds() \ + if (xpos + left >= vf->priv->mpi_width) break; \ + if (xpos + left < 0) continue; \ + if (ypos + top >= vf->priv->mpi_height || xpos + left >= vf->priv->mpi_width || \ + ypos + top < 0 || xpos + left < 0) continue; + +static int +put_image(struct vf_instance_s* vf, mp_image_t* mpi) +{ + mp_image_t *dmpi = mpi; + int ypos, xpos, top, left; + struct bmovl_image *img; + + // Remember last mp image when the video gets paused. + if (pause_state == 1) { + pause_state = 2; + pause_mpi = vf_get_image(vf, mpi->imgfmt, MP_IMGTYPE_IP, + MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_PREFER_ALIGNED_STRIDE, + mpi->w, mpi->h); + copy_mpi(pause_mpi, mpi); + } + + for (img = vf->priv->image; img != NULL; img = img->next) { + if (img->visible == FALSE || img->alpha == 0) + continue; + + // Don't bother if the image isn't even on screen + if (img->top >= vf->priv->mpi_height || + img->left >= vf->priv->mpi_width || + img->left + img->w < 0 || img->top + img->h < 0) + continue; + + // Only create the dmpi if we need to. + if (dmpi == mpi) { + dmpi = vf_get_image(vf->next, mpi->imgfmt, MP_IMGTYPE_TEMP, + MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_PREFER_ALIGNED_STRIDE, + mpi->w, mpi->h); + copy_mpi(dmpi, mpi); + } + + top = img->top; + left = img->left; +#ifdef HAVE_MMX + // Set this up here rather than in the inner loop -- it shaves about + // 2% CPU usage off my Athlon 1400, surprisingly. + layer_alpha_array[0] = layer_alpha_array[2] = layer_alpha_array[4] = + layer_alpha_array[6] = img->alpha; +#endif + + // Blend Y channel + for(ypos = 0; ypos < img->h; ypos++) { + if (ypos + top >= vf->priv->mpi_height) break; + if (ypos + top < 0) continue; + for (xpos = 0; xpos < img->w; xpos+=1) { + int img_pos, alpha, mpi_pos; + + check_bounds(); + + img_pos = ypos * (img->w) + xpos; + mpi_pos = (ypos + top) * dmpi->stride[0] + (xpos + left); + + if (xpos + 8 >= img->w || xpos + left + 8 >= vf->priv->mpi_width) { + // Less than 8 bytes left, so do them one at a time + alpha = img->a[img_pos]; + if (alpha == 0) + continue; + dmpi->planes[0][mpi_pos] = blend_byte(dmpi->planes[0][mpi_pos], img->y[img_pos], + alpha, img->alpha); + } else { + // Do a chunk of 8 bytes + blend_byte_8(&dmpi->planes[0][mpi_pos], &img->y[img_pos], + &img->a[img_pos], img->alpha); + xpos += 7; + + } + } // xpos + } // ypos + + // Blend U/V channels + for(ypos = 1; ypos < img->h; ypos+=2) { + if (ypos + top >= vf->priv->mpi_height) break; + if (ypos + top < 0) continue; + for (xpos = 1; xpos < img->w; xpos+=2) { + int img_pos, alpha, mpi_pos; + + check_bounds(); + + img_pos = (ypos >> 1) * (img->w >> 1) + (xpos >> 1); + mpi_pos = ((ypos + top) >> 1) * dmpi->stride[1] + ((xpos + left) >> 1); + if (xpos + 16 >= img->w || xpos + left + 16 >= vf->priv->mpi_width) { + // Less than 8 bytes left, so do them one at a time + alpha = img->uva[img_pos]; + dmpi->planes[1][mpi_pos] = blend_byte(dmpi->planes[1][mpi_pos], img->u[img_pos], + alpha, img->alpha); + dmpi->planes[2][mpi_pos] = blend_byte(dmpi->planes[2][mpi_pos], img->v[img_pos], + alpha, img->alpha); + } else{ + // Do a chunk of 8 bytes + blend_byte_8(&dmpi->planes[1][mpi_pos], &img->u[img_pos], + &img->uva[img_pos], img->alpha); + blend_byte_8(&dmpi->planes[2][mpi_pos], &img->v[img_pos], + &img->uva[img_pos], img->alpha); + xpos += 14; + } + + } // xpos + } // ypos + + } + return vf_next_put_image(vf, dmpi); +} + +static int +handle_commands_and_put_image(struct vf_instance_s* vf, mp_image_t* mpi) +{ + handle_commands(vf->priv); + return put_image(vf, mpi); +} + +static void ** +grow_array(void **old_array, int old_size, int diff) +{ + void **tmp = (void **)malloc(sizeof(void *) * (old_size + diff)); + memset(tmp, 0, sizeof(void *) * (old_size + diff)); + memmove(tmp, old_array, sizeof(void *) * old_size); + if (old_array) + free(old_array); + return tmp; +} + +static int +vf_open(vf_instance_t* vf, char* args) +{ + char filename[PATH_MAX]; + int nfilters, i = 0; + + vf->config = config; + vf->put_image = handle_commands_and_put_image; + vf->query_format = query_format; + vf->uninit = NULL; // persistent + + if(!args || sscanf(args, "%s", filename) < 1 ) { + mp_msg(MSGT_VFILTER, MSGL_ERR, "vf_bmovl2: missing fifo filename argument.\n"); + return FALSE; + } + + /* Check to see if we've already initialized a filter with this fifo. If + * we have, then we reuse the private data, which allows image layers to + * survive a loadfile or a loop. + */ + if (vf_bmovl2_priv) { + struct vf_priv_s *p; + for (i = 0, p = vf_bmovl2_priv[i]; p != NULL; p = vf_bmovl2_priv[++i]) { + if (!strcmp(p->fifo_fname, filename)) { + vf->priv = p; + vf_bmovl2[i] = vf; + return TRUE; + } + } + } + + // New filter, so create and initialize the private data + nfilters = i; + vf->priv = malloc(sizeof(struct vf_priv_s)); + vf->priv->image = 0; + vf->priv->stream_fd = open(filename, O_RDWR); + strcpy(vf->priv->fifo_fname, filename); + if (vf->priv->stream_fd < 0) + mp_msg(MSGT_VFILTER, MSGL_WARN, "vf_bmovl2: Error! Couldn't open FIFO %s: %s\n", + filename, strerror(errno)); + + mp_input_add_cmd_filter((mp_input_cmd_filter)cmd_filter, vf->priv); + + // Grow the arrays and keep track of the private data, as well as the instance + // structs, used for handling the pause loop. + vf_bmovl2_priv = (struct vf_priv_s **)grow_array((void **)vf_bmovl2_priv, nfilters, 2); + vf_bmovl2_priv[nfilters] = vf->priv; + vf_bmovl2 = (vf_instance_t **)grow_array((void **)vf_bmovl2, nfilters, 2); + vf_bmovl2[nfilters] = vf; + return TRUE; +} + +static int +cmd_filter(mp_cmd_t* cmd, int paused, struct vf_priv_s * priv) +{ + // Track the pause state. When the video gets paused, the next + // iteration of put_image will grab the mpi and save it. + pause_state = !paused; + return 0; +} + +vf_info_t vf_info_bmovl2 = { + "Read and manipulate bitmapped images from a FIFO, " + "overlaying them on the video window.", + "bmovl2", + "Jason Tackaberry", + "", + vf_open, + NULL +}; + +#endif +// vim: ts=3 --- main.orig/mplayer.c 2004-01-10 06:23:42.000000000 -0500 +++ main/mplayer.c 2004-01-11 15:15:18.000000000 -0500 @@ -334,6 +334,9 @@ static char* menu_root = "main"; #endif +#ifndef HAVE_NO_POSIX_SELECT +extern void vf_bmovl2_pause_update(vo_functions_t*); +#endif #ifdef HAVE_RTC static int nortc; @@ -2420,7 +2423,7 @@ if (audio_out && sh_audio) audio_out->pause(); // pause audio, keep data if possible - while( (cmd = mp_input_get_cmd(20,1)) == NULL) { + while( (cmd = mp_input_get_cmd(3,1)) == NULL) { if(sh_video && video_out && vo_config_count) video_out->check_events(); #ifdef HAVE_NEW_GUI if(use_gui){ @@ -2433,7 +2436,10 @@ if(vf_menu) vf_menu_pause_update(vf_menu); #endif - usec_sleep(20000); +#ifndef HAVE_NO_POSIX_SELECT + vf_bmovl2_pause_update(video_out); +#endif + usec_sleep(5000); } mp_cmd_free(cmd); osd_function=OSD_PLAY; --- main.orig/libmpcodecs/vf.c 2003-12-19 17:15:36.000000000 -0500 +++ main/libmpcodecs/vf.c 2004-01-10 11:52:19.000000000 -0500 @@ -23,6 +23,7 @@ extern vf_info_t vf_info_rectangle; #ifndef HAVE_NO_POSIX_SELECT extern vf_info_t vf_info_bmovl; +extern vf_info_t vf_info_bmovl2; #endif extern vf_info_t vf_info_crop; extern vf_info_t vf_info_expand; @@ -89,6 +90,7 @@ &vf_info_rectangle, #ifndef HAVE_NO_POSIX_SELECT &vf_info_bmovl, + &vf_info_bmovl2, #endif &vf_info_crop, &vf_info_expand, --- main.orig/libmpcodecs/Makefile 2003-12-23 16:04:50.000000000 -0500 +++ main/libmpcodecs/Makefile 2004-01-10 11:52:27.000000000 -0500 @@ -14,7 +14,7 @@ VIDEO_SRCS_OPT=vd_realvid.c vd_ffmpeg.c vd_dshow.c vd_dmo.c vd_vfw.c vd_vfwex.c vd_odivx.c vd_divx4.c vd_zrmjpeg.c vd_xanim.c vd_xvid.c vd_xvid4.c vd_libdv.c vd_qtvideo.c vd_theora.c VIDEO_SRCS=dec_video.c vd.c $(VIDEO_SRCS_NAT) $(VIDEO_SRCS_LIB) $(VIDEO_SRCS_OPT) -VFILTER_SRCS=vf.c vf_vo.c vf_crop.c vf_expand.c vf_scale.c vf_format.c vf_yuy2.c vf_flip.c vf_rgb2bgr.c vf_rotate.c vf_mirror.c vf_palette.c vf_lavc.c vf_dvbscale.c vf_cropdetect.c vf_test.c vf_noise.c vf_yvu9.c vf_rectangle.c vf_lavcdeint.c vf_eq.c vf_eq2.c vf_halfpack.c vf_dint.c vf_1bpp.c vf_bmovl.c vf_2xsai.c vf_unsharp.c vf_swapuv.c vf_il.c vf_boxblur.c vf_sab.c vf_smartblur.c vf_perspective.c vf_down3dright.c vf_field.c vf_denoise3d.c vf_hqdn3d.c vf_detc.c vf_telecine.c vf_tfields.c vf_ivtc.c vf_ilpack.c vf_dsize.c vf_decimate.c vf_softpulldown.c vf_tinterlace.c vf_pullup.c pullup.c vf_framestep.c vf_tile.c vf_delogo.c vf_fil.c vf_hue.c vf_spp.c vf_yuvcsp.c vf_filmdint.c +VFILTER_SRCS=vf.c vf_vo.c vf_crop.c vf_expand.c vf_scale.c vf_format.c vf_yuy2.c vf_flip.c vf_rgb2bgr.c vf_rotate.c vf_mirror.c vf_palette.c vf_lavc.c vf_dvbscale.c vf_cropdetect.c vf_test.c vf_noise.c vf_yvu9.c vf_rectangle.c vf_lavcdeint.c vf_eq.c vf_eq2.c vf_halfpack.c vf_dint.c vf_1bpp.c vf_bmovl.c vf_bmovl2.c vf_2xsai.c vf_unsharp.c vf_swapuv.c vf_il.c vf_boxblur.c vf_sab.c vf_smartblur.c vf_perspective.c vf_down3dright.c vf_field.c vf_denoise3d.c vf_hqdn3d.c vf_detc.c vf_telecine.c vf_tfields.c vf_ivtc.c vf_ilpack.c vf_dsize.c vf_decimate.c vf_softpulldown.c vf_tinterlace.c vf_pullup.c pullup.c vf_framestep.c vf_tile.c vf_delogo.c vf_fil.c vf_hue.c vf_spp.c vf_yuvcsp.c vf_filmdint.c ifeq ($(HAVE_FFPOSTPROCESS),yes) VFILTER_SRCS += vf_pp.c endif --- main.orig/DOCS/tech/vf_bmovl2.txt 1969-12-31 19:00:00.000000000 -0500 +++ main/DOCS/tech/vf_bmovl2.txt 2004-01-11 17:20:03.000000000 -0500 @@ -0,0 +1,147 @@ +vf_bmovl2 - BitMap OVerLay Video Filter -- version 2 +==================================================== + +Introduction +------------ + +bmovl2 is an MMX-optimized filter that allows you to feed any number of +images (bitmaps) to MPlayer via a fifo, and instruct MPlayer how to +render these images over top the video window. Each image sits on its +own layer, which you may manipulate using simple commands, such as +moving the image on the layer, changing the layer's z-index, toggling +visibility, or adjusting the layer's alpha value. + +This filter takes one argument: the filename of the fifo from which +to read commands. For example: + + mplayer video.avi -vf bmovl2=/tmp/mplayer.fifo + +Each layer is referenced by an arbitrary string identifier of up to 100 +characters. Images are sent to MPlayer via the fifo with the designated +identifer and placed on a new layer in the foreground. Subsequent +commands to manipulate layers take the layer's id as an argument. + +There is a one-to-one mapping between images and layers. You may +change the order (z-index) of layers, effectively moving an image in +front of or behind another. You may chain bmovl2 filters together. +Instances of bmovl2 filters are independent, therefore layers of +one instance will always be higher (over top) layers of previous +instances in the filter chain. + +bmovl2 allows you to group multiple commands into a single atomic +operation, which are guaranteed to be evaluated and executed before +the next frame. This allows you to smoothly perform transition +effects, such as a window sliding across the screen and fading in +or out. + +bmovl2 continues to read from the fifo even when the movie is paused. +This allows for an externally controlled menu system that is accessible +in MPlayer at all times. + + +Command Protocol +---------------- + +The protocol used to control bmovl2 via the fifo is fairly straight- +forward and defined below. Each command specification takes the form: + + COMMAND arg1 arg2 ... + +Where COMMAND is the case-sensitive name of the command, followed by +its arguments, followed by single new-line (\n). Each argument is +separated by a single space, and all arguments for a given command +are mandatory. Arguments are also case-sensitive. + +The commands are: + +RAWIMG id format w h reset + - Stores an image in a new layer with the given id. Format can + be any combination of either RGBA or RGB. For example, these + are all valid formats: RGBA, RBGA, ARGB, ABGR, RGB, GBR, etc. w + and h are the width and height of the image. reset specifies + whether or not to reset the other data associated with this layer + id if a layer with this id already exists. These values are: x and + y coordinates, alpha level, visibility, and z-index. If reset is + 1, these values are reset, otherwise they are unchanged. + - New layers are initialized with these defaults: visible, located at + coordinates (0,0), full alpha (255), and zindex of 0. + - This command is followed by w*h*bpp bytes of data. If format is a + combination of letters RGB, then bpp is 3; if format is a combination + of RGBA, bpp is 4. + +VISIBLE id visibility + - Sets visibility of the specified layer referenced by id. Visibility + is 0 to hide, or 1 to show. + +MOVE id x y + - Move the layer with id to coordinates x, y + +ALPHA id alpha + - Sets the transparency of the layer to alpha, where 0 <= alpha + <= 255, 0 being fully transparent and 255 being fully opaque. + This value is multiplied with the alpha channel of each individual + pixel in the image. So if a pixel has an alpha of 100 and the + layer has an alpha of 128 (as set by this command), the resulting + pixel will have an alpha of 50. + - Specifying an alpha of 0 is functionally equivalent to setting + its visibility to 0, except that toggling visibility will + preserve the layer's alpha level. + +DELETE id + - Delete the layer referenced by id and free all memory allocated by + the image on that layer. + +ZINDEX id zindex + - Sets the z-index of the specified layer. The z-index is an + arbitrary integer. The lower the z-index, the closer to the + background (i.e. movie) the layer is displayed. Z-index can be + negative, so a z-index of -1 will always appear underneath new + layers. New layers are initialized with a z-index of 0. + +ATOM + - Indicates that the operations that follow until an ENDATOM command + is reached are atomic -- that is, they will be all be + evaluated before the next frame is drawn. This allows you to, for + example, move a layer to a new position and change its alpha + channel in between frames. Mplayer waits until it receives + ENDATOM, so make sure there is little delay between ATOM and + ENDATOM. (That is, it must be less than 1/fps of a second where + fps is the frame rate of the video. This of course ignores + the time MPlayer requires to render the frame.) + +ENDATOM + - End the list of operations and render the frame (or more + accurately, pass control to the next video filter). + + +Example +------- + +Here is a simple example in Python that uses the Python Imaging Library +(PIL, available at http://www.pythonware.com/products/pil/). + +Assume you have created a fifo in /tmp/mplayer.fifo, and have called +MPlayer like so: + + mplayer movie.avi -vf bmovl2=/tmp/mplayer.fifo + +This code will load the image specified on the command line, and send it +to MPlayer over the fifo, causing it to render at coordinates (50,50) +with an overall alpha value of 25% (or 64). Hit the enter key, and the +image (with the id "foobar") will get relocated to (200,200). + + import sys, Image, os + + fifo = os.open("/tmp/mplayer.fifo", os.O_RDWR, os.O_NONBLOCK) + img = Image.open(sys.argv[1]) + data = "ATOM\n" + data += "RAWIMG foobar %s %d %d 1\n" % (img.mode, img.size[0], img.size[1]) + data += img.tostring() + data += "MOVE foobar 50 50\n" + data += "ALPHA foobar 64\n" + data += "ENDATOM\n" + os.write(fifo, data) + + print "Hit enter, and the image will move to (200,200)" + sys.stdin.readline() + os.write(fifo, "MOVE foobar 200 200\n") --- main.orig/DOCS/man/en/mplayer.1 2004-01-08 22:36:40.000000000 -0500 +++ main/DOCS/man/en/mplayer.1 2004-01-11 16:59:34.000000000 -0500 @@ -3206,6 +3206,14 @@ .RE .PD 1 .TP +.B bmovl2= +bmovl2 is an MMX-optimized filter that allows you to feed any number of +images (bitmaps) to MPlayer via a fifo, and instruct MPlayer how to render +these images over top the video window in real-time. + +For information about the command protocol, which is an enhancement of the +original bmovl, read DOCS/tech/vf_bmovl2.txt +.TP .B framestep=I|[i]step Renders only every nth frame or every Intra (key) frame.