[Sumover-dev] [svn commit] r3974 - in vic/branches/mpeg4: codec rtp tcl

sumover-dev at cs.ucl.ac.uk sumover-dev at cs.ucl.ac.uk
Fri Mar 23 18:12:16 GMT 2007


Author: piers
Date: Fri Mar 23 18:11:29 2007
New Revision: 3974

Added:
   vic/branches/mpeg4/codec/rtp_h264_depayloader.cpp
   vic/branches/mpeg4/codec/rtp_h264_depayloader.h
Modified:
   vic/branches/mpeg4/Makefile.in
   vic/branches/mpeg4/codec/databuffer.h
   vic/branches/mpeg4/codec/decoder-h264.cpp
   vic/branches/mpeg4/codec/encoder-h264.cpp
   vic/branches/mpeg4/codec/packetbuffer.cpp
   vic/branches/mpeg4/codec/packetbuffer.h
   vic/branches/mpeg4/rtp/rtp.h
   vic/branches/mpeg4/tcl/cf-main.tcl

Log:
RTP Payload format for H.264 (RFC 3984) 

Most of this work below has been done by Socrates. Tidy up and bug fixes by Piers.

General comments:

1. Implemented the H.264 RTP framing according to RFC 3984. The implementation covers the "Single NAL Unit" (Single NALU packet type) and "Non-Interleaved" modes (STAP-A, FU-A packet types). The interleaved mode (STAP-B, MTAP16, MTAP24, FU-B) has not been implemented at this stage. [I don't know of another tool, closed source or open, that has implemented this mode to date to be able to test with (the current QuickTime Player 7.1.3 does not implement it either, as far as I know).]

2. The decoder has been tested with the MPEG4IP's mp4live server 1.5.0.1. An example SDP file is provided to set up VIC to decode mp4live streams. Please edit the file default_mpeg4ip.sdp with your details, or better generate your own SDP files using mp4live, and place the edited SDP file in your home directory. The filename should be "default.sdp"

3. The H264 depayloader implementation is currently based libavformat (part of ffpmeg). The plan to is re-write the H264 depayloader under BSD license in the near future.

4. The H.264 VIC encoder will not aggregate NAL units at the moment (STAP-A). The decoder will properly decode such packets.

5. VIC sends SPS/PPS NAL units in-band at the start of H.264 transmission. One has to check whether this can be done periodically. This way we may avoid SDP and sprep-parameter-set.

6. The SDP may be configured for the decoder which uses either a default embedded SDP or from "$HOME/default.sdp"   

7. The SDP file is only used to convey SPS/PPS sprop-parameter-sets. IP addresses, ports, etc. are read from VIC command-line as normal.


Modified: vic/branches/mpeg4/Makefile.in
==============================================================================
--- vic/branches/mpeg4/Makefile.in	(original)
+++ vic/branches/mpeg4/Makefile.in	Fri Mar 23 18:11:29 2007
@@ -147,6 +147,7 @@
 	codec/packetbuffer.o codec/databuffer.o \
 	codec/ffmpeg_codec.o codec/encoder-mpeg4.o codec/decoder-mpeg4.o \
 	codec/x264encoder.o codec/encoder-h264.o codec/decoder-h264.o \
+	codec/rtp_h264_depayloader.o \
 	codec/decoder-h263.o codec/decoder-h263v2.o codec/decoder-cellb.o \
 	codec/decoder-h261.o codec/decoder-h261v1.o codec/decoder-raw.o \
 	render/renderer.o render/renderer-window.o render/color.o \

Modified: vic/branches/mpeg4/codec/databuffer.h
==============================================================================
--- vic/branches/mpeg4/codec/databuffer.h	(original)
+++ vic/branches/mpeg4/codec/databuffer.h	Fri Mar 23 18:11:29 2007
@@ -13,11 +13,11 @@
     bool setSize(int);
     bool write(char *, int);
     bool writeAppend(char *, int);
+    char *data;
 
   private:
     int length;
     int size;
-    char *data;
 
 };
 

Modified: vic/branches/mpeg4/codec/decoder-h264.cpp
==============================================================================
--- vic/branches/mpeg4/codec/decoder-h264.cpp	(original)
+++ vic/branches/mpeg4/codec/decoder-h264.cpp	Fri Mar 23 18:11:29 2007
@@ -3,6 +3,7 @@
 #include <string.h>
 #include <assert.h>
 #include <iostream>
+#include <errno.h>
 #include "inet.h"
 #include "rtp.h"
 #include "decoder.h"
@@ -10,12 +11,15 @@
 #include "packetbuffer.h"
 #include "databuffer.h"
 #include "ffmpeg_codec.h"
+#include "rtp_h264_depayloader.h"
 
-//#define DIRECT_DISPLAY 1
 
-using namespace std;
+#define SDP_LINE_LEN 10000
+
+//#define DIRECT_DISPLAY 1
+//using namespace std;
+//extern "C" UCHAR * video_frame;
 
-extern "C" UCHAR * video_frame;
 
 class H264Decoder:public Decoder
 {
@@ -23,6 +27,7 @@
     H264Decoder();
     ~H264Decoder();
 
+    void handleSDP();
     virtual void recv(pktbuf *);
     int colorhist(u_int * hist) const;
   protected:
@@ -30,12 +35,12 @@
     virtual void redraw();
 
     /* packet statistics */
-    u_int16_t last_seq;		/* sequence number */
+    uint16_t last_seq;		/* sequence number */
 
     UCHAR bitstream[MAX_CODED_SIZE];	/* bitstream data */
 
     /* collecting data for a frame */
-    int idx;
+    uint16_t idx;
     int last_mbit;
     int last_iframe;
     bool startPkt;
@@ -43,11 +48,12 @@
     /* image */
     UCHAR xxx_frame[MAX_FRAME_SIZE];
     FFMpegCodec h264;
-    PacketBuffer *stream;
-
+    PacketBuffer *stream; //SV: probably this is going to be substituted by the AVPacket->data when we call decode()...
+    H264Depayloader *h264depayloader;
 
     //For DEBUG
-    FILE *fptr;
+    //FILE *fptr;
+    FILE *sdp_fptr;
 };
 
 static class H264DecoderMatcher:public Matcher
