hi all,
if anyone is interested, I did this for myself and now, with little
make-up-for-public modifications, this filter ends up here.
Few notes: I used NMS, I hope I did everything right. Only
preprocessing is possible, not post. It converts from YUV to RGB and
other way round, and works only with RGB internally, I don't know if
it slows down transcode because of this, but I didn't notice any
performance hits:/ Available are two options: direction and frame
range (standard start-end/step). Direction can be ltr (left to right),
rtl (right to left), ttb (top to bottom), btt (bottom to top). It may
be a little cryptic, but I didn't come up with any other
'user-friendly' arguments for this.
I really, really hope this filter can not be achieved with
combinations of other transcode commands:)
Change source to your needs:)
Example: transcode ... -J mirror=dir=ttb:range:0-200

Dunno, maybe it will help someone (sorry if this filter is not in
scope of transcode 'duties', but I think with transcode is always 'the
more the better' philosophy:). If you think it's OK, i'm planning to
do similar filters - kaleidoscope, blur and sepia. Actually, I don't
need these ones for myself, but I learned NMS (at least I think so:)
and a bit of transcode internals writing this (it should be easy now -
shame to waste all those acquired knowledge on only one filter:), so
if anyone is interested in kaleidoscope/blur/sepia filter - tell.
Also, if you know these filters can be achieved using combinations of
other transcode commands, tell me.

P.S. you should also change filter/Makefile.am, but all of you know that:)

greetings, branko kokanovic
/*
 * filter_mirror.c -- mirror filter: does mirroring of frames
 * Written     by Branko Kokanovic <branko.kokanovic at gmail> - July 2007
 *
 * This file is part of transcode, a video stream processing tool.
 * transcode is free software, distributable under the terms of the GNU
 * General Public License (version 2 or later).  See the file COPYING
 * for details.
 */

/* public (user-visible) Name of the filter */
#define MOD_NAME    "filter_mirror.so"
/* version of the filter */
#define MOD_VERSION "v1.0.0 (2007-07-28)"
/* A short description */
#define MOD_CAP     "mirror filter plugin; does frame mirroring"
/* Author(s) of the filter */
#define MOD_AUTHOR  "Branko Kokanovic"
/* What this plugin can do (see NMS documentation for details) */
#define MOD_FEATURES \
    TC_MODULE_FEATURE_FILTER|TC_MODULE_FEATURE_VIDEO
/* How this module can work (see NMS documentation for details) */
#define MOD_FLAGS \
    TC_MODULE_FLAG_RECONFIGURABLE

/* -------------------------------------------------
 *
 * mandatory include files
 *
 *-------------------------------------------------*/

#include "transcode.h"
#include "filter.h"
#include "libtc/libtc.h"
#include "libtc/optstr.h"
#include "libtc/tcmodule-plugin.h"
/* For RGB->YUV conversion */
#include "libtcvideo/tcvideo.h"

static const char mirror_help[] = ""
    "Overview:\n"
    "    This filter mirrors frames in four possible ways\n"
    "Options:\n"
    "   'dir'   direction of mirroring. Can be one of: "
    "       ltr (Left To Right), rtl (Right To Left), ttb (Top To Bottom), btt (Bottom To Top) [ltr]\n"
    "   'range' apply filter to [start-end]/step frames [0-oo/1]\n";

static const char mirror_ltr[]="Left To Right";
static const char mirror_rtl[]="Right To Left";
static const char mirror_ttb[]="Top To Bottom";
static const char mirror_btt[]="Bottom To Top";

/*************************************************************************/

typedef enum { Mirror_LTR=0, Mirror_RTL, Mirror_TTB, Mirror_BTT} directions;

typedef struct {
	unsigned int start; /* start frame */
	unsigned int end;   /* end frame */
	unsigned int step;  /* every Nth frame */
    int boolstep;
    unsigned int dir;   /* direction of mirroring */
    int codec;          /* remembering codec */
    TCVHandle tcvhandle;
} MirrorData;

/*************************************************************************/

/* Module interface routines and data. */

/*************************************************************************/

/**
 * mirror_init:  Initialize this instance of the module.  See
 * tcmodule-data.h for function details.
 */

static int mirror_init(TCModuleInstance *self, uint32_t features)
{
    MirrorData *md=NULL;

    TC_MODULE_SELF_CHECK(self, "init");
    TC_MODULE_INIT_CHECK(self, MOD_FEATURES, features);

    md = tc_malloc(sizeof(MirrorData));
    if (!md) {
        tc_log_error(MOD_NAME, "init: out of memory!");
        return TC_ERROR;
    }
    self->userdata = md;

    if (verbose) {
        tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
    }
    return TC_OK;
}

/*************************************************************************/

/**
 * mirror_fini:  Clean up after this instance of the module.  See
 * tcmodule-data.h for function details.
 */

