[vdr] [ANNOUNCE] vdr-xine-0.7.1 plugin

mike lewis lachlanlewis at gmail.com
Fri Mar 4 15:29:42 CET 2005


> OK,I think I bit off more that I can chew here ;-).  I'mno coder lets
> face it.  I've read through the expand plugin and it does not have an
> existing event structure.  So I'mnot sure where exactly or what type
> of queue to use.
> 
> From xine.h, the options for queue's are:
> i) A callback: this is what the vdr plugin uses to send key events to
> vdr from xine.
> ii) a non-blocking wait. this is what the also sound plugin uses.
> 
> OK cool, so I can read code, that was pretty hard ;-) (no really, I
> had to get out my '12 easy lessons of C' book from 10 years ago).
> 
> So where too from here? Can anyone offer some psudo code ideas on how
> it couldwork?
> 
> From what I can see in event.c, "static post_plugin_t
> *expand_open_plugin" (what does static mean refer to?) is the function
> which makes call draw (function expand_draw):
> {{{
>   ...
>   port = _x_post_intercept_video_port(&this->post, video_target[0],
> &input, &output);
>   port->new_port.get_frame     = expand_get_frame;
>   port->intercept_ovl          = expand_intercept_ovl;
>   port->new_manager->add_event = expand_overlay_add_event;
>   port->new_frame->draw        = expand_draw;
>   ...
> }}}
> 
> So, what I'm thinking, in the spirit of KISS (this is a necessity for
> me, because the last S is me *wink*) is to simply:
> i) initiate the event_function with zoom setting of 0 (expected range 0-6)
>    if event = zoom in, zoom+=1
>    if event = zoom out, zoom-=1
> 
> ii) create a callback on an event_function.
> 
> iii) in expand_draw, modify centre_cut_out_mode code to "levels" of
> zoom, 0-6 (where 0 is no zoom and 6 is zoom as is).  Then do zoom as
> per zoom level.  If center_cut_out_mode=1 then do zoom 6 where
> appropriate.
> 
> OK wow, i hope the above make sense.
> 
> Comments PLEASE ;-)
>
I have tried to implement this, at least with a callback to spit a
message to stdout when a particular type of event is seen.  However,
fbxine just dies during execution.  Is there any way to debug the
code?  Without expand on, fbxine launches.  With it on, I just get
this:
---
/usr/local/bin/fbxine -d --stdctl -V vidixfb -A alsa --no-lirc
--post=expand:centre_cut_out=1 -D
vdr://tmp/vdr-xine/stream#demux:mpeg_pes
mga_crtc2_vid: Found MGA G400/G450
mga_crtc2_vid: detected RAMSIZE is 16 MB
mga_crtc2_vid: Set write-combining type of video memory
mga_crtc2_vid: IRQ support disabled
---
With the old expand plugin, i the above plus normal operation (video out ;-):
---
...
1
time: 0
libmpeg2: stream not demultiplexed ?
libmpeg2: stream not demultiplexed ?
mga_crtc2_vid: Saved colorkey (ON: 0  B9:72:32)
libmpeg2: stream not demultiplexed ?
...
---

Here is the new expand plugin...
freevo src # cat post/planar/expand.c
/*
 * Copyright (C) 2003 the xine project
 *
 * This file is part of xine, a free video player.
 *
 * xine is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * xine is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
 *
 * $Id:
 *
 * expand video filter by James Stembridge 24/05/2003
 *            improved by Michael Roitzsch
 *            centre_crop_out_mode by Reinhard Nissl
 *
 * based on invert.c
 *
 */

#include "xine_internal.h"
#include "post.h"

