/*
 * FSV demuxer
 * Copyright (c) 2011 William Kendi Nishio
 * Dedicated to you, our future pioneers...
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "libavutil/intreadwrite.h"
#include "avformat.h"
#include "riff.h"
#include "libavcodec/get_bits.h"
#include "mpegts.h"
#include <unistd.h>
#include <strings.h>
#include "rtpdec_formats.h"
#include "libavcodec/avcodec.h"
#include "rtpdec.h"

//FreeSWITCH Video MAGIC and/or VERSION
#define FSV_VERSION 4201

//FreeSWITCH VID_BIT
const unsigned int FSV_VID_BIT = (1 << 31);

#define RTP_SEQ_MOD (1<<16)

typedef struct PayloadContext
{
    int sizelength;
    int indexlength;
    int indexdeltalength;
    int profile_level_id;
    int streamtype;
    int objecttype;
    char *mode;

    //mpeg 4 AU headers
    struct AUHeaders {
        int size;
        int index;
        int cts_flag;
        int cts;
        int dts_flag;
        int dts;
        int rap_flag;
        int streamstate;
    } *au_headers;
    int au_headers_allocated;
    int nb_au_headers;
    int au_headers_length_bytes;
    int cur_au_index;
} ;

typedef struct {
    int32_t version;
    char video_codec_name[32];
    char video_fmtp[128];
    uint32_t audio_rate;
    uint32_t audio_ptime;
    int64_t created;
} FSVFileHeader;

typedef struct {
    RTPDemuxContext *rtp_video_dmx_ctx;
    //char *rtp_video_packet_buffer;
    char rtp_video_packet_buffer[1024 * 8];
    char audio_packet_buffer[1024 * 8];
    int last_video_result;
    //int last_video_packet_size;
    double timestamp_multiplier;
    uint64_t first_audio_timestamp;
    uint64_t first_video_timestamp;
    //int64_t diff_timestamp;
    //uint64_t semi_last_video_timestamp;
    uint64_t last_video_timestamp;
    int found_videopacket;
    int found_audiopacket;
    int video_rtp_payload_type;
    uint32_t audio_rate;
    uint64_t audio_base_timestamp;
} FSVDemuxContext;

static void rtp_init_sequence(RTPStatistics *s, uint16_t seq)
{
    s->max_seq= seq;
    s->cycles= 0;
    s->base_seq= seq -1;
    s->bad_seq= RTP_SEQ_MOD + 1;
    s->received= 0;
    s->expected_prior= 0;
    s->received_prior= 0;
    s->jitter= 0;
    s->transit= 0;
}

static int fsv_rtp_valid_packet_in_sequence(RTPStatistics *s, uint16_t seq)
{
    uint16_t udelta= seq - s->max_seq;
    const int MAX_DROPOUT= 3000;
    const int MAX_MISORDER = 100;
    const int MIN_SEQUENTIAL = 2;

    /* source not valid until MIN_SEQUENTIAL packets with sequence seq. numbers have been received */
    if(s->probation)
    {
        if(seq==s->max_seq + 1) {
            s->probation--;
            s->max_seq= seq;
            if(s->probation==0) {
                rtp_init_sequence(s, seq);
                s->received++;
                return 1;
            }
        } else {
            s->probation= MIN_SEQUENTIAL - 1;
            s->max_seq = seq;
        }
    } else if (udelta < MAX_DROPOUT) {
        // in order, with permissible gap
        if(seq < s->max_seq) {
            //sequence number wrapped; count antother 64k cycles
            s->cycles += RTP_SEQ_MOD;
        }
        s->max_seq= seq;
    } else if (udelta <= RTP_SEQ_MOD - MAX_MISORDER) {
        // sequence made a large jump...
        if(seq==s->bad_seq) {
            // two sequential packets-- assume that the other side restarted without telling us; just resync.
            rtp_init_sequence(s, seq);
        } else {
            s->bad_seq= (seq + 1) & (RTP_SEQ_MOD-1);
            return 0;
        }
    } else {
        // duplicate or reordered packet...
    }
    s->received++;
    return 1;
}


