| /* |
| * |
| * Copyright 2012 Samsung Electronics S.LSI Co. LTD |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * @file ExynosVideoDecoder.c |
| * @brief |
| * @author Jinsung Yang (jsgood.yang@samsung.com) |
| * @version 1.0.0 |
| * @history |
| * 2012.01.15: Initial Version |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <fcntl.h> |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/ioctl.h> |
| #include <sys/mman.h> |
| #include <pthread.h> |
| |
| #include <sys/poll.h> |
| |
| #include "ion.h" |
| #include "ExynosVideoApi.h" |
| #include "ExynosVideoDec.h" |
| #include "OMX_Core.h" |
| |
| /* #define LOG_NDEBUG 0 */ |
| #define LOG_TAG "ExynosVideoDecoder" |
| #include <utils/Log.h> |
| |
| #define MAX_OUTPUTBUFFER_COUNT 32 |
| |
| /* |
| * [Common] __CodingType_To_V4L2PixelFormat |
| */ |
| static unsigned int __CodingType_To_V4L2PixelFormat(ExynosVideoCodingType codingType) |
| { |
| unsigned int pixelformat = V4L2_PIX_FMT_H264; |
| |
| switch (codingType) { |
| case VIDEO_CODING_AVC: |
| pixelformat = V4L2_PIX_FMT_H264; |
| break; |
| case VIDEO_CODING_MPEG4: |
| pixelformat = V4L2_PIX_FMT_MPEG4; |
| break; |
| case VIDEO_CODING_VP8: |
| pixelformat = V4L2_PIX_FMT_VP8; |
| break; |
| case VIDEO_CODING_H263: |
| pixelformat = V4L2_PIX_FMT_H263; |
| break; |
| case VIDEO_CODING_VC1: |
| pixelformat = V4L2_PIX_FMT_VC1_ANNEX_G; |
| break; |
| case VIDEO_CODING_VC1_RCV: |
| pixelformat = V4L2_PIX_FMT_VC1_ANNEX_L; |
| break; |
| case VIDEO_CODING_MPEG2: |
| pixelformat = V4L2_PIX_FMT_MPEG2; |
| break; |
| default: |
| pixelformat = V4L2_PIX_FMT_H264; |
| break; |
| } |
| |
| return pixelformat; |
| } |
| |
| /* |
| * [Common] __ColorFormatType_To_V4L2PixelFormat |
| */ |
| static unsigned int __ColorFormatType_To_V4L2PixelFormat(ExynosVideoColorFormatType colorFormatType) |
| { |
| unsigned int pixelformat = V4L2_PIX_FMT_NV12M; |
| |
| switch (colorFormatType) { |
| case VIDEO_COLORFORMAT_NV12_TILED: |
| pixelformat = V4L2_PIX_FMT_NV12MT_16X16; |
| break; |
| case VIDEO_COLORFORMAT_NV21: |
| pixelformat = V4L2_PIX_FMT_NV21M; |
| break; |
| case VIDEO_COLORFORMAT_NV12: |
| default: |
| pixelformat = V4L2_PIX_FMT_NV12M; |
| break; |
| } |
| |
| return pixelformat; |
| } |
| |
| /* |
| * [Decoder OPS] Init |
| */ |
| static void *MFC_Decoder_Init(int nMemoryType) |
| { |
| ExynosVideoDecContext *pCtx = NULL; |
| pthread_mutex_t *pMutex = NULL; |
| int needCaps = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING); |
| |
| pCtx = (ExynosVideoDecContext *)malloc(sizeof(*pCtx)); |
| if (pCtx == NULL) { |
| ALOGE("%s: Failed to allocate decoder context buffer", __func__); |
| goto EXIT_ALLOC_FAIL; |
| } |
| |
| memset(pCtx, 0, sizeof(*pCtx)); |
| |
| pCtx->hDec = exynos_v4l2_open_devname(VIDEO_DECODER_NAME, O_RDWR, 0); |
| if (pCtx->hDec < 0) { |
| ALOGE("%s: Failed to open decoder device", __func__); |
| goto EXIT_OPEN_FAIL; |
| } |
| |
| if (!exynos_v4l2_querycap(pCtx->hDec, needCaps)) { |
| ALOGE("%s: Failed to querycap", __func__); |
| goto EXIT_QUERYCAP_FAIL; |
| } |
| |
| pCtx->bStreamonInbuf = VIDEO_FALSE; |
| pCtx->bStreamonOutbuf = VIDEO_FALSE; |
| |
| pCtx->nMemoryType = nMemoryType; |
| |
| pMutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); |
| if (pMutex == NULL) { |
| ALOGE("%s: Failed to allocate mutex about input buffer", __func__); |
| goto EXIT_QUERYCAP_FAIL; |
| } |
| if (pthread_mutex_init(pMutex, NULL) != 0) { |
| free(pMutex); |
| goto EXIT_QUERYCAP_FAIL; |
| } |
| pCtx->pInMutex = (void*)pMutex; |
| |
| pMutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); |
| if (pMutex == NULL) { |
| ALOGE("%s: Failed to allocate mutex about output buffer", __func__); |
| goto EXIT_QUERYCAP_FAIL; |
| } |
| if (pthread_mutex_init(pMutex, NULL) != 0) { |
| free(pMutex); |
| goto EXIT_QUERYCAP_FAIL; |
| } |
| pCtx->pOutMutex = (void*)pMutex; |
| |
| pCtx->hIONHandle = (void*)ion_client_create(); |
| pCtx->nPrivateDataShareFD = ion_alloc((ion_client)pCtx->hIONHandle, |
| sizeof(PrivateDataShareBuffer) * VIDEO_BUFFER_MAX_NUM, 0, ION_HEAP_SYSTEM_MASK, ION_FLAG_CACHED); |
| pCtx->nPrivateDataShareAddress = |
| ion_map(pCtx->nPrivateDataShareFD, sizeof(PrivateDataShareBuffer) * VIDEO_BUFFER_MAX_NUM, 0); |
| memset(pCtx->nPrivateDataShareAddress, -1, sizeof(PrivateDataShareBuffer) * VIDEO_BUFFER_MAX_NUM); |
| |
| return (void *)pCtx; |
| |
| EXIT_QUERYCAP_FAIL: |
| if (pCtx->pInMutex != NULL) { |
| pthread_mutex_destroy(pCtx->pInMutex); |
| free(pCtx->pInMutex); |
| } |
| |
| if (pCtx->pOutMutex != NULL) { |
| pthread_mutex_destroy(pCtx->pOutMutex); |
| free(pCtx->pOutMutex); |
| } |
| |
| close(pCtx->hDec); |
| |
| EXIT_OPEN_FAIL: |
| free(pCtx); |
| |
| EXIT_ALLOC_FAIL: |
| return NULL; |
| } |
| |
| /* |
| * [Decoder OPS] Finalize |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Finalize(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoPlane *pVideoPlane = NULL; |
| pthread_mutex_t *pMutex = NULL; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| int i, j; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| ion_unmap(pCtx->nPrivateDataShareAddress, sizeof(PrivateDataShareBuffer) * VIDEO_BUFFER_MAX_NUM); |
| ion_free(pCtx->nPrivateDataShareFD); |
| ion_client_destroy((ion_client)pCtx->hIONHandle); |
| |
| if (pCtx->pOutMutex != NULL) { |
| pMutex = (pthread_mutex_t*)pCtx->pOutMutex; |
| pthread_mutex_destroy(pMutex); |
| free(pMutex); |
| pCtx->pOutMutex = NULL; |
| } |
| |
| if (pCtx->pInMutex != NULL) { |
| pMutex = (pthread_mutex_t*)pCtx->pInMutex; |
| pthread_mutex_destroy(pMutex); |
| free(pMutex); |
| pCtx->pInMutex = NULL; |
| } |
| |
| if (pCtx->bShareInbuf == VIDEO_FALSE) { |
| for (i = 0; i < pCtx->nInbufs; i++) { |
| for (j = 0; j < VIDEO_DECODER_INBUF_PLANES; j++) { |
| pVideoPlane = &pCtx->pInbuf[i].planes[j]; |
| if (pVideoPlane->addr != NULL) { |
| munmap(pVideoPlane->addr, pVideoPlane->allocSize); |
| pVideoPlane->addr = NULL; |
| pVideoPlane->allocSize = 0; |
| pVideoPlane->dataSize = 0; |
| } |
| |
| pCtx->pInbuf[i].pGeometry = NULL; |
| pCtx->pInbuf[i].bQueued = VIDEO_FALSE; |
| pCtx->pInbuf[i].bRegistered = VIDEO_FALSE; |
| } |
| } |
| } |
| |
| if (pCtx->bShareOutbuf == VIDEO_FALSE) { |
| for (i = 0; i < pCtx->nOutbufs; i++) { |
| for (j = 0; j < VIDEO_DECODER_OUTBUF_PLANES; j++) { |
| pVideoPlane = &pCtx->pOutbuf[i].planes[j]; |
| if (pVideoPlane->addr != NULL) { |
| munmap(pVideoPlane->addr, pVideoPlane->allocSize); |
| pVideoPlane->addr = NULL; |
| pVideoPlane->allocSize = 0; |
| pVideoPlane->dataSize = 0; |
| } |
| |
| pCtx->pOutbuf[i].pGeometry = NULL; |
| pCtx->pOutbuf[i].bQueued = VIDEO_FALSE; |
| pCtx->pOutbuf[i].bRegistered = VIDEO_FALSE; |
| } |
| } |
| } |
| |
| if (pCtx->pInbuf != NULL) |
| free(pCtx->pInbuf); |
| |
| if (pCtx->pOutbuf != NULL) |
| free(pCtx->pOutbuf); |
| |
| if (pCtx->hDec > 0) |
| close(pCtx->hDec); |
| |
| free(pCtx); |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder OPS] Set Frame Tag |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Set_FrameTag( |
| void *pHandle, |
| int frameTag) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (exynos_v4l2_s_ctrl(pCtx->hDec, V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, frameTag) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder OPS] Get Frame Tag |
| */ |
| static int MFC_Decoder_Get_FrameTag(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| int frameTag = -1; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| goto EXIT; |
| } |
| |
| exynos_v4l2_g_ctrl(pCtx->hDec, V4L2_CID_MPEG_MFC51_VIDEO_FRAME_TAG, &frameTag); |
| |
| EXIT: |
| return frameTag; |
| } |
| |
| /* |
| * [Decoder OPS] Get Buffer Count |
| */ |
| static int MFC_Decoder_Get_ActualBufferCount(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| int bufferCount = -1; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| goto EXIT; |
| } |
| |
| exynos_v4l2_g_ctrl(pCtx->hDec, V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, &bufferCount); |
| |
| EXIT: |
| return bufferCount; |
| } |
| |
| /* |
| * [Decoder OPS] Set Display Delay |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Set_DisplayDelay( |
| void *pHandle, |
| int delay) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (exynos_v4l2_s_ctrl(pCtx->hDec, V4L2_CID_MPEG_MFC51_VIDEO_DECODER_H264_DISPLAY_DELAY, delay) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder OPS] Set I-Frame Decoding |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Set_IFrameDecoding( |
| void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (exynos_v4l2_s_ctrl(pCtx->hDec, V4L2_CID_MPEG_MFC51_VIDEO_I_FRAME_DECODING, 1) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder OPS] Enable Packed PB |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Enable_PackedPB(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (exynos_v4l2_s_ctrl(pCtx->hDec, V4L2_CID_MPEG_MFC51_VIDEO_PACKED_PB, 1) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder OPS] Enable Loop Filter |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Enable_LoopFilter(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (exynos_v4l2_s_ctrl(pCtx->hDec, V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER, 1) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder OPS] Enable Slice Mode |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Enable_SliceMode(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (exynos_v4l2_s_ctrl(pCtx->hDec, V4L2_CID_MPEG_VIDEO_DECODER_SLICE_INTERFACE, 1) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder OPS] Enable SEI Parsing |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Enable_SEIParsing(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (exynos_v4l2_s_ctrl(pCtx->hDec, V4L2_CID_MPEG_VIDEO_H264_SEI_FRAME_PACKING, 1) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder OPS] Get Frame Packing information |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Get_FramePackingInfo( |
| void *pHandle, |
| ExynosVideoFramePacking *pFramePacking) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| struct v4l2_ext_control ext_ctrl[FRAME_PACK_SEI_INFO_NUM]; |
| struct v4l2_ext_controls ext_ctrls; |
| |
| int seiAvailable, seiInfo, seiGridPos, i; |
| unsigned int seiArgmtId; |
| |
| |
| if ((pCtx == NULL) || (pFramePacking == NULL)) { |
| ALOGE("%s: Video context info or FramePacking pointer must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| memset(pFramePacking, 0, sizeof(*pFramePacking)); |
| memset(ext_ctrl, 0, (sizeof(struct v4l2_ext_control) * FRAME_PACK_SEI_INFO_NUM)); |
| |
| ext_ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG; |
| ext_ctrls.count = FRAME_PACK_SEI_INFO_NUM; |
| ext_ctrls.controls = ext_ctrl; |
| ext_ctrl[0].id = V4L2_CID_MPEG_VIDEO_H264_SEI_FP_AVAIL; |
| ext_ctrl[1].id = V4L2_CID_MPEG_VIDEO_H264_SEI_FP_ARRGMENT_ID; |
| ext_ctrl[2].id = V4L2_CID_MPEG_VIDEO_H264_SEI_FP_INFO; |
| ext_ctrl[3].id = V4L2_CID_MPEG_VIDEO_H264_SEI_FP_GRID_POS; |
| |
| if (exynos_v4l2_g_ext_ctrl(pCtx->hDec, &ext_ctrls) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| seiAvailable = ext_ctrl[0].value; |
| seiArgmtId = ext_ctrl[1].value; |
| seiInfo = ext_ctrl[2].value; |
| seiGridPos = ext_ctrl[3].value; |
| |
| pFramePacking->available = seiAvailable; |
| pFramePacking->arrangement_id = seiArgmtId; |
| |
| pFramePacking->arrangement_cancel_flag = OPERATE_BIT(seiInfo, 0x1, 0); |
| pFramePacking->arrangement_type = OPERATE_BIT(seiInfo, 0x3f, 1); |
| pFramePacking->quincunx_sampling_flag = OPERATE_BIT(seiInfo, 0x1, 8); |
| pFramePacking->content_interpretation_type = OPERATE_BIT(seiInfo, 0x3f, 9); |
| pFramePacking->spatial_flipping_flag = OPERATE_BIT(seiInfo, 0x1, 15); |
| pFramePacking->frame0_flipped_flag = OPERATE_BIT(seiInfo, 0x1, 16); |
| pFramePacking->field_views_flag = OPERATE_BIT(seiInfo, 0x1, 17); |
| pFramePacking->current_frame_is_frame0_flag = OPERATE_BIT(seiInfo, 0x1, 18); |
| |
| pFramePacking->frame0_grid_pos_x = OPERATE_BIT(seiGridPos, 0xf, 0); |
| pFramePacking->frame0_grid_pos_y = OPERATE_BIT(seiGridPos, 0xf, 4); |
| pFramePacking->frame1_grid_pos_x = OPERATE_BIT(seiGridPos, 0xf, 8); |
| pFramePacking->frame1_grid_pos_y = OPERATE_BIT(seiGridPos, 0xf, 12); |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Enable Cacheable (Input) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Enable_Cacheable_Inbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (exynos_v4l2_s_ctrl(pCtx->hDec, V4L2_CID_CACHEABLE, 2) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Enable Cacheable (Output) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Enable_Cacheable_Outbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (exynos_v4l2_s_ctrl(pCtx->hDec, V4L2_CID_CACHEABLE, 1) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Set Shareable Buffer (Input) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Set_Shareable_Inbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| pCtx->bShareInbuf = VIDEO_TRUE; |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Set Shareable Buffer (Output) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Set_Shareable_Outbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| pCtx->bShareOutbuf = VIDEO_TRUE; |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Get Buffer (Input) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Get_Buffer_Inbuf( |
| void *pHandle, |
| int nIndex, |
| ExynosVideoBuffer **pBuffer) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| *pBuffer = NULL; |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (pCtx->nInbufs <= nIndex) { |
| *pBuffer = NULL; |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| *pBuffer = (ExynosVideoBuffer *)&pCtx->pInbuf[nIndex]; |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Get Buffer (Output) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Get_Buffer_Outbuf( |
| void *pHandle, |
| int nIndex, |
| ExynosVideoBuffer **pBuffer) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| *pBuffer = NULL; |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (pCtx->nOutbufs <= nIndex) { |
| *pBuffer = NULL; |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| *pBuffer = (ExynosVideoBuffer *)&pCtx->pOutbuf[nIndex]; |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Set Geometry (Input) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Set_Geometry_Inbuf( |
| void *pHandle, |
| ExynosVideoGeometry *bufferConf) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| struct v4l2_format fmt; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (bufferConf == NULL) { |
| ALOGE("%s: Buffer geometry must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| memset(&fmt, 0, sizeof(fmt)); |
| |
| fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| fmt.fmt.pix_mp.pixelformat = __CodingType_To_V4L2PixelFormat(bufferConf->eCompressionFormat); |
| fmt.fmt.pix_mp.plane_fmt[0].sizeimage = bufferConf->nSizeImage; |
| |
| if (exynos_v4l2_s_fmt(pCtx->hDec, &fmt) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| memcpy(&pCtx->inbufGeometry, bufferConf, sizeof(pCtx->inbufGeometry)); |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Set Geometry (Output) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Set_Geometry_Outbuf( |
| void *pHandle, |
| ExynosVideoGeometry *bufferConf) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| struct v4l2_format fmt; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (bufferConf == NULL) { |
| ALOGE("%s: Buffer geometry must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| memset(&fmt, 0, sizeof(fmt)); |
| |
| fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| fmt.fmt.pix_mp.pixelformat = __ColorFormatType_To_V4L2PixelFormat(bufferConf->eColorFormat); |
| |
| if (exynos_v4l2_s_fmt(pCtx->hDec, &fmt) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| memcpy(&pCtx->outbufGeometry, bufferConf, sizeof(pCtx->outbufGeometry)); |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Get Geometry (Output) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Get_Geometry_Outbuf( |
| void *pHandle, |
| ExynosVideoGeometry *bufferConf) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| struct v4l2_format fmt; |
| struct v4l2_crop crop; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (bufferConf == NULL) { |
| ALOGE("%s: Buffer geometry must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| memset(&fmt, 0, sizeof(fmt)); |
| memset(&crop, 0, sizeof(crop)); |
| |
| fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| if (exynos_v4l2_g_fmt(pCtx->hDec, &fmt) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| if (exynos_v4l2_g_crop(pCtx->hDec, &crop) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| bufferConf->nFrameWidth = fmt.fmt.pix_mp.width; |
| bufferConf->nFrameHeight = fmt.fmt.pix_mp.height; |
| |
| bufferConf->cropRect.nTop = crop.c.top; |
| bufferConf->cropRect.nLeft = crop.c.left; |
| bufferConf->cropRect.nWidth = crop.c.width; |
| bufferConf->cropRect.nHeight = crop.c.height; |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Setup (Input) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Setup_Inbuf( |
| void *pHandle, |
| unsigned int nBufferCount) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoPlane *pVideoPlane = NULL; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| struct v4l2_requestbuffers req; |
| struct v4l2_buffer buf; |
| struct v4l2_plane planes[VIDEO_DECODER_INBUF_PLANES]; |
| int i; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (nBufferCount == 0) { |
| ALOGE("%s: Buffer count must be greater than 0", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| ALOGV("%s: setting up inbufs (%d) shared=%s\n", __func__, nBufferCount, |
| pCtx->bShareInbuf ? "true" : "false"); |
| |
| memset(&req, 0, sizeof(req)); |
| |
| req.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| req.count = nBufferCount; |
| |
| if (pCtx->bShareInbuf == VIDEO_TRUE) |
| req.memory = pCtx->nMemoryType; |
| else |
| req.memory = V4L2_MEMORY_MMAP; |
| |
| if (exynos_v4l2_reqbufs(pCtx->hDec, &req) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| if (req.count != nBufferCount) { |
| ALOGE("%s: asked for %d, got %d\n", __func__, nBufferCount, req.count); |
| ret = VIDEO_ERROR_NOMEM; |
| goto EXIT; |
| } |
| |
| pCtx->nInbufs = (int)req.count; |
| |
| pCtx->pInbuf = malloc(sizeof(*pCtx->pInbuf) * pCtx->nInbufs); |
| if (pCtx->pInbuf == NULL) { |
| ALOGE("Failed to allocate input buffer context"); |
| ret = VIDEO_ERROR_NOMEM; |
| goto EXIT; |
| } |
| memset(pCtx->pInbuf, 0, sizeof(*pCtx->pInbuf) * pCtx->nInbufs); |
| |
| memset(&buf, 0, sizeof(buf)); |
| |
| if (pCtx->bShareInbuf == VIDEO_FALSE) { |
| buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| buf.memory = V4L2_MEMORY_MMAP; |
| buf.m.planes = planes; |
| buf.length = VIDEO_DECODER_INBUF_PLANES; |
| |
| for (i = 0; i < pCtx->nInbufs; i++) { |
| buf.index = i; |
| if (exynos_v4l2_querybuf(pCtx->hDec, &buf) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| pVideoPlane = &pCtx->pInbuf[i].planes[0]; |
| |
| pVideoPlane->addr = mmap(NULL, |
| buf.m.planes[0].length, PROT_READ | PROT_WRITE, |
| MAP_SHARED, pCtx->hDec, buf.m.planes[0].m.mem_offset); |
| |
| if (pVideoPlane->addr == MAP_FAILED) { |
| ret = VIDEO_ERROR_MAPFAIL; |
| goto EXIT; |
| } |
| |
| pVideoPlane->allocSize = buf.m.planes[0].length; |
| pVideoPlane->dataSize = 0; |
| |
| pCtx->pInbuf[i].pGeometry = &pCtx->inbufGeometry; |
| pCtx->pInbuf[i].bQueued = VIDEO_FALSE; |
| pCtx->pInbuf[i].bRegistered = VIDEO_TRUE; |
| } |
| } |
| |
| return ret; |
| |
| EXIT: |
| if ((pCtx != NULL) && (pCtx->pInbuf != NULL)) { |
| if (pCtx->bShareInbuf == VIDEO_FALSE) { |
| for (i = 0; i < pCtx->nInbufs; i++) { |
| pVideoPlane = &pCtx->pInbuf[i].planes[0]; |
| if (pVideoPlane->addr == MAP_FAILED) { |
| pVideoPlane->addr = NULL; |
| break; |
| } |
| |
| munmap(pVideoPlane->addr, pVideoPlane->allocSize); |
| } |
| } |
| |
| free(pCtx->pInbuf); |
| } |
| |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Setup (Output) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Setup_Outbuf( |
| void *pHandle, |
| unsigned int nBufferCount) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoPlane *pVideoPlane = NULL; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| struct v4l2_requestbuffers req; |
| struct v4l2_buffer buf; |
| struct v4l2_plane planes[VIDEO_DECODER_OUTBUF_PLANES]; |
| int i, j; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (nBufferCount == 0) { |
| nBufferCount = MAX_OUTPUTBUFFER_COUNT; |
| ALOGV("%s: Change buffer count %d", __func__, nBufferCount); |
| } |
| |
| ALOGV("%s: setting up outbufs (%d) shared=%s\n", __func__, nBufferCount, |
| pCtx->bShareOutbuf ? "true" : "false"); |
| |
| memset(&req, 0, sizeof(req)); |
| |
| req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| req.count = nBufferCount; |
| |
| if (pCtx->bShareOutbuf == VIDEO_TRUE) |
| req.memory = pCtx->nMemoryType; |
| else |
| req.memory = V4L2_MEMORY_MMAP; |
| |
| if (exynos_v4l2_reqbufs(pCtx->hDec, &req) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| if (req.count != nBufferCount) { |
| ALOGE("%s: asked for %d, got %d\n", __func__, nBufferCount, req.count); |
| ret = VIDEO_ERROR_NOMEM; |
| goto EXIT; |
| } |
| |
| pCtx->nOutbufs = req.count; |
| |
| pCtx->pOutbuf = malloc(sizeof(*pCtx->pOutbuf) * pCtx->nOutbufs); |
| if (pCtx->pOutbuf == NULL) { |
| ALOGE("Failed to allocate output buffer context"); |
| ret = VIDEO_ERROR_NOMEM; |
| goto EXIT; |
| } |
| memset(pCtx->pOutbuf, 0, sizeof(*pCtx->pOutbuf) * pCtx->nOutbufs); |
| |
| memset(&buf, 0, sizeof(buf)); |
| |
| if (pCtx->bShareOutbuf == VIDEO_FALSE) { |
| buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| buf.memory = V4L2_MEMORY_MMAP; |
| buf.m.planes = planes; |
| buf.length = VIDEO_DECODER_OUTBUF_PLANES; |
| |
| for (i = 0; i < pCtx->nOutbufs; i++) { |
| buf.index = i; |
| if (exynos_v4l2_querybuf(pCtx->hDec, &buf) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| for (j = 0; j < VIDEO_DECODER_OUTBUF_PLANES; j++) { |
| pVideoPlane = &pCtx->pOutbuf[i].planes[j]; |
| pVideoPlane->addr = mmap(NULL, |
| buf.m.planes[j].length, PROT_READ | PROT_WRITE, |
| MAP_SHARED, pCtx->hDec, buf.m.planes[j].m.mem_offset); |
| |
| if (pVideoPlane->addr == MAP_FAILED) { |
| ret = VIDEO_ERROR_MAPFAIL; |
| goto EXIT; |
| } |
| |
| pVideoPlane->allocSize = buf.m.planes[j].length; |
| pVideoPlane->dataSize = 0; |
| } |
| |
| pCtx->pOutbuf[i].pGeometry = &pCtx->outbufGeometry; |
| pCtx->pOutbuf[i].bQueued = VIDEO_FALSE; |
| pCtx->pOutbuf[i].bSlotUsed = VIDEO_FALSE; |
| pCtx->pOutbuf[i].nIndexUseCnt = 0; |
| pCtx->pOutbuf[i].bRegistered = VIDEO_TRUE; |
| } |
| } |
| |
| return ret; |
| |
| EXIT: |
| if ((pCtx != NULL) && (pCtx->pOutbuf != NULL)) { |
| if (pCtx->bShareOutbuf == VIDEO_FALSE) { |
| for (i = 0; i < pCtx->nOutbufs; i++) { |
| for (j = 0; j < VIDEO_DECODER_OUTBUF_PLANES; j++) { |
| pVideoPlane = &pCtx->pOutbuf[i].planes[j]; |
| if (pVideoPlane->addr == MAP_FAILED) { |
| pVideoPlane->addr = NULL; |
| break; |
| } |
| |
| munmap(pVideoPlane->addr, pVideoPlane->allocSize); |
| } |
| } |
| } |
| |
| free(pCtx->pOutbuf); |
| pCtx->pOutbuf = NULL; |
| } |
| |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Run (Input) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Run_Inbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (pCtx->bStreamonInbuf == VIDEO_FALSE) { |
| if (exynos_v4l2_streamon(pCtx->hDec, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) != 0) { |
| ALOGE("%s: Failed to streamon for input buffer", __func__); |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| pCtx->bStreamonInbuf = VIDEO_TRUE; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Run (Output) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Run_Outbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (pCtx->bStreamonOutbuf == VIDEO_FALSE) { |
| if (exynos_v4l2_streamon(pCtx->hDec, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) != 0) { |
| ALOGE("%s: Failed to streamon for output buffer", __func__); |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| pCtx->bStreamonOutbuf = VIDEO_TRUE; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Stop (Input) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Stop_Inbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| int i = 0; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (pCtx->bStreamonInbuf == VIDEO_TRUE) { |
| if (exynos_v4l2_streamoff(pCtx->hDec, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) != 0) { |
| ALOGE("%s: Failed to streamoff for input buffer", __func__); |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| pCtx->bStreamonInbuf = VIDEO_FALSE; |
| } |
| |
| for (i = 0; i < pCtx->nInbufs; i++) { |
| pCtx->pInbuf[i].bQueued = VIDEO_FALSE; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Stop (Output) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Stop_Outbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| int i = 0; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (pCtx->bStreamonOutbuf == VIDEO_TRUE) { |
| if (exynos_v4l2_streamoff(pCtx->hDec, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) != 0) { |
| ALOGE("%s: Failed to streamoff for output buffer", __func__); |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| pCtx->bStreamonOutbuf = VIDEO_FALSE; |
| } |
| |
| for (i = 0; i < pCtx->nOutbufs; i++) { |
| pCtx->pOutbuf[i].bQueued = VIDEO_FALSE; |
| pCtx->pOutbuf[i].bSlotUsed = VIDEO_FALSE; |
| pCtx->pOutbuf[i].nIndexUseCnt = 0; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Wait (Input) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Wait_Inbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| struct pollfd poll_events; |
| int poll_state; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| poll_events.fd = pCtx->hDec; |
| poll_events.events = POLLOUT | POLLERR; |
| poll_events.revents = 0; |
| |
| do { |
| poll_state = poll((struct pollfd*)&poll_events, 1, VIDEO_DECODER_POLL_TIMEOUT); |
| if (poll_state > 0) { |
| if (poll_events.revents & POLLOUT) { |
| break; |
| } else { |
| ALOGE("%s: Poll return error", __func__); |
| ret = VIDEO_ERROR_POLL; |
| break; |
| } |
| } else if (poll_state < 0) { |
| ALOGE("%s: Poll state error", __func__); |
| ret = VIDEO_ERROR_POLL; |
| break; |
| } |
| } while (poll_state == 0); |
| |
| EXIT: |
| return ret; |
| } |
| |
| static ExynosVideoErrorType MFC_Decoder_Register_Inbuf( |
| void *pHandle, |
| ExynosVideoPlane *planes, |
| int nPlanes) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| int nIndex, plane; |
| |
| if ((pCtx == NULL) || (planes == NULL) || (nPlanes != VIDEO_DECODER_INBUF_PLANES)) { |
| ALOGE("%s: params must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| for (nIndex = 0; nIndex < pCtx->nInbufs; nIndex++) { |
| if (pCtx->pInbuf[nIndex].bRegistered == VIDEO_FALSE) { |
| for (plane = 0; plane < nPlanes; plane++) { |
| pCtx->pInbuf[nIndex].planes[plane].addr = planes[plane].addr; |
| pCtx->pInbuf[nIndex].planes[plane].allocSize = planes[plane].allocSize; |
| pCtx->pInbuf[nIndex].planes[plane].fd = planes[plane].fd; |
| ALOGV("%s: registered buf %d (addr=%p alloc_sz=%ld fd=%d)\n", __func__, nIndex, |
| planes[plane].addr, planes[plane].allocSize, planes[plane].fd); |
| } |
| pCtx->pInbuf[nIndex].bRegistered = VIDEO_TRUE; |
| break; |
| } |
| } |
| |
| if (nIndex == pCtx->nInbufs) { |
| ALOGE("%s: can not find non-registered input buffer", __func__); |
| ret = VIDEO_ERROR_NOBUFFERS; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| static ExynosVideoErrorType MFC_Decoder_Register_Outbuf( |
| void *pHandle, |
| ExynosVideoPlane *planes, |
| int nPlanes) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| int nIndex, plane; |
| |
| if ((pCtx == NULL) || (planes == NULL) || (nPlanes != VIDEO_DECODER_OUTBUF_PLANES)) { |
| ALOGE("%s: params must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| for (nIndex = 0; nIndex < pCtx->nOutbufs; nIndex++) { |
| if (pCtx->pOutbuf[nIndex].bRegistered == VIDEO_FALSE) { |
| for (plane = 0; plane < nPlanes; plane++) { |
| pCtx->pOutbuf[nIndex].planes[plane].addr = planes[plane].addr; |
| pCtx->pOutbuf[nIndex].planes[plane].allocSize = planes[plane].allocSize; |
| pCtx->pOutbuf[nIndex].planes[plane].fd = planes[plane].fd; |
| } |
| pCtx->pOutbuf[nIndex].bRegistered = VIDEO_TRUE; |
| ALOGV("%s: registered buf %d 0:(addr=%p alloc_sz=%d fd=%d) 1:(addr=%p alloc_sz=%d fd=%d)\n", |
| __func__, nIndex, planes[0].addr, planes[0].allocSize, planes[0].fd, |
| planes[1].addr, planes[1].allocSize, planes[1].fd); |
| break; |
| } |
| } |
| |
| if (nIndex == pCtx->nOutbufs) { |
| ALOGE("%s: can not find non-registered output buffer", __func__); |
| ret = VIDEO_ERROR_NOBUFFERS; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| static ExynosVideoErrorType MFC_Decoder_Clear_RegisteredBuffer_Inbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| int nIndex; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| for (nIndex = 0; nIndex < pCtx->nInbufs; nIndex++) { |
| pCtx->pInbuf[nIndex].planes[0].addr = NULL; |
| pCtx->pInbuf[nIndex].bRegistered = VIDEO_FALSE; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| static ExynosVideoErrorType MFC_Decoder_Clear_RegisteredBuffer_Outbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| int nIndex; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| for (nIndex = 0; nIndex < pCtx->nOutbufs; nIndex++) { |
| pCtx->pOutbuf[nIndex].planes[0].addr = NULL; |
| pCtx->pOutbuf[nIndex].bRegistered = VIDEO_FALSE; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Find (Input) |
| */ |
| static int MFC_Decoder_Find_Inbuf( |
| void *pHandle, |
| unsigned char *pBuffer) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| int nIndex = -1; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| goto EXIT; |
| } |
| |
| for (nIndex = 0; nIndex < pCtx->nInbufs; nIndex++) { |
| if (pCtx->pInbuf[nIndex].bQueued == VIDEO_FALSE) { |
| if ((pBuffer == NULL) || |
| (pCtx->pInbuf[nIndex].planes[0].addr == pBuffer)) |
| break; |
| } |
| } |
| |
| if (nIndex == pCtx->nInbufs) |
| nIndex = -1; |
| |
| EXIT: |
| return nIndex; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Find (Outnput) |
| */ |
| static int MFC_Decoder_Find_Outbuf( |
| void *pHandle, |
| unsigned char *pBuffer) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| int nIndex = -1; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| goto EXIT; |
| } |
| |
| for (nIndex = 0; nIndex < pCtx->nOutbufs; nIndex++) { |
| if (pCtx->pOutbuf[nIndex].bQueued == VIDEO_FALSE) { |
| if ((pBuffer == NULL) || |
| (pCtx->pOutbuf[nIndex].planes[0].addr == pBuffer)) |
| break; |
| } |
| } |
| |
| if (nIndex == pCtx->nOutbufs) |
| nIndex = -1; |
| |
| EXIT: |
| return nIndex; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Enqueue (Input) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Enqueue_Inbuf( |
| void *pHandle, |
| unsigned char *pBuffer[], |
| unsigned int dataSize[], |
| int nPlanes, |
| void *pPrivate) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| pthread_mutex_t *pMutex = NULL; |
| |
| struct v4l2_plane planes[VIDEO_DECODER_INBUF_PLANES]; |
| struct v4l2_buffer buf; |
| int index, i; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (VIDEO_DECODER_INBUF_PLANES < nPlanes) { |
| ALOGE("%s: Number of max planes : %d, nPlanes : %d", __func__, |
| VIDEO_DECODER_INBUF_PLANES, nPlanes); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| memset(&buf, 0, sizeof(buf)); |
| |
| buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| buf.m.planes = planes; |
| buf.length = VIDEO_DECODER_INBUF_PLANES; |
| |
| pMutex = (pthread_mutex_t*)pCtx->pInMutex; |
| pthread_mutex_lock(pMutex); |
| index = MFC_Decoder_Find_Inbuf(pCtx, pBuffer[0]); |
| if (index == -1) { |
| pthread_mutex_unlock(pMutex); |
| ALOGE("%s: Failed to get index", __func__); |
| ret = VIDEO_ERROR_NOBUFFERS; |
| goto EXIT; |
| } |
| |
| buf.index = index; |
| pCtx->pInbuf[buf.index].bQueued = VIDEO_TRUE; |
| |
| if (pCtx->bShareInbuf == VIDEO_TRUE) { |
| buf.memory = pCtx->nMemoryType; |
| for (i = 0; i < nPlanes; i++) { |
| /* V4L2_MEMORY_USERPTR */ |
| buf.m.planes[i].m.userptr = (unsigned long)pBuffer[i]; |
| /* V4L2_MEMORY_DMABUF */ |
| buf.m.planes[i].m.fd = pCtx->pInbuf[index].planes[i].fd; |
| buf.m.planes[i].length = pCtx->pInbuf[index].planes[i].allocSize; |
| buf.m.planes[i].bytesused = dataSize[i]; |
| ALOGV("%s: shared inbuf(%d) plane(%d) addr=%p fd=%d len=%d used=%d\n", __func__, |
| index, i, |
| buf.m.planes[i].m.userptr, |
| buf.m.planes[i].m.fd, |
| buf.m.planes[i].length, |
| buf.m.planes[i].bytesused); |
| } |
| } else { |
| buf.memory = V4L2_MEMORY_MMAP; |
| for (i = 0; i < nPlanes; i++) |
| buf.m.planes[i].bytesused = dataSize[i]; |
| } |
| |
| // FIXME: figure out why |pPrivate| may be NULL. |
| if (pPrivate && |
| (((OMX_BUFFERHEADERTYPE *)pPrivate)->nFlags & OMX_BUFFERFLAG_EOS) == OMX_BUFFERFLAG_EOS) { |
| buf.flags |= V4L2_BUF_FLAG_LAST_FRAME; |
| ALOGV("%s: OMX_BUFFERFLAG_EOS => LAST_FRAME: 0x%x", __func__, |
| !!(buf.flags & V4L2_BUF_FLAG_LAST_FRAME)); |
| } |
| |
| pCtx->pInbuf[buf.index].pPrivate = pPrivate; |
| |
| pthread_mutex_unlock(pMutex); |
| |
| if (exynos_v4l2_qbuf(pCtx->hDec, &buf) != 0) { |
| ALOGE("%s: Failed to enqueue input buffer", __func__); |
| pthread_mutex_lock(pMutex); |
| pCtx->pInbuf[buf.index].pPrivate = NULL; |
| pCtx->pInbuf[buf.index].bQueued = VIDEO_FALSE; |
| pthread_mutex_unlock(pMutex); |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Enqueue (Output) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Enqueue_Outbuf( |
| void *pHandle, |
| unsigned char *pBuffer[], |
| unsigned int dataSize[], |
| int nPlanes, |
| void *pPrivate) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| pthread_mutex_t *pMutex = NULL; |
| |
| struct v4l2_plane planes[VIDEO_DECODER_OUTBUF_PLANES]; |
| struct v4l2_buffer buf; |
| int i, index; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (VIDEO_DECODER_OUTBUF_PLANES < nPlanes) { |
| ALOGE("%s: Number of max planes : %d, nPlanes : %d", __func__, |
| VIDEO_DECODER_OUTBUF_PLANES, nPlanes); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| memset(&buf, 0, sizeof(buf)); |
| buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| buf.m.planes = planes; |
| buf.length = VIDEO_DECODER_OUTBUF_PLANES; |
| |
| pMutex = (pthread_mutex_t*)pCtx->pOutMutex; |
| pthread_mutex_lock(pMutex); |
| index = MFC_Decoder_Find_Outbuf(pCtx, pBuffer[0]); |
| if (index == -1) { |
| pthread_mutex_unlock(pMutex); |
| ALOGE("%s: Failed to get index", __func__); |
| ret = VIDEO_ERROR_NOBUFFERS; |
| goto EXIT; |
| } |
| buf.index = index; |
| pCtx->pOutbuf[buf.index].bQueued = VIDEO_TRUE; |
| |
| if (pCtx->bShareOutbuf == VIDEO_TRUE) { |
| buf.memory = pCtx->nMemoryType; |
| for (i = 0; i < nPlanes; i++) { |
| /* V4L2_MEMORY_USERPTR */ |
| buf.m.planes[i].m.userptr = (unsigned long)pBuffer[i]; |
| /* V4L2_MEMORY_DMABUF */ |
| buf.m.planes[i].m.fd = pCtx->pOutbuf[index].planes[i].fd; |
| buf.m.planes[i].length = pCtx->pOutbuf[index].planes[i].allocSize; |
| buf.m.planes[i].bytesused = dataSize[i]; |
| ALOGV("%s: shared outbuf(%d) plane=%d addr=%p fd=%d len=%d used=%d\n", __func__, |
| index, i, |
| buf.m.planes[i].m.userptr, |
| buf.m.planes[i].m.fd, |
| buf.m.planes[i].length, |
| buf.m.planes[i].bytesused); |
| } |
| } else { |
| ALOGV("%s: non-shared outbuf(%d)\n", __func__, index); |
| buf.memory = V4L2_MEMORY_MMAP; |
| } |
| |
| pCtx->pOutbuf[buf.index].pPrivate = pPrivate; |
| |
| pthread_mutex_unlock(pMutex); |
| |
| if (exynos_v4l2_qbuf(pCtx->hDec, &buf) != 0) { |
| ALOGE("%s: Failed to enqueue output buffer", __func__); |
| pthread_mutex_lock(pMutex); |
| pCtx->pOutbuf[buf.index].pPrivate = NULL; |
| pCtx->pOutbuf[buf.index].bQueued = VIDEO_FALSE; |
| pthread_mutex_unlock(pMutex); |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Dequeue (Input) |
| */ |
| static ExynosVideoBuffer *MFC_Decoder_Dequeue_Inbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoBuffer *pInbuf = NULL; |
| pthread_mutex_t *pMutex = NULL; |
| |
| struct v4l2_buffer buf; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| goto EXIT; |
| } |
| |
| if (pCtx->bStreamonInbuf == VIDEO_FALSE) { |
| pInbuf = NULL; |
| goto EXIT; |
| } |
| |
| memset(&buf, 0, sizeof(buf)); |
| |
| buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| |
| if (pCtx->bShareInbuf == VIDEO_TRUE) |
| buf.memory = pCtx->nMemoryType; |
| else |
| buf.memory = V4L2_MEMORY_MMAP; |
| |
| if (exynos_v4l2_dqbuf(pCtx->hDec, &buf) != 0) { |
| pInbuf = NULL; |
| goto EXIT; |
| } |
| |
| pMutex = (pthread_mutex_t*)pCtx->pInMutex; |
| pthread_mutex_lock(pMutex); |
| |
| pInbuf = &pCtx->pInbuf[buf.index]; |
| pCtx->pInbuf[buf.index].bQueued = VIDEO_FALSE; |
| |
| if (pCtx->bStreamonInbuf == VIDEO_FALSE) |
| pInbuf = NULL; |
| |
| pthread_mutex_unlock(pMutex); |
| |
| EXIT: |
| return pInbuf; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Dequeue (Output) |
| */ |
| static ExynosVideoBuffer *MFC_Decoder_Dequeue_Outbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoBuffer *pOutbuf = NULL; |
| pthread_mutex_t *pMutex = NULL; |
| |
| struct v4l2_buffer buf; |
| int value, state; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| goto EXIT; |
| } |
| |
| if (pCtx->bStreamonOutbuf == VIDEO_FALSE) { |
| pOutbuf = NULL; |
| goto EXIT; |
| } |
| |
| memset(&buf, 0, sizeof(buf)); |
| buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| |
| if (pCtx->bShareOutbuf == VIDEO_TRUE) |
| buf.memory = pCtx->nMemoryType; |
| else |
| buf.memory = V4L2_MEMORY_MMAP; |
| |
| /* HACK: pOutbuf return -1 means DECODING_ONLY for almost cases */ |
| if (exynos_v4l2_dqbuf(pCtx->hDec, &buf) != 0) { |
| pOutbuf = NULL; |
| goto EXIT; |
| } |
| |
| if (pCtx->bStreamonOutbuf == VIDEO_FALSE) { |
| pOutbuf = NULL; |
| goto EXIT; |
| } |
| |
| pMutex = (pthread_mutex_t*)pCtx->pOutMutex; |
| pthread_mutex_lock(pMutex); |
| |
| pOutbuf = &pCtx->pOutbuf[buf.index]; |
| |
| exynos_v4l2_g_ctrl(pCtx->hDec, V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS, &value); |
| |
| switch (value) { |
| case 0: |
| pOutbuf->displayStatus = VIDEO_FRAME_STATUS_DECODING_ONLY; |
| break; |
| case 1: |
| pOutbuf->displayStatus = VIDEO_FRAME_STATUS_DISPLAY_DECODING; |
| break; |
| case 2: |
| pOutbuf->displayStatus = VIDEO_FRAME_STATUS_DISPLAY_ONLY; |
| break; |
| case 3: |
| exynos_v4l2_g_ctrl(pCtx->hDec, V4L2_CID_MPEG_MFC51_VIDEO_CHECK_STATE, &state); |
| if (state == 1) /* Resolution is changed */ |
| pOutbuf->displayStatus = VIDEO_FRAME_STATUS_CHANGE_RESOL; |
| else /* Decoding is finished */ |
| pOutbuf->displayStatus = VIDEO_FRAME_STATUS_DECODING_FINISHED; |
| break; |
| default: |
| pOutbuf->displayStatus = VIDEO_FRAME_STATUS_UNKNOWN; |
| break; |
| } |
| |
| switch (buf.flags & (0x7 << 3)) { |
| case V4L2_BUF_FLAG_KEYFRAME: |
| pOutbuf->frameType = VIDEO_FRAME_I; |
| break; |
| case V4L2_BUF_FLAG_PFRAME: |
| pOutbuf->frameType = VIDEO_FRAME_P; |
| break; |
| case V4L2_BUF_FLAG_BFRAME: |
| pOutbuf->frameType = VIDEO_FRAME_B; |
| break; |
| default: |
| pOutbuf->frameType = VIDEO_FRAME_OTHERS; |
| break; |
| }; |
| |
| pOutbuf->bQueued = VIDEO_FALSE; |
| |
| pthread_mutex_unlock(pMutex); |
| |
| EXIT: |
| return pOutbuf; |
| } |
| |
| static ExynosVideoErrorType MFC_Decoder_Clear_Queued_Inbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| int i; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| for (i = 0; i < pCtx->nInbufs; i++) { |
| pCtx->pInbuf[i].bQueued = VIDEO_FALSE; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| static ExynosVideoErrorType MFC_Decoder_Clear_Queued_Outbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| int i; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| for (i = 0; i < pCtx->nOutbufs; i++) { |
| pCtx->pOutbuf[i].bQueued = VIDEO_FALSE; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Cleanup Buffer (Input) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Cleanup_Buffer_Inbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| struct v4l2_requestbuffers req; |
| int nBufferCount = 0; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| nBufferCount = 0; /* for clean-up */ |
| |
| memset(&req, 0, sizeof(req)); |
| |
| req.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; |
| req.count = nBufferCount; |
| |
| if (pCtx->bShareInbuf == VIDEO_TRUE) |
| req.memory = pCtx->nMemoryType; |
| else |
| req.memory = V4L2_MEMORY_MMAP; |
| |
| if (exynos_v4l2_reqbufs(pCtx->hDec, &req) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| pCtx->nInbufs = (int)req.count; |
| |
| if (pCtx->pInbuf != NULL) { |
| free(pCtx->pInbuf); |
| pCtx->pInbuf = NULL; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Cleanup Buffer (Output) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Cleanup_Buffer_Outbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| struct v4l2_requestbuffers req; |
| int nBufferCount = 0; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| nBufferCount = 0; /* for clean-up */ |
| |
| memset(&req, 0, sizeof(req)); |
| |
| req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| req.count = nBufferCount; |
| |
| if (pCtx->bShareOutbuf == VIDEO_TRUE) |
| req.memory = pCtx->nMemoryType; |
| else |
| req.memory = V4L2_MEMORY_MMAP; |
| |
| if (exynos_v4l2_reqbufs(pCtx->hDec, &req) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| pCtx->nOutbufs = (int)req.count; |
| |
| if (pCtx->pOutbuf != NULL) { |
| free(pCtx->pOutbuf); |
| pCtx->pOutbuf = NULL; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] FindIndex (Output) |
| */ |
| static int MFC_Decoder_FindEmpty_Outbuf(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| int nIndex = -1; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| goto EXIT; |
| } |
| |
| for (nIndex = 0; nIndex < pCtx->nOutbufs; nIndex++) { |
| if ((pCtx->pOutbuf[nIndex].bQueued == VIDEO_FALSE) && |
| (pCtx->pOutbuf[nIndex].bSlotUsed == VIDEO_FALSE)) |
| break; |
| } |
| |
| if (nIndex == pCtx->nOutbufs) |
| nIndex = -1; |
| |
| EXIT: |
| return nIndex; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] BufferIndexFree (Output) |
| */ |
| void MFC_Decoder_BufferIndexFree_Outbuf(void *pHandle, PrivateDataShareBuffer *pPDSB, int index) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| int i, j; |
| |
| ALOGV("De-queue buf.index:%d", index); |
| ALOGV("pOutbuf fd:%d", pCtx->pOutbuf[index].planes[0].fd); |
| |
| if (pCtx->pOutbuf[index].nIndexUseCnt == 0) { |
| pCtx->pOutbuf[index].bSlotUsed = VIDEO_FALSE; |
| } |
| |
| for (i = 0; pPDSB->dpbFD[i].fd > -1; i++) { |
| ALOGV("pPDSB->dpbFD[%d].fd:%d", i, pPDSB->dpbFD[i].fd); |
| for (j = 0; pCtx->nOutbufs > j; j++) |
| if (pPDSB->dpbFD[i].fd == pCtx->pOutbuf[j].planes[0].fd) { |
| if (pCtx->pOutbuf[j].bQueued == VIDEO_FALSE) { |
| if (pCtx->pOutbuf[j].nIndexUseCnt > 0) |
| pCtx->pOutbuf[j].nIndexUseCnt--; |
| } else if(pCtx->pOutbuf[j].bQueued == VIDEO_TRUE) { |
| if (pCtx->pOutbuf[j].nIndexUseCnt > 1) { |
| /* The buffer being used as the reference buffer came again. */ |
| pCtx->pOutbuf[j].nIndexUseCnt--; |
| } else { |
| /* Reference DPB buffer is internally reused. */ |
| } |
| } |
| ALOGV("dec FD:%d, pCtx->pOutbuf[%d].nIndexUseCnt:%d", pPDSB->dpbFD[i].fd, j, pCtx->pOutbuf[j].nIndexUseCnt); |
| if ((pCtx->pOutbuf[j].nIndexUseCnt == 0) && |
| (pCtx->pOutbuf[j].bQueued == VIDEO_FALSE)) { |
| pCtx->pOutbuf[j].bSlotUsed = VIDEO_FALSE; |
| } |
| } |
| } |
| memset((char *)pPDSB, -1, sizeof(PrivateDataShareBuffer)); |
| |
| return; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] ExtensionEnqueue (Output) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_ExtensionEnqueue_Outbuf( |
| void *pHandle, |
| unsigned char *pBuffer[], |
| unsigned int *pFd[], |
| unsigned int allocLen[], |
| unsigned int dataSize[], |
| int nPlanes, |
| void *pPrivate) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| pthread_mutex_t *pMutex = NULL; |
| |
| struct v4l2_plane planes[VIDEO_DECODER_OUTBUF_PLANES]; |
| struct v4l2_buffer buf; |
| int state, index, i; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (VIDEO_DECODER_OUTBUF_PLANES < nPlanes) { |
| ALOGE("%s: Number of max planes : %d, nPlanes : %d", __func__, |
| VIDEO_DECODER_OUTBUF_PLANES, nPlanes); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| memset(&buf, 0, sizeof(buf)); |
| buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| buf.m.planes = planes; |
| buf.length = VIDEO_DECODER_OUTBUF_PLANES; |
| |
| pMutex = (pthread_mutex_t*)pCtx->pOutMutex; |
| pthread_mutex_lock(pMutex); |
| |
| index = MFC_Decoder_Find_Outbuf(pCtx, pBuffer[0]); |
| if (index == -1) { |
| ALOGV("%s: Failed to find index", __func__); |
| index = MFC_Decoder_FindEmpty_Outbuf(pCtx); |
| if (index == -1) { |
| pthread_mutex_unlock(pMutex); |
| ALOGE("%s: Failed to get index", __func__); |
| ret = VIDEO_ERROR_NOBUFFERS; |
| goto EXIT; |
| } |
| } |
| |
| buf.index = index; |
| ALOGV("En-queue index:%d pCtx->pOutbuf[buf.index].bQueued:%d, pFd[0]:%d", |
| index, pCtx->pOutbuf[buf.index].bQueued, pFd[0]); |
| pCtx->pOutbuf[buf.index].bQueued = VIDEO_TRUE; |
| pCtx->pOutbuf[buf.index].bSlotUsed = VIDEO_TRUE; |
| |
| buf.memory = pCtx->nMemoryType; |
| for (i = 0; i < nPlanes; i++) { |
| /* V4L2_MEMORY_USERPTR */ |
| buf.m.planes[i].m.userptr = (unsigned long)pBuffer[i]; |
| /* V4L2_MEMORY_DMABUF */ |
| buf.m.planes[i].m.fd = pFd[i]; |
| buf.m.planes[i].length = allocLen[i]; |
| buf.m.planes[i].bytesused = dataSize[i]; |
| |
| /* Temporary storage for Dequeue */ |
| pCtx->pOutbuf[buf.index].planes[i].addr = (unsigned long)pBuffer[i]; |
| pCtx->pOutbuf[buf.index].planes[i].fd = (unsigned int)pFd[i]; |
| pCtx->pOutbuf[buf.index].planes[i].allocSize = allocLen[i]; |
| |
| ALOGV("%s: shared outbuf(%d) plane=%d addr=0x%x fd=%d len=%d used=%d\n", |
| __func__, index, i, |
| (void*)buf.m.planes[i].m.userptr, buf.m.planes[i].m.fd, |
| buf.m.planes[i].length, buf.m.planes[i].bytesused); |
| } |
| |
| pCtx->pOutbuf[buf.index].pPrivate = pPrivate; |
| pCtx->pOutbuf[buf.index].nIndexUseCnt++; |
| |
| pthread_mutex_unlock(pMutex); |
| |
| if (exynos_v4l2_qbuf(pCtx->hDec, &buf) != 0) { |
| pthread_mutex_lock(pMutex); |
| pCtx->pOutbuf[buf.index].nIndexUseCnt--; |
| pCtx->pOutbuf[buf.index].pPrivate = NULL; |
| pCtx->pOutbuf[buf.index].bQueued = VIDEO_FALSE; |
| if (pCtx->pOutbuf[buf.index].nIndexUseCnt == 0) |
| pCtx->pOutbuf[buf.index].bSlotUsed = VIDEO_FALSE; |
| exynos_v4l2_g_ctrl(pCtx->hDec, V4L2_CID_MPEG_MFC51_VIDEO_CHECK_STATE, &state); |
| pthread_mutex_unlock(pMutex); |
| |
| if (state == 1) { |
| /* The case of Resolution is changed */ |
| ret = VIDEO_ERROR_WRONGBUFFERSIZE; |
| } else { |
| ALOGE("%s: Failed to enqueue output buffer", __func__); |
| ret = VIDEO_ERROR_APIFAIL; |
| } |
| goto EXIT; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] ExtensionDequeue (Output) |
| */ |
| static ExynosVideoErrorType MFC_Decoder_ExtensionDequeue_Outbuf( |
| void *pHandle, |
| ExynosVideoBuffer *pVideoBuffer) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| pthread_mutex_t *pMutex = NULL; |
| ExynosVideoBuffer *pOutbuf = NULL; |
| PrivateDataShareBuffer *pPDSB = NULL; |
| struct v4l2_buffer buf; |
| int value, state, i, j; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (pCtx->bStreamonOutbuf == VIDEO_FALSE) { |
| pOutbuf = NULL; |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| memset(&buf, 0, sizeof(buf)); |
| buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; |
| |
| if (pCtx->bShareOutbuf == VIDEO_TRUE) |
| buf.memory = pCtx->nMemoryType; |
| else |
| buf.memory = V4L2_MEMORY_MMAP; |
| |
| /* HACK: pOutbuf return -1 means DECODING_ONLY for almost cases */ |
| if (exynos_v4l2_dqbuf(pCtx->hDec, &buf) != 0) { |
| pOutbuf = NULL; |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| pMutex = (pthread_mutex_t*)pCtx->pOutMutex; |
| pthread_mutex_lock(pMutex); |
| |
| pOutbuf = &pCtx->pOutbuf[buf.index]; |
| exynos_v4l2_g_ctrl(pCtx->hDec, V4L2_CID_MPEG_MFC51_VIDEO_DISPLAY_STATUS, &value); |
| |
| switch (value) { |
| case 0: |
| pOutbuf->displayStatus = VIDEO_FRAME_STATUS_DECODING_ONLY; |
| break; |
| case 1: |
| pOutbuf->displayStatus = VIDEO_FRAME_STATUS_DISPLAY_DECODING; |
| break; |
| case 2: |
| pOutbuf->displayStatus = VIDEO_FRAME_STATUS_DISPLAY_ONLY; |
| break; |
| case 3: |
| exynos_v4l2_g_ctrl(pCtx->hDec, V4L2_CID_MPEG_MFC51_VIDEO_CHECK_STATE, &state); |
| if (state == 1) /* Resolution is changed */ |
| pOutbuf->displayStatus = VIDEO_FRAME_STATUS_CHANGE_RESOL; |
| else /* Decoding is finished */ |
| pOutbuf->displayStatus = VIDEO_FRAME_STATUS_DECODING_FINISHED; |
| break; |
| default: |
| pOutbuf->displayStatus = VIDEO_FRAME_STATUS_UNKNOWN; |
| break; |
| } |
| |
| switch (buf.flags & (0x7 << 3)) { |
| case V4L2_BUF_FLAG_KEYFRAME: |
| pOutbuf->frameType = VIDEO_FRAME_I; |
| break; |
| case V4L2_BUF_FLAG_PFRAME: |
| pOutbuf->frameType = VIDEO_FRAME_P; |
| break; |
| case V4L2_BUF_FLAG_BFRAME: |
| pOutbuf->frameType = VIDEO_FRAME_B; |
| break; |
| default: |
| pOutbuf->frameType = VIDEO_FRAME_OTHERS; |
| break; |
| }; |
| |
| pPDSB = ((PrivateDataShareBuffer *)pCtx->nPrivateDataShareAddress) + buf.index; |
| if (pCtx->pOutbuf[buf.index].bQueued == VIDEO_TRUE) { |
| memcpy(pVideoBuffer, pOutbuf, sizeof(ExynosVideoBuffer)); |
| memcpy((char *)(&(pVideoBuffer->PDSB)), (char *)pPDSB, sizeof(PrivateDataShareBuffer)); |
| } else { |
| ret = VIDEO_ERROR_NOBUFFERS; |
| ALOGV("%s :: %d", __FUNCTION__, __LINE__); |
| } |
| |
| pCtx->pOutbuf[buf.index].bQueued = VIDEO_FALSE; |
| MFC_Decoder_BufferIndexFree_Outbuf(pHandle, pPDSB, buf.index); |
| |
| pthread_mutex_unlock(pMutex); |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder Buffer OPS] Enable Dynamic DPB |
| */ |
| static ExynosVideoErrorType MFC_Decoder_Enable_DynamicDPB(void *pHandle) |
| { |
| ExynosVideoDecContext *pCtx = (ExynosVideoDecContext *)pHandle; |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if (pCtx == NULL) { |
| ALOGE("%s: Video context info must be supplied", __func__); |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| if (exynos_v4l2_s_ctrl(pCtx->hDec, V4L2_CID_MPEG_MFC_SET_DYNAMIC_DPB_MODE, 1) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| if (exynos_v4l2_s_ctrl(pCtx->hDec, V4L2_CID_MPEG_MFC_SET_USER_SHARED_HANDLE, pCtx->nPrivateDataShareFD) != 0) { |
| ret = VIDEO_ERROR_APIFAIL; |
| goto EXIT; |
| } |
| |
| EXIT: |
| return ret; |
| } |
| |
| /* |
| * [Decoder OPS] Common |
| */ |
| static ExynosVideoDecOps defDecOps = { |
| .nSize = 0, |
| .Init = MFC_Decoder_Init, |
| .Finalize = MFC_Decoder_Finalize, |
| .Set_DisplayDelay = MFC_Decoder_Set_DisplayDelay, |
| .Set_IFrameDecoding = MFC_Decoder_Set_IFrameDecoding, |
| .Enable_PackedPB = MFC_Decoder_Enable_PackedPB, |
| .Enable_LoopFilter = MFC_Decoder_Enable_LoopFilter, |
| .Enable_SliceMode = MFC_Decoder_Enable_SliceMode, |
| .Get_ActualBufferCount = MFC_Decoder_Get_ActualBufferCount, |
| .Set_FrameTag = MFC_Decoder_Set_FrameTag, |
| .Get_FrameTag = MFC_Decoder_Get_FrameTag, |
| .Enable_SEIParsing = MFC_Decoder_Enable_SEIParsing, |
| .Get_FramePackingInfo = MFC_Decoder_Get_FramePackingInfo, |
| }; |
| |
| /* |
| * [Decoder Buffer OPS] Input |
| */ |
| static ExynosVideoDecBufferOps defInbufOps = { |
| .nSize = 0, |
| .Enable_Cacheable = MFC_Decoder_Enable_Cacheable_Inbuf, |
| .Set_Shareable = MFC_Decoder_Set_Shareable_Inbuf, |
| .Get_Buffer = NULL, |
| .Set_Geometry = MFC_Decoder_Set_Geometry_Inbuf, |
| .Get_Geometry = NULL, |
| .Setup = MFC_Decoder_Setup_Inbuf, |
| .Run = MFC_Decoder_Run_Inbuf, |
| .Stop = MFC_Decoder_Stop_Inbuf, |
| .Enqueue = MFC_Decoder_Enqueue_Inbuf, |
| .Enqueue_All = NULL, |
| .Dequeue = MFC_Decoder_Dequeue_Inbuf, |
| .Register = MFC_Decoder_Register_Inbuf, |
| .Clear_RegisteredBuffer = MFC_Decoder_Clear_RegisteredBuffer_Inbuf, |
| .Clear_Queue = MFC_Decoder_Clear_Queued_Inbuf, |
| .Cleanup_Buffer = MFC_Decoder_Cleanup_Buffer_Inbuf, |
| }; |
| |
| /* |
| * [Decoder Buffer OPS] Output |
| */ |
| static ExynosVideoDecBufferOps defOutbufOps = { |
| .nSize = 0, |
| .Enable_Cacheable = MFC_Decoder_Enable_Cacheable_Outbuf, |
| .Set_Shareable = MFC_Decoder_Set_Shareable_Outbuf, |
| .Get_Buffer = MFC_Decoder_Get_Buffer_Outbuf, |
| .Set_Geometry = MFC_Decoder_Set_Geometry_Outbuf, |
| .Get_Geometry = MFC_Decoder_Get_Geometry_Outbuf, |
| .Setup = MFC_Decoder_Setup_Outbuf, |
| .Run = MFC_Decoder_Run_Outbuf, |
| .Stop = MFC_Decoder_Stop_Outbuf, |
| .Enqueue = MFC_Decoder_Enqueue_Outbuf, |
| .Enqueue_All = NULL, |
| .Dequeue = MFC_Decoder_Dequeue_Outbuf, |
| .Register = MFC_Decoder_Register_Outbuf, |
| .Clear_RegisteredBuffer = MFC_Decoder_Clear_RegisteredBuffer_Outbuf, |
| .Clear_Queue = MFC_Decoder_Clear_Queued_Outbuf, |
| .Cleanup_Buffer = MFC_Decoder_Cleanup_Buffer_Outbuf, |
| .ExtensionEnqueue = MFC_Decoder_ExtensionEnqueue_Outbuf, |
| .ExtensionDequeue = MFC_Decoder_ExtensionDequeue_Outbuf, |
| .Enable_DynamicDPB = MFC_Decoder_Enable_DynamicDPB, |
| }; |
| |
| int Exynos_Video_Register_Decoder( |
| ExynosVideoDecOps *pDecOps, |
| ExynosVideoDecBufferOps *pInbufOps, |
| ExynosVideoDecBufferOps *pOutbufOps) |
| { |
| ExynosVideoErrorType ret = VIDEO_ERROR_NONE; |
| |
| if ((pDecOps == NULL) || (pInbufOps == NULL) || (pOutbufOps == NULL)) { |
| ret = VIDEO_ERROR_BADPARAM; |
| goto EXIT; |
| } |
| |
| defDecOps.nSize = sizeof(defDecOps); |
| defInbufOps.nSize = sizeof(defInbufOps); |
| defOutbufOps.nSize = sizeof(defOutbufOps); |
| |
| memcpy((char *)pDecOps + sizeof(pDecOps->nSize), (char *)&defDecOps + sizeof(defDecOps.nSize), |
| pDecOps->nSize - sizeof(pDecOps->nSize)); |
| |
| memcpy((char *)pInbufOps + sizeof(pInbufOps->nSize), (char *)&defInbufOps + sizeof(defInbufOps.nSize), |
| pInbufOps->nSize - sizeof(pInbufOps->nSize)); |
| |
| memcpy((char *)pOutbufOps + sizeof(pOutbufOps->nSize), (char *)&defOutbufOps + sizeof(defOutbufOps.nSize), |
| pOutbufOps->nSize - sizeof(pOutbufOps->nSize)); |
| |
| EXIT: |
| return ret; |
| } |