/*
 *******************************************************************************
 *  Copyright (c) 2010-2022 VATICS(KNERON) Inc. All rights reserved.
 *
 *  +-----------------------------------------------------------------+
 *  | THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED |
 *  | AND COPIED IN ACCORDANCE WITH THE TERMS AND CONDITIONS OF SUCH  |
 *  | A LICENSE AND WITH THE INCLUSION OF THE THIS COPY RIGHT NOTICE. |
 *  | THIS SOFTWARE OR ANY OTHER COPIES OF THIS SOFTWARE MAY NOT BE   |
 *  | PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY OTHER PERSON. THE   |
 *  | OWNERSHIP AND TITLE OF THIS SOFTWARE IS NOT TRANSFERRED.        |
 *  |                                                                 |
 *  | THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT   |
 *  | ANY PRIOR NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY |
 *  | VATICS(KNERON) INC.                                             |
 *  +-----------------------------------------------------------------+
 *
 *******************************************************************************
 */

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <getopt.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/resource.h>

#include <iniparser/iniparser.h>
#include <vmf/sync_shared_memory.h>
#include <vmf/ssm_info.h>
#include <vmf/video_display_mechanism.h>
#include <vmf_nnm_inference_app.h>
#include <vmf_nnm_fifoq_manager.h>
#include <linux/videodev2.h>
#include "application_init.h"
#include "kdp2_host_stream.h"
#include "fec_api.h"
#include "event_recorder.h"
#include "bt_uart.h"

//fifo queue buffer setting
#define IMAGE_BUFFER_COUNT      3

#define RESULT_BUFFER_COUNT     3
#define RESULT_BUFFER_SIZE      512 * 1024


#define HOST_STREAM_CONFIG_PATH "./ini/host_stream.ini"

extern void *kdp2_host_stream_image_thread(void *arg);
extern void *kdp2_host_update_result_thread(void *arg);
extern void *kdp2_host_video_thread(void *arg);
extern void *kdp2_host_roi_image_thread(void *arg);
extern void *kdp2_host_voc_thread(void *phandle);

extern int app_header_send_inference(uint32_t buf_addr, bool *bl_run_next_inference, void* arg, unsigned char* image_buffer, VMF_VSRC_SSM_OUTPUT_INFO_T* vsrc_ssm_info);
extern int app_header_recv_inference(uint32_t buf_addr, bool *bl_run_next_inference);

bool _blDispatchRunning = true;         //VMF_NNM_Fifoq_Manager_Enqueue_Image_Thread: This thread control argument.
                                        //true: keep running, false: this operation should only be invoked before terminating the process.
bool _blFifoqManagerRunning = true;     //VMF_NNM_Inference_Image_Dispatcher_Thread: This thread control argument.
                                        //true: keep running, false: this operation should only be invoked before terminating the process.
extern bool _blRecvRunning;
extern bool _blResultRunning;
extern ssm_handle_t  	*gptSsmHandle;
extern int g_iTerminate;
unsigned int g_dwVocEnable = 0;
unsigned int g_dwVocPixFmt = 0;
unsigned int g_dwOnlyPerson = 0;
unsigned int g_dwDrawBoxEnable = 0;
unsigned int g_verbose_log = 0;    /* 0=quiet, 1=per-frame class ratio log (web UI toggle) */

bool _blMainStanby = true;
DETECT_INFO g_atDrawInfo[MAX_RESULT_BOX];
unsigned int g_dwResultCounts = 0;

/* STDC segmentation map shared with draw-box thread */
uint8_t         g_stdc_seg_map[STDC_SEG_MAP_MAX];
uint32_t        g_stdc_seg_w = 0;
uint32_t        g_stdc_seg_h = 0;
pthread_mutex_t g_stdc_seg_mutex = PTHREAD_MUTEX_INITIALIZER;

