/*
 * Copyright (c) 1994-1999 The Regents of the University of California
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the University of
 *      California, Berkeley and the Network Research Group at
 *      Lawrence Berkeley Laboratory.
 * 4. Neither the name of the University nor of the Laboratory may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/* History:
 *
 * This encoder was written for a DFN project (http://www.dfn.de/) at the
 * University of Erlangen-Nuremberg, Lehrstuhl fuer Nachrichtentechnik by
 * Marcus Meissner in 1998.
 *
 * Merged into UCL vic development tree in 1998 by ?
 *
 * Cleanups, endianess, translation of comments into english, bugfixes,
 * done by Marcus Meissner in June 1999.
 */
#ifndef lint
static char rcsid[] =
    "@(#) $Header: /cs/research/mice/starship/src/local/CVS_repository/vic/encoder-h263.cc,v 1.4 1999/06/03 15:53:22 piers Exp $ (LBL)";
#endif

#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <time.h>

#include "defs.h"
#include "bsd-endian.h"
#include "transmitter.h"
#include "module.h"
#include "crdef.h"
 
#include "h263/bitOut.h"

#include "h263/h263.h"
#include "encoder-h263.h"
extern "C" {
#include "h263/h263encoder.h"
#include "h263/h263encoder.p"
int split_h263stream(
	u_char *bs,			/* bitstream [in] */
	u_int bitcount,			/* bitstream length in bits [in] */
	u_int gobs_per_frame,		/* gobs per frame [in] */
	u_int mbs_per_gob,		/* mbs per gob [in] */
	int *mbind,			/* macroblock indices [in] */
	int *mbquant,			/* quantifiers [in] */
	MVField mvfield,		/* motion vectors [in] */
	u_int ts_,			/* timestamp [in] */
	int headersize,			/* headerlength [in] */
	h263_rtp_packet	**packets	/* rtp packets [out] */
);
}

H263Encoder::H263Encoder() : TransmitterModule(FT_YUV_CIF)
{
	/* first frame must be coded INTRA */
	nexttype_ = PICTURE_CODING_TYPE_INTRA;

	lastw_ = 0;lasth_ = 0;
	oldpic_.w = 0;
	oldpic_.h = 0;
	oldpic_.y = new Byte[3];
	oldpic_.u = oldpic_.y+1;
	oldpic_.v = oldpic_.u+1;
	oldorigpic_.w = 0;
	oldorigpic_.h = 0;
	oldorigpic_.y = new Byte[3];
	oldorigpic_.u = oldorigpic_.y+1;
	oldorigpic_.v = oldorigpic_.u+1;

	mbind_=(int*)malloc(1);
	mbquant_=(int*)malloc(1);

	mvfield_.mx = (short*)malloc(1);
	mvfield_.my = (short*)malloc(1);
	mvfield_.mode = (short*)malloc(1);
	mvfield_.w = 1;
	mvfield_.h = 1;

	decpict_.w = 0; decpict_.h = 0;
	decpict_.y = new Byte[3];
	decpict_.u = decpict_.y+1;
	decpict_.v = decpict_.u+1;

	/* global maximum rate for the encoder.
	 * The encoder itself does not have a fixed bitrate delimiter, so
	 * we just allocate a very big buffer and hope that the encoder does
	 * not overflow it.
	 */
	maxrate_ = 100000;
	bitstr_.b = new Byte[maxrate_*10/8];
	bitstr_.size = maxrate_*10;
	bitstr_.ind = 0;
	bitstr_.actualSize = 0;
	bitstr_.fp = NULL;

	/* We use only codingtime 3, since it is the best time/quality/space
	 * tradeoff currently. 
	 */
	codingtime_ = 3;

	maycheck_ = 0;

	/* default Quantisierer is 10 */
	q_ = 10;
}

H263Encoder::~H263Encoder()
{
/* Bus Errors... Caused by destructors called out of sequence.
	if (tx_ != 0)
		tx_->flush();
 */
}

int
H263Encoder::command(int argc,const char* const *argv)
{
	if (argc==3) {
		if (!strcasecmp(argv[1],"q")) {
			q_ = atoi(argv[2]);
			return 0;
		}
	}
	return TransmitterModule::command(argc,argv);
}

