This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Appro Driver Pointer Rotation

I have been investigating a situation in the Appro software where one of my video tasks (analytics) does not do a transform, it just reads the video buffer pointer and then passes it along the chain.

In the usual operation:

  • The video tasks use the API in avserver.c to ask the previous video task for a full buffer pointer and asks the next task for an empty buffer pointer.
  • It then processes the full buffer pointer data into the empty buffer pointer.
  • It then passes the emptied full buffer pointer back to the previous video task as an empty buffer pointer and passes the filled empty buffer pointer to the next task as a full buffer pointer.

I now have a video task that:

  • Asks the previous task for a full buffer pointer and asks the next task for an empty buffer pointer.
  • Processes the data in the full buffer pointer
  • Swaps the full buffer pointer with the empty buffer pointer
  • It then passes the empty buffer pointer from the next task back to the previous task as an empty buffer pointer and passes the full buffer pointer from the previous task to the next task as a full buffer pointer.

I have implemented this functionality, however, had very curious streaming problems when the pass through video task was after the capture task.

After much debugging I found out that the capture task does not feed buffer pointers into the driver to fill. The way the capture task works is like so:

  • Ask driver for a full buffer pointer which is basically an index into an array of pointers.
  • Passes this full buffer on to the next task
  • Asks the next task for an empty buffer pointer.
  • Doesn't care what this empty buffer pointer is just say that the full buffer pointer that was given to me is now empty.

This is fine when the task after the capture task is a transpose task.  The next task will just 'copy' the data from the full buffer pointer provided by the driver into another empty buffer provided by the next task.

The problem occurs when the task after capture is returning a different empty buffer pointer to the one that the driver gave to capture.  For example in normal operation with a transpose task after capture:

  • Encode asks Capture for a full buffer pointer and Stream for an empty buffer pointer
  • Capture asks the IPIPE resize driver for a filled buffer pointer and it returns 0x8B7C6000 this is the pointer that is given to Encode.
  • Stream returns a pointer (0x8BA98000)  to one of it's allocated CMEM buffers that is empty.
  • Encode then processes the video information in  0x8B7C6000 ->  0x8BA98000
  • It then tells Stream that  0x8BA98000 is full and Stream starts it's processing.
  • It then tells Capture that  0x8B7C6000 is empty
  • Capture tells the IPIPE resize driver that the buffer pointer it returned (0x8B7C6000) is free.

In the situation where there isn't a transpose task after capture:

  • Analytics  asks Capture for a full buffer pointer and Encode for an empty buffer pointer
  • Capture asks the IPIPE resize driver for a filled buffer pointer and it returns 0x8B7C6000 this is the pointer that is given to Analytics.
  • Stream returns a pointer (0x8BA98000)  to one of it's allocated CMEM buffers that is empty.
  • Analytics then processes the video information in 0x8B7C6000
  • Analytics then swaps the pointers ( 0x8B7C6000 <-> 0x8BA98000) so that it can pass on the video information unmodified.
  • It then tells Encode that  0x8B7C6000 is full and Encode starts it's processing.
  • It then tells Capture that  0x8BA98000 is empty
  • Capture tells the IPIPE resize driver that the buffer pointer it returned  (0x8B7C6000) is free.
  • The driver is now filling a buffer that has been passed down the chain.

I have been looking at the driver code in the Appro framework.  I have noticed that the drv_ipipe.c allocates some contiguous memory:

gDRV_ipipeObj.bufInfoRsz[rszId][i].virtAddr = OSA_cmemAlloc(bufSize, 32);
gDRV_ipipeObj.bufInfoRsz[rszId][i].physAddr = OSA_cmemGetPhysAddr(gDRV_ipipeObj.bufInfoRsz[rszId][i].virtAddr);

These pointers are then passed to the CSL layer in the same file:

for(k=0; k<bufInit[i].numBuf; k++) {
    bufInit[i].bufAddr[k] = (Uint32)gDRV_ipipeObj.bufInfoRsz[i][k].physAddr + gDRV_ipipeObj.config.rszValidDataStartOffset;
}
[...snip...]
status = CSL_rszBufInit(&gCSL_rszHndl, i, &bufInit[i], &bufConfig[i]);

The CSL then takes these pointers and works out where to dump video information in:

