//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 enumreading_status state =SEQUENCE_HEADER; unsigned int g_index_in_packet_buffer =HEADER_LENGTH; staticunsigned long g_time_stamp =0; staticunsigned long g_time_stamp_current =0; staticfloat g_frame_rate =0; staticunsigned int g_delay_time =0; staticunsigned int g_timetramp_increment =0; FILE *mpfd; SOCKET socket1; RTP_FIXED_HEADER *rtp_hdr; MPEG_VID_SPECIFIC_HDR *mpeg_hdr; #if0 voidSend_RTP_Packet(unsigned char*buf,intbytes) { inti =0; intcount =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,intbytes) { returnsend( socket1, (char*) buf, bytes, 0); } #endif voidmain(intargc, char*argv[]) { unsigned intnext_start_code; unsigned intnext_start_code_index; unsigned intsent_bytes; unsigned shortseq_num =0; unsigned shortstream_num =10; structsockaddr_in server; intlen =sizeof(server); #if0 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, (constsockaddr *)&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; gotoSent_Packet; } } switch(next_start_code) { caseSEQUENCE_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; gotoSent_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; caseGROUP_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; gotoSent_Packet; } state =GROUP_START; casePICTURE_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; gotoSent_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; casePACKET_BUFFER_END: //<! There is one more slice in the packet buffer //<! Anyway, we only sent the integrated slice if(state ==SLICE_AGAIN) { state =UNKNOWN; gotoSent_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; gotoSent_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; gotoSent_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 intfind_next_start_code(unsigned int*next_start_code_index) //NOTE: all start codes ARE byte-aligned { unsigned intbyte0=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; returnPACKET_BUFFER_END; } printf("Error reading buffer../n"); exit(-1); return-1; } voidreset_buffer_index(void) { g_index_in_packet_buffer =HEADER_LENGTH; } //======================================================== floatframe_rate(intbuffer_index) { unsigned charframe_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 intextract_temporal_reference(intbuffer_index) //10 bits { unsigned intlow2bits=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 intread_picture_type(intbuffer_index) { unsigned intpictype=0; pictype =(unsigned int) buf[buffer_index+5]; pictype =(pictype >>3) &(0x7); return(pictype); } //======================================================= unsigned intread_FFV(intbuffer_index) //1 bit { return( (int) ((buf[buffer_index+7] &(0x4)) >>2)); } //======================================================= unsigned intread_FFC(intbuffer_index) //3 bits { unsigned intFFC=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 intread_FBV(intbuffer_index) //1 bit { return( (int) ((buf[buffer_index+8] &(0x40))>>6) ); } //======================================================= unsigned intread_BFC(intbuffer_index) //3 bits { return( (int) ( (buf[buffer_index+8] &(0x38) ) >>3) ); } voidvalidate_file() { /**//*to validate the file, ensure the existance of a startcode */ intj=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() { intError; WORD VersionRequested; WSADATA WsaData; VersionRequested=MAKEWORD(2,2); Error=WSAStartup(VersionRequested,&WsaData); //启动WinSock2 if(Error!=0) { returnFALSE; } else { if(LOBYTE(WsaData.wVersion)!=2||HIBYTE(WsaData.wHighVersion)!=2) { WSACleanup(); returnFALSE; } } returnTRUE; }