This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
tutorial:writing_your_own_filter [2010/04/14 00:04] j.m Fixing the REGISTERX snippet |
tutorial:writing_your_own_filter [2012/11/11 08:51] (current) |
||
|---|---|---|---|
| Line 1: | Line 1: | ||
| + | ====== Writing your own filter ====== | ||
| - | This page tries to explain how to write your own filter | + | This page tries to explain how to write your own filter. |
| - | ====== Setup & Tools ====== | + | ===== Setup & tools ===== |
| - | First of all you need avidemux sources ( if you do plugins you don't even have to compile avidemux itself but you still need the sources) | + | First of all you need Avidemux sources (if you do plugins you don't even have to compile Avidemux itself but you still need the sources). |
| - | For windows, you need mingw gcc/g++ (v4.x) or cygwin gcc/g++ (v4.x). | + | For windows, you need MinGW GCC/g++ (v4.x) or cygwin GCC/g++ (v4.x). |
| - | MSVC will not work as there will be some c++ links and MSVC c++ is incompatible with G++. | + | MSVC will not work as there will be some C++ links and MSVC C++ is incompatible with g++. |
| - | ====== Everything is a class ====== | + | ===== Everything is a class ===== |
| - | All filters inside avidemux are derivative of AVDMGenericVideoStream class. | + | All filters inside Avidemux are derivative of AVDMGenericVideoStream class. |
| - | Only a few methods need to be defined : | + | Only a few methods need to be defined: |
| - | ====== The constructor ====== | + | ===== The constructor ===== |
| - | It takes 2 arguments | + | |
| - | * The previous filter in the chain, it will be copied into _in | + | It takes 2 arguments: |
| - | * CONFcouple * couples which contains the configuration. It it is null it means you have to use your default value. | + | - The previous filter in the chain, it will be copied into _in |
| + | - CONFcouple * couples which contains the configuration. It it is null it means you have to use your default value. | ||
| The constructor has to update its _info field which contains a description of the output format. | The constructor has to update its _info field which contains a description of the output format. | ||
| - | If you dont change fps nor width/height you can just copy the one from the previous filter using _in->getInfo() | + | If you dont change fps nor width/height you can just copy the one from the previous filter using _in<nowiki>-></nowiki>getInfo(). |
| You should have a field of your class named _param which is a structure describing your parameters. | You should have a field of your class named _param which is a structure describing your parameters. | ||
| - | For example | + | For example: |
| - | typedef struct rotateParam | + | |
| - | { | + | <code cpp> |
| - | uint32_t angle; | + | typedef struct rotateParam |
| - | }rotateParam; | + | { |
| + | uint32_t angle; | ||
| + | }rotateParam; | ||
| + | </code> | ||
| In the constructor you can use the handy *GET* macro to retrive the parameter from CONFcouple | In the constructor you can use the handy *GET* macro to retrive the parameter from CONFcouple | ||
| e.g. GET(angle); | e.g. GET(angle); | ||
| + | ===== printConf ===== | ||
| - | ====== printConf ====== | + | The next method is printConf. It is used to print the configuration in the filter dialog box. |
| - | the next method is printConf. It is used to print the configuration in the filter dialog box. | + | <code cpp> |
| - | char *yourClassName::printConf (void) | + | char *yourClassName::printConf (void) |
| - | { | + | { |
| static char buf[[50]]; | static char buf[[50]]; | ||
| sprintf ((char *) buf, " My rotate filter, angle :%d",_param->angle); | sprintf ((char *) buf, " My rotate filter, angle :%d",_param->angle); | ||
| return buf; | return buf; | ||
| - | } | + | } |
| + | </code> | ||
| Note that the buffer is static to avoid allocating/freeing it as it can be complicated. | Note that the buffer is static to avoid allocating/freeing it as it can be complicated. | ||
| - | ====== GetCouple ====== | + | ===== GetCouple ===== |
| - | This method is used to retrieve the current configuration. | + | |
| - | The simplest way is to use the CSET macro like this | + | |
| - | uint8_t yourClassName::getCoupledConf (CONFcouple ** couples) | + | This method is used to retrieve the current configuration. The simplest way is to use the CSET macro like this: |
| - | { | + | |
| + | <code cpp> | ||
| + | uint8_t yourClassName::getCoupledConf (CONFcouple ** couples) | ||
| + | { | ||
| ADM_assert (_param); | ADM_assert (_param); | ||
| *couples = new CONFcouple (1); // Number of param in your structure | *couples = new CONFcouple (1); // Number of param in your structure | ||
| Line 57: | Line 66: | ||
| CSET (angle); | CSET (angle); | ||
| return 1; | return 1; | ||
| - | } | + | } |
| + | </code> | ||
| - | getCoupledConf and the constructor are a pair. | + | getCoupledConf and the constructor are a pair. It is vital they use the same number of parameters and the same parameter names. Else you will have asserts popping. |
| - | It is vital they use the same number of parameters and the same parameter names. | + | |
| - | Else you will have asserts popping. | + | |
| - | ====== Configure ====== | + | ===== Configure ===== |
| - | uint8_t yourClassName::configure (AVDMGenericVideoStream * in) | + | <code cpp>uint8_t yourClassName::configure (AVDMGenericVideoStream * in)</code> |
| This method is used to configure the filter. The parameter is the same as for the constructor. It is safer to update _in with it though. | This method is used to configure the filter. The parameter is the same as for the constructor. It is safer to update _in with it though. | ||
| - | It is recommended (strongly) to use dialogFactory as it will work on both GTK and windows and is quite simple to use. | + | It is recommended (strongly) to use dialogFactory as it will work on both GTK+ and Windows (Qt) and is quite simple to use. |
| - | Some infos : | + | |
| - | * Returning 0 means nothing has changed | + | Some info: |
| + | * Returning 0 means nothing has changed. | ||
| * Returning 1 means you have changes something and that the filter chain might have to be rebuild. | * Returning 1 means you have changes something and that the filter chain might have to be rebuild. | ||
| - | IMPORTANT: The change must be done immediately in configure! | + | **Important**: The change must be done immediately in configure! |
| - | ====== getFrameNoAlloc ====== | + | ===== getFrameNoAlloc ===== |
| This is the central method as the work will be done here. | This is the central method as the work will be done here. | ||
| - | uint8_t yourClassName::getFrameNumberNoAlloc (uint32_t inframe, | + | <code cpp> |
| - | uint32_t * len, | + | uint8_t yourClassName::getFrameNumberNoAlloc (uint32_t inframe, |
| - | ADMImage * data, uint32_t * flags) | + | uint32_t * len, |
| + | ADMImage * data, uint32_t * flags) | ||
| + | </code> | ||
| - | len is the size in bytes of the output image. | + | **len** is the size in bytes of the output image. |
| - | flags can be ignored. | + | **flags** can be ignored. |
| - | inframe is the frame you have to render. | + | **inframe** is the frame you have to render. |
| - | data is the place when you have to render the frame. | + | **data** is the place when you have to render the frame. |
| - | Remember that the filters are a chain, so each filter may ask for (any) frame to its predecessor. | + | Remember that the filters are a chain, so each filter may ask for (any) frame to its predecessor. In most cases you will want to allocate a temporary buffer in the constructor (which is often called _uncompressed) and ask the previous filter to fill it for you. For example, the verticalFlip can be simplified like that: |
| - | In most case you will want to allocate a temporary buffer in the constructor (which is often called _uncompressed) and ask the previous filter to fill it for you. | + | |
| - | For example, the verticalFlip can be simplified like that : | + | |
| - | uint8_t yourClassName::getFrameNumberNoAlloc (uint32_t inframe, | + | <code cpp> |
| - | uint32_t * len, | + | uint8_t yourClassName::getFrameNumberNoAlloc (uint32_t inframe, |
| - | ADMImage * data, uint32_t * flags) | + | uint32_t * len, |
| - | { | + | ADMImage * data, uint32_t * flags) |
| - | ADM_assert(inframe<_info.nb_frames); // Make sure we don't go out of bounds | + | { |
| - | // Read frames for the previous | + | ADM_assert(inframe<_info.nb_frames); // Make sure we don't go out of bounds |
| - | ADM_assert(_in->getFrameNumberNoAlloc(inframe,len,_uncompressed,flags)); // Now _uncompressed contains the frame | + | // Read frames for the previous |
| - | // Flip from _uncompressed to data, which holds the final image | + | ADM_assert(_in->getFrameNumberNoAlloc(inframe,len,_uncompressed,flags)); // Now _uncompressed contains the frame |
| - | Return 1; // ok | + | // Flip from _uncompressed to data, which holds the final image |
| - | } | + | Return 1; // ok |
| + | } | ||
| + | </code> | ||
| - | Some notes : | + | Some notes: |
| - | * All images are in YV12 format | + | * All images are in YV12 format. |
| - | * If you need several images to render one image out (3:2 pulldown, noise removal,...), you should instantiate a VideoCache object | + | * If you need several images to render one image out (3:2 pulldown, noise removal,...), you should instantiate a VideoCache object. |
| - | ====== Declaring your filter ====== | + | ===== Declaring your filter ===== |
| - | There is 2 ways of declaring your filter : bundled or plugin. | + | There are 2 ways of declaring your filter: bundled or plugin. You can do both at the same time, in the same file. |
| - | You can do both at the same time, in the same file. | + | |
| - | ===== Bundled filter ===== | + | ==== Bundled filter ==== |
| - | Is is a bit more complicated. You have to : | + | Is is a bit more complicated. You have to: |
| * Declare a tag for your filter in ADM_filter/video_filters.h | * Declare a tag for your filter in ADM_filter/video_filters.h | ||
| * Register your filter in ADM_filter/filter_declaration.cpp using | * Register your filter in ADM_filter/filter_declaration.cpp using | ||
| - | <code cpp>REGISTERX("rotate","Rotate","Rotate the picture by 90, 180 or 270 degrees.",VF_ROTATE,1,rotate_create,rotate_script);</code> | + | <code cpp> |
| + | REGISTERX("rotate","Rotate","Rotate the picture by 90, 180 or 270 degrees.",VF_ROTATE,1,rotate_create,rotate_script); | ||
| + | </code> | ||
| - | The first parameter is the internal name (must be unique) | + | The first parameter is the internal name (must be unique). |
| - | The second and 3rd parameters are the one used for display a,d brief | + | The second and 3rd parameters are the one used for display a,d brief. |
| - | The Fourth one is the tag you declared above. DONT REUSE AN EXISTING TAG. You would have big problems, believe me. | + | The fourth one is the tag you declared above. **DON'T REUSE AN EXISTING TAG**. You would have big problems, believe me. |
| The 2 last ones must be the same as the function described below | The 2 last ones must be the same as the function described below | ||
| Let's go back to our filter C++ file. | Let's go back to our filter C++ file. | ||
| - | First you need to declare a const which looks like | + | First you need to declare a const which looks like |
| - | static FILTER_PARAM yourfilter_param = { 1,"angle"}; | + | |
| - | The first parameter is the number of parameters in your param structure followed by the parameters name, EXACTLY like in your structure. | + | |
| - | Then use these two macros : | + | |
| - | BUILD_CREATE (rotate_create, yourClassName); | + | <code cpp>static FILTER_PARAM yourfilter_param = { 1,"angle"};</code> |
| - | SCRIPT_CREATE (rotate_script, yourClassName, yourfilter_param); | + | |
| + | The first parameter is the number of parameters in your param structure followed by the parameters name, EXACTLY like in your structure. Then use these two macros: | ||
| + | |||
| + | <code cpp> | ||
| + | BUILD_CREATE (rotate_create, yourClassName); | ||
| + | SCRIPT_CREATE (rotate_script, yourClassName, yourfilter_param); | ||
| + | </code> | ||
| Make sure the first parameter of these two macros match the one you used in REGISTERX. | Make sure the first parameter of these two macros match the one you used in REGISTERX. | ||
| - | That's all :). | + | That's all :-). |
| - | ===== Plugin ===== | + | ==== Plugin ==== |
| It is simpler. | It is simpler. | ||
| - | You still have to declare the const which looks like | + | You still have to declare the const which looks like |
| - | static FILTER_PARAM yourfilter_param = { 1,"angle"}; | + | |
| - | Then | + | |
| - | extern "C" | + | <code cpp>static FILTER_PARAM yourfilter_param = { 1,"angle"};</code> |
| + | |||
| + | Then | ||
| + | |||
| + | <code cpp> | ||
| + | extern "C" | ||
| + | { | ||
| + | SCRIPT_CREATE(FILTER_create_fromscript,yourClassName,yourfilter_param); | ||
| + | BUILD_CREATE(FILTER_create,yourClassName); | ||
| + | char *FILTER_getName(void) | ||
| + | { | ||
| + | return "DynRotate"; // Name of your filter. must be unique! | ||
| + | } | ||
| + | char *FILTER_getDesc(void) | ||
| + | { | ||
| + | return "Blah blah"; // Short description of your filter | ||
| + | } | ||
| + | uint32_t FILTER_getVersion(void) | ||
| + | { | ||
| + | return 1; // Version of your filter | ||
| + | } | ||
| + | uint32_t FILTER_getAPIVersion(void) | ||
| { | { | ||
| - | SCRIPT_CREATE(FILTER_create_fromscript,yourClassName,yourfilter_param); | + | return ADM_FILTER_API_VERSION; |
| - | BUILD_CREATE(FILTER_create,yourClassName); | + | |
| - | char *FILTER_getName(void) | + | |
| - | { | + | |
| - | return "DynRotate"; // Name of your filter. must be unique! | + | |
| - | } | + | |
| - | char *FILTER_getDesc(void) | + | |
| - | { | + | |
| - | return "Blah blah"; // Short description of your filter | + | |
| - | } | + | |
| - | uint32_t FILTER_getVersion(void) | + | |
| - | { | + | |
| - | return 1; // Version of your filter | + | |
| - | } | + | |
| - | uint32_t FILTER_getAPIVersion(void) | + | |
| - | { | + | |
| - | return ADM_FILTER_API_VERSION; | + | |
| - | } | + | |
| } | } | ||
| + | } | ||
| + | </code> | ||
| It is self contained, you don't have to change any file in ADM_filter/xxx if it just a plugin. | It is self contained, you don't have to change any file in ADM_filter/xxx if it just a plugin. | ||
| - | You can do both declaration in the same file, so that it can be bundled inside avidemux or compiled as a plugin filter. | + | You can do both declaration in the same file, so that it can be bundled inside Avidemux or compiled as a plugin filter. |
| - | ====== Final words ====== | + | ===== Final words ===== |
| The simplest it to start from the ADM_filter/ADM_vidDummyFilter.cpp which is a very simple filter you can build using | The simplest it to start from the ADM_filter/ADM_vidDummyFilter.cpp which is a very simple filter you can build using | ||
| - | sh builddummy.sh on unix | ||
| - | or | ||
| - | sh builddummy_win32.sh on windows (you may have to change the path/names for your gcc) | ||
| - | The main differences with avisynth are : | + | //sh(nbsp)builddummy.sh// on Unix |
| - | * Image format is always yv12 | + | |
| - | * stride=width | + | |
| - | * You don't return the frame, it is given to you by the caller as argument | + | |
| + | or | ||
| + | //sh(nbsp)builddummy_win32.sh// on Windows (you may have to change the path/names for your gcc). | ||
| + | |||
| + | The main differences with Avisynth are: | ||
| + | * Image format is always YV12. | ||
| + | * stride=width | ||
| + | * You don't return the frame, it is given to you by the caller as argument. | ||