mm-video: vidc: Add support for dynamic buffer mode

The OMX Component will support dynamic buffer mode
for capture port. With static buffer mode, all the
output buffers are pre-registered with firmware
before stream-on. In dynamic buffer mode, there will
be no pre-registeration of output buffers with
firmware. This mode is primarily for streaming
use case, to save on video start-up latency.

Change-Id: I108f3f832167b7000ef4d25a38c8f5006507c864
diff --git a/mm-video-v4l2/vidc/vdec/src/omx_vdec_msm8974.cpp b/mm-video-v4l2/vidc/vdec/src/omx_vdec_msm8974.cpp
index add623c..1d07edb 100644
--- a/mm-video-v4l2/vidc/vdec/src/omx_vdec_msm8974.cpp
+++ b/mm-video-v4l2/vidc/vdec/src/omx_vdec_msm8974.cpp
@@ -49,6 +49,9 @@
 #include <fcntl.h>
 #include <limits.h>
 #include <stdlib.h>
+#ifdef META_DATA_MODE_SUPPORTED
+#include <media/hardware/HardwareAPI.h>
+#endif
 #include <media/msm_media_info.h>
 
 #ifndef _ANDROID_
@@ -230,7 +233,39 @@
                     DEBUG_PRINT_HIGH("\n async_message_thread Exited  \n");
                     break;
                 }
-            } else {
+            }
+#ifdef META_DATA_MODE_SUPPORTED
+            else if (dqevent.type == V4L2_EVENT_MSM_VIDC_RELEASE_BUFFER_REFERENCE) {
+                unsigned int *ptr = (unsigned int *)dqevent.u.data;
+                DEBUG_PRINT_LOW("REFERENCE RELEASE EVENT RECVD fd = %d offset = %d\n", ptr[0], ptr[1]);
+                omx->buf_ref_remove(ptr[0], ptr[1]);
+            } else if (dqevent.type == V4L2_EVENT_MSM_VIDC_RELEASE_UNQUEUED_BUFFER) {
+                unsigned int *ptr = (unsigned int *)dqevent.u.data;
+                struct vdec_msginfo vdec_msg;
+
+                DEBUG_PRINT_LOW("Release unqueued buffer event recvd fd = %d offset = %d\n", ptr[0], ptr[1]);
+
+                v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+                v4l2_buf.memory = V4L2_MEMORY_USERPTR;
+                v4l2_buf.length = omx->drv_ctx.num_planes;
+                v4l2_buf.m.planes = plane;
+                v4l2_buf.index = ptr[5];
+                v4l2_buf.flags = 0;
+
+                vdec_msg.msgcode = VDEC_MSG_RESP_OUTPUT_BUFFER_DONE;
+                vdec_msg.status_code = VDEC_S_SUCCESS;
+                vdec_msg.msgdata.output_frame.client_data = (void*)&v4l2_buf;
+                vdec_msg.msgdata.output_frame.len = 0;
+                vdec_msg.msgdata.output_frame.bufferaddr = (void*)ptr[2];
+                vdec_msg.msgdata.output_frame.time_stamp = ((uint64_t)ptr[3] * (uint64_t)1000000) +
+                    (uint64_t)ptr[4];
+                if (omx->async_message_process(input,&vdec_msg) < 0) {
+                    DEBUG_PRINT_HIGH("\n async_message_thread Exited  \n");
+                    break;
+                }
+            }
+#endif
+            else {
                 DEBUG_PRINT_HIGH("\n VIDC Some Event recieved \n");
                 continue;
             }
@@ -609,12 +644,20 @@
 #endif
     m_fill_output_msg = OMX_COMPONENT_GENERATE_FTB;
     client_buffers.set_vdec_client(this);
+#ifdef META_DATA_MODE_SUPPORTED
+    dynamic_buf_mode = false;
+    out_dynamic_list = NULL;
+#endif
 }
 
 static const int event_type[] = {
     V4L2_EVENT_MSM_VIDC_FLUSH_DONE,
     V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_SUFFICIENT,
     V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_INSUFFICIENT,
+#ifdef META_DATA_MODE_SUPPORTED
+    V4L2_EVENT_MSM_VIDC_RELEASE_BUFFER_REFERENCE,
+    V4L2_EVENT_MSM_VIDC_RELEASE_UNQUEUED_BUFFER,
+#endif
     V4L2_EVENT_MSM_VIDC_CLOSE_DONE,
     V4L2_EVENT_MSM_VIDC_SYS_ERROR
 };
@@ -3356,6 +3399,7 @@
                                      break;
 
                                  }