/* The expand trick explained:
 *
 * The expand plugin is meant to take frames of arbitrary aspect ratio and
 * converts them to 4:3 aspect by adding black bars on the top and bottom
 * of the frame. This allows us to shift overlays down into the black area
 * so they don't cover the image.
 *
 * How do we do that? The naive approach would be to intercept the frame's
 * draw() function and simply copy the frame's content into a larger one.
 * This is quite CPU intensive because of the huge memcpy()s involved.
 *
 * Therefore the better idea is to trick the decoder into rendering the
 * image into a frame with pre-attached black borders. This is the way:
 *  - when the decoder asks for a new frame, we allocate an enlarged
 *    frame from the original port and prepare it with black borders
 *  - we modify this frame's base pointers so that the decoder will only see
 *    the area between the black bars
 *  - this frame is given to the decoder, which paints its image inside
 *  - when the decoder draws the frame, the post plugin architecture
 *    will automatically restore the old pointers
 * This way, the decoder (or any other post plugin up the tree) will only
 * see the frame area between the black bars and by that modify the
 * enlarged version directly. No need for later copying.
 *
 * When centre_crop_out_mode is enabled, the plugin will detect the black
 * bars to the left and right of the image and will then set up cropping
 * to efficiently remove the black border around the 4:3 image, which the
 * plugin would produce otherwise for this case.
 */


/* plugin class initialization function */
void *expand_init_plugin(xine_t *xine, void *);

/* plugin structures */
typedef struct expand_parameters_s {
  int enable_automatic_shift;
  int overlay_y_offset;
  int centre_cut_out_mode;
} expand_parameters_t;

START_PARAM_DESCR(expand_parameters_t)
PARAM_ITEM(POST_PARAM_TYPE_BOOL, enable_automatic_shift, NULL, 0, 1, 0,
  "enable automatic overlay shifting")
PARAM_ITEM(POST_PARAM_TYPE_INT, overlay_y_offset, NULL, -500, 500, 0,
  "manually shift the overlay vertically")
PARAM_ITEM(POST_PARAM_TYPE_BOOL, centre_cut_out_mode, NULL, 0, 1, 0,
  "cut out centered 4:3 image contained in 16:9 frame")
END_PARAM_DESCR(expand_param_descr)

typedef struct post_expand_s {

  xine_stream_t      *stream;

  post_plugin_t            post;

  xine_post_in_t           parameter_input;

  int                      enable_automatic_shift;
  int                      overlay_y_offset;
  int                      top_bar_height;
  int                      centre_cut_out_mode;
  int                      cropping_active;

  xine_event_queue_t *event_queue;
  xine_event_queue_t *event_queue_external;

} post_expand_t;

/* plugin class functions */
static post_plugin_t *expand_open_plugin(post_class_t *class_gen, int inputs,
                                         xine_audio_port_t **audio_target,
                                         xine_video_port_t **video_target,
                                         xine_stream_t *stream);

static char          *expand_get_identifier(post_class_t *class_gen);
static char          *expand_get_description(post_class_t *class_gen);
static void           expand_class_dispose(post_class_t *class_gen);

/* plugin instance functions */
static void           expand_dispose(post_plugin_t *this_gen);

/* parameter functions */
static xine_post_api_descr_t *expand_get_param_descr(void);
static int            expand_set_parameters(xine_post_t *this_gen,
void *param_gen);
static int            expand_get_parameters(xine_post_t *this_gen,
void *param_gen);
static char          *expand_get_help (void);

/* replaced video port functions */
static vo_frame_t    *expand_get_frame(xine_video_port_t *port_gen,
uint32_t width,
                                       uint32_t height, double ratio,
                                       int format, int flags);

/* replaced vo_frame functions */
static int            expand_draw(vo_frame_t *frame, xine_stream_t *stream);

/* event listener */
static int            is_event_callback (void *user_data, const
xine_event_t *event);

/* overlay manager intercept check */
static int            expand_intercept_ovl(post_video_port_t *port);

/* replaced overlay manager functions */
static int32_t        expand_overlay_add_event(video_overlay_manager_t
*this_gen, void *event);


void *expand_init_plugin(xine_t *xine, void *data)
{
  post_class_t *class = (post_class_t *)malloc(sizeof(post_class_t));

  if (!class)
    return NULL;

  class->open_plugin     = expand_open_plugin;
  class->get_identifier  = expand_get_identifier;
  class->get_description = expand_get_description;
  class->dispose         = expand_class_dispose;

  return class;
}