/***********************************************************************
 * H263Encoder::consume()
 *
 * Maininterface function to VIC is called for every grabbed frame.
 * Encodes the frames into h.263, paketises them according to RFC 2190 and
 * sends the RTP packets using the RTP networklayer of VIC.
 *
 * Returns number of sent bytes.
 */
int H263Encoder::consume(const VideoFrame* vf)
{
	YuvFrame*	yuv = (YuvFrame*)vf;	/* real YUV Frame */
	Picture		thispic;		/* current image */
	u_int		xfps,kbps;		/* parameter from Tcl */
	u_int		i;
	u_int		sentbytes=0;		/* sent bytes in this consume */
	u_int		bps;			/* target bitrate */
	int		k,mbs_per_gob,gobs_per_frame;
	int		timespent,w,h;
	Tcl&		tcl = Tcl::instance();
	Transmitter::pktbuf* pb;
	rtphdr*		rh;
	struct timeval tv1,tv2;

	w = yuv->width_;
	h = yuv->height_;


        k = 1;
        if (w == CIF4_WIDTH)         k = 2;
        if (w == 2*CIF4_WIDTH)       k = 4;
 
	/* FIXME: incorrect for CIF*2 and up! */
	mbs_per_gob = w/16/k;
	gobs_per_frame = h/16/k;

	/* did we the size of the grabbed frame change? */
	if (w!=lastw_ || h!=lasth_) {
		/* readjust structzures, temp. arrays, et.al. */
		delete[] oldpic_.y;
		delete[] oldorigpic_.y;
		delete[] decpict_.y;

		free(mvfield_.mx);
		free(mvfield_.my);
		free(mvfield_.mode);

		oldpic_.w = w;oldpic_.h = h;
		oldpic_.y = new Byte[w*h*3/2];
		oldpic_.u = oldpic_.y+w*h;
		oldpic_.v = oldpic_.u+w*h/4;

		oldorigpic_.w = w;oldpic_.h = h;
		oldorigpic_.y = new Byte[w*h*3/2];
		oldorigpic_.u = oldorigpic_.y+w*h;
		oldorigpic_.v = oldorigpic_.u+w*h/4;

		decpict_.w = w;decpict_.h = h;
		decpict_.y = new Byte[w*h*3/2];
		decpict_.u = decpict_.y+w*h;
		decpict_.v = decpict_.u+w*h/4;

		mvfield_.mx = (short*)malloc(mbs_per_gob*gobs_per_frame*sizeof(short));
		mvfield_.my = (short*)malloc(mbs_per_gob*gobs_per_frame*sizeof(short));
		mvfield_.mode = (short*)malloc(mbs_per_gob*gobs_per_frame*sizeof(short));
		mvfield_.w = mbs_per_gob;
		mvfield_.h = gobs_per_frame;
		nexttype_ = PICTURE_CODING_TYPE_INTRA;
		lastw_ = w;
		lasth_ = h;
		free(mbind_);free(mbquant_);
		mbind_=(int*)malloc((mbs_per_gob*gobs_per_frame+1)*sizeof(int));
		mbquant_=(int*)malloc(mbs_per_gob*gobs_per_frame*sizeof(int));


	}

	/* HACK: We query the Tcl layers for the specified bps and fps values.
	 * Hack due to the fact, that no other encoder uses this values in this
	 * way and we could get feedback loops from the network code (who uses
	 * them). Bummer.
	 */
	tcl.evalc("$fps_slider get");sscanf(tcl.result(),"%d",&xfps);
	tcl.evalc("$bps_slider get");sscanf(tcl.result(),"%d",&kbps);

	/* calculate bits/frame */
	bps = kbps*1024/xfps;

	/* temporary picture from passed YUV Frame */
	thispic.w = w;
	thispic.h = h;
	thispic.y = (Byte*)yuv->bp_;
	thispic.u = (Byte*)yuv->bp_+w*h;
	thispic.v = (Byte*)yuv->bp_+w*h+(w*h)/4;

	/* Bitstream starts at 0 */
	bitstr_.ind = 0;

	/* and GO */
	bitstr_.b[bitstr_.size/8-1] = 0x42;
	gettimeofday(&tv1,NULL);
	EncodeH263Q(
		q_,				/* [in] Quantisierer */
		codingtime_,			/* [in] Codingtime */
		nexttype_,			/* [in] codingtype */
		vf->ts_,			/* [in] Timestamp */
		5,				/* [in] percent intra blocks */
		&thispic,			/* [in] current image4 */
		&oldorigpic_,
		&oldpic_,			/* [in] prev. coded image */
		(unsigned char*)yuv->crvec_,	/* [in] condit. replenishment */
		&decpict_,			/* [out] new coded image */
		&bitstr_,			/* [out] bitstream */
		mbind_,				/* [out] MB indices */
		mbquant_,			/* [out] MacroBlock Quant. */
		&mvfield_			/* [out] MotionVector field */
	);

	{
		Picture tmppict;
		tmppict = oldpic_; oldpic_ = decpict_; decpict_ = tmppict;
	}
	memcpy(oldorigpic_.y,thispic.y,w*h*3/2);

	assert(bitstr_.ind < bitstr_.size);
	assert(bitstr_.b[bitstr_.size/8-1]== 0x42);

	/* mark the end of the bitstream */
	mbind_[mbs_per_gob*gobs_per_frame]=bitstr_.ind;

	/* The actual splitfunction */
	h263_rtp_packet	*packets = NULL;
	int nrofpackets = split_h263stream(
		bitstr_.b,			/* [in] bitstream */
		bitstr_.ind,			/* [in] length in bits*/
		gobs_per_frame,
		mbs_per_gob,
		mbind_,
		mbquant_,
		mvfield_,
		yuv->ts_,			/* [in] timestamp */
		tx_->mtu(),			/* [in] current MTU */
		&packets			/* [out] packets... */
	);
	/* And now we send the packets.
	 * We use the 2 standard iov buffers, buffer 1 just for the RTP header,
	 * buffer 2 for the H263 payloadheader and the h.263 bits.
	 * Reason for this is that the VIC decoder structure does not like 
	 * like differently sized headers and acts differently between loopback
	 * and network-in decoding.
	 */
	for (i=0;i<nrofpackets;i++) {
		pb = tx_->alloc(yuv->ts_,RTP_PT_H263);
		rh = (rtphdr*)pb->iov[0].iov_base;

		pb->iov[0].iov_len = sizeof(rtphdr);

		memcpy(pb->iov[1].iov_base,packets[i].header,packets[i].headersize);
		memcpy((char*)pb->iov[1].iov_base+packets[i].headersize,packets[i].data,packets[i].datasize);
		pb->iov[1].iov_len = packets[i].datasize+packets[i].headersize;

		/* mark the last packet */
		if (i==nrofpackets-1)
			rh->rh_flags |= htons(RTP_M);
		/* sending ...  */
		tx_->send(pb);
		sentbytes+=packets[i].headersize+packets[i].datasize+sizeof(rtphdr);
	}
	/* all other frames are coded with percentual INTRA part */
	nexttype_ = PICTURE_CODING_TYPE_INTER;
	/* flush the buffered RTP packets, so we can free the packets.
	 * Bad style probably */
	tx_->flush();
	free(packets);

	/* and TIME! */
	gettimeofday(&tv2,NULL);
	
	timespent = (tv2.tv_sec-tv1.tv_sec)*1000000+(tv2.tv_usec-tv1.tv_usec);
#ifdef TIME_ADAPTION
	if ((++maycheck_)>20) {
		maycheck_ = 0;
		/* Did we use too much time? */
		if ((1000000/xfps*3)<timespent) {
			/* Yes, first decrease imagesize */
			if (w==CIF_WIDTH) {
				tcl.evalc("grabber decimate 4");
				tcl.evalc("set inputSize 4");
				fprintf(stderr,"TOO SLOW: shrunk to QCIF\n");
				return 0;
			} else {
				/* no more ways to do speed it up YET */
			}
		}
		/* Did we use much less time as expected? */
		if ((1000000/xfps)>timespent*3) {
			if (w==QCIF_WIDTH) {
				tx_->flush();
				tcl.evalc("grabber decimate 2");
				tcl.evalc("set inputSize 2");
				fprintf(stderr,"TOO FAST: grown to CIF\n");
				return 0;
			}
		}
	}
#endif
	return sentbytes;
}