@@ -67,9 +73,11 @@
 dm_h264;
 
 
-H264Decoder::H264Decoder():Decoder(2)
+H264Decoder::H264Decoder():Decoder(0 /* 0 byte extra header */)
 {				/* , codec_(0), */
 
+
+    //Barz: =============================================
     decimation_ = 411;
     /*
      * Assume CIF.  Picture header will trigger a resize if
@@ -78,7 +86,8 @@
     inw_ = 0;
     inh_ = 0;
 
-     /*XXX*/ resize(inw_, inh_);
+     /*XXX*/ 
+    resize(inw_, inh_);
 
     // libavcodec
     h264.init(false, CODEC_ID_H264, PIX_FMT_YUV420P);
@@ -88,23 +97,85 @@
     last_mbit = 0;
     last_iframe = 0;
     last_seq = 0;
+    //===================================================
+
+
+    //SV: ===============================================
+    //Create payloader
+    h264depayloader = new H264Depayloader;
+
+    handleSDP();
+    //make sure all codec params are set up properly before decoding
+
+    //check whether AVPacket struct can be used in stead of Barz's PacketBuffer
+    //===================================================
+
 
     //256 packets, each 1600 byte (default will not exceed 1600 byte)
     //cout << "new PacketBuffer..\n";
-    stream = new PacketBuffer(1024, 1600);
+    stream = new PacketBuffer(1024, 1600); //SV: 1024 = ??? 
     startPkt = false;
+}
+
+void
+H264Decoder::handleSDP()
+{
+    char SdpFilename[SDP_LINE_LEN];
+    char *line, *sdp_string;
+    char *SdpLine=NULL;
+    int n_char;
+    size_t nBytes = 0;
+    ssize_t SdpRead;
+    char defaultSDP[]="a=rtpmap:96 H264/90000\na=fmtp:96 profile-level-id=00000d; packetization-mode=1\n";
+
+    sprintf(SdpFilename, "%s/default.sdp", getenv("HOME"));
 
     //fptr = fopen("out.m4v", "w");
+    if ((sdp_fptr = fopen(SdpFilename, "r")) != NULL ) {
+	//fprintf(stderr, "H264_RTP: Opened SDP file %s for read.\n", SdpFilename);
+
+      //Read SDP file into struct
+      //fprintf(stderr, "H264_RTP: Spitting SDP ================================================\n");
+      while ((SdpRead = getline(&SdpLine, &nBytes, sdp_fptr)) != -1) {
+	//fprintf(stderr, "H264_RTP: Read %d bytes from SDP file.\n", SdpRead);
+	//fprintf(stderr, "%s", SdpLine);
+	//call SDP parse h264 routine
+	  h264depayloader->parse_h264_sdp_line(h264.c, h264depayloader->h264_extradata, SdpLine);
+      }
+      if (SdpLine)
+	  free(SdpLine);
+	
+    } else {
+	fprintf(stderr, "H264_RTP: Couldn't open SDP file %s to read. Errno = %d\n", SdpFilename, errno);
+	fprintf(stderr, "H264_RTP: Using default SDP: %s \n", defaultSDP);
+
+      line=defaultSDP;
+      do {
+	n_char = strcspn(line, "\n");
+        SdpLine = (char *)malloc(n_char+1);
+	memset(SdpLine, '\0', n_char+1);
+	strncpy(SdpLine, line, n_char);
+	line += n_char + 1;
+	  h264depayloader->parse_h264_sdp_line(h264.c, h264depayloader->h264_extradata, SdpLine);
+	free(SdpLine);
+      } while (n_char != 0);
+    //fprintf(stderr, "H264_RTP: Done spitting SDP ===========================================\n");
+    }
 }
 
 H264Decoder::~H264Decoder()
 {
     delete stream;
+    delete h264depayloader;
     //fclose(fptr);
+    if (sdp_fptr != NULL) fclose(sdp_fptr);
+    //fprintf(stderr, "H264_RTP: Closed SDP file.\n");
 }
 
 int H264Decoder::colorhist(u_int * hist)  const
 {
+    UNUSED(hist);
+
     return (1);
 }
 
@@ -114,17 +185,20 @@
     int hdrsize = sizeof(rtphdr) + hdrlen();
     u_char *bp = pb->dp + hdrsize;
     int cc = pb->len - hdrsize;
-    static int iframe_c = 0, pframe_c = 0;
+    //static int iframe_c = 0, pframe_c = 0;
 
     int mbit = ntohs(rh->rh_flags) >> 7 & 1;
-    int seq = ntohs(rh->rh_seqno);
+    uint16_t seq = ntohs(rh->rh_seqno);
     int ts = ntohl(rh->rh_ts);
 
+
+
+    //Barz: =============================================
     if (!startPkt) {
        stream->clear();
        startPkt = true;
        idx = seq;
-	   last_seq = seq - 1;
+       last_seq = seq - 1;
     }
 	  
     int pktIdx = seq - idx;
@@ -133,32 +207,41 @@
     }
 
     if (abs(seq - last_seq) > 5) {
-	    debug_msg("h264dec: sequece interrupt!\n");
+	    //fprintf(stderr, "H264_RTP: sequece interrupt!\n");
 	    idx = seq;
 	    pktIdx = 0;
 	    stream->clear();
     }else if (last_seq + 1 != seq) {
-	    /* oops - missing packet */
-	    debug_msg("h264dec: missing packet\n");
+	    // oops - missing packet
+	    //fprintf(stderr, "H264_RTP: missing packet\n");
     }
 
+    //===================================================
+
+
     //copy packet