static post_plugin_t *expand_open_plugin(post_class_t *class_gen, int inputs,
                                         xine_audio_port_t **audio_target,
                                         xine_video_port_t **video_target,
                                         xine_stream_t *stream)
{
  post_expand_t     *this        = (post_expand_t
*)xine_xmalloc(sizeof(post_expand_t));
  post_in_t         *input;
  xine_post_in_t    *input_param;
  post_out_t        *output;
  post_video_port_t *port;
  static xine_post_api_t post_api =
    { expand_set_parameters, expand_get_parameters,
expand_get_param_descr, expand_get_help };

  if (!this || !video_target || !video_target[0]) {
    free(this);
    return NULL;
  }

  _x_post_init(&this->post, 0, 1);

  this->stream     = stream;

  this->enable_automatic_shift = 0;
  this->overlay_y_offset       = 0;
  this->centre_cut_out_mode    = 0;
  this->cropping_active        = 0;

  this->event_queue = xine_event_new_queue (this->stream);

  xine_event_create_listener_thread (this->event_queue,
                                     is_event_callback,
                                     this);


  port = _x_post_intercept_video_port(&this->post, video_target[0],
&input, &output);
  port->new_port.get_frame     = expand_get_frame;
  port->intercept_ovl          = expand_intercept_ovl;
  port->new_manager->add_event = expand_overlay_add_event;
  port->new_frame->draw        = expand_draw;

  input_param       = &this->parameter_input;
  input_param->name = "parameters";
  input_param->type = XINE_POST_DATA_PARAMETERS;
  input_param->data = &post_api;
  xine_list_append_content(this->post.input, input_param);

  input->xine_in.name   = "video";
  output->xine_out.name = "expanded video";

  this->post.xine_post.video_input[0] = &port->new_port;

  this->post.dispose = expand_dispose;

  return &this->post;
}

static char *expand_get_identifier(post_class_t *class_gen)
{
  return "expand";
}

static char *expand_get_description(post_class_t *class_gen)
{
  return "add black borders to top and bottom of video to expand it to
4:3 aspect ratio";
}

static void expand_class_dispose(post_class_t *class_gen)
{
  free(class_gen);
}


static void expand_dispose(post_plugin_t *this_gen)
{
  post_expand_t     *this = (post_expand_t *)this_gen;

  if (_x_post_dispose(this_gen))
    free(this);
}


static xine_post_api_descr_t *expand_get_param_descr(void)
{
  return &expand_param_descr;
}

static int expand_set_parameters(xine_post_t *this_gen, void *param_gen)
{
  post_expand_t *this = (post_expand_t *)this_gen;
  expand_parameters_t *param = (expand_parameters_t *)param_gen;

  this->enable_automatic_shift = param->enable_automatic_shift;
  this->overlay_y_offset       = param->overlay_y_offset;
  this->centre_cut_out_mode    = param->centre_cut_out_mode;

  return 1;
}

static int expand_get_parameters(xine_post_t *this_gen, void *param_gen)
{
  post_expand_t *this = (post_expand_t *)this_gen;
  expand_parameters_t *param = (expand_parameters_t *)param_gen;

  param->enable_automatic_shift = this->enable_automatic_shift;
  param->overlay_y_offset       = this->overlay_y_offset;
  param->centre_cut_out_mode    = this->centre_cut_out_mode;

  return 1;
}

static char *expand_get_help(void) {
  return _("The expand plugin is meant to take frames of arbitrary
aspect ratio and "
           "converts them to 4:3 aspect by adding black bars on the
top and bottom "
           "of the frame. This allows us to shift overlays down into
the black area "
           "so they don't cover the image.\n"
           "\n"
           "Parameters (FIXME: better help)\n"
           "  Enable_automatic_shift: Enable automatic overlay shifting\n"
           "  Overlay_y_offset: Manually shift the overlay vertically\n"
           "  Centre_cut_out_mode: extracts 4:3 image contained in 16:9 frame\n"
           "\n"
         );
}


