hi all
new version of filter_mirror, with corrected things as Francesco
pointed out, and with my small fixes.
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.
*/
#define MOD_NAME "filter_mirror.so"
#define MOD_VERSION "v1.0.1 (2007-07-29)"
#define MOD_CAP "mirror filter plugin; does frame mirroring"
#define MOD_AUTHOR "Branko Kokanovic"
#define MOD_FEATURES \
TC_MODULE_FEATURE_FILTER|TC_MODULE_FEATURE_VIDEO
#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;
/* initialize */
md->dir = MIRROR_LTR;
md->start=0;
md->end=(unsigned int)-1;
md->step=1;
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;
if (md->tcvhandle){
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){
md->tcvhandle = tcv_init();
if (!md->tcvhandle) {
tc_log_error(MOD_NAME, "Error at image conversion initialization.");
return TC_ERROR;
}
}
md->start=0;
md->end=(unsigned int)-1;
md->step=1;
if (options) {
int ret;
char buf[TC_BUF_MAX];
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 TC_ERROR;
}
}
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 TC_ERROR;
}
}
} /* end if in range */
return TC_OK;
}
/*************************************************************************/
static const TCCodecID mirror_codecs_in[] = {
TC_CODEC_RGB, TC_CODEC_YUV420P, TC_CODEC_ERROR
};
static const TCCodecID mirror_codecs_out[] = {
TC_CODEC_RGB, TC_CODEC_YUV420P, 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[TC_BUF_MIN];
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", "Mirrors the frames", "", "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:
*/