/*---------------------------------------------------------------------------*/
/* video_channel.h                                                           */
/* copyright (c) innovaphone 2017                                            */
/*---------------------------------------------------------------------------*/

class VideoChunk : public istd::listElement<VideoChunk> {
public:
    VideoChunk(char * buf, int len, dword timestamp, bool marker) {
        this->buf = buf;
        this->len = len;
        this->timestamp = timestamp;
        this->marker = marker;
    };
    ~VideoChunk() { free(this->buf); };

    char* buf;
    int len;
    dword timestamp;
    bool marker;
};

class VideoEndpoint : public IMediaEndpoint, public UTimer {

    char * videoFrame;
    bool dropFrame;
    bool rtpMarker;
    dword lTimestamp;
    int maxRtpVideoData;
    VideoCoder coder;
    bool isKeyFrame;
    int keyFrameLen;
    int gotKeyFrame;

    ulong64 firstTimestamp;
    ulong64 lastTimestamp;
    dword totalSize;
    int totalNumPacket;
    int totalNumLost;
    int totalNumOoO;
    int totalNumPacketLost;
    int totalNumPacketOoO;

    class ITimer * fullIntraFrameTimer;
    class ITimer * videoChunkTimer;

    istd::list<class VideoChunk> videoChunkList;
    int videoChunkListSize;
    int maxAppSharingData;

    char * sps;
    int spsLength;
    char * pps;
    int ppsLength;

    byte firCommandSequence;

    int vp8PicId;
    int av1PicId;
    bool firstObu;

    class UMediaEndpoint * channel;
    class IInstanceLog * const log;

    byte sequenceRx[2048];
    int indexSeq;
    int indexStart;
    int indexHigher;
    int packetsRx;
    class ITimer * oooTimer;

    int calcNalLength(char *data, int *bRead, int tLength, int *nalStart, int *idc, int *type);

    void SendH264(char * buf, int len, dword timestamp, enum MediaType mediaType);
    void SendVp8(char * buf, int len, dword timestamp, enum MediaType mediaType);
    void SendAV1(char* buf, int len, dword timestamp, enum MediaType mediaType);

    void RecvH264(char * buf, int len, dword timestamp, short sequenceNumberDiff, bool marker);
    void RecvVp8(char * buf, int len, dword timestamp, short sequenceNumberDiff, bool marker);
    void RecvAV1(char* buf, int len, dword timestamp, short sequenceNumberDiff, bool marker);

    void SendPacketLostIndication(void);
    void SendFullIntraRequest(void);
    void TimerOnTimeout(ITimer * timer);

public:
    VideoEndpoint(class IIoMux * const iomux, VideoCoder coder, class UMediaEndpoint * channel, class IInstanceLog * const log);
    ~VideoEndpoint();

    void Recv(char * buf, int len, dword timestamp, short sequenceNumberDiff, bool marker);
    void Send(char * buf, int len, dword timestamp, unsigned num, enum MediaType mediaTye, bool isConference);
};

typedef enum {
    FILE_TYPE_BOX = 0x0,
    FREE_SPACE_BOX,
    MOVIE_BOX,
    TRACK_BOX,
    MOVIE_HEADER_BOX,
    TRACK_HEADER_BOX,
    MEDIA_BOX,
    MEDIA_HEADER_BOX,
    HANDLER_REFERENCE_BOX,
    MEDIA_INFORMATION_BOX,
    VIDEO_MEDIA_HEADER_BOX,
    DATA_INFORMATION_BOX,
    DATA_REFERENCE_BOX,
    SAMPLE_TABLE_BOX,
    SAMPLE_DESCRIPTION_BOX,
    DECODING_SAMPLE_BOX,
    SAMPLE_CHUNK_BOX,
    SAMPLE_SIZE_BOX,
    CHUNK_OFFSET_BOX,
    AVC1_BOX,
    AVC_CODING_BOX,
    MOVIE_FRAGMENT_BOX,
    MOVIE_FRAGMENT_HEADER_BOX,
    TRACK_FRAGMENT_BOX,
    TRACK_FRAGMENT_HEADER_BOX,
    TRACK_FRAGMENT_DECODE_TIME_BOX,
    TRACK_FRAGMENT_RUN_BOX,
    MOVIE_DATA_BOX,
    MOVIE_EXTEND_BOX,
    TRACK_EXTEND_BOX,
    USER_DATA_BOX
} box_type_t;

typedef struct {
    const char * name;
    box_type_t type;
} ws_type_box_t;