static vo_frame_t *expand_get_frame(xine_video_port_t *port_gen, uint32_t width,
                                    uint32_t height, double ratio,
                                    int format, int flags)
{
  post_video_port_t *port = (post_video_port_t *)port_gen;
  post_expand_t     *this = (post_expand_t *)port->post;
  vo_frame_t        *frame;
  uint32_t           new_height, top_bar_height;
  int                i, end;

  _x_post_rewire(&this->post);

  if (ratio <= 0.0) ratio = (double)width / (double)height;

  /* Calculate height of expanded frame */
  new_height = (double)height * ratio * 3.0 / 4.0;
  new_height = (new_height + 1) & ~1;
  top_bar_height = (new_height - height) / 2;
  top_bar_height = (top_bar_height + 1) & ~1;

  this->top_bar_height = top_bar_height;

  if (new_height > height &&
      (format == XINE_IMGFMT_YV12 || format == XINE_IMGFMT_YUY2)) {
    frame = port->original_port->get_frame(port->original_port,
      width, new_height, 4.0 / 3.0, format, flags);

    _x_post_inc_usage(port);
    frame = _x_post_intercept_video_frame(frame, port);

    /* paint black bars in the top and bottom of the frame and hide these
     * from the decoders by modifying the pointers to and
     * the size of the drawing area */
    frame->height = height;
    frame->ratio  = ratio;
    switch (format) {
    case XINE_IMGFMT_YV12:
      /* paint top bar */
      memset(frame->base[0],   0, frame->pitches[0] * top_bar_height    );
      memset(frame->base[1], 128, frame->pitches[1] * top_bar_height / 2);
      memset(frame->base[2], 128, frame->pitches[2] * top_bar_height / 2);
      /* paint bottom bar */
      memset(frame->base[0] + frame->pitches[0] * (top_bar_height +
height)    ,   0,
        frame->pitches[0] * (new_height - top_bar_height - height)    );
      memset(frame->base[1] + frame->pitches[1] * (top_bar_height +
height) / 2, 128,
        frame->pitches[1] * (new_height - top_bar_height - height) / 2);
      memset(frame->base[2] + frame->pitches[2] * (top_bar_height +
height) / 2, 128,
        frame->pitches[2] * (new_height - top_bar_height - height) / 2);
      /* modify drawing area */
      frame->base[0] += frame->pitches[0] * top_bar_height;
      frame->base[1] += frame->pitches[1] * top_bar_height / 2;
      frame->base[2] += frame->pitches[2] * top_bar_height / 2;
      break;
    case XINE_IMGFMT_YUY2:
      /* paint top bar */
      end = frame->pitches[0] * top_bar_height;
      for (i = 0; i < end; i += 2) {
        frame->base[0][i]   = 0;
        frame->base[0][i+1] = 128;
      }
      /* paint bottom bar */
      end = frame->pitches[0] * new_height;
      for (i = frame->pitches[0] * (top_bar_height + height); i < end; i += 2) {
        frame->base[0][i]   = 0;
        frame->base[0][i+1] = 128;
      }
      /* modify drawing area */
      frame->base[0] += frame->pitches[0] * top_bar_height;
    }
  } else {
    frame = port->original_port->get_frame(port->original_port,
      width, height, ratio, format, flags);
    /* no need to intercept this one, we are not going to do anything with it */
  }

  return frame;
}


static int expand_intercept_ovl(post_video_port_t *port)
{
  post_expand_t         *this = (post_expand_t *)port->post;

  if (this->centre_cut_out_mode && this->cropping_active) return 0;

  /* we always intercept overlay manager */
  return 1;
}