int loadConfig(HOST_STREAM_INIT_OPT_T* pHostStreamInit)
{
	dictionary* ini = NULL;
	const char* tmp = NULL;
	struct stat info;
    char search_str[30] = {0};

	//! check file
	if (0 != stat(HOST_STREAM_CONFIG_PATH, &info))
		return -1;

	if (!(info.st_mode & S_IFREG))
		return -1;

	printf("iniparser_load %s \n", HOST_STREAM_CONFIG_PATH);
	ini = iniparser_load(HOST_STREAM_CONFIG_PATH);

	tmp = iniparser_getstring(ini, "sensor:sensor_cfg", NULL);
	if(tmp)
		pHostStreamInit->pszSensorConfigPath = strdup(tmp);

	tmp = iniparser_getstring(ini, "sensor:autoscene_config", NULL);
	if(tmp /*&& (app_mode != VMF_VSRC_APP_MODE_DMA422TO420)*/)	// Disable autoscene when app_mode == VMF_VSRC_APP_MODE_DMA422TO420
		pHostStreamInit->pszAutoSceneConfigPath = strdup(tmp);

	tmp = iniparser_getstring(ini, "sensor:fusion_cfg", NULL);
	if(tmp)
		pHostStreamInit->pszFusionConfigPath = strdup(tmp);

	tmp = iniparser_getstring(ini, "sensor:fec_calibrate_path", NULL);
	if(tmp)
		pHostStreamInit->pszFecCalibratePath = strdup(tmp);

	tmp = iniparser_getstring(ini, "sensor:fec_conf_path", NULL);
	if(tmp)
		pHostStreamInit->pszFecConfPath = strdup(tmp);

	pHostStreamInit->dwFecMode = iniparser_getint(ini, "sensor:fec_mode", 0);
	pHostStreamInit->dwFecAppType = iniparser_getint(ini, "sensor:initial_fec_app_type", 0);
	pHostStreamInit->dwEisEnable = iniparser_getint(ini, "sensor:eis_enable", 0);

	printf("[NNM] sensor_cfg: %s autoscene_config: %s \n", pHostStreamInit->pszSensorConfigPath, pHostStreamInit->pszAutoSceneConfigPath);
    if(pHostStreamInit->pszFusionConfigPath){
    	printf("[NNM] fusion_cfg: %s\n", pHostStreamInit->pszFusionConfigPath);
    }
    printf("[NNM] fec_calibrate: %s fec_conf: %s \n", pHostStreamInit->pszFecCalibratePath, pHostStreamInit->pszFecConfPath);

	pHostStreamInit->pszModelPath = strdup(iniparser_getstring(ini, "nnm:ModelPath", "nef/STDC_0520.nef"));
    pHostStreamInit->dwInferenceStream = iniparser_getint(ini, "nnm:InferenceStream", 1);
	pHostStreamInit->fThreshold = (float)iniparser_getdouble(ini, "nnm:Threshold", 0.5);
	pHostStreamInit->dwModelId = iniparser_getint(ini, "nnm:ModelId", 32769);
	pHostStreamInit->dwJobId = iniparser_getint(ini, "nnm:JobId", 200);
	pHostStreamInit->fFps = (float)iniparser_getdouble(ini, "nnm:Fps", 25);
    if (pHostStreamInit->fFps == 0) {
        pHostStreamInit->fFps = 1;
        printf("[Fps = 0], set a default fps %f.\n", pHostStreamInit->fFps);
    }
    pHostStreamInit->dwGetImageBufMode = iniparser_getint(ini, "nnm:GetImageBufMode", 0);
    pHostStreamInit->bRoiEnable = iniparser_getint(ini, "nnm:RoiEnable", 0);
    pHostStreamInit->dwRoiX = iniparser_getint(ini, "nnm:RoiX", 0);
    pHostStreamInit->dwRoiY = iniparser_getint(ini, "nnm:RoiY", 0);
    pHostStreamInit->bDrawBoxEnable = iniparser_getint(ini, "nnm:DrawBoxEnable", 0);
	pHostStreamInit->dwOnlyPerson = iniparser_getint(ini, "nnm:OnlyPerson", 0);
    g_dwOnlyPerson = pHostStreamInit->dwOnlyPerson;
    g_verbose_log  = (unsigned int)iniparser_getint(ini, "nnm:verbose_log", 0);
    pHostStreamInit->dwDrawOnResize = iniparser_getint(ini, "nnm:DrawOnResize", 0);
    if (pHostStreamInit->bDrawBoxEnable && (pHostStreamInit->dwInferenceStream == 0) && (pHostStreamInit->dwDrawOnResize == 0)) {
        pHostStreamInit->dwDrawOnResize = 1;
        printf("[Setting Error] Force enabled DrawOnResize \n");
    }

    if ((pHostStreamInit->dwDrawOnResize == 1) && (pHostStreamInit->dwInferenceStream != 0)) {
        pHostStreamInit->dwDrawOnResize = 0;
        printf("[Setting Error] Force disable DrawOnResize \n");
    }

    pHostStreamInit->dwNnmSource= iniparser_getint(ini, "nnm:NnmSource", 0);
    pHostStreamInit->pszSsmName= strdup(iniparser_getstring(ini, "nnm:ssm_name", "vsrc_ssm_0"));

    pHostStreamInit->dwEncodeStreamCount = iniparser_getint(ini, "streamer:StreamCount", 3);
    pHostStreamInit->eMemType = iniparser_getint(ini, "streamer:MemType", 0);
    pHostStreamInit->dwEncodeStreamCount = ( pHostStreamInit->dwEncodeStreamCount > MAX_STREAM )?MAX_STREAM:pHostStreamInit->dwEncodeStreamCount;
    for (unsigned int i=0; i < MAX_STREAM; i++) {
        snprintf(search_str, 30, "stream%u:Codec", i);
        pHostStreamInit->tVEncoder[i].eCodecType = iniparser_getint(ini, search_str, 1);
        snprintf(search_str, 30, "stream%u:QP", i);
        pHostStreamInit->tVEncoder[i].dwQp = iniparser_getint(ini, search_str, 25);
        snprintf(search_str, 30, "stream%u:FPS", i);
        pHostStreamInit->tVEncoder[i].dwFps = iniparser_getint(ini, search_str, 25);
        snprintf(search_str, 30, "stream%u:Bitrate", i);
        pHostStreamInit->tVEncoder[i].dwBitrate = iniparser_getint(ini, search_str, 2000000);
        snprintf(search_str, 30, "stream%u:Virt_I_Interval", i);
        pHostStreamInit->tVEncoder[i].dwVirtIFrameInterval = iniparser_getint(ini, search_str, 0);
        snprintf(search_str, 30, "stream%u:GOP", i);
        pHostStreamInit->tVEncoder[i].dwGop = iniparser_getint(ini, search_str, 50);
        snprintf(search_str, 30, "stream%u:PIQ", i);
        pHostStreamInit->tVEncoder[i].dwPiq = iniparser_getint(ini, search_str, 0);
        snprintf(search_str, 30, "stream%u:Width", i);
        pHostStreamInit->tVEncoder[i].dwWidth = iniparser_getint(ini, search_str, 2560);
        snprintf(search_str, 30, "stream%u:Height", i);
        pHostStreamInit->tVEncoder[i].dwHeight = iniparser_getint(ini, search_str, 1920);
        snprintf(search_str, 30, "stream%u:KeepFrameRatio", i);
        pHostStreamInit->tVEncoder[i].dwKeepRatio = iniparser_getint(ini, search_str, 0);
        snprintf(search_str, 30, "stream%u:EncodeBufferSize", i);
        pHostStreamInit->tVEncoder[i].dwEncodeBufferSize = iniparser_getint(ini, search_str, 6291456);
        snprintf(search_str, 30, "stream%u:EncodeBufferAmount", i);
        pHostStreamInit->tVEncoder[i].dwEncodeBufferAmount = iniparser_getint(ini, search_str, 3);
    }

	pHostStreamInit->dwVocEnable = iniparser_getint(ini, "voc:voc_enable", 0);
    g_dwVocEnable = pHostStreamInit->dwVocEnable;
	pHostStreamInit->dwVocWidth = VMF_32_ALIGN(iniparser_getint(ini, "voc:VocWidth", -1));
    pHostStreamInit->dwVocHeight = VMF_8_ALIGN(iniparser_getint(ini, "voc:VocHeight", -1));

    /* ISP pipeline always outputs YM12 (planar YUV420, three separate planes).
     * The VOC (HDMI) display input must match. Hardcode YM12 to match SDK reference. */
    (void)iniparser_getstring(ini, "voc:PixFmt", "YM12");
    g_dwVocPixFmt = v4l2_fourcc('Y', 'M', '1', '2');
    printf("[VOC] PixFmt: YM12 (0x%08X)\n", g_dwVocPixFmt);

	if(pHostStreamInit->dwEisEnable == 1) {
        FILE *fDeviceBufferEnable = NULL;
		if (pHostStreamInit->dwFecMode != FEC_MODE_1O && pHostStreamInit->dwFecMode != FEC_MODE_1R ) {
			printf("EIS mode does NOT support mode %u [Disable EIS]\n", pHostStreamInit->dwFecMode);
			pHostStreamInit->dwEisEnable = 0;
		}
		fDeviceBufferEnable = fopen(IMU_CONFIG_PATH,"r");
		if(!fDeviceBufferEnable) {
			printf("[streamer] Cannot read %s in EIS mode. [Disable EIS]\n", IMU_CONFIG_PATH);
			pHostStreamInit->dwEisEnable = 0;
		}
        if (fDeviceBufferEnable)
            fclose(fDeviceBufferEnable);
	}

	printf("[NNM] Model: %s ImageWidth: %d ImageHeight: %d Threshold: %f Fps: %f \n", pHostStreamInit->pszModelPath, pHostStreamInit->tVEncoder[pHostStreamInit->dwInferenceStream].dwWidth, pHostStreamInit->tVEncoder[pHostStreamInit->dwInferenceStream].dwHeight, pHostStreamInit->fThreshold, pHostStreamInit->fFps);
	printf("[NNM] Model: %s dwModelId: %d dwJobId: %d \n", pHostStreamInit->pszModelPath, pHostStreamInit->dwModelId, pHostStreamInit->dwJobId);
    /* --- [event] section: violation event recording + upload --- */
    {
        int ev_enable       = iniparser_getint(ini,    "event:enable",           0);
        const char *bt_dev  = iniparser_getstring(ini, "event:bt_uart_dev",      "/dev/ttyS1");
        int bt_at_probe     = iniparser_getint(ini,    "event:bt_at_probe",      0);
        const char *ev_up   = iniparser_getstring(ini, "event:upload_url",       "http://192.168.0.114:8081/api/upload");
        const char *ev_sd   = iniparser_getstring(ini, "event:sd_path",          "/tmp/sdcard/events");
        int ev_max_mb       = iniparser_getint(ini,    "event:sd_max_mb",        7168);
        int ev_delay        = iniparser_getint(ini,    "event:upload_delay_ms",  60000);
        bt_uart_init(bt_dev, bt_at_probe);
        event_recorder_init(ev_up, ev_sd, ev_max_mb, ev_delay, ev_enable);
    }

    iniparser_freedict(ini);
	return 0;
}

