[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