static int32_t expand_overlay_add_event(video_overlay_manager_t
*this_gen, void *event_gen)
{
  video_overlay_event_t *event = (video_overlay_event_t *)event_gen;
  post_video_port_t     *port = _x_post_ovl_manager_to_port(this_gen);
  post_expand_t         *this = (post_expand_t *)port->post;

  if (event->event_type == OVERLAY_EVENT_SHOW) {
    switch (event->object.object_type) {
    case 0:
      /* regular subtitle */
      if (this->enable_automatic_shift)
        event->object.overlay->y += 2 * this->top_bar_height;
      else
        event->object.overlay->y += this->overlay_y_offset;
      break;
    case 1:
      /* menu overlay */
      event->object.overlay->y += this->top_bar_height;
    }
  }

  return port->original_manager->add_event(port->original_manager, event_gen);
}


static int is_pixel_black(vo_frame_t *frame, int x, int y)
{
  int Y = 0x00, Cr = 0x00, Cb = 0x00;

  if (x < 0)              x = 0;
  if (x >= frame->width)  x = frame->width - 1;
  if (y < 0)              y = 0;
  if (y >= frame->height) y = frame->height - 1;

  switch (frame->format)
  {
  case XINE_IMGFMT_YV12:
    Y  = *(frame->base[ 0 ] + frame->pitches[ 0 ] * y     + x);
    Cr = *(frame->base[ 1 ] + frame->pitches[ 1 ] * y / 2 + x / 2);
    Cb = *(frame->base[ 2 ] + frame->pitches[ 2 ] * y / 2 + x / 2);
    break;

  case XINE_IMGFMT_YUY2:
    Y  = *(frame->base[ 0 ] + frame->pitches[ 0 ] * y + x * 2 + 0);
    x &= ~1;
    Cr = *(frame->base[ 0 ] + frame->pitches[ 0 ] * y + x * 2 + 1);
    Cb = *(frame->base[ 0 ] + frame->pitches[ 0 ] * y + x * 2 + 3);
    break;
  }

  return (Y == 0x10 && Cr == 0x80 && Cb == 0x80);
}

static int is_event_callback (void *user_data, const xine_event_t *event)
{
  /* this doesn't do anything for now: it's a start at attempting to display
   * CMML clips which occur at 0 seconds into the track.  see
   *
   *   http://marc.theaimsgroup.com/?l=xine-devel&m=109202443013890&w=2
   *
   * for a description of the problem. */

  switch (event->type) {
    case XINE_EVENT_INPUT_MENU1:
      lprintf("video_frame_format_change_callback called!\n");
      break;
    default:
      lprintf("video_frame_format_change_callback called with unknown
event %d\n", event->type);
      break;
  }
}


static int expand_draw(vo_frame_t *frame, xine_stream_t *stream)
{
  post_video_port_t *port = (post_video_port_t *)frame->port;
  post_expand_t     *this = (post_expand_t *)port->post;
  int                skip;

  if (this->centre_cut_out_mode && !frame->bad_frame)
  {
    /* expected area of inner 4:3 image */
    int centre_width = frame->width * (9 * 4) / (16 * 3);
    int centre_left  = (frame->width - centre_width ) / 2;

    /* centre point for detecting a black frame */
    int centre_x = frame->width  / 2;
    int centre_y = frame->height / 2;

    /* ignore a black frame as it could lead to wrong results */
    if (!is_pixel_black(frame, centre_x, centre_y))
    {
      /* coordinates for testing black border near the centre area */
      int test_left  = centre_left - 16;
      int test_right = centre_left + 16 + centre_width;

      /* enable cropping when these pixels are black */
      this->cropping_active = is_pixel_black(frame, test_left, centre_y)
        && is_pixel_black(frame, test_right, centre_y);
    }

    /* crop frame */
    if (this->centre_cut_out_mode && this->cropping_active) {
      frame->crop_left  += centre_left;
      frame->crop_right += centre_left;

      /* get_frame() allocated an extra high frame */
      frame->crop_top    += (frame->next->height - frame->height) / 2;
      frame->crop_bottom += (frame->next->height - frame->height) / 2;
    }
  }

  _x_post_frame_copy_down(frame, frame->next);
  skip = frame->next->draw(frame->next, stream);
  _x_post_frame_copy_up(frame, frame->next);

  return skip;
}

 
> Mick
>



More information about the vdr mailing list