Home | History | Annotate | Download | only in src
      1 /*
      2  * portbase.cpp, base port class
      3  *
      4  * Copyright (c) 2009-2010 Wind River Systems, Inc.
      5  *
      6  * Licensed under the Apache License, Version 2.0 (the "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  * http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 
     19 #include <errno.h>
     20 #include <stdlib.h>
     21 #include <string.h>
     22 
     23 #include <OMX_Core.h>
     24 #include <OMX_Component.h>
     25 
     26 #include <portbase.h>
     27 #include <componentbase.h>
     28 //#define LOG_NDEBUG 0
     29 
     30 #define LOG_TAG "portbase"
     31 #include <log.h>
     32 
     33 /*
     34  * constructor & destructor
     35  */
     36 void PortBase::__PortBase(void)
     37 {
     38     buffer_hdrs = NULL;
     39     nr_buffer_hdrs = 0;
     40     buffer_hdrs_completion = false;
     41 
     42     custom_mem_alloc = NULL;
     43     custom_mem_free = NULL;
     44     custom_mem_userdata = NULL;
     45 
     46     mem_alignment = 0;
     47 
     48     pthread_mutex_init(&hdrs_lock, NULL);
     49     pthread_cond_init(&hdrs_wait, NULL);
     50 
     51     __queue_init(&bufferq);
     52     pthread_mutex_init(&bufferq_lock, NULL);
     53 
     54     __queue_init(&retainedbufferq);
     55     pthread_mutex_init(&retainedbufferq_lock, NULL);
     56 
     57     __queue_init(&markq);
     58     pthread_mutex_init(&markq_lock, NULL);
     59 
     60     state = OMX_PortEnabled;
     61     pthread_mutex_init(&state_lock, NULL);
     62 
     63     memset(&portdefinition, 0, sizeof(portdefinition));
     64     ComponentBase::SetTypeHeader(&portdefinition, sizeof(portdefinition));
     65     memset(definition_format_mimetype, 0, OMX_MAX_STRINGNAME_SIZE);
     66     portdefinition.format.audio.cMIMEType = &definition_format_mimetype[0];
     67     portdefinition.format.video.cMIMEType = &definition_format_mimetype[0];
     68     portdefinition.format.image.cMIMEType = &definition_format_mimetype[0];
     69 
     70     memset(&audioparam, 0, sizeof(audioparam));
     71 
     72     owner = NULL;
     73     appdata = NULL;
     74     callbacks = NULL;
     75 
     76     cbase = NULL;
     77 }
     78 
     79 PortBase::PortBase()
     80 {
     81     __PortBase();
     82 }
     83 
     84 PortBase::PortBase(const OMX_PARAM_PORTDEFINITIONTYPE *portdefinition)
     85 {
     86     __PortBase();
     87     SetPortDefinition(portdefinition, true);
     88 }
     89 
     90 PortBase::~PortBase()
     91 {
     92     struct list *entry, *temp;
     93 
     94     /* should've been already freed at FreeBuffer() */
     95     list_foreach_safe(buffer_hdrs, entry, temp) {
     96         free(entry->data); /* OMX_BUFFERHEADERTYPE */
     97         __list_delete(buffer_hdrs, entry);
     98     }
     99 
    100     pthread_cond_destroy(&hdrs_wait);
    101     pthread_mutex_destroy(&hdrs_lock);
    102 
    103     /* should've been already freed at buffer processing */
    104     queue_free_all(&bufferq);
    105     pthread_mutex_destroy(&bufferq_lock);
    106 
    107     /* should've been already freed at buffer processing */
    108     queue_free_all(&retainedbufferq);
    109     pthread_mutex_destroy(&retainedbufferq_lock);
    110 
    111     /* should've been already empty in PushThisBuffer () */
    112     queue_free_all(&markq);
    113     pthread_mutex_destroy(&markq_lock);
    114 
    115     pthread_mutex_destroy(&state_lock);
    116 }
    117 
    118 /* end of constructor & destructor */
    119 
    120 /*
    121  * accessor
    122  */
    123 /* owner */
    124 void PortBase::SetOwner(OMX_COMPONENTTYPE *handle)
    125 {
    126     owner = handle;
    127     cbase = static_cast<ComponentBase *>(handle->pComponentPrivate);
    128 }
    129 
    130 OMX_COMPONENTTYPE *PortBase::GetOwner(void)
    131 {
    132     return owner;
    133 }
    134 
    135 OMX_ERRORTYPE PortBase::SetCallbacks(OMX_HANDLETYPE hComponent,
    136                                      OMX_CALLBACKTYPE *pCallbacks,
    137                                      OMX_PTR pAppData)
    138 {
    139     if (owner != hComponent)
    140         return OMX_ErrorBadParameter;
    141 
    142     appdata = pAppData;
    143     callbacks = pCallbacks;
    144 
    145     return OMX_ErrorNone;
    146 }
    147 
    148 OMX_ERRORTYPE PortBase::SetMemAllocator(CustomMemAlloc *pMemAlloc, CustomMemFree *pMemFree, OMX_PTR pUserData)
    149 {
    150     custom_mem_alloc = pMemAlloc;
    151     custom_mem_free = pMemFree;
    152     custom_mem_userdata = pUserData;
    153     return OMX_ErrorNone;
    154 }
    155 
    156 OMX_ERRORTYPE PortBase::SetMemAlignment(OMX_U32 nAlignment)
    157 {
    158     mem_alignment = nAlignment;
    159     return OMX_ErrorNone;
    160 }
    161 
    162 OMX_U32 PortBase::getFrameBufSize(OMX_COLOR_FORMATTYPE colorFormat, OMX_U32 width, OMX_U32 height)
    163 {
    164     switch (colorFormat) {
    165     case OMX_COLOR_FormatYCbYCr:
    166     case OMX_COLOR_FormatCbYCrY:
    167         return width * height * 2;
    168     case OMX_COLOR_FormatYUV420Planar:
    169     case OMX_COLOR_FormatYUV420SemiPlanar:
    170         return (width * height * 3) >> 1;
    171 
    172     default:
    173         LOGV("unsupport color format !");
    174         return -1;
    175     }
    176 }
    177 
    178 /* end of accessor */
    179 
    180 /*
    181  * component methods & helpers
    182  */
    183 /* Get/SetParameter */
    184 OMX_ERRORTYPE PortBase::SetPortDefinition(
    185     const OMX_PARAM_PORTDEFINITIONTYPE *p, bool overwrite_readonly)
    186 {
    187     OMX_PARAM_PORTDEFINITIONTYPE temp;
    188 
    189     memcpy(&temp, &portdefinition, sizeof(temp));
    190 
    191     if (!overwrite_readonly) {
    192         if (temp.nPortIndex != p->nPortIndex)
    193             return OMX_ErrorBadParameter;
    194         if (temp.eDir != p->eDir)
    195             return OMX_ErrorBadParameter;
    196         if (temp.eDomain != p->eDomain)
    197             return OMX_ErrorBadParameter;
    198         if (temp.nBufferCountActual != p->nBufferCountActual) {
    199             if (temp.nBufferCountMin > p->nBufferCountActual)
    200                 return OMX_ErrorBadParameter;
    201             temp.nBufferCountActual = p->nBufferCountActual;
    202         }
    203         if ((p->nBufferSize > temp.nBufferSize) && (temp.eDir == OMX_DirInput)) {
    204             if (p->nBufferSize <= MAX_INPUT_PORT_SIZE) {
    205                 LOGW("Input port size has been changed!");
    206                 temp.nBufferSize = p->nBufferSize;
    207             } else {
    208                 LOGE("Invalid input port size!");
    209                 return OMX_ErrorBadParameter;
    210             }
    211         }
    212     }
    213     else {
    214         temp.nPortIndex = p->nPortIndex;
    215         temp.eDir = p->eDir;
    216         temp.nBufferCountActual = p->nBufferCountActual;
    217         temp.nBufferCountMin = p->nBufferCountMin;
    218         temp.nBufferSize = p->nBufferSize;
    219         temp.bEnabled = p->bEnabled;
    220         temp.bPopulated = p->bPopulated;
    221         temp.eDomain = p->eDomain;
    222         temp.bBuffersContiguous = p->bBuffersContiguous;
    223         temp.nBufferAlignment = p->nBufferAlignment;
    224     }
    225 
    226     switch (p->eDomain) {
    227     case OMX_PortDomainAudio: {
    228         OMX_AUDIO_PORTDEFINITIONTYPE *format = &temp.format.audio;
    229         const OMX_AUDIO_PORTDEFINITIONTYPE *pformat = &p->format.audio;
    230         OMX_U32 mimetype_len = strlen(pformat->cMIMEType);
    231 
    232         mimetype_len = OMX_MAX_STRINGNAME_SIZE-1 > mimetype_len ?
    233                        mimetype_len : OMX_MAX_STRINGNAME_SIZE-1;
    234 
    235         strncpy(format->cMIMEType, pformat->cMIMEType,
    236                 mimetype_len);
    237         format->cMIMEType[mimetype_len+1] = '\0';
    238         format->pNativeRender = pformat->pNativeRender;
    239         format->bFlagErrorConcealment = pformat->bFlagErrorConcealment;
    240         format->eEncoding = pformat->eEncoding;
    241 
    242         break;
    243     }
    244     case OMX_PortDomainVideo: {
    245         OMX_VIDEO_PORTDEFINITIONTYPE *format = &temp.format.video;
    246         const OMX_VIDEO_PORTDEFINITIONTYPE *pformat = &p->format.video;
    247         OMX_U32 mimetype_len = strlen(pformat->cMIMEType);
    248 
    249         mimetype_len = OMX_MAX_STRINGNAME_SIZE-1 > mimetype_len ?
    250                        mimetype_len : OMX_MAX_STRINGNAME_SIZE-1;
    251 
    252         strncpy(format->cMIMEType, pformat->cMIMEType,
    253                 mimetype_len);
    254         format->cMIMEType[mimetype_len+1] = '\0';
    255         format->pNativeRender = pformat->pNativeRender;
    256         format->nFrameWidth = pformat->nFrameWidth;
    257         format->nFrameHeight = pformat->nFrameHeight;
    258         format->nBitrate = pformat->nBitrate;
    259         format->xFramerate = pformat->xFramerate;
    260         format->bFlagErrorConcealment = pformat->bFlagErrorConcealment;
    261         format->eCompressionFormat = pformat->eCompressionFormat;
    262         format->eColorFormat = pformat->eColorFormat;
    263         format->pNativeWindow = pformat->pNativeWindow;
    264         OMX_S32 nFrameSize = getFrameBufSize(format->eColorFormat,format->nFrameWidth,format->nFrameHeight);
    265         if(nFrameSize!=-1)
    266             temp.nBufferSize = nFrameSize;
    267         if (overwrite_readonly) {
    268             format->nStride = pformat->nStride;
    269             format->nSliceHeight = pformat->nSliceHeight;
    270         } else {
    271             format->nStride = pformat->nFrameWidth;
    272             format->nSliceHeight = pformat->nFrameHeight;
    273         }
    274 
    275         break;
    276     }
    277     case OMX_PortDomainImage: {
    278         OMX_IMAGE_PORTDEFINITIONTYPE *format = &temp.format.image;
    279         const OMX_IMAGE_PORTDEFINITIONTYPE *pformat = &p->format.image;
    280         OMX_U32 mimetype_len = strlen(pformat->cMIMEType);
    281 
    282         mimetype_len = OMX_MAX_STRINGNAME_SIZE-1 > mimetype_len ?
    283                        mimetype_len : OMX_MAX_STRINGNAME_SIZE-1;
    284 
    285         strncpy(format->cMIMEType, pformat->cMIMEType,
    286                 mimetype_len+1);
    287         format->cMIMEType[mimetype_len+1] = '\0';
    288         format->nFrameWidth = pformat->nFrameWidth;
    289         format->nFrameHeight = pformat->nFrameHeight;
    290         format->nStride = pformat->nStride;
    291         format->bFlagErrorConcealment = pformat->bFlagErrorConcealment;
    292         format->eCompressionFormat = pformat->eCompressionFormat;
    293         format->eColorFormat = pformat->eColorFormat;
    294         format->pNativeWindow = pformat->pNativeWindow;
    295 
    296         if (overwrite_readonly)
    297             format->nSliceHeight = pformat->nSliceHeight;
    298 
    299         break;
    300     }
    301     case OMX_PortDomainOther: {
    302         OMX_OTHER_PORTDEFINITIONTYPE *format = &temp.format.other;
    303         const OMX_OTHER_PORTDEFINITIONTYPE *pformat = &p->format.other;
    304 
    305         format->eFormat = pformat->eFormat;
    306         break;
    307     }
    308     default:
    309         LOGE("cannot find 0x%08x port domain\n", p->eDomain);
    310         return OMX_ErrorBadParameter;
    311     }
    312 
    313     memcpy(&portdefinition, &temp, sizeof(temp));
    314     return OMX_ErrorNone;
    315 }
    316 
    317 const OMX_PARAM_PORTDEFINITIONTYPE *PortBase::GetPortDefinition(void)
    318 {
    319     return &portdefinition;
    320 }
    321 
    322 /* Use/Allocate/FreeBuffer */
    323 OMX_ERRORTYPE PortBase::UseBuffer(OMX_BUFFERHEADERTYPE **ppBufferHdr,
    324                                   OMX_U32 nPortIndex,
    325                                   OMX_PTR pAppPrivate,
    326                                   OMX_U32 nSizeBytes,
    327                                   OMX_U8 *pBuffer)
    328 {
    329     OMX_BUFFERHEADERTYPE *buffer_hdr;
    330     struct list *entry;
    331 
    332     LOGV("%s(): %s:%s:PortIndex %u: enter, nSizeBytes=%u\n", __FUNCTION__,
    333          cbase->GetName(), cbase->GetWorkingRole(), nPortIndex, nSizeBytes);
    334 
    335     pthread_mutex_lock(&hdrs_lock);
    336 
    337     if (portdefinition.bPopulated == OMX_TRUE) {
    338         pthread_mutex_unlock(&hdrs_lock);
    339         LOGV("%s(): %s:%s:PortIndex %u: exit done, already populated\n",
    340              __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    341              nPortIndex);
    342         return OMX_ErrorNone;
    343     }
    344 
    345     buffer_hdr = (OMX_BUFFERHEADERTYPE *)calloc(1, sizeof(*buffer_hdr));
    346     if (!buffer_hdr) {
    347         pthread_mutex_unlock(&hdrs_lock);
    348         LOGE("%s(): %s:%s:PortIndex %u: exit failure, "
    349              "connot allocate buffer header\n", __FUNCTION__,
    350              cbase->GetName(), cbase->GetWorkingRole(), nPortIndex);
    351         return OMX_ErrorInsufficientResources;
    352     }
    353 
    354     entry = list_alloc(buffer_hdr);
    355     if (!entry) {
    356         free(buffer_hdr);
    357         pthread_mutex_unlock(&hdrs_lock);
    358         LOGE("%s(): %s:%s:PortIndex %u: exit failure, "
    359              "cannot allocate list entry\n", __FUNCTION__,
    360              cbase->GetName(), cbase->GetWorkingRole(), nPortIndex);
    361         return OMX_ErrorInsufficientResources;
    362     }
    363 
    364     ComponentBase::SetTypeHeader(buffer_hdr, sizeof(*buffer_hdr));
    365     buffer_hdr->pBuffer = pBuffer;
    366     buffer_hdr->nAllocLen = nSizeBytes;
    367     buffer_hdr->pAppPrivate = pAppPrivate;
    368     buffer_hdr->pInputPortPrivate = NULL;
    369     buffer_hdr->pOutputPortPrivate = NULL;
    370     if (portdefinition.eDir == OMX_DirInput) {
    371         buffer_hdr->nInputPortIndex = nPortIndex;
    372         buffer_hdr->nOutputPortIndex = 0x7fffffff;
    373     }
    374     else {
    375         buffer_hdr->nOutputPortIndex = nPortIndex;
    376         buffer_hdr->nInputPortIndex = 0x7fffffff;
    377     }
    378 
    379     buffer_hdrs = __list_add_tail(buffer_hdrs, entry);
    380     nr_buffer_hdrs++;
    381 
    382     LOGV("%s(): %s:%s:PortIndex %u: a buffer allocated (%p:%u/%u)\n",
    383          __FUNCTION__,
    384          cbase->GetName(), cbase->GetWorkingRole(), nPortIndex,
    385          buffer_hdr, nr_buffer_hdrs, portdefinition.nBufferCountActual);
    386 
    387     if (nr_buffer_hdrs >= portdefinition.nBufferCountActual) {
    388         portdefinition.bPopulated = OMX_TRUE;
    389         buffer_hdrs_completion = true;
    390         pthread_cond_signal(&hdrs_wait);
    391         LOGV("%s(): %s:%s:PortIndex %u: allocate all buffers (%u)\n",
    392              __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    393              nPortIndex, portdefinition.nBufferCountActual);
    394     }
    395 
    396     *ppBufferHdr = buffer_hdr;
    397 
    398     pthread_mutex_unlock(&hdrs_lock);
    399 
    400     LOGV("%s(): %s:%s:PortIndex %u: exit done\n", __FUNCTION__,
    401          cbase->GetName(), cbase->GetWorkingRole(), nPortIndex);
    402     return OMX_ErrorNone;
    403 }
    404 
    405 OMX_ERRORTYPE PortBase:: AllocateBuffer(OMX_BUFFERHEADERTYPE **ppBuffer,
    406                                        OMX_U32 nPortIndex,
    407                                        OMX_PTR pAppPrivate,
    408                                        OMX_U32 nSizeBytes)
    409 {
    410     OMX_BUFFERHEADERTYPE *buffer_hdr;
    411     struct list *entry;
    412 
    413     LOGV("%s(): %s:%s:PortIndex %u: enter, nSizeBytes=%u\n", __FUNCTION__,
    414          cbase->GetName(), cbase->GetWorkingRole(), nPortIndex, nSizeBytes);
    415 
    416     pthread_mutex_lock(&hdrs_lock);
    417     if (portdefinition.bPopulated == OMX_TRUE) {
    418         pthread_mutex_unlock(&hdrs_lock);
    419         LOGV("%s(): %s:%s:PortIndex %u: exit done, already populated\n",
    420              __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    421              nPortIndex);
    422         return OMX_ErrorNone;
    423     }
    424 
    425     if (custom_mem_alloc) {
    426         buffer_hdr = (OMX_BUFFERHEADERTYPE *) calloc(1, sizeof(*buffer_hdr));
    427     } else {
    428         if (mem_alignment > 0)
    429             buffer_hdr = (OMX_BUFFERHEADERTYPE *) calloc(1, sizeof(*buffer_hdr) + nSizeBytes + mem_alignment);
    430         else
    431             buffer_hdr = (OMX_BUFFERHEADERTYPE *) calloc(1, sizeof(*buffer_hdr) + nSizeBytes);
    432     }
    433 
    434     if (!buffer_hdr) {
    435         pthread_mutex_unlock(&hdrs_lock);
    436         LOGE("%s(): %s:%s:PortIndex %u: exit failure, "
    437              "connot allocate buffer header\n", __FUNCTION__,
    438              cbase->GetName(), cbase->GetWorkingRole(), nPortIndex);
    439         return OMX_ErrorInsufficientResources;
    440     }
    441 
    442     entry = list_alloc(buffer_hdr);
    443     if (!entry) {
    444         free(buffer_hdr);
    445         pthread_mutex_unlock(&hdrs_lock);
    446         LOGE("%s(): %s:%s:PortIndex %u: exit failure, "
    447              "connot allocate list entry\n", __FUNCTION__,
    448              cbase->GetName(), cbase->GetWorkingRole(), nPortIndex);
    449         return OMX_ErrorInsufficientResources;
    450     }
    451 
    452     ComponentBase::SetTypeHeader(buffer_hdr, sizeof(*buffer_hdr));
    453     if (custom_mem_alloc) {
    454         buffer_hdr->pBuffer = (*custom_mem_alloc)(nSizeBytes, custom_mem_userdata);
    455     } else {
    456         if (mem_alignment > 0)
    457             buffer_hdr->pBuffer = (OMX_U8 *)(((OMX_U32)((OMX_U8 *)buffer_hdr + sizeof(*buffer_hdr)) / mem_alignment + 1) * mem_alignment);
    458         else
    459             buffer_hdr->pBuffer = (OMX_U8 *)buffer_hdr + sizeof(*buffer_hdr);
    460     }
    461     if (buffer_hdr->pBuffer == NULL) {
    462         return OMX_ErrorInsufficientResources;
    463     }
    464 
    465     buffer_hdr->nAllocLen = nSizeBytes;
    466     buffer_hdr->pAppPrivate = pAppPrivate;
    467     buffer_hdr->pInputPortPrivate = NULL;
    468     buffer_hdr->pOutputPortPrivate = NULL;
    469     if (portdefinition.eDir == OMX_DirInput) {
    470         buffer_hdr->nInputPortIndex = nPortIndex;
    471         buffer_hdr->nOutputPortIndex = (OMX_U32)-1;
    472     }
    473     else {
    474         buffer_hdr->nOutputPortIndex = nPortIndex;
    475         buffer_hdr->nInputPortIndex = (OMX_U32)-1;
    476     }
    477 
    478     buffer_hdrs = __list_add_tail(buffer_hdrs, entry);
    479     nr_buffer_hdrs++;
    480 
    481     LOGV("%s(): %s:%s:PortIndex %u: a buffer allocated (%p:%u/%u)\n",
    482          __FUNCTION__,
    483          cbase->GetName(), cbase->GetWorkingRole(), nPortIndex,
    484          buffer_hdr, nr_buffer_hdrs, portdefinition.nBufferCountActual);
    485 
    486     if (nr_buffer_hdrs == portdefinition.nBufferCountActual) {
    487         portdefinition.bPopulated = OMX_TRUE;
    488         buffer_hdrs_completion = true;
    489         pthread_cond_signal(&hdrs_wait);
    490         LOGV("%s(): %s:%s:PortIndex %u: allocate all buffers (%u)\n",
    491              __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    492              nPortIndex, portdefinition.nBufferCountActual);
    493     }
    494 
    495     *ppBuffer = buffer_hdr;
    496 
    497     pthread_mutex_unlock(&hdrs_lock);
    498 
    499     LOGV("%s(): %s:%s:PortIndex %u: exit done\n", __FUNCTION__,
    500          cbase->GetName(), cbase->GetWorkingRole(), nPortIndex);
    501     return OMX_ErrorNone;
    502 }
    503 
    504 OMX_ERRORTYPE PortBase::FreeBuffer(OMX_U32 nPortIndex,
    505                                    OMX_BUFFERHEADERTYPE *pBuffer)
    506 {
    507     struct list *entry;
    508     OMX_ERRORTYPE ret;
    509 
    510     LOGV("%s(): %s:%s:PortIndex %u:pBuffer %p: enter\n", __FUNCTION__,
    511          cbase->GetName(), cbase->GetWorkingRole(), nPortIndex, pBuffer);
    512 
    513     pthread_mutex_lock(&hdrs_lock);
    514     entry = list_find(buffer_hdrs, pBuffer);
    515 
    516     if (!entry) {
    517         pthread_mutex_unlock(&hdrs_lock);
    518         LOGE("%s(): %s:%s:PortIndex %u:pBuffer %p: exit failure, "
    519              "cannot find list entry for pBuffer\n", __FUNCTION__,
    520              cbase->GetName(), cbase->GetWorkingRole(), nPortIndex, pBuffer);
    521         return OMX_ErrorBadParameter;
    522     }
    523 
    524     if (entry->data != pBuffer) {
    525         pthread_mutex_unlock(&hdrs_lock);
    526         LOGE("%s(): %s:%s:PortIndex %u:pBuffer %p: exit failure,"
    527              "mismatch list entry\n" , __FUNCTION__,
    528              cbase->GetName(), cbase->GetWorkingRole(), nPortIndex, pBuffer);
    529         return OMX_ErrorBadParameter;
    530     }
    531 
    532     ret = ComponentBase::CheckTypeHeader(pBuffer, sizeof(*pBuffer));
    533     if (ret != OMX_ErrorNone) {
    534         pthread_mutex_unlock(&hdrs_lock);
    535         LOGE("%s(): %s:%s:PortIndex %u:pBuffer %p: exit failure,"
    536              "invalid type header\n", __FUNCTION__,
    537              cbase->GetName(), cbase->GetWorkingRole(), nPortIndex, pBuffer);
    538         return ret;
    539     }
    540 
    541     buffer_hdrs = __list_delete(buffer_hdrs, entry);
    542     nr_buffer_hdrs--;
    543 
    544     LOGV("%s(): %s:%s:PortIndex %u:pBuffer %p: free a buffer (%u/%u)\n",
    545          __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(), nPortIndex,
    546          pBuffer, nr_buffer_hdrs, portdefinition.nBufferCountActual);
    547     if (custom_mem_free) {
    548         (*custom_mem_free)(pBuffer->pBuffer, custom_mem_userdata);
    549         pBuffer->pBuffer = NULL;
    550     }
    551     free(pBuffer);
    552 
    553     portdefinition.bPopulated = OMX_FALSE;
    554     if (!nr_buffer_hdrs) {
    555         buffer_hdrs_completion = true;
    556         pthread_cond_signal(&hdrs_wait);
    557         LOGV("%s(): %s:%s:PortIndex %u: free all allocated buffers (%u)\n",
    558              __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    559              nPortIndex, portdefinition.nBufferCountActual);
    560     }
    561 
    562     pthread_mutex_unlock(&hdrs_lock);
    563 
    564     LOGV("%s(): %s:%s:PortIndex %u: exit done\n", __FUNCTION__,
    565          cbase->GetName(), cbase->GetWorkingRole(), nPortIndex);
    566     return OMX_ErrorNone;
    567 }
    568 
    569 void PortBase::WaitPortBufferCompletion(void)
    570 {
    571     pthread_mutex_lock(&hdrs_lock);
    572     if (!buffer_hdrs_completion) {
    573         LOGV("%s(): %s:%s:PortIndex %u: wait for buffer header completion\n",
    574              __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    575              portdefinition.nPortIndex);
    576         pthread_cond_wait(&hdrs_wait, &hdrs_lock);
    577         LOGV("%s(): %s:%s:PortIndex %u: wokeup (buffer header completion)\n",
    578              __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    579              portdefinition.nPortIndex);
    580     }
    581     buffer_hdrs_completion = !buffer_hdrs_completion;
    582     pthread_mutex_unlock(&hdrs_lock);
    583 }
    584 
    585 OMX_ERRORTYPE PortBase::WaitPortBufferCompletionTimeout(int64_t milliseconds)
    586 {
    587     int rc = 0;
    588     OMX_ERRORTYPE ret = OMX_ErrorNone;
    589     pthread_mutex_lock(&hdrs_lock);
    590     if (!buffer_hdrs_completion) {
    591         LOGV("%s(): %s:%s:PortIndex %u: wait for buffer header completion\n",
    592              __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    593              portdefinition.nPortIndex);
    594         struct timespec tv;
    595         clock_gettime(CLOCK_REALTIME, &tv);
    596         tv.tv_sec += milliseconds/1000;
    597         tv.tv_nsec+= (milliseconds%1000) * 1000000;
    598         if (tv.tv_nsec >= 1000000000) {
    599             tv.tv_sec += 1;
    600             tv.tv_nsec -= 1000000000;
    601         }
    602 
    603         rc = pthread_cond_timedwait(&hdrs_wait, &hdrs_lock, &tv);
    604     }
    605     if (rc == ETIMEDOUT) {
    606         LOGE("%s(): %s:%s:PortIndex %u: wokeup (buffer header timeout)\n",
    607              __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    608              portdefinition.nPortIndex);
    609         ret = OMX_ErrorTimeout;
    610     }
    611     buffer_hdrs_completion = !buffer_hdrs_completion;
    612     pthread_mutex_unlock(&hdrs_lock);
    613     return ret;
    614 }
    615 
    616 /* Empty/FillThisBuffer */
    617 OMX_ERRORTYPE PortBase::PushThisBuffer(OMX_BUFFERHEADERTYPE *pBuffer)
    618 {
    619     int ret;
    620     LOGV_IF(pBuffer != NULL, "%s(): %s:%s:PortIndex %u:pBuffer %p:\n",
    621             __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    622            portdefinition.nPortIndex, pBuffer);
    623     pthread_mutex_lock(&bufferq_lock);
    624     ret = queue_push_tail(&bufferq, pBuffer);
    625     pthread_mutex_unlock(&bufferq_lock);
    626 
    627     if (ret)
    628         return OMX_ErrorInsufficientResources;
    629 
    630     return OMX_ErrorNone;
    631 }
    632 
    633 OMX_BUFFERHEADERTYPE *PortBase::PopBuffer(void)
    634 {
    635     OMX_BUFFERHEADERTYPE *buffer;
    636 
    637     pthread_mutex_lock(&bufferq_lock);
    638     buffer = (OMX_BUFFERHEADERTYPE *)queue_pop_head(&bufferq);
    639     pthread_mutex_unlock(&bufferq_lock);
    640     LOGV_IF((buffer != NULL || RetainedBufferQueueLength() > 0), "%s(): %s:%s:PortIndex %u:pBuffer %p:\n",
    641             __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    642             portdefinition.nPortIndex, buffer);
    643     return buffer;
    644 }
    645 
    646 OMX_U32 PortBase::BufferQueueLength(void)
    647 {
    648     OMX_U32 length;
    649 
    650     pthread_mutex_lock(&bufferq_lock);
    651     length = queue_length(&bufferq);
    652     pthread_mutex_unlock(&bufferq_lock);
    653 
    654     return length;
    655 }
    656 
    657 OMX_U32 PortBase::RetainedBufferQueueLength(void)
    658 {
    659     OMX_U32 length;
    660 
    661     pthread_mutex_lock(&retainedbufferq_lock);
    662     length = queue_length(&retainedbufferq);
    663     pthread_mutex_unlock(&retainedbufferq_lock);
    664 
    665     return length;
    666 }
    667 
    668 OMX_ERRORTYPE PortBase::ReturnThisBuffer(OMX_BUFFERHEADERTYPE *pBuffer)
    669 {
    670     OMX_DIRTYPE direction = portdefinition.eDir;
    671     OMX_U32 port_index;
    672     OMX_ERRORTYPE (*bufferdone_callback)(OMX_HANDLETYPE,
    673                                          OMX_PTR,
    674                                          OMX_BUFFERHEADERTYPE *);
    675     OMX_ERRORTYPE ret;
    676 
    677     LOGV("%s(): %s:%s:PortIndex %u:pBuffer %p: enter\n", __FUNCTION__,
    678          cbase->GetName(), cbase->GetWorkingRole(), portdefinition.nPortIndex,
    679          pBuffer);
    680 
    681     if (!pBuffer) {
    682         LOGE("%s(): %s:%s:PortIndex %u:pBuffer %p: exit failure, "
    683              "invalid buffer pointer\n",
    684              __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    685              portdefinition.nPortIndex, pBuffer);
    686         return OMX_ErrorBadParameter;
    687     }
    688 
    689     if (direction == OMX_DirInput) {
    690         port_index = pBuffer->nInputPortIndex;
    691         bufferdone_callback = callbacks->EmptyBufferDone;
    692     }
    693     else if (direction == OMX_DirOutput) {
    694         port_index = pBuffer->nOutputPortIndex;
    695         bufferdone_callback = callbacks->FillBufferDone;
    696     }
    697     else {
    698         LOGE("%s(): %s:%s:PortIndex %u:pBuffer %p: exit failure, "
    699              "invalid direction (%d)\n",
    700              __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    701              portdefinition.nPortIndex, pBuffer,
    702              direction);
    703         return OMX_ErrorBadParameter;
    704     }
    705 
    706     if (port_index != portdefinition.nPortIndex) {
    707         LOGE("%s(): %s:%s:PortIndex %u:pBuffer %p: exit failure, "
    708              "invalid port index (%u)\n", __FUNCTION__,
    709              cbase->GetName(), cbase->GetWorkingRole(),
    710              portdefinition.nPortIndex, pBuffer, port_index);
    711         return OMX_ErrorBadParameter;
    712     }
    713 
    714     if (pBuffer->nFlags & OMX_BUFFERFLAG_EOS) {
    715         LOGV("%s(): %s:%s:PortIndex %u:pBuffer %p: "
    716              "Report OMX_EventBufferFlag (OMX_BUFFERFLAG_EOS)\n",
    717              __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    718              portdefinition.nPortIndex, pBuffer);
    719 
    720         callbacks->EventHandler(owner, appdata,
    721                                 OMX_EventBufferFlag,
    722                                 port_index, pBuffer->nFlags, NULL);
    723     }
    724 
    725     if (pBuffer->hMarkTargetComponent == owner) {
    726         LOGV("%s(): %s:%s:PortIndex %u:pBuffer %p: "
    727              "Report OMX_EventMark\n",
    728              __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    729              portdefinition.nPortIndex, pBuffer);
    730 
    731         callbacks->EventHandler(owner, appdata, OMX_EventMark,
    732                                 0, 0, pBuffer->pMarkData);
    733         pBuffer->hMarkTargetComponent = NULL;
    734         pBuffer->pMarkData = NULL;
    735     }
    736 
    737     ret = bufferdone_callback(owner, appdata, pBuffer);
    738 
    739     LOGV("%s(): %s:%s:PortIndex %u: exit done, "
    740          "callback returned (0x%08x)\n", __FUNCTION__,
    741          cbase->GetName(), cbase->GetWorkingRole(), portdefinition.nPortIndex,
    742          ret);
    743 
    744     return OMX_ErrorNone;
    745 }
    746 
    747 OMX_ERRORTYPE PortBase::RetainAndReturnBuffer( OMX_BUFFERHEADERTYPE *pRetain, OMX_BUFFERHEADERTYPE *pReturn)
    748 {
    749     OMX_ERRORTYPE ret;
    750     OMX_U32 length;
    751     if (pReturn == pRetain) {
    752         return ReturnThisBuffer(pReturn);
    753     }
    754     ret = RetainThisBuffer(pRetain, false);
    755     if (ret != OMX_ErrorNone) {
    756         return ret;
    757     }
    758 
    759     pthread_mutex_lock(&bufferq_lock);
    760     length = queue_length(&bufferq);
    761     OMX_BUFFERHEADERTYPE *p;
    762     /* remove returned buffer from the queue */
    763     OMX_U32 i = 0;
    764     for (i = 0; i < length; i++) {
    765         p = (OMX_BUFFERHEADERTYPE *)queue_pop_head(&bufferq);
    766         if (p == pReturn) {
    767             break;
    768         }
    769         queue_push_tail(&bufferq, p);
    770     }
    771     pthread_mutex_unlock(&bufferq_lock);
    772 
    773     if (i == length) {
    774         return OMX_ErrorNone;
    775     }
    776 
    777     return ReturnThisBuffer(pReturn);
    778 }
    779 
    780 /* retain buffer */
    781 OMX_ERRORTYPE PortBase::RetainThisBuffer(OMX_BUFFERHEADERTYPE *pBuffer,
    782         bool accumulate)
    783 {
    784     int ret;
    785 
    786     LOGV("%s(): %s:%s:PortIndex %u:pBuffer %p: enter, %s\n", __FUNCTION__,
    787          cbase->GetName(), cbase->GetWorkingRole(), portdefinition.nPortIndex,
    788          pBuffer, (accumulate == true) ? "accumulate" : "getagain");
    789 
    790     /* push at tail of retainedbufferq */
    791     if (accumulate == true) {
    792 
    793         if (cbase->GetWorkingRole() == NULL || (strncmp((char*)cbase->GetWorkingRole(), "video_encoder", 13) != 0)) {
    794             /* do not accumulate a buffer set EOS flag if not video encoder*/
    795             if (pBuffer->nFlags & OMX_BUFFERFLAG_EOS) {
    796                 LOGE("%s(): %s:%s:PortIndex %u:pBuffer %p: exit failure, "
    797                      "cannot accumulate EOS buffer\n", __FUNCTION__,
    798                      cbase->GetName(), cbase->GetWorkingRole(),
    799                      portdefinition.nPortIndex, pBuffer);
    800                 return OMX_ErrorBadParameter;
    801             }
    802         }
    803 
    804         pthread_mutex_lock(&retainedbufferq_lock);
    805         if ((OMX_U32)queue_length(&retainedbufferq) <
    806                 portdefinition.nBufferCountActual)
    807             ret = queue_push_tail(&retainedbufferq, pBuffer);
    808         else {
    809             ret = OMX_ErrorInsufficientResources;
    810             LOGE("%s(): %s:%s:PortIndex %u:pBuffer %p: exit failure, "
    811                  "retained bufferq length (%d) exceeds port's actual count "
    812                  "(%u)\n", __FUNCTION__,
    813                  cbase->GetName(), cbase->GetWorkingRole(),
    814                  portdefinition.nPortIndex, pBuffer,
    815                  queue_length(&retainedbufferq),
    816                  portdefinition.nBufferCountActual);
    817         }
    818         pthread_mutex_unlock(&retainedbufferq_lock);
    819     }
    820     /*
    821      * just push at head of bufferq to get this buffer again in
    822      * ComponentBase::ProcessorProcess()
    823      */
    824     else {
    825         pthread_mutex_lock(&bufferq_lock);
    826         ret = queue_push_head(&bufferq, pBuffer);
    827         pthread_mutex_unlock(&bufferq_lock);
    828     }
    829 
    830     if (ret)
    831         return OMX_ErrorInsufficientResources;
    832 
    833     LOGV("%s(): %s:%s:PortIndex %u:pBuffer %p: exit done\n", __FUNCTION__,
    834          cbase->GetName(), cbase->GetWorkingRole(),
    835          portdefinition.nPortIndex, pBuffer);
    836     return OMX_ErrorNone;
    837 }
    838 
    839 void PortBase::ReturnAllRetainedBuffers(void)
    840 {
    841     OMX_BUFFERHEADERTYPE *buffer;
    842     OMX_ERRORTYPE ret;
    843     int i = 0;
    844 
    845     pthread_mutex_lock(&retainedbufferq_lock);
    846 
    847     do {
    848         buffer = (OMX_BUFFERHEADERTYPE *)queue_pop_head(&retainedbufferq);
    849 
    850         if (buffer) {
    851             LOGV("%s(): %s:%s:PortIndex %u: returns a retained buffer "
    852                  "(%p:%d/%d)\n", __FUNCTION__, cbase->GetName(),
    853                  cbase->GetWorkingRole(), portdefinition.nPortIndex,
    854                  buffer, i++, queue_length(&retainedbufferq));
    855 
    856             ret = ReturnThisBuffer(buffer);
    857             if (ret != OMX_ErrorNone)
    858                 LOGE("%s(): %s:%s:PortIndex %u: failed (ret : 0x%x08x)\n",
    859                      __FUNCTION__,
    860                      cbase->GetName(), cbase->GetWorkingRole(),
    861                      portdefinition.nPortIndex, ret);
    862         }
    863     } while (buffer);
    864 
    865     pthread_mutex_unlock(&retainedbufferq_lock);
    866     LOGV_IF(i != 0,
    867             "%s(): %s:%s:PortIndex %u: returned all retained buffers (%d)\n",
    868             __FUNCTION__, cbase->GetName(), cbase->GetWorkingRole(),
    869             portdefinition.nPortIndex, i);
    870 }
    871 
    872 void PortBase::ReturnOneRetainedBuffer(void)
    873 {
    874     OMX_BUFFERHEADERTYPE *buffer;
    875     OMX_ERRORTYPE ret;
    876     int i =0;
    877 
    878     pthread_mutex_lock(&retainedbufferq_lock);
    879 
    880     buffer = (OMX_BUFFERHEADERTYPE *)queue_pop_head(&retainedbufferq);
    881 
    882     if (buffer) {
    883         LOGV("%s(): %s:%s:PortIndex %u: returns a retained buffer "
    884              "(%p:%d/%d)\n", __FUNCTION__, cbase->GetName(),
    885              cbase->GetWorkingRole(), portdefinition.nPortIndex,
    886              buffer, i++, queue_length(&retainedbufferq));
    887 
    888         ret = ReturnThisBuffer(buffer);
    889         if (ret != OMX_ErrorNone)
    890             LOGE("%s(): %s:%s:PortIndex %u: failed (ret : 0x%x08x)\n",
    891                  __FUNCTION__,
    892                  cbase->GetName(), cbase->GetWorkingRole(),
    893                  portdefinition.nPortIndex, ret);
    894     }
    895 
    896     pthread_mutex_unlock(&retainedbufferq_lock);
    897 
    898 }
    899 
    900 /* SendCommand:Flush/PortEnable/Disable */
    901 /* must be held ComponentBase::ports_block */
    902 OMX_ERRORTYPE PortBase::FlushPort(void)
    903 {
    904     OMX_BUFFERHEADERTYPE *buffer;
    905 
    906     LOGV("%s(): %s:%s:PortIndex %u: enter\n", __FUNCTION__,
    907          cbase->GetName(), cbase->GetWorkingRole(),
    908          portdefinition.nPortIndex);
    909 
    910     ReturnAllRetainedBuffers();
    911 
    912     while ((buffer = PopBuffer()))
    913         ReturnThisBuffer(buffer);
    914 
    915     LOGV("%s(): %s:%s:PortIndex %u: exit\n", __FUNCTION__,
    916          cbase->GetName(), cbase->GetWorkingRole(),
    917          portdefinition.nPortIndex);
    918 
    919     return OMX_ErrorNone;
    920 }
    921 
    922 OMX_STATETYPE PortBase::GetOwnerState(void)
    923 {
    924     OMX_STATETYPE state = OMX_StateInvalid;
    925 
    926     if (owner) {
    927         ComponentBase *cbase;
    928         cbase = static_cast<ComponentBase *>(owner->pComponentPrivate);
    929         if (!cbase)
    930             return state;
    931 
    932         cbase->CBaseGetState((void *)owner, &state);
    933     }
    934 
    935     return state;
    936 }
    937 
    938 bool PortBase::IsEnabled(void)
    939 {
    940     bool enabled;
    941     bool unlock = true;
    942 
    943     if (pthread_mutex_trylock(&state_lock))
    944         unlock = false;
    945 
    946     enabled = (state == OMX_PortEnabled) ? true : false;
    947 
    948     if (unlock)
    949         pthread_mutex_unlock(&state_lock);
    950 
    951     return enabled;
    952 }
    953 
    954 OMX_DIRTYPE PortBase::GetPortDirection(void)
    955 {
    956     return portdefinition.eDir;
    957 }
    958 
    959 OMX_U32 PortBase::GetPortBufferCount(void)
    960 {
    961     return nr_buffer_hdrs;
    962 }
    963 
    964 OMX_ERRORTYPE PortBase::PushMark(OMX_MARKTYPE *mark)
    965 {
    966     int ret;
    967 
    968     pthread_mutex_lock(&markq_lock);
    969     ret = queue_push_tail(&markq, mark);
    970     pthread_mutex_unlock(&markq_lock);
    971 
    972     if (ret)
    973         return OMX_ErrorInsufficientResources;
    974 
    975     return OMX_ErrorNone;
    976 }
    977 
    978 OMX_MARKTYPE *PortBase::PopMark(void)
    979 {
    980     OMX_MARKTYPE *mark;
    981 
    982     pthread_mutex_lock(&markq_lock);
    983     mark = (OMX_MARKTYPE *)queue_pop_head(&markq);
    984     pthread_mutex_unlock(&markq_lock);
    985 
    986     return mark;
    987 }
    988 
    989 static const char *state_name[PortBase::OMX_PortEnabled+2] = {
    990     "OMX_PortDisabled",
    991     "OMX_PortEnabled",
    992     "UnKnown Port State",
    993 };
    994 
    995 const char *GetPortStateName(OMX_U8 state)
    996 {
    997     if (state > PortBase::OMX_PortEnabled)
    998         state = PortBase::OMX_PortEnabled+1;
    999 
   1000     return state_name[state];
   1001 }
   1002 
   1003 OMX_ERRORTYPE PortBase::TransState(OMX_U8 transition)
   1004 {
   1005     OMX_U8 current;
   1006     OMX_ERRORTYPE ret = OMX_ErrorNone;
   1007 
   1008     LOGV("%s(): %s:%s:PortIndex %u: enter, transition from %s to %s\n",
   1009          __FUNCTION__,
   1010          cbase->GetName(), cbase->GetWorkingRole(), portdefinition.nPortIndex,
   1011          GetPortStateName(state), GetPortStateName(transition));
   1012 
   1013     pthread_mutex_lock(&state_lock);
   1014 
   1015     current = state;
   1016 
   1017     if (current == transition) {
   1018         ret = OMX_ErrorSameState;
   1019         LOGE("%s(): %s:%s:PortIndex %u: exit failure, same state (%s)\n",
   1020              __FUNCTION__,
   1021              cbase->GetName(), cbase->GetWorkingRole(),
   1022              portdefinition.nPortIndex, GetPortStateName(current));
   1023         goto unlock;
   1024     }
   1025 
   1026     if (transition == OMX_PortEnabled) {
   1027         if (cbase->GetWorkingRole() != NULL &&
   1028                 !strncmp (cbase->GetWorkingRole(),"video_decoder", 13 )) {
   1029             ret = WaitPortBufferCompletionTimeout(800); //0.8s timeout
   1030             if (!nr_buffer_hdrs) {
   1031                 // event is trigger by freeing buffer instead of allocating buffer
   1032                 ret = OMX_ErrorBadParameter;
   1033             }
   1034             if (ret != OMX_ErrorNone) {
   1035                 goto unlock;
   1036             }
   1037         } else {
   1038             WaitPortBufferCompletion();
   1039         }
   1040         portdefinition.bEnabled = OMX_TRUE;
   1041     }
   1042     else if(transition == OMX_PortDisabled) {
   1043         /*need to flush only if port is not empty*/
   1044         if (nr_buffer_hdrs)
   1045         {
   1046             FlushPort();
   1047             WaitPortBufferCompletion();
   1048         }
   1049         portdefinition.bEnabled = OMX_FALSE;
   1050     }
   1051     else {
   1052         ret = OMX_ErrorBadParameter;
   1053         LOGE("%s(): %s:%s:PortIndex %u: exit failure, invalid transition "
   1054              "(%s)\n", __FUNCTION__,
   1055              cbase->GetName(), cbase->GetWorkingRole(),
   1056              portdefinition.nPortIndex, GetPortStateName(transition));
   1057         goto unlock;
   1058     }
   1059 
   1060     state = transition;
   1061 
   1062     LOGV("%s(): %s:%s:PortIndex %u: transition from %s to %s complete\n",
   1063          __FUNCTION__,
   1064          cbase->GetName(), cbase->GetWorkingRole(), portdefinition.nPortIndex,
   1065          GetPortStateName(current), GetPortStateName(state));
   1066 
   1067 unlock:
   1068     pthread_mutex_unlock(&state_lock);
   1069     return ret;
   1070 }
   1071 
   1072 OMX_ERRORTYPE PortBase::ReportPortSettingsChanged(void)
   1073 {
   1074     OMX_ERRORTYPE ret;
   1075 
   1076     ret = callbacks->EventHandler(owner, appdata,
   1077                                   OMX_EventPortSettingsChanged,
   1078                                   portdefinition.nPortIndex,OMX_IndexParamPortDefinition, NULL);
   1079 
   1080     FlushPort();
   1081 
   1082     return ret;
   1083 }
   1084 
   1085 OMX_ERRORTYPE PortBase::ReportOutputCrop(void)
   1086 {
   1087     OMX_ERRORTYPE ret;
   1088 
   1089     ret = callbacks->EventHandler(owner, appdata,
   1090                                   OMX_EventPortSettingsChanged,
   1091                                   portdefinition.nPortIndex,OMX_IndexConfigCommonOutputCrop, NULL);
   1092 
   1093     return ret;
   1094 }
   1095 
   1096 
   1097 /* end of component methods & helpers */
   1098 
   1099 /* end of PortBase */
   1100