void sig_kill(int signo)
{
    printf("receive SIGNAL: %d\n", signo);

    if(gptSsmHandle)//get image sensor yuv data on HICO/HOST mode
        SSM_Reader_Wakeup(gptSsmHandle);

    _blMainStanby = false;
    _blDispatchRunning = false;
    _blRecvRunning = false;     //kdp2_host_stream_image_thread
    _blResultRunning = false;   //kdp2_host_update_result_thread
    g_iTerminate = 1;

    _blFifoqManagerRunning = false;

    VMF_NNM_Fifoq_Manager_Wakeup();

    if (SIGSEGV == signo) {
    #ifdef DEBUG
        struct rlimit rlim, rlim_new;
        printf("Dump stack start...ulimit -c unlimited\n");
        if (getrlimit(RLIMIT_CORE, &rlim) == 0) {
            rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY;
            if (setrlimit(RLIMIT_CORE, &rlim_new) != 0) {
                rlim_new.rlim_cur = rlim_new.rlim_max = rlim.rlim_max;
                (void) setrlimit(RLIMIT_CORE, &rlim_new);
            }
        }
        system("echo /tmp/core.%e.%p.%t > /proc/sys/kernel/core_pattern");
	    printf("Dump stack end...\n");
    #endif

	    fflush(stdout);
	    signal(SIGSEGV, SIG_DFL);
	    raise(SIGSEGV);
    }
    printf("sig_kill end\n");
}

