Pushing application data to a GPAC filter session using a custom filter

We saw previously that source filters generaly are file (or pipe/sockets/memory files) access objects.

But sometimes when integrating gpac with other pieces of software, the data may be available directly in the memory. so it can be beneficial to access data from memory within a gpac filters session.

The following is an example of a custom filter named “**mem_in**”  which is  source filter that provides a way to create a PID where the raw data is located in memory (ex audio/video frames located in memory). Allowing for the creation of a filter chain to process this data ( feeding the PID to other filters).


### Defining the process callback function

We start by defining a process callback, which the main logic will execute whenever our custom filter is called for a process execution by the the filters session:

```C
GF_Err  mem_in_process_ckb(GF_Filter  *filter) {
  GF_Err  gf_err  =  GF_OK;
  GF_FilterPid  *opid  =  NULL;

  opid  =  gf_filter_get_opid(filter, 0);
  if (!opid) {
    opid  =  gf_filter_pid_new(filter);
    Properties  properties[]  = {
          {.prop_4cc  =  GF_PROP_PID_CODECID,
        .val  = {.type  =  GF_PROP_UINT, .value.uint  =  GF_CODECID_AVC},
          .flags  =  GF_CAPS_INPUT},
    {0},
    };
    push_props(opid, properties);    
  }
  
  const  u8  *data  =  NULL;
  u32  data_size  =  0;
  u64  dts  =  0, pts  =  0;
  MemInCtx  *ctx  = (MemInCtx  *)gf_filter_get_rt_udta(filter);
  if (!ctx)
    return  GF_BAD_PARAM;

  ctx->parent  = (void  *)ctx;
  ctx->getData  =  &inputGetData;
  if (!ctx->getData(ctx->parent, &data, &data_size, &dts, &pts)){
    gf_filter_pid_set_eos(opid);
    return  GF_EOS;
  } 
  
  if (!data) {
     gf_filter_ask_rt_reschedule(filter, 1);
     return  GF_OK;
  }
  
  GF_FilterPacket  *pck  =gf_filter_pck_new_shared(opid, data, data_size, mem_in_pck_destructor);
  if (!pck) {
    gf_err  =  GF_OUT_OF_MEM;
    goto  exit;
  }
  
  gf_filter_pck_set_dts(pck, dts);
  gf_filter_pck_set_cts(pck, pts);
  gf_filter_pck_set_duration(pck, 1);
  gf_filter_pck_set_sap(pck, GF_FILTER_SAP_1);
  gf_filter_pck_set_framing(pck, GF_TRUE, GF_TRUE);
  gf_filter_pck_send(pck);
  
  exit:
  return  gf_err;
}
```

**Code explanation**

First thing we declare a PID that will serve us as an output PID, than we add the properties to this PID. Here for example we are adding the property  **GF_PROP_PID_CODECID**  with the value **GF_CODECID_AVC** to indicate that w are sending an AVC frame through this PID.

```C
opid  =  gf_filter_get_opid(filter, 0);
if (!opid) {
  opid  =  gf_filter_pid_new(filter);
  Properties  properties[]  = {
      {.prop_4cc  =  GF_PROP_PID_CODECID,
     .val  = {.type  =  GF_PROP_UINT, .value.uint  =  GF_CODECID_AVC},
       .flags  =  GF_CAPS_INPUT},
 {0},
};

  push_props(opid, properties);    
}
```

The push props function is defined below:

```C
static GF_Err push_props(GF_FilterPid *PID, Properties pid_props[])  
{  
GF_Err gf_err = GF_OK;  
int i = 0;  
while(pid_props[i].prop_4cc) {  
 if(pid_props[i].flags == GF_CAPS_INPUT) {  
   gf_filter_pid_set_property(PID, pid_props[i].prop_4cc, &pid_props[i].val);  
   }  
  i++;  
}}  
```

We loop through the props that we want to add (defined here through a specific struct). And add them through the [gf_filter_pid_set_property](https://doxygen.gpac.io/group__fs__pid.html#gaa9d532d9ca4c10a19973bcbd5e8af4fd) function.

An alternative way is to use the [gf_filter_push_caps()](https://doxygen.gpac.io/group__filters____cust__grp.html#ga878516ab46d5bfc96692a91f3ba6b124) function.

Our output PID and its properties are now configured, Next order of business is  to fetch the data from memory. We keep a reference to the internal data of the filter through the following struct. This allows us to pass references to the getData() and freeData() functions from the main program to the filters session internals, we also keep some metadata like the current decoding / presentation timestamps and the number of max frames we will process.   
 
```C
typedef  struct {
   void  *parent;
   Bool (*getData)(void  *parent, const  u8  **data, u32  *data_size, u64  *dts,
   u64  *pts);
   void (*freeData)(void  *parent, const  u8  *data);
   int  max_frames;
   u64  dts;
   u64  pts;
} MemInCtx;
```
 
With that in mind , lets return to our mem_in process callback:  

```C
const u8 *data  =  NULL;
u32  data_size  =  0;
u64  dts  =  0, pts  =  0;
MemInCtx  *ctx  = (MemInCtx  *)gf_filter_get_rt_udta(filter);
if (!ctx)
  return  GF_BAD_PARAM;
ctx->parent  = (void  *)ctx;
ctx->getData  =  &inputGetData;
if (!ctx->getData(ctx->parent, &data, &data_size, &dts, &pts)){
  gf_filter_pid_set_eos(opid);
  return  GF_EOS;
} 
if (!data) {
   gf_filter_ask_rt_reschedule(filter, 1);
   return  GF_OK;
} 
```

After initialisation of local variables, we use the [gf_filter_get_rt_udta()](https://doxygen.gpac.io/group__fs__filter.html#ga47b46ae728e700f983f53fdc069032f3) function to retrieve the user data that we set from the main function(see code bellow). This function is typically used by bindings and custom filters to share runtime data. 
  
Now we can access the data in memory using the getData(), in case the function is not available we send an End Of Stream signal through our output pid.

If there is no data available  we ask for rescheduling with [gf_filter_ask_rt_reschedule()](https://doxygen.gpac.io/group__fs__filter.html#ga36bb988aa964b3c6220aae11773d7c9e). 


Otherwise, we create a new packet to be shared, we set some packets properties and we send the packet upstream. 
  
```C
GF_FilterPacket *pck = gf_filter_pck_new_shared(opid, data, data_size, mem_in_pck_destructor);
if(!pck) { gf_err = GF_OUT_OF_MEM; goto exit; }
gf_filter_pck_set_dts(pck, dts);
gf_filter_pck_set_cts(pck, pts); 
gf_filter_pck_set_duration(pck, 1);
gf_filter_pck_set_sap(pck, GF_FILTER_SAP_1); 
gf_filter_pck_set_framing(pck, GF_TRUE, GF_TRUE);
gf_filter_pck_send(pck);
```

### Instantiation of the process callback

Once we defined our process callback we need to instantiate it and  assign it to our filter. so the logic of the filter will be executed each time the filter process is called by the gpac filters session. 

```C
// create a new GF_filter pointer called src_filter 
GF_Filter *src_filter = gf_fs_new_filter(session, "mem_in", 0, &gf_err);
// declare the mem_in process callback function if not defined in the same file
GF_Err mem_in_process_ckb(GF_Filter * filter);
// assign the callback to the filter   
gf_filter_set_process_ckb(src_filter, mem_in_process_ckb);
```

Our source filter is ready to be used by the filter session.