Appendix C contains the code listing for the server
program. A description of this program can be found in Chapter
5.
//****************************************************************
// Author: Hu Imm Lee
// Filename: server.h
// File Created: February 11, 1998
//
// Comments:
// Contains the global settings for the server.
// Include this file before everything else.
//
//****************************************************************
// system start codes
#define PACK_START_CODE 0x000001ba
#define SYSTEM_HEADER_START_CODE 0x000001bb
#define ISO_11172_END_CODE 0x000001b9
// video start codes
#define PICTURE_START_CODE 0x00000100
#define SEQUENCE_HEADER_CODE 0x000001b3
#define SEQUENCE_END_CODE 0x000001b7
#define GROUP_START_CODE 0x000001b8
#define DEFAULT_VID_BITRATE 565000 // in bits per second
#define DEFAULT_FRAMERATE 29.97 // Broadcast rate
NTSC (fps)
// frame/packet types
#define CONTROL 0
#define I_FRAME 1
#define P_FRAME 2
#define B_FRAME 3
#define HEADER 4
// packet controls
#define RESPONSE 6 // response from a retransmission request
#define RETRANS 7 // retransmission request
#define TERMINAL 8 // signal to end a transmission
#define PINGREQ 9 // request a ping service
#define PINGRES 10 // respond to a ping
#define INVALID 0 // indicates that packet not
control type
// TCP packet headers
#define REQUEST 5 // requesting a connection
#define RATE 11
// TEMPORARILY hard-coded:
// UDP-related stuff
#define MYUDPPORT 8080
#define MYTCPPORT 8181
#define MYPINGPORT 8282
#define MAXPAYLOAD 2048 // UDP packet's payload
max 2K
#define DEFAULT_BUFSIZE 100 // retransmission buffer
typedef struct UDP_packet {
WORD seqnum; // NOTE: need to reinitialize seqnum if seqnum >= 2^16-1
WORD pkt_type;
WORD control;
WORD f_parts[2]; // f_parts[0] = order of frame pkt
//f_parts[1] = total number of pkts per frame
WORD pl_size; // payload size
BYTE payload[MAXPAYLOAD];
} UDP_PACKET;
typedef struct TCP_packet {
int pkt_type;
double data;
struct sockaddr_in udpclientaddr;
} TCP_PACKET;
typedef struct PING_packet {
WORD pkt_type;
WORD seqnum;
WORD control;
DWORD timestamp;
} PING_PACKET;
typedef struct Dummy {
int seqnum;
int pkt_type;
BYTE payload[MAXPAYLOAD];
} DUMMY_PACKET;
struct StreamNode {
UDP_PACKET NodeItem;
struct StreamNode* pNextNode;
}; // Node used in PacketStream
//****************************************************************
// Author: Hu Imm Lee
// Filename: main.cpp
//
// The main function along with its threads
//****************************************************************
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include "server.h"
#include "MPEGparser.h"
#include "NetInterface.h"
#include "PacketStream.h"
#include "RetransmissionBuffer.h"
#include "SystemUtility.h"
long vid_bitrate = DEFAULT_VID_BITRATE;
double framerate = DEFAULT_FRAMERATE;
HANDLE gh_packetizer;
HANDLE gh_packetsender;
HANDLE gh_retransmitter;
HANDLE gh_pingServer;
char g_filename[20]; // name of MPEG file (input)
int curpkt_id;
int oldpkt_id = -1;
int packets = 0; // tracks # of packets sent
extern enum M_Choice {
M_FILE,
M_STREAM,
M_NONE
};
void PrintWin32Error(char *);
void choose();
DWORD __stdcall packetsenderThread(LPVOID);
DWORD __stdcall packetizerThread(LPVOID);
DWORD __stdcall retransmissionThread(LPVOID);
DWORD __stdcall pingServerThread(LPVOID);
DWORD __stdcall TerminationThread(LPVOID);
NetInterface* pNetI = new NetInterface();
RetransmissionBuffer* pRetBuf = new RetransmissionBuffer(DEFAULT_BUFSIZE);
MPEGparser* pMpeg = NULL;
void main() {
// instantiating PacketStream class
PacketStream* pStream = new PacketStream();
HANDLE h_packetizer; // thread packetizer's handle (unsigned long)
DWORD packetizerThreadId; // packetizer's id
LPDWORD lpExitCode_packetizer = new DWORD;
HANDLE h_packetsender; // thread packetsender's handle
DWORD packetsenderThreadId; // packetsender's id
LPDWORD lpExitCode_packetsender = new DWORD;
HANDLE h_retransmitter; // thread retransmitter's handle
DWORD retransmissionThreadId; // retransmitter's id
LPDWORD lpExitCode_retransmitter = new DWORD;
HANDLE h_pingServer; // thread retransmitter's handle
DWORD pingServerThreadId; // retransmitter's id
LPDWORD lpExitCode_pingServer = new DWORD;
BOOL done = FALSE;
printf("TIMER-CONTROLLED SERVER BEGINS: \n");
// initialize network settings before starting up the threads
if (!pNetI->net_init()) {
delete pNetI;
return; // return if failed to initialize
}
// start the packetizer thread
h_packetizer = CreateThread(
NULL, // security
0, // stack size
packetizerThread, // thread routine
(LPVOID) pStream, // pointer to PacketStream class
CREATE_SUSPENDED, // suspend thread to prevent filling up
// the packet stream before a client connects
&packetizerThreadId); // thread id
if (h_packetizer == NULL) {
printf("MAIN: ERR[%d] on CreateThread(packetizer)\n",
GetLastError());
PrintWin32Error("CreateThread: packetizerThread");
exit(0);
}
else gh_packetizer = h_packetizer; // assign to
a global handle
// start the packetsender thread
h_packetsender = CreateThread(
NULL,
0,
packetsenderThread,
(LPVOID) pStream, //** NOTE: both threads share one resource,
// the PacketStream class
0, // run immediately
&packetsenderThreadId);
if (h_packetsender == NULL) {
printf("MAIN: ERR[%d] on CreateThread(packetsender)\n",
GetLastError());
PrintWin32Error("CreateThread: packetsenderThread");
exit(0);
}
else gh_packetsender = h_packetsender; // assign
to a global handle
h_retransmitter = CreateThread(
NULL,
0,
retransmissionThread,
NULL,
0, // run immediately
&retransmissionThreadId);
if (h_retransmitter == NULL) {
printf("MAIN: ERR[%d] on CreateThread(retransmission)\n",
GetLastError());
PrintWin32Error("CreateThread: retransmissionThread");
exit(0);
}
else
gh_retransmitter = h_retransmitter; // assign to
a global handle
h_pingServer = CreateThread(
NULL,
0,
pingServerThread,
NULL,
0, // run immediately
&pingServerThreadId);
if (h_pingServer == NULL) {
printf("MAIN: ERR[%d] on CreateThread(pingServer)\n",
GetLastError());
PrintWin32Error("CreateThread: pingServerThread");
exit(0);
}
else
gh_pingServer = h_pingServer; // assign to a global
handle
// wait for all threads to finish
while (!done) {
if (GetExitCodeThread(gh_packetizer, lpExitCode_packetizer)
== 0) {
printf("GetExitCodeThread(packetizer) ERR[%d]\n",
GetLastError() );
break;
}
if (GetExitCodeThread(gh_packetsender,
lpExitCode_packetsender) == 0) {
printf("GetExitCodeThread(packetsender) ERR[%d]\n",
GetLastError() );
break;
}
if (GetExitCodeThread(gh_retransmitter,
lpExitCode_retransmitter) == 0) {
PrintWin32Error("GetExitCodeThread(retransmitter)");
break;
}
if (GetExitCodeThread(gh_pingServer, lpExitCode_pingServer)
== 0) {
PrintWin32Error("GetExitCodeThread(pingServer)");
break;
}
if ( (*lpExitCode_packetizer != STILL_ACTIVE) &&
(*lpExitCode_packetsender != STILL_ACTIVE) &&
(*lpExitCode_retransmitter != STILL_ACTIVE)
done = TRUE;
}
}
void PrintWin32Error(char *pszErrorString) {
LPVOID lpMsgBuf;
printf("Error: returned from [%s]\n",
pszErrorString);
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&lpMsgBuf,
0,
NULL))
printf("Error Message: %s\n", lpMsgBuf);
}
//****************************************************************
// CHOOSE
// accesory to let user input an option
//****************************************************************
void choose() {
int option;
begin:
printf("\n************************************************\n");
printf("Choose an input option (by number):\n");
printf("1. File\n2. Capture board\n");
printf("Choose: ");
scanf("%d", &option);
printf("**************************************************\n");
switch (option) {
case 1:
// get user input for MPEG file to stream
printf("Enter MPEG filename \".mpg\": ");
scanf("%s", &g_filename);
pMpeg = new MPEGparser(M_FILE);
break;
case 2:
pMpeg = new MPEGparser(M_STREAM);
break;
default:
printf("Incorrect input choice. Please try again\n\n");
goto begin;
}
}
//****************************************************************
// PACKET SENDER THREAD -
// Thread Routine, called by CreateThread in main()
// Syntax
// + CreateThread(NULL,
// 0,
// packetsenderThread, // this routine
// pSystem. // pointer to PacketStream class object
// 0,
// &dwThreadId);
// + lpdwThreadParam = pStream
//****************************************************************
DWORD __stdcall packetsenderThread(LPVOID lpdwThreadParam)
{
// PacketStream stuff
PacketStream* pStream = (PacketStream*) lpdwThreadParam;
BOOL Success;
UDP_PACKET* pFrontpkt = new UDP_PACKET;
LPDWORD lpExitCode_packetizer = new DWORD;
double inv_transmit_rate; //inverse packet rate
printf("\nPacket Sender Thread running\n");
// determine transmission rate (seconds per packet)
inv_transmit_rate = 1/framerate * 1000;
printf("inversed transmission rate: %ld millisec/pkt\n",
(DWORD)inv_transmit_rate);
char choice;
printf("\t Change transmission rate? \n\t1 for yes\n");
scanf("%c", &choice);
if (choice == '1') {
printf("Enter (in pkt/sec) float value: ");
scanf("%lf", &framerate);
inv_transmit_rate = (double)1/(double)framerate * 1000;
printf("inversed transmission rate: %ld millisec/pkt\n",
(DWORD)inv_transmit_rate);
}
// wait for client connection request
if (!pNetI->WaitForRequest() ) {
delete pNetI;
return (0); // if not OK
}
// send transmission rate to client
TCP_PACKET rate_pkt;
ZeroMemory(&rate_pkt, sizeof(TCP_PACKET));
rate_pkt.pkt_type = RATE;
rate_pkt.data = framerate;
pNetI->tcpsend(&rate_pkt);
// resume execution of packetizerThread after WaitForRequest falls thru
if ( ResumeThread(gh_packetizer) == 0xffffffff ) {
printf("packetsenderThread: ResumeThread fail ERR[%d]\n",
GetLastError() );
return (0);
}
printf("PacketSender: ResumeThread on packetizer
success\n");
// check that packetizer thread has/ has not exited
if ( ! GetExitCodeThread(gh_packetizer, lpExitCode_packetizer) ) {
printf("ExitCode Error %d\n", GetLastError() );
return (0);
}
// if packet stream is not empty, send
// if packetizer thread is still active (packet stream may be empty)
// wait for it
while ( !pStream->StreamIsEmpty() ||
(*lpExitCode_packetizer==STILL_ACTIVE) ) {
// while there is something to send
if ( !pStream->StreamIsEmpty() ) { // if there's
something to send
pStream->GetFrontPacket(*pFrontpkt, Success);
if (!Success)
break; // failure to retrieve packet from stream
pStream->RemovePacket(Success); // pop off the Q front
if (!Success)
break;
pRetBuf->add(pFrontpkt); // add the packet into the
// retransmission buffer
if ( !pNetI->sendpacket(pFrontpkt) ) // send the packet
break; // get out of loop if sendpacket fails
}
// check packetizer's thread status
if ( ! GetExitCodeThread(gh_packetizer, lpExitCode_packetizer) )
break;
// sleep for a duration of 'transmit_rate'
// to provide a constant transmission rate
Sleep((DWORD)inv_transmit_rate); // in milliseconds
}
// disconnect transmission
pNetI->Disconnect();
printf("packetsender exiting...\n");
return (0);
}
//****************************************************************
// PACKETIZER THREAD -
// Thread Routine, called by CreateThread in main()
// to packetize the MPEG stream
// Syntax
// + CreateThread(NULL, //security
// 0, //stack size
// packetizerThread, // this routine
// pStream, // pointer to PacketStream class object
// CREATE_SUSPENDED, // run immediately
// &dwThreadId) // Thread Id
//
// + lpdwThreadParam = pStream
// + Thread is CREATED SUSPENDED; resumption of
// execution is determined by the packetsender thread
//
// effects
// + opens a file stream (via MPEGparser)
// + this routine will pack non-pictures in packets of type
// HEADER, increment the packet sequence number
// for each packet produced, stuff the packet's payload
// one byte after another
// + pictures are packed differently than the non-pictures,
// because the frame types are different and hence, the
// packet type would also be different
// + all packets are added onto the packet stream once they
// have been made
//
// note
// + seqnum ranges from 0 to (2^16 - 1) [=65535]
//
//****************************************************************
DWORD __stdcall packetizerThread(LPVOID lpdwThreadParam)
{
unsigned int dw; // used to store DWord read from MPEG stream
BYTE bw; // byte word used to manipulate picture type
WORD ptype; // stores picture-type
int pict_idx, pict_size; // manipulates packetization of picture
int f_pkt_order; // stores value of current pkt's order within a picture
int f_pkt_total; // stores total number of pkts
within a picture
UDP_PACKET* pCurrentpkt = new UDP_PACKET; // packet-related
WORD seqnum = 0;
int payload_index = 0;
choose(); // choose MPEG I/O option & then instantiate
MPEGparser class
// instantiating the SystemUtility class
SystemUtility* pSysUtil = new SystemUtility();
// PacketStream stuff
PacketStream* pStream = (PacketStream*) lpdwThreadParam;
BOOL Success;
printf("\nPacketizer Thread running\n");
// start packetizing and putting packets into packet stream
printf("packetizerThread: making packets ...\n");
ZeroMemory(pCurrentpkt, sizeof(UDP_PACKET)); // prepare packet
pCurrentpkt->control = INVALID;
// the big loop
while (!pMpeg->endofplayout()) {
dw = pMpeg->nextbits(32);
if (dw != PICTURE_START_CODE) {
// not a picture (maybe a system header)
// make the packet
pCurrentpkt->seqnum = seqnum; //** sometimes
pCurrentpkt->pkt_type = HEADER; //** redundant
//** is this a problem?
pCurrentpkt->payload[payload_index] =
pMpeg->getbits(8);
payload_index++;
// add to packet stream if
// (a) payload full
// or
// (b) next 32bits = picture_start_code (looking ahead)
// otherwise, continue stuffing the payload
if ( (payload_index >= MAXPAYLOAD) ||
(pMpeg->nextbits(32) ==
PICTURE_START_CODE) ) {
// assign the size of that packet
pCurrentpkt->pl_size =
(payload_index >= MAXPAYLOAD) ?
MAXPAYLOAD : payload_index;
// add to stream
pStream->AddPacket(*pCurrentpkt, Success);
// prepare for next iteration
delete pCurrentpkt;
pCurrentpkt = new UDP_PACKET;
ZeroMemory(pCurrentpkt,
sizeof(UDP_PACKET));
pCurrentpkt->control = INVALID;
if ( !pSysUtil->checkoverflow(&seqnum) )
seqnum = 0; // if seqnum = 65535, don't
// increment
else seqnum++;
payload_index = 0;
}
}
else { // if it's a picture
pict_size = pMpeg->getpict();
// manipulate byte to determine picture-type value
bw = pMpeg->pFrame[5];
bw >>= 3;
ptype = (WORD) bw & 0x0007;
// test case to see if bit manipulation is correct
or not
if ( (ptype != I_FRAME) && (ptype != P_FRAME) &&
(ptype != B_FRAME) ) {
printf("ptype: Invalid pkt_type\n");
}
// proceed otherwise
// first pkt of frame-type "ptype"
pCurrentpkt->f_parts[0] = f_pkt_order = 1;
// determine the total number of pkts that constitute a frame
pCurrentpkt->f_parts[1] = f_pkt_total =
( (pict_size % MAXPAYLOAD)==0 ) ?
pict_size/MAXPAYLOAD :
(pict_size/MAXPAYLOAD)+1;
// stuff picture into packet(s)
for (pict_idx = 0; pict_idx < pict_size; pict_idx++) {
if (payload_index < MAXPAYLOAD) {
// stuff till MAXPAYLOAD is reached
pCurrentpkt->payload[payload_index] =
pMpeg->pFrame[pict_idx];
payload_index++;
}
else { // picture can't fit into one packet
// so fill in the rest of the current packet load
pCurrentpkt->seqnum = seqnum;
pCurrentpkt->pkt_type = ptype;
pCurrentpkt->pl_size = payload_index;
// add current pkt to stream
pStream->AddPacket(*pCurrentpkt,
Success);
delete pCurrentpkt;
// create a fresh packet for next portion of
// picture
pCurrentpkt = new UDP_PACKET;
ZeroMemory(pCurrentpkt,
sizeof(UDP_PACKET));
pCurrentpkt->control = INVALID;
// now, prepare for next packet load-in (of
// the same pict) ...
if ( !pSysUtil->checkoverflow(&seqnum) )
// if seqnum = 65535, don't increment
seqnum = 0; else seqnum++;
payload_index = 0;
f_pkt_order++;
pCurrentpkt->f_parts[0] = f_pkt_order;
pCurrentpkt->f_parts[1] = f_pkt_total;
// array correction, pict_idx should not
// increment yet because
// pFrame[pict_idx] has not been read!
pict_idx--;
}
}
// after packetizing a frame, prepare to ship out
pCurrentpkt->seqnum = seqnum;
pCurrentpkt->pkt_type = ptype;
pCurrentpkt->pl_size = payload_index;
// send out final packet of the frame
pStream->AddPacket(*pCurrentpkt, Success);
delete pCurrentpkt;
//prepare a fresh packet
pCurrentpkt = new UDP_PACKET;
ZeroMemory(pCurrentpkt, sizeof(UDP_PACKET));
pCurrentpkt->control = INVALID;
if ( !pSysUtil->checkoverflow(&seqnum) )
seqnum = 0; // if seqnum = 65535, don't increment
else seqnum++;
payload_index = 0;
} // end picture-type packets
} // end of playout
// finished
pCurrentpkt->seqnum = seqnum;
pCurrentpkt->pkt_type = CONTROL;
pCurrentpkt->control = TERMINAL;
ZeroMemory(&pCurrentpkt->payload, MAXPAYLOAD);
pStream->AddPacket(*pCurrentpkt, Success); // add terminating packet to
// stream
pMpeg->~MPEGparser();
delete pCurrentpkt;
pCurrentpkt = NULL;
printf("packetizerThread: Exiting\n");
return (0);
}
//****************************************************************
// RETRANSMISSION THREAD -
// This thread deals with the retransmission of a requested packet
// It needs an ACK from the client to know that it isn't needed
// anymore
// effects
// + thread stops if client sends a TERMINAL pkt to end the session
// + thread can't depend on packetsender thread's termination to
// stop itself because the client may still want to request for
// retransmission (will need to be modified once a TCP dialog
// thread is set up)
//
// resources
// + NetInterface (pNetI)
// + RetransmissionBuffer (pRetBuf)
//
//****************************************************************
DWORD __stdcall retransmissionThread(LPVOID lpdwThreadParam)
{
UDP_PACKET packet;
BOOL success;
UDP_PACKET requestedpkt;
printf("retransmissionThread started ...\n");
do {
success = pNetI->recvpacket(packet);
// get "message" from packet
if (success) {
if (packet.control == RETRANS) {
printf("\nretransmitter: requesting pkt[%d]\n",
packet.seqnum);
// retrieve requested video pkt from buffer and send
if ( pRetBuf->retrieve(requestedpkt,
packet.seqnum) ) {
requestedpkt.pkt_type = CONTROL;
requestedpkt.control = RETRANS;
if (requestedpkt.seqnum != packet.seqnum)
printf("\n\tBUFFER FAILURE!\n");
else {
printf("\n\tRetransmitting
pkt[%d]\n", requestedpkt.seqnum);
pNetI->sendpacket(&requestedpkt);
}
} // don't send if failed to find video packet
in buffer
}
}
else break;
} while (packet.control != TERMINAL);
// end loop if packet received calls for session
termination
printf("retransmitter exiting...\n");
return(0);
}
//****************************************************************
// PINGSERVICETHREAD
// Listen for incoming PING packets
// This thread should eventually incorporate other requests not
// directly related to video packet transmissions (i.e. controls)
//
//****************************************************************
DWORD __stdcall pingServerThread(LPVOID lpdwThreadParam)
{
PING_PACKET packet;
do {
pNetI->recvping(packet);
if (packet.control == PINGREQ) {
// send back the packet
packet.control = PINGRES;
pNetI->sendping(&packet);
}
} while (1);
return (0);
}
//****************************************************************
// Author: Hu Imm Lee
// Filename: MPEGio.h
// File Created: March 10, 1998
//
// Comments: Contains class prototype of MPEGio
//
//****************************************************************
#include <mtruid.h> // MPEGator sdk includes
#include <mtrif.h>
class MPEGio {
private:
int filedesc;
PSTR pFileBuf;
int fptr; // pointer to track current location of data in pFileBuf
unsigned long FileBufSize;
IMtrCapture *pCapture; // MPEGator capture object, the COM interface pointer
MTRSTREAMPTR mtrstream_info;
PSTR pStreamBuf; // MPEGator stream buffer pointer
int sptr;
int streamBufSize;
MTRCAPINFO capInfo;
public:
MPEGio(const char* filename); // constructor for file streaming
MPEGio(); // constructor for real-time streaming w/ the encoder board
~MPEGio(); // destructor
int readfile(void*, int);
void startcapture(); // fire up the encoder
void stopcapture();
int readstream(void*, int);
void checkstream(int);
void getcaptureinfo();
void printStatus(int); // hardware status
char *formatMtrError(HRESULT);
protected:
long openfile(const char *); // handles input from file
void closefile();
int openstream(); // handles input from encoder
void closestream();
};
//****************************************************************
// Author: Hu Imm Lee
// Filename: MPEGio.cpp
// File Created: March 10, 1998
// Last Modified: March 16, 1998
//
// Comments: Contains implementation of the MPEGio class
// The MPEGio class provides 2 options to access MPEG stream,
// from
// a) file
// b) real-time encoder
//
//****************************************************************
#include <windows.h>
#include <initguid.h>
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "MPEGio.h"
extern long vid_bitrate;
//****************************************************************
// MPEGio CLASS IMPLEMENTATION BELOW
//****************************************************************
//****************************************************************
// CONSTRUCTOR
// initializes for file stream input
//****************************************************************
MPEGio::MPEGio(const char* filename):pCapture(NULL),filedesc(0),fptr(0),pStreamBuf(NULL)
{
printf("MPEGio:opening file [%s]\n", filename);
FileBufSize = openfile(filename);
if (FileBufSize <= 0)
printf("openfile encountered error\n");
}
//****************************************************************
// CONSTRUCTOR
// initializes for real-time encoder stream input
//****************************************************************
MPEGio::MPEGio():pCapture(NULL),filedesc(0),fptr(0),pStreamBuf(NULL)
{
if (openstream() < 0)
printf("MPEGio failed to initialize\n");
}
// destructor
MPEGio::~MPEGio() {
if (pCapture != NULL)
closestream();
if (filedesc != 0)
closefile();
}
//****************************************************************
// OPENFILE
// input:
// name of file containing MPEG video
// effects:
// + opens file containing MPEG stream (assigns 'filedesc' to
// the file descriptor)
// + read data into buffer (pFileBuf)
// return:
// + 0 if successful
// + -1 if error occurred
//****************************************************************
long MPEGio::openfile(const char *name) {
struct _stat statBuf; // buffer to store fstat info (like file size)
unsigned long filesize; // size of file
int nread = 0, totalread = 0; // # bytes read
// open file
filedesc = _open(name, _O_RDONLY | _O_BINARY);
if (filedesc < 0) {
printf("\nMPEGparser.openstream: _open ERRNO[%d] on file
%s\n", errno, name);
return -1;
}
// get file size info
if ( _fstat(filedesc, &statBuf) < 0 )
printf("_fstat ERRNO[%d]\n", errno);
filesize = statBuf.st_size;
printf("filesize: %d\n", filesize);
// allocate enough storage space for incoming MPEG filestream
pFileBuf = (PSTR) malloc(filesize);
// read the file but do some error prevention, in case it's not completely
// read
while ( nread < filesize ) {
if ( (nread = _read(filedesc, pFileBuf, filesize)) < 0 ) {
printf("MPEGio::readfile ERR[%d]\n", errno);
return -1;
}
totalread += nread;
}
return totalread; //filesize;
}
void MPEGio::closefile() {
_close(filedesc);
}
//****************************************************************
// READFILE
// read directly from pFileBuf --> into buffer
//
// input:
// + pointer to the buffer to write to
// + bufsize -- size of the buffer
//
// effects:
// + assumes that buffer already allocated to memory
// elsewhere
// + corrects fptr as it "reads" along
// + deallocate whatever data already read?? (no)
//
// return:
// + 0 if there's no more to read
// + total bytes read
//
//****************************************************************
int MPEGio::readfile(void* buffer, int bufsize) {
int oldptr = fptr;
if ( (FileBufSize <= 0) && (pFileBuf == NULL) ) {
free(pFileBuf);
pFileBuf == NULL;
return 0; // no more to read
}
if (bufsize <= FileBufSize) // check that you don't read more than you
// have
memcpy(buffer, &pFileBuf[fptr], bufsize);
else {
memcpy(buffer, &pFileBuf[fptr], FileBufSize);
bufsize = FileBufSize;
}
fptr += bufsize;
FileBufSize -= bufsize;
return bufsize;
}
//****************************************************************
// OPENSTREAM
//
// opens the video stream ... real-time capture by the encoder board
//
// effects:
// + initializes the encoder board using OLE
// + opens the internal channel and prepares the encoder for
// upcoming video capture
//
// returns:
// + 0 if success
// + -1 if failure
//
//****************************************************************
int MPEGio::openstream() {
HRESULT Handle;
CoInitialize(NULL); // initialize the COM library
Handle = CoCreateInstance(
CLSID_MtrMe, // class identifier
NULL,
CLSCTX_SERVER, // context for running executable code
IID_IMtrCapture, // interface identifier
(void **)&pCapture ); // ptr to storage of
interface pointer
if (FAILED(Handle)) {
printf("Can't create COM interface for MPEGator\n");
printf("%s\n", formatMtrError(Handle));
return -1;
}
// open INTERNAL channel for realtime MPEG capture & prepare the
// hardware for the upcoming video capture ...
if (pCapture->Open() == S_FALSE) {
pCapture->Release();
return -1;
}
pCapture->DlgParam(NULL); // MPEG parameters
compound dialog
{
IVMpegSetup* pM_VidSetup;
pCapture->GetVMpegSetup(&pM_VidSetup);
vid_bitrate = pM_VidSetup->get_Bitrate();
printf("video bitrate : %ld\n", vid_bitrate);
pM_VidSetup->Release();
}
return 0; // zero status for success
}
void MPEGio::startcapture() {
// open/create an output stream
pCapture->OpenStreamEx();
pCapture->Start();
}
void MPEGio::stopcapture() {
pCapture->Stop();
}
void MPEGio::closestream() {
if ( (pCapture->GetCapInfo(&capInfo) == S_OK) &&
(capInfo.state != MTRSTAT_STOP) ) {
printf("Stopping capture\n");
pCapture->Stop(); // stop the video capture if not already
// done so
}
pCapture->CloseStreamEx(); // close capturing process &
// its associated stream
pCapture->Close();
pCapture->Release();
CoUninitialize(); // uninitialize the COM video capture object
}
//****************************************************************
// READSTREAM
// Read from the encoded stream, the requested amount (bytes) or
// however much available
//
// input:
// + buffer = the buffer that it writes to
// + bufsize = size of buffer in bytes (amount of bytes to
// read into)
//
// effects:
// + store/load a chunk of data pointed by pStreamBuf
// + keep a pointer (sptr) that tracks where it should next
// read the buffer (pStreamBuf)
//
// return:
// + total bytes actually read, if success
// + 0, if nothing read
// + -1, if failure to obtain encoder's stream pointer
//
//****************************************************************
int MPEGio::readstream(void* buffer, int bufsize)
{
if ( (pStreamBuf == NULL) && (streamBufSize
<= 0) ) { // empty buffer
WaitForSingleObject(pCapture->GetWaitHandle(),
100); // mutex
if ( pCapture->StreamGetPtr(&mtrstream_info, TRUE)
== S_FALSE ) {
printf("IMtrCapture.StreamGetPtr() failed\n");
return -1;
}
if (mtrstream_info.isUsed) {
streamBufSize = mtrstream_info.actualSize; // in bytes
pStreamBuf = (PSTR) malloc(streamBufSize);
// copy from encoder's buffer to MY buffer (pStreamBuf)
memcpy(pStreamBuf, mtrstream_info.bufferPtr,
mtrstream_info.actualSize);
sptr = 0;
}
else return 0;
// release current buffer before fetching the next chunk of data
pCapture->StreamReleasePtr(&mtrstream_info);
}
// check that you don't read more than you have
if (bufsize <= streamBufSize)
memcpy(buffer, &pStreamBuf[sptr], bufsize);
// if amount (bytes) requested is more than what the buffer has,
// return only as much as it's got
else {
memcpy(buffer, &pStreamBuf[sptr], streamBufSize);
bufsize = streamBufSize;
}
sptr += bufsize;
streamBufSize -= bufsize;
if (streamBufSize == 0) {
free(pStreamBuf);
pStreamBuf = NULL;
}
return bufsize;
}
// test function
void MPEGio::checkstream(int fd) {
WaitForSingleObject(pCapture->GetWaitHandle(),
100); // mutex
if ( pCapture->StreamGetPtr(&mtrstream_info, TRUE) == S_FALSE )
printf("IMtrCapture.StreamGetPtr() failed\n");
if (mtrstream_info.isUsed)
_write(fd, mtrstream_info.bufferPtr, mtrstream_info.actualSize);
pCapture->StreamReleasePtr(&mtrstream_info);
// release current buffer before fetching the next chunk of data
}
//****************************************************************
// GETCAPTUREINFO
// to print information about the encoder's state, etc
//****************************************************************
void MPEGio::getcaptureinfo() {
HRESULT status;
status = pCapture->GetCapInfo(&capInfo);
if (status == S_FALSE)
printf("getcaptureinfo failure\n");
else {
printf("frames captured: %d\n", capInfo.framesProcessed);
printf("clock ticks elapsed(1 Hz per tick): %d\n", capInfo.numClockTics);
printf("Encoder state:\t");
printStatus(capInfo.state);
}
}
//****************************************************************
// PRINTSTATUS
// accessory to print state of encoder (hardware)
//****************************************************************
void MPEGio::printStatus(int state) {
if (state == MTRSTAT_IDLE)
printf("Hardware is idle\n");
else if (state == MTRSTAT_PROCESS)
printf("Hardward is busy capturing\n");
else if (state == MTRSTAT_ERROR)
printf("Error occurred\n");
else if (state == MTRSTAT_STOP)
printf("Capture process has stopped\n");
else if (state == MTRSTAT_PAUSE)
printf("Capture process has paused\n");
else printf("Status undetermined at this point
of time\n");
}
char *MPEGio::formatMtrError(HRESULT handle) {
char *mesg = NULL;
DWORD msgLen;
/* get the text description for that error number from the system */
msgLen = FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
0,
(DWORD) handle,
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
(LPTSTR) &mesg,
512,
0);
if(msgLen > 0)
printf("Error %x: %s\n", handle, mesg);
else
printf("Error %x\n", handle);
return mesg;
}
//****************************************************************
// Author: Hu Imm Lee
// Filename: MPEGparser.h
// File Created: February 11, 1998
//
// Comments: Contains class definition for MPEGparser
//****************************************************************
#include "MPEGio.h"
extern enum M_Choice;
// MPEG parsing class
class MPEGparser {
public:
//constructor and destructor
MPEGparser(M_Choice);
~MPEGparser();
// file handling routine
int openstream(const char *name); // ## INVALIDATED by MPEGio
// class methods
BOOL endofplayout();
void closestream(); // ## INVALIDATED by MPEGio
class methods
// parsing routines
int getbits_file(int n); // reads directly from file (original procedure)
int getbits(int n);
int getbits_(int n); // reads from buffer
int getbits_mtr(int n); // read from buffer created
by encoder
int nextbits_file(int);
int nextbits(int n);
int nextbits_(int n);
int nextbits_mtr(int);
void nextstartcode_file();
void nextstartcode();
void nextstartcode_();
void nextstartcode_mtr();
// other useful routines
int getbitrate();
int getpict_file();
int getpict();
int getpict_();
int getpict_mtr();
// public variable
BYTE *pFrame;
private:
unsigned int curBits;
int bit_ptr;
BYTE byteword;
// file I/O stuff
BOOL end_of_file;
int fd;
MPEGio* mpegIO;
M_Choice mode; // mode of input, whether it's a file or a real-time stream
unsigned int capturelen; // run-time length of the video capture
// (via encoder)
unsigned int elapsetime;
};
//****************************************************************
// Author: Hu Imm Lee
// Filename: MPEGparser.cpp
// File Created: February 11, 1998
//
// Comments: Contains implementation of the MPEGparser class
//
//****************************************************************
#include <stdio.h>
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include "MPEGparser.h"
#include "server.h"
extern char g_filename[];
enum M_Choice {
M_FILE,
M_STREAM,
M_NONE }; // choose either file mode, encoder mode
or neither
//****************************************************************
// MPEGPARSER CLASS IMPLEMENTATION BELOW
// - used by packetizer thread -
//****************************************************************
MPEGparser::MPEGparser(M_Choice choice):curBits(0x00000000),bit_ptr(0),end_of_file(FALSE),pFrame(NULL),
capturelen(0), elapsetime(0) {
if (choice == M_FILE)
mpegIO = new MPEGio(g_filename);
else if (choice == M_STREAM) {
mpegIO = new MPEGio();
printf("Enter the length of video to capture
(in units of seconds): ");
scanf("%d", &capturelen);
elapsetime = GetTickCount() + (capturelen*1000);
mpegIO->startcapture();
}
else mpegIO = NULL;
mode = choice;
}
//****************************************************************
// DESTRUCTOR
// implicitly close relevant I/O, whether file or encoder stream
//****************************************************************
MPEGparser::~MPEGparser() {
if (mpegIO != NULL) {
mpegIO->~MPEGio();
mpegIO = NULL;
}
}
int MPEGparser::openstream(const char *name) {
// open file
fd = _open(name, _O_RDONLY | _O_BINARY);
if (fd < 0) {
printf("\nMPEGparser.openstream: _open ERRNO[%d] on file %s\n", errno, name);
return -1;
}
else return fd;
}
BOOL MPEGparser::endofplayout() {
if ( (mode == M_STREAM) && (elapsetime <= GetTickCount()) )
return (end_of_file = TRUE);
return end_of_file;
}
void MPEGparser::closestream() {
_close(fd);
}
//****************************************************************
// GETBITS_FILE -- ##Invalidated by GETBITS and GETBITS_MTR##
// MPEG parsing method
// input
// n = number of bits to be read from MPEG stream
// fd = file descriptor from which MPEG stream is to be read
// effects
// + reads n bits fr FILE data stream and corrects the stream
// pointer, bit_ptr
// returns
// + value of the first n bits read
// + -1 if error occurred
//
// (CLASS GLOBAL) bitptr = "virtual" pointer to MPEG stream
// points to curBits
// (CLASS GLOBAL) curBits = 32-bit unsigned int containing
// the first 32-bits of the MPEG stream (the remaining MPEG
// stream can be pointed to by the file descriptor
//****************************************************************
int MPEGparser::getbits_file(int n) {
unsigned int value;
int nread;
// if bits are in the buffer, no need to read fr stream if
// remaining bits > than n
if (bit_ptr >= n) {
value = curBits >> (bit_ptr-n);
// keep remaining bits, after reading value
curBits &= ~( 0xffffffff << (bit_ptr-n) );
bit_ptr -= n;
return value;
}
while (bit_ptr < n) {
// manipulate data stream into 'big-endian' order
// -- reading data into unsigned int's won't work on Intel x86s
nread = _read(fd, (BYTE *)&byteword, sizeof(BYTE));
if (nread == 0) {
printf("EOF\n");
end_of_file = TRUE;
return -1;
}
else if (nread < 0) {
printf("read err: %d\n", errno);
exit(0);
}
bit_ptr += 8;
curBits = (curBits << 8) | byteword;
}
//extract value
value = curBits >> (bit_ptr-n);
// re-adjust pointer in curBits
curBits &= ~( 0xffffffff << (bit_ptr-n) );
bit_ptr -= n;
return value;
}
//****************************************************************
// GETBITS
//
//****************************************************************
int MPEGparser::getbits(int n) {
if (mode == M_FILE)
return getbits_(n);
else if (mode == M_STREAM)
return getbits_mtr(n);
return getbits_file(n); // old method
}
int MPEGparser::getbits_(int n) {
unsigned int value;
int nread;
if (bit_ptr >= n) {
value = curBits >> (bit_ptr-n);
// keep remaining bits, after reading value
curBits &= ~( 0xffffffff << (bit_ptr-n) );
bit_ptr -= n;
return value;
}
while (bit_ptr < n) {
// manipulate data stream into 'big-endian' order
// -- reading data into unsigned int's won't work on Intel x86s
nread = mpegIO->readfile((BYTE *)&byteword,
sizeof(BYTE));
if (nread == 0) {
printf("EOF\n");
end_of_file = TRUE;
return -1;
}
bit_ptr += 8;
curBits = (curBits << 8) | byteword;
}
//extract value
value = curBits >> (bit_ptr-n);
// re-adjust pointer in curBits
curBits &= ~( 0xffffffff << (bit_ptr-n) );
bit_ptr -= n;
return value;
}
//****************************************************************
// GETBITS_MTR
//
//****************************************************************
int MPEGparser::getbits_mtr(int n) {
unsigned int value;
int nread;
if (bit_ptr >= n) {
value = curBits >> (bit_ptr-n);
// keep remaining bits, after reading value
curBits &= ~( 0xffffffff << (bit_ptr-n) );
bit_ptr -= n;
return value;
}
while (bit_ptr < n) {
// manipulate data stream into 'big-endian' order
// -- reading data into unsigned int's won't work on Intel x86s
nread = mpegIO->readstream((BYTE *)&byteword,
sizeof(BYTE));
if (nread == 0) {
printf("EOF\n");
end_of_file = TRUE;
return -1;
}
else if (nread < 0) {
//printf("read err: %d\n", errno);
exit(0);
}
bit_ptr += 8;
curBits = (curBits << 8) | byteword;
}
//extract value
value = curBits >> (bit_ptr-n);
// re-adjust pointer in curBits
curBits &= ~( 0xffffffff << (bit_ptr-n) );
bit_ptr -= n;
return value;
}
//****************************************************************
// nextbits_file - ##Superceded by NEXTBITS and NEXTBITS_MTR##
// MPEG parsing method
// input
// n = number of bits from MPEG stream to read
// * n no greater than 32
// fd = the file descriptor from which to read
// 'sniffs' bits at data stream
// effect
// + does not correct the bit_ptr
// + bit_ptr is corrected only if n > size(curBits)
// returns
// + value of first n bits
// + -1 on EOF or read error
//****************************************************************
int MPEGparser::nextbits_file(int n) {
int value, nread;
while (bit_ptr < n) {
// do byte-aligned read until size(curBits) >= n
nread = _read(fd, (BYTE *)&byteword, sizeof(BYTE));
if (nread == 0) {
printf("\nMPEGparser.nextbits: EOF\n");
end_of_file = TRUE;
return -1;
}
else if (nread < 0) {
printf("read err: %d\n", errno);
return -1;
}
bit_ptr += 8;
curBits = (curBits << 8) | byteword;
}
// no need to read fr data stream
value = curBits >> bit_ptr-n;
return value;
}
//****************************************************************
// NEXTBITS
// obtains the next n bits from stream using MPEGio::readfile
// procedure
//
// input
// n = number of bits from MPEG stream to read
// * n no greater than 32
//
// effect
// + 'sniffs' bits at data stream, does not correct the bit_ptr
// + bit_ptr is corrected only if n > size(curBits)
// returns
// + value of first n bits
// + -1 on EOF or read error
//****************************************************************
int MPEGparser::nextbits(int n) {
if (mode == M_FILE)
return nextbits_(n);
else if (mode == M_STREAM)
return nextbits_mtr(n);
return nextbits_file(n);
}
int MPEGparser::nextbits_(int n) {
int value, nread;
while (bit_ptr < n) {
// do byte-aligned read until size(curBits) >= n
nread = mpegIO->readfile((BYTE *)&byteword, sizeof(BYTE));
if (nread == 0) {
printf("\nMPEGparser.nextbits: EOF\n");
end_of_file = TRUE;
return -1;
}
bit_ptr += 8;
curBits = (curBits << 8) | byteword;
}
// no need to read fr data stream
value = curBits >> bit_ptr-n;
return value;
}
//****************************************************************
// NEXTBITS_MTR
// obtains the next n bits from encoder stream
// using MPEGio::readstream procedure
//
// input
// n = number of bits from MPEG stream to read
// * n no greater than 32
//
// effect
// + 'sniffs' bits at data stream, does not correct the bit_ptr
// + bit_ptr is corrected only if n > size(curBits)
// returns
// + value of first n bits
// + -1 on EOF or read error
//****************************************************************
int MPEGparser::nextbits_mtr(int n) {
int value, nread;
while (bit_ptr < n) {
// do byte-aligned read until size(curBits) >= n
nread = mpegIO->readstream((BYTE *)&byteword,
sizeof(BYTE));
if (nread == 0) {
printf("\nMPEGparser.nextbits: EOF\n");
end_of_file = TRUE;
return -1;
}
bit_ptr += 8;
curBits = (curBits << 8) | byteword;
}
// no need to read fr data stream
value = curBits >> bit_ptr-n;
return value;
}
//****************************************************************
// nextstartcode_file -
// searches for the 24-bit start-code 0x000001
// effects
// + moves stream pointer, bit_ptr, to beginning of start-code
// on return,
// it is required to call getbits(32) to retrieve the start-code
//****************************************************************
void MPEGparser::nextstartcode_file() {
int align_mode;
// check for byte-alignment
align_mode = bit_ptr % 8;
if (align_mode != 0) // not byte-aligned
bit_ptr -= align_mode;
while (!end_of_file && (nextbits_file(24) != 0x000001)) {
getbits_file(8); // skip a byte till 0x000001 is found
}
return;
}
void MPEGparser::nextstartcode() {
if (mode == M_FILE)
nextstartcode_();
else if (mode == M_STREAM)
nextstartcode_mtr();
else nextstartcode_file();
}
void MPEGparser::nextstartcode_() {
int align_mode;
// check for byte-alignment
align_mode = bit_ptr % 8;
if (align_mode != 0) // not byte-aligned
bit_ptr -= align_mode;
while (!end_of_file && (nextbits_ (24) != 0x000001)) {
getbits_(8); // skip a byte till 0x000001 is found
}
return;
}
void MPEGparser::nextstartcode_mtr() {
int align_mode;
// check for byte-alignment
align_mode = bit_ptr % 8;
if (align_mode != 0) // not byte-aligned
bit_ptr -= align_mode;
while (!end_of_file && (nextbits_mtr(24) != 0x000001)) {
getbits_mtr(8); // skip a byte till 0x000001 is found
}
return;
}
//****************************************************************
// getbitrate -
// procedure to obtain encoding bitrate in video sequence header
//
// returns
// + value of bitrate
// + -1 if error occured
//****************************************************************
int MPEGparser::getbitrate() {
DWORD dw;
WORD picWidth = 0x0000, picHeight = 0x0000;
int frameRate = 0x00000000, bitRate = 0x00000000;
// begin by linear search for the video sequence header
nextstartcode();
dw = getbits(32);
while ( (dw != SEQUENCE_HEADER_CODE) && !endofplayout() ) {
nextstartcode();
dw = getbits(32);
}
// end search because it's probably found the header
already
// start looking for bitrate
// get rid of the next 32 bits because they're impertinent
information
picWidth = getbits(12);
printf("picWidth: %d\n", picWidth);
picHeight = getbits(12);
printf("picHeight: %d\n", picHeight);
getbits(4); // pel_aspect ratio
frameRate = getbits(4);
printf("frameRate: %d fps\n", frameRate);
bitRate = getbits(18);
printf("bitRate: %d\n", bitRate);
printf("True bitrate: %d bits/sec\n",
bitRate*400);
return bitRate;
}
//****************************************************************
// getpict -
// stores a frame into array pointed to by pframe, assume that
// front of MPEG stream begins with picture_start_code
//
// returns
// + size of frame
// + -1 if error occured
//****************************************************************
int MPEGparser::getpict() {
if (mode == M_FILE)
return getpict_();
else if (mode == M_STREAM)
return getpict_mtr();
return getpict_file(); // old
}
int MPEGparser::getpict_() {
int i = 0, f_size;
int byte_idx;
DWORD dw;
dw = getbits_(32);
// check that dw = PICTURE_START_CODE
if (dw != PICTURE_START_CODE) {
printf("first 32bits not PICTURE_START_CODE! Returning\n");
return -1;
}
// put picture_start_code into *pframe
for (byte_idx=32; byte_idx != 0; byte_idx -= 8)
{
pFrame = (BYTE *)realloc(pFrame, i+1); // is realloc expensive ??
pFrame[i] = (BYTE) 0xff & (dw >> (byte_idx-8));
i++;
}
// allocate picture into an array of BYTES
// and then find its size
while ( (nextbits_(32) != PICTURE_START_CODE) &&
(nextbits_(32) != GROUP_START_CODE) && !endofplayout() ) {
pFrame = (BYTE *)realloc(pFrame, i+1); // is realloc expensive ??
pFrame[i] = getbits_(8);
i++;
}
f_size = i;
return f_size;
}
int MPEGparser::getpict_file() {
int i = 0, f_size;
int byte_idx;
DWORD dw;
dw = getbits_file(32);
// check that dw = PICTURE_START_CODE
if (dw != PICTURE_START_CODE) {
printf("first 32bits not PICTURE_START_CODE! Returning\n");
return -1;
}
// put picture_start_code into *pframe
for (byte_idx=32; byte_idx != 0; byte_idx -= 8)
{
pFrame = (BYTE *)realloc(pFrame, i+1); // is realloc expensive ??
pFrame[i] = (BYTE) 0xff & (dw >> (byte_idx-8));
i++;
}
// allocate picture into an array of BYTES
// and then find its size
while ( (nextbits_file(32) != PICTURE_START_CODE) &&
(nextbits_file(32) != GROUP_START_CODE) && !endofplayout() ) {
pFrame = (BYTE *)realloc(pFrame, i+1); // is realloc expensive ??
pFrame[i] = getbits_file(8);
i++;
}
f_size = i;
return f_size;
}
int MPEGparser::getpict_mtr() {
int i = 0, f_size;
int byte_idx;
DWORD dw;
dw = getbits_mtr(32);
// check that dw = PICTURE_START_CODE
if (dw != PICTURE_START_CODE) {
printf("first 32bits not PICTURE_START_CODE! Returning\n");
return -1;
}
// put picture_start_code into *pframe
for (byte_idx=32; byte_idx != 0; byte_idx -= 8)
{
pFrame = (BYTE *)realloc(pFrame, i+1); // is realloc expensive ??
pFrame[i] = (BYTE) 0xff & (dw >> (byte_idx-8));
i++;
}
// allocate picture into an array of BYTES
// and then find its size
while ( (nextbits_mtr(32) != PICTURE_START_CODE) &&
(nextbits_mtr(32) != GROUP_START_CODE) && !endofplayout() ) {
pFrame = (BYTE *)realloc(pFrame, i+1); // is realloc expensive ??
pFrame[i] = getbits_mtr(8);
i++;
}
f_size = i;
return f_size;
}
//****************************************************************
// Author: Hu Imm Lee
// Filename: NetInterface.h
// File Created: February 11, 1998
//
// Comments: Contains class definition for server NetInterface
//****************************************************************
struct UDP_packet;
typedef struct UDP_packet UDP_PACKET;
// network interface class
class NetInterface {
public:
// constructor and destructor
NetInterface();
~NetInterface();
BOOL net_init();
BOOL WaitForRequest();
BOOL sendpacket(UDP_PACKET* pPacket);
BOOL recvpacket(UDP_PACKET& packet);
BOOL tcpsend(TCP_PACKET* pPacket);
BOOL tcprecv(TCP_PACKET& packet);
BOOL sendping(PING_PACKET* pPacket);
BOOL recvping(PING_PACKET& packet);
void Disconnect();
SOCKET udpd;
SOCKET tcpd;
SOCKET pingd;
SOCKET client_tcpd;
struct sockaddr_in tcpclientaddr;
struct sockaddr_in udpclientaddr; // contains addr to send to for video packets
struct sockaddr_in pingclientaddr; // addr to send to for other services //(modified each time by recvpacket()
};
//**********************************************************************
// Author: Hu Imm Lee
// Filename: NetInterface.cpp
// File Created: February 11, 1998
//
// Comments: Contains implementation of the NetInterface class
//
//**********************************************************************
#include <stdio.h>
#include <windows.h>
#include "server.h"
#include "NetInterface.h"
extern void PrintWin32Error(char *);
extern int curpkt_id, oldpkt_id;
//**********************************************************************
// NETWORK INTERFACE CLASS IMPLEMENTATION BELOW
// - used by packet sender thread -
//**********************************************************************
NetInterface::NetInterface() {
WSADATA WsaData;
WORD wVersionRequested;
int err;
wVersionRequested = MAKEWORD(1,1);
if ( (err = WSAStartup(wVersionRequested, &WsaData)) != 0 ) {
printf("Could not find WinSock 1.1 DLL\n");
exit(0);
}
}
NetInterface::~NetInterface() {
closesocket(udpd);
closesocket(tcpd);
closesocket(pingd);
WSACleanup();
}
//****************************************************************
// NET_INIT
// initializes the network interface
// effects
// + creates UDP socket and allocates handle to
// NetInterface::udpd
// + binds UDP socket to a "well-known" port
// returns
// + TRUE if UDP socket successfully created and binded
// + FALSE on failure
//****************************************************************
BOOL NetInterface::net_init() {
struct sockaddr_in udpaddr; // udp
int optval, optlen = sizeof(optval);
struct sockaddr_in tcpaddr; // tcp
struct sockaddr_in pingaddr; // ping
// ** UDP/IP SETUP ** //
// make udp socket
if ( (udpd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))
== INVALID_SOCKET ) {
printf("UDP socket error [%d]\n", WSAGetLastError() );
return FALSE;
}
// udp bind
memset( (char *)&udpaddr, 0x00, sizeof(udpaddr) );
udpaddr.sin_family = AF_INET;
udpaddr.sin_port = htons(MYUDPPORT);
udpaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if ( bind(udpd, (struct sockaddr *)&udpaddr, sizeof(udpaddr))
== SOCKET_ERROR ) {
printf("UDP bind error [%d]\n", WSAGetLastError() );
return FALSE;
}
// increase SO_SNDBUF buffer size -- no effect!
if ( getsockopt(udpd, SOL_SOCKET, SO_SNDBUF, (char *)&optval,
&optlen) == SOCKET_ERROR ) {
printf("UDP getsockopt error [%d]\n", WSAGetLastError() );
return FALSE;
}
optval = optval * 10;
if ( setsockopt(udpd, SOL_SOCKET, SO_SNDBUF, (char *)&optval,
optlen) == SOCKET_ERROR ) {
printf("UDP setsockopt error [%d]\n", WSAGetLastError() );
return FALSE;
}
// check to confirm new SO_SNDBUF buffer size
getsockopt(udpd, SOL_SOCKET, SO_SNDBUF, (char *)&optval,
&optlen);
printf("UDP so_sndbuf set to %d\n", optval);
// ** TCP/IP SETUP ** //
if ( (tcpd = socket(AF_INET, SOCK_STREAM, 0))
== INVALID_SOCKET ) {
printf("TCP socket error [%d]\n", WSAGetLastError() );
return FALSE;
}
// tcp bind
ZeroMemory( (char *)&tcpaddr, sizeof(tcpaddr) );
tcpaddr.sin_family = AF_INET;
tcpaddr.sin_port = htons(MYTCPPORT);
tcpaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if ( bind(tcpd, (struct sockaddr *)&tcpaddr, sizeof(tcpaddr))
== SOCKET_ERROR ) {
//printf("TCP bind error [%d]\n", WSAGetLastError() );
PrintWin32Error("TCP bind");
return FALSE;
}
// tcp listen
if ( listen(tcpd, SOMAXCONN) == SOCKET_ERROR ) {
printf("TCP listen error [%d]\n", WSAGetLastError() );
return FALSE;
}
// ** PING SERVICES SETUP ** //
if ( (pingd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) ==
INVALID_SOCKET ) {
printf("PING socket error [%d]\n", WSAGetLastError() );
return FALSE;
}
// ping bind
memset( (char *)&pingaddr, 0x00, sizeof(pingaddr) );
pingaddr.sin_family = AF_INET;
pingaddr.sin_port = htons(MYPINGPORT);
pingaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if ( bind(pingd, (struct sockaddr *)&pingaddr, sizeof(pingaddr)) ==
SOCKET_ERROR ) {
printf("PING bind error [%d]\n", WSAGetLastError() );
return FALSE;
}
return TRUE;
}
//****************************************************************
// WAITFORREQUEST -
// effects
// method blocks until a udp packet is received
// returns
// FALSE if encountered either
// a) a recv error
// b) wrong packet type received
// TRUE is successful
//****************************************************************
BOOL NetInterface::WaitForRequest() {
int nrecv;
TCP_PACKET* pTcppkt = new TCP_PACKET;
int* pclnaddrsize = new int(sizeof(tcpclientaddr));
int addrlen = sizeof(struct sockaddr_in);
// accept a TCP client connection
if ( (client_tcpd = accept(tcpd, (struct sockaddr *)&tcpclientaddr,
pclnaddrsize)) == SOCKET_ERROR ) {
printf("\nNetInterface.WaitForRequest: accept ERRNO[%d]\n",
WSAGetLastError() );
return FALSE;
}
// receive a TCP request pkt
nrecv = recv(client_tcpd, (char *)pTcppkt, sizeof(*pTcppkt), 0);
if (nrecv == SOCKET_ERROR) {
printf("\nNetInterface.WaitForRequest: recv ERRNO[%d]\n",
WSAGetLastError() );
return FALSE;
}
// check if it's a valid packet received
if (pTcppkt->pkt_type != REQUEST) {
printf("\nNot a request packet [type %d]. Hmmmm ...\n",
pTcppkt->pkt_type);
return FALSE;
}
else {
// get client's udp connection address
getpeername(client_tcpd, (struct sockaddr *)&udpclientaddr,
&addrlen);
// get client UDP port #
udpclientaddr.sin_port = pTcppkt->udpclientaddr.sin_port;
delete pTcppkt;
pTcppkt = NULL;
delete pclnaddrsize;
return TRUE;
}
}
//****************************************************************
// RECVPACKET -
// receives a datagram via UDP
//****************************************************************
BOOL NetInterface::recvpacket(UDP_PACKET& packet)
{
int nrecv;
struct sockaddr_in address;
int fromlen = sizeof(struct sockaddr_in);
nrecv = recvfrom( udpd, (char *)&packet, sizeof(packet), 0,
(struct sockaddr *)&address, &fromlen );
if (nrecv == SOCKET_ERROR) {
printf("NetInterface::recvpacket : recvfrom ERR[%d]\n",
WSAGetLastError() );
return FALSE;
}
else return TRUE;
}
//****************************************************************
// SENDPACKET -
// effects
// sends a datagram via UDP
// returns
// + TRUE is successful
// + FALSE is failure
//****************************************************************
BOOL NetInterface::sendpacket(UDP_PACKET* pPacket) {
int nsend;
curpkt_id = pPacket->seqnum;
oldpkt_id = curpkt_id;
nsend = sendto( udpd, (char *)pPacket, sizeof(*pPacket), 0,
(struct sockaddr *)&udpclientaddr, sizeof(udpclientaddr) );
if (nsend == SOCKET_ERROR) {
printf("NetInterface::sendpacket : sendto ERR[%d]\n",
WSAGetLastError() );
return FALSE;
}
else if (nsend != sizeof(*pPacket)) {
printf("NetInterface::sendpacket : sendto incomplete\n");
return FALSE;
}
else {
printf("\rsent pkt [%d] ", pPacket->seqnum);
return TRUE;
}
}
//****************************************************************
// TCPSEND -
// sends packet via TCP
//****************************************************************
BOOL NetInterface::tcpsend(TCP_PACKET* pPacket) {
int nsend;
nsend = send( NetInterface::client_tcpd, (char *)pPacket,
sizeof(TCP_PACKET), 0);
if (nsend == SOCKET_ERROR) {
printf("NetInterface::tcpsend ERR[%d]\n", WSAGetLastError() );
return FALSE;
}
return TRUE;
}
//****************************************************************
// TCPRECV -
// receives packet via TCP
//****************************************************************
BOOL NetInterface::tcprecv(TCP_PACKET& packet) {
int nrecv;
nrecv = recv( NetInterface::client_tcpd, (char *)&packet,
sizeof(TCP_PACKET), 0);
if (nrecv == SOCKET_ERROR) {
printf("NetInterface::tcprecv ERR[%d]\n", WSAGetLastError() );
return FALSE;
}
return TRUE;
}
//****************************************************************
// SENDPING -
// to send a packet for services like PING
// (in case the client requesting such service is different from
// the one that's receiving the video packets)
//
// effects
// sends a datagram via UDP using pingclientaddr
// returns
// + TRUE is successful
// + FALSE is failure
//
// CAUTION:
// server cannot initiate a PING if pingclientaddr has no value
//****************************************************************
BOOL NetInterface::sendping(PING_PACKET* pPacket) {
int nsend;
nsend = sendto( pingd, (char *)pPacket, sizeof(PING_PACKET), 0,
(struct sockaddr *)&pingclientaddr, sizeof(struct
sockaddr_in) );
if (nsend == SOCKET_ERROR) {
printf("NetInterface::sendping : sendto ERR[%d]\n",
WSAGetLastError() );
return FALSE;
}
else if (nsend != sizeof(*pPacket)) {
printf("NetInterface::sendping: sendto incomplete\n");
return FALSE;
}
else {
return TRUE;
}
}
//****************************************************************
// RECVPING -
// receives a PING datagram via UDP
//****************************************************************
BOOL NetInterface::recvping(PING_PACKET& packet)
{
int nrecv;
struct sockaddr_in address;
int fromlen = sizeof(struct sockaddr_in);
nrecv = recvfrom( pingd, (char *)&packet, sizeof(packet), 0,
(struct sockaddr *)&address, &fromlen );
pingclientaddr = address;
if (nrecv == SOCKET_ERROR) {
printf("NetInterface::recvping : recvfrom ERR[%d]\n",
WSAGetLastError() );
return FALSE;
}
else return TRUE;
}
//****************************************************************
// DISCONNECT -
// effects
// + sends a TERMINAL pkt via TCP to the client to indicate
// end of transmission
//****************************************************************
void NetInterface::Disconnect() {
int nsend;
TCP_PACKET* pTCPpkt = new TCP_PACKET;
pTCPpkt->pkt_type = TERMINAL;
ZeroMemory( (char *)&pTCPpkt->udpclientaddr,
sizeof(struct sockaddr_in) );
nsend = send(client_tcpd, (char *)pTCPpkt, sizeof(*pTCPpkt), 0);
if (nsend == SOCKET_ERROR)
printf("\nNetInterface.Disconnect : send error [%d]\n",
WSAGetLastError() );
}
//****************************************************************
// Author: Hu Imm Lee
// Filename: PacketStream.h
// File Created: February 11, 1998
//
// Comments: Contains prototype for PacketStream class
//
//****************************************************************
typedef struct UDP_packet UDP_PACKET;
// packet stream class (a circular pointer-based queue)
class PacketStream {
public:
// constructor and destructor
PacketStream();
~PacketStream();
BOOL StreamIsEmpty();
void AddPacket(UDP_PACKET NewPacket, BOOL& Status);
void GetFrontPacket(UDP_PACKET& FrontPacket, BOOL& Status);
void RemovePacket(BOOL& Status);
private:
struct StreamNode* pRear;
LPCRITICAL_SECTION lpCriticalSection;
};
//****************************************************************
// Author: Hu Imm Lee
// Filename: PacketStream.cpp
// File Created: February 11, 1998
//
// Comments: Contains implementation of PacketStream class
//
//****************************************************************
#include <stdio.h>
#include <windows.h>
#include "server.h"
#include "PacketStream.h"
extern int packets;
//****************************************************************
// PACKET STREAM CLASS IMPLEMENTATION
// - shared by packetizer and packetsender threads -
//****************************************************************
PacketStream::PacketStream():pRear(NULL) {
// create critical section object to synchronize sharing of the stream
// esp for AddPacket and RemovePacket
lpCriticalSection = new CRITICAL_SECTION;
InitializeCriticalSection(lpCriticalSection);
}
PacketStream::~PacketStream() {
DeleteCriticalSection(lpCriticalSection); // release the CS object
}
//****************************************************************
// STREAM IS EMPTY -
// returns
// TRUE if stream (queue) is empty
// FALSE if stream is not empty
//****************************************************************
BOOL PacketStream::StreamIsEmpty() {
if (pRear == NULL)
return TRUE;
else return FALSE;
}
//****************************************************************
// ADD PACKET -
// Add packet to the back of the queue
// effects
// + critical section, so routine has to first take ownership
// before proceeding
// + allocates a new node into the stream
// + alters the rear pointer to point to new node (containing
// new packet
//****************************************************************
void PacketStream::AddPacket(UDP_PACKET NewPacket,
BOOL& Status) {
__try {
// request for ownership to cs object
// if cs obj has already been owned, it blocks till obj is released
EnterCriticalSection(lpCriticalSection);
// create new node
struct StreamNode* pRearNew = new struct StreamNode;
Status = boolean(pRearNew != NULL);
if (Status) {
pRearNew->NodeItem = NewPacket;
// inserting new packet node into stream
if (StreamIsEmpty())
// first node points to itself
pRearNew->pNextNode = pRearNew;
else {
// insertion into non-empty stream
pRearNew->pNextNode = pRear->pNextNode;
pRear->pNextNode = pRearNew;
}
pRear = pRearNew; // new node is now at rear
}
packets++;
}
__finally {
// release ownership of cs obj when done
LeaveCriticalSection(lpCriticalSection);
}
}
//****************************************************************
// GET FRONT PACKET -
// Retrieve packet at front of stream
// effects
// + NIL (doesn't alter the stream)
//****************************************************************
void PacketStream::GetFrontPacket(UDP_PACKET&
FrontPacket, BOOL& Status) {
// request for ownership to cs object
// if cs obj has already been owned, it blocks till obj is released
EnterCriticalSection(lpCriticalSection);
Status = boolean(!StreamIsEmpty());
if (Status) // stream isn't empty
FrontPacket = pRear->pNextNode->NodeItem;
LeaveCriticalSection(lpCriticalSection);
}
//****************************************************************
// REMOVE PACKET -
// Removes front node of stream
// effects
// + critical section, so routine has to first take ownership
// before proceeding
// + change rear pointer to point to second node of stream
// + front node is deallocated
//****************************************************************
void PacketStream::RemovePacket(BOOL& Status)
{
__try {
// request for ownership to cs object
// if cs obj has already been owned, it blocks till obj is released
EnterCriticalSection(lpCriticalSection);
Status = boolean(!StreamIsEmpty());
if (Status) { // stream is not empty
struct StreamNode* pFront = pRear->pNextNode;
if (pFront == pRear)
pRear = NULL; // if only one node in stream
else
pRear->pNextNode = pFront->pNextNode;
pFront->pNextNode = NULL;
delete pFront;
}
}
__finally {
// release ownership of CS obj when done
LeaveCriticalSection(lpCriticalSection);
}
}
//****************************************************************
// Author: Hu Imm Lee
// Filename: RetransmissionBuffer.h
// File Created: February 11, 1998
//
// Comments: Contains class definition for RetransmissionBuffer
//
//****************************************************************
typedef struct UDP_packet UDP_PACKET;
// Retransmission Buffer class (a fixed-size circular queue)
class RetransmissionBuffer {
public:
// constructor and destructor
RetransmissionBuffer(int minbufsize);
~RetransmissionBuffer();
void add(UDP_PACKET* pNewPacket);
BOOL retrieve(UDP_PACKET& RequestedPacket, int
seqnum);
private:
static int buffsize;
static UDP_PACKET* pBuf; // ptr to circular array
//int iFront; // index to front of buffer
static int iRear; // index to rear of buffer
};
//**********************************************************************
// Author: Hu Imm Lee
// Filename: RetransmissionBuffer.cpp
// File Created: February 11, 1998
//
// Comments: Contains implementation of RetransmissionBuffer class
//
//**********************************************************************
#include <stdio.h>
#include <windows.h>
#include <math.h>
#include "server.h"
#include "RetransmissionBuffer.h"
//**********************************************************************
// Retransmission Buffer Class Implementation Below
//**********************************************************************
int RetransmissionBuffer::buffsize = 0;
UDP_PACKET* RetransmissionBuffer::pBuf = NULL;
int RetransmissionBuffer::iRear = 0;
RetransmissionBuffer::RetransmissionBuffer(int minbufsize) {
// rear starts off pointing to the front because
buffer is empty
// make the buffer size divisible by 8 because the udp
// packet's seqnum is a max of 2^16 - 1, i.e., 2 bytes long
// for example: if minbufsize is 14, then buffsize should be 16
RetransmissionBuffer::buffsize = (int) ceil((double)minbufsize/8)
* 8;
printf("RetransmissionBuffer initialization:\n\tbuffsize
is: %d\n", RetransmissionBuffer::buffsize);
// make empty circular buffer here
RetransmissionBuffer::pBuf = new UDP_PACKET[RetransmissionBuffer::buffsize];
// an empty fixed size array
}
RetransmissionBuffer::~RetransmissionBuffer() {
delete RetransmissionBuffer::pBuf;
RetransmissionBuffer::pBuf = NULL;
}
// It doesn't matter if the buffer is empty or not
//**********************************************************************
// ADD -
// effects
// + each packet is assigned a "slot"
// + OVERWRITES the "older" packets which have already expired
// (located at the front of the buffer)
//
//**********************************************************************
void RetransmissionBuffer::add(UDP_PACKET* pNewPacket)
{
int index = pNewPacket->seqnum % buffsize;
pBuf[index] = *pNewPacket;
}
//****************************************************************
// RETRIEVE -
// effects
// + reads the packet from pBuf containing packet with
// seqnum
// + causes NO effect on pBuf's index pointer
//
// the algorithm searches for a match for the requested packet by
// elegantly calculating the exact place within pBuf that it should
// be located.
//
// returns
// + TRUE if packet was found
// + FALSE if not found
//****************************************************************
BOOL RetransmissionBuffer::retrieve(UDP_PACKET&
RequestedPacket, int seqnum) {
int index; // indicates where to find the packet in pBuf
UDP_PACKET packet;
printf("RetransmissionBuffer:: retrieve\n\trequesting pkt[%d]\n",
seqnum);
index = seqnum % buffsize;
printf("\tLooking into index %d\n", index);
packet = RetransmissionBuffer::pBuf[index];
printf("\tFound pkt[%d] in buffer\n",
packet.seqnum);
if (packet.seqnum == seqnum) { // check that it's the right packet
RequestedPacket = packet;
return TRUE;
}
else return FALSE; // failure if requested packet has already expired
}
//****************************************************************
// Filename: SystemUtility.h
// Comments: Contains class definition for SystemUtility
//****************************************************************
// system interface class
class SystemUtility {
public:
// constructor and destructor
SystemUtility();
~SystemUtility();
BOOL checkoverflow(LPWORD pN);
};
//**********************************************************************
// Filename: SystemUtility.cpp
// Comments: Contains implementation of SystemUtility class
//**********************************************************************
#include <stdio.h>
#include <windows.h>
#include <math.h>
#include "SystemUtility.h"
//****************************************************************
// SYSTEM UTILITY CLASS IMPLEMENTATION BELOW
//****************************************************************
SystemUtility::SystemUtility() {
}
SystemUtility::~SystemUtility() {
}
//****************************************************************
// CHECK OVERFLOW -
// avoids overflow in a 16bit unsigned int
// returns
// + FALSE if *pN is about to overflow
// + TRUE if still OK
//
//****************************************************************
BOOL SystemUtility::checkoverflow(LPWORD pN) {
WORD max;
// [2 ^ (# bytes x 8bi