void print_usage(char* argv[])
{
    printf("Usage 1, setting by ini: %s, default auto load [%s] \r\n", argv[0], HOST_STREAM_CONFIG_PATH);
}

int parse_argument(int argc, char* argv[], HOST_STREAM_INIT_OPT_T* pHostStreamInit) {
    int ch;

    while ((ch = getopt(argc, argv, "c:a:m:i:j:w:h:t:f:")) != -1)
    {
        switch(ch)
        {
            case 'c':
                pHostStreamInit->pszSensorConfigPath = strdup(optarg);
                printf("SensorConfigPath = %s\n", pHostStreamInit->pszSensorConfigPath);
                break;

            case 'a':
                pHostStreamInit->pszAutoSceneConfigPath = strdup(optarg);
                printf("AutoSceneConfigPath = %s\n", pHostStreamInit->pszAutoSceneConfigPath);
                break;

            case 'm':
                pHostStreamInit->pszModelPath = strdup(optarg);
                printf("ModelPath = %s\n", pHostStreamInit->pszModelPath);
                break;

            case 'i':
                pHostStreamInit->dwModelId = atoi(optarg);
                break;

            case 'j':
                pHostStreamInit->dwJobId = atoi(optarg);
                break;

            case 't':
				pHostStreamInit->fThreshold = atof(optarg);
				break;

            case 'f':
                pHostStreamInit->fFps = atof(optarg);
                break;

            default:
                print_usage(argv);
                return -1;
        }
    }

    if (NULL == pHostStreamInit->pszModelPath ) {
        print_usage(argv);

        return -1;
    }

    return 0;
}