CSL_Status CSL_rszBufInit(CSL_RszHandle hndl, Uint8 rszMod, CSL_BufInit * bufInit, CSL_RszBufConfig *bufConfig)
{
  CSL_Status status;
  Uint8 *pAddrY, *pAddrC;

  if (rszMod >= CSL_RSZ_CH_MAX)
    return CSL_EFAIL;;

  status = CSL_bufInit(&hndl->outBuf[rszMod], bufInit);

  hndl->yuv420BufCoffset[rszMod] = bufConfig->offsetH*bufConfig->offsetV;

  if(bufConfig->format==CSL_RSZ_CHROMA_FORMAT_422) {
    hndl->flipVOffsetY[rszMod] = bufConfig->offsetH*2*(bufConfig->height-1);
    hndl->flipHOffsetYC[rszMod] = bufConfig->width*2-1;
  } else {
    hndl->flipVOffsetY[rszMod] = bufConfig->offsetH*(bufConfig->height-1);
    hndl->flipHOffsetYC[rszMod] = bufConfig->width-1;
  }

  hndl->flipVOffsetC[rszMod] = bufConfig->offsetH * (bufConfig->height/2-1);

  hndl->curBufInfo[rszMod].id = CSL_BUF_ID_INVALID;

  pAddrY = (Uint8 *) bufInit->bufAddr[0];
  pAddrC = (Uint8 *)( bufInit->bufAddr[0] + hndl->yuv420BufCoffset[rszMod]);

  if(hndl->flipV[rszMod]) {
    pAddrY += hndl->flipVOffsetY[rszMod];
    pAddrC += hndl->flipVOffsetC[rszMod];
  }

  if(hndl->flipH[rszMod]) {
    pAddrY += hndl->flipHOffsetYC[rszMod];
    pAddrC += hndl->flipHOffsetYC[rszMod];
  }

  CSL_rszSetOutAddr(hndl, rszMod, pAddrY, pAddrC);

  return status;
}

These configured pointers are changed in CSL_rszSetOutAddr on an interupt in csl_ccdcInt.c.

It seems the drivers were written with no runtime support to change the output buffers, which is exactly what I need.  I feel it has been a huge oversight that the driver doesn't care what free buffer pointer is being passed back in videoCaptureThr.c.

So what I need is to able reconfigure the output pointers of the IPIPE at runtime.  I want to add an API call so that the pointer offsets calculated above are done for each frame.

I was not there when the drivers were designed and also have never written a linux kernel driver before.  I cannot comment on design decisions made when these were written.

Could anyone from TI or Appro guide me in modifing the driver in a sensible way so that it has the ability to support runtime buffer changes.  There are so many layers in the driver I do not know what I will be breaking when I modify the code.

Thanks,

Matt

  • Hi Matt,

    If i understand your requirement correctly...you want to insert analytics task between capture and encode tasks.

    Analytics task will get the full buffer from the capture task ,just see it and pass it on to the encode task.It is not going to modify the captured frame.

    I have an easy implementation in mind:

    Inside analytics task you get the full buffer from capture task by calling --

    pInBufInfo = AVSERVER_bufGetFull(VIDEO_TSK_ENCODE, streamId, &inBufId, OSA_TIMEOUT_FOREVER);

    Use the video frame in the full buffer to apply analytics and then pass it on to the encode task.

    Encode task will consume the full buffer and pass it back directly to the capture task.

    Now the question is how to pass the full buffer from Analytics task to the encode task,this you can implement by using message queue.

    After Analytics task has analysed the full buffer it will send a message to the encode task with the buffer address as parameter.Now encode task will wake up consume the full buffer and send back the buffer directly to the capture task.

    Does this approach look sensible?

    regards,

    Anand

     

  • Anand,

    You understand my situation correctly. I just want to clarify what will occur in your suggestion.

    My current situation is like so:

    .-----.     .-----.     .-----.     .-----.     .-----.
    | CAP |     |  1  |     | ANA |     |  4  |     | ENC |
    '-----'     |-----|     '-----'     |-----|     '-----'
                |  2  |                 |  5  | 
                |-----|                 |-----| 
                |  3  |                 |  6  | 
                '-----'                 '-----' 

    The stacks are the three pointers to CMEM allocated buffers.

    What I want to do is this. Capture (CAP) retrieves a buffer from the driver that is full.

    .-----.     .-----.     .-----.     .-----.     .-----.
    | CAP | --> | (1) |     | ANA |     |  4  |     | ENC |
    '-----'     |-----|     '-----'     |-----|     '-----'
                |  2  |                 |  5  | 
                |-----|                 |-----| 
                |  3  |                 |  6  | 
                '-----'                 '-----' 

    In putting the full buffer to the queue, this sends the message to Analytics (ANA) to process. AVSERVER_putBufFull() sends the message when it puts to the queue. Analytics then retrieves a full pointer from the previous queue and an empty one from the next queue.

    .-----.     .-----.     .-----.     .-----.     .-----.
    | CAP |     | (1) | --> | ANA | <-- |  4  |     | ENC |
    '-----'     |-----|     '-----'     |-----|     '-----'
                |  2  |                 |  5  | 
                |-----|                 |-----| 
                |  3  |                 |  6  | 
                '-----'                 '-----' 

    Analytics then processes its full buffer and passes on the pointer. It must pass an empty pointer back to the capture, otherwise capture will run out of empty buffers!

    .-----.     .-----.     .-----.     .-----.     .-----.
    | CAP |     |  4  | <-- | ANA | --> | (1) |     | ENC |
    '-----'     |-----|     '-----'     |-----|     '-----'
                |  2  |                 |  5  | 
                |-----|                 |-----| 
                |  3  |                 |  6  | 
                '-----'                 '-----' 

    In putting the buffer Encode (ENC) receives a message to get a full buffer from it's queue and start processing:

    .-----.     .-----.     .-----.     .-----.     .-----.
    | CAP |     |  4  |     | ANA |     | (1) | --> | ENC |
    '-----'     |-----|     '-----'     |-----|     '-----'
                |  2  |                 |  5  | 
                |-----|                 |-----| 
                |  3  |                 |  6  | 
                '-----'                 '-----' 

    This is where the current implementation breaks down as capture does not pass the buffer pointer back to the driver so the actual situation looks like this:

    .-----.     .-----.     .-----.     .-----.     .-----.
    | CAP |     |  1  |     | ANA |     | (1) | --> | ENC |
    '-----'     |-----|     '-----'     |-----|     '-----'
                |  2  |                 |  5  | 
                |-----|                 |-----| 
                |  3  |                 |  6  | 
                '-----'                 '-----' 

    You solution suggests using the same queue for both Analytics and Encode.

    Like before capture gets a buffer from the driver that is full.


    .-----.     .-----.     .-----.
    | CAP | --> | (1) |     | ANA |
    '-----'     |-----|     '-----'
                |  2  |
                |-----|     .-----.
                |  3  |     | ENC |
                '-----'     '-----'

    This then notifies Analytics that there is a full buffer in the queue. Analytics grabs it and starts processing


    .-----.     .-----.     .-----.
    | CAP |     | (1) | --> | ANA |
    '-----'     |-----|     '-----'
                |  2  |
                |-----|     .-----.
                |  3  |     | ENC |
                '-----'     '-----'

    After processing Analytics puts a full buffer back to the queue and notifies Encode that it should start processing.

    .-----.     .-----.     .-----.
    | CAP |     |  1  |  .- | ANA |
    '-----'     |-----|  |  '-----'
                | (2) | <'
                |-----|     .-----.
                |  3  |     | ENC |
                '-----'     '-----'

    Encode grabs the full buffer from the queue and starts processing it.

    .-----.     .-----.     .-----.
    | CAP |     |  1  |     | ANA |
    '-----'     |-----|     '-----'
                | (2) | -.
                |-----|  |  .-----.
                |  3  |  '> | ENC |
                '-----'     '-----'

    If Capture now puts another buffer to the queue:

    .-----.     .-----.     .-----.
    | CAP | -.  |  1  |     | ANA |
    '-----'  |  |-----|     '-----'
             |  | (2) | -.
             |  |-----|  |  .-----.
             '> | (3) |  '> | ENC |
                '-----'     '-----'

    This will notify Analytics that there is a full buffer in the queue and to start processing it. Analytics will start processing the most recent written buffer:

    .-----.     .-----.     .-----.
    | CAP |     |  1  |  .> | ANA |
    '-----'     |-----|  |  '-----'
                | (2) | -|
                |-----|  |  .-----.
                | (3) | -'> | ENC |
                '-----'     '-----'

    If I allocate twice the amount of buffer pointers I should be OK, correct?

    I'm just really worried about two tasks putting buffers to the same queue.  Is it not possible that between dumping a buffer on the queue and sending the message to the next task that a task could grab a wrong buffer?

    Matt

     

  • Hi Matt,

    First of all there are 2 seperate queues for full and empty buffers.

    'AVSERVER_bufPutFull' API puts the buffer into 'fullQue'.

    'AVSERVER_bufPutEmpty' API puts the buffer into 'emptyQue'.

    The 'fullQue' is between ENC and ANA and 'emptyQue' is between ENC and CAP.

    ANA gets the full buffer from 'fullQue',looks at the video frame and then just intimates the ENC by way of a message(buffer address can be passed as a message parameter) that a full buffer is available from the CAP,and note that ANA doesn't put buffer back  the queue.

    Now ENC will take the full buffer process it and then put it back to the 'emptyQue'.

    regards,

    Anand

  • Anand,

    Yep, I know about the empty and full buffers.  I was thinking that to preserve the architectural fact that buffers in the AV server task list will be passed through a queue, the Analytics would put back to a buffer that the Encode would get.

    We currently have a library that loads a .so that is a self contained AV server task.  The idea is that when we move to new Appro SDKs we can just drop the library in, load the .so (which there will be multiple) and chain them together in the AV server set up.  This ONLY fails if we load a non-transcode (buffer pass-through) task after the capture task.

    We will probably have multiple pass-through tasks in the future.  So we just wanted to modify the AV server code as little as possible.  What you are suggesting is modifying the way the encode task processes its AVSERVER_CMD_NEW_DATA command.  It will no longer use AVSERVER_bufGetFull() but instead use the incoming message parameters.

    If this is an unavoidable work around it'll have to happen.

    Is there any plans to make the driver runtime configurable?

    Matt