VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gvrtppacketstream.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gvrtppacketstream.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gvrtppacketstream.h"
23 #include "gravc.h"
24 #include "ghexdump.h"
25 #include "gassert.h"
26 
27 // TODO denial-of-service limits
28 // TODO elide jpeg and avc code
29 
30 Gv::RtpPacketStream::RtpPacketStream( int jpeg_fudge_factor ) :
31  m_jpeg_fudge_factor(jpeg_fudge_factor) ,
32  m_timestamp(0)
33 {
34 }
35 
36 bool Gv::RtpPacketStream::add( const RtpPacket & rtp_packet , const RtpJpegPacket & jpeg_packet )
37 {
38  const size_t fragment_offset = jpeg_packet.fo() ;
39  const bool last_fragment = rtp_packet.marker() ;
40  const bool first_fragment = fragment_offset == 0U ;
41  const bool is_single = first_fragment && last_fragment ;
42 
43  bool used = false ;
44  if( is_single )
45  {
46  clear() ;
47  m_buffer.insert( m_buffer.end() , jpeg_packet.payloadBegin() , jpeg_packet.payloadEnd() ) ;
48  m_seq_list.push_back( rtp_packet.seq() ) ;
49  commit( jpeg_packet ) ;
50  used = true ;
51  }
52  else
53  {
54  if( first_fragment )
55  {
56  clear() ;
57  m_timestamp = rtp_packet.timestamp() ;
58  m_buffer.insert( m_buffer.end() , jpeg_packet.payloadBegin() , jpeg_packet.payloadEnd() ) ;
59  m_seq_list.push_back( rtp_packet.seq() ) ;
60  used = true ;
61  }
62  else if( rtp_packet.timestamp() != m_timestamp || fragment_offset != m_buffer.size() )
63  {
64  G_WARNING( "Gv::RtpServer::processRtpJpegPacket: ignoring out-of-sequence packet: " << rtp_packet.str() ) ;
65  clear() ;
66  used = false ;
67  }
68  else
69  {
70  m_buffer.insert( m_buffer.end() , jpeg_packet.payloadBegin() , jpeg_packet.payloadEnd() ) ;
71  m_seq_list.push_back( rtp_packet.seq() ) ;
72  if( last_fragment )
73  {
74  if( contiguous() )
75  {
76  commit( jpeg_packet ) ;
77  clear() ;
78  used = true ;
79  }
80  else
81  {
82  G_WARNING( "Gv::RtpServer::processRtpJpegPacket: ignoring frame with missing packets" ) ;
83  clear() ;
84  used = false ;
85  }
86  }
87  }
88  }
89  return used ;
90 }
91 
92 bool Gv::RtpPacketStream::add( const RtpPacket & rtp_packet , const RtpAvcPacket & avc_packet )
93 {
94  bool used = false ;
95  if( avc_packet.type_is_single() )
96  {
97  clear() ;
98  m_buffer.insert( m_buffer.end() , avc_packet.payloadBegin() , avc_packet.payloadEnd() ) ;
99  m_buffer.at(0U) = avc_packet.payloadFirst() ;
100  commit( avc_packet ) ;
101  used = true ;
102  }
103  else if( avc_packet.type_is_fu() )
104  {
105  if( avc_packet.fu_start() )
106  {
107  clear() ;
108  m_timestamp = rtp_packet.timestamp() ;
109  m_buffer.insert( m_buffer.end() , avc_packet.payloadBegin() , avc_packet.payloadEnd() ) ;
110  m_buffer.at(0U) = avc_packet.payloadFirst() ;
111  m_seq_list.push_back( rtp_packet.seq() ) ;
112  used = true ;
113  }
114  else if( rtp_packet.timestamp() != m_timestamp )
115  {
116  G_WARNING( "Gv::RtpServer::processRtpAvcPacket: ignoring out-of-sequence packet: " << rtp_packet.str() ) ;
117  clear() ;
118  used = false ;
119  }
120  else
121  {
122  size_t pos = m_buffer.size() ;
123  m_buffer.insert( m_buffer.end() , avc_packet.payloadBegin() , avc_packet.payloadEnd() ) ;
124  m_buffer.at(pos) = avc_packet.payloadFirst() ;
125  m_seq_list.push_back( rtp_packet.seq() ) ;
126  if( avc_packet.fu_end() )
127  {
128  if( contiguous() )
129  {
130  commit( avc_packet ) ;
131  clear() ;
132  used = true ;
133  }
134  else
135  {
136  G_WARNING( "Gv::RtpServer::processRtpAvcPacket: ignoring frame with missing packets" ) ;
137  clear() ;
138  used = false ;
139  }
140  }
141  }
142  }
143  else
144  {
145  G_WARNING( "Gv::RtpServer::processRtpAvcPacket: ignoring rtp-avc aggregation packet: not implemented" ) ;
146  used = false ;
147  }
148  return used ;
149 }
150 
151 bool Gv::RtpPacketStream::contiguous() const
152 {
153  unsigned int old = 0U ;
154  bool first = true ;
155  bool ok = true ;
156  for( std::vector<unsigned int>::const_iterator p = m_seq_list.begin() ; ok && p != m_seq_list.end() ; ++p , first = false )
157  {
158  ok = first || *p == (old+1U) || ( *p == 0U && old != 0U ) ;
159  old = *p ;
160  }
161  return ok ;
162 }
163 
164 void Gv::RtpPacketStream::commit( const RtpJpegPacket & jpeg_packet )
165 {
166  // add JFIF header and trailer - arbitrarily use the last packet for some of the header values
167  RtpJpegPacket::generateHeader( std::inserter(m_buffer,m_buffer.begin()) , jpeg_packet , m_jpeg_fudge_factor ) ;
168  m_buffer.push_back( 0xff ) ;
169  m_buffer.push_back( 0xd9 ) ; // EOI
170 
171  m_list.push_back( std::vector<char>() ) ;
172  m_buffer.swap( m_list.back() ) ;
173  G_ASSERT( m_buffer.empty() ) ;
174 }
175 
176 void Gv::RtpPacketStream::commit( const RtpAvcPacket & )
177 {
178  // add NALU four-byte 00-00-00-01 start-code
179  const std::string & start_code = Gr::Avc::Rbsp::_0001() ;
180  m_buffer.insert( m_buffer.begin() , start_code.begin() , start_code.end() ) ;
181 
182  m_list.push_back( std::vector<char>() ) ;
183  m_buffer.swap( m_list.back() ) ;
184  G_ASSERT( m_buffer.empty() ) ;
185 }
186 
187 void Gv::RtpPacketStream::clear()
188 {
189  m_buffer.clear() ;
190  m_seq_list.clear() ;
191  m_timestamp = 0UL ;
192 }
193 
195 {
196  return !m_list.empty() ;
197 }
198 
199 std::vector<char> Gv::RtpPacketStream::get()
200 {
201  G_ASSERT( !m_list.empty() ) ;
202  std::vector<char> result = m_list.front() ;
203  m_list.pop_front() ;
204  return result ;
205 }
206 
207 /// \file gvrtppacketstream.cpp
const char * payloadBegin() const
Returns the RTP-AVC payload pointer, but the first byte may be wrong so use payloadFirst() to overwri...
bool add(const RtpPacket &, const RtpAvcPacket &)
Adds a AVC packet.
An RTP payload parser for the jpeg payload type.
unsigned long fo() const
Returns the fragment offset.
std::string str() const
Returns a one-line summary of header fields.
Definition: gvrtppacket.cpp:72
Synopsis:
bool type_is_single() const
Returns true if type() is SINGLE_NALU_x.
bool fu_start() const
Returns true for the first FU packet in the fragmented NALU.
static iterator_t generateHeader(iterator_t out, const RtpJpegPacket &, int fudge=0)
Generates the start of a JFIF buffer; the rest of the JFIF buffer is a simple copy of all the payload...
const char * payloadEnd() const
Returns the RTP-AVC payload end pointer.
RtpPacketStream(int jpeg_fudge_factor)
Constructor.
const char * payloadBegin() const
Returns payload().begin.
const char * payloadEnd() const
Returns payload().end.
An RTP packet parser, as per RFC 3550 (section 5).
Definition: gvrtppacket.h:44
bool more() const
Returns true if NALUs or JFIFs are available.
std::vector< char > get()
Extracts a NALU or JFIF.
unsigned long timestamp() const
Returns the timestamp.
An RTP payload parser for the "H264" payload type.
char payloadFirst() const
Returns the first RTP-AVC payload byte.
unsigned int seq() const
Returns the sequence number.
Definition: gvrtppacket.cpp:98
bool marker() const
Returns the marker bit.
Definition: gvrtppacket.cpp:93
bool type_is_fu() const
Returns true if type() is FU_A or FU_B.
bool fu_end() const
Returns true for the last FU packet in the fragmented NALU.
static const std::string & _0001()
Returns the 00-00-00-01 string.
Definition: gravc.cpp:697