static const ws_type_box_t boxSuffixes[] ={
    {"ftyp", FILE_TYPE_BOX},
    {"free", FREE_SPACE_BOX},
    {"moov", MOVIE_BOX},
    {"trak", TRACK_BOX},
    {"mvhd", MOVIE_HEADER_BOX},
    {"tkhd", TRACK_HEADER_BOX},
    {"mdia", MEDIA_BOX},
    {"mdhd", MEDIA_HEADER_BOX},
    {"hdlr", HANDLER_REFERENCE_BOX},
    {"minf", MEDIA_INFORMATION_BOX},
    {"vmhd", VIDEO_MEDIA_HEADER_BOX},
    {"dinf", DATA_INFORMATION_BOX},
    {"dref", DATA_REFERENCE_BOX},
    {"stbl", SAMPLE_TABLE_BOX},
    {"stsd", SAMPLE_DESCRIPTION_BOX},
    {"stts", DECODING_SAMPLE_BOX},
    {"stsc", SAMPLE_CHUNK_BOX},
    {"stsz", SAMPLE_SIZE_BOX},
    {"stco", CHUNK_OFFSET_BOX},
    {"avc1", AVC1_BOX},
    {"avcC", AVC_CODING_BOX},
    {"moof", MOVIE_FRAGMENT_BOX},
    {"mfhd", MOVIE_FRAGMENT_HEADER_BOX},
    {"traf", TRACK_FRAGMENT_BOX},
    {"tfhd", TRACK_FRAGMENT_HEADER_BOX},
    {"tfdt", TRACK_FRAGMENT_DECODE_TIME_BOX},
    {"trun", TRACK_FRAGMENT_RUN_BOX},
    {"mdat", MOVIE_DATA_BOX},
    {"mvex", MOVIE_EXTEND_BOX},
    {"trex", TRACK_EXTEND_BOX},
    {"udta", USER_DATA_BOX},
};

/* bit input */
/* buffer, buffer_end and size_in_bits must be present and used by every reader */
typedef struct GetBitContext {
    const byte *buffer, *buffer_end;
    int index;
    int size_in_bits;
} GetBitContext;

#define AV_RB32(x) (((dword)((const byte*)(x))[0] << 24) | (((const byte*)(x))[1] << 16) | (((const byte*)(x))[2] <<  8) | ((const byte*)(x))[3])

static const byte ff_ue_golomb_vlc_code[512] = {
    32, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
    7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14,
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

static const byte ff_log2_tab[256] = {
    0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
};

static const byte ff_golomb_vlc_len[512] = {
    19, 17, 15, 15, 13, 13, 13, 13, 11, 11, 11, 11, 11, 11, 11, 11, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};

static const char ff_se_golomb_vlc_code[512] = {
    17, 17, 17, 17, 17, 17, 17, 17, 16, 17, 17, 17, 17, 17, 17, 17, 8, -8, 9, -9, 10, -10, 11, -11, 12, -12, 13, -13, 14, -14, 15, -15,
    4, 4, 4, 4, -4, -4, -4, -4, 5, 5, 5, 5, -5, -5, -5, -5, 6, 6, 6, 6, -6, -6, -6, -6, 7, 7, 7, 7, -7, -7, -7, -7,
    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

class MP4Container: public IMediaContainer {
    class UMediaContainer * const user;
    class IInstanceLog * const log;

    int CreateMvhdBox(char * buf, ulong64 tTime);
    int CreateTkhdBox(char * buf, ulong64 tTime);
    int CreateMdhdBox(char * buf, ulong64 tTime);
    int CreateHdlrBox(char * buf);
    int CreateVmhdBox(char * buf);
    int CreateDrefBox(char * buf);
    int CreateAvc1Box(char * buf);
    int CreateStsdBox(char * buf);
    int CreateSttsBox(char * buf);
    int CreateStscBox(char * buf);
    int CreateStszBox(char * buf);
    int CreateStcoBox(char * buf);
    int CreateAvccBox(char * buf);
    int CreateTrexBox(char * buf);

    char * CreateFtypBox(int * olen);
    char * CreateMoovBox(int * olen);
    char * CreateMoofBox(int * olen, int slen, dword timestamp);
    char * CreateMdatBox(char * buf, int len, int * olen);
    int calcNalLength(char *data, int *bytesRead, int tLength, int *nalStart, int *idc, int *type);

    int DecodeRbspTrailing(const byte *nal);
    void DecodeSliceHeader(GetBitContext * gb);
    void DecodeSPS(GetBitContext * gb);

    GetBitContext gb;

    bool first;
    FILE * fp;
    FILE * fpH264;
    dword seq;

    int width;
    int height;

    char * sps;
    char * pps;
    int spsLength;
    int ppsLength;
    byte level;
    byte profile;
    dword fTimestamp;
    dword lTimestamp;
    ulong64 lTime;

    int trunBuf[2048];
    int trunCnt;

    char * rxSample;
    int rxSampleLen;
    int offDuration;
    dword sampleCompositionTimeOffset;
    int sliceType;

    int numSamples;
public:
    MP4Container(class UMediaContainer * user, class IInstanceLog * const log);
    ~MP4Container();

    void PutSample(char * buf, int len, dword timestamp) override;
    void Initialize(void) override;
};

class WebMContainer: public IMediaContainer {
    class UMediaContainer * const user;
    class IInstanceLog * const log;

    FILE * fp;
    dword fTimestamp;
    dword lTimestamp;
    ulong64 lTime;
    bool first;
    bool keyframe;
    dword keyframeTimeOffset;
    bool vp9;

    int width;
    int height;
    int horScale;
    int verScale;

    char * rxSample;
    int offDuration;
    int rxSampleLen;
    dword sampleCompositionTimeOffset;

    char * CreateWebMHeader(int * olen);
    char * CreateSegment(int * olen);
    char * CreateCluster(char * data, int len, dword timestamp, int * olen);
    int CreateMediaTracks(byte * buf);
    int CreateVideoTrack(byte * buf);

public:
    WebMContainer(class UMediaContainer * user, class IInstanceLog * const log, bool vp9);
    ~WebMContainer();

    void PutSample(char * buf, int len, dword timestamp) override;
    void Initialize(void) override;
};