-    stream->write(pktIdx, cc, (char *) bp);
-    
+    //stream->write(pktIdx, cc, (char *) bp);
+    //fprintf(stderr, "H264_RTP: -------------------------------- seq = %d, m=%d, ts=%d, cc=%d\n", seq, mbit, ts, cc);
+    //fprintf(stderr, "H264_RTP: pktIdx = %d\n", pktIdx);
+    int yo = h264depayloader->h264_handle_packet(h264depayloader->h264_extradata, pktIdx, stream, /*ts,*/ bp, cc);
+    //fprintf(stderr, "H264_RTP: h264_handle_packet = %d\n", yo);
+
+
+    //Barz: =============================================
+
     last_seq = seq;
     int len=0;
-	
     if (mbit) {
 	    stream->setTotalPkts(pktIdx + 1);
 
-	    DataBuffer *f;	    
+	    DataBuffer *f;
 	    if (stream->isComplete()) {
-			f = stream->getStream();
+		    f = stream->getStream();
 		    len =  h264.decode((UCHAR *) f->getData(), f->getDataSize(), xxx_frame);
 	    }
 	    
 	    if (len < 0) {
-	       debug_msg("h264dec: frame error\n");
+		//fprintf(stderr, "H264_RTP: frame error\n");
 	    }
 	   
 	    if (inw_ != h264.width || inh_ != h264.height) {
@@ -169,10 +252,12 @@
 	    else {
 			Decoder::redraw(xxx_frame);
 	    }
-        stream->clear();
+	    stream->clear();
 	    idx = seq+1;
-		     
     }
+
+    //===================================================
+
     pb->release();
 }
 

Modified: vic/branches/mpeg4/codec/encoder-h264.cpp
==============================================================================
--- vic/branches/mpeg4/codec/encoder-h264.cpp	(original)
+++ vic/branches/mpeg4/codec/encoder-h264.cpp	Fri Mar 23 18:11:29 2007
@@ -17,6 +17,10 @@
 #include "databuffer.h"
 #include "x264encoder.h"
 #include "deinterlace.h"
+/*extern "C"
+{
+#include "base64.h"
+}*/
 
 static Transmitter *tx;
 static RTP_BufferPool *pool;
@@ -119,14 +123,18 @@
 {
     pktbuf *pb;
     rtphdr *rh;
-    //int n,ps, len;
     ts = vf->ts_;
     tx = tx_;
     pool = pool_;
-    bool first = true;
 
-    int i_nal, i, sent_size;
+    int numNAL, i, sent_size = 0;
     int frame_size = 0;
+    unsigned char f_seq = 0;
+    //unsigned char f_total_pkt = 0;
+    int RTP_HDR_LEN = sizeof(rtphdr);
+    int NAL_FRAG_THRESH = tx->mtu() - RTP_HDR_LEN; /* payload max in one packet */
+    //fprintf(stderr, "MTU=%d, RTP_HDR_LEN=%d\n", NAL_FRAG_THRESH, RTP_HDR_LEN);
+
 
     tx->flush();
 
@@ -140,55 +148,93 @@
     }
 
     frame_size = vf->width_ * vf->height_;
-
     deinterlacer.render(vf->bp_, vf->width_, vf->height_);
-	
     enc->encodeFrame(vf->bp_);
+    numNAL = enc->numNAL();
 
