📄 rtp.c
字号:
/*
***********************************************************************
* COPYRIGHT AND WARRANTY INFORMATION
*
* Copyright 2001, International Telecommunications Union, Geneva
*
* DISCLAIMER OF WARRANTY
*
* These software programs are available to the user without any
* license fee or royalty on an "as is" basis. The ITU disclaims
* any and all warranties, whether express, implied, or
* statutory, including any implied warranties of merchantability
* or of fitness for a particular purpose. In no event shall the
* contributor or the ITU be liable for any incidental, punitive, or
* consequential damages of any kind whatsoever arising from the
* use of these programs.
*
* This disclaimer of warranty extends to the user of these programs
* and user's customers, employees, agents, transferees, successors,
* and assigns.
*
* The ITU does not represent or warrant that the programs furnished
* hereunder are free of infringement of any third-party patents.
* Commercial implementations of ITU-T Recommendations, including
* shareware, may be subject to royalty fees to patent holders.
* Information regarding the ITU-T patent policy is available from
* the ITU Web site at http://www.itu.int.
*
* THIS IS NOT A GRANT OF PATENT RIGHTS - SEE THE ITU-T PATENT POLICY.
************************************************************************
*/
/*!
************************************************************************
* \file rtp.c
*
* \brief
* Network Adaptation layer for RTP packets
*
* \author
* Main contributors (see contributors.h for copyright, address and affiliation details)
* - Stephan Wenger <stewe@cs.tu-berlin.de>
************************************************************************
*/
/*!
A quick guide to the basics of the RTP decoder implementation
This module contains the RTP packetization, de-packetization, and the
handling of Parameter Sets, see VCEG-N52 and accompanying documents.
Note: Compound packets are not yet implemented!
The interface between every NAL (including the RTP NAL) and the VCL is
based on Slices. The slice data structure on which the VCL is working
is defined in the type Slice (in defines.h). This type contains the
various fields of the slice header and a partition array, which itself
contains the data partitions the slice consists of. When data
partitioning is not used, then the whole slice bit string is stored
in partition #0. When individual partitions are missing, this is
indicated by the size of the bit strings in the partition array.
A complete missing slice (e.g. if a Full Slice packet was lost) is
indicated in a similar way.
part of the slice structure is the error indication (ei-flag). The
Ei-flag is set in such cases in which at least one partition of a slice
is damaged or missing.When data partitioning is used, it can happen that
one partition does not contain any symbols but the ei_flag is cleared,
which indicates the intentional missing of symbols of that partition.
A typical example for this behaviour is the Intra Slice, which does not
have symnbols in its type C partition.
The VCL requests new data to work on through the call of readSliceRTP().
This function calls the main state machine of this module in ReadRTPpaacket().
ReadRTPpacket assumes, when called, that in an error free environment
a complete slice, consisting of one Full Slice RTP packet, or three Partition
packets of types A, B, C with consecutive sequence numbers, can be read.
It first interprets any trailing SUPP and Parameter Update (Header) packets.
Then it reads one video data packet. Two cases have to be distinguished:
1. Type A, or Full Slice packet
In this case, the PictureID and the macroblock mumbers are used to
identify the potential loss of a slice. A slice is lost, when the
StartMB of the newly read slice header is not equal to the current
state of the decoder
1.1 Loss detected
In this case the last packet is unread (fseek back), and a dummy slice
containing the missing macroblocks is conveyed to the VCL. At the next
call of the NAL, the same packet is read again, but this time no packet
loss is detected by the above algorithm,
1.2. No loss
In this case it is checked whether a Full Slice packet or a type A data
partition was read
1.2.1 Full Slice
The Full Slice packet is conveyed to the NAL
1.2.2 Type A Partition
The function RTPReadDataPartitionedSlice() is called, which collects
the remaining type B, C partitions and handles them appropriately.
Paraneter Update Packets (aka Header packets) are in an SDP-like syntax
and are interpreted by a simple parser in the function
RTPInterpretParameterSetPacket()
Each Slice header contaions the information on which parameter set to be used.
The function RTPSetImgInp() copies the information of the relevant parameter
set in the VCL's global variables img-> and inp-> IMPORTANT: any changes
in the semantics of the img-> and inp-> structure members must be represented
in this function as well!
A note to the stream-buffer data structure: The stream buffer always contains
only the contents of the partition in question, and not the slice/partition
header. Decoding has to start at bitoffset 0 (UVLC) or bytreoffset 0 (CABAC).
The remaining functions should be self-explanatory.
*/
#include "contributors.h"
#include <assert.h>
#include <stdlib.h>
#include <malloc.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include "global.h"
#include "elements.h"
#include "bitsbuf.h"
#include "rtp.h"
extern void tracebits(const char *trace_str, int len, int info,int value1,
int value2) ;
extern FILE *bits;
#define MAX_PARAMETER_STRINGLEN 1000
static int CurrentParameterSet = -1;
typedef struct
{
int FramesToBeEncoded;
int FrameSkip;
char SequenceFileName[MAX_PARAMETER_STRINGLEN];
int NumberBFrames;
} InfoSet_t;
static InfoSet_t InfoSet;
ParameterSet_t ParSet[RTP_MAX_PARAMETER_SET];
//! The following two variables are used to calculate the size of a lost slice.
//! The NAL conveyes this "lost" slice to the VCL with the ei_flag set in order
//! to trigger concealment
static int LastPicID; //! PicID of the last packet (current state of the decoder
//! before reading the next packet
static int ExpectedMBNr; //! MB Nr of the last decoded MB
/*!
************************************************************************
* \brief
* read all Partitions of one Slice from RTP packets
* \return
* SOS if this is not a new frame \n
* SOP if this is a new frame \n
* EOS if end of sequence reached
************************************************************************
*/
int readSliceRTP (struct img_par *img, struct inp_par *inp)
{
Slice *currSlice = img->currentSlice;
int PartitionMask;
assert (currSlice != NULL);
PartitionMask = ReadRTPPacket (img, inp, bits);
/*
{
int i;
for (i=0; i<25; i++)
printf ("%02x ", currSlice->partArr[0].bitstream->streamBuffer[i]);
printf ("\n");
for (i=0; i<25; i++)
printf ("%02x ", currSlice->partArr[1].bitstream->streamBuffer[i]);
printf ("\n");
for (i=0; i<25; i++)
printf ("%02x ", currSlice->partArr[2].bitstream->streamBuffer[i]);
printf ("\n");
}
*/
if(PartitionMask == -4711)
return EOS;
if(currSlice->start_mb_nr != 0)
return SOS;
else
return SOP;
}
/*!
************************************************************************
* \brief
* read all partitions of one slice from RTP packet stream, also handle
* any Parameter Update packets and SuUPP-Packets
* \return
* -1 for EOF \n
* Partition-Bitmask otherwise:
* Partition Bitmask: 0x1: Full Slice, 0x2: type A, 0x4: Type B
* 0x8: Type C
************************************************************************
*/
int ReadRTPPacket (struct img_par *img, struct inp_par *inp, FILE *bits)
{
Slice *currSlice = img->currentSlice;
DecodingEnvironmentPtr dep;
byte *buf;
int i=0, dt=0;
unsigned int last_mb=0, picid=0;
int eiflag=1;
static int first=1;
RTPpacket_t *p, *nextp;
RTPSliceHeader_t *sh, *nextsh;
int DoUnread = 0;
int MBDataIndex;
int PartitionMask = 0;
int done = 0;
int err, back=0;
int intime=0;
int ei_flag;
static int last_pframe=0, bframe_to_code=0;
int b_interval;
static unsigned int old_seq=0;
int FirstMacroblockInSlice;
assert (currSlice != NULL);
assert (bits != 0);
if (first)
{
currSlice->max_part_nr = MAX_PART_NR; // Just to get a start
ExpectedMBNr = 0;
LastPicID = -1;
}
// Tenporal storage for this function only
p=alloca (sizeof (RTPpacket_t)); // the RTP packet
p->packet=alloca (MAXRTPPACKETSIZE);
p->payload=alloca (MAXRTPPAYLOADLEN);
nextp=alloca (sizeof (RTPpacket_t));
sh=alloca(sizeof (RTPSliceHeader_t));
sh->RMPNIbuffer=NULL;
sh->MMCObuffer=NULL;
nextsh=alloca(sizeof (RTPSliceHeader_t));
nextsh->RMPNIbuffer=NULL;
nextsh->MMCObuffer=NULL;
ExpectedMBNr = img->current_mb_nr;
LastPicID = img->tr;
done = 0;
do
{
// Filepos = ftell (bits); // to be able to go back one packet
if (RTPReadPacket (p, bits) < 0) // Read and decompose
return -4711;
switch (p->payload[0] & 0xf)
{
case 0: // Full Slice packet
case 1: // Partition type A packet
done = 1;
break;
case 2: // Partition B
case 3: // Partition C
// Do nothing. this results in discarding the unexpected Partition B, C
// packet, which will later be concealed "automatically", because when
// interpreting the next Partition A or full slice packet a range of
// lost blocks is found which will be concealed in the usual manner.
//
// If anyonme comes up with an idea how to use coefficients without the
// header information then this code has to be changed
printf ("ReadRTPPacket(): found unexpected Partition %c packet, skipping\n", p->payload[0]&0xf==2?'B':'C');
break;
case 4:
//! Compound packets may be handled here, but I (StW) would personally prefer and
//! recommend to handle them externally, by a pre-processor tool. For now,
//! compounds lead to exit()
printf ("Compound packets not yet implemented, exit\n");
exit (-700);
break;
case 5:
//! Add implementation of SUPP packets here
printf ("SUPP packets not yet implemented, skipped\n");
break;
case 6:
printf ("Found header packet\n");
if ((err = RTPInterpretParameterSetPacket (&p->payload[1], p->paylen-1)) < 0)
{
printf ("RTPInterpretParameterSetPacket returns error %d\n", err);
}
break;
default:
printf ("Undefined packet type %d found, skipped\n", p->payload[0] & 0xf);
assert (0==1);
break;
}
} while (!done);
// Here, all the non-video data packets and lonely type B, C partitions
// are handled. Now work on the expected type A and full slice packets
assert ((p->payload[0] & 0xf) < 2);
if ((p->payload[0] & 0xf) == 0) // Full Slice packet
{
currSlice->ei_flag = 0;
MBDataIndex = 1; // Skip First Byte
MBDataIndex += RTPInterpretSliceHeader (&p->payload[1], p->paylen-1, 0, sh);
}
else // Partition A packet
{
currSlice->ei_flag = 0;
MBDataIndex = 1; // Skip First Byte
MBDataIndex += RTPInterpretSliceHeader (&p->payload[1], p->paylen-1, 1, sh);
}
FirstMacroblockInSlice = sh->FirstMBInSliceY * (img->width/16) +
sh->FirstMBInSliceX; //! assumes picture sizes divisble by 16
// The purpose of the following if cascade is to check for a lost
// segment of macroblocks. if such a segment is found, a dummy slice
// without content, but with ei_flag set is generated in order to trigger
// concealment.
if(first)
{
bframe_to_code = InfoSet.NumberBFrames+1;
}
if (LastPicID == sh->PictureID) // we are in the same picture
{
first = FALSE;
if (ExpectedMBNr == FirstMacroblockInSlice)
{
currSlice->partArr[0].bitstream->ei_flag = 0; // everything seems to be ok.
}
else
{
if (FirstMacroblockInSlice == 0)
{
assert ("weird! PicID wrap around? Should not happen\n");
}
else
{
//printf ("SLICE LOSS 1: Slice loss at PicID %d, macoblocks %d to %d\n",LastPicID, ExpectedMBNr, FirstMacroblockInSlice-1);
back=p->packlen+8;
fseek (bits, -back, SEEK_CUR);
//FirstMacroblockInSlice = (img->width*img->height)/(16*16);
currSlice->ei_flag = 1;
currSlice->dp_mode = PAR_DP_1;
currSlice->start_mb_nr = ExpectedMBNr;
//! just the same slice we just read!
currSlice->next_header = RTPGetFollowingSliceHeader (img, nextp, nextsh);
assert (currSlice->start_mb_nr == img->current_mb_nr);
#if _ERROR_CONCEALMENT_
currSlice->last_mb_nr = FirstMacroblockInSlice-1;
#else
currSlice->last_mb_nr = FirstMacroblockInSlice;
#endif
currSlice->partArr[0].bitstream->bitstream_length=0;
currSlice->partArr[0].bitstream->read_len=0;
currSlice->partArr[0].bitstream->code_len=0;
currSlice->partArr[0].bitstream->ei_flag=1;
img->tr = currSlice->picture_id = LastPicID;
return 0;
}
}
}
else // we are in a different picture
{
b_interval = (int)((float)(InfoSet.FrameSkip +1)/(float)(InfoSet.NumberBFrames +1) + 0.49999);
if (ExpectedMBNr == 0) // the old picture was finished
{
if (((last_pframe + InfoSet.FrameSkip +1)%256) == sh->PictureID &&
(bframe_to_code > InfoSet.NumberBFrames) && !first) //! we received a new P-Frame and coded all B-Frames
{
//! This P-Frame is the one we expected but maybe parts of it are lost!
if(InfoSet.NumberBFrames)
{
last_pframe = sh->PictureID-(InfoSet.FrameSkip +1);
if(last_pframe < 0)
last_pframe += 256;
bframe_to_code = 1;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -