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

Copyright Datapath Ltd. 2014, 2015

File:    dgcmediahelper.c

Purpose: Helper functionality for Audio and Video media types.

History:
         09 JUN 14    JL   Created.

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

#if ( (defined WIN32) || (defined _WIN32) )
#include <Windows.h>
#endif

#include <dgctypes.h>
#include <dgcmedia.h>
#include <dgcmediahelper.h>

/******************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
/******************************************************************************/

#ifdef __cplusplus
void* dgcnew( uintptr_t size ) { return new uint8_t[size]; }
void  dgcdelete( void *pData ) { delete[] pData; }
PDGCALLOCFN DGCAllocFn = dgcnew;
PDGCFREEFN  DGCFreeFn = dgcdelete;
#else
PDGCALLOCFN DGCAllocFn = malloc;
PDGCFREEFN  DGCFreeFn = free;
#endif

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

typedef struct tagSubTypeInfo
{
   DGCMEDIASAMPLESUBTYPE   SubType;
   uint32_t                Bpp;
   uint32_t                NumPlanes;
} SubTypeInfo, *PSubTypeInfo;

SubTypeInfo gSubTypeInfo[] =
{
   { DGCMEDIASAMPLESUBTYPE_UNKNOWN, 0, 1 },
   { DGCRAWVIDEOSAMPLESUBTYPE_RGB555, 16, 1 },
   { DGCRAWVIDEOSAMPLESUBTYPE_RGB565, 16, 1 },
   { DGCRAWVIDEOSAMPLESUBTYPE_RGB888, 32, 1 },
   { DGCRAWVIDEOSAMPLESUBTYPE_YYYY, 8, 1 },
   { DGCRAWVIDEOSAMPLESUBTYPE_RGB24, 24, 1 },
   { DGCRAWVIDEOSAMPLESUBTYPE_YUY2, 16, 1 },
   { DGCRAWVIDEOSAMPLESUBTYPE_YVYU, 16, 1 },
   { DGCRAWVIDEOSAMPLESUBTYPE_UYVY, 16, 1 },
   { DGCRAWVIDEOSAMPLESUBTYPE_NV12, 12, 2 },
   { DGCRAWVIDEOSAMPLESUBTYPE_YV12, 12, 3 },
   { DGCRAWVIDEOSAMPLESUBTYPE_RGB10, 32, 1 },
   { DGCRAWVIDEOSAMPLESUBTYPE_Y410, 32, 1 },

   { DGCENCVIDEOSAMPLESUBTYPE_H264, 8, 1 },
};

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

uint32_t SubTypeGetIndex( DGCMEDIASAMPLESUBTYPE subType )
{
   uint32_t i;
   for ( i = 0; i < sizeof(gSubTypeInfo) / sizeof(SubTypeInfo); i++ )
   {
      if ( gSubTypeInfo[i].SubType == subType )
      {
         return i;
      }
   }
   return 0; // Unknown
}

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

uint32_t SubTypeGetBitDepth( DGCMEDIASAMPLESUBTYPE subType )
{
   return gSubTypeInfo[SubTypeGetIndex( subType )].Bpp;
}

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

uint32_t SubTypeGetNumPlanes( DGCMEDIASAMPLESUBTYPE subType )
{
   return gSubTypeInfo[SubTypeGetIndex( subType )].NumPlanes;
}

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

uint8_t SubTypeGetPlaneSize( DGCMEDIASAMPLESUBTYPE subType,
                             uint16_t plane,
                             uint32_t width,
                             uint32_t height,
                             uint32_t *pSize )
{
   uint32_t numplanes = SubTypeGetNumPlanes( subType );
   uint32_t bitdepth = SubTypeGetBitDepth( subType );

   if ( (numplanes == 0) || (bitdepth == 0) )
   {
      return FALSE; // unknown
   }

   if ( plane >= numplanes )
   {
      return FALSE; // invalid
   }

   if ( numplanes == 1 )
   {
      *pSize = width * height * bitdepth / 8;
   }
   else
   {
      switch ( subType )
      {
         case DGCRAWVIDEOSAMPLESUBTYPE_NV12:
         {
            if ( plane == 0 )
            {
               *pSize = width * height;
            }
            else
            {
               *pSize = width * height / 2;
            }
            break;
         }

         case DGCRAWVIDEOSAMPLESUBTYPE_YV12:
         {
            if ( plane == 0 )
            {
               *pSize = width * height;
            }
            else
            {
               *pSize = width * height / 4;
            }
            break;
         }

         default:
            return FALSE;
      }
   }

   return TRUE;
}

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

