/*
 * FSV demuxer
 * Copyright (c) 2011 William Kendi Nishio
 *
 * 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.h"
#include "rtpdec_formats.h"

//FreeSWITCH Video MAGIC 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 {
    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[1024 * 8];
    char audio_packet_buffer[1024 * 8];
} FSVDemuxContext;

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;
};

static int fsv_probe(AVProbeData *p)
{
    if (AV_RL32(p->buf) == 4201)
    {
        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 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 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;

    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 finalize_packet(RTPDemuxContext *s, AVPacket *pkt, uint32_t timestamp)
{
    if (pkt->pts != AV_NOPTS_VALUE || pkt->dts != AV_NOPTS_VALUE)

    if (s->last_rtcp_ntp_time != AV_NOPTS_VALUE && timestamp != RTP_NOTS_VALUE) {
        int64_t addend;
        int delta_timestamp;


        delta_timestamp = timestamp - s->last_rtcp_timestamp;

        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 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);
    ssrc = AV_RB32(buf + 8);
    //store the ssrc in the RTPDemuxContext
    s->ssrc = ssrc;

    //NOTE: we can handle only one payload type
    //av_log(NULL, AV_LOG_DEBUG, "RTP PACKET PAYLOAD TYPE: %d\n", payload_type);
    
    /*if (s->payload_type >= 0 && s->payload_type != payload_type)
    {
        //av_log(NULL, AV_LOG_DEBUG, "BAD PAYLOAD TYPE!\n");
        av_log(NULL, AV_LOG_DEBUG, "BAD RTP PACKET PAYLOAD TYPE: %d\n", payload_type);
        return -1;
    }*/
    st = s->st;
    // only do something with this if all the rtp checks pass...
    if(!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)
        {
            av_log(NULL, AV_LOG_DEBUG, "BAD RTP LEN!\n");
            return -1;
        }
        //calculate the header extension length (stored as number
        //of 32-bit words)
        ext = (AV_RB16(buf + 2) + 1) << 2;

        if (len < ext)
        {
            av_log(NULL, AV_LOG_DEBUG, "BAD RTP LEN EXT!\n");
            return -1;
        }
        // skip past RTP header extension
        len -= ext;
        buf += ext;
    }

    if (!st) {

        ret = ff_mpegts_parse_packet(s->ts, pkt, buf, len);

        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);
        if (rv < 0)
        {
            av_log(NULL, AV_LOG_DEBUG, "BAD RTP PACKET!\n");
        }
    } 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:

            if (len <= 4)
            {
                av_log(NULL, AV_LOG_DEBUG, "BAD RTP LEN MP3!\n");
                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:

            if (len <= 4)
            {
                av_log(NULL, AV_LOG_DEBUG, "BAD RTP LEN MPEG2!\n");
                return -1;
            }
            h = AV_RB32(buf);
            buf += 4;
            len -= 4;
            if (h & (1 << 26)) {

                if (len <= 4)
                {
                    av_log(NULL, AV_LOG_DEBUG, "BAD RTP LEN MPEG2 HEADER!\n");
                    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....
    finalize_packet(s, pkt, timestamp);

    return rv;
}

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;
   
    //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;
    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, 10);
    if (!video_dmx_ctx)
    {
        return -1;
    }
    if (video_rtp_handler)
    {
        rtp_parse_set_dynamic_protocol(video_dmx_ctx, payload_ctx, video_rtp_handler);
    }
    //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;
    fsv_dmx_ctx->rtp_video_dmx_ctx = video_dmx_ctx;
    return 0;
}


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

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

    if (video)
    {
        //video packet detected...
        if (get_buffer(pb, fsv_dmx_ctx->rtp_video_packet_buffer, size) < 0)
        {
            return AVERROR_EOF;
        }
        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...
        if (get_buffer(pb, fsv_dmx_ctx->audio_packet_buffer, size) < 0)
        {
            return AVERROR_EOF;
        }
        result = av_new_packet(pkt, size);
        memcpy(pkt->data, fsv_dmx_ctx->audio_packet_buffer, size);
        pkt->stream_index = 1;
    }
    if (video && result < 0)
    {
        goto try_again;
    }
    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},
};