-    i_nal = enc->numNAL();
+    //Send out numNAL packets framed according to RFC3984
+    for (i = 0; i < numNAL; i++) {
 
-    sent_size = 0;
-    unsigned char f_seq = 0;
-    unsigned char f_total_pkt = 0;
-    //TODO: send out i_nal packets
-    for (i = 0; i < i_nal; i++) {
+	bool firstFragment = true;
+	int FU_HDR_LEN = 1;
+	int offset = 0;
 
 	enc->getNALPacket(i, fOut);
-
-	sent_size += fOut->getDataSize();
+	int nalSize1 = fOut->getDataSize();
+	int nalSize = nalSize1-5;
+	char *data1 = fOut->getData();
 	char *data = fOut->getData();
+ 	uint8_t NALhdr = data1[4]; //SV-XXX why does our x.264 provide 4-byte StartSync in the NALU?
+	uint8_t NALtype = NALhdr & 0x1f;
+	//fprintf(stderr, "Got NALhdr=0x%02x, NALtype=0x%02x from encoded frame.\n", NALhdr, NALtype);
+	memcpy(data, &data1[5], nalSize);
+
+	sent_size += nalSize;
 
 
-	int nalSize = fOut->getDataSize();
-	if (i == i_nal - 1 && f_total_pkt == 0)
-	    f_total_pkt = f_seq + (nalSize / 1000) + 2;
-	int offset = 0;
 	while (nalSize > 0) {
-	    pb = pool_->alloc(vf->ts_, RTP_PT_H264);
+	    pb = pool->alloc(vf->ts_, RTP_PT_H264);
 	    rh = (rtphdr *) pb->data;
-	    if (nalSize > 1000) {
-		memcpy(&pb->data[14], data + offset, 1000);
 
-		pb->len = 1000 + 14;
-		offset += 1000;
-		nalSize -= 1000;
-	    }
-	    else {
-		if (i == i_nal - 1) {
-		    //Last Packet
-		    rh->rh_flags |= htons(RTP_M);	// set M bit
+	    if (nalSize <= NAL_FRAG_THRESH) {
+		//==============================================
+		//Single NAL or last fragment of FU-A
+		//==============================================
+
+		rh->rh_flags |= htons(RTP_M);	// set M bit
+		pb->len = nalSize + RTP_HDR_LEN + FU_HDR_LEN;
+
+		fprintf(stderr, "NAL : ");
+
+		if (FU_HDR_LEN==2) {
+			//==============================================
+			//Last fragment of FU-A
+			//==============================================
+	       		pb->data[12] = 0x00 | (NALhdr & 0x60) | 28; 	//FU indicator
+       			pb->data[13] = 0x40  | NALtype; 		//FU header
+			
+			fprintf(stderr, "FU_Indicator=0x%02x, FU_Header=0x%02x, ", pb->data[12], pb->data[13]);
+		} 
+		else {
+	       		pb->data[12] = NALhdr; 				//NAL Header
+			fprintf(stderr, "-----------------, --------------, ");
 		}
-		memcpy(&pb->data[14], data + offset, nalSize);
 
-		pb->len = nalSize + 14;
+		memcpy(&pb->data[RTP_HDR_LEN + FU_HDR_LEN], data + offset, nalSize);
+
+		fprintf(stderr, "i=%d/%d, nalSize=%4d len=%4d firstFrag=%d offset=%4d\n", i, numNAL, nalSize, pb->len, firstFragment, offset);
+
 		nalSize = 0;
+		offset = 0;
+
+	    } else {
+		//==============================================
+		//FU-A (not the last fragment though)
+		//==============================================
+
+		FU_HDR_LEN = 2;
+		pb->len = (NAL_FRAG_THRESH - FU_HDR_LEN) + RTP_HDR_LEN + FU_HDR_LEN;
+
+       		pb->data[12] = 0x00 | (NALhdr & 0x60) | 28; 			//FU indicator
+       		pb->data[13] = ( (firstFragment) ? 0x80 : 0x00 ) | NALtype;	//FU header
+
+		memcpy(&pb->data[RTP_HDR_LEN + FU_HDR_LEN], data + offset, NAL_FRAG_THRESH - FU_HDR_LEN);
+
+		fprintf(stderr, "FU-A: FU_Indicator=0x%02x, FU_Header=0x%02x, i=%d/%d, nalSize=%4d len=%4d firstFrag=%d offset=%4d\n",  pb->data[12], pb->data[13], i, numNAL, nalSize, pb->len, firstFragment, offset);
+
+		nalSize -= (NAL_FRAG_THRESH-FU_HDR_LEN);
+		offset += (NAL_FRAG_THRESH-FU_HDR_LEN);
+		firstFragment = false;
 	    }
-	    //printf("send out %d\n", pb->len);
+
 	    tx->send(pb);
 	    f_seq++;
 	}
+
     }
+
     frame_seq++;
 
+
     return (kbps*1024) / (fps*8);
 }

Modified: vic/branches/mpeg4/codec/packetbuffer.cpp
==============================================================================
--- vic/branches/mpeg4/codec/packetbuffer.cpp	(original)
+++ vic/branches/mpeg4/codec/packetbuffer.cpp	Fri Mar 23 18:11:29 2007
@@ -28,6 +28,13 @@
 	delete packets[i];
 }
 
+void PacketBuffer::writeAppend(int idx, int size, char *buffer)
+{
+    if (idx > MAX_PACKETS || idx < 0)
+	return;
+    isDataRecv[idx] = packets[idx]->writeAppend(buffer, size);
+}
+
 void PacketBuffer::write(int idx, int size, char *buffer)
 {
     if (idx > MAX_PACKETS || idx < 0)
@@ -35,13 +42,20 @@
     isDataRecv[idx] = packets[idx]->write(buffer, size);
 }
 
+void PacketBuffer::setSize(int idx, int size)
+{
+    if (idx > MAX_PACKETS || idx < 0)
+	return;
+    isDataRecv[idx] = packets[idx]->setSize(size);
+}
+
 bool PacketBuffer::isComplete()
 {
     if (totalPkts == 0)
 	return false;
     for (int i = 0; i < totalPkts; i++){	
 	if (!isDataRecv[i]){
-	    debug_msg("lost packet %d\n", i);	
+	    //debug_msg("lost packet %d\n", i);	
 	    return false;
 	}
     }

Modified: vic/branches/mpeg4/codec/packetbuffer.h
==============================================================================
--- vic/branches/mpeg4/codec/packetbuffer.h	(original)
+++ vic/branches/mpeg4/codec/packetbuffer.h	Fri Mar 23 18:11:29 2007
@@ -3,25 +3,29 @@
 
 #define MAX_PACKETS 2048
 
+#include "databuffer.h"
 
-class DataBuffer;
+
+//class DataBuffer;
 class PacketBuffer
 {
   public:
     PacketBuffer(int, int);	//maxPackets, maxLength
        ~PacketBuffer();
     void write(int, int, char *);	//pktIdx, length, buffer
+    void writeAppend(int, int, char *);	//pktIdx, length, buffer
+    void setSize(int, int);	//pktIdx, length
     void setTotalPkts(int);
     int getTotalPkts();
     bool isComplete();
     DataBuffer *getStream();
     void clear();
+    DataBuffer *packets[MAX_PACKETS];
   private:
     int totalPkts;
     int maxPkts;
     int maxLen;
     DataBuffer *stream;
-    DataBuffer *packets[MAX_PACKETS];
     bool isDataRecv[MAX_PACKETS];
 };
 

Added: vic/branches/mpeg4/codec/rtp_h264_depayloader.cpp
==============================================================================
--- (empty file)
+++ vic/branches/mpeg4/codec/rtp_h264_depayloader.cpp	Fri Mar 23 18:11:29 2007
@@ -0,0 +1,601 @@
+/*
+ * RTP H264 Protocol (RFC3984)
+ * Copyright (c) 2006 Ryan Martell.
+ *
+ * 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
+ */
+
+/**
+* @file rtp_h264.c
+ * @brief H.264 / RTP Code (RFC3984)
+ * @author Ryan Martell <rdm4 at martellventures.com>
+ *
+ * @note Notes:
+ * Notes:
+ * This currently supports packetization mode:
+ * Single Nal Unit Mode (0), or
+ * Non-Interleaved Mode (1).  It currently does not support
+ * Interleaved Mode (2). (This requires implementing STAP-B, MTAP16, MTAP24, FU-B packet types)
+ *
+ * @note TODO:
+ * 1) RTCP sender reports for udp streams are required..
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "config.h"
+
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <assert.h>
+//#ifndef __BEOS__
+# include <arpa/inet.h>
+//#else
+//# include "barpainet.h"
+//#endif
+#include <netdb.h>
+
+#include "rtp_h264_depayloader.h"
+#include "packetbuffer.h"
+
+using namespace std;
+
+
+
+
+/**
+ * Return TRUE if val is a prefix of str. If it returns TRUE, ptr is
+ * set to the next character in 'str' after the prefix.
+ *
+ * @param str input string
+ * @param val prefix to test
+ * @param ptr updated after the prefix in str in there is a match
+ * @return TRUE if there is a match
+ */
+int strstart(const char *str, const char *val, const char **ptr)
+{
+    const char *p, *q;
+    p = str;
+    q = val;
+    while (*q != '\0') {
+        if (*p != *q)
+            return 0;
+        p++;
+        q++;
+    }
+    if (ptr)
+        *ptr = p;
+    return 1;
+}
+
+
+//helpers from libavformat/utils.c =============================================
+/**
+ * Default packet destructor.
+ */
+/*void av_destruct_packet(AVPacket *pkt)
+{
+    av_free(pkt->data);
+    pkt->data = NULL; pkt->size = 0;
+}
+*/
+
+/**
+ * Allocate the payload of a packet and intialized its fields to default values.
+ *
+ * @param pkt packet
+ * @param size wanted payload size
+ * @return 0 if OK. AVERROR_xxx otherwise.
+ */
+/*
+int av_new_packet(AVPacket *pkt, int size)
+{
+    uint8_t *data;
+    if((unsigned)size > (unsigned)size + FF_INPUT_BUFFER_PADDING_SIZE)
+        return AVERROR_NOMEM;
+    data = (uint8_t *) av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
+    if (!data)
+        return AVERROR_NOMEM;
+    memset(data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+
+    av_init_packet(pkt);
+    pkt->data = data;
+    pkt->size = size;
+    pkt->destruct = av_destruct_packet;
+    return 0;
+}
+*/
+
+//helpers from rtsp.c =============================================
+static int redir_isspace(int c)
+{
+    return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
+}
+
+static void skip_spaces(const char **pp)
+{
+    const char *p;
+    p = *pp;
+    while (redir_isspace(*p))
+        p++;
+    *pp = p;
+}
+
+static void get_word_sep(char *buf, int buf_size, const char *sep,
+                         const char **pp)
+{
+    const char *p;
+    char *q;
+
+    p = *pp;
+    if (*p == '/')
+        p++;
+    skip_spaces(&p);
+    q = buf;
+    while (!strchr(sep, *p) && *p != '\0') {
+        if ((q - buf) < buf_size - 1)
+            *q++ = *p;
+        p++;
+    }
+    if (buf_size > 0)
+        *q = '\0';
+    *pp = p;
+}
+//===================================================================
+
+/** parse the attribute line from the fmtp a line of an sdp resonse.  This is broken out as a function
+* because it is used in rtp_h264.c, which is forthcoming.
+*/
+int rtsp_next_attr_and_value(const char **p, char *attr, int attr_size, char *value, int value_size)
+{
+    skip_spaces(p);
+    if(**p)
+    {
+        get_word_sep(attr, attr_size, "=", p);
+        if (**p == '=')
+            (*p)++;
+        get_word_sep(value, value_size, ";", p);
+        if (**p == ';')
+            (*p)++;
+        return 1;
+    }
+    return 0;
+}
+
+
+
+
+
+H264Depayloader::H264Depayloader()
+{
+    h264_extradata = (h264_rtp_extra_data *) h264_new_extradata();
+    //fprintf(stderr, "H264_RTP: H264Depayloader Constructor done.\n");
+}
+
+H264Depayloader::~H264Depayloader()
+{
+    h264_free_extradata(h264_extradata);
+    //fprintf(stderr, "H264_RTP: H264Depayloader Destructor done.\n");
+}
+
+
+
+
+/* ---------------- private code */
+void H264Depayloader::sdp_parse_fmtp_config_h264(AVCodecContext *codec, /*AVStream * stream,*/
+                                       h264_rtp_extra_data * h264_data,
+                                       char *attr, char *value)
+{
+    //AVCodecContext *codec = stream->codec;
+    assert(codec->codec_id == CODEC_ID_H264);
+    assert(h264_data != NULL);
+
+    if (!strcmp(attr, "packetization-mode")) {
+        //fprintf(stderr, /*NULL, AV_LOG_DEBUG,*/ "H.264/RTP Packetization Mode: %d\n", atoi(value));
+        h264_data->packetization_mode = atoi(value);
+        /*
+           Packetization Mode:
+           0 or not present: Single NAL mode (Only nals from 1-23 are allowed)
+           1: Non-interleaved Mode: 1-23, 24 (STAP-A), 28 (FU-A) are allowed.
+           2: Interleaved Mode: 25 (STAP-B), 26 (MTAP16), 27 (MTAP24), 28 (FU-A), and 29 (FU-B) are allowed.
+         */
+        if (h264_data->packetization_mode > 1)
+            fprintf(stderr, /*stream, AV_LOG_ERROR,*/
+                   "H.264/RTP: Interleaved RTP mode is not supported yet.");
+    } else if (!strcmp(attr, "profile-level-id")) {
+        if (strlen(value) == 6) {
+            char buffer[3];
+            // 6 characters=3 bytes, in hex.
+            uint8_t profile_idc;
+            uint8_t profile_iop;
+            uint8_t level_idc;
+
+            buffer[0] = value[0]; buffer[1] = value[1]; buffer[2] = '\0';
+            profile_idc = strtol(buffer, NULL, 16);
+            buffer[0] = value[2]; buffer[1] = value[3];
+            profile_iop = strtol(buffer, NULL, 16);
+            buffer[0] = value[4]; buffer[1] = value[5];
+            level_idc = strtol(buffer, NULL, 16);
+
+            // set the parameters...
+            //fprintf(stderr, /* NULL, AV_LOG_DEBUG,*/ "H.264/RTP Profile IDC: %x Profile IOP: %x Level: %x\n", profile_idc, profile_iop, level_idc);
+            h264_data->profile_idc = profile_idc;
+            h264_data->profile_iop = profile_iop;
+            h264_data->level_idc = level_idc;
+        }
+    } else  if (!strcmp(attr, "sprop-parameter-sets")) {
+        uint8_t start_sequence[]= { 0, 0, 1 };
+        codec->extradata_size= 0;
+        codec->extradata= NULL;
+
+        while (*value) {
+            char base64packet[1024];
+            uint8_t decoded_packet[1024];
+            uint32_t packet_size;
+            char *dst = base64packet;
+
+            while (*value && *value != ','
+                   && (dst - base64packet) < sizeof(base64packet) - 1) {
+                *dst++ = *value++;
+            }
+            *dst++ = '\0';
+
+            if (*value == ',')
+                value++;
+
+            packet_size= base64decode((const unsigned char *)base64packet, dst-base64packet-1, decoded_packet, sizeof(decoded_packet));
+            if (packet_size) {
+                uint8_t *dest= (uint8_t *) av_malloc(packet_size+sizeof(start_sequence)+codec->extradata_size);
+                if(dest)
+                {
+                    if(codec->extradata_size)
+                    {
+                        // av_realloc?
+                        memcpy(dest, codec->extradata, codec->extradata_size);
+                        av_free(codec->extradata);
+                    }
+
+                    memcpy(dest+codec->extradata_size, start_sequence, sizeof(start_sequence));
+                    memcpy(dest+codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size);
+
+                    codec->extradata= dest;
+                    codec->extradata_size+= sizeof(start_sequence)+packet_size;
+                } else {
+                    fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H.264/RTP Unable to allocate memory for extradata!");
+                }
+            }
+        }
+        //fprintf(stderr, /*NULL, AV_LOG_DEBUG,*/ "H.264/RTP Extradata set to %p (size: %d)!\n", codec->extradata, codec->extradata_size);
+    }
+}
+
+// return 0 on packet, no more left, 1 on packet, 1 on partial packet...
+int H264Depayloader::h264_handle_packet(h264_rtp_extra_data *data,
+                              /*AVPacket * pkt,*/ int pktIdx, PacketBuffer *pb,
+                              /*uint32_t * timestamp,*/ 
+                              const uint8_t * buf,
+                              int len)
+{
+
+     char *temp; //SV: XXX
+
+    //h264_rtp_extra_data *data = s->dynamic_protocol_context;
+    uint8_t nal = buf[0];
+    uint8_t type = (nal & 0x1f);
+    int result= 0;
+    char start_sequence[]= {0, 0, 1};
+    int sslen = sizeof(start_sequence);
+
+    assert(data);
+    assert(data->cookie == MAGIC_COOKIE);
+    assert(buf);
+
+    if (type >= 1 && type <= 23)
+        type = 1;              // simplify the case. (these are all the nal types used internally by the h264 codec)
+    switch (type) {
+    case 0:                    // undefined;
+        fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H.264/RTP: Undefined NAL type (%d)\n", type);
+        result= -1;
+        break;
+
+    case 1:
+	//fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H264_RTP: Single NAL type (%d, equiv. to >=1 or <=23), len=%4d\n", type, len);
+
+        //av_new_packet(pkt, len+sizeof(start_sequence));
+        //memcpy(pkt->data, start_sequence, sizeof(start_sequence));
+        //memcpy(pkt->data+sizeof(start_sequence), buf, len);
+
+	//SV: XXX
+/*
+	temp = (char *) malloc(sslen+len);
+        assert(temp);
+        memcpy(temp, start_sequence, sslen);
+        memcpy(temp+sslen, buf, len);
+
+	pb->write(pktIdx, sslen+len, temp); //SV: XXX
+*/	
+
+	pb->writeAppend(pktIdx, sslen, start_sequence); //PO: XXX
+	pb->writeAppend(pktIdx, len, (char *)buf); //PO: XXX
+	
+
+#ifdef DEBUG
+        data->packet_types_received[nal & 0x1f]++;
+#endif
+        break;
+
+    case 24:                   // STAP-A (one packet, multiple nals)
+        // consume the STAP-A NAL
+	//fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H264_RTP: STAP-A NAL type (%d)\n", type);
+
+        buf++;
+        len--;
+        // first we are going to figure out the total size....
+        {
+            int pass= 0;
+            int total_length= 0;
+            uint8_t *dst= NULL;
+
+            for(pass= 0; pass<2; pass++) {
+                const uint8_t *src= buf;
+                int src_len= len;
+
+                do {
+                    uint16_t nal_size = BE_16(src); // this going to be a problem if unaligned (can it be?)
+
+                    // consume the length of the aggregate...
+                    src += 2;
+                    src_len -= 2;
+
+                    if (nal_size <= src_len) {
+                        if(pass==0) {
+                            // counting...
+                            total_length+= sizeof(start_sequence)+nal_size;
+                        } else {
+                            // copying
+                            assert(dst);
+                            memcpy(dst, start_sequence, sizeof(start_sequence));
+                            dst+= sizeof(start_sequence);
+                            memcpy(dst, src, nal_size);
+#ifdef DEBUG
+                            data->packet_types_received[*src & 0x1f]++;
+#endif
+                            dst+= nal_size;
+                        }
+                    } else {
+                        //fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H264_RTP: nal size exceeds length: %d %d\n", nal_size, src_len);
+                    }
+
+                    // eat what we handled...
+                    src += nal_size;
+                    src_len -= nal_size;
+
+                    if (src_len < 0) {
+			;
+                        //fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H264_RTP: Consumed more bytes than we got! (%d)\n", src_len);
+		   }
+                } while (src_len > 2);      // because there could be rtp padding..
+
+                if(pass==0) {
+                    // now we know the total size of the packet (with the start sequences added)
+
+                    //SV: XXX
+                    //av_new_packet(pkt, total_length);
+                    //dst= pkt->data;
+
+                    //temp = (char *) malloc(total_length);
+                    pb->setSize(pktIdx,total_length); //PO:
+                    dst = (uint8_t *) pb->packets[pktIdx]->data;
+
+                } else {
+                    //SV: XXX
+                    //assert(dst-pkt->data==total_length);
+                    assert(dst-(uint8_t *)(pb->packets[pktIdx]->data) == total_length);
+                }
+            }
+        }
+        break;
+
+    case 25:                   // STAP-B
+    case 26:                   // MTAP-16
+    case 27:                   // MTAP-24
+    case 29:                   // FU-B
+        //fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H.264/RTP: Unhandled type (%d) (See RFC for implementation details\n", type);
+        result= -1;
+        break;
+
+    case 28:                   // FU-A (fragmented nal)
+
+        buf++;
+        len--;                  // skip the fu_indicator
+        {
+            // these are the same as above, we just redo them here for clarity...
+            uint8_t fu_indicator = nal;
+            uint8_t fu_header = *buf;   // read the fu_header.
+            uint8_t start_bit = (fu_header & 0x80) >> 7;
+            uint8_t end_bit = (fu_header & 0x40) >> 6;
+            uint8_t nal_type = (fu_header & 0x1f);
+            uint8_t reconstructed_nal;
+
+            // reconstruct this packet's true nal; only the data follows..
+            reconstructed_nal = fu_indicator & (0xe0);  // the original nal forbidden bit and NRI are stored in this packet's nal;
+            reconstructed_nal |= (nal_type & 0x1f);
+
+            // skip the fu_header...
+            buf++;
+            len--;
+
+#ifdef DEBUG
+            if (start_bit)
+                data->packet_types_received[nal_type & 0x1f]++;
+#endif
+            if(start_bit) {
+                // copy in the start sequence, and the reconstructed nal....
+
+                //SV: XXX
+                //av_new_packet(pkt, sizeof(start_sequence)+sizeof(nal)+len);
+                //memcpy(pkt->data, start_sequence, sizeof(start_sequence));
+                //pkt->data[sizeof(start_sequence)]= reconstructed_nal;
+                //memcpy(pkt->data+sizeof(start_sequence)+sizeof(nal), buf, len);
+/*
+		//SV: XXX
+		temp = (char *) malloc(sizeof(start_sequence)+sizeof(nal)+len);
+        	memcpy(temp, start_sequence, sizeof(start_sequence));
+		temp[sizeof(start_sequence)] = reconstructed_nal;
+        	memcpy(temp+sizeof(start_sequence)+sizeof(nal), buf, len);
+
+		pb->write(pktIdx, len+sizeof(start_sequence)+sizeof(nal), temp); //SV: XXX
+*/
+	pb->writeAppend(pktIdx, sizeof(start_sequence), start_sequence); //PO: XXX
+	pb->writeAppend(pktIdx, sizeof(nal), (char*)&reconstructed_nal); //PO: XXX
+	pb->writeAppend(pktIdx, len, (char *)buf); //PO: XXX
+
+            } else {
+                //SV: XXX
+                //av_new_packet(pkt, len);
+                //memcpy(pkt->data, buf, len);
+
+		//temp = (char *) malloc(len);
+        	//memcpy(temp, buf, len);
+
+		pb->write(pktIdx, len, (char*)buf); //PO: XXX
+            }
+
+	    //fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H264_RTP: FU-A NAL type (%d): start_bit=%d, end_bit=%d, NAL_Header=0x%02x, FU_Header=0x%02x\n", type, start_bit, end_bit, reconstructed_nal, fu_header);
+
+        }
+        break;
+
+    case 30:                   // undefined
+    case 31:                   // undefined
+    default:
+        fprintf(stderr, /*NULL, AV_LOG_ERROR,*/ "H.264/RTP: Undefined NAL type (%d)\n", type);
+        result= -1;
+        break;
+    }
+
+    return result;
+}
+
+/* ---------------- public code */
+void *H264Depayloader::h264_new_extradata()
+{
+    h264_rtp_extra_data *data = (h264_rtp_extra_data *) av_mallocz(sizeof(h264_rtp_extra_data) +
+                   FF_INPUT_BUFFER_PADDING_SIZE);
+
+    if (data) {
+        data->cookie = MAGIC_COOKIE;
+    }
+
+    return data;
+}
+
+void H264Depayloader::h264_free_extradata(void *d)
+{
+    h264_rtp_extra_data *data = (h264_rtp_extra_data *) d;
+#ifdef DEBUG
+    int ii;
+
+    for (ii = 0; ii < 32; ii++) {
+        if (data->packet_types_received[ii])
+            fprintf(stderr, /*NULL, AV_LOG_DEBUG,*/ "Received %d packets of type %d\n",
+                   data->packet_types_received[ii], ii);
+    }
+#endif
+
+    assert(data);
+    assert(data->cookie == MAGIC_COOKIE);
+
+    // avoid stale pointers (assert)
+    data->cookie = DEAD_COOKIE;
+
+    // and clear out this...
+    av_free(data);
+}
+
+int H264Depayloader::parse_h264_sdp_line(AVCodecContext *codec, /*AVStream * stream,*/ void *data,
+                               const char *line)
+{
+    //AVCodecContext *codec = stream->codec;
+    h264_rtp_extra_data *h264_data = (h264_rtp_extra_data *) data;
+    const char *p = line;
+
+    assert(h264_data->cookie == MAGIC_COOKIE);
+
+    if (strstart(p, "a=framesize:", &p)) {
+        char buf1[50];
+        char *dst = buf1;
+
+        // remove the protocol identifier..
+        while (*p && *p == ' ') p++; // strip spaces.
+        while (*p && *p != ' ') p++; // eat protocol identifier
+        while (*p && *p == ' ') p++; // strip trailing spaces.
+        while (*p && *p != '-' && (buf1 - dst) < sizeof(buf1) - 1) {
+            *dst++ = *p++;
+        }
+        *dst = '\0';
+
+        // a='framesize:96 320-240'
+        // set our parameters..
+        codec->width = atoi(buf1);
+        codec->height = atoi(p + 1); // skip the -
+        codec->pix_fmt = PIX_FMT_YUV420P;
+    } else if (strstart(p, "a=fmtp:", &p)) { //SV: wtf!!!
+        char attr[256];
+        char value[4096];
+
+        // remove the protocol identifier..
+        while (*p && *p == ' ') p++; // strip spaces.
+        while (*p && *p != ' ') p++; // eat protocol identifier
+        while (*p && *p == ' ') p++; // strip trailing spaces.
+
+        /* loop on each attribute */
+        while (rtsp_next_attr_and_value
+               (&p, attr, sizeof(attr), value, sizeof(value))) {
+            /* grab the codec extra_data from the config parameter of the fmtp line */
+	    //fprintf(stderr, "==========> attr=%s, value=%s \n", attr, value);
+            sdp_parse_fmtp_config_h264(codec, /* stream, */ h264_data, attr, value);
+        }
+    } else if (strstart(p, "cliprect:", &p)) {
+        // could use this if we wanted.
+    }
+
+    //av_set_pts_info(stream, 33, 1, 90000);      // 33 should be right, because the pts is 64 bit? (done elsewhere; this is a one time thing)
+
+    return 0;                   // keep processing it the normal way...
+}
+
+/**
+This is the structure for expanding on the dynamic rtp protocols (makes everything static. yay!)
+*/
+/*
+RTPDynamicProtocolHandler ff_h264_dynamic_handler = {
+    "H264",
+    CODEC_TYPE_VIDEO,
+    CODEC_ID_H264,
+    parse_h264_sdp_line,
+    h264_new_extradata,
+    h264_free_extradata,
+    h264_handle_packet
+};
+*/

Added: vic/branches/mpeg4/codec/rtp_h264_depayloader.h
==============================================================================
--- (empty file)
+++ vic/branches/mpeg4/codec/rtp_h264_depayloader.h	Fri Mar 23 18:11:29 2007
@@ -0,0 +1,98 @@
+/*
+ * RTP H264 Protocol (RFC3984)
+ * Copyright (c) 2006 Ryan Martell.
+ *
+ * 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
+ */
+
+#ifndef RTP_H264DEPAYLOADER_H
+#define RTP_H264DEPAYLOADER_H
+
+extern "C"
+{
+#include "ffmpeg/avcodec.h"
+#include "ffmpeg/avformat.h"
+#include "base64.h"
+#include "packetbuffer.h"
+//#include "rtp_internal.h"
+//#include "mpegts.h"
+//#include "bitstream.h"
+}
+
+int strstart(const char *str, const char *val, const char **ptr);
+int rtsp_next_attr_and_value(const char **p, char *attr, int attr_size, char *value, int value_size);
+
+
+
+#if !defined(BE_16) || !defined(BE_32) || !defined(LE_16) || !defined(LE_32)
+#define BE_16(x)  ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1])
+#define BE_32(x)  ((((uint8_t*)(x))[0] << 24) | \
+                   (((uint8_t*)(x))[1] << 16) | \
+                   (((uint8_t*)(x))[2] << 8) | \
+                    ((uint8_t*)(x))[3])
+#define LE_16(x)  ((((uint8_t*)(x))[1] << 8) | ((uint8_t*)(x))[0])
+#define LE_32(x)  ((((uint8_t*)(x))[3] << 24) | \
+                   (((uint8_t*)(x))[2] << 16) | \
+                   (((uint8_t*)(x))[1] << 8) | \
+                    ((uint8_t*)(x))[0])
+#endif
+
+
+
+#define MAGIC_COOKIE (0xdeadbeef)       ///< Cookie for the extradata; to verify we are what we think we are, and that we haven't been freed.
+#define DEAD_COOKIE (0xdeaddead)        ///< Cookie for the extradata; once it is freed.
+
+
+typedef struct h264_rtp_extra_data {
+    unsigned long cookie;       ///< sanity check, to make sure we get the pointer we're expecting.
+
+    //sdp setup parameters
+    uint8_t profile_idc;        ///< from the sdp setup parameters.
+    uint8_t profile_iop;        ///< from the sdp setup parameters.
+    uint8_t level_idc;          ///< from the sdp setup parameters.
+    int packetization_mode;     ///< from the sdp setup parameters.
+#ifdef DEBUG
+    int packet_types_received[32];
+#endif
+} h264_rtp_extra_data;
+
+
+class H264Depayloader
+{
+  public:
+    H264Depayloader();
+    ~H264Depayloader();
+    
+    h264_rtp_extra_data *h264_extradata;
+
+    int parse_h264_sdp_line(AVCodecContext *codec, /*AVStream * stream,*/ void *data, const char *line);
+    int h264_handle_packet(h264_rtp_extra_data *data, /*AVPacket * pkt*/ int pktIdx, PacketBuffer *pb, /*uint32_t * timestamp,*/ const uint8_t * buf, int len);
+
+/**
+    RTP/H264 specific private data.
+*/
+  private:
+    void h264_free_extradata(void *d);
+    void *h264_new_extradata();
+    void sdp_parse_fmtp_config_h264(AVCodecContext *codec, /*AVStream * stream,*/
+                                       h264_rtp_extra_data * h264_data,
+                                       char *attr, char *value);
+};
+
+
+//extern RTPDynamicProtocolHandler ff_h264_dynamic_handler;
+#endif /* RTP_H264DEPAYLOADER_H */

Modified: vic/branches/mpeg4/rtp/rtp.h
==============================================================================
--- vic/branches/mpeg4/rtp/rtp.h	(original)
+++ vic/branches/mpeg4/rtp/rtp.h	Fri Mar 23 18:11:29 2007
@@ -61,7 +61,7 @@
 #define RTP_PT_H263		34 	/* ITU H.263 */
 #define RTP_PT_DV               123     /* DV */
 #define RTP_PT_MPEG4		45
-#define RTP_PT_H264		47
+#define RTP_PT_H264		96
 
 /* backward compat hack for decoding RTPv1 ivs streams */
 #define RTP_PT_H261_COMPAT 127

Modified: vic/branches/mpeg4/tcl/cf-main.tcl
==============================================================================
--- vic/branches/mpeg4/tcl/cf-main.tcl	(original)
+++ vic/branches/mpeg4/tcl/cf-main.tcl	Fri Mar 23 18:11:29 2007
@@ -49,7 +49,7 @@
 set rtp_type(77) h261as
 set rtp_type(123) dv
 set rtp_type(45) mpeg4
-set rtp_type(47) h264
+set rtp_type(96) h264
 
 
 proc vic_main {} {



More information about the Sumover-dev mailing list