static void fsv_finalize_packet(RTPDemuxContext *s, AVPacket *pkt, uint32_t timestamp)
{
    /*if (s->ignore_timestamp)
        return;*/
    if (pkt->pts != AV_NOPTS_VALUE || pkt->dts != AV_NOPTS_VALUE)
        return; /* Timestamp already set by depacketizer */
    if (s->last_rtcp_ntp_time != AV_NOPTS_VALUE && timestamp != RTP_NOTS_VALUE) {
        int64_t addend;
        int delta_timestamp;

        /* compute pts from timestamp with received ntp_time */
        delta_timestamp = timestamp - s->last_rtcp_timestamp;
        /* convert to the PTS timebase */
        addend = av_rescale(s->last_rtcp_ntp_time - s->first_rtcp_ntp_time, s->st->time_base.den, (uint64_t)s->st->time_base.num << 32);
        pkt->pts = s->range_start_offset + s->rtcp_ts_offset + addend +
                   delta_timestamp;
        return;
    }
    if (timestamp == RTP_NOTS_VALUE)
        return;
    if (!s->base_timestamp)
        s->base_timestamp = timestamp;
    pkt->pts = s->range_start_offset + timestamp - s->base_timestamp;
}

static int fsv_rtp_parse_packet_internal(RTPDemuxContext *s, AVPacket *pkt,
                                     const uint8_t *buf, int len)
{
    unsigned int ssrc, h;
    int payload_type, seq, ret, flags = 0;
    int ext;
    AVStream *st;
    uint32_t timestamp;
    int rv= 0;

    ext = buf[0] & 0x10;
    payload_type = buf[1] & 0x7f;
    if (buf[1] & 0x80)
        flags |= RTP_FLAG_MARKER;
    seq  = AV_RB16(buf + 2);
    timestamp = AV_RB32(buf + 4);
    //av_log(NULL, AV_LOG_DEBUG, "timestamp: %d\n", timestamp);
    ssrc = AV_RB32(buf + 8);
    /* store the ssrc in the RTPDemuxContext */
    s->ssrc = ssrc;
    //av_log(NULL, AV_LOG_DEBUG, "seq: %d\n", seq);
    /* NOTE: we can handle only one payload type */
    /*if (s->payload_type != payload_type)
        return -1;*/
    /*if (s->payload_type >= 0 && s->payload_type != payload_type)
        return -1;*/
      if (s->payload_type != payload_type)
        return -1;
 
    st = s->st;
    // only do something with this if all the rtp checks pass...
    if(!fsv_rtp_valid_packet_in_sequence(&s->statistics, seq))
    {
        av_log(st?st->codec:NULL, AV_LOG_ERROR, "RTP: PT=%02x: bad cseq %04x expected=%04x\n",
               payload_type, seq, ((s->seq + 1) & 0xffff));
        return -1;
    }

    if (buf[0] & 0x20) {
        int padding = buf[len - 1];
        if (len >= 12 + padding)
            len -= padding;
    }

    s->seq = seq;
    len -= 12;
    buf += 12;

    /* RFC 3550 Section 5.3.1 RTP Header Extension handling */
    if (ext) {
        if (len < 4)
            return -1;
        /* calculate the header extension length (stored as number
         * of 32-bit words) */
        ext = (AV_RB16(buf + 2) + 1) << 2;

        if (len < ext)
            return -1;
        // skip past RTP header extension
        len -= ext;
        buf += ext;
    }

    if (!st) {
        /* specific MPEG2TS demux support */
        ret = ff_mpegts_parse_packet(s->ts, pkt, buf, len);
        /* The only error that can be returned from ff_mpegts_parse_packet
         * is "no more data to return from the provided buffer", so return
         * AVERROR(EAGAIN) for all errors */
        if (ret < 0)
            return AVERROR(EAGAIN);
        if (ret < len) {
            s->read_buf_size = len - ret;
            memcpy(s->buf, buf + ret, s->read_buf_size);
            s->read_buf_index = 0;
            return 1;
        }
        return 0;
    } else if (s->parse_packet) {
        rv = s->parse_packet(s->ic, s->dynamic_protocol_context,
                             s->st, pkt, &timestamp, buf, len, flags);
    } else {
        // at this point, the RTP header has been stripped;  This is ASSUMING that there is only 1 CSRC, which in't wise.
        switch(st->codec->codec_id) {
        case CODEC_ID_MP2:
        case CODEC_ID_MP3:
            /* better than nothing: skip mpeg audio RTP header */
            if (len <= 4)
                return -1;
            h = AV_RB32(buf);
            len -= 4;
            buf += 4;
            av_new_packet(pkt, len);
            memcpy(pkt->data, buf, len);
            break;
        case CODEC_ID_MPEG1VIDEO:
        case CODEC_ID_MPEG2VIDEO:
        case CODEC_ID_H263:
        case CODEC_ID_H261:
            /* better than nothing: skip mpeg video RTP header */
            if (len <= 4)
                return -1;
            h = AV_RB32(buf);
            buf += 4;
            len -= 4;
            if (h & (1 << 26)) {
                /* mpeg2 */
                if (len <= 4)
                    return -1;
                buf += 4;
                len -= 4;
            }
            av_new_packet(pkt, len);
            memcpy(pkt->data, buf, len);
            break;
        default:
            av_new_packet(pkt, len);
            memcpy(pkt->data, buf, len);
            break;
        }

        pkt->stream_index = st->index;
    }

    // now perform timestamp things....
    fsv_finalize_packet(s, pkt, timestamp);
    return rv;
}


