[MPlayer-G2-dev] basic g2 vp (video pipeline) code
D Richard Felker III
dalias at aerifal.cx
Sun Nov 2 08:09:21 CET 2003
Hey devs! It's far from finished, but here's a glimpse at the new
video pipeline layer for mplayer-g2. The main functions of interest
are vp_pull_image, vp_get_image, and vp_release_image. vp_config and
vp_insert_filter are somewhat relevant to the dreaded auto-insertion
hack too. :)
Please read and make comments. I have no idea if the code actually
compiles but it should be roughly correct. Some key points:
Image dimensions: You don't pick the image size when requesting an
image like you did in G1. Instead it's implicit from how config() was
called. Along with this change, there's a much clearer distinction
between actual picture dimensions and allocated (stride). Thus filters
will be sure to have the correct values for width/height so they don't
end up processing extra junk/blackspace outside the image (possibly
causing them to behave wrong!).
Aspect: Use sample aspect ratio rather than display aspect ratio! This
means we don't have to constantly recompute d_width/d_height (making
bad estimates and introducing error) every time we crop/expand/etc.
Most filters can just pass sample aspect through unchanged, or making
simple changes such as doubling the sample width or height (the only
notable exception is scale). Monitor aspect can get figured in at the
_end_ of the chain (where it belongs) instead of the beginning.
Stride restrictions: Greatly simplified, and the flag names are
actually consistent with what they mean now. From vp.h:
// Note: Stride restrictions are for compatibility with legacy codecs ONLY!
// ALL filters and vo MUST accept any stride as input!
// Aligned stride is always implicitly preferred when not forbidden!!
PTS: We now use a discrete time base fraction instead of floats.
Filters are free to modify the time base if they wish. This should
prevent error accumulation during lengthy encodes and help mencoder
automatically select a good time base for the output file. Decoders
should select the correct natural timebase (msec for asf, 60000/1001
for ntsc mpeg, 50/1 for pal mpeg, fps for fixed-fps formats, etc.).
DR and slices: "Slices" are replaced with an indirect buffer type,
indirect meaning that you can't write directly to it, only through
the draw_indirect() function. Both DR and indirect rendering (slices)
are accomplished by requesting special buffer types from vp_get_image.
Rather than falling back to automatic (allocated) buffer, vp_get_image
will _fail_ if DR/indirect buffer is not available! This is to give
the source filter/codec more flexibility in deciding which buffer type
to use. (For example, it may be better to fallback to export instead
of automatic buffer if DR fails.) Unlike in G1, indirect rendering
does not have to be done in "slices", but any region can be drawn via
draw_indirect().
The auto-insertion hack: ... :)) It should be pretty self-explanatory.
Basically, when a new filter is auto-inserted at vp_config() time, the
link between the two filters is split in two. The part connected to
the dest filter remains in place, and a new "sister" link is created
to remain with the source filter. Then scale (or whatever you like) is
inserted in between. When execution gets back to vp_pull_image, it
sees that the link it's processing has a sister, so it stuffs the
image back to the sister and clears the sister field, then calls
pull_image on the link again, this time getting an image from the
newly inserted conversion filter. It's hard to explain so RTFS!! :)
Comments are welcome, but please make an attempt to understand the
code before just flaming!
Rich
-------------- next part --------------
#include "vp.h"
vp_node_t *vp_open_filter(...)
{
// FIXME: how do we load modules?
}
int vp_insert_filter(vp_link_t **link, vp_node_t *vf)
{
vf->out = *link;
vf->in = *link = calloc(1, sizeof(vp_link_t));
vf->in->src = vf->out->src;
vf->out->src = vf;
vf->in->dest = vf;
}
int vp_config(vp_link_t **link, int w, int h, int samp_w, int samp_h, mid_t *mid)
{
vp_node_t *dest = *link->dest;
int flags = dest->query_format(dest, mid);
if (!flags) {
vp_link_t *orig = *link;
vp_insert_filter(link, vf_open_filter("scale"));
orig->sister = *link;
}
link->w = w;
link->h = h;
link->mid = mid;
return *link->dest->config(*link, w, h, samp_w, samp_h, mid);
}
mp_image_t *vp_get_image(vp_link_t *link, int type, int flags)
{
int i, j;
mp_image_t *img = NULL;
mp_image_t **pool = link->image_pool;
int stride[MAX_PLANES];
mid_t *mid = link->mid;
int num_planes = mid->num_planes;
int bpp = mid->bpp;
int width = link->w;
if (type == MPI_AUTO) {
memset(stride, 0, sizeof stride);
switch(mid->type) {
case MID_TYPE_YUV:
switch (num_planes) {
case 0:
stride[0] = width*bpp/8;
break;
case 2:
stride[0] = width;
stride[1] = (2*width)>>mid->csh;
break;
case 4:
stride[3] = width>>mid->csh;
case 3:
stride[2] = width>>mid->csh;
stride[1] = width>>mid->csh;
case 1:
stride[0] = width;
break;
default:
return NULL;
}
break;
case MID_TYPE_RGB:
case MID_TYPE_PALETTE:
stride[0] = width*bpp/8;
break;
default:
if (type != MPI_EXPORT && type != MPI_DIRECT)
return NULL;
}
// Always MB-align stride unless EXACT_WIDTH is demanded.
if (!(flags & MPI_STRIDE_EXACT_WIDTH)) {
if (mid->type == MID_TYPE_YUV) {
int align = (8<<mid->csh)-1;
switch (mid->num_planes) {
case 0:
case 1:
stride[0] = (stride[0]+15)&~15;
break;
case 2:
stride[0] = (stride[0]+align)&~align;
stride[1] = (stride[1]+15)&~15;
break;
case 4:
stride[3] = (stride[3]+7)&~7;
case 3:
stride[0] = (stride[0]+align)&~align;
stride[1] = (stride[1]+7)&~7;
stride[2] = (stride[2]+7)&~7;
break;
}
} else {
stride[0] = (stride[0]+15)&~15;
}
}
// Search for already-allocated image
for (i=0; i<link->image_pool_size; i++) {
if (pool[i]->lock) continue;
if (!pool[i]->planes[0]) continue;
if (pool[i]->w != link->w) continue;
if (pool[i]->h != link->h) continue;
if (pool[i]->mid != link->mid) continue;
for (j=0; j<MAX_PLANES; j++)
if (stride[j] != pool[i]->stride[j]) break;
if (j != MAX_PLANES) continue;
img = pool[i];
break;
}
}
if (!img) {
for (i=0; i<link->image_pool_size; i++) {
if (!pool[i]->lock) {
img = pool[i];
if (img->type == MPI_AUTO) {
free(img->planes[0]);
}
memset(img, 0, sizeof(mp_image_t));
break;
}
}
}
if (!img) {
i = ++link->image_pool_size;
pool = link->image_pool = realloc(pool, i*sizeof(mp_image_t *));
img = pool[--i] = calloc(1, sizeof(mp_image_t));
}
img->type = type;
img->link = link;
img->w = link->w;
img->h = link->h;
img->mid = link->mid;
img->cw = (img->w + (1<<mid->csh)) >> mid->csh;
img->ch = (img->h + (1<<mid->csv)) >> mid->csv;
switch (type) {
case MPI_AUTO:
if (!img->planes[0]) {
img->planes[0] = memalign(64, img->h*stride[0]
+ img->ch*(stride[1]+stride[2]+stride[3]));
if (num_planes > 1) img->planes[1] = img->planes[1] + stride[0]*img->h;
if (num_planes > 2) img->planes[2] = img->planes[2] + stride[1]*img->ch;
// IF09 crap...
if (num_planes > 3) img->planes[3] = img->planes[3] + stride[2]*img->ch;
for (i=0; i<MAX_PLANES; i++)
img->stride[i] = stride[i];
memset(planes[0], 0, stride[0]*link->h);
if (planes[1]) memset(planes[1], 128, stride[1]*link->ch);
if (planes[2]) memset(planes[2], 128, stride[2]*link->ch);
// Swapped means YVU, unswapped is YUV...is this ok?
if ((mid->flags & MID_FLAG_SWAPPED) && num_planes > 2) {
unsigned char *temp = planes[1];
planes[1] = planes[2];
planes[2] = temp;
}
}
break;
case MPI_EXPORT:
owner = link->src;
break;
case MPI_INDIRECT:
flags = MPI_MEM_INDIRECT;
case MPI_DIRECT:
if (!link->dest->get_buffer) return NULL;
if (!link->dest->get_buffer(link, flags, img))
return NULL;
img->owner = link->dest;
break;
}
img->lock++;
return img;
}
void vp_lock_image(mp_image_t *img)
{
img->lock++;
}
void vp_release_image(mp_image_t *img)
{
if (--img->lock) return;
if (img->owner && img->owner->release_buffer)
img->owner->release_buffer(img->owner, img);
}
mp_image_t *vp_pull_image(vp_link_t *link, int drop)
{
mp_image_t *img = link->img_redir;
if (img) {
link->img_redir = NULL;
return img;
}
again:
img = link->src->pull_image(link, drop);
if (link->sister) {
link->sister->img_redir = img;
link->sister = NULL;
goto again;
}
return img;
}
-------------- next part --------------
#include "cfg.h"
typedef module_info_t vf_info_t;
typedef struct vp_link_s vp_link_t;
typedef struct vp_node_s vp_node_t;
typedef struct vp_priv_s vp_priv_t;
typedef struct mp_image_s mp_image_t;
// MPlayer Imageformat Description
#define MID_TYPE_YUV 1
#define MID_TYPE_RGB 2
#define MID_TYPE_PALETTE 3
#define MID_TYPE_SPECIAL 4
#define MID_FLAG_SWAPPED 0x1
typedef struct mid_s {
int type;
int flags;
int bpp;
int depth;
int csh, csv; // chroma shift, horiz. and vert.
int num_planes; // 0=packed, 1=gray, 2=semiplanar(nv12), 3=full planar
int chan_bits[4]; // sizes and offsets of channels (in bits). order is:
int chan_offs[4]; // R,G,B[,A] for RGB; Y1,Y2,U,V for packed YUV
unsigned int imgfmt; // legacy "fourcc"
char *name, *longname;
} mid_t;
// Buffer management types
#define MPI_AUTO 1 // auto-managed by link layer
#define MPI_EXPORT 2 // exported from source to dest
#define MPI_DIRECT 3 // exported from dest to source (DR)
#define MPI_INDIRECT 4 // indirect rendering via slices, no pointers
// Buffer memory restrictions
#define MPI_MEM_READABLE 0x1 // buffer is in readable memory
#define MPI_MEM_PRESERVE 0x2 // dest will not clobber buffer contents
#define MPI_MEM_REUSABLE 0x4 // source can reuse & re-return buffer >1 times
#define MPI_MEM_INDIRECT 0x8 // indirect buffer
// Stride restrictions (define B==bpp/8 for packed, B==1 for planar)
// Note: Stride restrictions are for compatibility with legacy codecs ONLY!
// ALL filters and vo MUST accept any stride as input!
// Aligned stride is always implicitly preferred when not forbidden!!
#define MPI_STRIDE_MB_ALIGNED 0x0100 // stride%(16*B) == 0
#define MPI_STRIDE_PIXEL_ALIGNED 0x0200 // stride%B == 0
#define MPI_STRIDE_EXACT_WIDTH 0x0400 // stride == w*B
#define MPI_STRIDE_POSITIVE 0x0800 // stride > 0
#define MPI_STRIDE_COMMON_STRIDE 0x1000 // stride[1,2] == stride[0]>>csh
#define MPI_STRIDE_COMMON_PLANE 0x2000 // planes[1] == planes[0]+stride[0]*h..
#define MPI_STRIDE_MASK 0x3F00
// PTS flags
#define MPI_PTS_PTS_VALID 0x01
#define MPI_PTS_DURATION_VALID 0x02
#define MPI_PTS_REL_PTS_VALID 0x04
#define MPI_PTS_PTS_ACCURATE 0x08
#define MPI_PTS_DISCONTINUITY 0x10
struct mp_image_s {
// Buffer management
int buftype;
int flags;
vp_node_t *owner;
vp_link_t *link;
int lock;
// Format & dimensions
mid_t *mid;
int w, h;
int cw, ch;
// Pointers & strides
unsigned char *planes[MAX_PLANES];
int stride[MAX_PLANES];
unsigned char *qscale;
int qstride;
int qscale_type;
unsigned char *palette;
mid_t *palette_mid;
// Frame information
int pict_type;
int color_type;
int field_flags;
// Timing
int time_base_num;
int time_base_denom;
long long pts;
int duration;
int rel_pts;
unsigned int pts_flags;
// Private data for use by owner
void *priv;
};
struct vp_link_s {
vp_node_t *src, *dest;
vp_link_t *sister;
mp_image_t *img_redir;
int configured;
int w, h;
mid_t *mid;
mp_image_t **image_pool;
int image_pool_size;
};
struct vp_node_s {
vp_info_t *info;
config_data_t *cfg;
// Links
vp_link_t *in, *out;
// Basic VP API
mp_image_t *(*pull_image)(vp_link_t *link);
int (*query_format)(vp_link_t *link, mid_t *mid);
int (*config)(vp_link_t *link, int w, int h, int samp_w, int samp_h, mid_t *mid);
int (*control)(vp_link_t *link, int request, void *data);
int (*close)(vp_node_t *self);
// Direct rendering
int (*get_buffer)(vp_link_t *link, int flags, mp_image_t *img);
void (*release_buffer)(vp_node_t *self, mp_image_t *img);
// Slices
void (*draw_indirect)(vp_link_t *link, mp_image_t *img,
unsigned char **src, int *stride, int x, int y, int w, int h);
// Private data for this node of the pipeline
vp_priv_t *priv;
};
More information about the MPlayer-G2-dev
mailing list