+#ifdef META_DATA_MODE_SUPPORTED
         case OMX_QcomIndexParamVideoMetaBufferMode:
         {
             StoreMetaDataInBuffersParams *metabuffer =
@@ -3379,6 +3423,7 @@
                     if (!rc) {
                         DEBUG_PRINT_HIGH(" %s buffer mode\n",
                            (metabuffer->bStoreMetaData == true)? "Enabled dynamic" : "Disabled dynamic");
+                               dynamic_buf_mode = metabuffer->bStoreMetaData;
                     } else {
                         DEBUG_PRINT_ERROR("Failed to %s buffer mode\n",
                            (metabuffer->bStoreMetaData == true)? "enable dynamic" : "disable dynamic");
@@ -3392,6 +3437,7 @@
                 }
                 break;
         }
+#endif
         default: {
                  DEBUG_PRINT_ERROR("Setparameter: unknown param %d\n", paramIndex);
                  eRet = OMX_ErrorUnsupportedIndex;
@@ -3744,9 +3790,11 @@
         *indexType = (OMX_INDEXTYPE)OMX_GoogleAndroidIndexGetAndroidNativeBufferUsage;
     }
 #endif
+#ifdef META_DATA_MODE_SUPPORTED
     else if (!strncmp(paramName, "OMX.google.android.index.storeMetaDataInBuffers", sizeof("OMX.google.android.index.storeMetaDataInBuffers") - 1)) {
         *indexType = (OMX_INDEXTYPE)OMX_QcomIndexParamVideoMetaBufferMode;
     }
+#endif
     else {
         DEBUG_PRINT_ERROR("Extension: %s not implemented\n", paramName);
         return OMX_ErrorNotImplemented;
@@ -3897,6 +3945,29 @@
         eRet = OMX_ErrorInsufficientResources;
     }
 