static int mirror_fini(TCModuleInstance *self)
{
    MirrorData *md = NULL;
    TC_MODULE_SELF_CHECK(self, "fini");

    md = self->userdata;

    tcv_free(md->tcvhandle);
    tc_free(self->userdata);
    self->userdata = NULL;
    return TC_OK;
}

/*************************************************************************/

/**
 * mirror_configure:  Configure this instance of the module.  See
 * tcmodule-data.h for function details.
 */

static int mirror_configure(TCModuleInstance *self,
                          const char *options, vob_t *vob)
{
    MirrorData *md = NULL;

    TC_MODULE_SELF_CHECK(self, "configure");

    md = self->userdata;

    /* checks for RGB or YUV */
    if ((vob->im_v_codec != CODEC_YUV) && (vob->im_v_codec != CODEC_RGB)){
        tc_log_error(MOD_NAME, "This filter is only capable of RGB and YUV mode");
        return TC_ERROR;
    }
    /* setup defaults */
    md->dir = Mirror_LTR;
    md->codec = vob->im_v_codec;
    if (vob->im_v_codec == CODEC_YUV){
        if (!(md->tcvhandle = tcv_init())) {
            tc_log_error(MOD_NAME, "Error at image conversion initialization.");
            return(-1);
	    }
    }
    md->start=0;
    md->end=(unsigned int)-1;
    md->step=1;

    if (options) {
        int ret;
        char buf[1024];
        if (verbose >= TC_STATS) {
            tc_log_info(MOD_NAME, "options=%s", options);
        }
        ret=optstr_get(options, "dir","%[^:]",buf);
        if (ret>0){
            if      (strcmp(buf,"ltr")==0) md->dir=Mirror_LTR;
            else if (strcmp(buf,"rtl")==0) md->dir=Mirror_RTL;
            else if (strcmp(buf,"ttb")==0) md->dir=Mirror_TTB;
            else if (strcmp(buf,"btt")==0) md->dir=Mirror_BTT;
        }
        optstr_get(options, "range",  "%u-%u/%d", &md->start, &md->end, &md->step);
    }

    if (md->start % md->step == 0)  md->boolstep = 0;
    else                            md->boolstep = 1;

    return TC_OK;
}

/*************************************************************************/

/**
 * mirror_stop:  Reset this instance of the module.  See tcmodule-data.h
 * for function details.
 */

static int mirror_stop(TCModuleInstance *self)
{
    TC_MODULE_SELF_CHECK(self, "stop");
    return TC_OK;
}

/*************************************************************************/

/**
 * mirror_inspect:  Return the value of an option in this instance of
 * the module.  See tcmodule-data.h for function details.
 */

static int mirror_inspect(TCModuleInstance *self,
                        const char *param, const char **value)
{
    MirrorData *md = NULL;

    TC_MODULE_SELF_CHECK(self, "inspect");
    TC_MODULE_SELF_CHECK(param, "inspect");
    TC_MODULE_SELF_CHECK(value, "inspect");

    md = self->userdata;

    if (optstr_lookup(param, "help")) {
        *value = mirror_help; 
    }else if (optstr_lookup(param, "dir")) {
        switch (md->dir){
            case Mirror_LTR: *value=mirror_ltr; break;
            case Mirror_RTL: *value=mirror_rtl; break;
            case Mirror_TTB: *value=mirror_ttb; break;
            case Mirror_BTT: *value=mirror_btt; break;
        }
    }
    return TC_OK;
}

/*************************************************************************/

/**
 * mirror_filter_video:  mirrors frame
 * See tcmodule-data.h for function details.
 */