static int fsv_probe(AVProbeData *p)
{
    if (AV_RL32(p->buf) == FSV_VERSION)
    {
        //av_log(NULL, AV_LOG_DEBUG, "FSV PROBE OK!\n");
        return AVPROBE_SCORE_MAX;
    }
    //av_log(NULL, AV_LOG_DEBUG, "FSV PROBE FAILED!\n");
    return 0;
}

//RTP things here

static int fsv_read_header(AVFormatContext *s, AVFormatParameters *ap)
{
    FSVFileHeader *fsv_header = av_malloc(sizeof(FSVFileHeader));
    FSVDemuxContext *fsv_dmx_ctx = s->priv_data;
    ByteIOContext *pb = s->pb;
    PayloadContext *payload_ctx;
    RTPDynamicProtocolHandler *video_rtp_handler;
    AVStream *audio_st;
    AVStream *video_st;
    //RTPDemuxContext *audio_dmx_ctx;
    RTPDemuxContext *video_dmx_ctx;
    char prefmtp[133] = "fmtp:";
    //int audio_payload_type;
    int video_payload_type;
    //double den1, den2;
   
    //parsing file header...
    fsv_header->version = get_le32(pb);
    get_buffer(pb, fsv_header->video_codec_name, 32);
    get_buffer(pb, fsv_header->video_fmtp, 128);
    av_strlcat(prefmtp, fsv_header->video_fmtp);
    fsv_header->audio_rate = get_le32(pb);
    fsv_header->audio_ptime = get_le32(pb);
    fsv_header->created = get_le64(pb);
    //creating dynamic video rtp handler from codec name...
    video_rtp_handler = ff_rtp_handler_find_by_name(fsv_header->video_codec_name, AVMEDIA_TYPE_VIDEO);
    if (video_rtp_handler && video_rtp_handler->open)
    {
        payload_ctx = video_rtp_handler->open();
    }
    else
    {
        payload_ctx = av_malloc(sizeof(PayloadContext));
    }

    if (!payload_ctx)
    {
        return -1;
    }
    //creating mandatory video track...
    video_st = av_new_stream(s, 0);
    if (!video_st)
    {
        return -1;
    }
    video_st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
    //video_st->discard = AVDISCARD_NONKEY;
    //video_st->time_base.num = 1;
    //video_st->time_base.den = 90000;
    if (video_rtp_handler)
    {
        video_st->codec->codec_id = video_rtp_handler->codec_id;
    }
    else
    {
        video_st->codec->codec_id = ff_rtp_codec_id(fsv_header->video_codec_name, AVMEDIA_TYPE_VIDEO);
    }
    //parsing video FTMP line...
    if (video_rtp_handler && video_rtp_handler->parse_sdp_a_line)
    {
        video_rtp_handler->parse_sdp_a_line(s, 0, payload_ctx, prefmtp);
    }
    //creating rtp video demux context...
    video_payload_type = ff_rtp_get_payload_type(video_st->codec);
    video_dmx_ctx = rtp_parse_open(s, video_st, NULL, video_payload_type, 32);
    if (!video_dmx_ctx)
    {
        return -1;
    }
    if (video_rtp_handler)
    {
        rtp_parse_set_dynamic_protocol(video_dmx_ctx, payload_ctx, video_rtp_handler);
    }
    //ff_rtp_reset_packet_queue(video_dmx_ctx);
    //video_dmx_ctx->ignore_timestamp = 1;
    //creating mandatory audio track...
    audio_st = av_new_stream(s, 1);
    if (!audio_st)
    {
        return -1;
    }
    audio_st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
    audio_st->codec->codec_id = CODEC_ID_PCM_S16LE;
    audio_st->codec->channels = 1;
    audio_st->codec->sample_rate = fsv_header->audio_rate;
    //audio_st->need_parsing = AVSTREAM_PARSE_FULL;
    //audio_st->codec->frame_size = fsv_header->audio_ptime;
    audio_st->time_base.num = 1;
    audio_st->time_base.den = fsv_header->audio_rate;
    //audio_st->time_base.den = 90000;
    //den1 = 90000;
    //den2 = fsv_header->audio_rate;
    //fsv_dmx_ctx->timestamp_multiplier = ((double) den1) / ((double) den2);
    fsv_dmx_ctx->rtp_video_dmx_ctx = video_dmx_ctx;
    fsv_dmx_ctx->audio_rate = fsv_header->audio_rate; 
    //av_log(NULL, AV_LOG_DEBUG, "multiplier: %f \n", fsv_dmx_ctx->timestamp_multiplier);
    return 0;
}

