1 # agent-rtp.tcl --
2 #
3 # Source/RTP, MediaAgent and RTPAgent object definitions.
4 #
5 # Copyright (c) 1996-2002 The Regents of the University of California.
6 # All rights reserved.
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions are met:
10 #
11 # A. Redistributions of source code must retain the above copyright notice,
12 # this list of conditions and the following disclaimer.
13 # B. Redistributions in binary form must reproduce the above copyright notice,
14 # this list of conditions and the following disclaimer in the documentation
15 # and/or other materials provided with the distribution.
16 # C. Neither the names of the copyright holders nor the names of its
17 # contributors may be used to endorse or promote products derived from this
18 # software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
21 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
24 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 #
31 # @(#) $Header: /usr/mash/src/repository/mash/mash-1/tcl/net/agent-rtp.tcl,v 1.79 2004/01/12 22:18:59 aswan Exp $
32
33
34 # make sure we have network utilities
35 import SessionAddress NetworkManager Observable mashutils Configuration
36
37 #
38 # Default value for C++ instance variables.
39 #
40 Source/RTP set reportLoss_ 0
41 Session/RTP set nb_ 0
42 Session/RTP set nf_ 0
43 Session/RTP set np_ 0
44 Session/RTP set loopback_ 1
45
46 Source/RTP set badsesslen_ 0
47 Source/RTP set badsessver_ 0
48 Source/RTP set badsessopt_ 0
49 Source/RTP set badsdes_ 0
50 Source/RTP set badbye_ 0
51 SourceLayer/RTP set nchan_ 1
52
53 Session/RTP set badversion_ 0
54 Session/RTP set badoptions_ 0
55 Session/RTP set badfmt_ 0
56 Session/RTP set badext_ 0
57 Session/RTP set nrunt_ 0
58
59 #FIXME
60 Session/RTP set loopbackLayer_ 1000
61
62 Source/RTP public layer-stat which {
63 $self instvar layers_
64 set s 0
65 foreach l $layers_ {
66 set s [expr $s + [$l set $which]]
67 }
68 return $s
69 }
70
71 Source/RTP public ns {} {
72 $self instvar layers_
73 set s 0
74 foreach l $layers_ {
75 set s [expr $s + [$l set cs_] - [$l set fs_]]
76 }
77 return $s
78 }
79
80 Source/RTP public missing {} {
81 $self instvar layers_
82 set s 0
83 foreach l $layers_ {
84 set nm [expr [$l set cs_] - [$l set fs_] - [$l set np_]]
85 if { $nm > 0 } {
86 set s [expr $s + $nm]
87 }
88 }
89 return $s
90 }
91
92 # FIXME
93 Source/RTP instproc is_mixer {} {
94 return [expr [$self srcid] != [$self ssrc]]
95 }
96
97 SourceLayer/RTP set nrunt_ 0
98 SourceLayer/RTP set ndup_ 0
99 SourceLayer/RTP set fs_ 0
100 SourceLayer/RTP set cs_ 0
101 SourceLayer/RTP set np_ 0
102 SourceLayer/RTP set nf_ 0
103 SourceLayer/RTP set nb_ 0
104 SourceLayer/RTP set nm_ 0
105 SourceLayer/RTP set ntp_ts_sec_ 0
106 SourceLayer/RTP set ntp_ts_fsec_ 0
107 SourceLayer/RTP set mts_ 0
108 SourceLayer/RTP set ref_ntp_sec_ 0
109 SourceLayer/RTP set ref_ntp_fsec_ 0
110 SourceLayer/RTP set ref_mts_ 0
111
112
113 Source/RTP public init { sm srcid ssrc addr } {
114 $self next $srcid $ssrc $addr
115 $self set sm_ $sm
116 $self instvar layers_
117 set k 0
118 set report 0
119 if { [$sm info vars network_] != "" } {
120 set net [$sm set network_]
121 set n [$net set nchan_]
122 set report [$net usingRLM]
123 } else {
124 set n [SourceLayer/RTP set nchan_]
125 }
126 while { $k < $n } {
127 set l [new SourceLayer/RTP]
128 lappend layers_ $l
129 $self layer $k $l
130 incr k
131 }
132
133 $self set reportLoss_ $report
134 }
135
136
137 Source/RTP public destroy {} {
138 $self instvar sm_
139
140 # delete all the layers associated to this source (SourceLayer/RTP objects)
141 foreach layer [$self set layers_] {
142 $layer destroy
143 }
144
145 # if the Source/RTP object is associated to a data handler (i.e., if the
146 # source to which the object is associated is sending data), deactivate
147 # the corresponding decoder
148 if {[$self data-handler] != ""} {
149 $self deactivate
150 }
151
152 # unregister and delete the source in the SourceManager
153 $self unregister
154 $sm_ delete $self;
155
156 # destroy the object
157 $self next
158 }
159
160
161
162 #
163 # Return the best name for the given source, based on
164 # the possibly limited information we have.
165 #
166 Source/RTP public getid {} {
167 set name [$self sdes name]
168 if { $name == "" } {
169 set name [$self sdes cname]
170 if { $name == "" } {
171 set name [$self addr]
172 }
173 }
174 return $name
175 }
176
177 #
178 # Return a the RTP format name as a string for the RTP
179 # payload format of the media stream underlying this source
180 # object.
181 #
182 Source/RTP public format_name {} {
183 $self instvar sm_
184 return [$sm_ rtp_type [$self format]]
185 }
186
187 #
188 # The <u>MediaAgent</u> class is the core programming layer
189 # for all ``media agents'' that manipulate media over the network.
190 # These agents are relatively large and complex objects,
191 # but are used by most applications in a fairly straightforward
192 # manner. The idea is to give the MASH programmer
193 # a fairly course-grained programming object that relieves the
194 # burden of managing video capture devices, multicast sockets,
195 # audio processing code, etc. into a high-level, easy-to-use API.
196 # The effort can then be spent figuring out how to cleanly
197 # integrate video, audio, or whiteboard objects into the
198 # application under design.
199 # <p>
200 # MediaAgents are ``bare processing engines''. They do not create
201 # or rely upon a user-interface. Instead, the script that creates
202 # the object manipulates it directly and if a user-interface is desired,
203 # that script must create and bind the UI to the agent.
204 # The protocol for communicating between the agent and the UI
205 # follows the MASH Observer model.
206 # In fact, since the observer abstraction is completely general,
207 # any object can attach itself to a MediaAgent as
208 # an observer, not just UIs.
209 #
210 # <p>
211 # <i>Note: This is an abstract base class. Do not create objects directly
212 # of this class</i>
213 #
214 Class MediaAgent -superclass {SourceManager Observable}
215
216 #
217 # Arrange for each of the methods on an RTP source object to be
218 # re-directed to the source-manager and in turn dispatched
219 # to each of the observers. This arrangement allows subclasses
220 # to override this default behavior (e.g., the VideoAgent class
221 # catches activate so it can create a decoder).
222 #
223 foreach method "unregister activate deactivate \
224 trigger_media \
225 trigger_format \
226 trigger_sdes \
227 trigger_idle \
228 trigger_sr \
229 notify" {
230 Source/RTP public $method {args} \
231 "\$self instvar sm_ ; eval \$sm_ $method \$self \$args"
232 MediaAgent public $method src "\$self notify_observers $method \$src"
233 }
234
235 MediaAgent public init {} {
236 $self next
237 $self set sources_ ""
238 }
239
240 MediaAgent public destroy {} {
241 $self instvar sources_
242
243 # destroy all the Source/RTP objects
244 foreach src $sources_ {
245 $src destroy;
246 }
247
248 $self next
249 }
250
251
252 MediaAgent public active_list {} {
253 $self instvar active_
254 if ![info exists active_] {
255 return ""
256 }
257 return [array names active_]
258 }
259
260 #
261 # Override some of the above SourceManager methods...
262 #
263
264 MediaAgent public activate src {
265 $self instvar active_
266 set active_($src) 1
267 $self notify_observers activate $src
268 }
269
270 MediaAgent public deactivate src {
271 $self instvar active_
272 unset active_($src)
273 $self notify_observers deactivate $src
274 }
275
276 MediaAgent public unregister src {
277 $self notify_observers unregister $src
278 $self instvar sources_
279 set k [lsearch -exact $sources_ $src]
280 set sources_ [lreplace $sources_ $k $k]
281 }
282
283 #
284 # Attach an observer to this agent.
285 #
286 MediaAgent public attach o {
287 $self attach_observer $o
288 #
289 # FIXME local_ is in sources_ list
290 # $o register $local_
291 $self instvar sources_ active_
292 foreach s $sources_ {
293 $o update register $s
294 if [info exists active_($s)] {
295 $o update activate $s
296 $s enable_trigger
297 }
298 }
299 }
300
301 #
302 # Detach an observer from this agent.
303 #
304 MediaAgent public detach o {
305 $self detach_observer $o
306 $self instvar sources_ active_
307 foreach s $sources_ {
308 if [info exists active_($s)] {
309 $o update deactivate $s
310 }
311 $o update unregister $s
312 }
313 }
314
315 #
316 # Called from C++ when a new RTP source is seen (and validated).
317 #
318 MediaAgent public create-source { srcid ssrc addr srcsess } {
319 set s [new Source/RTP $self $srcid $ssrc $addr]
320 $s set session_ $srcsess
321 $self instvar sources_
322 lappend sources_ $s
323 return $s
324 }
325
326 #
327 # This class implements the machinery of an RTP session.
328 # It is pure mechanism; no user-interface etc.
329 # Instead it is manipulated by user interfaces or
330 # other agents that define policy.
331 #
332 Class RTPAgent -superclass MediaAgent -configuration {
333 mtu 1024
334 loopback 0
335 siteDropTime "300"
336 }
337
338 RTPAgent public init {ab {callback {}} } {
339 $self next
340 $self instvar session_ mtu_ callback_
341 if { $callback!={} } { set callback_ $callback }
342 set session_ [$self create_session]
343 $session_ sm $self
344 $session_ buffer-pool [new BufferPool]
345
346 if { $ab != "" } {
347 $self reset $ab
348 }
349 set mtu_ [$self get_option mtu]
350
351 #FIXME
352 global V
353 set V(sm) $self
354 }
355
356 RTPAgent public destroy {} {
357 $self instvar session_ network_
358
359 # exit the M/C session so all the other sources realize we're leaving
360 $session_ exit;
361 delete $session_
362
363 # clean up the network infrastructure
364 delete $network_
365
366 $self next
367 }
368
369 RTPAgent public reset_spec spec {
370 set ab [SessionAddress parse $spec]
371 $self reset $ab
372 delete $ab
373 }
374
375 #
376 # Reset the address specifier of the underlying session in question.
377 # This allows the network objects to be dynamically reconfigured to
378 # facilitate higher-level control protocols that might instruct an
379 # application to switch multicast sessions or speak to a different
380 # unicast destination. FIXME should only reset the piece that matters.
381 #
382 RTPAgent public reset ab {
383 $self instvar network_ session_ sources_
384
385 if [catch {
386 set c [$ab info class]
387 if { [lsearch [$c info heritage] "SessionAddress"] == -1 } {
388 error "ab is a $c, which is not a SessionAddress"
389 }
390 } err] {
391 error "Got bogus argument to RTPAgent::reset: $err"
392 }
393
394
395 # It is important to create the new objects before destroying the
396 # old ones (NetworkManager and Source/RTP) so that, in the case
397 # the agent is transmitting, the encoder has a place to deliver
398 # its packets
399
400
401 # copy all the old object handlers to destroy them at the end
402 if [info exists network_] {
403 set old_network $network_
404 }
405 set old_sources $sources_
406
407 # inform the other session members that we're leaving
408 # (send RTP BYE message)
409 $session_ exit
410
411 # create the new network object
412 set class [$ab net-class]
413 set network_ [new $class $ab $session_ $self]
414
415 #FIXME
416 $self app_loopback 1
417 $self net_loopback [$self get_option loopback]
418
419 set key [$self get_option sessionKey]
420 if { $key != "" } {
421 $network_ install-key $key
422 }
423
424 # create the local rtp-source object
425 $self mk_local_source
426
427 #FIXME currently session object understands only one bandwidth limit
428 # FIXME get this into b/s eventually.
429 $session_ max-bandwidth [expr [$ab maxbw]/1000.]
430
431
432 # FIXME Eventually use 'attach' mechanism.
433 $self instvar callback_
434 if [info exists callback_] {
435 eval $callback_ [list $ab]
436 } else {
437 catch {
438 set a [Application instance]
439 if [catch {$a reset $ab rtp $self}] {
440 $a reset $ab
441 }
442 }
443 }
444
445 # clean up the old object
446
447 # destroy all the Source/RTP objects
448 foreach src $old_sources {
449 $src destroy
450 }
451
452 # destroy the old NetworkManager object
453 if [info exists old_network] {
454 delete $old_network
455 }
456
457 }
458
459 RTPAgent private notify {src layer} {
460 $self instvar network_
461 if ![$network_ usingRLM] { return }
462 $network_ notify-loss $src $layer
463 }
464
465 #
466 # Return a list of names and values of the statistics
467 # for the RTP session as a whole.
468 #
469 RTPAgent public stats {} {
470 set s [$self set session_]
471 return " \
472 Bad-RTP-version [$s set badversion_] \
473 Bad-RTPv1-options [$s set badoptions_] \
474 Bad-Payload-Format [$s set badfmt_] \
475 Bad-RTP-Extension [$s set badext_] \
476 Runts [$s set nrunt_]"
477 #FIXME should report counters with bad crypt operations
478 }
479
480 #
481 # Create the local source object and do proper initialization
482 # of its various parameters and fields like sdes info.
483 #
484 RTPAgent private mk_local_source {} {
485 $self instvar network_ session_ local_
486
487 set net [$network_ data-net 0]
488
489 # choose the initial RTP srcid
490 set a [$net addr]
491 set srcid [$session_ random-srcid $a]
492 # create the local source object
493 set src [$self create-local $srcid [$net interface]]
494 set local_ $src
495 $self notify_observers register $local_
496
497 set cname [$self get_option cname]
498 if { $cname == "" } {
499 set interface [$net interface]
500 if { $interface == "0.0.0.0" } {
501 # this happens under solaris
502 set interface [$session_ local-addr-heuristic]
503 }
504 set cname [user_heuristic]@$interface
505 }
506 $src sdes name [$self get_option rtpName]
507 $src sdes email [$self get_option rtpEmail]
508 # We use the loc field for distributed recording, setting it to
509 # be the closest local AS1 platform
510 $src sdes loc [$self get_option rtpLoc]
511 $src sdes cname $cname
512
513 set tool [Application name]\-[version]
514
515 global tcl_platform
516 if {[info exists tcl_platform(os)] && $tcl_platform(os) != "" && \
517 $tcl_platform(os) != "unix"} {
518 set p $tcl_platform(os)
519 if {$tcl_platform(osVersion) != ""} {
520 set p $p-$tcl_platform(osVersion)
521 }
522 if {$tcl_platform(machine) != ""} {
523 set p $p-$tcl_platform(machine)
524 }
525 set tool "$tool/$p"
526 }
527 $src sdes tool $tool
528
529 return $src
530 }
531
532 #
533 # Return true iff an underlying network object has been
534 # created and attached to this RTP agent.
535 #
536 RTPAgent public have_network {} {
537 $self instvar network_
538 return [info exists network_]
539 }
540
541 #
542 # Return true iff the underlying object that represents
543 # the local source has been created and attached to this RTP agent.
544 #
545 RTPAgent public have_localsrc {} {
546 $self instvar local_
547 return [info exists local_]
548 }
549
550 #
551 # Instruct the media-agent to perform encryption and decryption of all
552 # data sent to or received from the network. The format of the key is
553 # as specified by the SDP specification. FIXME put format here.
554 #
555 RTPAgent public install-key key {
556 $self instvar network_
557 if [info exists network_] {
558 $network_ install-key $key
559 }
560 }
561
562 #
563 # Return the network object that underlies the RTP session,
564 # or return "none" if it does not yet exist.
565 #
566 RTPAgent public network {} {
567 $self instvar network_
568 if ![info exists network_] {
569 return none
570 }
571 return [$network_ data-net 0]
572 }
573
574 #
575 # Returns a string that describes the network session
576 #
577 RTPAgent public session-info {} {
578 $self instvar network_
579 if ![info exists network_] {
580 return "No network"
581 }
582 return [$network_ session-info]
583 }
584
585 #
586 # Return the network address of the underlying
587 # communication session, or the string "none"
588 # if the underlying network object hasn't yet
589 # been created.
590 #
591 RTPAgent public session-addr {} {
592 $self instvar network_
593 if ![info exists network_] {
594 return none
595 }
596 return [[$self network] addr]
597 }
598
599 #
600 # Return the network port number of the underlying
601 # communication session, or the string "none"
602 # if the underlying network object hasn't yet
603 # been created. FIXME why do we have three port methods?
604 #
605 RTPAgent public session-port {} {
606 $self instvar network_
607 if ![info exists network_] {
608 return none
609 }
610 return [[$self network] port]
611 }
612
613 #
614 # Return the inbound port number of the underlying
615 # communication session, or the string "none"
616 # if the underlying network object hasn't yet
617 # been created.
618 #
619 RTPAgent public session-rport {} {
620 $self instvar network_
621 if ![info exists network_] {
622 return none
623 }
624 return [[$self network] rport]
625 }
626
627 #
628 # Return the outbound port number of the underlying
629 # communication session, or the string "none"
630 # if the underlying network object hasn't yet
631 # been created.
632 #
633 RTPAgent public session-sport {} {
634 $self instvar network_
635 if ![info exists network_] {
636 return none
637 }
638 return [[$self network] sport]
639 }
640
641 #
642 # Return the RTP SRCID of the object that represents
643 # the local source, or the string "none"
644 # if the underlying network object hasn't yet
645 # been created.
646 #
647 RTPAgent public get_local_srcid {} {
648 $self instvar network_
649 if ![info exists network_] {
650 return none
651 }
652 $self instvar local_
653 return [$local_ srcid]
654 }
655
656 #
657 # Return the RTP SDES element of the object that represents
658 # the local source, or the string "none" if the underlying
659 # network object hasn't yet been created.
660 #
661 RTPAgent public get_local_sdes {which} {
662 $self instvar network_
663 if ![info exists network_] {
664 return none
665 }
666 $self instvar local_
667 return [$local_ sdes $which]
668 }
669
670
671 #
672 # Return the name of an object that sinks the locally generated
673 # RTP packet stream and in turns sends it out over the network.
674 #
675 RTPAgent public get_transmitter {} {
676 return [$self set session_]
677 }
678
679 #
680 # Return the time-to-live values used by the underlying
681 # communication session, or the string "none"
682 # if the underlying network object hasn't yet
683 # been created. This value is undefined if the
684 # underlying session is not a multicast address.
685 #
686 RTPAgent public session-ttl {} {
687 $self instvar network_
688 if ![info exists network_] {
689 return none
690 }
691 return [[$self network] ttl]
692 }
693
694 #
695 # Return the time-to-live values used by the underlying
696 # communication session, or the string "none"
697 # if the underlying network object hasn't yet
698 # been created. This value is undefined if the
699 # underlying session is not a multicast address.
700 #
701 # Deprecated by "$self get_local_sdes name"
702 #
703 RTPAgent public local-name {} {
704 $self instvar network_
705 if ![info exists network_] {
706 return none
707 }
708 $self instvar local_
709 return [$local_ sdes name]
710 }
711
712 #
713 # Set an RTP SDES string for the local source
714 # to the indicate value. <i>which</i> is the SDES
715 # string type and <i>value</i> is the desired string.
716 # Example usage:
717 # <pre>
718 # $o set_local_sdes email mccanne@cs.berkeley.edu
719 # </pre>
720 #
721 RTPAgent public set_local_sdes { which value } {
722 $self instvar local_
723 $local_ sdes $which $value
724 }
725
726 #
727 # Turn off encryption.
728 #
729 RTPAgent public crypt_clear {} {
730 if [info exists network_] {
731 $network_ crypt_clear
732 }
733 }
734
735 #
736 # Gracefully tear down the underlying session. Set or query the media
737 # associated with this session. Please note that the media is
738 # automatically set by the init method of this class. Programmers
739 # should not invoke this method directly to set the media.
740 #
741 RTPAgent public shutdown {} {
742 $self instvar session_
743 $session_ exit
744 }
745
746 #
747 # Set the maximum number of network channels used by the
748 # session to <i>n</i>. Multiple channels are used
749 # when transmitting layered media streams (e.g., the PVH
750 # video coding format). See
751 # <a href=http://www.cs.berkeley.edu/~mccanne/phd-work>McCanne's
752 # PhD work</a> for more info on layered video transmission.
753 #
754 RTPAgent public set_maxchannel n {}
755
756 #
757 # Set the network bandwidth used by the underlying media transmission
758 # protocols to <i>bps</i> bits per second. This rate constraint is
759 # met in a media-specific fashion. Currently, most all of the video
760 # codecs simply adjust the inter-frame spacing to meet the bit-rate
761 # budget.
762 #
763 RTPAgent public set-bandwidth bps {
764 [$self set session_] data-bandwidth $bps
765 }
766
767 #
768 # Set the multicast loopback flag on the underlying session
769 # according to the <i>sense</i> argument. If true, then
770 # packets are looped back according to the semantics of
771 # the underlying operating system's loopback flag.
772 # This can have unpredictable results if you don't know
773 # exactly what you're doing. Most of the mash applications
774 # assume that packets are not looped back and will take
775 # unpredictable action if they see their own traffic looped back.
776 # However, unless you set filter_own to 0, the app will never
777 # see the traffic.
778 #
779 RTPAgent public net_loopback enable {
780 $self instvar network_
781 $network_ loopback $enable
782 }
783
784 RTPAgent public app_loopback enable {
785 $self instvar session_
786 $session_ set loopback_ $enable
787 }
788
789 RTPAgent public set-bandwidth bps {
790 [$self set session_] data-bandwidth $bps
791 }
792
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.