File Coverage

blib/lib/RTSP/Client.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package RTSP::Client;
2              
3 1     1   20767 use Moose;
  0            
  0            
4             use RTSP::Lite;
5             use Carp qw/croak/;
6              
7             our $VERSION = '0.4';
8              
9             =head1 NAME
10              
11             RTSP::Client - High-level client for the Real-Time Streaming Protocol
12              
13             =head1 SYNOPSIS
14              
15             use RTSP::Client;
16             my $client = new RTSP::Client(
17             port => 554,
18             client_port_range => '6970-6971',
19             transport_protocol => 'RTP/AVP;unicast',
20             address => '10.0.1.105',
21             media_path => '/mpeg4/media.amp',
22             );
23            
24             # OR
25             my $client = RTSP::Client->new_from_uri(uri => 'rtsp://10.0.1.105:554/mpeg4/media.amp');
26              
27             $client->open or die $!;
28            
29             my $sdp = $client->describe;
30             my @allowed_public_methods = $client->options_public;
31              
32             $client->setup;
33             $client->reset;
34            
35             $client->play;
36             $client->pause;
37            
38            
39             $client->teardown;
40            
41            
42             =head1 DESCRIPTION
43              
44             This module provides a high-level interface for communicating with an RTSP server.
45             RTSP is a protocol for controlling streaming applications, it is not a media transport or a codec.
46             It supports describing media streams and controlling playback, and that's about it.
47              
48             In typical usage, you will open a connection to an RTSP server and send it the PLAY method. The server
49             will then stream the media at you on the client port range using the specified transport protocol.
50             You are responsible for listening on the client port range and handling the actual media data yourself,
51             actually receiving a media stream or decoding it is beyond the scope of RTSP and this module.
52              
53             =head2 EXPORT
54              
55             No namespace pollution here!
56              
57             =head2 ATTRIBUTES
58              
59             =over 4
60              
61             =item session_id
62              
63             RTSP session id. It will be set on a successful OPEN request and added to each subsequent request
64              
65             =cut
66             has session_id => (
67             is => 'rw',
68             isa => 'Str',
69             );
70              
71             =item client_port_range
72              
73             Ports the client receives data on. Listening and receiving data is not handled by RTSP::Client
74              
75             =cut
76             has client_port_range => (
77             is => 'rw',
78             isa => 'Str',
79             );
80              
81             =item media_path
82              
83             Path to the requested media stream
84              
85             e.g. /mpeg4/media.amp
86              
87             =cut
88             has media_path => (
89             is => 'rw',
90             isa => 'Str',
91             default => sub { '/' },
92             );
93              
94             =item transport_protocol
95              
96             Requested transport protocol, RTP by default
97              
98             =cut
99             has transport_protocol => (
100             is => 'rw',
101             isa => 'Str',
102             default => sub { 'RTP/AVP;unicast' },
103             );
104              
105             =item address
106              
107             RTSP server address. This is required.
108              
109             =cut
110             has address => (
111             is => 'rw',
112             isa => 'Str',
113             required => 1,
114             );
115              
116             =item port
117              
118             RTSP server port. Defaults to 554
119              
120             =cut
121             has port => (
122             is => 'rw',
123             isa => 'Int',
124             default => sub { 554 },
125             );
126              
127             =item connected
128              
129             Is the client connected?
130              
131             =cut
132             has connected => (
133             is => 'rw',
134             isa => 'Bool',
135             default => sub { 0 },
136             );
137              
138             =item print_headers
139              
140             Print out debug headers
141              
142             =cut
143             has print_headers => (
144             is => 'rw',
145             isa => 'Bool',
146             );
147              
148             has have_set_session_header => (
149             is => 'rw',
150             isa => 'Bool',
151             );
152              
153             =item debug
154              
155             Print debugging information (request status)
156              
157             =cut
158             has debug => (
159             is => 'rw',
160             isa => 'Bool',
161             );
162              
163             # RTSP::Lite client
164             has _rtsp => (
165             is => 'rw',
166             isa => 'RTSP::Lite',
167             default => sub { RTSP::Lite->new },
168             handles => [qw/
169             body headers_array headers_string get_header user_agent get_header
170             delete_req_header get_req_header add_req_header status
171             /],
172             );
173              
174             =back
175              
176             =head1 METHODS
177              
178             =over 4
179              
180             =cut
181              
182             # construct uri to media
183             sub _request_uri {
184             my ($self) = @_;
185             return "rtsp://" . $self->address . ':' . $self->port . $self->media_path;
186             }
187              
188             =item open
189              
190             This method opens a connection to the RTSP server. Returns true on success, false with $! possibly set on failure.
191              
192             =cut
193             sub open {
194             my ($self) = @_;
195            
196             # open connection, returns $! set on failure
197             my $connected = $self->_rtsp->open($self->address, $self->port);
198            
199             $self->connected($connected ? 1 : 0);
200             return $connected;
201             }
202              
203             =item setup
204              
205             A SETUP request specifies how a single media stream must be transported. This must be done before a PLAY request is sent. The request contains the media stream URL and a transport specifier. This specifier typically includes a local port for receiving RTP data (audio or video), and another for RTCP data (meta information). The server reply usually confirms the chosen parameters, and fills in the missing parts, such as the server's chosen ports. Each media stream must be configured using SETUP before an aggregate play request may be sent.
206              
207             =cut
208             sub setup {
209             my ($self) = @_;
210            
211             # request transport
212             my $proto = $self->transport_protocol;
213             my $ports = $self->client_port_range;
214             if ($ports) {
215             my $transport_req_str = join(';', $proto, "client_port=$ports");
216             $self->_rtsp->add_req_header("Transport", $transport_req_str);
217             } elsif (! $self->get_req_header('Transport')) {
218             warn "no Transport header set in setup()";
219             }
220              
221             return unless $self->request('SETUP');
222            
223             # get session ID
224             my $se = $self->_rtsp->get_header("Session");
225             my $session = @$se[0];
226            
227             if ($session) {
228             $self->session_id($session);
229             $self->add_session_header;
230             }
231            
232             return $session ? 1 : 0;
233             }
234              
235             sub add_session_header {
236             my ($self) = @_;
237            
238             return if $self->have_set_session_header;
239             $self->have_set_session_header(1);
240            
241             $self->add_req_header("Session", $self->session_id)
242             if $self->session_id && ! $self->get_req_header('Session');
243             }
244              
245             =item new_from_uri(%opts)
246              
247             Takes same opts as new() and adds additional param: uri
248              
249             e.g. C<my $rtsp_client = RTSP::Client-E<gt>new_from_uri(uri =E<gt> 'rtsp://10.0.1.105:554/mpeg4/media.amp', debug =E<gt> 1);>
250              
251             =cut
252             sub new_from_uri {
253             my ($class, %opts) = @_;
254            
255             my $uri = delete $opts{uri}
256             or croak "No URI passed to RTSP::Client::new_from_uri()";
257            
258             # todo: parse auth
259             my ($host, $port, $media_path) = $uri =~ m!^rtsp://([-\w.]+):?(\d+)?(/.+)?$!ism;
260              
261             unless ($host) {
262             croak "Invalid RTSP URI '$uri' passed to RTSP::Client::new_from_uri()";
263             }
264            
265             $opts{address} ||= $host;
266             $opts{port} ||= $port if $port;
267             $opts{media_path} ||= $media_path if $media_path;
268            
269             return $class->new(%opts);
270             }
271              
272             =item play
273              
274             A PLAY request will cause one or all media streams to be played. Play requests can be stacked by sending multiple PLAY requests. The URL may be the aggregate URL (to play all media streams), or a single media stream URL (to play only that stream). A range can be specified. If no range is specified, the stream is played from the beginning and plays to the end, or, if the stream is paused, it is resumed at the point it was paused.
275              
276             =cut
277             sub play {
278             my ($self) = @_;
279             $self->add_session_header;
280             return $self->request('PLAY');
281             }
282              
283             =item pause
284              
285             A PAUSE request temporarily halts one or all media streams, so it can later be resumed with a PLAY request. The request contains an aggregate or media stream URL.
286              
287             =cut
288             sub pause {
289             my ($self) = @_;
290             $self->add_session_header;
291             return $self->request('PAUSE');
292             }
293              
294             =item record
295              
296             The RECORD request can be used to send a stream to the server for storage.
297              
298             =cut
299             sub record {
300             my ($self) = @_;
301             $self->add_session_header;
302             return $self->request('RECORD');
303             }
304              
305             =item teardown
306              
307             A TEARDOWN request is used to terminate the session. It stops all media streams and frees all session related data on the server.
308              
309             =cut
310             sub teardown {
311             my ($self) = @_;
312             $self->add_session_header;
313             return $self->request('TEARDOWN');
314             $self->connected(0);
315             $self->reset;
316             }
317              
318             sub options {
319             my ($self, $uri) = @_;
320             return $self->request('OPTIONS');
321             }
322              
323             =item options_public
324              
325             An OPTIONS request returns the request types the server will accept.
326              
327             This returns an array of allowed public methods.
328              
329             =cut
330             sub options_public {
331             my ($self) = @_;
332             return unless $self->options;
333             my $public = $self->_rtsp->get_header('Public');
334             return $public ? @$public : undef;
335             }
336              
337             =item describe
338              
339             The reply to a DESCRIBE request includes the presentation description, typically in Session Description Protocol (SDP) format. Among other things, the presentation description lists the media streams controlled with the aggregate URL. In the typical case, there is one media stream each for audio and video.
340              
341             This method returns the actual DESCRIBE content, as SDP data
342              
343             =cut
344             sub describe {
345             my ($self) = @_;
346             return unless $self->request('DESCRIBE');
347             return $self->body;
348             }
349              
350             =item request($method)
351              
352             Sends a $method request, returns true on success, false with $! possibly set on failure
353              
354             =cut
355             sub request {
356             my ($self, $method, $uri) = @_;
357            
358             # make sure we're connected
359             unless ($self->connected) {
360             $self->open or return;
361             }
362            
363             $self->_rtsp->method(uc $method);
364            
365             # request media
366             my $req_uri = $uri || $self->_request_uri;
367             $self->_rtsp->request($req_uri)
368             or return;
369            
370             # request status
371             my $status = $self->_rtsp->status;
372             if ($self->debug) {
373             print STDERR "Status: $status " . $self->_rtsp->status_message . "\n";
374             }
375             if (! $status || $status != 200) {
376             return;
377             }
378            
379             if ($self->print_headers) {
380             my @headers = $self->_rtsp->headers_array;
381             my $body = $self->_rtsp->body;
382             print STDERR "$_\n" foreach @headers;
383             print STDERR "$body\n" if $body;
384             }
385            
386             return 1;
387             }
388              
389             # clean up connection if we're still connected
390             sub DEMOLISH {
391             my ($self) = @_;
392            
393             $self->reset;
394             return unless $self->connected;
395             #$self->teardown;
396            
397             }
398              
399             =item reset
400              
401             If you wish to reuse the client for multiple requests, you should call reset after each request unless you want to keep the socket open.
402              
403             =cut
404             sub reset {
405             my ($self) = @_;
406            
407             $self->_rtsp->reset;
408             }
409              
410              
411             =item status_message
412              
413             Get the status message of the last request (e.g. "Bad Request")
414              
415             =cut
416             sub status_message {
417             my ($self) = @_;
418             my $msg = $self->_rtsp->status_message || '';
419             $msg =~ s/(\r\n)$//sm;
420             return $msg;
421             }
422              
423             #### these are handled by RTSP::Lite
424              
425             =item status
426              
427             Get the status code of the last request (e.g. 200, 405)
428              
429             =item get_header ($header)
430              
431             returns response header
432              
433             =item add_req_header ($header, $value)
434              
435             =item get_req_header ($header)
436              
437             =item delete_req_header ($header)
438              
439             =cut
440              
441              
442              
443             no Moose;
444             __PACKAGE__->meta->make_immutable;
445              
446             =back
447              
448             =head1 SEE ALSO
449              
450             L<RTSP::Lite>, L<http://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol>
451              
452             =head1 AUTHOR
453              
454             Mischa Spiegelmock E<lt>revmischa@cpan.orgE<gt>
455              
456             =head1 ACKNOWLEDGEMENTS
457              
458             This is based entirely on L<RTSP::Lite> by Masaaki Nabeshima.
459              
460             =head1 COPYRIGHT AND LICENSE
461              
462             Copyright (C) 2010 by Mischa Spiegelmock
463              
464             This library is free software; you can redistribute it and/or modify
465             it under the same terms as Perl itself, either Perl version 5.10.0 or,
466             at your option, any later version of Perl 5 you may have available.
467              
468              
469             =cut