uint8_t MediaSampleSetAllocFunctionPair( PDGCALLOCFN allocFn,
                                         PDGCFREEFN  freeFn )
{
   if ( allocFn && freeFn )
   {
      DGCAllocFn = allocFn;
      DGCFreeFn = freeFn;
      return TRUE;
   }
   return FALSE;
}

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

PDGCMEDIASAMPLE MediaSampleAllocate( DGCMEDIASAMPLETYPE     type,
                                     DGCMEDIASAMPLESUBTYPE  subType )
{
   uint32_t mediaSampleSize = sizeof(DGCMEDIASAMPLE);
   PDGCMEDIASAMPLE pMediaSample =
      (PDGCMEDIASAMPLE) DGCAllocFn( mediaSampleSize );
   if ( pMediaSample )
   {
      pMediaSample->Size = mediaSampleSize;
      pMediaSample->MajorType = type;
      pMediaSample->SubType = subType;
      pMediaSample->PFormatHeader = NULL;
      pMediaSample->BufferHeaderType = DGCBUFFERHEADERTYPE_UNKNOWN;
      pMediaSample->PBufferHeader = NULL;
   }

   return pMediaSample;
}

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

uint8_t MediaSampleAllocateFormatHeader( PDGCMEDIASAMPLE pMediaSample )
{
   uint8_t bReturn = FALSE;
   void *pFormatHeader = NULL;

   if ( (pMediaSample->MajorType == DGCMEDIASAMPLETYPE_RAW_VIDEO) ||
        (pMediaSample->MajorType == DGCMEDIASAMPLETYPE_ENC_VIDEO) )
   {
      uint32_t headerSize = sizeof(DGCVIDEOHEADER);
      pFormatHeader = DGCAllocFn( headerSize );
      if ( pFormatHeader )
      {
         PDGCVIDEOHEADER pVideoHeader = (PDGCVIDEOHEADER) pFormatHeader;
         pVideoHeader->Size = headerSize;
         pVideoHeader->Flags = 0;
         pVideoHeader->Width = 0;
         pVideoHeader->Height = 0;
         pVideoHeader->FrameRate = 0;
         bReturn = TRUE;
      }
   }/*
   else if ( (pMediaSample->MajorType == DGCMEDIASAMPLETYPE_RAW_AUDIO) ||
             (pMediaSample->MajorType == DGCMEDIASAMPLETYPE_ENC_AUDIO) )
   {
      uint32_t headerSize = sizeof(DGCAUDIOHEADER);
      pFormatHeader = DGCAllocFn( headerSize );
      if ( pFormatHeader )
      {
         PDGCAUDIOHEADER pVideoHeader = (PDGCAUDIOHEADER) pFormatHeader;
         pVideoHeader->Size = headerSize;
         pVideoHeader->Flags = 0;
         pVideoHeader->SampleDepth = 0;
         pVideoHeader->SampleFrequency = 0;
         bReturn = TRUE;
      }
   }
   */
   pMediaSample->PFormatHeader = pFormatHeader;

   return bReturn;
}

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