+#ifdef META_DATA_MODE_SUPPORTED
+    if (dynamic_buf_mode) {
+        *bufferHdr = (m_out_mem_ptr + i );
+        (*bufferHdr)->pBuffer = NULL;
+        if (i == (drv_ctx.op_buf.actualcount -1) && !streaming[CAPTURE_PORT]) {
+            enum v4l2_buf_type buf_type;
+            int rr = 0;
+            buf_type=V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+            if (rr = ioctl(drv_ctx.video_driver_fd, VIDIOC_STREAMON,&buf_type)) {
+                DEBUG_PRINT_ERROR("STREAMON FAILED : %d", rr);
+                return OMX_ErrorInsufficientResources;
+            } else {
+                streaming[CAPTURE_PORT] = true;
+                DEBUG_PRINT_LOW("STREAMON Successful");
+            }
+        }
+        BITMASK_SET(&m_out_bm_count,i);
+        (*bufferHdr)->pAppPrivate = appData;
+        (*bufferHdr)->pBuffer = buffer;
+        (*bufferHdr)->nAllocLen = sizeof(struct VideoDecoderOutputMetaData);
+        return eRet;
+    }
+#endif
     if (eRet == OMX_ErrorNone) {
 #if defined(_ANDROID_HONEYCOMB_) || defined(_ANDROID_ICS_)
         if (m_enable_android_native_buffers) {
@@ -5497,6 +5568,42 @@
 OMX_ERRORTYPE  omx_vdec::fill_this_buffer(OMX_IN OMX_HANDLETYPE  hComp,
         OMX_IN OMX_BUFFERHEADERTYPE* buffer)
 {
+#ifdef META_DATA_MODE_SUPPORTED
+    if (dynamic_buf_mode) {
+        private_handle_t *handle = NULL;
+        struct VideoDecoderOutputMetaData *meta;
+        OMX_U8 *buff = NULL;
+        unsigned int nPortIndex = 0;
+
+        if (!buffer || !buffer->pBuffer) {
+            DEBUG_PRINT_ERROR("%s: invalid params: %p %p", __FUNCTION__, buffer, buffer->pBuffer);
+            return OMX_ErrorBadParameter;
+        }
+
+        //get the buffer type and fd info
+        meta = (struct VideoDecoderOutputMetaData *)buffer->pBuffer;
+        handle = (private_handle_t *)meta->pHandle;
+        DEBUG_PRINT_LOW("FTB: buftype: %d bufhndl: %p", meta->eType, meta->pHandle);
+
+        //map the buffer handle based on the size set on output port definition.
+        if (!secure_mode) {
+            buff = (OMX_U8*)mmap(0, drv_ctx.op_buf.buffer_size,
+                          PROT_READ|PROT_WRITE, MAP_SHARED, handle->fd, 0);
+        } else {
+            buff = (OMX_U8*) buffer;
+        }
+
+        //Fill outputbuffer with buffer details, this will be sent to f/w during VIDIOC_QBUF
+        nPortIndex = buffer-((OMX_BUFFERHEADERTYPE *)client_buffers.get_il_buf_hdr());
+        drv_ctx.ptr_outputbuffer[nPortIndex].pmem_fd = handle->fd;
+        drv_ctx.ptr_outputbuffer[nPortIndex].offset = 0;
+        drv_ctx.ptr_outputbuffer[nPortIndex].bufferaddr = buff;
+        drv_ctx.ptr_outputbuffer[nPortIndex].buffer_len = drv_ctx.op_buf.buffer_size;
+        drv_ctx.ptr_outputbuffer[nPortIndex].mmaped_size = drv_ctx.op_buf.buffer_size;
+        buf_ref_add(drv_ctx.ptr_outputbuffer[nPortIndex].pmem_fd,
+                    drv_ctx.ptr_outputbuffer[nPortIndex].offset);
+    }
+#endif
 
     if (m_state == OMX_StateInvalid) {
         DEBUG_PRINT_ERROR("FTB in Invalid State\n");
@@ -5611,6 +5718,9 @@
     }
     buf.m.planes = plane;
     buf.length = drv_ctx.num_planes;
+    DEBUG_PRINT_LOW("SENDING FTB TO F/W - fd[0] = %d fd[1] = %d offset[1] = %d\n",
+             plane[0].reserved[0],plane[extra_idx].reserved[0], plane[extra_idx].reserved[1]);
+
     rc = ioctl(drv_ctx.video_driver_fd, VIDIOC_QBUF, &buf);
     if (rc) {
         /*TODO: How to handle this case */
@@ -6172,6 +6282,15 @@
             buffer, buffer->pBuffer);
     pending_output_buffers --;
 
+#ifdef META_DATA_MODE_SUPPORTED
+    if (dynamic_buf_mode && !secure_mode) {
+        unsigned int nPortIndex = 0;
+        nPortIndex = buffer-((OMX_BUFFERHEADERTYPE *)client_buffers.get_il_buf_hdr());
+        munmap(drv_ctx.ptr_outputbuffer[nPortIndex].bufferaddr,
+                        drv_ctx.ptr_outputbuffer[nPortIndex].mmaped_size);
+    }
+#endif
+
     if (buffer->nFlags & OMX_BUFFERFLAG_EOS) {
         DEBUG_PRINT_HIGH("\n Output EOS has been reached");
         if (!output_flush_progress)
@@ -6446,6 +6565,11 @@
                     ((omxhdr - omx->m_out_mem_ptr) < omx->drv_ctx.op_buf.actualcount) &&
                     (((struct vdec_output_frameinfo *)omxhdr->pOutputPortPrivate
                       - omx->drv_ctx.ptr_respbuffer) < omx->drv_ctx.op_buf.actualcount)) {
+#ifdef META_DATA_MODE_SUPPORTED
+                if (omx->dynamic_buf_mode && vdec_msg->msgdata.output_frame.len) {
+                    vdec_msg->msgdata.output_frame.len = omxhdr->nAllocLen;
+                }
+#endif
                 if ( vdec_msg->msgdata.output_frame.len <=  omxhdr->nAllocLen) {
                     omxhdr->nFilledLen = vdec_msg->msgdata.output_frame.len;
                     omxhdr->nOffset = vdec_msg->msgdata.output_frame.offset;
@@ -6470,6 +6594,17 @@
                     if (v4l2_buf_ptr->flags & V4L2_QCOM_BUF_FLAG_DECODEONLY) {
                         omxhdr->nFlags |= OMX_BUFFERFLAG_DECODEONLY;
                     }
+#ifdef META_DATA_MODE_SUPPORTED
+                    if (v4l2_buf_ptr->flags & V4L2_QCOM_BUF_FLAG_READONLY) {
+                         DEBUG_PRINT_LOW("F_B_D: READONLY BUFFER - REFERENCE WITH F/W fd = %d",
+                                    omx->drv_ctx.ptr_outputbuffer[v4l2_buf_ptr->index].pmem_fd);
+                    }
+
+                    if (omx->dynamic_buf_mode && !(v4l2_buf_ptr->flags & V4L2_QCOM_BUF_FLAG_READONLY)) {
+                        omx->buf_ref_remove(omx->drv_ctx.ptr_outputbuffer[v4l2_buf_ptr->index].pmem_fd,
+                            omxhdr->nOffset);
+                    }
+#endif
                     if (omxhdr && (v4l2_buf_ptr->flags & V4L2_QCOM_BUF_DROP_FRAME) &&
                             !(v4l2_buf_ptr->flags & V4L2_QCOM_BUF_FLAG_DECODEONLY) &&
                             !(v4l2_buf_ptr->flags & V4L2_QCOM_BUF_FLAG_EOS)) {
@@ -7232,6 +7367,12 @@
         drv_ctx.op_buf_ion_info = NULL;
     }
 #endif
+#ifdef META_DATA_MODE_SUPPORTED
+    if (out_dynamic_list) {
+        free(out_dynamic_list);
+        out_dynamic_list = NULL;
+    }
+#endif
 }
 
 void omx_vdec::free_input_buffer_header()
@@ -7612,7 +7753,12 @@
         drv_ctx.op_buf_ion_info = (struct vdec_ion * ) \
                       calloc (sizeof(struct vdec_ion),drv_ctx.op_buf.actualcount);
 #endif
-
+#ifdef META_DATA_MODE_SUPPORTED
+        if (dynamic_buf_mode) {
+            out_dynamic_list = (struct dynamic_buf_list *) \
+                calloc (sizeof(struct dynamic_buf_list), drv_ctx.op_buf.actualcount);
+        }
+#endif
         if (m_out_mem_ptr && pPtr && drv_ctx.ptr_outputbuffer
                 && drv_ctx.ptr_respbuffer) {
             bufHdr          =  m_out_mem_ptr;
@@ -8752,3 +8898,66 @@
     }
     return status;
 }
+
+#ifdef META_DATA_MODE_SUPPORTED
+void omx_vdec::buf_ref_add(OMX_U32 fd, OMX_U32 offset)
+{
+    int i = 0;
+    bool buf_present = false;
+    pthread_mutex_lock(&m_lock);
+    for (i = 0; i < drv_ctx.op_buf.actualcount; i++) {
+        //check the buffer fd, offset, uv addr with list contents
+        //If present increment reference.
+        if ((out_dynamic_list[i].fd == fd) &&
+            (out_dynamic_list[i].offset == offset)) {
+               out_dynamic_list[i].ref_count++;
+               DEBUG_PRINT_LOW("buf_ref_add: [ALREADY PRESENT] fd = %d ref_count = %d\n",
+                     out_dynamic_list[i].fd, out_dynamic_list[i].ref_count);
+               buf_present = true;
+               break;
+        }
+    }
+    if (!buf_present) {
+        for (i = 0; i < drv_ctx.op_buf.actualcount; i++) {
+            //search for a entry to insert details of the new buffer
+            if (out_dynamic_list[i].dup_fd == 0) {
+                out_dynamic_list[i].fd = fd;
+                out_dynamic_list[i].offset = offset;
+                out_dynamic_list[i].dup_fd = dup(fd);
+                out_dynamic_list[i].ref_count++;
+                DEBUG_PRINT_LOW("buf_ref_add: [ADDED] fd = %d ref_count = %d\n",
+                     out_dynamic_list[i].fd, out_dynamic_list[i].ref_count);
+                break;
+            }
+        }
+    }
+   pthread_mutex_unlock(&m_lock);
+}
+
+void omx_vdec::buf_ref_remove(OMX_U32 fd, OMX_U32 offset)
+{
+    int i = 0;
+    pthread_mutex_lock(&m_lock);
+    for (i = 0; i < drv_ctx.op_buf.actualcount; i++) {
+        //check the buffer fd, offset, uv addr with list contents
+        //If present decrement reference.
+        if ((out_dynamic_list[i].fd == fd) &&
+            (out_dynamic_list[i].offset == offset)) {
+            out_dynamic_list[i].ref_count--;
+            if (out_dynamic_list[i].ref_count == 0) {
+                close(out_dynamic_list[i].dup_fd);
+                DEBUG_PRINT_LOW("buf_ref_remove: [REMOVED] fd = %d ref_count = %d\n",
+                     out_dynamic_list[i].fd, out_dynamic_list[i].ref_count);
+                out_dynamic_list[i].dup_fd = 0;
+                out_dynamic_list[i].fd = 0;
+                out_dynamic_list[i].offset = 0;
+            }
+            break;
+        }
+    }
+    if (i  >= drv_ctx.op_buf.actualcount) {
+        DEBUG_PRINT_ERROR("Error - could not remove ref, no match with any entry in list\n");
+    }
+    pthread_mutex_unlock(&m_lock);
+}
+#endif