static int mirror_filter_video(TCModuleInstance *self, vframe_list_t *frame)
{
    MirrorData *md = NULL;
    int w,h,i,j;

    TC_MODULE_SELF_CHECK(self, "filer_video");
    TC_MODULE_SELF_CHECK(frame, "filer_video");

    md = self->userdata;
    if (md->start <= frame->id && frame->id <= md->end && frame->id%md->step == md->boolstep) { /* in range */
        /* converting YUV->RGB */
        if (md->codec==CODEC_YUV){
        	if (!tcv_convert(md->tcvhandle, frame->video_buf, frame->video_buf, frame->v_width, frame->v_height, 
                    IMG_YUV_DEFAULT, IMG_RGB24)){
                tc_log_error(MOD_NAME, "cannot convert YUV stream to RGB format !");
                return -1;
            }
        }

        w=frame->v_width;
        h=frame->v_height;

        if (md->dir==Mirror_LTR){
            for(j=0;j<h;j++){
                for(i=0;i<w/2;i++){
                    frame->video_buf[3*w*j+3*(w-i-1)]   = frame->video_buf[3*w*j+3*i];
                    frame->video_buf[3*w*j+3*(w-i-1)+1] = frame->video_buf[3*w*j+3*i+1];
                    frame->video_buf[3*w*j+3*(w-i-1)+2] = frame->video_buf[3*w*j+3*i+2];
                }
            }
        }else if (md->dir==Mirror_RTL){
            for(j=0;j<h;j++){
                for(i=0;i<w/2;i++){
                    frame->video_buf[3*w*j+3*i]   = frame->video_buf[3*w*j+3*(w-i-1)];
                    frame->video_buf[3*w*j+3*i+1] = frame->video_buf[3*w*j+3*(w-i-1)+1];
                    frame->video_buf[3*w*j+3*i+2] = frame->video_buf[3*w*j+3*(w-i-1)+2];
                }
            }
        }else if (md->dir==Mirror_TTB){
            for(i=0;i<w;i++){
                for(j=0;j<h/2;j++){
                    frame->video_buf[3*w*(h-j-1)+3*i]   = frame->video_buf[3*w*j+3*i];
                    frame->video_buf[3*w*(h-j-1)+3*i+1] = frame->video_buf[3*w*j+3*i+1];
                    frame->video_buf[3*w*(h-j-1)+3*i+2] = frame->video_buf[3*w*j+3*i+2];
                }
            }
        }else if (md->dir==Mirror_BTT){
            for(i=0;i<w;i++){
                for(j=0;j<h/2;j++){
                    frame->video_buf[3*w*j+3*i]   = frame->video_buf[3*w*(h-j-1)+3*i];
                    frame->video_buf[3*w*j+3*i+1] = frame->video_buf[3*w*(h-j-1)+3*i+1];
                    frame->video_buf[3*w*j+3*i+2] = frame->video_buf[3*w*(h-j-1)+3*i+2];
                }
            }
        }
        /* converting RGB->YUV */
        if (md->codec==CODEC_YUV){
            if (!tcv_convert(md->tcvhandle, frame->video_buf, frame->video_buf, frame->v_width, frame->v_height,
                    IMG_RGB24, IMG_YUV_DEFAULT)){
                tc_log_error(MOD_NAME, "cannot convert RGB stream to YUV format !");
                return -1;
	        }
        }
    } /* end if in range */
    return TC_OK;
}

/*************************************************************************/

static const TCCodecID mirror_codecs_in[] = { 
    TC_CODEC_ANY, TC_CODEC_ERROR
};
static const TCCodecID mirror_codecs_out[] = {
    TC_CODEC_ANY, TC_CODEC_ERROR
};
static const TCFormatID mirror_formats[] = {
    TC_FORMAT_ERROR
};

static const TCModuleInfo mirror_info = {
    .features    = MOD_FEATURES,
    .flags       = MOD_FLAGS,
    .name        = MOD_NAME,
    .version     = MOD_VERSION,
    .description = MOD_CAP,
    .codecs_in   = mirror_codecs_in,
    .codecs_out  = mirror_codecs_out,
    .formats_in  = mirror_formats,
    .formats_out = mirror_formats
};

static const TCModuleClass mirror_class = {
    .info         = &mirror_info,

    .init         = mirror_init,
    .fini         = mirror_fini,
    .configure    = mirror_configure,
    .stop         = mirror_stop,
    .inspect      = mirror_inspect,

    .filter_video = mirror_filter_video
};

extern const TCModuleClass *tc_plugin_setup(void)
{
    return &mirror_class;
}

/*************************************************************************/

static int mirror_get_config(TCModuleInstance *self, char *options)
{
    MirrorData *md = NULL;
    char b[128];

    TC_MODULE_SELF_CHECK(self, "get_config");

    md = self->userdata;

    optstr_filter_desc(options, MOD_NAME, MOD_CAP, MOD_VERSION, MOD_AUTHOR, "VRYME", "1");
    optstr_param (options, "help", "Prints out a short help", "",   "0");
    optstr_param(options,  "dir",  "Direction of mirroring (ltr, rtl, ttb, btt)",  "%s", "");
    tc_snprintf(b, 128, "%u-%u/%d", md->start, md->end, md->step);
    optstr_param (options, "range", "apply filter to [start-end]/step frames",
        "%u-%u/%d", b, "0", "oo", "0", "oo", "1", "oo");

    return TC_OK;
}

static int mirror_process(TCModuleInstance *self, 
                            frame_list_t *frame)
{
    TC_MODULE_SELF_CHECK(self, "process");

    /* choose what to do by frame->tag */
    if ((frame->tag & TC_VIDEO) && !(frame->attributes & TC_FRAME_IS_SKIPPED)
        && (frame->tag & TC_PRE_M_PROCESS)) {
            return mirror_filter_video(self, (vframe_list_t*)frame);
    }
    return TC_OK;
}

/*************************************************************************/

/* Old-fashioned module interface. */

TC_FILTER_OLDINTERFACE(mirror)

/*************************************************************************/

/*
 * Local variables:
 *   c-file-style: "stroustrup"
 *   c-file-offsets: ((case-label . *) (statement-case-intro . *))
 *   indent-tabs-mode: nil
 * End:
 *
 * vim: expandtab shiftwidth=4:
 */

Reply via email to