static int find_rtp_marker(FSVDemuxContext *fsv_dmx_ctx)
{
    char *data = fsv_dmx_ctx->rtp_video_packet_buffer;
    int marker = 0;
    marker = data[1] & 0x80;
    return marker;
}

static int find_rtp_payload_type(FSVDemuxContext *fsv_dmx_ctx)
{
    char *data = fsv_dmx_ctx->rtp_video_packet_buffer;
    int payload_type;
    payload_type = data[1] & 0x7f;
    return payload_type;
}

static int fsv_read_packet(AVFormatContext *s, AVPacket *pkt)
{
    FSVDemuxContext *fsv_dmx_ctx = s->priv_data;
    ByteIOContext *pb  = s->pb;
    //int data, size, result, audio_timestamp;
    int data, size, result;
    unsigned int video;

    try_again:
    if (fsv_dmx_ctx->last_video_result == 1)
    {
        //has video packets in queue
        //av_log(NULL, AV_LOG_DEBUG, "got rtp packets in queue!\n");
        video = FSV_VID_BIT;
        result = rtp_parse_packet(fsv_dmx_ctx->rtp_video_dmx_ctx, pkt, NULL, size);
        fsv_dmx_ctx->last_video_result = result;
        fsv_dmx_ctx->last_video_timestamp = pkt->pts;
        //pkt->dts = fsv_dmx_ctx->last_video_timestamp;
        goto check_result;
    }

    data = get_le32(pb);
    video = data & FSV_VID_BIT;
    size = data & ~FSV_VID_BIT;

    if (video)
    {
        //video packet detected...
        fsv_dmx_ctx->found_videopacket = 1;
        /*if (fsv_dmx_ctx->rtp_video_packet_buffer && fsv_dmx_ctx->last_video_result == 0)
        {
            av_free(fsv_dmx_ctx->rtp_video_packet_buffer);
        }
        fsv_dmx_ctx->rtp_video_packet_buffer = av_malloc(size);*/
        if (get_buffer(pb, fsv_dmx_ctx->rtp_video_packet_buffer, size) < 0)
        {
            return AVERROR_EOF;
        }
        //av_log(NULL, AV_LOG_DEBUG, "rtp marker: %d\n", find_rtp_marker(fsv_dmx_ctx));
        if (!fsv_dmx_ctx->found_audiopacket)
        {
            goto try_again;
        }
        if (!fsv_dmx_ctx->video_rtp_payload_type)
        {
            fsv_dmx_ctx->video_rtp_payload_type = find_rtp_payload_type(fsv_dmx_ctx);
            fsv_dmx_ctx->rtp_video_dmx_ctx->payload_type = fsv_dmx_ctx->video_rtp_payload_type;
        }
        //result = rtp_parse_packet(fsv_dmx_ctx->rtp_video_dmx_ctx, pkt, &fsv_dmx_ctx->rtp_video_packet_buffer, size);
        result = fsv_rtp_parse_packet_internal(fsv_dmx_ctx->rtp_video_dmx_ctx, pkt, fsv_dmx_ctx->rtp_video_packet_buffer, size);
        fsv_dmx_ctx->last_video_result = result;
        fsv_dmx_ctx->last_video_timestamp = pkt->pts;
        if (!fsv_dmx_ctx->first_video_timestamp)
        {
            fsv_dmx_ctx->first_video_timestamp = fsv_dmx_ctx->last_video_timestamp;
        }
        if (fsv_dmx_ctx->rtp_video_dmx_ctx->st->time_base.den && !fsv_dmx_ctx->timestamp_multiplier)
        {
            fsv_dmx_ctx->timestamp_multiplier = (double)(((double)fsv_dmx_ctx->rtp_video_dmx_ctx->st->time_base.den) / ((double)fsv_dmx_ctx->audio_rate));
            //s->streams[1]->time_base.num = 1;
            //s->streams[1]->time_base.den = fsv_dmx_ctx->rtp_video_dmx_ctx->st->time_base.den;
            //fsv_dmx_ctx->audio_base_timestamp = fsv_dmx_ctx->rtp_video_dmx_ctx->base_timestamp / fsv_dmx_ctx->timestamp_multiplier;
        }
        //if (fsv_dmx_ctx->first_video_timestamp < (fsv_dmx_ctx->first_audio_timestamp * fsv_dmx_ctx->timestamp_multiplier))
        //if (fsv_dmx_ctx->first_video_timestamp < fsv_dmx_ctx->first_audio_timestamp)
        //{
            //fsv_dmx_ctx->first_video_timestamp = 0;
            //fsv_dmx_ctx->rtp_video_dmx_ctx->base_timestamp = 0;
            //fsv_dmx_ctx->audio_base_timestamp = 0;
            //fsv_dmx_ctx->timestamp_multiplier = 0;
            //goto try_again;
        //}
        //pkt->dts = fsv_dmx_ctx->last_video_timestamp;
        //result = rtp_parse_packet_internal(fsv_dmx_ctx->rtp_video_dmx_ctx, pkt, fsv_dmx_ctx->rtp_video_packet_buffer, size);
    }
    else
    {
        //audio packet detected...
        fsv_dmx_ctx->found_audiopacket = 1;
        //fsv_dmx_ctx->rtp_video_dmx_ctx->ignore_timestamp = 0;
        //read audio timestamp
        //audio_timestamp = get_le32(pb);
        //buffer first audio chunk
        if (get_buffer(pb, fsv_dmx_ctx->audio_packet_buffer, size) < 0)
        {
            return AVERROR_EOF;
        }
        /*if (!fsv_dmx_ctx->found_videopacket)
        {
            goto try_again;
        }*/
        /*if (!fsv_dmx_ctx->first_audio_timestamp)
        {
            fsv_dmx_ctx->first_audio_timestamp = audio_timestamp;
        }*/
        result = av_new_packet(pkt, size);
        memcpy(pkt->data, fsv_dmx_ctx->audio_packet_buffer, size);
        pkt->stream_index = 1;
        //pkt->pts = fsv_dmx_ctx->last_video_timestamp;
        //pkt->dts = fsv_dmx_ctx->last_video_timestamp;
        //if (fsv_dmx_ctx->rtp_video_dmx_ctx->base_timestamp)
        //{
            //pkt->pts = ((audio_timestamp) * fsv_dmx_ctx->timestamp_multiplier) - fsv_dmx_ctx->rtp_video_dmx_ctx->base_timestamp;
            //pkt->pts = audio_timestamp - fsv_dmx_ctx->audio_base_timestamp;
            //pkt->pts = audio_timestamp - fsv_dmx_ctx->first_audio_timestamp;
        //}
        if (fsv_dmx_ctx->timestamp_multiplier)
        {
            pkt->pts = fsv_dmx_ctx->last_video_timestamp / fsv_dmx_ctx->timestamp_multiplier;
        }
        /*else
        {
            goto try_again;
        }*/
        if (pkt->pts < 0)
        {
            goto try_again;
        }
        //pkt->dts = ((audio_timestamp) * fsv_dmx_ctx->timestamp_multiplier) - fsv_dmx_ctx->rtp_video_dmx_ctx->base_timestamp;
    }
    check_result:
    if (result < 0)
    {
        //av_log(NULL, AV_LOG_DEBUG, "fsv parse failed, retrying!\n");
        goto try_again;
    }
    //av_log(NULL, AV_LOG_DEBUG, "video: %u ", video);
    //av_log(NULL, AV_LOG_DEBUG, "timestamp: %ld \n", pkt->pts);
    return result;
}

AVInputFormat ff_fsv_demuxer = {
    .name           = "fsv",
    .long_name      = NULL_IF_CONFIG_SMALL("FreeSWITCH Video Format"),
    .priv_data_size = sizeof(FSVDemuxContext),
    .read_probe     = fsv_probe,
    .read_header    = fsv_read_header,
    .read_packet    = fsv_read_packet,
    .extensions = "fsv",
    //.codec_tag      = (const AVCodecTag* const []){lxf_tags, 0},
};

