The RTP receiving part is relatively simple (you do not need to consider jitterbuffer and so on). Start with here.
In fact, there are three steps:
1. Create a UDP listener, such as 5200.
2. After receiving the RTP package, send it to the unpacking program and continue receiving the second one.
3. After one frame is collected, the file is saved or decoded to play the video.
The following describes the specific process in detail:
1. It is very simple to create UDP (here it is just a simple simulation of RTP reception, although it can work normally, but it does not process the RTCP part, it will affect the sender ):
Lass cudpsocket: Public casyncsocket
{
Public:
Cudpsocket ();
Virtual ~ Cudpsocket ();
Virtual void onreceive (INT nerrorcode );
};
Caller: cudpsocket m_udp; m_udp.create. Note: If the specified port cannot be created successfully, try again on port + 1 or + 2.
Override onreceive:
Void cudpsocket: onreceive (INT nerrorcode)
{
Char szbuffer [1500];
Sockaddr_in sockaddr;
Memset (& sockaddr, 0, sizeof (sockaddr ));
Int nsockaddrlen = sizeof (sockaddr );
Int nresult = receivefrom (szbuffer, 1500, (sockaddr *) & sockaddr, & nsockaddrlen, 0 );
If (nresult = socket_error)
{
Return;
}
// Handle the IP address and port of the other party if necessary
Ushort unport = ntohs (sockaddr. sin_port );
Ulong ulip = sockaddr. sin_addr.s_addr;
// Decode the received data
Decode (byte *) szbuffer, nresult );
}
2 received the data, start decode, generally through RTP transmission video mainly h263 (old, 1998,2000), h264, mpeg4-es. Mpeg4-es format is the simplest, start with it.
If you understand rfc3160, you can directly write the analysis format. If you want to be lazy, you can also find out that there is a plugins directory under the opal project. The video contains multiple decompackages, such as h261, h263, h264, and MPEG4, the decoded source code can be used with slight changes.
First, you can see the rtpframe. h file in Video/common, which encapsulates the data and operations of the RTP Header:
/*************************************** **************************************/
/* The contents of this file are subject to the Mozilla Public License */
/* Version 1.0 (the "License"); you may not use this file except T in */
/* Compliance with the license. You may be obtain a copy of the license */
/* Http://www.mozilla.org/MPL */
/**/
/* Software distributed under the license is distributed on an "as is "*/
/* Basis, without warranty of any kind, either express or implied. See */
/* License for the specific language governing rights and limitations under */
/* The license .*/
/**/
/* The original code is the open h323 library .*/
/**/
/* The initial developer of the original code is Matthias Schneider */
/* Copyright (c) 2007 Matthias Schneider, All rights reserved .*/
/**/
/* Contributor (s): Matthias Schneider (ma30002000@yahoo.de )*/
/**/
/* Alternatively, the contents of this file may be used under the terms */
/* The GNU General Public License version 2 or later (the "GPL"), in which */
/* Case the provisions of the GPL are applicable instead of those abve. If */
/* You wish to allow use of your version of this file only under the terms */
/* Of the GPL and not to allow others to use your version of this file under */
/* The MPL, indicate your demo-by deleting the provisions above and */
/* Replace them with the notice and other provisions required by the GPL .*/
/* If you do not delete the provisions above, a recipient may use your */
/* Version of this file under either the MPL or the GPL .*/
/**/
/* The original code was written by Matthias Schneider <ma30002000@yahoo.de> */
/*************************************** **************************************/
# Ifndef _ rtpframe_h __
# DEFINE _ rtpframe_h _ 1
# Ifdef _ msc_ver
# Pragma warning (Disable: 4800) // disable performance warning
# Endif
Class rtpframe {
Public:
Rtpframe (const unsigned char * frame, int framelen ){
_ Frame = (unsigned char *) frame;
_ Framelen = framelen;
};
Rtpframe (unsigned char * frame, int framelen, unsigned char payloadtype ){
_ Frame = frame;
_ Framelen = framelen;
If (_ framelen> 0)
_ Frame [0] = 0x80;
Setpayloadtype (payloadtype );
}
Unsigned getpayloadsize () const {
Return (_ framelen-getheadersize ());
}
Void setpayloadsize (INT size ){
_ Framelen = size + getheadersize ();
}
Int getframelen () const {
Return (_ framelen );
}
Unsigned char * getpayloadptr () const {
Return (_ frame + getheadersize ());
}
Int getheadersize () const {
Int size;
Size = 12;
If (_ framelen <12)
Return 0;
Size + = (_ frame [0] & 0x0f) * 4;
If (! (_ Frame [0] & 0x10 ))
Return size;
If (size + 4) <_ framelen)
Return (size + 4 + (_ frame [size + 2] <8) + _ frame [size + 3]);
Return 0;
}
Bool getmarker () const {
If (_ framelen <2)
Return false;
Return (_ frame [1] & 0x80 );
}
Unsigned getsequencenumber () const {
If (_ framelen <4)
Return 0;
Return (_ frame [2] <8) + _ frame [3];
}
Void setmarker (bool set ){
If (_ framelen <2)
Return;
_ Frame [1] = _ frame [1] & 0x7f;
If (SET) _ frame [1] = _ frame [1] | 0x80;
}
Void setpayloadtype (unsigned char type ){
If (_ framelen <2)
Return;
_ Frame [1] = _ frame [1] & 0x80;
_ Frame [1] = _ frame [1] | (type & 0x7f );
}
Unsigned char getpayloadtype () const
{
If (_ framelen <1)
Return 0xff;
Return _ frame [1] & 0x7f;
}
Unsigned long gettimestamp () const {
If (_ framelen <8)
Return 0;
Return (_ frame [4] <24) + (_ frame [5] <16) + (_ frame [6] <8) + _ frame [7]);
}
Void settimestamp (unsigned long timestamp ){
If (_ framelen <8)
Return;
_ Frame [4] = (unsigned char) (timestamp> 24) & 0xff );
_ Frame [5] = (unsigned char) (timestamp> 16) & 0xff );
_ Frame [6] = (unsigned char) (timestamp> 8) & 0xff );
_ Frame [7] = (unsigned char) (timestamp & 0xff );
};
Protected:
Unsigned char * _ frame;
Int _ framelen;
};
Struct frameheader {
Unsigned int X;
Unsigned int y;
Unsigned int width;
Unsigned int height;
};
# Endif/* _ rtpframe_h __*/
It can be used directly. Of course, writing one by yourself is not troublesome. Many people may not write well. It is estimated that the operation is on the card.
Then, go to the video/MPEG4-ffmpeg directory to see mpeg4.cxx, which contains the complete RFC unpackage reorganization and MPEG4 decoding source code. Directly compiling can be passed. Fortunately, the code is neatly written and extracted. To decode the package, you only need to check this function:
Bool mpeg4decodercontext: decodeframes (const byte * SRC, unsigned & srclen,
Byte * DST, unsigned & dstlen,
Unsigned Int & flags)
{
If (! Ffmpeglibraryinstance. isloaded ())
Return 0;
// Creates our frames
Rtpframe srcrtp (SRC, srclen );
Rtpframe dstrtp (DST, dstlen, rtp_dynamic_payload );
Dstlen = 0;
Flags = 0;
Int srcpayloadsize = srcrtp. getpayloadsize ();
Setdynamicdecodingparams (true); // adjust dynamic settings, restart allowed
// Don't exceed buffer limits. _ encframelen set by resizedecodingframe
If (_ lastpktoffset + srcpayloadsize <_ encframelen)
{
// Copy the payload data into the buffer and update the offset
Memcpy (_ encframebuffer + _ lastpktoffset, srcrtp. getpayloadptr (),
Srcpayloadsize );
_ Lastpktoffset + = srcpayloadsize;
}
Else {
// Likely we dropped the marker packet, so at this point we have
// Full buffer with some of the frame we wanted and some of the next
// Frame.
// I'm on the fence about whether to send the data to
// Decoder and hope for the best, or to throw it all away and start
// Again.
// Throw the data away and ask for an IFRAME
Trace (1, "MPEG4/tdecoder/twaiting for an I-frame ");
_ Lastpktoffset = 0;
Flags = (_ gotagoodframe? Plugincodec_returncoderrequestiframe: 0 );
_ Gotagoodframe = false;
Return 1;
}
// Decode the frame if we got the marker Packet
Int got_picture = 0;
If (srcrtp. getmarker ()){
_ Framenum ++;
Int Len = ffmpeglibraryinstance. avcodecdecodevideo
(_ Avcontext, _ avpicture, & got_picture,
_ Encframebuffer, _ lastpktoffset );
If (LEN> = 0 & got_picture ){
# Ifdef libavcodec_have_source_dir
If (decodererror (_ keyrefreshthresh )){
// Ask for an IFRAME update, but still show what we 've ve got
Flags = (_ gotagoodframe? Plugincodec_returncoderrequestiframe: 0 );
_ Gotagoodframe = false;
}
# Endif
Trace_up (4, "MPEG4/tdecoder/tdecoded" <Len <"bytes" <", resolution: "<_ avcontext-> width <" X "<_ avcontext-> height );
// If the decoding size changes on us, we can catch it and resize
If (! _ Disableresize
& (_ Framewidth! = (Unsigned) _ avcontext-> width
| _ Frameheight! = (Unsigned) _ avcontext-> height ))
{
// Set the decoding width to what avcodec says it is
_ Framewidth = _ avcontext-> width;
_ Frameheight = _ avcontext-> height;
// Set dynamic settings (framesize), restart as needed
Setdynamicdecodingparams (true );
Return true;
}
// It's stride time
Int framebytes = (_ framewidth * _ frameheight * 3)/2;
Plugincodec_video_frameheader * Header
= (Plugincodec_video_frameheader *) dstrtp. getpayloadptr ();
Header-> X = header-> Y = 0;
Header-> width = _ framewidth;
Header-> Height = _ frameheight;
Unsigned char * dstdata = opal_video_frame_data_ptr (header );
For (INT I = 0; I <3; I ++ ){
Unsigned char * srcdata = _ avpicture-> data [I];
Int dst_stride = I? _ Framewidth> 1: _ framewidth;
Int src_stride = _ avpicture-> linesize [I];
Int H = I? _ Frameheight> 1: _ frameheight;
If (src_stride = dst_stride ){
Memcpy (dstdata, srcdata, dst_stride * H );
Dstdata + = dst_stride * h;
}
Else
{
While (h --){
Memcpy (dstdata, srcdata, dst_stride );
Dstdata + = dst_stride;
Srcdata + = src_stride;
}
}
}
// Treating the screen as an RTP is weird
Dstrtp. setpayloadsize (sizeof (plugincodec_video_frameheader)
+ Framebytes );
Dstrtp. setpayloadtype (rtp_dynamic_payload );
Dstrtp. settimestamp (srcrtp. gettimestamp ());
Dstrtp. setmarker (true );
Dstlen = dstrtp. getframelen ();
Flags = plugincodec_returncoderlastframe;
_ Gotagoodframe = true;
}
Else {
Trace (1, "MPEG4/tdecoder/tdecoded" <Len <"bytes without getting a picture ...");
// Decoding error, ask for an IFRAME update
Flags = (_ gotagoodframe? Plugincodec_returncoderrequestiframe: 0 );
_ Gotagoodframe = false;
}
_ Lastpktoffset = 0;
}
Return true;
}
The write is very clear: if (srcrtp. getmarker (), it indicates that the package is full and the decoding starts.
The RFC reduction reorganization of the mpeg4-es is so simple, the next decoding involves using libavcodec. dll.
For more technical articles, see Shi Changquan's personal website: http://www.joyvc.cn