/*
 * Copyright(C) 2023 Reconova Information Technologies Co., Ltd.
 * All rights reserved.
 *
 * Hi3516DV500收流封装。
 *
 * Created by Cyberman Wu on Sep 28th, 2023.
 *
 */
#ifndef __RW_MPP_API_H__
#define __RW_MPP_API_H__

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdint.h>


///////////////////////////////////////////////////////////////////////////////
//                       编码相关的一些数据结构                              //
///////////////////////////////////////////////////////////////////////////////

#define NR__VENC_CHN                        4

typedef 
enum VENC_CODEC_type
{
    CODEC_H264  = 0, 
    CODEC_H265  = 1, 
    CODEC_MJPEG = 2
} VENC_CODEC_t;


typedef 
enum VENC_PROFILE
{
    H264_baseline   = 0, 
    H264_main       = 1, 
    H264_high       = 2, 
    H265_main       = 0, 
    MJPEG_baseline  = 0
} VENC_PROFILE_t;

typedef 
enum VENC_RC
{
    RC_FIXQP    = 0,
    RC_VBR      = 1, 
} VENC_RC_t;

/*
 * 最早我们只需要支持H.264/H.265编码，所以数据结构都是根据这两个定的，
 * 现在支持MJPEG之后我们还是用这个数据结构，不过图像质量只取IQp，并且
 * 取决范围为[1, 99]。
 *
 * ***注意：H.264/H.265的质量参数是值越小图像质量越好；而MJPEG则是值
 *          越大图像质量越好。
 */
typedef 
struct venc_rc_fixQp
{
    int IQp;                    /* RW; Range:[0, 51]; qp of the I frame (H.264/H.265) */
                                /* RW; Range:[1, 99]; image quality.    (MJPEG)       */
    int PQp;                    /* RW; Range:[0, 51]; qp of the P frame (H.264/H.265) */
    int BQp;                    /* RW; Range:[0, 51]; qp of the B frame (H.264/H.265) */
} S_venc_rc_fixQp;

/*
 * 最早我们只需要支持H.264/H.265编码，所以数据结构都是根据这两个定的，
 * 现在支持MJPEG之后我们还是用这个数据结构，不过图像质量只取minIQp/maxIQp，
 * 并且取决范围为[1, 99]。
 *
 * ***注意：H.264/H.265的质量参数是值越小图像质量越好；而MJPEG则是值
 *          越大图像质量越好。
 */
typedef 
struct venc_rc_vbr
{
    // max_bitrate单位是Kbps。
    int max_bitrate;            /* RW; Range:[2, (160 * 1024)]，编码器输出最大码率，单位是kbps。 */
    int stats_time;             /* RW; Range:[1, 60]，单位是秒。 */
    // 这些高级属性不再支持配置。
#if 0
    // 下面的参数在海思里面是单独配置的，可以不配置而使用系统
    // 缺省值（或者是根据码流以及图像大小、帧率自动计算的）。int src_img_spec, int dst_img_spec
    // 如果全部设置为-1表示使用缺省缺省值，不自己进行配置。
    int minIQp;                 /* RW; Range:[0, 51]; the min I qp                   (H.264/H.265) */
                                /* RW; Range:[1, 99]; min image quality allowed      (MJPEG)*/
    int maxIQp;                 /* RW; Range:(MinIQp, 51]; the max I qp              (H.264/H.265) */
                                /* RW; Range:[MinIQp, 99]; max image quailty allowed (MJPEG) */
    int minQp;                  /* RW; Range:[0, 51]; the min P B qp                 (H.264/H.265) */
    int maxQp;                  /* RW; Range:(MinQp, 51]; the max P B qp             (H.264/H.265) */
#endif
} S_venc_rc_vbr;


typedef 
struct venc_config
{
    /*
     * 编码器属性
     */
    // H.264 or H.265 or MJPEG
    VENC_CODEC_t codec;
    VENC_PROFILE_t profile;
    // 输入图像不严格要求，但限制它的最大值有助于节省存储，通常我们直接设置为进入
    // 图像的分辨率。
    int raw_max_width;
    int raw_max_height;
    // 编码输出的图像大小。
    int width;
    int height;

