/* =========================================================================== ITU-T Telecommunications Standardization Sector Document Q15-G-38 Study Group 16 Filename q15g38.c Video Coding Experts Group (Question 15) Generated: 10 Feb. '99 ---------------------------------------- Seventh meeting: Monterey, California, 16-19 February, 1999 pseudomux8.c ~~~~~~~~~~~~ Simple video packet mux simulator. Approximates AL3 mux of H.223 Annex B. Source: ~~~~~~~ Gary Sullivan Tel: +1 978 623 4324 PictureTel Corp. Fax: +1 978 749 2804 100 Minuteman Rd. Email: garys@pictel.com Andover MA 01810 USA Written with help from Stephan Wenger, Ari Hourunranta, Simao Campos-Neto, and Maximillian Luttrell. Purpose: Information ~~~~~~~~ History: ~~~~~~~~ 08.Oct.98 V.0.04 Changed CRC generator; solved MSDOS warnings with MSC. 09.Oct.98 V.0.05 Warnings solved (with gcc -Wall (in a HPUX system): removed unused CRC polynomial tables crctab_xmodem[], crctab_arc[]; replaced %d with %ld in fprint()'s. 22.Oct.98 V.0.06 Changed to send a pseudo-audio packet first. Reduced MAX_PACKET_SIZE to 254 (max AL3 MPL value). CRC length for audio reduced to 1 byte (AL2 CRC length). Corrected time computation to include video overhead. Increased amount of final summary information reported. 28.Oct.98 V.0.07 Increased AUDIO_PACKET_SIZE to 24 bytes (to reflect G.723.1 high bit-rate operation). Added optional ErrStart command-line parameter. Submitted as contribution Q15-F-16 to ITU-T Q.15/SG16 meeting in Seoul, Korea, 3-6 Nov. 98. 29.Jan.99 V.0.08 Changed to handle larger packets by splitting a single AL3 packet across multiple mux packets if necessary. Packets up to 252 bytes in size are processed the same way as in V.0.07. Packets 253 and 254 bytes in size are processed more realistically. Packets larger than 254 bytes could not be processed by V.0.07. Packets up to 65535 bytes in size can be processed by V.0.08. Submitted as contribution Q15-G-38 to ITU-T Q.15/SG16 meeting in Monterey, California, 16-19 February, 1999. =========================================================================== */ #include #include #include typedef unsigned char byte; typedef unsigned short unt16; typedef short int16; typedef long int32; /* initialization value of CRC */ #define INIT_CRC_C ((unt16) 0xFFFF) /* --------------------------------------------------------------------------- unt16 updcrc (unt16 icrc, unsigned char *icp, int icnt, unt16 *crctab, ~~~~~~~~~~~~ char swapped); Description: ~~~~~~~~~~~~ Calculate, intelligently, the CRC-16 of a dataset incrementally given a buffer full at a time. The parameters on which the function relies are: Parameter CCITT XMODEM ARC --------- ------ ------ ---- the polynomial P 0x1021 0x1021 A001 initial CRC value: 0xFFFF 0 0 bit-order swap No (0) Yes (1) Yes (1) bits in CRC: 16 16 16 Usage: ~~~~~~ newcrc = updcrc( oldcrc, bufadr, buflen, crctab, swapped) unt16 oldcrc; int buflen; char *bufadr; unt16 *crctab; char swapped; Notes: Regards the data stream as an integer whose MSB is the MSB of the first byte recieved. This number is 'divided' (using xor instead of subtraction) by the crc-polynomial P. XMODEM does things a little differently, essentially treating the LSB of the first data byte as the MSB of the integer. Original Author: ~~~~~~~~~~~~~~~~ Mark G. Mendel, 7/86 UUCP: ihnp4!umn-cs!hyper!mark, GEnie: mgm [ Original code available in comp.sources.unix volume 6 (1989) ] History: ~~~~~~~~ xx.Jul.86 v1.0 Release by the author [posted to c.s.misc in 1989] 01.Sep.93 v1.0a Adapted by Simao to be used in the x{en,de}code.c. 08.Oct.98 v1.0b Adopted into pseudo-mux simulator by Gary (garys@pictel.com) --------------------------------------------------------------------------- */ #define BPS 8 /* bits per sample */ #define CRCW 16 /* crc width */ /* Table for CCITT CRCs */ static unt16 crctab_ccitt[1 << BPS] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0, }; /* -------------- Begin of function updcrc() ---------------------- */ unt16 updcrc(icrc, icp, icnt, crctab, swapped) unt16 icrc, *crctab; unsigned char *icp; int icnt; char swapped; { register unt16 crc = icrc; register unsigned char *cp = icp; register int cnt = icnt; if (swapped) while (cnt--) crc = (crc >> BPS) ^ crctab[(crc & ((1 << BPS) - 1)) ^ *cp++]; else while (cnt--) crc = (crc << BPS) ^ crctab[(crc >> (CRCW - BPS)) ^ *cp++]; return crc; } /* .......................... End of updcrc() ............................ */ /* The maximum value of maximumAL3SDUSize negotiable by H.245 is 65535. */ #define maximumAL3SDUSize 65535 /* bytes */ #define MAX_MUX_PACKET_SIZE 254 /* bytes */ #define AUDIO_TIME_INTERVAL 0.030 /* seconds */ #define AUDIO_PACKET_SIZE 24 /* bytes (not including overhead) */ #define SYNC_SIZE 2 /* bytes */ #define HEADER_SIZE 3 /* bytes */ #define AL2_CRC_SIZE 1 /* bytes */ #define AL3_CRC_SIZE 2 /* bytes */ #define AL2_OVERHEAD_SIZE (SYNC_SIZE + HEADER_SIZE + AL2_CRC_SIZE) #define AL3_OVERHEAD_SIZE (SYNC_SIZE + HEADER_SIZE + AL3_CRC_SIZE) #define MAX_SYNC_ERRORS 3 #define MAX_HEADER_ERRORS 2 /* The 2 byte sync is 0xE14D (binary 11100001 followed by 01001101) */ /* The 3 byte AL2 mux header we are using contains the following: bits contents explanation 0-3 MC Multiplex Code, describes multiplex scheme of packet 4-11 MPL Multiplex Payload Length, the length of the data field 12-23 golay (24,12) Golay block code which codes combined MC+MPL */ char *pname; FILE *ErrorFile, *InputFile, *OutputFile, *MessageFile; int GetVideoMuxPacket(int32 *pLastMuxPacketOfVideoPacket, int32 *pVideoPacketSize, byte *PacketData, int32 dposition) { int MuxPacketSize; unt16 transmitted_crc, msb_crc; if(*pLastMuxPacketOfVideoPacket) { if(fread(pVideoPacketSize, sizeof(*pVideoPacketSize), 1, InputFile) != 1) *pVideoPacketSize = -1; /* indicate end of session */ else{ if(*pVideoPacketSize < 0 || *pVideoPacketSize > maximumAL3SDUSize) { fprintf(MessageFile, "%s: packet size (%ld) out of bounds\n", pname, *pVideoPacketSize); exit(-7); } if(fread(PacketData, sizeof(byte), *pVideoPacketSize, InputFile) != (size_t)*pVideoPacketSize) { fprintf(MessageFile, "%s: error reading video packet of %ld bytes\n", pname, *pVideoPacketSize); exit(-8); } /* calculate packet CRC to be transmitted */ transmitted_crc = updcrc(INIT_CRC_C, PacketData, *pVideoPacketSize, crctab_ccitt, 0); /* Technically, each byte of the CRC should be bit-reversed (according to the last */ /* paragraph of Section 3 of H.223), however an equality test on a transmitted and */ /* received crc value will get the same result regardless of the bit order, */ /* provided the transmitter and receiver use the same bit order. */ /* put CRC into data buffer to be sent (manipulating in endian-neutral fashion) */ msb_crc = transmitted_crc >> 8; PacketData[(*pVideoPacketSize)++] = msb_crc; PacketData[(*pVideoPacketSize)++] = transmitted_crc - (msb_crc << 8); } } if(*pLastMuxPacketOfVideoPacket = (*pVideoPacketSize - dposition <= MAX_MUX_PACKET_SIZE)) MuxPacketSize = *pVideoPacketSize - dposition; else MuxPacketSize = MAX_MUX_PACKET_SIZE; return MuxPacketSize; } int main(int ac, char *av[]) { byte PacketData[maximumAL3SDUSize+AL3_CRC_SIZE], *errors, ebyte, CRCcheckFail; int32 esize, eposition, j, k, SyncErrorCount, HeaderErrorCount, TotalBytes; int32 LastMuxPacketOfVideoPacket, VideoPacketSize, dposition; int32 TotalBitRate, TotalVideoBytes, VideoPacketNumber; int32 VideoMuxPacketSize, VideoMuxPacketNumber; unt16 received_crc, rcvr_computed_crc; double time_since_audio_sent, time_duration_of_audio_packet; int32 ai = 0, bad_usage = 0; pname = av[ai++]; if(ac > ai && !bad_usage) { if(bad_usage = ((ErrorFile = fopen(av[ai++], "rb")) == NULL)) fprintf(MessageFile, "%s: Cannot open file (%s)\n", pname, av[--ai]); }else bad_usage = 1; if(ac > ai && !bad_usage) { if(bad_usage = ((TotalBitRate = atoi(av[ai++])) < 0)) fprintf(MessageFile, "%s: Bad TotalBitRate (%ld)\n", pname, TotalBitRate); }else bad_usage = 1; if(ac > ai) eposition = atoi(av[ai++]); else eposition = 0; if(ac > ai && !bad_usage) { if(bad_usage = ((InputFile = fopen(av[ai++], "rb")) == NULL)) fprintf(MessageFile, "%s: Cannot open input file (%s)\n", pname, av[--ai]); }else InputFile = stdin; if(ac > ai && !bad_usage) { if(bad_usage = ((OutputFile = fopen(av[ai++], "wb")) == NULL)) fprintf(MessageFile, "%s: Cannot open output file (%s)\n", pname, av[--ai]); }else OutputFile = stdout; if(ac > ai && !bad_usage) { if(bad_usage = ((MessageFile = fopen(av[ai++], "wb")) == NULL)) fprintf(MessageFile, "%s: Cannot open output file (%s)\n", pname, av[--ai]); }else MessageFile = stderr; if(ac > ai || bad_usage) { fprintf(stderr, "\n"); fprintf(stderr, "USAGE: %s ErrorFile TotalBitRate [[[[ErrStart"); fprintf(stderr, "] InputFile] OutputFile] MessageFile]\n", pname); fprintf(stderr, "\n"); fprintf(stderr, "Error patterns are binary-format files in which\n"); fprintf(stderr, "the least-significant bit of the first byte signifies\n"); fprintf(stderr, "the error state of the temporally first bit on the channel.\n"); fprintf(stderr, "\n"); fprintf(stderr, "If there is more input data than the length of the\n"); fprintf(stderr, "error pattern file, then the pattern will be used\n"); fprintf(stderr, "repetitively by circular wrapping.\n"); fprintf(stderr, "\n"); fprintf(stderr, "TotalBitRate (integer) is used to determine when to\n"); fprintf(stderr, "insert periodic audio packets.\n"); fprintf(stderr, "\n"); fprintf(stderr, "ErrStart (integer) is used to specify the starting\n"); fprintf(stderr, "byte number in the error pattern file (default = 0).\n"); fprintf(stderr, "Note that all bytes are used when wrapping, but\n"); fprintf(stderr, "ErrStart bytes are jumped over at start-up.\n"); fprintf(stderr, "\n"); fprintf(stderr, "Input packets (from stdin or InputFile if specified)\n"); fprintf(stderr, "are each prefaced by a packet length in bytes (integer).\n"); fprintf(stderr, "\n"); fprintf(stderr, "Output (to stdout or OutputFile if specified) for each\n"); fprintf(stderr, "(non-lost) packet contains:\n"); fprintf(stderr, " CRCcheckFail flag (byte),\n"); fprintf(stderr, " packet length (integer), and\n"); fprintf(stderr, " packet data (with errors applied)\n"); fprintf(stderr, "\n"); fprintf(stderr, "Total number of video bytes used including packet\n"); fprintf(stderr, "overhead and other information is provided\n"); fprintf(stderr, "(to stderr or MessageFile if specified).\n"); fprintf(stderr, "\n"); exit(-1); } /* determine size of error pattern file and read it into errors buffer */ if(fseek(ErrorFile, 0L, SEEK_END)) { fprintf(MessageFile, "%s: fseek failed\n", pname); exit(-3); } if((esize = ftell(ErrorFile)) == -1) { fprintf(MessageFile, "%s: ftell returns -1", pname); exit(-4); } fseek(ErrorFile, 0L, SEEK_SET); if((errors = (byte *)malloc(esize)) == NULL) { fprintf(MessageFile, "%s: cannot malloc %ld bytes for error patterns\n", pname, esize); exit(-5); } if(fread(errors, esize, 1, ErrorFile) != 1) { fprintf(MessageFile, "%s: error while reading error patterns\n", pname); exit(-6); } fclose(ErrorFile); time_duration_of_audio_packet = (AUDIO_PACKET_SIZE + AL2_OVERHEAD_SIZE) * 8.0 / TotalBitRate; for(VideoPacketNumber = 0, VideoMuxPacketNumber = 0, LastMuxPacketOfVideoPacket = 1, TotalBytes = 0, TotalVideoBytes = 0, rcvr_computed_crc = INIT_CRC_C, dposition = 0, time_since_audio_sent = AUDIO_TIME_INTERVAL; (VideoMuxPacketSize = GetVideoMuxPacket(&LastMuxPacketOfVideoPacket, &VideoPacketSize, PacketData, dposition)) > 0; VideoMuxPacketNumber++, VideoPacketNumber += LastMuxPacketOfVideoPacket, TotalVideoBytes += VideoMuxPacketSize - LastMuxPacketOfVideoPacket * AL3_CRC_SIZE, TotalBytes += VideoMuxPacketSize + SYNC_SIZE + HEADER_SIZE, time_since_audio_sent += (VideoMuxPacketSize + SYNC_SIZE + HEADER_SIZE) * 8.0 / TotalBitRate) { /* send audio when necessary (and account for time to send it) */ while(time_since_audio_sent >= AUDIO_TIME_INTERVAL) { TotalBytes += AUDIO_PACKET_SIZE + AL2_OVERHEAD_SIZE; eposition += AUDIO_PACKET_SIZE + AL2_OVERHEAD_SIZE; if(eposition >= esize) eposition -= esize; time_since_audio_sent -= AUDIO_TIME_INTERVAL; time_since_audio_sent += time_duration_of_audio_packet; } /* discard SYNC_SIZE bytes while counting errors in them */ for(j=0, SyncErrorCount=0; j> k) & 1) SyncErrorCount++; } /* discard HEADER_SIZE bytes while counting errors in them */ for(j=0, HeaderErrorCount=0; j> k) & 1) HeaderErrorCount++; } /* apply errors to video packet */ for(j=0; j MAX_SYNC_ERRORS || HeaderErrorCount > MAX_HEADER_ERRORS) { fprintf(MessageFile, " Video Mux Packet %4ld Was Lost", VideoMuxPacketNumber); fprintf(MessageFile, " (SyncErrorCount: %2ld, HeaderErrorCount: %2ld)\n", SyncErrorCount, HeaderErrorCount); /* throw away the data so it doesn't get to the receiver */ for(j=dposition; j AL3_CRC_SIZE) { /* if something got through */ /* calculate new CRC computed by receiver */ rcvr_computed_crc = updcrc(INIT_CRC_C, PacketData, VideoPacketSize - AL3_CRC_SIZE, crctab_ccitt, 0); for(j=AL3_CRC_SIZE, received_crc=0; j>0; j--) received_crc = (received_crc << 8) + PacketData[VideoPacketSize-j]; CRCcheckFail = (received_crc != rcvr_computed_crc); /* write out the crc cross-check flag, packet size, and packet */ fwrite(&CRCcheckFail, sizeof(CRCcheckFail), 1, OutputFile); fwrite(&VideoPacketSize, sizeof(VideoPacketSize), 1, OutputFile); fwrite(PacketData, sizeof(byte), VideoPacketSize - AL3_CRC_SIZE, OutputFile); fprintf(MessageFile, "Video Packet %4ld Was Received (CRCcheckFail = %d)\n", VideoPacketNumber, CRCcheckFail); }else fprintf(MessageFile, "Video Packet %4ld Was Lost\n", VideoPacketNumber); dposition = 0; } } fprintf(MessageFile, "Processing Complete.\n"); fprintf(MessageFile, "TotalVideoPackets: %ld\n", VideoPacketNumber); fprintf(MessageFile, "TotalVideoBytes: %ld\n", TotalVideoBytes); fprintf(MessageFile, "PayloadAndOverheadBytesForVideo: %ld\n", (TotalVideoBytes + VideoPacketNumber * AL3_CRC_SIZE + VideoMuxPacketNumber * (SYNC_SIZE + HEADER_SIZE))); fprintf(MessageFile, "VideoPayloadBitRate: %f bps\n", TotalVideoBytes * (double) TotalBitRate / (double) TotalBytes); fprintf(MessageFile, "VideoPayloadPlusOverheadBitRate: %f bps\n", (TotalVideoBytes + VideoPacketNumber * AL3_CRC_SIZE + VideoMuxPacketNumber * (SYNC_SIZE + HEADER_SIZE)) * (double) TotalBitRate / (double) TotalBytes); fprintf(MessageFile, "TotalTimeElapsed: %f sec\n", TotalBytes * 8.0 / TotalBitRate); fprintf(MessageFile, "End of program.\n"); return 0; }