diff --git a/osal/CMakeLists.txt b/osal/CMakeLists.txt index 2e93ad6e..6a824a57 100644 --- a/osal/CMakeLists.txt +++ b/osal/CMakeLists.txt @@ -1,7 +1,7 @@ # vim: syntax=cmake if(${ANDROID}) set(OS_DIR android) -set(MPP_ALLOCATOR allocator/allocator_ion.c) +set(MPP_ALLOCATOR allocator/allocator_ion.c allocator/allocator_drm.c) elseif(${UNIX}) set(OS_DIR linux) set(MPP_ALLOCATOR allocator/allocator_ion.c allocator/allocator_drm.c) diff --git a/osal/allocator/allocator_drm.c b/osal/allocator/allocator_drm.c index 98a4d431..cf57a9a5 100644 --- a/osal/allocator/allocator_drm.c +++ b/osal/allocator/allocator_drm.c @@ -1,388 +1,481 @@ -/* - * Copyright 2010 Rockchip Electronics S.LSI Co. LTD - * - * Licensed under the Apache License, Versdrm 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 CONDITDRMS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissdrms and - * limitatdrms under the License. - */ - -#define MODULE_TAG "mpp_drm" - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "os_mem.h" -#include "allocator_drm.h" - -#include "mpp_mem.h" -#include "mpp_log.h" -#include "mpp_common.h" - -static RK_U32 drm_debug = 0; - -#define DRM_FUNCTDRM (0x00000001) -#define DRM_DEVICE (0x00000002) -#define DRM_CLINET (0x00000004) -#define DRM_IOCTL (0x00000008) - -#define DRM_DETECT_IOMMU_DISABLE (0x0) /* use DRM_HEAP_TYPE_DMA */ -#define DRM_DETECT_IOMMU_ENABLE (0x1) /* use DRM_HEAP_TYPE_SYSTEM */ -#define DRM_DETECT_NO_DTS (0x2) /* use DRM_HEAP_TYPE_CARVEOUT */ - -#define drm_dbg(flag, fmt, ...) _mpp_dbg(drm_debug, flag, fmt, ## __VA_ARGS__) - -static int drm_ioctl(int fd, int req, void *arg) -{ - int ret = ioctl(fd, req, arg); - if (ret < 0) { - mpp_err("drm_ioctl %x failed with code %d: %s\n", req, - ret, strerror(errno)); - return -errno; - } - return ret; -} - -static int drm_alloc(int fd, size_t len, size_t align, unsigned int heap_mask, - unsigned int flags, RK_U32 *handle) -{ - int ret; - struct drm_mode_create_dumb dmcb; - memset(&dmcb, 0, sizeof(struct drm_mode_create_dumb)); - dmcb.bpp = align; - dmcb.width = ((len + align) & (~align)) / align; - dmcb.height = 8; - dmcb.size = (len + align) & (~align); - - if (handle == NULL) - return -EINVAL; - - ret = drm_ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &dmcb); - if (ret < 0) - return ret; - *handle = dmcb.handle; - (void)heap_mask; - (void)flags; - return ret; -} - -static int drm_handle_to_fd(int fd, RK_U32 handle, int *map_fd, RK_U32 flags) -{ - int ret; - struct drm_prime_handle dph; - memset(&dph, 0, sizeof(struct drm_prime_handle)); - dph.handle = handle; - dph.fd = 1; - dph.flags = 0; - - if (map_fd == NULL) - return -EINVAL; - - ret = drm_ioctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &dph); - if (ret < 0) { - return ret; - } - - *map_fd = dph.fd; - - if (*map_fd < 0) { - mpp_err("map ioctl returned negative fd\n"); - return -EINVAL; - } - (void)flags; - return ret; -} - -static int drm_free(int fd, RK_U32 handle) -{ - struct drm_mode_destroy_dumb data = { - .handle = handle, - }; - return drm_ioctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &data); -} - -static int drm_map(int fd, RK_U32 handle, size_t length, int prot, - int flags, off_t offset, unsigned char **ptr, int *map_fd) -{ - int ret; - struct drm_mode_map_dumb dmmd; - memset(&dmmd, 0, sizeof(dmmd)); - dmmd.handle = handle; - - if (map_fd == NULL) - return -EINVAL; - if (ptr == NULL) - return -EINVAL; - - ret = drm_handle_to_fd(fd, handle, map_fd, 0); - if (ret < 0) - return ret; - - ret = drm_ioctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &dmmd); - if (ret < 0) - return ret; - - *ptr = mmap(NULL, length, prot, flags, *map_fd, dmmd.offset); - if (*ptr == MAP_FAILED) { - mpp_err("mmap failed: %s\n", strerror(errno)); - return -errno; - } - (void)offset; - return ret; -} - -#include - -static const char *search_name = NULL; - -static int _compare_name(const struct dirent *dir) -{ - if (search_name && strstr(dir->d_name, search_name)) - return 1; - - return 0; -} - -/* - * directory search functdrm: - * search directory with dir_name on path. - * if found match dir append name on path and return - * - * return 0 for failure - * return positive value for length of new path - */ -static RK_S32 find_dir_in_path(char *path, const char *dir_name, size_t max_length) -{ - struct dirent **dir; - RK_S32 path_len = strnlen(path, max_length); - RK_S32 new_path_len = 0; - RK_S32 n; - - search_name = dir_name; - n = scandir(path, &dir, _compare_name, alphasort); - if (n <= 0) { - mpp_err("scan %s for %s return %d\n", path, dir_name, n); - } else { - mpp_assert(n == 1); - - new_path_len = path_len; - new_path_len += snprintf(path + path_len, max_length - path_len - 1, - "/%s", dir[0]->d_name); - free(dir[0]); - free(dir); - } - search_name = NULL; - return new_path_len; -} - -static char *dts_devices[] = { - "vpu_service", - "hevc_service", - "rkvdec", - "rkvenc", -}; - -static RK_S32 check_sysfs_iommu() -{ - RK_U32 i = 0, found = 0; - RK_S32 ret = DRM_DETECT_IOMMU_DISABLE; - char path[256]; - - for (i = 0; i < MPP_ARRAY_ELEMS(dts_devices); i++) { - snprintf(path, sizeof(path), "/proc/device-tree"); - if (find_dir_in_path(path, dts_devices[i], sizeof(path))) { - if (find_dir_in_path(path, "iommu_enabled", sizeof(path))) { - FILE *iommu_fp = fopen(path, "rb"); - - if (iommu_fp) { - RK_U32 iommu_enabled = 0; - fread(&iommu_enabled, sizeof(RK_U32), 1, iommu_fp); - mpp_log("%s iommu_enabled %d\n", dts_devices[i], (iommu_enabled > 0)); - fclose(iommu_fp); - if (iommu_enabled) - ret = DRM_DETECT_IOMMU_ENABLE; - } - found = 1; - break; - } - } - } - - if (!found) { - mpp_err("can not find dts for all possible devices\n"); - ret = DRM_DETECT_NO_DTS; - } - - return ret; -} - -typedef struct { - RK_U32 alignment; - RK_S32 drm_device; -} allocator_ctx_drm; - -#define VPU_IOC_MAGIC 'l' -#define VPU_IOC_PROBE_IOMMU_STATUS _IOR(VPU_IOC_MAGIC, 5, unsigned long) -#define VPU_IOC_PROBE_HEAP_STATUS _IOR(VPU_IOC_MAGIC, 6, unsigned long) - -enum { - DRM_HEAP_TYPE_SYSTEM = 0, - DRM_HEAP_TYPE_CMA, - DRM_HEAP_NUMS, -}; - -const char *dev_drm = "/dev/dri/card0"; -static RK_S32 drm_heap_id = -1; -static RK_U32 drm_heap_mask = 1; - -MPP_RET os_allocator_drm_open(void **ctx, size_t alignment) -{ - RK_S32 fd; - allocator_ctx_drm *p; - - if (NULL == ctx) { - mpp_err("os_allocator_open Android do not accept NULL input\n"); - return MPP_ERR_NULL_PTR; - } - - *ctx = NULL; - - fd = open(dev_drm, O_RDWR); - if (fd < 0) { - mpp_err("open %s failed!\n", dev_drm); - return MPP_ERR_UNKNOW; - } - - drm_dbg(DRM_DEVICE, "open drm dev fd %d\n", fd); - - p = mpp_malloc(allocator_ctx_drm, 1); - if (NULL == p) { - close(fd); - mpp_err("os_allocator_open Android failed to allocate context\n"); - return MPP_ERR_MALLOC; - } else { - /* - * default drm use cma, do nothing here - */ - drm_heap_mask = (1 << DRM_HEAP_TYPE_CMA); - drm_heap_id = DRM_HEAP_TYPE_CMA; - p->alignment = alignment; - p->drm_device = fd; - *ctx = p; - } - - return MPP_OK; -} - -MPP_RET os_allocator_drm_alloc(void *ctx, MppBufferInfo *info) -{ - MPP_RET ret = MPP_OK; - allocator_ctx_drm *p = NULL; - - if (NULL == ctx) { - mpp_err("os_allocator_close Android do not accept NULL input\n"); - return MPP_ERR_NULL_PTR; - } - - p = (allocator_ctx_drm *)ctx; - ret = drm_alloc(p->drm_device, info->size, p->alignment, - drm_heap_mask, 0, - (RK_U32 *)&info->hnd); - if (ret) { - mpp_err("os_allocator_drm_alloc drm_alloc failed ret %d\n", ret); - return ret; - } - ret = drm_map(p->drm_device, (RK_U32)((intptr_t)info->hnd), info->size, - PROT_READ | PROT_WRITE, MAP_SHARED, (off_t)0, - (unsigned char**)&info->ptr, &info->fd); - if (ret) { - mpp_err("os_allocator_drm_alloc drm_map failed ret %d\n", ret); - return ret; - } - return ret; -} - -MPP_RET os_allocator_drm_import(void *ctx, MppBufferInfo *data) -{ - MPP_RET ret = MPP_OK; - (void)ctx; - // NOTE: do not use the original buffer fd, - // use dup fd to avoid unexpected external fd close - data->fd = dup(data->fd); - /* I don't know whether it is correct for drm */ - data->ptr = mmap(NULL, data->size, PROT_READ | PROT_WRITE, MAP_SHARED, data->fd, 0); - if (data->ptr == MAP_FAILED) { - mpp_err_f("map error %s\n", strerror(errno)); - ret = MPP_NOK; - close(data->fd); - data->fd = -1; - data->ptr = NULL; - } - return ret; -} - -MPP_RET os_allocator_drm_release(void *ctx, MppBufferInfo *data) -{ - (void)ctx; - munmap(data->ptr, data->size); - close(data->fd); - return MPP_OK; -} - -MPP_RET os_allocator_drm_free(void *ctx, MppBufferInfo *data) -{ - allocator_ctx_drm *p = NULL; - - if (NULL == ctx) { - mpp_err("os_allocator_close Android do not accept NULL input\n"); - return MPP_ERR_NULL_PTR; - } - - p = (allocator_ctx_drm *)ctx; - munmap(data->ptr, data->size); - close(data->fd); - drm_free(p->drm_device, (RK_U32)((intptr_t)data->hnd)); - return MPP_OK; -} - -MPP_RET os_allocator_drm_close(void *ctx) -{ - int ret; - allocator_ctx_drm *p; - - if (NULL == ctx) { - mpp_err("os_allocator_close Android do not accept NULL input\n"); - return MPP_ERR_NULL_PTR; - } - - p = (allocator_ctx_drm *)ctx; - ret = close(p->drm_device); - mpp_free(p); - if (ret < 0) - return (MPP_RET) - errno; - return MPP_OK; -} - -os_allocator allocator_drm = { - os_allocator_drm_open, - os_allocator_drm_alloc, - os_allocator_drm_free, - os_allocator_drm_import, - os_allocator_drm_release, - os_allocator_drm_close, -}; - +/* + * Copyright 2010 Rockchip Electronics S.LSI Co. LTD + * + * Licensed under the Apache License, Versdrm 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 CONDITDRMS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissdrms and + * limitatdrms under the License. + */ + +#define MODULE_TAG "mpp_drm" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "os_mem.h" +#include "allocator_drm.h" + +#include "mpp_mem.h" +#include "mpp_log.h" +#include "mpp_common.h" + +static RK_U32 drm_debug = 0; + +#define DRM_FUNCTION (0x00000001) +#define DRM_DEVICE (0x00000002) +#define DRM_CLIENT (0x00000004) +#define DRM_IOCTL (0x00000008) + +#define DRM_DETECT_IOMMU_DISABLE (0x0) /* use DRM_HEAP_TYPE_DMA */ +#define DRM_DETECT_IOMMU_ENABLE (0x1) /* use DRM_HEAP_TYPE_SYSTEM */ +#define DRM_DETECT_NO_DTS (0x2) /* use DRM_HEAP_TYPE_CARVEOUT */ + +#define drm_dbg(flag, fmt, ...) _mpp_dbg_f(drm_debug, flag, fmt, ## __VA_ARGS__) + +static int drm_ioctl(int fd, int req, void *arg) +{ + int ret; + + do { + ret = ioctl(fd, req, arg); + } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); + + drm_dbg(DRM_FUNCTION, "drm_ioctl %x with code %d: %s", req, + ret, strerror(errno)); + + return ret; +} + +static void* drm_mmap(int fd, size_t len, int prot, int flags, loff_t offset) +{ + static unsigned long pagesize_mask = 0; + + if (fd < 0) + return NULL; + + if (!pagesize_mask) + pagesize_mask = getpagesize() - 1; + + len = (len + pagesize_mask) & ~pagesize_mask; + + if (offset & 4095) { + return NULL; + } + + return mmap64(NULL, len, prot, flags, fd, offset); +} + +static int drm_handle_to_fd(int fd, RK_U32 handle, int *map_fd, RK_U32 flags) +{ + int ret; + struct drm_prime_handle dph; + memset(&dph, 0, sizeof(struct drm_prime_handle)); + dph.handle = handle; + dph.fd = -1; + dph.flags = flags; + + if (map_fd == NULL) + return -EINVAL; + + ret = drm_ioctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &dph); + if (ret < 0) { + return ret; + } + + *map_fd = dph.fd; + + drm_dbg(DRM_FUNCTION, "get fd %d", *map_fd); + + if (*map_fd < 0) { + mpp_err("map ioctl returned negative fd\n"); + return -EINVAL; + } + + return ret; +} + +static int drm_fd_to_handle(int fd, int map_fd, RK_U32 *handle, RK_U32 flags) +{ + int ret; + struct drm_prime_handle dph; + + dph.fd = map_fd; + dph.flags = flags; + + ret = drm_ioctl(fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &dph); + if (ret < 0) { + return ret; + } + + *handle = dph.handle; + drm_dbg(DRM_FUNCTION, "get handle %d", *handle); + + return ret; +} + +static int drm_map(int fd, RK_U32 handle, size_t length, int prot, + int flags, unsigned char **ptr, int *map_fd) +{ + int ret; + struct drm_mode_map_dumb dmmd; + memset(&dmmd, 0, sizeof(dmmd)); + dmmd.handle = handle; + + if (map_fd == NULL) + return -EINVAL; + if (ptr == NULL) + return -EINVAL; + + ret = drm_handle_to_fd(fd, handle, map_fd, 0); + drm_dbg(DRM_FUNCTION, "drm_map fd %d", *map_fd); + if (ret < 0) + return ret; + + ret = drm_ioctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &dmmd); + if (ret < 0) { + close(*map_fd); + return ret; + } + + drm_dbg(DRM_FUNCTION, "dev fd %d length %d", fd, length); + + *ptr = drm_mmap(fd, length, prot, flags, dmmd.offset); + if (*ptr == MAP_FAILED) { + close(*map_fd); + *map_fd = -1; + mpp_err("mmap failed: %s\n", strerror(errno)); + return -errno; + } + + return ret; +} + +static int drm_alloc(int fd, size_t len, size_t align, RK_U32 *handle) +{ + int ret; + struct drm_mode_create_dumb dmcb; + + drm_dbg(DRM_FUNCTION, "len %ld aligned %ld\n", len, align); + + memset(&dmcb, 0, sizeof(struct drm_mode_create_dumb)); + dmcb.bpp = 8; + dmcb.width = (len + align - 1) & (~(align - 1)); + dmcb.height = 1; + dmcb.size = dmcb.width * dmcb.bpp; + + drm_dbg(DRM_FUNCTION, "fd %d aligned %d size %lld\n", fd, align, dmcb.size); + + if (handle == NULL) + return -EINVAL; + + ret = drm_ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &dmcb); + if (ret < 0) + return ret; + *handle = dmcb.handle; + + drm_dbg(DRM_FUNCTION, "get handle %d size %d", *handle, dmcb.size); + + return ret; +} + +static int drm_free(int fd, RK_U32 handle) +{ + struct drm_mode_destroy_dumb data = { + .handle = handle, + }; + return drm_ioctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &data); +} + +#include + +static const char *search_name = NULL; + +static int _compare_name(const struct dirent *dir) +{ + if (search_name && strstr(dir->d_name, search_name)) + return 1; + + return 0; +} + +/* + * directory search functdrm: + * search directory with dir_name on path. + * if found match dir append name on path and return + * + * return 0 for failure + * return positive value for length of new path + */ +static RK_S32 find_dir_in_path(char *path, const char *dir_name, size_t max_length) +{ + struct dirent **dir; + RK_S32 path_len = strnlen(path, max_length); + RK_S32 new_path_len = 0; + RK_S32 n; + + search_name = dir_name; + n = scandir(path, &dir, _compare_name, alphasort); + if (n <= 0) { + mpp_err("scan %s for %s return %d\n", path, dir_name, n); + } else { + mpp_assert(n == 1); + + new_path_len = path_len; + new_path_len += snprintf(path + path_len, max_length - path_len - 1, + "/%s", dir[0]->d_name); + free(dir[0]); + free(dir); + } + search_name = NULL; + return new_path_len; +} + +static RK_S32 check_sysfs_iommu() +{ + RK_U32 i = 0; + RK_U32 dts_info_found = 0; + RK_U32 ion_info_found = 0; + RK_S32 ret = DRM_DETECT_IOMMU_DISABLE; + char path[256]; + static char *dts_devices[] = { + "vpu_service", + "hevc_service", + "rkvdec", + "rkvenc", + }; + static char *system_heaps[] = { + "vmalloc", + "system-heap", + }; + + mpp_env_get_u32("drm_debug", &drm_debug, 0); +#ifdef SOFIA_3GR_LINUX + return ret; +#endif + + for (i = 0; i < MPP_ARRAY_ELEMS(dts_devices); i++) { + snprintf(path, sizeof(path), "/proc/device-tree"); + if (find_dir_in_path(path, dts_devices[i], sizeof(path))) { + if (find_dir_in_path(path, "iommu_enabled", sizeof(path))) { + FILE *iommu_fp = fopen(path, "rb"); + + if (iommu_fp) { + RK_U32 iommu_enabled = 0; + fread(&iommu_enabled, sizeof(RK_U32), 1, iommu_fp); + mpp_log("%s iommu_enabled %d\n", dts_devices[i], (iommu_enabled > 0)); + fclose(iommu_fp); + if (iommu_enabled) + ret = DRM_DETECT_IOMMU_ENABLE; + } + dts_info_found = 1; + break; + } + } + } + + if (!dts_info_found) { + for (i = 0; i < MPP_ARRAY_ELEMS(system_heaps); i++) { + snprintf(path, sizeof(path), "/sys/kernel/debug/ion/heaps"); + if (find_dir_in_path(path, system_heaps[i], sizeof(path))) { + mpp_log("%s found\n", system_heaps[i]); + ret = DRM_DETECT_IOMMU_ENABLE; + ion_info_found = 1; + break; + } + } + } + + if (!dts_info_found && !ion_info_found) { + mpp_err("can not find any hint from all possible devices\n"); + ret = DRM_DETECT_NO_DTS; + } + + return ret; +} + +typedef struct { + RK_U32 alignment; + RK_S32 drm_device; +} allocator_ctx_drm; + +#define VPU_IOC_MAGIC 'l' +#define VPU_IOC_PROBE_IOMMU_STATUS _IOR(VPU_IOC_MAGIC, 5, unsigned long) +#define VPU_IOC_PROBE_HEAP_STATUS _IOR(VPU_IOC_MAGIC, 6, unsigned long) + +enum { + DRM_HEAP_TYPE_SYSTEM = 0, + DRM_HEAP_TYPE_CMA, + DRM_HEAP_NUMS, +}; + +const char *dev_drm = "/dev/dri/card0"; + +MPP_RET os_allocator_drm_open(void **ctx, size_t alignment) +{ + RK_S32 fd; + allocator_ctx_drm *p; + + drm_dbg(DRM_FUNCTION, "enter"); + + if (NULL == ctx) { + mpp_err("os_allocator_open Android do not accept NULL input\n"); + return MPP_ERR_NULL_PTR; + } + + *ctx = NULL; + + fd = open(dev_drm, O_RDWR); + if (fd < 0) { + mpp_err("open %s failed!\n", dev_drm); + return MPP_ERR_UNKNOW; + } + + drm_dbg(DRM_DEVICE, "open drm dev fd %d\n", fd); + + p = mpp_malloc(allocator_ctx_drm, 1); + if (NULL == p) { + close(fd); + mpp_err("os_allocator_open Android failed to allocate context\n"); + return MPP_ERR_MALLOC; + } else { + /* + * default drm use cma, do nothing here + */ + p->alignment = alignment; + p->drm_device = fd; + *ctx = p; + } + + drm_dbg(DRM_FUNCTION, "leave"); + + return MPP_OK; +} + +MPP_RET os_allocator_drm_alloc(void *ctx, MppBufferInfo *info) +{ + MPP_RET ret = MPP_OK; + allocator_ctx_drm *p = NULL; + + if (NULL == ctx) { + mpp_err("os_allocator_close Android do not accept NULL input\n"); + return MPP_ERR_NULL_PTR; + } + + p = (allocator_ctx_drm *)ctx; + drm_dbg(DRM_FUNCTION, "alignment %d size %d", p->alignment, info->size); + ret = drm_alloc(p->drm_device, info->size, p->alignment, + (RK_U32 *)&info->hnd); + if (ret) { + mpp_err("os_allocator_drm_alloc drm_alloc failed ret %d\n", ret); + return ret; + } + drm_dbg(DRM_FUNCTION, "handle %d", (RK_U32)((intptr_t)info->hnd)); + ret = drm_map(p->drm_device, (RK_U32)((intptr_t)info->hnd), info->size, + PROT_READ | PROT_WRITE, MAP_SHARED, (unsigned char **)&info->ptr, &info->fd); + if (ret) { + mpp_err("os_allocator_drm_alloc drm_map failed ret %d\n", ret); + return ret; + } + return ret; +} + +MPP_RET os_allocator_drm_import(void *ctx, MppBufferInfo *data) +{ + MPP_RET ret = MPP_OK; + allocator_ctx_drm *p = (allocator_ctx_drm *)ctx; + struct drm_mode_map_dumb dmmd; + memset(&dmmd, 0, sizeof(dmmd)); + + drm_dbg(DRM_FUNCTION, "enter"); + // NOTE: do not use the original buffer fd, + // use dup fd to avoid unexpected external fd close + data->fd = dup(data->fd); + + ret = drm_fd_to_handle(p->drm_device, data->fd, (RK_U32 *)&data->hnd, 0); + + drm_dbg(DRM_FUNCTION, "get handle %d", (RK_U32)(data->hnd)); + + dmmd.handle = (RK_U32)(data->hnd); + + ret = drm_ioctl(p->drm_device, DRM_IOCTL_MODE_MAP_DUMB, &dmmd); + if (ret < 0) + return ret; + + drm_dbg(DRM_FUNCTION, "dev fd %d length %d", p->drm_device, data->size); + + data->ptr = drm_mmap(p->drm_device, data->size, PROT_READ | PROT_WRITE, MAP_SHARED, dmmd.offset); + if (data->ptr == MAP_FAILED) { + mpp_err("mmap failed: %s\n", strerror(errno)); + return -errno; + } + + drm_dbg(DRM_FUNCTION, "leave"); + + return ret; +} + +MPP_RET os_allocator_drm_release(void *ctx, MppBufferInfo *data) +{ + (void)ctx; + munmap(data->ptr, data->size); + close(data->fd); + return MPP_OK; +} + +MPP_RET os_allocator_drm_free(void *ctx, MppBufferInfo *data) +{ + allocator_ctx_drm *p = NULL; + + if (NULL == ctx) { + mpp_err("os_allocator_close Android do not accept NULL input\n"); + return MPP_ERR_NULL_PTR; + } + + p = (allocator_ctx_drm *)ctx; + munmap(data->ptr, data->size); + close(data->fd); + drm_free(p->drm_device, (RK_U32)((intptr_t)data->hnd)); + return MPP_OK; +} + +MPP_RET os_allocator_drm_close(void *ctx) +{ + int ret; + allocator_ctx_drm *p; + + if (NULL == ctx) { + mpp_err("os_allocator_close Android do not accept NULL input\n"); + return MPP_ERR_NULL_PTR; + } + + p = (allocator_ctx_drm *)ctx; + drm_dbg(DRM_FUNCTION, "close fd %d", p->drm_device); + ret = close(p->drm_device); + mpp_free(p); + if (ret < 0) + return (MPP_RET) - errno; + return MPP_OK; +} + +os_allocator allocator_drm = { + os_allocator_drm_open, + os_allocator_drm_alloc, + os_allocator_drm_free, + os_allocator_drm_import, + os_allocator_drm_release, + os_allocator_drm_close, +}; diff --git a/osal/android/os_allocator.c b/osal/android/os_allocator.c index c04b9f91..aa6584d8 100644 --- a/osal/android/os_allocator.c +++ b/osal/android/os_allocator.c @@ -1,138 +1,148 @@ -/* - * Copyright 2015 Rockchip Electronics 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. - */ - -#include -#include -#include -#include - -#include "os_mem.h" -#include "os_allocator.h" -#include "allocator_ion.h" - -#include "mpp_mem.h" -#include "mpp_log.h" - -typedef struct { - RK_U32 alignment; - RK_S32 fd_count; -} allocator_ctx_normal; - -MPP_RET os_allocator_normal_open(void **ctx, size_t alignment) -{ - MPP_RET ret = MPP_OK; - allocator_ctx_normal *p = NULL; - - if (NULL == ctx) { - mpp_err("os_allocator_open Android do not accept NULL input\n"); - return MPP_ERR_NULL_PTR; - } - - p = mpp_malloc(allocator_ctx_normal, 1); - if (NULL == p) { - mpp_err("os_allocator_open Android failed to allocate context\n"); - ret = MPP_ERR_MALLOC; - } else - p->alignment = alignment; - - p->fd_count = 0; - - *ctx = p; - return ret; -} - -MPP_RET os_allocator_normal_alloc(void *ctx, MppBufferInfo *info) -{ - allocator_ctx_normal *p = NULL; - - if (NULL == ctx) { - mpp_err("os_allocator_close Android do not accept NULL input\n"); - return MPP_ERR_NULL_PTR; - } - - p = (allocator_ctx_normal *)ctx; - info->fd = p->fd_count++; - return os_malloc(&info->ptr, p->alignment, info->size); -} - -MPP_RET os_allocator_normal_free(void *ctx, MppBufferInfo *info) -{ - (void) ctx; - if (info->ptr) - os_free(info->ptr); - return MPP_OK; -} - -MPP_RET os_allocator_normal_import(void *ctx, MppBufferInfo *info) -{ - allocator_ctx_normal *p = (allocator_ctx_normal *)ctx; - mpp_assert(ctx); - mpp_assert(info->ptr); - mpp_assert(info->size); - info->hnd = NULL; - info->fd = p->fd_count++; - return MPP_OK; -} - -MPP_RET os_allocator_normal_release(void *ctx, MppBufferInfo *info) -{ - (void) ctx; - mpp_assert(info->ptr); - mpp_assert(info->size); - info->ptr = NULL; - info->size = 0; - info->hnd = NULL; - info->fd = -1; - return MPP_OK; -} - -MPP_RET os_allocator_normal_close(void *ctx) -{ - if (ctx) { - mpp_free(ctx); - return MPP_OK; - } - mpp_err("os_allocator_close Linux found NULL context input\n"); - return MPP_NOK; -} - -static os_allocator allocator_normal = { - os_allocator_normal_open, - os_allocator_normal_alloc, - os_allocator_normal_free, - os_allocator_normal_import, - os_allocator_normal_release, - os_allocator_normal_close, -}; - -MPP_RET os_allocator_get(os_allocator *api, MppBufferType type) -{ - MPP_RET ret = MPP_OK; - switch (type) { - case MPP_BUFFER_TYPE_NORMAL : - case MPP_BUFFER_TYPE_V4L2 : { - *api = allocator_normal; - } break; - case MPP_BUFFER_TYPE_ION : { - *api = allocator_ion; - } break; - default : { - ret = MPP_NOK; - } break; - } - return ret; -} - +/* + * Copyright 2015 Rockchip Electronics 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. + */ + +#include +#include +#include +#include + +#include "os_mem.h" +#include "os_allocator.h" +#include "allocator_ion.h" +#include "allocator_drm.h" + +#include "mpp_mem.h" +#include "mpp_log.h" +#include "mpp_common.h" + +typedef struct { + RK_U32 alignment; + RK_S32 fd_count; +} allocator_ctx_normal; + +MPP_RET os_allocator_normal_open(void **ctx, size_t alignment) +{ + MPP_RET ret = MPP_OK; + allocator_ctx_normal *p = NULL; + + if (NULL == ctx) { + mpp_err("os_allocator_open Android do not accept NULL input\n"); + return MPP_ERR_NULL_PTR; + } + + p = mpp_malloc(allocator_ctx_normal, 1); + if (NULL == p) { + mpp_err("os_allocator_open Android failed to allocate context\n"); + ret = MPP_ERR_MALLOC; + } else + p->alignment = alignment; + + p->fd_count = 0; + + *ctx = p; + return ret; +} + +MPP_RET os_allocator_normal_alloc(void *ctx, MppBufferInfo *info) +{ + allocator_ctx_normal *p = NULL; + + if (NULL == ctx) { + mpp_err("os_allocator_close Android do not accept NULL input\n"); + return MPP_ERR_NULL_PTR; + } + + p = (allocator_ctx_normal *)ctx; + info->fd = p->fd_count++; + return os_malloc(&info->ptr, p->alignment, info->size); +} + +MPP_RET os_allocator_normal_free(void *ctx, MppBufferInfo *info) +{ + (void)ctx; + if (info->ptr) + os_free(info->ptr); + return MPP_OK; +} + +MPP_RET os_allocator_normal_import(void *ctx, MppBufferInfo *info) +{ + allocator_ctx_normal *p = (allocator_ctx_normal *)ctx; + mpp_assert(ctx); + mpp_assert(info->ptr); + mpp_assert(info->size); + info->hnd = NULL; + info->fd = p->fd_count++; + return MPP_OK; +} + +MPP_RET os_allocator_normal_release(void *ctx, MppBufferInfo *info) +{ + (void)ctx; + mpp_assert(info->ptr); + mpp_assert(info->size); + info->ptr = NULL; + info->size = 0; + info->hnd = NULL; + info->fd = -1; + return MPP_OK; +} + +MPP_RET os_allocator_normal_close(void *ctx) +{ + if (ctx) { + mpp_free(ctx); + return MPP_OK; + } + mpp_err("os_allocator_close Linux found NULL context input\n"); + return MPP_NOK; +} + +static os_allocator allocator_normal = { + os_allocator_normal_open, + os_allocator_normal_alloc, + os_allocator_normal_free, + os_allocator_normal_import, + os_allocator_normal_release, + os_allocator_normal_close, +}; + +MPP_RET os_allocator_get(os_allocator *api, MppBufferType type) +{ + MPP_RET ret = MPP_OK; + + switch (type) { + case MPP_BUFFER_TYPE_NORMAL : + case MPP_BUFFER_TYPE_V4L2 : { + *api = allocator_normal; + } + break; + case MPP_BUFFER_TYPE_ION : { + *api = allocator_ion; + } + break; + case MPP_BUFFER_TYPE_DRM : { + *api = allocator_drm; + } + break; + default : { + ret = MPP_NOK; + } + break; + } + return ret; +} + diff --git a/osal/linux/drm.h b/osal/linux/drm.h index 4dcccce5..ef8b95b8 100644 --- a/osal/linux/drm.h +++ b/osal/linux/drm.h @@ -630,17 +630,17 @@ struct drm_get_cap { /** * DRM_CLIENT_CAP_UNIVERSAL_PLANES * - * if set to 1, the DRM core will expose the full universal plane list - * (including primary and cursor planes). + * If set to 1, the DRM core will expose all planes (overlay, primary, and + * cursor) to userspace. */ -#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2 +#define DRM_CLIENT_CAP_UNIVERSAL_PLANES 2 /** * DRM_CLIENT_CAP_ATOMIC * - * If set to 1, the DRM core will allow atomic modesetting requests. + * If set to 1, the DRM core will expose atomic properties to userspace */ -#define DRM_CLIENT_CAP_ATOMIC 3 +#define DRM_CLIENT_CAP_ATOMIC 3 /** DRM_IOCTL_SET_CLIENT_CAP ioctl argument type */ struct drm_set_client_cap { @@ -816,7 +816,6 @@ struct drm_event_vblank { #define DRM_CAP_PRIME 0x5 #define DRM_CAP_TIMESTAMP_MONOTONIC 0x6 #define DRM_CAP_ASYNC_PAGE_FLIP 0x7 -#define DRM_CAP_ADDFB2_MODIFIERS 0x10 #define DRM_PRIME_CAP_IMPORT 0x1 #define DRM_PRIME_CAP_EXPORT 0x2 diff --git a/osal/linux/drm_mode.h b/osal/linux/drm_mode.h index 25c6833c..f74fa3c4 100644 --- a/osal/linux/drm_mode.h +++ b/osal/linux/drm_mode.h @@ -92,6 +92,14 @@ #define DRM_MODE_DIRTY_ON 1 #define DRM_MODE_DIRTY_ANNOTATE 2 +/* rotation property bits */ +#define DRM_ROTATE_0 0 +#define DRM_ROTATE_90 1 +#define DRM_ROTATE_180 2 +#define DRM_ROTATE_270 3 +#define DRM_REFLECT_X 4 +#define DRM_REFLECT_Y 5 + struct drm_mode_modeinfo { __u32 clock; __u16 hdisplay, hsync_start, hsync_end, htotal, hskew; @@ -259,6 +267,13 @@ struct drm_mode_get_connector { #define DRM_MODE_PROP_OBJECT DRM_MODE_PROP_TYPE(1) #define DRM_MODE_PROP_SIGNED_RANGE DRM_MODE_PROP_TYPE(2) +/* the PROP_ATOMIC flag is used to hide properties from userspace that + * is not aware of atomic properties. This is mostly to work around + * older userspace (DDX drivers) that read/write each prop they find, + * witout being aware that this could be triggering a lengthy modeset. + */ +#define DRM_MODE_PROP_ATOMIC 0x80000000 + struct drm_mode_property_enum { __u64 value; char name[DRM_PROP_NAME_LEN]; @@ -322,8 +337,7 @@ struct drm_mode_fb_cmd { __u32 handle; }; -#define DRM_MODE_FB_INTERLACED (1<<0) /* for interlaced framebuffers */ -#define DRM_MODE_FB_MODIFIERS (1<<1) /* enables ->modifer[] */ +#define DRM_MODE_FB_INTERLACED (1<<0) /* for interlaced framebuffers */ struct drm_mode_fb_cmd2 { __u32 fb_id; @@ -344,18 +358,10 @@ struct drm_mode_fb_cmd2 { * So it would consist of Y as offset[0] and UV as * offset[1]. Note that offset[0] will generally * be 0. - * - * To accommodate tiled, compressed, etc formats, a per-plane - * modifier can be specified. The default value of zero - * indicates "native" format as specified by the fourcc. - * Vendor specific modifier token. This allows, for example, - * different tiling/swizzling pattern on different planes. - * See discussion above of DRM_FORMAT_MOD_xxx. */ __u32 handles[4]; __u32 pitches[4]; /* pitch for each plane */ __u32 offsets[4]; /* offset of each plane */ - __u64 modifier[4]; /* ie, tiling, compressed (per plane) */ }; #define DRM_MODE_FB_DIRTY_ANNOTATE_COPY 0x01 @@ -517,9 +523,16 @@ struct drm_mode_destroy_dumb { }; /* page-flip flags are valid, plus: */ -#define DRM_MODE_ATOMIC_TEST_ONLY 0x0100 -#define DRM_MODE_ATOMIC_NONBLOCK 0x0200 -#define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400 +#define DRM_MODE_ATOMIC_TEST_ONLY 0x0100 +#define DRM_MODE_ATOMIC_NONBLOCK 0x0200 +#define DRM_MODE_ATOMIC_ALLOW_MODESET 0x0400 + +#define DRM_MODE_ATOMIC_FLAGS (\ + DRM_MODE_PAGE_FLIP_EVENT |\ + DRM_MODE_PAGE_FLIP_ASYNC |\ + DRM_MODE_ATOMIC_TEST_ONLY |\ + DRM_MODE_ATOMIC_NONBLOCK |\ + DRM_MODE_ATOMIC_ALLOW_MODESET) struct drm_mode_atomic { __u32 flags; @@ -552,5 +565,4 @@ struct drm_mode_destroy_blob { __u32 blob_id; }; - #endif diff --git a/test/vpu_api_test.c b/test/vpu_api_test.c index ac129c07..4e546b40 100644 --- a/test/vpu_api_test.c +++ b/test/vpu_api_test.c @@ -1,782 +1,789 @@ -/* - * Copyright 2015 Rockchip Electronics 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. - */ - -#define MODULE_TAG "vpu_api_demo" - -#include -#include -#include - -#include "mpp_log.h" -#include "mpp_time.h" - -#include "vpu_api.h" -#include "utils.h" - -#define FOR_TEST_ENCODE 1 - -static RK_U32 VPU_API_DEMO_DEBUG_DISABLE = 0; - -#define BSWAP32(x) \ - ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ - (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) - -#define DEMO_ERR_RET(err) do { ret = err; goto DEMO_OUT; } while (0) -#define DECODE_ERR_RET(err) do { ret = err; goto DECODE_OUT; } while (0) -#define ENCODE_ERR_RET(err) do { ret = err; goto ENCODE_OUT; } while (0) - - -typedef enum VPU_API_DEMO_RET { - VPU_DEMO_OK = 0, - VPU_DEMO_PARSE_HELP_OK = 1, - - VPU_DEMO_ERROR_BASE = -100, - ERROR_INVALID_PARAM = VPU_DEMO_ERROR_BASE - 1, - ERROR_INVALID_STREAM = VPU_DEMO_ERROR_BASE - 2, - ERROR_IO = VPU_DEMO_ERROR_BASE - 3, - ERROR_MEMORY = VPU_DEMO_ERROR_BASE - 4, - ERROR_INIT_VPU = VPU_DEMO_ERROR_BASE - 5, - - ERROR_VPU_DECODE = VPU_DEMO_ERROR_BASE - 90, -} VPU_API_DEMO_RET; - -typedef struct VpuApiDemoCmdContext { - RK_U32 width; - RK_U32 height; - CODEC_TYPE codec_type; - OMX_RK_VIDEO_CODINGTYPE coding; - char input_file[200]; - char output_file[200]; - RK_U8 have_input; - RK_U8 have_output; - RK_U8 disable_debug; - RK_U32 record_frames; - RK_S64 record_start_ms; -} VpuApiDemoCmdContext_t; - -typedef struct VpuApiEncInput { - EncInputStream_t stream; - RK_U32 capability; -} VpuApiEncInput; - -static OptionInfo vpuApiCmd[] = { - {"i", "input_file", "input bitstream file"}, - {"o", "output_file", "output bitstream file, "}, - {"w", "width", "the width of input bitstream"}, - {"h", "height", "the height of input bitstream"}, - {"t", "codec_type", "the codec type, dec: deoder, enc: encoder, default: decoder"}, - {"coding", "coding_type", "encoding type of the bitstream"}, - {"vframes", "number", "set the number of video frames to record"}, - {"ss", "time_off", "set the start time offset, use Ms as the unit."}, - {"d", "disable", "disable the debug output info."}, -}; - -static void show_usage() -{ - mpp_log("usage: vpu_apiDemo [options] input_file, \n\n"); - - mpp_log("Getting help:\n"); - mpp_log("-help --print options of vpu api demo\n"); -} - -static RK_S32 show_help() -{ - mpp_log("usage: vpu_apiDemo [options] input_file, \n\n"); - show_options(vpuApiCmd); - return 0; -} - -static RK_S32 parse_options(int argc, char **argv, VpuApiDemoCmdContext_t* cmdCxt) -{ - char *opt; - RK_S32 optindex, handleoptions = 1, ret = 0; - - if ((argc < 2) || (cmdCxt == NULL)) { - mpp_log("vpu api demo, input parameter invalid\n"); - show_usage(); - return ERROR_INVALID_PARAM; - } - - /* parse options */ - optindex = 1; - while (optindex < argc) { - opt = argv[optindex++]; - - if (handleoptions && opt[0] == '-' && opt[1] != '\0') { - if (opt[1] == '-') { - if (opt[2] != '\0') { - opt++; - } else { - handleoptions = 0; - continue; - } - } - - opt++; - - switch (*opt) { - case 'i': - if (argv[optindex]) { - memcpy(cmdCxt->input_file, argv[optindex], strlen(argv[optindex])); - cmdCxt->input_file[strlen(argv[optindex])] = '\0'; - cmdCxt->have_input = 1; - } else { - mpp_log("input file is invalid\n"); - ret = -1; - goto PARSE_OPINIONS_OUT; - } - break; - case 'o': - if (argv[optindex]) { - memcpy(cmdCxt->output_file, argv[optindex], strlen(argv[optindex])); - cmdCxt->output_file[strlen(argv[optindex])] = '\0'; - cmdCxt->have_output = 1; - break; - } else { - mpp_log("out file is invalid\n"); - ret = -1; - goto PARSE_OPINIONS_OUT; - } - case 'd': - cmdCxt->disable_debug = 1; - break; - case 'w': - if (argv[optindex]) { - cmdCxt->width = atoi(argv[optindex]); - break; - } else { - mpp_log("input width is invalid\n"); - ret = -1; - goto PARSE_OPINIONS_OUT; - } - case 'h': - if ((*(opt + 1) != '\0') && !strncmp(opt, "help", 4)) { - show_help(); - ret = VPU_DEMO_PARSE_HELP_OK; - goto PARSE_OPINIONS_OUT; - } else if (argv[optindex]) { - cmdCxt->height = atoi(argv[optindex]); - } else { - mpp_log("input height is invalid\n"); - ret = -1; - goto PARSE_OPINIONS_OUT; - } - break; - case 't': - if (argv[optindex]) { - cmdCxt->codec_type = atoi(argv[optindex]); - break; - } else { - mpp_log("input codec_type is invalid\n"); - ret = -1; - goto PARSE_OPINIONS_OUT; - } - - default: - if ((*(opt + 1) != '\0') && argv[optindex]) { - if (!strncmp(opt, "coding", 6)) { - mpp_log("coding, argv[optindex]: %s", - argv[optindex]); - cmdCxt->coding = atoi(argv[optindex]); - } else if (!strncmp(opt, "vframes", 7)) { - cmdCxt->record_frames = atoi(argv[optindex]); - } else if (!strncmp(opt, "ss", 2)) { - cmdCxt->record_start_ms = atoi(argv[optindex]); - } else { - ret = -1; - goto PARSE_OPINIONS_OUT; - } - } else { - ret = -1; - goto PARSE_OPINIONS_OUT; - } - break; - } - - optindex += ret; - } - } - -PARSE_OPINIONS_OUT: - if (ret < 0) { - mpp_log("vpu api demo, input parameter invalid\n"); - show_usage(); - return ERROR_INVALID_PARAM; - } - return ret; -} - -static RK_S32 readBytesFromFile(RK_U8* buf, RK_S32 aBytes, FILE* file) -{ - RK_S32 ret = 0; - - if ((NULL == buf) || (NULL == file) || (0 == aBytes)) { - return -1; - } - - ret = (RK_S32)fread(buf, 1, aBytes, file); - if (ret != aBytes) { - mpp_log("read %d bytes from file fail\n", aBytes); - return -1; - } - - return 0; -} - -static RK_S32 vpu_encode_demo(VpuApiDemoCmdContext_t *cmd) -{ - FILE* pInFile = NULL; - FILE* pOutFile = NULL; - struct VpuCodecContext *ctx = NULL; - RK_S32 nal = 0x00000001; - RK_S32 fileSize, ret, size; - RK_U32 readOneFrameSize = 0; - EncoderOut_t enc_out_yuv; - EncoderOut_t *enc_out = NULL; - VpuApiEncInput enc_in_strm; - VpuApiEncInput *api_enc_in = &enc_in_strm; - EncInputStream_t *enc_in = NULL; - EncParameter_t *enc_param = NULL; - RK_S64 fakeTimeUs = 0; - RK_U32 w_align = 0; - RK_U32 h_align = 0; - - int Format = ENC_INPUT_YUV420_PLANAR; - - if (cmd == NULL) { - return -1; - } - - if ((cmd->have_input == 0) || (cmd->width <= 0) || (cmd->height <= 0) - || (cmd->coding <= OMX_RK_VIDEO_CodingAutoDetect)) { - mpp_log("Warning: missing needed parameters for vpu api demo\n"); - } - - if (cmd->have_input) { - mpp_log("input bitstream w: %d, h: %d, coding: %d(%s), path: %s\n", - cmd->width, cmd->height, cmd->coding, - cmd->codec_type == CODEC_DECODER ? "decode" : "encode", - cmd->input_file); - - pInFile = fopen(cmd->input_file, "rb"); - if (pInFile == NULL) { - mpp_log("input file not exsist\n"); - ENCODE_ERR_RET(ERROR_INVALID_PARAM); - } - } else { - mpp_log("please set input bitstream file\n"); - ENCODE_ERR_RET(ERROR_INVALID_PARAM); - } - - if (cmd->have_output) { - mpp_log("vpu api demo output file: %s\n", - cmd->output_file); - pOutFile = fopen(cmd->output_file, "wb"); - if (pOutFile == NULL) { - mpp_log("can not write output file\n"); - ENCODE_ERR_RET(ERROR_INVALID_PARAM); - } - } - -#ifdef FOR_TEST_ENCODE - ctx = (struct VpuCodecContext*)malloc(sizeof(struct VpuCodecContext)); - if (!ctx) { - mpp_err("Input context has not been properly allocated"); - return -1; - } - memset(ctx, 0, sizeof(struct VpuCodecContext)); - - ctx->videoCoding = OMX_RK_VIDEO_CodingAVC; - ctx->codecType = CODEC_ENCODER; - ctx->width = cmd->width; - ctx->height = cmd->height; -#endif - - fseek(pInFile, 0L, SEEK_END); - fileSize = ftell(pInFile); - fseek(pInFile, 0L, SEEK_SET); - - memset(&enc_in_strm, 0, sizeof(VpuApiEncInput)); - enc_in = &enc_in_strm.stream; - enc_in->buf = NULL; - - memset(&enc_out_yuv, 0, sizeof(EncoderOut_t)); - enc_out = &enc_out_yuv; - enc_out->data = (RK_U8*)malloc(cmd->width * cmd->height); - if (enc_out->data == NULL) { - ENCODE_ERR_RET(ERROR_MEMORY); - } - - ret = vpu_open_context(&ctx); - if (ret || (ctx == NULL)) { - ENCODE_ERR_RET(ERROR_MEMORY); - } - - /* - ** now init vpu api context. codecType, codingType, width ,height - ** are all needed before init. - */ - ctx->codecType = cmd->codec_type; - ctx->videoCoding = cmd->coding; - ctx->width = cmd->width; - ctx->height = cmd->height; - ctx->no_thread = 1; - - ctx->private_data = malloc(sizeof(EncParameter_t)); - memset(ctx->private_data, 0, sizeof(EncParameter_t)); - - enc_param = (EncParameter_t*)ctx->private_data; - enc_param->width = cmd->width; - enc_param->height = cmd->height; - enc_param->format = ENC_INPUT_YUV420_PLANAR; - enc_param->rc_mode = 0; - enc_param->bitRate = 4000000; - enc_param->framerate = 25; - enc_param->enableCabac = 1; - enc_param->cabacInitIdc = 0; - enc_param->intraPicRate = 30; - enc_param->profileIdc = 66; - enc_param->levelIdc = 40; - - if ((ret = ctx->init(ctx, NULL, 0)) != 0) { - mpp_log("init vpu api context fail, ret: 0x%X\n", ret); - ENCODE_ERR_RET(ERROR_INIT_VPU); - } - - /* - ** init of VpuCodecContext while running encode, it returns - ** sps and pps of encoder output, you need to save sps and pps - ** after init. - */ - mpp_log("encode init ok, sps len: %d\n", ctx->extradata_size); - if (pOutFile && (ctx->extradata_size > 0)) { - mpp_log("dump %d bytes enc output stream to file\n", - ctx->extradata_size); - - /* save sps and pps */ - fwrite(ctx->extradata, 1, ctx->extradata_size, pOutFile); - fflush(pOutFile); - } - - ret = ctx->control(ctx, VPU_API_ENC_SETFORMAT, &Format); - if (ret) - mpp_err("VPU_API_ENC_SETFORMAT ret %d\n", ret); - - ret = ctx->control(ctx, VPU_API_ENC_GETCFG, enc_param); - if (ret) - mpp_log("VPU_API_ENC_GETCFG ret %d\n", ret); - - enc_param->rc_mode = 1; - - ret = ctx->control(ctx, VPU_API_ENC_SETCFG, enc_param); - if (ret) - mpp_log("VPU_API_ENC_SETCFG ret %d\n", ret); - - /* - ** vpu api encode process. - */ - mpp_log("init vpu api context ok, input yuv stream file size: %d\n", fileSize); - w_align = ((ctx->width + 15) & (~15)); - h_align = ((ctx->height + 15) & (~15)); - size = w_align * h_align * 3 / 2; - readOneFrameSize = ctx->width * ctx->height * 3 / 2; - mpp_log("%d %d %d %d %d", ctx->width, ctx->height, w_align, h_align, size); - nal = BSWAP32(nal); - - do { - if (ftell(pInFile) >= fileSize) { - mpp_log("read end of file, complete\n"); - break; - } - - if (enc_in && (enc_in->size == 0)) { - if (enc_in->buf == NULL) { - enc_in->buf = (RK_U8*)(malloc)(size); - if (enc_in->buf == NULL) { - ENCODE_ERR_RET(ERROR_MEMORY); - } - api_enc_in->capability = size; - } - - if (api_enc_in->capability < ((RK_U32)size)) { - enc_in->buf = (RK_U8*)(realloc)((void*)(enc_in->buf), size); - if (enc_in->buf == NULL) { - ENCODE_ERR_RET(ERROR_MEMORY); - } - api_enc_in->capability = size; - } - - if (readBytesFromFile(enc_in->buf, readOneFrameSize, pInFile)) { - break; - } else { - enc_in->size = size; - enc_in->timeUs = fakeTimeUs; - fakeTimeUs += 40000; - } - - mpp_log("read one frame, size: %d, timeUs: %lld, filePos: %ld\n", - enc_in->size, enc_in->timeUs , ftell(pInFile)); - } - - if ((ret = ctx->encode(ctx, enc_in, enc_out)) < 0) { - ENCODE_ERR_RET(ERROR_VPU_DECODE); - } else { - enc_in->size = 0; // TODO encode completely, and set enc_in->size to 0 - mpp_log("vpu encode one frame, out len: %d, left size: %d\n", - enc_out->size, enc_in->size); - - /* - ** encoder output stream is raw bitstream, you need to add nal - ** head by yourself. - */ - if ((enc_out->size) && (enc_out->data)) { - if (pOutFile) { - mpp_log("dump %d bytes enc output stream to file\n", - enc_out->size); - //fwrite((RK_U8*)&nal, 1, 4, pOutFile); // because output stream have start code, so here mask this code - fwrite(enc_out->data, 1, enc_out->size, pOutFile); - fflush(pOutFile); - } - - enc_out->size = 0; - } - } - - msleep(3); - } while (1); - -ENCODE_OUT: - if (enc_in && enc_in->buf) { - free(enc_in->buf); - enc_in->buf = NULL; - } - if (enc_out && (enc_out->data)) { - free(enc_out->data); - enc_out->data = NULL; - } - if (ctx) { - if (ctx->private_data) { - free(ctx->private_data); - ctx->private_data = NULL; - } - vpu_close_context(&ctx); - ctx = NULL; - } - if (pInFile) { - fclose(pInFile); - pInFile = NULL; - } - if (pOutFile) { - fclose(pOutFile); - pOutFile = NULL; - } - - if (ret) { - mpp_log("encode demo fail, err: %d\n", ret); - } else { - mpp_log("encode demo complete OK.\n"); - } - return ret; - -} - -static RK_S32 vpu_decode_demo(VpuApiDemoCmdContext_t *cmd) -{ - FILE* pInFile = NULL; - FILE* pOutFile = NULL; - struct VpuCodecContext* ctx = NULL; - RK_S32 fileSize = 0, pkt_size = 0; - RK_S32 ret = 0; - RK_U32 frame_count = 0; - DecoderOut_t decOut; - VideoPacket_t demoPkt; - VideoPacket_t* pkt = NULL; - DecoderOut_t *pOut = NULL; - VPU_FRAME *frame = NULL; - RK_S64 fakeTimeUs = 0; - RK_U8* pExtra = NULL; - RK_U32 extraSize = 0; - RK_U32 wAlign16 = 0; - RK_U32 hAlign16 = 0; - RK_U32 frameSize = 0; - - if (cmd == NULL) { - return -1; - } - - if ((cmd->have_input == 0) || (cmd->width <= 0) || (cmd->height <= 0) - || (cmd->coding <= OMX_RK_VIDEO_CodingAutoDetect)) { - mpp_log("Warning: missing needed parameters for vpu api demo\n"); - } - - if (cmd->have_input) { - mpp_log("input bitstream w: %d, h: %d, coding: %d(%s), path: %s\n", - cmd->width, cmd->height, cmd->coding, - cmd->codec_type == CODEC_DECODER ? "decode" : "encode", - cmd->input_file); - - pInFile = fopen(cmd->input_file, "rb"); - if (pInFile == NULL) { - mpp_log("input file not exsist\n"); - DECODE_ERR_RET(ERROR_INVALID_PARAM); - } - } else { - mpp_log("please set input bitstream file\n"); - DECODE_ERR_RET(ERROR_INVALID_PARAM); - } - - if (cmd->have_output) { - mpp_log("vpu api demo output file: %s\n", - cmd->output_file); - pOutFile = fopen(cmd->output_file, "wb"); - if (pOutFile == NULL) { - mpp_log("can not write output file\n"); - DECODE_ERR_RET(ERROR_INVALID_PARAM); - } - if (cmd->record_frames == 0) - cmd->record_frames = 5; - } - - fseek(pInFile, 0L, SEEK_END); - fileSize = ftell(pInFile); - fseek(pInFile, 0L, SEEK_SET); - - memset(&demoPkt, 0, sizeof(VideoPacket_t)); - pkt = &demoPkt; - pkt->data = NULL; - pkt->pts = VPU_API_NOPTS_VALUE; - pkt->dts = VPU_API_NOPTS_VALUE; - - memset(&decOut, 0, sizeof(DecoderOut_t)); - pOut = &decOut; - - pOut->data = (RK_U8*)(malloc)(sizeof(VPU_FRAME)); - if (pOut->data == NULL) { - DECODE_ERR_RET(ERROR_MEMORY); - } - memset(pOut->data, 0, sizeof(VPU_FRAME)); - - ret = vpu_open_context(&ctx); - if (ret || (ctx == NULL)) { - DECODE_ERR_RET(ERROR_MEMORY); - } - - /* - ** read codec extra data from input stream file. - */ - if (readBytesFromFile((RK_U8*)(&extraSize), 4, pInFile)) { - DECODE_ERR_RET(ERROR_IO); - } - - mpp_log("codec extra data size: %d\n", extraSize); - - pExtra = (RK_U8*)(malloc)(extraSize); - if (pExtra == NULL) { - DECODE_ERR_RET(ERROR_MEMORY); - } - memset(pExtra, 0, extraSize); - - if (readBytesFromFile(pExtra, extraSize, pInFile)) { - DECODE_ERR_RET(ERROR_IO); - } - - /* - ** now init vpu api context. codecType, codingType, width ,height - ** are all needed before init. - */ - ctx->codecType = cmd->codec_type; - ctx->videoCoding = cmd->coding; - ctx->width = cmd->width; - ctx->height = cmd->height; - ctx->no_thread = 1; - - if ((ret = ctx->init(ctx, pExtra, extraSize)) != 0) { - mpp_log("init vpu api context fail, ret: 0x%X\n", ret); - DECODE_ERR_RET(ERROR_INIT_VPU); - } - - /* - ** vpu api decoder process. - */ - mpp_log("init vpu api context ok, fileSize: %d\n", fileSize); - - do { - if (ftell(pInFile) >= fileSize) { - mpp_log("read end of file, complete\n"); - break; - } - - if (pkt && (pkt->size == 0)) { - if (readBytesFromFile((RK_U8*)(&pkt_size), 4, pInFile)) { - break; - } - - if (pkt->data == NULL) { - pkt->data = (RK_U8*)(malloc)(pkt_size); - if (pkt->data == NULL) { - DECODE_ERR_RET(ERROR_MEMORY); - } - pkt->capability = pkt_size; - } - - if (pkt->capability < ((RK_U32)pkt_size)) { - pkt->data = (RK_U8*)(realloc)((void*)(pkt->data), pkt_size); - if (pkt->data == NULL) { - DECODE_ERR_RET(ERROR_MEMORY); - } - pkt->capability = pkt_size; - } - - if (readBytesFromFile(pkt->data, pkt_size, pInFile)) { - break; - } else { - pkt->size = pkt_size; - pkt->pts = fakeTimeUs; - fakeTimeUs += 40000; - } - - mpp_log("read one packet, size: %d, pts: %lld, filePos: %ld\n", - pkt->size, pkt->pts, ftell(pInFile)); - } - - /* note: must set out put size to 0 before do decoder. */ - pOut->size = 0; - - if ((ret = ctx->decode(ctx, pkt, pOut)) != 0) { - DECODE_ERR_RET(ERROR_VPU_DECODE); - } else { - mpp_log("vpu decode one frame, out len: %d, left size: %d\n", - pOut->size, pkt->size); - - /* - ** both virtual and physical address of the decoded frame are contained - ** in structure named VPU_FRAME, if you want to use virtual address, make - ** sure you have done VPUMemLink before. - */ - if ((pOut->size) && (pOut->data)) { - frame = (VPU_FRAME *)(pOut->data); - VPUMemLink(&frame->vpumem); - wAlign16 = ((frame->DisplayWidth + 15) & (~15)); - hAlign16 = ((frame->DisplayHeight + 15) & (~15)); - frameSize = wAlign16 * hAlign16 * 3 / 2; - - if (pOutFile && (frame_count++ < cmd->record_frames)) { - mpp_log("write %d frame(yuv420sp) data, %d bytes to file\n", - frame_count, frameSize); - - fwrite((RK_U8*)(frame->vpumem.vir_addr), 1, frameSize, pOutFile); - fflush(pOutFile); - } - - /* - ** remember use VPUFreeLinear to free, other wise memory leak will - ** give you a surprise. - */ - VPUFreeLinear(&frame->vpumem); - pOut->size = 0; - } - } - - msleep(3); - } while (!(ctx->decoder_err)); - -DECODE_OUT: - if (pkt && pkt->data) { - free(pkt->data); - pkt->data = NULL; - } - if (pOut && (pOut->data)) { - free(pOut->data); - pOut->data = NULL; - } - if (pExtra) { - free(pExtra); - pExtra = NULL; - } - if (ctx) { - vpu_close_context(&ctx); - ctx = NULL; - } - if (pInFile) { - fclose(pInFile); - pInFile = NULL; - } - if (pOutFile) { - fclose(pOutFile); - pOutFile = NULL; - } - - if (ret) { - mpp_log("decode demo fail, err: %d\n", ret); - } else { - mpp_log("encode demo complete OK.\n"); - } - return ret; -} - - -int main(int argc, char **argv) -{ - RK_S32 ret = 0; - VpuApiDemoCmdContext_t demoCmdCtx; - VpuApiDemoCmdContext_t *cmd = NULL; - VPU_API_DEMO_DEBUG_DISABLE = 0; - - mpp_log("/******* vpu api demo in *******/\n"); - if (argc == 1) { - show_usage(); - mpp_log("vpu api demo complete directly\n"); - return 0; - } - - cmd = &demoCmdCtx; - memset (cmd, 0, sizeof(VpuApiDemoCmdContext_t)); - cmd->codec_type = CODEC_DECODER; - if ((ret = parse_options(argc, argv, cmd)) != 0) { - if (ret == VPU_DEMO_PARSE_HELP_OK) { - return 0; - } - - mpp_log("parse_options fail\n\n"); - show_usage(); - DEMO_ERR_RET(ERROR_INVALID_PARAM); - } - - if (cmd->disable_debug) { - VPU_API_DEMO_DEBUG_DISABLE = 1; - } - - switch (cmd->codec_type) { - case CODEC_DECODER: - ret = vpu_decode_demo(cmd); - break; - case CODEC_ENCODER: - ret = vpu_encode_demo(cmd); - break; - - default: - ret = ERROR_INVALID_PARAM; - break; - } - -DEMO_OUT: - if (ret) { - mpp_log("vpu api demo fail, err: %d\n", ret); - } else { - mpp_log("vpu api demo complete OK.\n"); - } - return ret; -} +/* + * Copyright 2015 Rockchip Electronics 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. + */ + +#define MODULE_TAG "vpu_api_demo" + +#include +#include +#include + +#include "mpp_log.h" +#include "mpp_time.h" + +#include "vpu_api.h" +#include "utils.h" + +#define FOR_TEST_ENCODE 1 + +static RK_U32 VPU_API_DEMO_DEBUG_DISABLE = 0; + +#define BSWAP32(x) \ + ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) + +#define DEMO_ERR_RET(err) do { ret = err; goto DEMO_OUT; } while (0) +#define DECODE_ERR_RET(err) do { ret = err; goto DECODE_OUT; } while (0) +#define ENCODE_ERR_RET(err) do { ret = err; goto ENCODE_OUT; } while (0) + + +typedef enum VPU_API_DEMO_RET { + VPU_DEMO_OK = 0, + VPU_DEMO_PARSE_HELP_OK = 1, + + VPU_DEMO_ERROR_BASE = -100, + ERROR_INVALID_PARAM = VPU_DEMO_ERROR_BASE - 1, + ERROR_INVALID_STREAM = VPU_DEMO_ERROR_BASE - 2, + ERROR_IO = VPU_DEMO_ERROR_BASE - 3, + ERROR_MEMORY = VPU_DEMO_ERROR_BASE - 4, + ERROR_INIT_VPU = VPU_DEMO_ERROR_BASE - 5, + + ERROR_VPU_DECODE = VPU_DEMO_ERROR_BASE - 90, +} VPU_API_DEMO_RET; + +typedef struct VpuApiDemoCmdContext { + RK_U32 width; + RK_U32 height; + CODEC_TYPE codec_type; + OMX_RK_VIDEO_CODINGTYPE coding; + char input_file[200]; + char output_file[200]; + RK_U8 have_input; + RK_U8 have_output; + RK_U8 disable_debug; + RK_U32 record_frames; + RK_S64 record_start_ms; +} VpuApiDemoCmdContext_t; + +typedef struct VpuApiEncInput { + EncInputStream_t stream; + RK_U32 capability; +} VpuApiEncInput; + +static OptionInfo vpuApiCmd[] = { + { "i", "input_file", "input bitstream file" }, + { "o", "output_file", "output bitstream file, " }, + { "w", "width", "the width of input bitstream" }, + { "h", "height", "the height of input bitstream" }, + { "t", "codec_type", "the codec type, dec: deoder, enc: encoder, default: decoder" }, + { "coding", "coding_type", "encoding type of the bitstream" }, + { "vframes", "number", "set the number of video frames to record" }, + { "ss", "time_off", "set the start time offset, use Ms as the unit." }, + { "d", "disable", "disable the debug output info." }, +}; + +static void show_usage() +{ + mpp_log("usage: vpu_apiDemo [options] input_file, \n\n"); + + mpp_log("Getting help:\n"); + mpp_log("-help --print options of vpu api demo\n"); +} + +static RK_S32 show_help() +{ + mpp_log("usage: vpu_apiDemo [options] input_file, \n\n"); + show_options(vpuApiCmd); + return 0; +} + +static RK_S32 parse_options(int argc, char **argv, VpuApiDemoCmdContext_t *cmdCxt) +{ + char *opt; + RK_S32 optindex, handleoptions = 1, ret = 0; + + if ((argc < 2) || (cmdCxt == NULL)) { + mpp_log("vpu api demo, input parameter invalid\n"); + show_usage(); + return ERROR_INVALID_PARAM; + } + + /* parse options */ + optindex = 1; + while (optindex < argc) { + opt = argv[optindex++]; + + if (handleoptions && opt[0] == '-' && opt[1] != '\0') { + if (opt[1] == '-') { + if (opt[2] != '\0') { + opt++; + } else { + handleoptions = 0; + continue; + } + } + + opt++; + + switch (*opt) { + case 'i': + if (argv[optindex]) { + memcpy(cmdCxt->input_file, argv[optindex], strlen(argv[optindex])); + cmdCxt->input_file[strlen(argv[optindex])] = '\0'; + cmdCxt->have_input = 1; + } else { + mpp_log("input file is invalid\n"); + ret = -1; + goto PARSE_OPINIONS_OUT; + } + break; + case 'o': + if (argv[optindex]) { + memcpy(cmdCxt->output_file, argv[optindex], strlen(argv[optindex])); + cmdCxt->output_file[strlen(argv[optindex])] = '\0'; + cmdCxt->have_output = 1; + break; + } else { + mpp_log("out file is invalid\n"); + ret = -1; + goto PARSE_OPINIONS_OUT; + } + case 'd': + cmdCxt->disable_debug = 1; + break; + case 'w': + if (argv[optindex]) { + cmdCxt->width = atoi(argv[optindex]); + break; + } else { + mpp_log("input width is invalid\n"); + ret = -1; + goto PARSE_OPINIONS_OUT; + } + case 'h': + if ((*(opt + 1) != '\0') && !strncmp(opt, "help", 4)) { + show_help(); + ret = VPU_DEMO_PARSE_HELP_OK; + goto PARSE_OPINIONS_OUT; + } else if (argv[optindex]) { + cmdCxt->height = atoi(argv[optindex]); + } else { + mpp_log("input height is invalid\n"); + ret = -1; + goto PARSE_OPINIONS_OUT; + } + break; + case 't': + if (argv[optindex]) { + cmdCxt->codec_type = atoi(argv[optindex]); + break; + } else { + mpp_log("input codec_type is invalid\n"); + ret = -1; + goto PARSE_OPINIONS_OUT; + } + + default: + if ((*(opt + 1) != '\0') && argv[optindex]) { + if (!strncmp(opt, "coding", 6)) { + mpp_log("coding, argv[optindex]: %s", + argv[optindex]); + cmdCxt->coding = atoi(argv[optindex]); + } else if (!strncmp(opt, "vframes", 7)) { + cmdCxt->record_frames = atoi(argv[optindex]); + } else if (!strncmp(opt, "ss", 2)) { + cmdCxt->record_start_ms = atoi(argv[optindex]); + } else { + ret = -1; + goto PARSE_OPINIONS_OUT; + } + } else { + ret = -1; + goto PARSE_OPINIONS_OUT; + } + break; + } + + optindex += ret; + } + } + +PARSE_OPINIONS_OUT: + if (ret < 0) { + mpp_log("vpu api demo, input parameter invalid\n"); + show_usage(); + return ERROR_INVALID_PARAM; + } + return ret; +} + +static RK_S32 readBytesFromFile(RK_U8 *buf, RK_S32 aBytes, FILE *file) +{ + RK_S32 ret = 0; + + if ((NULL == buf) || (NULL == file) || (0 == aBytes)) { + return -1; + } + + ret = (RK_S32)fread(buf, 1, aBytes, file); + if (ret != aBytes) { + mpp_log("read %d bytes from file fail\n", aBytes); + return -1; + } + + return 0; +} + +static RK_S32 vpu_encode_demo(VpuApiDemoCmdContext_t *cmd) +{ + FILE *pInFile = NULL; + FILE *pOutFile = NULL; + struct VpuCodecContext *ctx = NULL; + RK_S32 nal = 0x00000001; + RK_S32 fileSize, ret, size; + RK_U32 readOneFrameSize = 0; + EncoderOut_t enc_out_yuv; + EncoderOut_t *enc_out = NULL; + VpuApiEncInput enc_in_strm; + VpuApiEncInput *api_enc_in = &enc_in_strm; + EncInputStream_t *enc_in = NULL; + EncParameter_t *enc_param = NULL; + RK_S64 fakeTimeUs = 0; + RK_U32 w_align = 0; + RK_U32 h_align = 0; + + int Format = ENC_INPUT_YUV420_PLANAR; + + if (cmd == NULL) { + return -1; + } + + if ((cmd->have_input == 0) || (cmd->width <= 0) || (cmd->height <= 0) + || (cmd->coding <= OMX_RK_VIDEO_CodingAutoDetect)) { + mpp_log("Warning: missing needed parameters for vpu api demo\n"); + } + + if (cmd->have_input) { + mpp_log("input bitstream w: %d, h: %d, coding: %d(%s), path: %s\n", + cmd->width, cmd->height, cmd->coding, + cmd->codec_type == CODEC_DECODER ? "decode" : "encode", + cmd->input_file); + + pInFile = fopen(cmd->input_file, "rb"); + if (pInFile == NULL) { + mpp_log("input file not exsist\n"); + ENCODE_ERR_RET(ERROR_INVALID_PARAM); + } + } else { + mpp_log("please set input bitstream file\n"); + ENCODE_ERR_RET(ERROR_INVALID_PARAM); + } + + if (cmd->have_output) { + mpp_log("vpu api demo output file: %s\n", + cmd->output_file); + pOutFile = fopen(cmd->output_file, "wb"); + if (pOutFile == NULL) { + mpp_log("can not write output file\n"); + ENCODE_ERR_RET(ERROR_INVALID_PARAM); + } + } + +#ifdef FOR_TEST_ENCODE + ctx = (struct VpuCodecContext *)malloc(sizeof(struct VpuCodecContext)); + if (!ctx) { + mpp_err("Input context has not been properly allocated"); + return -1; + } + memset(ctx, 0, sizeof(struct VpuCodecContext)); + + ctx->videoCoding = OMX_RK_VIDEO_CodingAVC; + ctx->codecType = CODEC_ENCODER; + ctx->width = cmd->width; + ctx->height = cmd->height; +#endif + + fseek(pInFile, 0L, SEEK_END); + fileSize = ftell(pInFile); + fseek(pInFile, 0L, SEEK_SET); + + memset(&enc_in_strm, 0, sizeof(VpuApiEncInput)); + enc_in = &enc_in_strm.stream; + enc_in->buf = NULL; + + memset(&enc_out_yuv, 0, sizeof(EncoderOut_t)); + enc_out = &enc_out_yuv; + enc_out->data = (RK_U8 *)malloc(cmd->width * cmd->height); + if (enc_out->data == NULL) { + ENCODE_ERR_RET(ERROR_MEMORY); + } + + ret = vpu_open_context(&ctx); + if (ret || (ctx == NULL)) { + ENCODE_ERR_RET(ERROR_MEMORY); + } + + /* + ** now init vpu api context. codecType, codingType, width ,height + ** are all needed before init. + */ + ctx->codecType = cmd->codec_type; + ctx->videoCoding = cmd->coding; + ctx->width = cmd->width; + ctx->height = cmd->height; + ctx->no_thread = 1; + + ctx->private_data = malloc(sizeof(EncParameter_t)); + memset(ctx->private_data, 0, sizeof(EncParameter_t)); + + enc_param = (EncParameter_t *)ctx->private_data; + enc_param->width = cmd->width; + enc_param->height = cmd->height; + enc_param->format = ENC_INPUT_YUV420_PLANAR; + enc_param->rc_mode = 0; + enc_param->bitRate = 4000000; + enc_param->framerate = 25; + enc_param->enableCabac = 1; + enc_param->cabacInitIdc = 0; + enc_param->intraPicRate = 30; + enc_param->profileIdc = 66; + enc_param->levelIdc = 40; + + if ((ret = ctx->init(ctx, NULL, 0)) != 0) { + mpp_log("init vpu api context fail, ret: 0x%X\n", ret); + ENCODE_ERR_RET(ERROR_INIT_VPU); + } + + /* + ** init of VpuCodecContext while running encode, it returns + ** sps and pps of encoder output, you need to save sps and pps + ** after init. + */ + mpp_log("encode init ok, sps len: %d\n", ctx->extradata_size); + if (pOutFile && (ctx->extradata_size > 0)) { + mpp_log("dump %d bytes enc output stream to file\n", + ctx->extradata_size); + + /* save sps and pps */ + fwrite(ctx->extradata, 1, ctx->extradata_size, pOutFile); + fflush(pOutFile); + } + + ret = ctx->control(ctx, VPU_API_ENC_SETFORMAT, &Format); + if (ret) + mpp_err("VPU_API_ENC_SETFORMAT ret %d\n", ret); + + ret = ctx->control(ctx, VPU_API_ENC_GETCFG, enc_param); + if (ret) + mpp_log("VPU_API_ENC_GETCFG ret %d\n", ret); + + enc_param->rc_mode = 1; + + ret = ctx->control(ctx, VPU_API_ENC_SETCFG, enc_param); + if (ret) + mpp_log("VPU_API_ENC_SETCFG ret %d\n", ret); + + /* + ** vpu api encode process. + */ + mpp_log("init vpu api context ok, input yuv stream file size: %d\n", fileSize); + w_align = ((ctx->width + 15) & (~15)); + h_align = ((ctx->height + 15) & (~15)); + size = w_align * h_align * 3 / 2; + readOneFrameSize = ctx->width * ctx->height * 3 / 2; + mpp_log("%d %d %d %d %d", ctx->width, ctx->height, w_align, h_align, size); + nal = BSWAP32(nal); + + do { + if (ftell(pInFile) >= fileSize) { + mpp_log("read end of file, complete\n"); + break; + } + + if (enc_in && (enc_in->size == 0)) { + if (enc_in->buf == NULL) { + enc_in->buf = (RK_U8 *)(malloc)(size); + if (enc_in->buf == NULL) { + ENCODE_ERR_RET(ERROR_MEMORY); + } + api_enc_in->capability = size; + } + + if (api_enc_in->capability < ((RK_U32)size)) { + enc_in->buf = (RK_U8 *)(realloc)((void *)(enc_in->buf), size); + if (enc_in->buf == NULL) { + ENCODE_ERR_RET(ERROR_MEMORY); + } + api_enc_in->capability = size; + } + + if (readBytesFromFile(enc_in->buf, readOneFrameSize, pInFile)) { + break; + } else { + enc_in->size = size; + enc_in->timeUs = fakeTimeUs; + fakeTimeUs += 40000; + } + + mpp_log("read one frame, size: %d, timeUs: %lld, filePos: %ld\n", + enc_in->size, enc_in->timeUs, ftell(pInFile)); + } + + if ((ret = ctx->encode(ctx, enc_in, enc_out)) < 0) { + ENCODE_ERR_RET(ERROR_VPU_DECODE); + } else { + enc_in->size = 0; // TODO encode completely, and set enc_in->size to 0 + mpp_log("vpu encode one frame, out len: %d, left size: %d\n", + enc_out->size, enc_in->size); + + /* + ** encoder output stream is raw bitstream, you need to add nal + ** head by yourself. + */ + if ((enc_out->size) && (enc_out->data)) { + if (pOutFile) { + mpp_log("dump %d bytes enc output stream to file\n", + enc_out->size); + //fwrite((RK_U8*)&nal, 1, 4, pOutFile); // because output stream have start code, so here mask this code + fwrite(enc_out->data, 1, enc_out->size, pOutFile); + fflush(pOutFile); + } + + enc_out->size = 0; + } + } + + msleep(3); + } while (1); + +ENCODE_OUT: + if (enc_in && enc_in->buf) { + free(enc_in->buf); + enc_in->buf = NULL; + } + if (enc_out && (enc_out->data)) { + free(enc_out->data); + enc_out->data = NULL; + } + if (ctx) { + if (ctx->private_data) { + free(ctx->private_data); + ctx->private_data = NULL; + } + vpu_close_context(&ctx); + ctx = NULL; + } + if (pInFile) { + fclose(pInFile); + pInFile = NULL; + } + if (pOutFile) { + fclose(pOutFile); + pOutFile = NULL; + } + + if (ret) { + mpp_log("encode demo fail, err: %d\n", ret); + } else { + mpp_log("encode demo complete OK.\n"); + } + return ret; + +} + +static RK_S32 vpu_decode_demo(VpuApiDemoCmdContext_t *cmd) +{ + FILE *pInFile = NULL; + FILE *pOutFile = NULL; + struct VpuCodecContext *ctx = NULL; + RK_S32 fileSize = 0, pkt_size = 0; + RK_S32 ret = 0; + RK_U32 frame_count = 0; + DecoderOut_t decOut; + VideoPacket_t demoPkt; + VideoPacket_t *pkt = NULL; + DecoderOut_t *pOut = NULL; + VPU_FRAME *frame = NULL; + RK_S64 fakeTimeUs = 0; + RK_U8 *pExtra = NULL; + RK_U32 extraSize = 0; + RK_U32 wAlign16 = 0; + RK_U32 hAlign16 = 0; + RK_U32 frameSize = 0; + + if (cmd == NULL) { + return -1; + } + + if ((cmd->have_input == 0) || (cmd->width <= 0) || (cmd->height <= 0) + || (cmd->coding <= OMX_RK_VIDEO_CodingAutoDetect)) { + mpp_log("Warning: missing needed parameters for vpu api demo\n"); + } + + if (cmd->have_input) { + mpp_log("input bitstream w: %d, h: %d, coding: %d(%s), path: %s\n", + cmd->width, cmd->height, cmd->coding, + cmd->codec_type == CODEC_DECODER ? "decode" : "encode", + cmd->input_file); + + pInFile = fopen(cmd->input_file, "rb"); + if (pInFile == NULL) { + mpp_log("input file not exsist\n"); + DECODE_ERR_RET(ERROR_INVALID_PARAM); + } + } else { + mpp_log("please set input bitstream file\n"); + DECODE_ERR_RET(ERROR_INVALID_PARAM); + } + + if (cmd->have_output) { + mpp_log("vpu api demo output file: %s\n", + cmd->output_file); + pOutFile = fopen(cmd->output_file, "wb"); + if (pOutFile == NULL) { + mpp_log("can not write output file\n"); + DECODE_ERR_RET(ERROR_INVALID_PARAM); + } + if (cmd->record_frames == 0) + cmd->record_frames = 5; + } + + fseek(pInFile, 0L, SEEK_END); + fileSize = ftell(pInFile); + fseek(pInFile, 0L, SEEK_SET); + + memset(&demoPkt, 0, sizeof(VideoPacket_t)); + pkt = &demoPkt; + pkt->data = NULL; + pkt->pts = VPU_API_NOPTS_VALUE; + pkt->dts = VPU_API_NOPTS_VALUE; + + memset(&decOut, 0, sizeof(DecoderOut_t)); + pOut = &decOut; + + pOut->data = (RK_U8 *)(malloc)(sizeof(VPU_FRAME)); + if (pOut->data == NULL) { + DECODE_ERR_RET(ERROR_MEMORY); + } + memset(pOut->data, 0, sizeof(VPU_FRAME)); + + ret = vpu_open_context(&ctx); + if (ret || (ctx == NULL)) { + DECODE_ERR_RET(ERROR_MEMORY); + } + + /* + ** read codec extra data from input stream file. + */ + if (readBytesFromFile((RK_U8 *)(&extraSize), 4, pInFile)) { + DECODE_ERR_RET(ERROR_IO); + } + + mpp_log("codec extra data size: %d\n", extraSize); + + pExtra = (RK_U8 *)(malloc)(extraSize); + if (pExtra == NULL) { + DECODE_ERR_RET(ERROR_MEMORY); + } + memset(pExtra, 0, extraSize); + + if (readBytesFromFile(pExtra, extraSize, pInFile)) { + DECODE_ERR_RET(ERROR_IO); + } + + /* + ** now init vpu api context. codecType, codingType, width ,height + ** are all needed before init. + */ + ctx->codecType = cmd->codec_type; + ctx->videoCoding = cmd->coding; + ctx->width = cmd->width; + ctx->height = cmd->height; + ctx->no_thread = 1; + + if ((ret = ctx->init(ctx, pExtra, extraSize)) != 0) { + mpp_log("init vpu api context fail, ret: 0x%X\n", ret); + DECODE_ERR_RET(ERROR_INIT_VPU); + } + + /* + ** vpu api decoder process. + */ + mpp_log("init vpu api context ok, fileSize: %d\n", fileSize); + + do { + if (ftell(pInFile) >= fileSize) { + mpp_log("read end of file, complete\n"); + break; + } + + if (pkt && (pkt->size == 0)) { + if (readBytesFromFile((RK_U8 *)(&pkt_size), 4, pInFile)) { + break; + } + + if (pkt->data == NULL) { + pkt->data = (RK_U8 *)(malloc)(pkt_size); + if (pkt->data == NULL) { + DECODE_ERR_RET(ERROR_MEMORY); + } + pkt->capability = pkt_size; + } + + if (pkt->capability < ((RK_U32)pkt_size)) { + pkt->data = (RK_U8 *)(realloc)((void *)(pkt->data), pkt_size); + if (pkt->data == NULL) { + DECODE_ERR_RET(ERROR_MEMORY); + } + pkt->capability = pkt_size; + } + + if (readBytesFromFile(pkt->data, pkt_size, pInFile)) { + break; + } else { + pkt->size = pkt_size; + pkt->pts = fakeTimeUs; + fakeTimeUs += 40000; + } + + mpp_log("read one packet, size: %d, pts: %lld, filePos: %ld\n", + pkt->size, pkt->pts, ftell(pInFile)); + } + + /* note: must set out put size to 0 before do decoder. */ + pOut->size = 0; + + if (ctx->decode_sendstream(ctx, pkt) != 0) { + mpp_log("send packet failed"); + DECODE_ERR_RET(ERROR_VPU_DECODE); + } + + + if ((ret = ctx->decode_getframe(ctx, pOut)) != 0) { + mpp_log("get decoded data failed\n"); + DECODE_ERR_RET(ERROR_VPU_DECODE); + } else { + mpp_log("vpu decode one frame, out len: %d, left size: %d\n", + pOut->size, pkt->size); + + /* + ** both virtual and physical address of the decoded frame are contained + ** in structure named VPU_FRAME, if you want to use virtual address, make + ** sure you have done VPUMemLink before. + */ + if ((pOut->size) && (pOut->data)) { + frame = (VPU_FRAME *)(pOut->data); + VPUMemLink(&frame->vpumem); + wAlign16 = ((frame->DisplayWidth + 15) & (~15)); + hAlign16 = ((frame->DisplayHeight + 15) & (~15)); + frameSize = wAlign16 * hAlign16 * 3 / 2; + + if (pOutFile && (frame_count++ < cmd->record_frames)) { + mpp_log("write %d frame(yuv420sp) data, %d bytes to file\n", + frame_count, frameSize); + + fwrite((RK_U8 *)(frame->vpumem.vir_addr), 1, frameSize, pOutFile); + fflush(pOutFile); + } + + /* + ** remember use VPUFreeLinear to free, other wise memory leak will + ** give you a surprise. + */ + VPUFreeLinear(&frame->vpumem); + pOut->size = 0; + } + } + + msleep(3); + } while (!(ctx->decoder_err)); + +DECODE_OUT: + if (pkt && pkt->data) { + free(pkt->data); + pkt->data = NULL; + } + if (pOut && (pOut->data)) { + free(pOut->data); + pOut->data = NULL; + } + if (pExtra) { + free(pExtra); + pExtra = NULL; + } + if (ctx) { + vpu_close_context(&ctx); + ctx = NULL; + } + if (pInFile) { + fclose(pInFile); + pInFile = NULL; + } + if (pOutFile) { + fclose(pOutFile); + pOutFile = NULL; + } + + if (ret) { + mpp_log("decode demo fail, err: %d\n", ret); + } else { + mpp_log("encode demo complete OK.\n"); + } + return ret; +} + + +int main(int argc, char **argv) +{ + RK_S32 ret = 0; + VpuApiDemoCmdContext_t demoCmdCtx; + VpuApiDemoCmdContext_t *cmd = NULL; + VPU_API_DEMO_DEBUG_DISABLE = 0; + + mpp_log("/******* vpu api demo in *******/\n"); + if (argc == 1) { + show_usage(); + mpp_log("vpu api demo complete directly\n"); + return 0; + } + + cmd = &demoCmdCtx; + memset(cmd, 0, sizeof(VpuApiDemoCmdContext_t)); + cmd->codec_type = CODEC_DECODER; + if ((ret = parse_options(argc, argv, cmd)) != 0) { + if (ret == VPU_DEMO_PARSE_HELP_OK) { + return 0; + } + + mpp_log("parse_options fail\n\n"); + show_usage(); + DEMO_ERR_RET(ERROR_INVALID_PARAM); + } + + if (cmd->disable_debug) { + VPU_API_DEMO_DEBUG_DISABLE = 1; + } + + switch (cmd->codec_type) { + case CODEC_DECODER: + ret = vpu_decode_demo(cmd); + break; + case CODEC_ENCODER: + ret = vpu_encode_demo(cmd); + break; + + default: + ret = ERROR_INVALID_PARAM; + break; + } + +DEMO_OUT: + if (ret) { + mpp_log("vpu api demo fail, err: %d\n", ret); + } else { + mpp_log("vpu api demo complete OK.\n"); + } + return ret; +}