    /* Group of Picture
     * 严格来说H.264/H.265的GOP除了帧间隔之前还有其它一些属性，至少海思的是这样的，
     * 不过我们不留这些配置了，只配置关键帧的间隔。
     * 在海思平台上，这个值实际上是在每种RC类型的配置里面；而GOP则是配置了其它一些
     * 属性。
     */
    // 关键帧间隔。对MJPEG编码无意义。
    int gop;

    /*
     * 码率控制属性
     * rate control
     * 在海思的平台上，这一块相关的控制分在两个部分，分别在不同的接口中使用，输出
     * 帧率虽然每个都支持，但出现在每个类型的配置中；源帧率也可配置，但这个配置会
     * 导致一些奇怪的问题，所以我们自动内部使用发送给VENC的帧率。
     * 这两部分，有一部分还不是所有的类型有，用起来比较麻烦，我们统一到一个接口。
     */
    VENC_RC_t rc_type;
    // 编码输出的帧率。
    int framerate;
    // 这个是指向一个S_venc_rc_fixQp或者S_venc_rc_vbr的结构，根据前面的
    // rc_type来确定。如果这个指针为NULL，表示使用库里面的缺省配置。
    void *rc_param;
} S_venc_config;

/*
 * 编码帧
 */
typedef 
struct mpp_venc_frame
{
    uint8_t *data;
    uint32_t len;
    int is_key_frame;

    void *priv;
} S_mpp_venc_frame;

///////////////////////////////////////////////////////////////////////////////
//                       编码相关的一些数据结构                              //
///////////////////////////////////////////////////////////////////////////////



///////////////////////////////////////////////////////////////////////////////
//                       解码相关的一些数据结构                              //
///////////////////////////////////////////////////////////////////////////////
#define NR__VDEC_CHN                        4

/*
 * 两种发送模式。配置为FRAME模式，每次发送要求是一个完整的编码帧，而STREAM
 * 模式则直接一直发送即可，应用中可以不去解析帧结构。
 *
 * 注意： FRAME模式输出的解码图像帧的PTS保留了发送时的PTS，而STREAM模式固定
 *        是0，这个是海思的MPP导致的。不过目前我们解码基本上只用于验证算法，
 *        所以PTS影响不大。
 */
#define VDEC_SEND__FRAME                    0
#define VDEC_SEND__STREAM                   1

typedef 
struct vdec_config
{
    // H.264 or H.265 or MJPEG
    VENC_CODEC_t codec;
    // 编码图像的大小。
    int raw_max_width;
    int raw_max_height;
    // 输出图像大小。
    int width;
    int height;

    int send_mode;
} S_vdec_config;

typedef 
struct mpp_vdec_frame
{
    uint8_t *data;
    uint32_t len;

    // 如果输出一个非0值，发送接口就使用这里输入的值；如果输出0，则发送接口
    // 内部自动生成一个PTS（对于STREAM模式PTS无效）。
    uint64_t pts;
} S_mpp_vdec_frame;
///////////////////////////////////////////////////////////////////////////////
//                       解码相关的一些数据结构                              //
///////////////////////////////////////////////////////////////////////////////



///////////////////////////////////////////////////////////////////////////////
//                       JPEG相关的一些数据结构                              //
///////////////////////////////////////////////////////////////////////////////
/*
 * JPEG图像
 */
typedef 
struct jpeg_img
{
    uint8_t *data;
    int size;
} S_jpeg_img;
///////////////////////////////////////////////////////////////////////////////
//                       JPEG相关的一些数据结构                              //
///////////////////////////////////////////////////////////////////////////////



///////////////////////////////////////////////////////////////////////////////
//                       收流相关的一些数据结构                              //
///////////////////////////////////////////////////////////////////////////////
#define NR__VIDEO_CHN                       1

#define PIX_FMT__NV21                       0
#define PIX_FMT__GRAY                       1


/*
 * 海思平台上图像均采用NV12（YVU420SP），所以这里不设置图像格式参数。
 */
