diff options
Diffstat (limited to 'TAO/orbsvcs/orbsvcs/AV/RTP.cpp')
-rw-r--r-- | TAO/orbsvcs/orbsvcs/AV/RTP.cpp | 785 |
1 files changed, 785 insertions, 0 deletions
diff --git a/TAO/orbsvcs/orbsvcs/AV/RTP.cpp b/TAO/orbsvcs/orbsvcs/AV/RTP.cpp new file mode 100644 index 00000000000..4e53a5f63e5 --- /dev/null +++ b/TAO/orbsvcs/orbsvcs/AV/RTP.cpp @@ -0,0 +1,785 @@ +/* + * Copyright (c) 1994-1995 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and the Network Research Group at + * Lawrence Berkeley Laboratory. + * 4. Neither the name of the University nor of the Laboratory may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +// $Id$ + +#include "orbsvcs/AV/RTP.h" +#include "orbsvcs/AV/RTCP.h" + +#include "tao/debug.h" +#include "ace/OS_NS_arpa_inet.h" +#include "ace/OS_NS_strings.h" + +TAO_BEGIN_VERSIONED_NAMESPACE_DECL + +// RTP_Packet + +// Constructor for RTP packets received +RTP_Packet::RTP_Packet(char* buffer, int length) +{ + // skip the standard header info + int index = 12; + + memcpy(this->packet_, buffer, length); + + for (int j=0; j<(int)this->cc(); j++) + { + this->host_byte_order_csrc_list_[j] = ntohl(*(ACE_UINT32*)&buffer[index]); + index+=4; + } + + // ignore the header extension if there is one + if (this->ext()) + { + index+=2; + int extension_data_size = ntohs(*(ACE_UINT16*)&buffer[index]); + index+=2; + index+=extension_data_size; + + this->extension_bytes_ = 4 + extension_data_size; + } + else + this->extension_bytes_ = 0; + + this->packet_size_ = static_cast<ACE_UINT16> (length); + this->payload_size_ = static_cast<ACE_UINT16> (length-index); + + // This is necessary only for payload types that have 16 bit values to correct + // the network byte ordering. + if ((this->pt() == RTP_PT_L16_OTHER) || + (this->pt() == RTP_PT_L16_STEREO) || + (this->pt() == RTP_PT_L16_MONO)) + { + for (int i=0; i < payload_size_; i+=2) + { + *(ACE_UINT16*)&this->host_byte_order_payload_[i] = ntohs(*(ACE_UINT16*)&this->packet_[index]); + index+=2; + } + } + else + for (int i=0; i<this->payload_size_; i++) + { + this->host_byte_order_payload_[i] = this->packet_[index]; + index++; + } +} + +RTP_Packet::RTP_Packet(unsigned char padding, + unsigned char marker, + unsigned char payload_type, + ACE_UINT32 seq_num, + ACE_UINT32 timestamp, + ACE_UINT32 ssrc, + unsigned char csrc_count, + ACE_UINT32 *csrc_list, + char *data, + ACE_UINT16 data_size) + :extension_bytes_(0) +{ + //size of header (in octets) without contributing sources + ACE_UINT16 size = 3*4; + int index = 0; + + if (data_size > RTP_MTU-12) + { + data_size = RTP_MTU-12; + ACE_DEBUG ((LM_DEBUG, "\n(%N,%l) RTP_Packet: Warning - packet truncated\n")); + } + + if (csrc_count > 15) + csrc_count = 15; // Only 15 contributing sources can be specified. + + if (csrc_list != 0) + for (unsigned char i=0; i<csrc_count; i++) + { + size+=4; + this->host_byte_order_csrc_list_[i] = csrc_list[i]; + } + + this->packet_size_ = size + data_size; + + index = 0; + this->packet_[index] = ((RTP_VERSION & 0x3) << 6) | + ((padding & 0x1) << 5) | + ((0 & 0x1) << 4) | // extension bit + ((csrc_count & 0xf)); + + index++; + this->packet_[index] = ((marker & 0x1) << 7 ) | + ((payload_type & 0x7f)); + index++; + *((ACE_UINT16*)&this->packet_[index]) = (ACE_UINT16)htons(static_cast<u_short> (seq_num)); + index+=2; + *((ACE_UINT32*)&this->packet_[index]) = (ACE_UINT32)htonl(timestamp); + index+=4; + *((ACE_UINT32*)&this->packet_[index]) = htonl(ssrc); + index+=4; + + for (int i=0; i<csrc_count; i++) + { + *((ACE_UINT32*)&this->packet_[index]) = htonl(this->host_byte_order_csrc_list_[i]); + index+=4; + } + + memcpy (this->host_byte_order_payload_, data, data_size); + + this->payload_size_ = data_size; + + // Correct byte ordering for multi-byte payload types. + if ((this->pt() == RTP_PT_L16_OTHER) || + (this->pt() == RTP_PT_L16_STEREO) || + (this->pt() == RTP_PT_L16_MONO)) + { + for (int i=0; i<this->payload_size_; i+=2) + { + *(ACE_UINT16*)&this->packet_[index] = htons(*(ACE_UINT16*)&data[i]); + index+=2; + } + } + else + for (int i=0; i<this->payload_size_; i++) + { + this->packet_[index] = data[i]; + index++; + } +} + +RTP_Packet::~RTP_Packet(void) +{ +} + +ACE_UINT16 +RTP_Packet::packet_size(void) +{ + return this->packet_size_; +} + +ACE_UINT16 +RTP_Packet::payload_size(void) +{ + return this->payload_size_; +} + +unsigned int +RTP_Packet::ver (void) +{ + return ( this->packet_[0] & 0xC0 ) >> 6; +} + +unsigned int +RTP_Packet::pad (void) +{ + return ( this->packet_[0] & 0x20 ) >> 5; +} + +unsigned int +RTP_Packet::ext (void) +{ + return ( this->packet_[0] & 0x10 ) >> 4; +} + +unsigned int +RTP_Packet::cc (void) +{ + return ( this->packet_[0] & 0x0F ) ; +} + +unsigned int +RTP_Packet::mrk (void) +{ + return ( this->packet_[1] & 0x80 ) >> 7; +} + +unsigned int +RTP_Packet::pt (void) +{ + return ( this->packet_[1] & 0x7F ) ; +} + +ACE_UINT16 +RTP_Packet::sn (void) +{ + return ntohs(*(ACE_UINT16*)(&this->packet_[2])) ; +} + +ACE_UINT32 +RTP_Packet::ts (void) +{ + return ntohl(*(ACE_UINT32*)(&this->packet_[4])) ; +} + +ACE_UINT32 +RTP_Packet::ssrc (void) +{ + return ntohl(*(ACE_UINT32*)(&this->packet_[8])) ; +} + +unsigned int +RTP_Packet::ext_bytes (void) +{ + return this->extension_bytes_; +} + +void +RTP_Packet::get_frame_info (TAO_AV_frame_info *frame_info) +{ + frame_info->timestamp = this->mrk(); + frame_info->timestamp = this->ts(); + frame_info->ssrc = this->ssrc(); + frame_info->sequence_num = this->sn(); + frame_info->format = static_cast<CORBA::Octet> (this->pt()); +} + +int +RTP_Packet::is_valid (void) +{ + // taken from RFC 1889 - Appendix A.1 + + // make sure the RTP version is correct + if (this->ver() != RTP_VERSION) + return 0; + + // make sure the payload type is not SR or RR + if ((this->pt() == RTCP_PT_SR) || (this->pt() == RTCP_PT_RR)) + return 0; + + // if the p bit is set, the last octet of the packet must contain a valid + // octet count, in particular, less than the total packet length minus + // the header size. + if (this->pad() != 0) + if ((unsigned int)this->packet_[this->packet_size_] >= + (this->packet_size_ - (12 + this->cc() + this->extension_bytes_))) + return 0; + + // If there is an extension, it is ignored (taken care of in constructor) + + // The length of the packet must be consistent with CC and payload type (if + // payloads have a known length) + + return 1; +} + +void +RTP_Packet::get_csrc_list (ACE_UINT32 **csrc_list, ACE_UINT16 &length) +{ + *csrc_list = this->host_byte_order_csrc_list_; + length = static_cast<ACE_UINT16> (this->cc ()); +} + +void +RTP_Packet::get_payload (char **payload, ACE_UINT16 &length) +{ + *payload = this->host_byte_order_payload_ ; + length = this->payload_size_; +} + +void +RTP_Packet::get_packet_data (char **packet, ACE_UINT16 &length) +{ + *packet = this->packet_; + length = this->packet_size_; +} + + +// TAO_AV_RTP_Object + +int +TAO_AV_RTP_Object::handle_input (void) +{ + TAO_AV_frame_info frame_info; + + if (TAO_debug_level > 1) + ACE_DEBUG ((LM_DEBUG, + "\nTAO_AV_RTP_Object::handle_input\n")); + + // Handles the incoming RTP packet input. + this->frame_.rd_ptr (this->frame_.base ()); + + int n = this->transport_->recv (this->frame_.rd_ptr (), + this->frame_.size ()); + if (n == 0) + ACE_ERROR_RETURN ( (LM_ERROR,"TAO_AV_RTP::handle_input:connection closed\n"),-1); + if (n < 0) + { + if ((errno == EADDRNOTAVAIL) || (errno == ECONNRESET)) + { + this->connection_gone_ = 1; + return -1; + } + else + ACE_ERROR_RETURN ( (LM_ERROR,"TAO_AV_RTP::handle_input:recv error\n"),-1); + } + + this->frame_.wr_ptr (this->frame_.rd_ptr () + n); + ACE_Addr *addr = this->transport_->get_peer_addr (); + + if(this->control_object_) + this->control_object_->handle_control_input (&this->frame_, *addr); + + // Get payload that has been converted to host byte order + char *data_ptr; + ACE_UINT16 length; + RTP_Packet rtp_packet(this->frame_.rd_ptr (), + static_cast<int> (this->frame_.length ())); + + rtp_packet.get_frame_info (&frame_info); + rtp_packet.get_payload(&data_ptr, length); + + this->frame_.rd_ptr (this->frame_.base ()); + memcpy (this->frame_.rd_ptr (), data_ptr, length); + this->frame_.wr_ptr (this->frame_.rd_ptr() + length); + + this->callback_->receive_frame (&this->frame_, &frame_info, *addr); + + return 0; +} + +int +TAO_AV_RTP_Object::send_frame (ACE_Message_Block *frame, + TAO_AV_frame_info *frame_info) +{ +// ACE_Addr *addr = this->transport_->get_peer_addr (); + + if (this->connection_gone_) + { + errno = ECONNRESET; + return -1; + } + + int result = -1; + RTP_Packet *rtp_packet; + ACE_UINT32 csrc_count = 0; // Assume for now no mixers/translators + ACE_UINT32 *csrc_list = 0; + + if (frame_info != 0) + { + if (frame_info->format != this->format_) + ACE_DEBUG ((LM_DEBUG, + "TAO_AV_RTP_Object::send_frame - error: format type mismatch")); + if (frame_info->ssrc != 0) + this->ssrc_ = frame_info->ssrc; + + TAO_AV_RTCP_Object *rtcp_prot_obj = dynamic_cast<TAO_AV_RTCP_Object*> (this->control_object_); + // set the ssrc on the control object so the RTCP traffic can be matched + // to the RTP traffic + rtcp_prot_obj->ssrc(this->ssrc_); + + ACE_NEW_RETURN (rtp_packet, + RTP_Packet (0, // padding + frame_info->boundary_marker, // marker + static_cast<unsigned char> (this->format_), // payload type + frame_info->sequence_num, // sequence num + frame_info->timestamp, // time stamp + this->ssrc_, // ssrc + static_cast<unsigned char> (csrc_count), // csrc count + csrc_list, // csrc list + frame->rd_ptr (), // data + (ACE_UINT16)frame->length ()),// data size + -1); + + frame_info->sequence_num ++; + } + else + { + // TODO: For periodic RTP packets (constant rate), the RFC suggests + // increasing the clock by the number of samples each frame rather + // than relying on the system time + + // The RFC specifies at least one timestamp unit per sample as well as a + // random offset. It used to be in milliseconds so I left it that way + // for non-audio streams. + unsigned int samples_per_sec; + double samples_per_usec; + + switch (this->format_) + { + case RTP_PT_PCMU: + case RTP_PT_CELP: + case RTP_PT_G721: + case RTP_PT_GSM: + case RTP_PT_DVI: + case RTP_PT_LPC: + case RTP_PT_PCMA: + case RTP_PT_G722: + samples_per_sec = 8000; + break; + case RTP_PT_L16_STEREO: + case RTP_PT_L16_MONO: + samples_per_sec = 44100; + break; + default: + samples_per_sec = 1000000; + }; + + samples_per_usec = samples_per_sec/1000000.0; + + ACE_Time_Value now = ACE_OS::gettimeofday(); + + ACE_UINT32 ts = (ACE_UINT32) + (now.sec () * samples_per_sec + + ((double)now.usec () * samples_per_usec) + + this->timestamp_offset_); + + ACE_NEW_RETURN (rtp_packet, + RTP_Packet (0, // padding + 0, // marker + static_cast<unsigned char> (this->format_), // payload type + this->sequence_num_, // sequence num + ts, // time stamp + this->ssrc_, // ssrc + static_cast<unsigned char> (csrc_count), // csrc count + csrc_list, // csrc list + frame->rd_ptr (), // data + (ACE_UINT16)frame->length ()),// data size + -1); + + this->sequence_num_ ++; + } + + char *data_ptr; + ACE_UINT16 data_length; + rtp_packet->get_packet_data (&data_ptr, data_length); + + ACE_Message_Block mb (data_ptr, data_length); + mb.wr_ptr (data_length); + + result = this->transport_->send (&mb); + if (result < 0) + ACE_ERROR_RETURN ( (LM_ERROR,"TAO_AV_RTP::send_frame failed\n"),result); + + TAO_AV_RTCP_Object *rtcp_prot_obj = dynamic_cast<TAO_AV_RTCP_Object*> (this->control_object_); + if (rtcp_prot_obj) + rtcp_prot_obj->handle_control_output (&mb); + + delete rtp_packet; + + return 0; +} + +int +TAO_AV_RTP_Object::send_frame (const iovec *iov, + int iovcnt, + TAO_AV_frame_info *frame_info) +{ + int result = -1; + RTP_Packet *rtp_packet = 0; + ACE_UINT32 csrc_count = 0; // Assume for now no mixers/translators + ACE_UINT32 *csrc_list = 0; + + if (this->connection_gone_) + { + errno = ECONNRESET; + return -1; + } + + if (frame_info != 0) + { + if (frame_info->format != this->format_) + ACE_DEBUG ((LM_DEBUG, + "TAO_AV_RTP_Object::send_frame - error: format type mismatch")); + this->sequence_num_ = static_cast<ACE_UINT16> (frame_info->sequence_num); + if (frame_info->ssrc != 0) + this->ssrc_ = frame_info->ssrc; + + TAO_AV_RTCP_Object *rtcp_prot_obj = dynamic_cast<TAO_AV_RTCP_Object*> (this->control_object_); + // set the ssrc on the control object so the RTCP traffic can be matched + // to the RTP traffic + rtcp_prot_obj->ssrc(this->ssrc_); + ACE_UINT16 data_size = static_cast<ACE_UINT16> (iov[0].iov_len); + + ACE_NEW_RETURN (rtp_packet, + RTP_Packet (0, // padding + frame_info->boundary_marker, // marker + static_cast<unsigned char> (this->format_), // payload type + frame_info->sequence_num, // sequence num + frame_info->timestamp, // time stamp + this->ssrc_, // ssrc + static_cast<unsigned char> (csrc_count), // csrc count + csrc_list, // csrc list + (char *)iov[0].iov_base, // data + data_size), // data size + -1); + + frame_info->sequence_num ++; + } + else + { + // TODO: For periodic RTP packets (constant rate), the RFC suggests + // increasing the clock by the number of samples each frame rather + // than relying on the system time + + // The RFC specifies at least one timestamp unit per sample as well as a + // random offset. It used to be in milliseconds so I left it that way + // for non-audio streams. + + unsigned int samples_per_sec; + double samples_per_usec; + + switch (this->format_) + { + case RTP_PT_PCMU: + case RTP_PT_CELP: + case RTP_PT_G721: + case RTP_PT_GSM: + case RTP_PT_DVI: + case RTP_PT_LPC: + case RTP_PT_PCMA: + case RTP_PT_G722: + samples_per_sec = 8000; + break; + case RTP_PT_L16_STEREO: + case RTP_PT_L16_MONO: + samples_per_sec = 44100; + break; + default: + samples_per_sec = 1000000; + }; + + samples_per_usec = samples_per_sec/1000000.0; + + ACE_Time_Value now = ACE_OS::gettimeofday(); + + ACE_UINT32 ts = (ACE_UINT32) + (now.sec () * samples_per_sec + + ((double)now.usec () * samples_per_usec) + + this->timestamp_offset_); + ACE_UINT16 data_size = static_cast<ACE_UINT16> (iov[0].iov_len); + + ACE_NEW_RETURN (rtp_packet, + RTP_Packet (0, // padding + 0, // marker + static_cast<unsigned char> (this->format_), // payload type + this->sequence_num_, // sequence num + ts, // time stamp + this->ssrc_, // ssrc + static_cast<unsigned char> (csrc_count), // csrc count + csrc_list, // csrc list + (char *)iov[0].iov_base, // data + data_size), // data size + -1); + + this->sequence_num_ ++; + } + + char *data_ptr; + ACE_UINT16 data_length; + rtp_packet->get_packet_data (&data_ptr, data_length); + + iovec send_iov[ACE_IOV_MAX]; + send_iov [0].iov_base = data_ptr; + send_iov [0].iov_len = data_length; + for (int i=1;i<iovcnt; i++) + send_iov [i] = iov [i]; + result = this->transport_->send (send_iov, iovcnt); + + delete rtp_packet; + + if (result < 0) + ACE_ERROR_RETURN ((LM_ERROR,"TAO_AV_RTP::send_frame failed\n"),result); + + return 0; +} + +int +TAO_AV_RTP_Object::send_frame (const char*, + size_t) +{ + return 0; +} + + +TAO_AV_RTP_Object::TAO_AV_RTP_Object (TAO_AV_Callback *callback, + TAO_AV_Transport *transport) + :TAO_AV_Protocol_Object (callback,transport), + control_object_ (0), + connection_gone_ (0) +{ + this->sequence_num_ = static_cast<ACE_UINT16> (ACE_OS::rand ()); + this->timestamp_offset_ = ACE_OS::rand (); + + char buf [BUFSIZ]; + int result = ACE_OS::hostname (buf, BUFSIZ); + unsigned long ipaddr = 0; + if (result == 0) + ipaddr = ACE_OS::inet_addr (buf); + this->ssrc_ = TAO_AV_RTCP::alloc_srcid (ipaddr); + + this->frame_.size (2 * this->transport_->mtu ()); +} + +TAO_AV_RTP_Object::~TAO_AV_RTP_Object (void) +{ +} + +int +TAO_AV_RTP_Object::destroy (void) +{ + if(this->control_object_) + this->control_object_->destroy (); + + this->callback_->handle_destroy (); + delete this; + + return 0; +} + +int +TAO_AV_RTP_Object::set_policies (const TAO_AV_PolicyList &policy_list) +{ + this->policy_list_ = policy_list; + CORBA::ULong const num_policies = this->policy_list_.length (); + TAO_AV_Policy *policy = 0; + + for (u_int i=0; i< num_policies;i++) + { + policy = this->policy_list_ [i]; + switch (policy->type ()) + { + case TAO_AV_PAYLOAD_TYPE_POLICY: + { + TAO_AV_Payload_Type_Policy *payload_policy = + static_cast<TAO_AV_Payload_Type_Policy *> (policy); + if (payload_policy == 0) + ACE_ERROR_RETURN ( (LM_ERROR,"TAO_AV_RTP_Object::send_frame:Payload policy not defined\n"),-1); + this->format_ = payload_policy->value (); + } + break; + case TAO_AV_SSRC_POLICY: + { + TAO_AV_SSRC_Policy *ssrc_policy = + static_cast<TAO_AV_SSRC_Policy *> (policy); + if (ssrc_policy == 0) + ACE_ERROR_RETURN ( (LM_ERROR,"TAO_AV_RTP_Object::send_frame:SSRC policy not defined\n"),-1); + this->ssrc_ = ssrc_policy->value ();; + } + break; + default: + break; + } + } + + return 0; +} + +void +TAO_AV_RTP_Object::control_object (TAO_AV_Protocol_Object *object) +{ + this->control_object_ = object; + TAO_AV_RTCP_Object *rtcp_prot_obj = dynamic_cast<TAO_AV_RTCP_Object*> (this->control_object_); + rtcp_prot_obj->ssrc (this->ssrc_); + rtcp_prot_obj->ts_offset (this->timestamp_offset_); +} + +int +TAO_AV_RTP_Object::start (void) +{ + this->control_object_->start (); + return this->callback_->handle_start (); +} + +int +TAO_AV_RTP_Object::stop (void) +{ + this->control_object_->stop (); + return this->callback_->handle_stop (); +} + +// TAO_AV_RTP_Flow_Factory +TAO_AV_RTP_Flow_Factory::TAO_AV_RTP_Flow_Factory (void) +{ +} + +TAO_AV_RTP_Flow_Factory::~TAO_AV_RTP_Flow_Factory (void) +{ +} + +int +TAO_AV_RTP_Flow_Factory::init (int /* argc */, + char * /* argv */ []) +{ + return 0; +} + +TAO_AV_Protocol_Object* +TAO_AV_RTP_Flow_Factory::make_protocol_object (TAO_FlowSpec_Entry *entry, + TAO_Base_StreamEndPoint *endpoint, + TAO_AV_Flow_Handler *handler, + TAO_AV_Transport *transport) +{ + TAO_AV_Callback *callback = 0; + + if( endpoint->get_callback (entry->flowname (), callback) ) { + ACE_ERROR_RETURN ((LM_ERROR, "(%N,%l) Invalid callback\n"), 0); + } + + TAO_AV_Protocol_Object *object = 0; + ACE_NEW_RETURN (object, + TAO_AV_RTP_Object (callback, + transport), + 0); + callback->open (object, + handler); + endpoint->set_protocol_object (entry->flowname (), + object); + + endpoint->protocol_object_set (); + return object; +} + +int +TAO_AV_RTP_Flow_Factory::match_protocol (const char *flow_string) +{ + if (ACE_OS::strncasecmp (flow_string,"RTP",3) == 0) + { + return 1; + } + return 0; +} + +const char * +TAO_AV_RTP_Flow_Factory::control_flow_factory (void) +{ + return "RTCP"; +} + +TAO_END_VERSIONED_NAMESPACE_DECL + +ACE_FACTORY_DEFINE (TAO_AV, TAO_AV_RTP_Flow_Factory) +ACE_STATIC_SVC_DEFINE (TAO_AV_RTP_Flow_Factory, + ACE_TEXT ("RTP_Flow_Factory"), + ACE_SVC_OBJ_T, + &ACE_SVC_NAME (TAO_AV_RTP_Flow_Factory), + ACE_Service_Type::DELETE_THIS | + ACE_Service_Type::DELETE_OBJ, + 0) |