uint8_t MediaSampleAllocateBufferHeader( PDGCMEDIASAMPLE       pMediaSample,
                                         DGCBUFFERHEADERTYPE   bufferType )
{
   uint8_t  bReturn = FALSE;
   uint32_t numPlanes = SubTypeGetNumPlanes( pMediaSample->SubType );
   void *pBufferHeader = NULL;

   switch ( bufferType )
   {
      case DGCBUFFERHEADERTYPE_MEMORY:
      {
         uint32_t bufferSize = sizeof(DGCMEMORYBUFFERHEADER);
         pBufferHeader = DGCAllocFn( bufferSize );
         if ( pBufferHeader )
         {
            uint32_t i;
            PDGCMEMORYBUFFERHEADER pMemoryBufferHeader =
               (PDGCMEMORYBUFFERHEADER)pBufferHeader;
            
            pMemoryBufferHeader->Size = sizeof(DGCMEMORYBUFFERHEADER);
            pMemoryBufferHeader->Flags = 0;
            pMemoryBufferHeader->StartTime = 0;
            pMemoryBufferHeader->EndTime = 0;
            pMemoryBufferHeader->NumberOfPlanes = numPlanes;

            for ( i = 0; i < numPlanes; i++ )
            {
               PDGCMEMORYBUFFER pDesc = &pMemoryBufferHeader->Planes[i];
               if ( pDesc )
               {
                  pDesc->Size = sizeof(DGCMEMORYBUFFER);
                  pDesc->Flags = 0;
                  pDesc->PBuffer = NULL;
                  pDesc->Length = 0;
                  pDesc->Pitch = 0;
                  pDesc->OffsetX = 0;
                  pDesc->OffsetY = 0;
                  pDesc->ActualLength = 0;
               }
            }

            bReturn = TRUE;
         }
         break;
      }
   }

   pMediaSample->BufferHeaderType = bufferType;
   pMediaSample->PBufferHeader = pBufferHeader;

   return bReturn;
}

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

uint8_t MediaSampleInitialisePlane( PDGCMEDIASAMPLE       pMediaSample,
                                    uint16_t              plane,
                                    uint32_t              bufferSize )
{
   uint8_t  bReturn = FALSE;

   switch ( pMediaSample->BufferHeaderType )
   {
      case DGCBUFFERHEADERTYPE_MEMORY:
      {
         PDGCMEMORYBUFFERHEADER pMemoryBufferHeader =
            (PDGCMEMORYBUFFERHEADER) pMediaSample->PBufferHeader;
         if ( pMemoryBufferHeader )
         {
            if ( pMemoryBufferHeader->NumberOfPlanes > plane )
            {
               PDGCMEMORYBUFFER pDesc =
                  &pMemoryBufferHeader->Planes[plane];

               pDesc->PBuffer = DGCAllocFn( bufferSize );
               if ( pDesc->PBuffer )
               {
                  pDesc->Length = bufferSize;
                  bReturn = TRUE;
               }
            }
         }

         break;
      }
   }

   return bReturn;
}

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

uint8_t MediaSampleInitialisePlaneWH( PDGCMEDIASAMPLE       pMediaSample,
                                      uint16_t              plane,
                                      uint32_t              width,
                                      uint32_t              height )
{
   uint8_t bReturn = FALSE;

   if ( pMediaSample->MajorType == DGCMEDIASAMPLETYPE_RAW_VIDEO )
   {
      uint32_t bufferSize;

      bReturn = SubTypeGetPlaneSize( pMediaSample->SubType, plane,
                                     width, height, &bufferSize );
      if ( bReturn )
      {
         bReturn = MediaSampleInitialisePlane( pMediaSample,
                                               plane,
                                               bufferSize );
      }
   }

   return bReturn;
}

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

void MediaSampleFree( PDGCMEDIASAMPLE pMediaSample )
{
   if ( pMediaSample )
   {
      if ( pMediaSample->PFormatHeader )
      {
         DGCFreeFn( pMediaSample->PFormatHeader );
      }

      if ( pMediaSample->PBufferHeader )
      {
         switch ( pMediaSample->BufferHeaderType )
         {
            case DGCBUFFERHEADERTYPE_MEMORY:
            {
               uint16_t i;
               PDGCMEMORYBUFFERHEADER pMemoryBufferHeader =
                  (PDGCMEMORYBUFFERHEADER) pMediaSample->PBufferHeader;

               for ( i = 0; i < pMemoryBufferHeader->NumberOfPlanes; i++ )
               {
                  if ( pMemoryBufferHeader->Planes[i].PBuffer )
                  {
                     DGCFreeFn( pMemoryBufferHeader->Planes[i].PBuffer );
                  }
               }
               break;
            }
         }

         DGCFreeFn( pMediaSample->PBufferHeader );
      }

      DGCFreeFn( pMediaSample );
   }
}

/******************************************************************************/
#ifdef __cplusplus
}
#endif
/******************************************************************************/