void free_stream_init(HOST_STREAM_INIT_OPT_T* pHostStreamInit) {
    if (pHostStreamInit->pszSensorConfigPath){
        free(pHostStreamInit->pszSensorConfigPath);
        pHostStreamInit->pszSensorConfigPath = NULL;
    }

    if (pHostStreamInit->pszFusionConfigPath){
        free(pHostStreamInit->pszFusionConfigPath);
        pHostStreamInit->pszFusionConfigPath = NULL;
    }

    if (pHostStreamInit->pszAutoSceneConfigPath){
        free(pHostStreamInit->pszAutoSceneConfigPath);
        pHostStreamInit->pszAutoSceneConfigPath = NULL;
    }

    if (pHostStreamInit->pszFecCalibratePath){
        free(pHostStreamInit->pszFecCalibratePath);
        pHostStreamInit->pszFecCalibratePath = NULL;
    }

    if (pHostStreamInit->pszFecConfPath){
        free(pHostStreamInit->pszFecConfPath);
        pHostStreamInit->pszFecConfPath = NULL;
    }

    if (pHostStreamInit->pszModelPath){
        free(pHostStreamInit->pszModelPath);
        pHostStreamInit->pszModelPath = NULL;
    }

    if (pHostStreamInit->pszSsmName){
        free(pHostStreamInit->pszSsmName);
        pHostStreamInit->pszSsmName = NULL;
    }
}