typedef 
struct mpp_img
{
    // 我们同时传物理地址，这样如果能直接使用物理地址就可以少一此处理，
    // 而不能使用物理地址就忽略它。
    uint64_t phy_addr;
    // 数据格式有可能是NV21或GRAY，具体由后面的pix_fmt来确定。对于灰度
    // 也确保了指向的空间大小是NV21，只是灰度数据后面是随机数据。
    // 这个实际上比较理想的是直接在收流部分把它封装掉，但因为这个项目
    // 追求极致性能，而有些算法能直接用灰度处理，所以这里只保留空间和
    // 给出图像格式，具体是否需要用memset()来处理后面VU部分由实际应用sample_vio_sys_init
    // 根据算法来确定。
    union
    {
        uint8_t *nv21;
        uint8_t *gray;
    };
    int width;
    int height;
    // 图像一行的实际长度，如果不等于width说明有padding。这个是图像处理中
    // 常用的手段。
    int linesize;
    int pix_fmt;

    // 这个或者增加接口通过priv来获取也可以？但使用起来不太方便。严格来说
    // 在图像信息中增加这个时间信息有些怪异，不过目前我们先这样用起来。
    uint64_t pts;
    void *priv;
} S_mpp_img;


typedef 
struct video_cfg
{
    // 这两个配置sensor的属性，配置为-1表示使用缺省值；如果有多种属性可以
    // 选择的话就配置为最相近的。
    // 具体内部的旋转等，我们具体的产品上做不同实现，目前在这个库中封装死。
    int sns_w;
    int sns_h;

    // 输出图像大小。
    int img_w;
    int img_h;
} S_video_cfg;
///////////////////////////////////////////////////////////////////////////////
//                       收流相关的一些数据结构                              //
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
//                       缩放相关的一些数据结构                              //
///////////////////////////////////////////////////////////////////////////////
#define NR__VSCALE_CHN                      4
typedef 
struct vscale_cfg
{
    int src_max_width;
    int src_max_height;
    int width;
    int height;
} S_vscale_cfg;
///////////////////////////////////////////////////////////////////////////////
//                       缩放相关的一些数据结构                              //
///////////////////////////////////////////////////////////////////////////////



/*
 * 初始化平台的MPP功能，分配内存。
 * 或者增加一个VB配置的参数，可以由外部来做一些控制？不过这部分目前还没有完成
 * 摸透，具体什么样的buffer参数还没太想明白。也可以考虑用做一个缺省初始化，
 * 后面同时又实现另一个带参数的初始化？
 *
 * 参数说明：
 *   无。
 *
 * 返回值：
 *   成功返回0，失败返回错误码。
 *
 */
int rw_mpp__init(void);

/*
 * 结束收流，释放相关的资源。
 *
 * 参数说明：
 *   无。()获取到的视频图像，
 *
 * 返回值：
 *   成功返回0，失败返回错误码。
 *
 */
int rw_mpp__finalize(void);


/*
 * 配置调试级别。0只打印错误信息，值越大测试信息越多。初始化之后
 * 缺省是0。
 *
 * 参数说明：
 *   level              - 调试级别。
 *
 * 返回值：
 *   成功返回0，失败返回错误码。
 *
 */
int rw_mpp__set_debug(int level);



/*
 * 打开一路视频。
 *
 * 参数说明：
 *   vid                - 视频编号。取值范围[0, NR__VIDEO_CHN]。
 *   vcfg               - 视频配置。
 *
 * 返回值：
 *   成功返回0，失败返回错误码。
 *
 */
int rw_mpp__video_start(int vid, S_video_cfg *vcfg);

/*
 * 关闭一路视频。
 *
 * 参数说明：
 *   vid                - 视频编号。取值范围[0, NR__VIDEO_CHN]。
 *
 * 返回值：
 *   成功返回0，失败返回错误码。
 *
 */
int rw_mpp__video_stop(int vid);

