// MPEG2RTP.h
#include <stdio.h> #include <stdlib.h> #include <conio.h> #include <string.h> #include <winsock2.h> #include <winsock2.h> //#include "mem.h" //<! Start code or other signal #define PACK_STARTCODE (unsigned int)0x000001ba #define SYSTEM_HEADER_STARTCODE (unsigned int)0x000001bb #define PICTURE_START_CODE (unsigned int)0x00000100 #define GROUP_START_CODE (unsigned int)0x000000B8 #define ISO_11172_ENDCODE (unsigned int)0x000001b9 #define SEQUENCE_HEADER_CODE (unsigned int)0x000001b3 #define PACKET_BUFFER_END (unsigned int)0x00000000 #define MAX_RTP_PKT_LENGTH 1440 #define HEADER_LENGTH 16 #define DEST_IP "192.168.0.98" #define DEST_PORT 1000 #define MPA 14 /*MPEG PAYLOAD TYPE */ #define MPV 32 typedef struct { /**//* byte 0 */ unsigned char csrc_len:4; /**//* expect 0 */ unsigned char extension:1; /**//* expect 1, see RTP_OP below */ unsigned char padding:1; /**//* expect 0 */ unsigned char version:2; /**//* expect 2 */ /**//* byte 1 */ unsigned char payload:7; /**//* RTP_PAYLOAD_RTSP */ unsigned char marker:1; /**//* expect 1 */ /**//* bytes 2, 3 */ unsigned short seq_no; /**//* bytes 4-7 */ unsigned long timestamp; /**//* bytes 8-11 */ unsigned long ssrc; /**//* stream number is used here. */ } RTP_FIXED_HEADER; typedef struct{ //byte 0 unsigned char TR_high2:2; /**//* Temporal Reference high 2 bits*/ unsigned char T:1; /**//* video specific head extension flag */ unsigned char MBZ:5; /**//* unused */ //byte1 unsigned char TR_low8:8; /**//* Temporal Reference low 8 bits*/ //byte3 unsigned char P:3; /**//* picture type; 1=I,2=P,3=B,4=D */ unsigned char E:1; /**//* set if last byte of payload is slice end code */ unsigned char B:1; /**//* set if start of payload is slice start code */ unsigned char S:1; /**//* sequence header present flag */ unsigned char N:1; /**//* N bit; used in MPEG 2 */ unsigned char AN:1; /**//* Active N bit */ //byte4 unsigned char FFC:3; /**//* forward_f_code */ unsigned char FFV:1; /**//* full_pel_forward_vector */ unsigned char BFC:3; /**//* backward_f_code */ unsigned char FBV:1; /**//* full_pel_backward_vector */ } MPEG_VID_SPECIFIC_HDR; /**//* 4 BYTES */ enum reading_status { SLICE_AGAIN, SLICE_BREAK, UNKNOWN, SLICE, SEQUENCE_HEADER, GROUP_START, PICTURE }; void validate_file(); float frame_rate(int buffer_index); unsigned int read_picture_type(int buffer_index); unsigned int read_FBV(int buffer_index); unsigned int read_BFC(int buffer_index); unsigned int read_FFV(int buffer_index); unsigned int read_FFC(int buffer_index); unsigned int extract_temporal_reference(int buffer_index); unsigned int find_next_start_code(unsigned int*buffer_index); void reset_buffer_index(void); BOOL InitWinsock();
//MPEG2RTP.cpp
//这个程序主要用于RTP封装MPEG2数据的学习和测试,不作任何其它用途 //软件在VS.net 2003中编译通过,但在linux下作小量修改也应编译通过。 //通过VLC测试,VLC能正确接收和解码由本程序发送的TEST.MPV编码流。 // //作者:冯富秋 Tinnal //邮箱:tinnal@163.com #include "MPEG2RTP.h" #pragma comment(lib,"Ws2_32") unsigned char buf[MAX_RTP_PKT_LENGTH +4]; //input buffer enum reading_status state = SEQUENCE_HEADER; unsigned int g_index_in_packet_buffer = HEADER_LENGTH; static unsigned long g_time_stamp =0; static unsigned long g_time_stamp_current =0; staticfloat g_frame_rate =0; static unsigned int g_delay_time =0; static unsigned int g_timetramp_increment =0; FILE *mpfd; SOCKET socket1; RTP_FIXED_HEADER *rtp_hdr; MPEG_VID_SPECIFIC_HDR *mpeg_hdr; #if 0 void Send_RTP_Packet(unsigned char*buf,int bytes) { int i =0; int count =0; printf("/nPacket length %d/n",bytes); printf("RTP Header: [M]:%s [sequence number]:0x%lx [timestamp]:0x%lx/n", rtp_hdr->marker ==1?"TRUE":"FALSE", rtp_hdr->seq_no, rtp_hdr->timestamp); printf(" [TR]:%d [AN]:%d [N]:%d [Sequence Header]:%s / /n [Begin Slice]:%s [End Slice]:%s / /n [Pictute Type]:%d / /n [FBV]:%d [BFC]:%d [FFV]:%d [FFC]:%d/n", (mpeg_hdr->TR_high2 <<8| mpeg_hdr->TR_low8), mpeg_hdr->AN, mpeg_hdr->N, mpeg_hdr->S ==1?"TRUE":"FALSE", mpeg_hdr->B ==1?"TRUE":"FALSE", mpeg_hdr->E ==1?"TRUE":"FALSE", mpeg_hdr->P, mpeg_hdr->FBV, mpeg_hdr->BFC, mpeg_hdr->FFV, mpeg_hdr->FFC); while(bytes --) { printf("%02x ",buf[count++]); if(++i ==16) { i=0; printf("/n"); } } printf("/n"); } #else Send_RTP_Packet(unsigned char*buf,int bytes) { return send( socket1, (char*) buf, bytes, 0 ); } #endif void main(int argc, char*argv[]) { unsigned int next_start_code; unsigned int next_start_code_index; unsigned int sent_bytes; unsigned short seq_num =0; unsigned short stream_num =10; struct sockaddr_in server; int len =sizeof(server); #if 0 mpfd = fopen("E://tinnal//live555//vc_proj//es//Debug//test.mpv", "rb"); #else if (argc <2) { printf("/nUSAGE: %s mpegfile/nExiting../n/n",argv[0]); exit(0); } mpfd = fopen(argv[1], "rb"); #endif if (mpfd == NULL ) { printf("/nERROR: could not open input file %s/n/n",argv[1]); exit(0); } rtp_hdr = (RTP_FIXED_HEADER*)&buf[0]; mpeg_hdr = (MPEG_VID_SPECIFIC_HDR*)&buf[12]; memset((void*)rtp_hdr,0,12); //zero-out the rtp fixed hdr memset((void*)mpeg_hdr,0,4); //zero-out the video specific hdr memset((void*)buf,0,MAX_RTP_PKT_LENGTH +4); InitWinsock(); server.sin_family=AF_INET; server.sin_port=htons(DEST_PORT); //server的监听端口 server.sin_addr.s_addr=inet_addr(DEST_IP); //server的地址 socket1=socket(AF_INET,SOCK_DGRAM,0); connect(socket1, (const sockaddr *)&server, len) ; //read the first packet from the mpeg file //always read 4 extra bytes in (in case there's a startcode there) //but dont send more than MAX_RTP_PKT_LENGTH in one packet fread(&(buf[HEADER_LENGTH]), MAX_RTP_PKT_LENGTH-HEADER_LENGTH+4, 1,mpfd); validate_file(); do { /**//* initialization of the two RTP headers */ rtp_hdr->seq_no = htons(seq_num ++); rtp_hdr->payload = MPV; rtp_hdr->version =2; rtp_hdr->marker =0; rtp_hdr->ssrc = htonl(stream_num); mpeg_hdr->S = mpeg_hdr->E = mpeg_hdr->B=0; do{ next_start_code = find_next_start_code(&next_start_code_index); if ((next_start_code >0x100) && (next_start_code<0x1b0) ) { //<! We reach the first slice start code in current packet buffer. //<! Set the B flag of the mpeg special header if(state == SEQUENCE_HEADER || state ==GROUP_START || state ==PICTURE || state == UNKNOWN) { state = SLICE; mpeg_hdr->B =1; } //<! We reach slice start code in current packet again. //<! Set the E flag of the mpeg special header, //<! and update the sent_bytes to the last slice data end. elseif (state == SLICE ||state == SLICE_AGAIN) { state = SLICE_AGAIN; sent_bytes = next_start_code_index; mpeg_hdr->E =1; } //<! We reach slice start code(the previous slice end) //<! for a broken slice. set the E flag. //<! According to RFC2550, we shouldn't put another slice data to this packet, //<! instead of sent it out. elseif (state == SLICE_BREAK) { state = UNKNOWN; sent_bytes = next_start_code_index; mpeg_hdr->E =1; goto Sent_Packet; } } switch(next_start_code) { case SEQUENCE_HEADER_CODE: //<! SEQUENCE_HEADER_CODE after SLICE_START_CODE //<! we must sent the packet now, so that, the SEQUENCE_HEADER_CODE //<! will appear at the start of the next packet if(state == SLICE || state == SLICE_AGAIN) { state = SEQUENCE_HEADER; sent_bytes = next_start_code_index; //<! Accord to RFC 2550, //<! at the end of a frame we should set RTP marker bit to 1. rtp_hdr->marker =1; goto Sent_Packet; } state = SEQUENCE_HEADER; g_frame_rate = frame_rate(next_start_code_index); g_delay_time = (unsigned int)(1000.0/ g_frame_rate +0.5); //ms g_timetramp_increment = (unsigned int)(90000.0/ g_frame_rate +0.5); //90K Hz mpeg_hdr->S=1; break; case GROUP_START_CODE: //<! GROUP_START_CODE after SLICE_START_CODE //<! we must sent the packet now, so that, the GROUP_START_CODE //<! will appear at the start of the next packet if(state == SLICE || state == SLICE_AGAIN) { state = GROUP_START; sent_bytes = next_start_code_index; //<! Accord to RFC 2550, //<! at the end of a frame we should set RTP marker bit to 1. rtp_hdr->marker =1; goto Sent_Packet; } state = GROUP_START; case PICTURE_START_CODE: //<! PICTURE_START_CODE after PICTURE_START_CODE //<! we must sent the packet now, so that, the PICTURE_START_CODE //<! will appear at the start of the next packet if(state == SLICE || state == SLICE_AGAIN) { state = PICTURE; sent_bytes = next_start_code_index; //<! Accord to RFC 2550, //<! at the end of a frame we should set RTP marker bit to 1. rtp_hdr->marker =1; goto Sent_Packet; } state = PICTURE; mpeg_hdr->TR_high2 = (extract_temporal_reference(next_start_code_index) &0x300 )>>8; mpeg_hdr->TR_low8 = extract_temporal_reference(next_start_code_index) &0xff; mpeg_hdr->P = read_picture_type(next_start_code_index); //now read the motion vectors information if( (mpeg_hdr->P==2) || (mpeg_hdr->P==3)) { //if B- or P-type picture, need forward mv mpeg_hdr->FFV = read_FFV(next_start_code_index); mpeg_hdr->FFC = read_FFC(next_start_code_index); } if( mpeg_hdr->P==3) { // if B-type pictue, need backward mv mpeg_hdr->FBV = read_FBV(next_start_code_index); mpeg_hdr->BFC = read_BFC(next_start_code_index); } //<! Time stamp only increate per frame. //<! But I or P frame. if( mpeg_hdr->P==1|| mpeg_hdr->P ==2 ){ g_time_stamp += g_timetramp_increment; g_time_stamp_current = g_time_stamp; }else{ g_time_stamp += g_timetramp_increment; } break; case PACKET_BUFFER_END: //<! There is one more slice in the packet buffer //<! Anyway, we only sent the integrated slice if(state == SLICE_AGAIN) { state = UNKNOWN; goto Sent_Packet; } //<! There is one Slice in the packet buffer. //<! But the Slice is to big, so we break the slice. if(state == SLICE) { state = SLICE_BREAK; sent_bytes = next_start_code_index; goto Sent_Packet; } //<! There if a broke slice, but in current packet buffer //<! we could not find the end of the slice. //<! Let it in the broke state. if(state == SLICE_BREAK ) { state = SLICE_BREAK; sent_bytes = next_start_code_index; goto Sent_Packet; } break; } }while(next_start_code != PACKET_BUFFER_END); Sent_Packet: rtp_hdr->timestamp = htonl(g_time_stamp_current); Send_RTP_Packet(buf, sent_bytes); //copy the tail data to the head of the packet buffer memmove(&buf[HEADER_LENGTH], &buf[sent_bytes], MAX_RTP_PKT_LENGTH-sent_bytes); //reset the buffer index to zero reset_buffer_index(); //reading data into buffer again fread(&(buf[(MAX_RTP_PKT_LENGTH-sent_bytes)+HEADER_LENGTH]), sent_bytes -HEADER_LENGTH , 1,mpfd); // sleep g_delay_time msec for sending next picture data if(rtp_hdr->marker ==1) Sleep( g_delay_time ); }while(!feof(mpfd)); closesocket(socket1); fclose(mpfd); printf("stream end./n"); } //================================================================== unsigned int find_next_start_code(unsigned int*next_start_code_index) //NOTE: all start codes ARE byte-aligned { unsigned int byte0=0,byte1=0,byte2=0,byte3=0,startcode=0; //while not startcode and have not exceeded max packet length while (g_index_in_packet_buffer < MAX_RTP_PKT_LENGTH) { if (buf[g_index_in_packet_buffer+0] ==0 && buf[g_index_in_packet_buffer+1] ==0 && buf[g_index_in_packet_buffer+2] ==1) { //printf("FOUND startcode %d/n",indx); byte0=(int)buf[g_index_in_packet_buffer+0]; byte1=(int)buf[g_index_in_packet_buffer+1]; byte2=(int)buf[g_index_in_packet_buffer+2]; byte3=(int)buf[g_index_in_packet_buffer+3]; startcode=(byte0 <<24) + (byte1 <<16) + (byte2 <<8) + byte3; *next_start_code_index = g_index_in_packet_buffer; g_index_in_packet_buffer = g_index_in_packet_buffer+4; return(startcode); } else g_index_in_packet_buffer++; } //<! reach the end of the packet buffer if (g_index_in_packet_buffer >= (MAX_RTP_PKT_LENGTH)) { *next_start_code_index = g_index_in_packet_buffer -1; g_index_in_packet_buffer = HEADER_LENGTH; return PACKET_BUFFER_END; } printf("Error reading buffer../n"); exit(-1); return-1; } void reset_buffer_index(void) { g_index_in_packet_buffer = HEADER_LENGTH; } //======================================================== float frame_rate(int buffer_index) { unsigned char frame_rate_code; frame_rate_code = (unsigned char)buf[buffer_index +7] &0xf; switch(frame_rate_code) { case0x1: return23.976; case0x2: return24.0; case0x3: return25.0; case0x4: return29.97; case0x5: return30.0; case0x6: return50.0; case0x7: return59.94; case0x8: return60.0; default: return0; } } //======================================================== unsigned int extract_temporal_reference(int buffer_index) // 10 bits { unsigned int low2bits=0,TR=0; // TR = temporal reference; TR = (unsigned int) (buf[buffer_index+4]); TR <<=2; low2bits = (unsigned int) (buf[buffer_index+5]); TR |= (low2bits >>6); return(TR); } //======================================================== unsigned int read_picture_type(int buffer_index) { unsigned int pictype=0; pictype = (unsigned int) buf[buffer_index+5]; pictype = (pictype >>3) & (0x7); return (pictype); } //======================================================= unsigned int read_FFV(int buffer_index) // 1 bit { return( (int) ((buf[buffer_index+7] & (0x4)) >>2)); } //======================================================= unsigned int read_FFC(int buffer_index) // 3 bits { unsigned int FFC=0,lowbit=0; FFC = (int) (buf[buffer_index+7] & (0x3)); FFC <<=1; lowbit = (int) ((buf[buffer_index+8]) & (0x80)); FFC = FFC | (lowbit >>7 ); return(FFC); } //======================================================= unsigned int read_FBV(int buffer_index) // 1 bit { return( (int) ((buf[buffer_index+8] & (0x40))>>6) ); } //======================================================= unsigned int read_BFC(int buffer_index) // 3 bits { return( (int) ( (buf[buffer_index+8] & (0x38) ) >>3 ) ); } void validate_file() { /**//* to validate the file, ensure the existance of a startcode */ int j=0,valid=0; while ((j++<MAX_RTP_PKT_LENGTH) && (!valid)) { if (!((int)buf[j+0] + (int)buf[j+1]) && (((int)buf[j+2])==1)) valid=1; } if (!valid) { printf("/nERROR: start code not found. / /nInput file must be a valid MPEG I file./n"); exit(0); } } BOOL InitWinsock() { int Error; WORD VersionRequested; WSADATA WsaData; VersionRequested=MAKEWORD(2,2); Error=WSAStartup(VersionRequested,&WsaData); //启动WinSock2 if(Error!=0) { return FALSE; } else { if(LOBYTE(WsaData.wVersion)!=2||HIBYTE(WsaData.wHighVersion)!=2) { WSACleanup(); return FALSE; } } return TRUE; }