38 const std::string & group_address ,
unsigned int packet_type ,
const G::Path & fmtp_file ,
39 int jpeg_fudge_factor ,
const std::string & filter_spec ,
unsigned int source_stale_timeout ) :
42 m_monochrome(monochrome) ,
43 m_packet_type(packet_type) ,
46 m_source_stale_timeout(10U) ,
47 m_fmtp_file(fmtp_file) ,
48 m_jpeg_fudge_factor(jpeg_fudge_factor) ,
49 m_filter_spec(filter_spec) ,
50 m_socket(bind_address.domain()) ,
51 m_packet_buffer(1500U) ,
52 m_packet_stream(jpeg_fudge_factor) ,
54 m_test_timer(*this,&
RtpServer::onTimer,*this) ,
59 m_socket.
bind( bind_address ) ;
64 if( !group_address.empty() )
65 join( group_address ) ;
68 m_test_timer.startTimer( 2U ) ;
74 for( G::StringArray::iterator p = list.begin() ; p != list.end() ; ++p )
86 void Gv::RtpServer::join(
const std::string & group )
91 void Gv::RtpServer::onTimer()
95 G_DEBUG(
"Gv::RtpServer::onTimer: timeout: " << m_seq_old <<
" " << m_test_index ) ;
96 if( ( m_seq_old == 0U && m_test_index == 0 ) || m_test_index > 0 )
98 std::string test_file =
"test-" +
G::Str::fromInt(++m_test_index) +
".dat" ;
101 G_LOG_S(
"Server::onTimer: replaying test data file [" << test_file <<
"]" ) ;
102 std::ifstream f( test_file.c_str() ) ;
103 std::stringstream ss ;
105 std::string s = ss.str() ;
106 onData( s.data() , s.size() ) ;
107 m_test_timer.startTimer( 0U ) ;
112 void Gv::RtpServer::readEvent()
114 G_ASSERT( m_packet_buffer.size() == 1500U ) ;
115 GNet::Socket::ssize_type n = m_socket.read( &m_packet_buffer[0] , m_packet_buffer.size() ) ;
117 onData( &m_packet_buffer[0] , n ) ;
120 void Gv::RtpServer::onData(
const char * p , std::string::size_type n )
126 std::ostringstream ss ;
127 ss <<
"test-" << ++seq <<
".dat" ;
128 std::ofstream file( ss.str().c_str() ) ;
129 file << std::string(p,n) ;
133 processRtpData( p , n ) ;
136 void Gv::RtpServer::processRtpData(
const char * p , std::string::size_type n )
142 G_WARNING(
"Server::processRtpData: ignoring invalid rtp packet: too small" ) ;
146 if( !rtp_packet.valid() )
148 G_WARNING(
"Server::processRtpData: ignoring invalid rtp packet: " << rtp_packet.reason() ) ;
153 if( m_packet_type == 0U )
155 G_LOG(
"Gv::RtpServer::processRtpData: processing packets of type " << rtp_packet.type() ) ;
156 m_packet_type = rtp_packet.type() ;
158 if( m_packet_type != 0U && rtp_packet.type() != m_packet_type )
160 G_WARNING_ONCE(
"Gv::RtpServer::processRtpData: ignoring unwanted rtp packet type: " << rtp_packet.type() <<
" != " << m_packet_type ) ;
161 G_DEBUG(
"Gv::RtpServer::processRtpData: ignoring unwanted rtp packet type: " << rtp_packet.type() <<
" != " << m_packet_type ) ;
166 if( m_source_id == 0U )
168 G_DEBUG(
"Gv::RtpServer::processRtpData: source id: " << rtp_packet.src() ) ;
169 m_source_id = rtp_packet.src() ;
171 if( rtp_packet.src() == m_source_id )
173 m_source_time = ::time(
nullptr) ;
177 if( (m_source_time+m_source_stale_timeout) < ::time(
nullptr) )
179 G_LOG(
"Gv::RtpServer::processRtpData: switching source: " << m_source_id <<
" -> " << rtp_packet.src() ) ;
180 m_source_id = rtp_packet.src() ;
181 m_source_time = ::time(
nullptr) ;
185 G_WARNING(
"Gv::RtpServer::processRtpData: ignoring unknown rtp packet source: " << rtp_packet.src() <<
" != " << m_source_id ) ;
191 if( m_seq_old != 0U && rtp_packet.seq() != (m_seq_old+1U) && rtp_packet.seq() != 0U )
193 G_WARNING(
"Gv::RtpServer::processRtpData: missing packet(s): " << (m_seq_old+1U) <<
"-" << (rtp_packet.seq()-1U) ) ;
195 m_seq_old = rtp_packet.seq() ;
199 if( rtp_packet.typeJpeg() )
205 G_WARNING(
"Gv::RtpServer::processRtpData: invalid rtp-jpeg packet: too small" ) ;
210 if( !jpeg_packet.valid() )
212 G_WARNING(
"Gv::RtpServer::processRtpData: invalid rtp-jpeg packet: " << jpeg_packet.reason() ) ;
217 G_LOG(
"Gv::RtpPacketStream::add: rtp-jpeg packet: " << rtp_packet.str() <<
" ; " << jpeg_packet.str() ) ;
219 if( !filter(jpeg_packet) )
220 m_packet_stream.add( rtp_packet , jpeg_packet ) ;
222 while( m_packet_stream.more() )
223 processJpegPayload( m_packet_stream.get() ) ;
225 else if( rtp_packet.typeDynamic() )
231 G_WARNING(
"Gv::RtpServer::processRtpData: invalid rtp-avc packet: too small" ) ;
236 if( !avc_packet.valid() )
238 G_WARNING(
"Gv::RtpServer::processRtpData: invalid rtp-avc packet: " << avc_packet.reason() ) ;
243 G_LOG(
"Gv::RtpServer::processRtpAvcPacket: rtp-avc packet: " << rtp_packet.str() <<
" ; " << avc_packet.str(
G::Log::at(G::Log::s_Debug)) ) ;
245 if( !filter(avc_packet) )
246 m_packet_stream.add( rtp_packet , avc_packet ) ;
248 while( m_packet_stream.more() )
249 processAvcPayload( m_packet_stream.get() ) ;
260 if( !m_filter_list.empty() )
264 bool match = std::find( m_filter_list.begin() , m_filter_list.end() , nalu_type ) != m_filter_list.end() ;
273 void Gv::RtpServer::processJpegPayload(
const std::vector<char> & payload )
276 Gr::JpegInfo jpeg_info( &payload[0] , payload.size() ) ;
278 G_DEBUG(
"Gv::RtpServer::processJpegFrame: processing jpeg image: " << image_type ) ;
280 int scale = autoscale( m_scale , jpeg_info.dx() ) ;
281 if( scale > 1 || m_monochrome )
283 m_jpeg_reader.setup( scale , m_monochrome ) ;
284 m_jpeg_reader.decode( m_jpeg_image_data , &payload[0] , payload.size() ) ;
285 m_jpeg_writer.encode( m_jpeg_image_data , m_jpeg_buffer ) ;
286 m_handler.onImage( m_jpeg_buffer ,
Gr::ImageType::jpeg(image_type,scale,m_monochrome) ,
true ) ;
290 m_handler.onImage( payload , image_type ,
true ) ;
294 void Gv::RtpServer::processAvcPayload(
const std::vector<char> & payload )
297 if( m_avcc.get() ==
nullptr && m_fmtp_file !=
G::Path() )
299 G_DEBUG(
"Gv::RtpServer::processAvcPayload: fmtp processing" ) ;
300 std::string fmtp = readFmtpFile( m_fmtp_file ) ;
305 throw InvalidFmtp( avcc.
reason() ) ;
309 if( m_avc_reader_stream.get() == nullptr )
315 if( m_avc_reader_stream.get() == nullptr )
317 G_DEBUG(
"Gv::RtpServer::processAvcFrame: initialising avc decoder without fmtp" ) ;
322 G_DEBUG(
"Gv::RtpServer::processAvcFrame: avc decode: " << G::hexdump<16>(payload.begin(),payload.end()) ) ;
323 Gv::AvcReader reader( *m_avc_reader_stream.get() , &payload[0] , payload.size() ) ;
326 m_output_buffer.clear() ;
327 Gr::ImageType image_type = reader.fill( m_output_buffer , autoscale(m_scale,reader.dx()) , m_monochrome ) ;
328 m_handler.onImage( m_output_buffer , image_type , reader.keyframe() ) ;
332 G_DEBUG(
"Gv::RtpServer::processAvcFrame: avc decode failed" ) ;
336 int Gv::RtpServer::autoscale(
int s ,
int dx )
341 dx = std::max( 0 , dx ) ;
342 while( (dx/s) > 800 )
348 void Gv::RtpServer::onException( std::exception & e )
350 G_DEBUG(
"Gv::RtpServer::onException: " << e.what() ) ;
354 std::string Gv::RtpServer::readFmtpFile(
const G::Path & fmtp_file )
359 f.open( fmtp_file.
str().c_str() ) ;
365 std::getline( f , line ) ;
366 }
while( line.find(
"#") == 0U ) ;
372 Gv::RtpServerHandler::~RtpServerHandler()
std::string str() const
Returns the path string.
static size_t smallest()
The smallest parsable packet.
A decoder for an AVC (aka H.264) video packet.
static size_t smallest()
Returns the smallest valid packet size.
RtpServer(RtpServerHandler &, int scale, bool monochrome, GNet::Address bind_address, const std::string &group_address, unsigned int packet_type, const G::Path &fmtp_file, int jpeg_fudge_factor, const std::string &filter_spec, unsigned int source_stale_timeout)
Constructor.
An RTP payload parser for the jpeg payload type.
static bool at(Severity)
Returns true if G::LogOutput::output() would log at the given level.
static void join(SOCKET, const std::string &)
Joins the socket to the multicast group. IPv4 only. Throws on error.
static ImageType jpeg(int dx, int dy, int channels=3)
Factory function for a jpeg image type.
Contains AVC configuration parameters, initialised from an "avcC" file segment or from an SDP "fmtp" ...
The GNet::Address class encapsulates a TCP/UDP transport address.
std::vector< std::string > StringArray
A std::vector of std::strings.
An encapsulation of image type, including width, height and number of channels, with support for a st...
A class which acquires the process's special privileges on construction and releases them on destruct...
void addReadHandler(EventHandler &handler)
Adds this socket to the event source list so that the given handler receives read events...
static void splitIntoTokens(const std::string &in, StringArray &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
unsigned int type() const
Returns the RTP-AVC packet type, matching the Type enum.
static bool isNumeric(const std::string &s, bool allow_minus_sign=false)
Returns true if every character is a decimal digit.
virtual ~RtpServer()
Destructor.
static std::string fromInt(int i)
Converts int 'i' to a string.
void bind(const Address &)
Binds the socket with the given address.
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
static bool enabled()
Returns true if test features are enabled.
static Configuration fromFmtp(const std::string &fmtp)
Factory function taking a SDP (Session Description Protocol) "fmtp" attribute string, something like "profile-level-id=...; ...; sprop-parameters-sets=Z00AKZpmA8==,aO48gA==".
static size_t smallest()
The smallest parsable packet.
std::string reason() const
Returns the in-valid() reason.
static bool exists(const Path &file)
Returns true if the file (directory, device etc.) exists.
An RTP packet parser, as per RFC 3550 (section 5).
bool valid() const
Returns true if a usable object.
unsigned int fu_type() const
Returns the type of the fragmented NALU.
Holds state for an AVC (aka H.264) decoder.
Provides some basic information about a jpeg image without full decoding.
An RTP payload parser for the "H264" payload type.
A interface that Gv::RtpServer uses to deliver image data.
bool type_is_fu() const
Returns true if type() is FU_A or FU_B.
A Path object represents a file system path.