/*
 * 对于创建的不用回调的一种视频，主动获取一帧视频。这个获取的帧必须调用rw_mpp_free()
 * 释放之后才会再次调用获取下一帧，否则获取帧会直接返回失败。
 *
 * 参数说明：
 *   vid                - 视频编号。取值范围[0, NR__VIDEO_CHN]。
 *   ori_img            - [输出]原始图像。这个是直接旋转好的图像。
 *   tmout_ms           - 最大等待时间，单位是ms。
 *
 * 返回值：
 *   成功返回0，失败返回错误码。
 *
 */
int rw_mpp__video_recv(int vid, S_mpp_img *img, int tmout_ms);
/*
 * 释放rw_mpp_recv获取的帧，参数使用rw_mpp_recv()的输出。
 *
 * 参数说明：
 *   vid                - 视频编号。取值范围[0, NR__VIDEO_CHN]。
 *   img                - 视频图像。
 *
 * 返回值：
 *   成功返回0，失败返回错误码。
 *
 */
int rw_mpp__video_free(int vid, S_mpp_img *img);



/*
 * 打开一路视频图像缩放。注意目前只能缩小，不能放大。
 *
 * 参数说明：
 *   sid                - 缩放通道编号，取值范围[0, NR__SCALER_CHN)。
 *   cfg                - 缩放通道配置。
 *
 * 返回值：
 *   0表示成功，小于0表示失败。
 */
int rw_mpp__vscale_start(int sid, S_vscale_cfg *cfg);

/*
 * 打开一路视频图像缩放。
 *
 * 参数说明：
 *   sid                - 缩放通道编号，取值范围[0, NR__SCALER_CHN)。
 *
 * 返回值：
 *   0表示成功，小于0表示失败。
 */
int rw_mpp__vscale_stop(int sid);

/*
 * 执行图像缩放，获取缩放后的图像。
 *
 * 参数说明：
 *   sid                - 缩放通道编号，取值范围[0, NR__SCALER_CHN)。
 *   src_img            - 原始图像。
 *   scaled_img         - 缩放后的图像。会分配底层资源，需要用户及时释放。
 *
 * 返回值：
 *   0表示成功，小于0表示失败。
 */
int rw_mpp__vscale_exec(int sid, S_mpp_img *src_img, S_mpp_img *scaled_img);

/*
 * 执行图像缩放，获取缩放后的图像。
 *
 * 参数说明：
 *   sid                - 缩放通道编号，取值范围[0, NR__SCALER_CHN)。
 *   scaled_img         - 缩放后的图像。
 *
 *
 * 返回值：
 *   0表示成功，小于0表示失败。
 */
int rw_mpp__vscale_free(int sid, S_mpp_img *scaled_img);





/*
 * 打开一路视频编码。
 *
 * 参数说明：
 *   chn                - 编码通道，取值范围[0, NR__VENC_CHN)。
 *   cfg                - 编码配置。
 *   vid                - 是否绑定video，传-1的话需要每次调用rw_mpp_vend_send()
 *                        发送一帧编码一帧；而传入视频通道则自动绑定获取数据，
 *                        用户直接接收编码帧。
 *
 * 返回值：
 *   成功返回0，失败返回错误码。
 *
 */
int rw_mpp__venc_start(int chn, S_venc_config *cfg, int vid);


/*
 * 关闭一路视频编码。
 *
 * 参数说明：
 *   chn                - 编码通道，取值范围[0, NR__VENC_CHN)。
 *
 * 返回值：
 *   成功返回0，失败返回错误码。
 *
 */
int rw_mpp__venc_stop(int chn);

/*
 * 发送一帧数据做编码。如果编码通道绑定了vid发送会失败。这个没有封装为直接获取
 * 编码后的数据，这样更灵活一些，对于绑定了vid的视频编码，直接一直调用
 * rw_mpp_venc_recv()收帧即可。
 *
 * 参数说明：
 *   chn                - 编码通道，取值范围[0, 4)。
 *   img                - rw_mpp__video_recv()获取到的视频图像，只能修改图像内容，
 *                        不能修改其它值。
 */
int rw_mpp__venc_send(int chn, S_mpp_img *img);

/*
 * 接收一帧编码后的数据结果。编码没有设计是直接给原始帧编码出一帧来，分开有时候
 * 更灵活一些。

 * 参数说明：
 *   chn                - 编码通道，取值范围[0, 4)。
 *   frame              - 编码后的视频帧。对于H.264/H.265是NAL格式；对于MJPEG是
 *                        JPEG图像数据。
 */
int rw_mpp__venc_recv(int chn, S_mpp_venc_frame *frame, int tmout_ms);

/*
 * 释放rw_mpp__venc_recv()获取到的编码帧。
 */
int rw_mpp__venc_free(int chn, S_mpp_venc_frame *frame);



/*
 * 初始化编码器的整体配置。编码器不能使用公共VB，所以需要单独初始化一下。
 */
int rw_mpp__vdec_init(void);

/*
 * 反初始化编码器的整体配置。
 */
int rw_mpp__vdec_finalize(void);

/*
 * 打开一路视频解码。
 *
 * 参数说明：
 *   chn                - 解码通道，取值范围[0, NR__VDEC_CHN)。
 *   cfg                - 编码配置。
 *
 * 返回值：
 *   成功返回0，失败返回错误码。
 *
 */
int rw_mpp__vdec_start(int chn, S_vdec_config *cfg);


/*
 * 关闭一路视频解码。
 *
 * 参数说明：
 *   chn                - 解码通道，取值范围[0, NR__VDEC_CHN)。
 *
 * 返回值：
 *   成功返回0，失败返回错误码。
 *
 */
int rw_mpp__vdec_stop(int chn);

/*
 * 发送一帧数据做解码。
 *
 * 参数说明：
 *   chn                - 解码通道，取值范围[0, NR__VDEC_CHN)。
 *   frame              - 编码后的图像帧。
 */
int rw_mpp__vdec_send(int chn, S_mpp_vdec_frame *frame);

/*
 * 发送结束。因为解码一般是用文件，有可能多个文件顺序解码，或者同一个文件循环
 * 解码。为了避免前后数据混到一起用，所以发送完一个文件之后调用这个接口发送结
 * 束通知解码器。
 */
int rw_mpp__vdec_send_end(int chn);

/*
 * 接收一帧解码后的数据图像。

 * 参数说明：
 *   chn                - 解码通道，取值范围[0, NR__VDEC_CHN)。
 *   img                - 编码后的视频图像。
 */
int rw_mpp__vdec_recv(int chn, S_mpp_img *img, int tmout_ms);

/*
 * 释放rw_mpp__vdec_recv()获取到的解码。
 */
int rw_mpp__vdec_free(int chn, S_mpp_img *img);




/*
 * 初始化JPEG编码。
 *
 * 参数说明：
 *   max_width          - 图像最大宽度。
 *   max_height         - 图像最大高度。
 *
 * 返回值：
 *   0表示成功，小于0表示失败。
 */
int rw_mpp__jpeg_init(int max_width, int max_height);

/*
 * 关闭JPEG编码。
 *
 * 参数说明：
 *   无。
 *
 * 返回值：
 *   0表示成功，小于0表示失败。
 */
int rw_mpp__jpeg_finalize(void);

/*
 * 图像编码。
 *
 * 参数说明：
 *   img                - 原始图像，一般是Gray或NV21。这里可以是前面vscale或者video的
 *                        图像，也可以是用户自己构造的buffer。用户自己构造的buffer这
 *                        一结构中priv传NULL；物理地址如果是特殊申请的连续的空间并且
 *                        获取到了就填写，如果没有就直接填0。
 *   jpeg               - JPEG图像。成功后输入JPEG图像的信息，使用完之后需要释放。
 *
 * 返回值：
 *   0表示成功，小于0表示出错。
 */
int rw_mpp__jpeg_enc(S_mpp_img *img, int quality, S_jpeg_img *jpeg);

/*
 * 释放图像编码。
 *
 * 参数说明：
 *   jpeg               - 要释放的JPEG图像。
 *
 * 返回值：
 *   0表示成功，小于0表示出错。
 */
int rw_mpp__jpeg_free(S_jpeg_img *jpeg);


#ifdef __cplusplus
}
#endif

#endif  /* __RW_MPP_API_H__ */