int main (int argc, char* argv[])
{
    uint32_t major, minor, patch, build;
	pthread_t thread_f = 0;

    VMF_NNM_Get_Version(&major, &minor, &patch, &build);

    printf("\n\n**********************************************************\n");
    printf("Kneron Firmware\n");
    printf("Ver. %d.%d.%d.%d\n", major, minor, patch, build);
    printf("Build Time: %s %s\n", __DATE__, __TIME__);
    printf("**********************************************************\n");
    printf("HOST STREAM mode \n");

    //pthread_t task_infcb_handle;  //kmdw_inference_result_handler_callback_thread;
    pthread_t task_inf_data_handle; //VMF_NNM_Inference_Image_Dispatcher_Thread;
    pthread_t task_stream_image_handle; // <-> kdp2_host_stream_image_thread;        //pthread_t task_usb_recv_handle; // <-> kdp2_usb_companion_image_thread;
    pthread_t task_update_result_handle; // <-> kdp2_host_update_result_thread;  //pthread_t task_usb_send_handle; // <-> kdp2_usb_companion_result_thread;

    pthread_t task_buf_mgr_handle;
    pthread_t task_video_mgr_handle;
    pthread_t task_roi_image_handle;

    int ret = KP_SUCCESS;
    HOST_STREAM_INIT_OPT_T HostStreamInit; //for host stream mode/thread
    uint32_t ImageBufferSize = 0;

    memset(&HostStreamInit, 0, sizeof(HOST_STREAM_INIT_OPT_T));
	pthread_mutex_init(&HostStreamInit.tSuspendpMutex, NULL);
	pthread_cond_init(&HostStreamInit.tSuspendCond, NULL);

    // Always load config from ini to get encoder settings
    if(0 != loadConfig(&HostStreamInit)) {
        goto EXIT;
    }
    
    // Command-line arguments override ini settings
    if(argc > 1) {
        if(parse_argument(argc, argv, &HostStreamInit) < 0) {
            goto EXIT;
        }
    }
    ImageBufferSize = HostStreamInit.tVEncoder[HostStreamInit.dwInferenceStream].dwWidth * HostStreamInit.tVEncoder[HostStreamInit.dwInferenceStream].dwHeight * 2;

    //! register signal
    signal(SIGTERM, sig_kill);
    signal(SIGKILL, sig_kill);
    signal(SIGINT, sig_kill);

    //SIGSEGV
    struct sigaction sa;
    memset(&sa, 0, sizeof(struct sigaction));
    sigemptyset(&sa.sa_mask);
    sa.sa_handler  = sig_kill;
    sa.sa_flags = SA_SIGINFO|SA_RESETHAND;  // Reset signal handler to system default after signal triggered
    sigaction(SIGSEGV, &sa, NULL);

    printf("[%s] app_initialize \n", __func__);
    app_initialize();
    HostStreamInit.ptDmaInfo = dma2d_init();
    if(HostStreamInit.ptDmaInfo == NULL){
        goto EXIT;
    }

    printf("[DBG] Before VMF_NNM_Load_Model_From_File: %s\n", HostStreamInit.pszModelPath);
    VMF_NNM_Load_Model_From_File(HostStreamInit.pszModelPath);
    printf("[DBG] After VMF_NNM_Load_Model_From_File\n");

    VMF_NNM_Fifoq_Manager_Allocate_Buffer(IMAGE_BUFFER_COUNT, ImageBufferSize, RESULT_BUFFER_COUNT, RESULT_BUFFER_SIZE);
    printf("[DBG] After AllocateBuffer, ImageBufferSize=%u\n", ImageBufferSize);

    pthread_create(&task_stream_image_handle, NULL, kdp2_host_stream_image_thread, &HostStreamInit);
    pthread_create(&task_update_result_handle, NULL, kdp2_host_update_result_thread, &HostStreamInit);
    if(!HostStreamInit.dwNnmSource && HostStreamInit.dwEncodeStreamCount > 0)
        pthread_create(&task_video_mgr_handle, NULL, kdp2_host_video_thread, &HostStreamInit);
    if (HostStreamInit.bRoiEnable)
        pthread_create(&task_roi_image_handle, NULL, kdp2_host_roi_image_thread, &HostStreamInit);

    pthread_create(&task_buf_mgr_handle, NULL, VMF_NNM_Fifoq_Manager_Enqueue_Image_Thread, &_blFifoqManagerRunning);
    pthread_create(&task_inf_data_handle, NULL, VMF_NNM_Inference_Image_Dispatcher_Thread, &_blDispatchRunning);
    if(HostStreamInit.dwVocEnable)
    	pthread_create(&thread_f, NULL, kdp2_host_voc_thread, &HostStreamInit);

    pthread_join(task_stream_image_handle, NULL);
    pthread_join(task_update_result_handle, NULL);
    if(!HostStreamInit.dwNnmSource && HostStreamInit.dwEncodeStreamCount > 0)
        pthread_join(task_video_mgr_handle, NULL);
    if (HostStreamInit.bRoiEnable)
        pthread_join(task_roi_image_handle, NULL);

    pthread_join(task_buf_mgr_handle, NULL);
    pthread_join(task_inf_data_handle, NULL);

    app_destroy();  //VMF_NNM_Inference_App_Destroy();
    VMF_NNM_Fifoq_Manager_Release_All_Buffer();
    ret = 0;

EXIT:
    printf("%s free\n", __func__);

    free_fec_def_str();
    free_stream_init(&HostStreamInit);
    if(HostStreamInit.ptDmaInfo)
        dma2d_release(HostStreamInit.ptDmaInfo);

	pthread_mutex_destroy(&HostStreamInit.tSuspendpMutex);
	pthread_cond_destroy(&HostStreamInit.tSuspendCond);

    printf("%s end\n", __func__);
    return ret;
}
