1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27#include <stdlib.h>
28#include <stdio.h>
29#include <string.h>
30#include <ctype.h>
31
32#define CONTAINER_IS_BIG_ENDIAN
33//#define ENABLE_CONTAINERS_LOG_FORMAT
34//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
35#define CONTAINER_HELPER_LOG_INDENT(a) 0
36#include "containers/core/containers_private.h"
37#include "containers/core/containers_io_helpers.h"
38#include "containers/core/containers_utils.h"
39#include "containers/core/containers_logging.h"
40#include "containers/core/containers_list.h"
41#include "containers/core/containers_uri.h"
42
43/******************************************************************************
44Configurable defines and constants.
45******************************************************************************/
46
47/** Maximum number of tracks allowed in an RTSP reader */
48#define RTSP_TRACKS_MAX 4
49
50/** Space for sending requests and receiving responses */
51#define COMMS_BUFFER_SIZE 2048
52
53/** Largest allowed RTSP URI. Must be substantially smaller than COMMS_BUFFER_SIZE
54 * to allow for the headers that may be sent. */
55#define RTSP_URI_LENGTH_MAX 1024
56
57/** Maximum allowed length for the Session: header recevied in a SETUP response,
58 * This is to ensure comms buffer is not overflowed. */
59#define SESSION_HEADER_LENGTH_MAX 100
60
61/** Number of milliseconds to block trying to read from the RTSP stream when no
62 * data is available from any of the tracks */
63#define DATA_UNAVAILABLE_READ_TIMEOUT_MS 1
64
65/** Size of buffer for each track to use when receiving packets */
66#define UDP_READ_BUFFER_SIZE 520000
67
68/* Arbitrary number of different dynamic ports to try */
69#define DYNAMIC_PORT_ATTEMPTS_MAX 16
70
71/******************************************************************************
72Defines and constants.
73******************************************************************************/
74
75#define RTSP_SCHEME "rtsp:"
76#define RTP_SCHEME "rtp"
77
78/** The RTSP PKT scheme is used with test pkt files */
79#define RTSP_PKT_SCHEME "rtsppkt:"
80
81#define RTSP_NETWORK_URI_START "rtsp://"
82#define RTSP_NETWORK_URI_START_LENGTH (sizeof(RTSP_NETWORK_URI_START)-1)
83
84/** Initial capacity of header list */
85#define HEADER_LIST_INITIAL_CAPACITY 16
86
87/** Format of the first line of an RTSP request */
88#define RTSP_REQUEST_LINE_FORMAT "%s %s RTSP/1.0\r\n"
89
90/** Format string for common headers used with all request methods.
91 * Note: includes double new line to terminate headers */
92#define TRAILING_HEADERS_FORMAT "CSeq: %u\r\nConnection: Keep-Alive\r\nUser-Agent: Broadcom/1.0\r\n\r\n"
93
94/** Format for the Transport: header */
95#define TRANSPORT_HEADER_FORMAT "Transport: RTP/AVP;unicast;client_port=%hu-%hu;mode=play\r\n"
96
97/** Format for including Session: header. */
98#define SESSION_HEADER_FORMAT "Session: %s\r\n"
99
100/** \name RTSP methods, used as the first item in the request line
101 * @{ */
102#define DESCRIBE_METHOD "DESCRIBE"
103#define SETUP_METHOD "SETUP"
104#define PLAY_METHOD "PLAY"
105#define TEARDOWN_METHOD "TEARDOWN"
106/* @} */
107
108/** \name Names of headers used by the code
109 * @{ */
110#define CONTENT_PSEUDOHEADER_NAME ":"
111#define CONTENT_LENGTH_NAME "Content-Length"
112#define CONTENT_BASE_NAME "Content-Base"
113#define CONTENT_LOCATION_NAME "Content-Location"
114#define RTP_INFO_NAME "RTP-Info"
115#define SESSION_NAME "Session"
116/* @} */
117
118/** Supported RTSP major version number */
119#define RTSP_MAJOR_VERSION 1
120/** Supported RTSP minor version number */
121#define RTSP_MINOR_VERSION 0
122
123/** Lowest successful status code value */
124#define RTSP_STATUS_OK 200
125/** Next failure status code after the set of successful ones */
126#define RTSP_STATUS_MULTIPLE_CHOICES 300
127
128/** Maximum size of a decimal string representation of a uint16_t, plus NUL */
129#define PORT_BUFFER_SIZE 6
130/** Start of private / dynamic port region */
131#define FIRST_DYNAMIC_PORT 0xC000
132/** End of private / dynamic port region */
133#define LAST_DYNAMIC_PORT 0xFFF0
134
135/** Format of RTP track file extension */
136#define RTP_PATH_EXTENSION_FORMAT ".t%u.pkt"
137/** Extra space need for creating an RTP track file name from an RTSP URI path */
138#define RTP_PATH_EXTRA 17
139
140/** \name RTP URI parameter names
141 * @{ */
142#define PAYLOAD_TYPE_NAME "rtppt"
143#define MIME_TYPE_NAME "mime-type"
144#define SAMPLE_RATE_NAME "rate"
145#define CHANNELS_NAME "channels"
146/* @} */
147
148/** Largest signed 64-bit integer */
149#define MAXIMUM_INT64 (int64_t)((1ULL << 63) - 1)
150
151/******************************************************************************
152Type definitions
153******************************************************************************/
154
155typedef int (*PARSE_IS_DELIMITER_FN_T)(int char_to_test);
156
157typedef struct rtsp_header_tag
158{
159 const char *name;
160 char *value;
161} RTSP_HEADER_T;
162
163typedef struct VC_CONTAINER_TRACK_MODULE_T
164{
165 VC_CONTAINER_T *reader; /**< RTP reader for track */
166 VC_URI_PARTS_T *reader_uri; /**< URI built up from SDP and used to open reader */
167 char *control_uri; /**< URI used to control track playback */
168 char *session_header; /**< Session header to be used when sending control requests */
169 char *payload_type; /**< RTP payload type for track */
170 char *media_type; /**< MIME type for track */
171 VC_CONTAINER_PACKET_T info; /**< Latest track packet info block */
172 unsigned short rtp_port; /**< UDP listener port being used in RTP reader */
173} VC_CONTAINER_TRACK_MODULE_T;
174
175typedef struct VC_CONTAINER_MODULE_T
176{
177 VC_CONTAINER_TRACK_T *tracks[RTSP_TRACKS_MAX];
178 char *comms_buffer; /**< Buffer used for sending and receiving RTSP messages */
179 VC_CONTAINERS_LIST_T *header_list; /**< Parsed response headers, pointing into comms buffer */
180 uint32_t cseq_value; /**< CSeq header value for next request */
181 uint16_t next_rtp_port; /**< Next RTP port to use when opening track reader */
182 uint16_t media_item; /**< Current media item number during initialization */
183 bool uri_has_network_info; /**< True if the RTSP URI contains network info */
184 int64_t ts_base; /**< Base value for dts and pts */
185 VC_CONTAINER_TRACK_MODULE_T *current_track; /**< Next track to be read, to keep info/data on same track */
186} VC_CONTAINER_MODULE_T;
187
188/******************************************************************************
189Function prototypes
190******************************************************************************/
191static int rtsp_header_comparator(const RTSP_HEADER_T *first, const RTSP_HEADER_T *second);
192
193VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T * );
194
195/******************************************************************************
196Local Functions
197******************************************************************************/
198
199/**************************************************************************//**
200 * Trim whitespace from the end and start of the string
201 *
202 * \param str String to be trimmed
203 * \return Trimmed string
204 */
205static char *rtsp_trim( char *str )
206{
207 char *trim = str + strlen(str);
208
209 /* Search backwards for first non-whitespace */
210 while (--trim >= str && isspace((int)*trim))
211 ; /* Everything done in the while */
212 trim[1] = '\0';
213
214 /* Now move start of string forwards to first non-whitespace */
215 trim = str;
216 while (isspace((int)*trim))
217 trim++;
218
219 return trim;
220}
221
222/**************************************************************************//**
223 * Send out the data in the comms buffer.
224 *
225 * @param p_ctx The reader context.
226 * @return The resulting status of the function.
227 */
228static VC_CONTAINER_STATUS_T rtsp_send( VC_CONTAINER_T *p_ctx )
229{
230 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
231 uint32_t to_write;
232 uint32_t written;
233 const char *buffer = module->comms_buffer;
234
235 /* When reading from a captured file, do not attempt to send data */
236 if (!module->uri_has_network_info)
237 return VC_CONTAINER_SUCCESS;
238
239 to_write = strlen(buffer);
240
241 while (to_write)
242 {
243 written = vc_container_io_write(p_ctx->priv->io, buffer, to_write);
244 if (!written)
245 break;
246 to_write -= written;
247 buffer += written;
248 }
249
250 return p_ctx->priv->io->status;
251}
252
253/**************************************************************************//**
254 * Send a DESCRIBE request to the RTSP server.
255 *
256 * @param p_ctx The reader context.
257 * @return The resulting status of the function.
258 */
259static VC_CONTAINER_STATUS_T rtsp_send_describe_request( VC_CONTAINER_T *p_ctx )
260{
261 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
262 char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE;
263 char *uri = p_ctx->priv->io->uri;
264
265 if (strlen(uri) > RTSP_URI_LENGTH_MAX)
266 {
267 LOG_ERROR(p_ctx, "RTSP: URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX);
268 return VC_CONTAINER_ERROR_URI_OPEN_FAILED;
269 }
270
271 ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, DESCRIBE_METHOD, uri);
272 if (ptr < end)
273 ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++);
274 vc_container_assert(ptr < end);
275
276 return rtsp_send(p_ctx);
277}
278
279/**************************************************************************//**
280 * Send a SETUP request to the RTSP server.
281 *
282 * @param p_ctx The reader context.
283 * @param t_module The track module relating to the SETUP.
284 * @return The resulting status of the function.
285 */
286static VC_CONTAINER_STATUS_T rtsp_send_setup_request( VC_CONTAINER_T *p_ctx,
287 VC_CONTAINER_TRACK_MODULE_T *t_module)
288{
289 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
290 char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE;
291 char *uri = t_module->control_uri;
292
293 if (strlen(uri) > RTSP_URI_LENGTH_MAX)
294 {
295 LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX);
296 return VC_CONTAINER_ERROR_URI_OPEN_FAILED;
297 }
298
299 ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, SETUP_METHOD, uri);
300 if (ptr < end)
301 ptr += snprintf(ptr, end - ptr, TRANSPORT_HEADER_FORMAT, t_module->rtp_port, t_module->rtp_port + 1);
302 if (ptr < end)
303 ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++);
304 vc_container_assert(ptr < end);
305
306 return rtsp_send(p_ctx);
307}
308
309/**************************************************************************//**
310 * Send a PLAY request to the RTSP server.
311 *
312 * @param p_ctx The reader context.
313 * @param t_module The track module relating to the PLAY.
314 * @return The resulting status of the function.
315 */
316static VC_CONTAINER_STATUS_T rtsp_send_play_request( VC_CONTAINER_T *p_ctx,
317 VC_CONTAINER_TRACK_MODULE_T *t_module )
318{
319 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
320 char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE;
321 char *uri = t_module->control_uri;
322
323 if (strlen(uri) > RTSP_URI_LENGTH_MAX)
324 {
325 LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX);
326 return VC_CONTAINER_ERROR_URI_OPEN_FAILED;
327 }
328
329 ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, PLAY_METHOD, uri);
330 if (ptr < end)
331 ptr += snprintf(ptr, end - ptr, SESSION_HEADER_FORMAT, t_module->session_header);
332 if (ptr < end)
333 ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++);
334 vc_container_assert(ptr < end);
335
336 return rtsp_send(p_ctx);
337}
338
339/**************************************************************************//**
340 * Send a TEARDOWN request to the RTSP server.
341 *
342 * @param p_ctx The reader context.
343 * @param t_module The track module relating to the SETUP.
344 * @return The resulting status of the function.
345 */
346static VC_CONTAINER_STATUS_T rtsp_send_teardown_request( VC_CONTAINER_T *p_ctx,
347 VC_CONTAINER_TRACK_MODULE_T *t_module )
348{
349 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
350 char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE;
351 char *uri = t_module->control_uri;
352
353 if (strlen(uri) > RTSP_URI_LENGTH_MAX)
354 {
355 LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX);
356 return VC_CONTAINER_ERROR_URI_OPEN_FAILED;
357 }
358
359 ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, TEARDOWN_METHOD, uri);
360 if (ptr < end)
361 ptr += snprintf(ptr, end - ptr, SESSION_HEADER_FORMAT, t_module->session_header);
362 if (ptr < end)
363 ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++);
364 vc_container_assert(ptr < end);
365
366 return rtsp_send(p_ctx);
367}
368
369/**************************************************************************//**
370 * Check a response status line to see if the response is usable or not.
371 * Reasons for invalidity include:
372 * - Incorrectly formatted
373 * - Unsupported version
374 * - Status code is not in the 2xx range
375 *
376 * @param p_ctx The reader context.
377 * @param status_line The response status line.
378 * @return The resulting status of the function.
379 */
380static bool rtsp_successful_response_status( VC_CONTAINER_T *p_ctx,
381 const char *status_line)
382{
383 unsigned int major_version, minor_version, status_code;
384
385 /* coverity[secure_coding] String is null-terminated */
386 if (sscanf(status_line, "RTSP/%u.%u %u", &major_version, &minor_version, &status_code) != 3)
387 {
388 LOG_ERROR(p_ctx, "RTSP: Invalid response status line:\n%s", status_line);
389 return false;
390 }
391
392 if (major_version != RTSP_MAJOR_VERSION || minor_version != RTSP_MINOR_VERSION)
393 {
394 LOG_ERROR(p_ctx, "RTSP: Unexpected response RTSP version: %u.%u", major_version, minor_version);
395 return false;
396 }
397
398 if (status_code < RTSP_STATUS_OK || status_code >= RTSP_STATUS_MULTIPLE_CHOICES)
399 {
400 LOG_ERROR(p_ctx, "RTSP: Response status unsuccessful:\n%s", status_line);
401 return false;
402 }
403
404 return true;
405}
406
407/**************************************************************************//**
408 * Get the content length header from the response headers as an unsigned
409 * 32-bit integer.
410 * If the content length header is not found or badly formatted, zero is
411 * returned.
412 *
413 * @param header_list The response headers.
414 * @return The content length.
415 */
416static uint32_t rtsp_get_content_length( VC_CONTAINERS_LIST_T *header_list )
417{
418 unsigned int content_length = 0;
419 RTSP_HEADER_T header;
420
421 header.name = CONTENT_LENGTH_NAME;
422 if (header_list && vc_containers_list_find_entry(header_list, &header))
423 /* coverity[secure_coding] String is null-terminated */
424 sscanf(header.value, "%u", &content_length);
425
426 return content_length;
427}
428
429/**************************************************************************//**
430 * Get the session header from the response headers.
431 * If the session header is not found, the empty string is returned.
432 *
433 * @param header_list The response headers.
434 * @return The session header.
435 */
436static const char *rtsp_get_session_header(VC_CONTAINERS_LIST_T *header_list)
437{
438 RTSP_HEADER_T header;
439
440 header.name = SESSION_NAME;
441 if (header_list && vc_containers_list_find_entry(header_list, &header))
442 return header.value;
443
444 return "";
445}
446
447/**************************************************************************//**
448 * Returns pointer to the string with any leading whitespace trimmed and
449 * terminated by a delimiter, as determined by is_delimiter_fn. The delimiter
450 * character replaced by NUL (to terminate the string) is returned in the
451 * variable pointed at by p_delimiter_replaced, if it is not NULL.
452 *
453 * The parse_str pointer is moved on to the character after the delimiter, or
454 * the end of the string if it is reached first.
455 *
456 * @param parse_str Pointer to the string pointer to parse.
457 * @param is_delimiter_fn Function to test if a character is a delimiter or not.
458 * @param p_delimiter_replaced Pointer to variable to receive delimiter character, or NULL.
459 * @return Pointer to extracted string.
460 */
461static char *rtsp_parse_extract(char **parse_str,
462 PARSE_IS_DELIMITER_FN_T is_delimiter_fn,
463 char *p_delimiter_replaced)
464{
465 char *ptr;
466 char *result;
467
468 vc_container_assert(parse_str);
469 vc_container_assert(*parse_str);
470 vc_container_assert(is_delimiter_fn);
471
472 ptr = *parse_str;
473
474 while (isspace((int)*ptr))
475 ptr++;
476
477 result = ptr;
478
479 while (*ptr && !(*is_delimiter_fn)(*ptr))
480 ptr++;
481 if (p_delimiter_replaced)
482 *p_delimiter_replaced = *ptr;
483 if (*ptr)
484 *ptr++ = '\0';
485
486 *parse_str = ptr;
487 return result;
488}
489
490/**************************************************************************//**
491 * Specialised form of rtsp_parse_extract() where the delimiter is whitespace.
492 * Returns pointer to the string with any leading whitespace trimmed and
493 * terminated by further whitespace.
494 *
495 * The parse_str pointer is moved on to the character after the delimiter, or
496 * the end of the string if it is reached first.
497 *
498 * @param parse_str Pointer to the string pointer to parse.
499 * @return Pointer to extracted string.
500 */
501static char *rtsp_parse_extract_ws(char **parse_str)
502{
503 char *ptr;
504 char *result;
505
506 vc_container_assert(parse_str);
507 vc_container_assert(*parse_str);
508
509 ptr = *parse_str;
510
511 while (isspace((int)*ptr))
512 ptr++;
513
514 result = ptr;
515
516 while (*ptr && !isspace((int)*ptr))
517 ptr++;
518 if (*ptr)
519 *ptr++ = '\0';
520
521 *parse_str = ptr;
522 return result;
523}
524
525/**************************************************************************//**
526 * Returns whether the given character is a parameter name delimiter or not.
527 *
528 * @param char_to_test The character under test.
529 * @return True if the character is a name delimiter, false if not.
530 */
531static int name_delimiter_fn(int char_to_test)
532{
533 switch (char_to_test)
534 {
535 case ' ':
536 case '\t':
537 case '=':
538 case ';':
539 return true;
540 default:
541 return false;
542 }
543}
544
545/**************************************************************************//**
546 * Returns whether the given character is a parameter value delimiter or not.
547 *
548 * @param char_to_test The character under test.
549 * @return True if the character is a value delimiter, false if not.
550 */
551static int value_delimiter_fn(int char_to_test)
552{
553 switch (char_to_test)
554 {
555 case ' ':
556 case '\t':
557 case ';':
558 return true;
559 default:
560 return false;
561 }
562}
563
564/**************************************************************************//**
565 * Extract a name/value pair from a given string.
566 * Each pair consists of a name, optionally followed by '=' and a value, with
567 * optional whitespace around either or both name and value. The parameter is
568 * terminated by a semi-colon, ';'.
569 *
570 * The parse_str pointer is moved on to the next parameter, or the end of the
571 * string if that is reached first.
572 *
573 * Name can be empty if there are two consecutive semi-colons, or a trailing
574 * semi-colon.
575 *
576 * @param parse_str Pointer to the string pointer to be parsed.
577 * @param p_name Pointer to where name string pointer shall be written.
578 * @param p_value Pointer to where value string pointer shall be written.
579 * @return True if the name is not empty.
580 */
581static bool rtsp_parse_extract_parameter(char **parse_str, char **p_name, char **p_value)
582{
583 char delimiter;
584
585 vc_container_assert(parse_str);
586 vc_container_assert(*parse_str);
587 vc_container_assert(p_name);
588 vc_container_assert(p_value);
589
590 /* General form of each parameter:
591 * <name>[=<value>]
592 * but allow for spaces before and after name and value */
593 *p_name = rtsp_parse_extract(parse_str, name_delimiter_fn, &delimiter);
594 if (isspace((int)delimiter))
595 {
596 /* Skip further spaces after parameter name */
597 do {
598 delimiter = **parse_str;
599 if (delimiter)
600 (*parse_str)++;
601 } while (isspace((int)delimiter));
602 }
603
604 if (delimiter == '=')
605 {
606 /* Parameter value present (although may be empty) */
607 *p_value = rtsp_parse_extract(parse_str, value_delimiter_fn, &delimiter);
608 if (isspace((int)delimiter))
609 {
610 /* Skip spaces after parameter value */
611 do {
612 delimiter = **parse_str;
613 if (delimiter)
614 (*parse_str)++;
615 } while (isspace((int)delimiter));
616 }
617 } else {
618 *p_value = NULL;
619 }
620
621 return (**p_name != '\0');
622}
623
624/**************************************************************************//**
625 * Parses RTP-Info header and stores relevant parts.
626 *
627 * @param header_list The response header list.
628 * @param t_module The track module relating to the response headers.
629 */
630static void rtsp_store_rtp_info(VC_CONTAINERS_LIST_T *header_list,
631 VC_CONTAINER_TRACK_MODULE_T *t_module )
632{
633 RTSP_HEADER_T header;
634 char *ptr;
635
636 header.name = RTP_INFO_NAME;
637 if (!vc_containers_list_find_entry(header_list, &header))
638 return;
639
640 ptr = header.value;
641 while (ptr && *ptr)
642 {
643 char *name;
644 char *value;
645
646 if (!rtsp_parse_extract_parameter(&ptr, &name, &value))
647 continue;
648
649 if (strcasecmp(name, "rtptime") == 0)
650 {
651 unsigned int timestamp_base = 0;
652
653 /* coverity[secure_coding] String is null-terminated */
654 if (sscanf(value, "%u", &timestamp_base) == 1)
655 (void)vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_SET_TIMESTAMP_BASE, timestamp_base);
656 }
657 else if (strcasecmp(name, "seq") == 0)
658 {
659 unsigned short int sequence_number = 0;
660
661 /* coverity[secure_coding] String is null-terminated */
662 if (sscanf(value, "%hu", &sequence_number) == 1)
663 (void)vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_SET_NEXT_SEQUENCE_NUMBER, (uint32_t)sequence_number);
664 }
665 }
666}
667
668/**************************************************************************//**
669 * Reads an RTSP response and parses it into headers and content.
670 * The headers and content remain stored in the comms buffer, but referenced
671 * by the module's header list. Content uses a special header name that cannot
672 * occur in the real headers.
673 *
674 * @param p_ctx The RTSP reader context.
675 * @return The resulting status of the function.
676 */
677static VC_CONTAINER_STATUS_T rtsp_read_response( VC_CONTAINER_T *p_ctx )
678{
679 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
680 VC_CONTAINER_IO_T *p_ctx_io = p_ctx->priv->io;
681 char *next_read = module->comms_buffer;
682 uint32_t space_available = COMMS_BUFFER_SIZE - 1; /* Allow for a NUL */
683 uint32_t received;
684 char *ptr = next_read;
685 bool found_content = false;
686 RTSP_HEADER_T header;
687
688 vc_containers_list_reset(module->header_list);
689
690 /* Response status line doesn't need to be stored, just checked */
691 header.name = NULL;
692 header.value = next_read;
693
694 while (space_available)
695 {
696 received = vc_container_io_read(p_ctx_io, next_read, space_available);
697 if (p_ctx_io->status != VC_CONTAINER_SUCCESS)
698 break;
699
700 next_read += received;
701 space_available -= received;
702
703 while (!found_content && ptr < next_read)
704 {
705 switch (*ptr)
706 {
707 case ':':
708 if (header.value)
709 {
710 /* Just another character in the value */
711 ptr++;
712 } else {
713 /* End of name, expect value next */
714 *ptr++ = '\0';
715 header.value = ptr;
716 }
717 break;
718
719 case '\n':
720 if (header.value)
721 {
722 /* End of line while parsing the value part of the header, add name/value pair to list */
723 *ptr++ = '\0';
724 header.value = rtsp_trim(header.value);
725 if (header.name)
726 {
727 if (!vc_containers_list_insert(module->header_list, &header, false))
728 {
729 LOG_ERROR(p_ctx, "RTSP: Failed to add <%s> header to list", header.name);
730 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
731 }
732 } else {
733 /* Check response status line */
734 if (!rtsp_successful_response_status(p_ctx, header.value))
735 return VC_CONTAINER_ERROR_FORMAT_INVALID;
736 }
737 /* Ready for next header */
738 header.name = ptr;
739 header.value = NULL;
740 } else {
741 uint32_t content_length;
742
743 /* End of line while parsing the name of a header */
744 *ptr++ = '\0';
745 if (*header.name && *header.name != '\r')
746 {
747 /* A non-empty name is invalid, so fail */
748 LOG_ERROR(p_ctx, "RTSP: Invalid name in header - no colon:\n%s", header.name);
749 return VC_CONTAINER_ERROR_FORMAT_INVALID;
750 }
751
752 /* An empty name signifies the start of the content has been found */
753 found_content = true;
754
755 /* Make a pseudo-header for the content and add it to the list */
756 header.name = CONTENT_PSEUDOHEADER_NAME;
757 header.value = ptr;
758 if (!vc_containers_list_insert(module->header_list, &header, false))
759 {
760 LOG_ERROR(p_ctx, "RTSP: Failed to add content pseudoheader to list");
761 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
762 }
763
764 /* Calculate how much content there is left to read, based on Content-Length header */
765 content_length = rtsp_get_content_length(module->header_list);
766 if (ptr + content_length < next_read)
767 {
768 /* Final content byte already present, with extra data after it */
769 space_available = 0;
770 } else {
771 uint32_t content_to_read = content_length - (next_read - ptr);
772
773 if (content_to_read >= space_available)
774 {
775 LOG_ERROR(p_ctx, "RTSP: Not enough room to read content");
776 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
777 }
778
779 /* Restrict further reading to the number of content bytes left */
780 space_available = content_to_read;
781 }
782 }
783 break;
784
785 default:
786 /* Just another character in either the name or the value */
787 ptr++;
788 }
789 }
790 }
791
792 if (!space_available)
793 {
794 if (found_content)
795 {
796 /* Terminate content region */
797 *next_read = '\0';
798 } else {
799 /* Ran out of buffer space and never found the content */
800 LOG_ERROR(p_ctx, "RTSP: Response header section too big / content missing");
801 return VC_CONTAINER_ERROR_FORMAT_INVALID;
802 }
803 }
804
805 return p_ctx_io->status;
806}
807
808/**************************************************************************//**
809 * Creates a new track from an SDP media field.
810 * Limitation: only the first payload type of the field is used.
811 *
812 * @param p_ctx The RTSP reader context.
813 * @param media The media field.
814 * @param p_track Pointer to the variable to receive the new track pointer.
815 * @return The resulting status of the function.
816 */
817static VC_CONTAINER_STATUS_T rtsp_create_track_for_media_field(VC_CONTAINER_T *p_ctx,
818 char *media,
819 VC_CONTAINER_TRACK_T **p_track )
820{
821 VC_CONTAINER_TRACK_T *track = NULL;
822 VC_CONTAINER_TRACK_MODULE_T *t_module = NULL;
823 char *ptr = media;
824 char *media_type;
825 char *rtp_port;
826 char *transport_type;
827 char *payload_type;
828
829 *p_track = NULL;
830 if (p_ctx->tracks_num == RTSP_TRACKS_MAX)
831 {
832 LOG_DEBUG(p_ctx, "RTSP: Too many media items in SDP data, only %d are supported.", RTSP_TRACKS_MAX);
833 return VC_CONTAINER_ERROR_FORMAT_INVALID;
834 }
835
836 /* Format of media item:
837 * m=<media type> <port> <transport> <payload type(s)>
838 * Only RTP/AVP transport and the first payload type are supported */
839 media_type = rtsp_parse_extract_ws(&ptr);
840 rtp_port = rtsp_parse_extract_ws(&ptr);
841 transport_type = rtsp_parse_extract_ws(&ptr);
842 payload_type = rtsp_parse_extract_ws(&ptr);
843 if (!*media_type || !*rtp_port || strcmp(transport_type, "RTP/AVP") || !*payload_type)
844 {
845 LOG_ERROR(p_ctx, "RTSP: Failure to parse media field");
846 return VC_CONTAINER_ERROR_FORMAT_INVALID;
847 }
848
849 track = vc_container_allocate_track(p_ctx, sizeof(VC_CONTAINER_TRACK_MODULE_T));
850 if (!track) goto out_of_memory_error;
851 t_module = track->priv->module;
852
853 /* If the port specifier is invalid, treat it as if it were zero */
854 /* coverity[secure_coding] String is null-terminated */
855 sscanf(rtp_port, "%hu", &t_module->rtp_port);
856 t_module->payload_type = payload_type;
857 t_module->media_type = media_type;
858
859 t_module->reader_uri = vc_uri_create();
860 if (!t_module->reader_uri) goto out_of_memory_error;
861 if (!vc_uri_set_scheme(t_module->reader_uri, RTP_SCHEME)) goto out_of_memory_error;
862 if (!vc_uri_add_query(t_module->reader_uri, PAYLOAD_TYPE_NAME, payload_type)) goto out_of_memory_error;
863
864 p_ctx->tracks[p_ctx->tracks_num++] = track;
865 *p_track = track;
866 return VC_CONTAINER_SUCCESS;
867
868out_of_memory_error:
869 if (track)
870 {
871 if (t_module->reader_uri)
872 vc_uri_release(t_module->reader_uri);
873 vc_container_free_track(p_ctx, track);
874 }
875 LOG_ERROR(p_ctx, "RTSP: Memory allocation failure creating track");
876 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
877}
878
879/**************************************************************************//**
880 * Returns whether the given character is a slash or not.
881 *
882 * @param char_to_test The character under test.
883 * @return True if the character is a slash, false if not.
884 */
885static int slash_delimiter_fn(int char_to_test)
886{
887 return char_to_test == '/';
888}
889
890/**************************************************************************//**
891 * Parse an rtpmap attribute and store values in the related track.
892 *
893 * @param p_ctx The RTSP reader context.
894 * @param track The track relating to the rtpmap.
895 * @param attribute The rtpmap attribute value.
896 * @return The resulting status of the function.
897 */
898static VC_CONTAINER_STATUS_T rtsp_parse_rtpmap_attribute( VC_CONTAINER_T *p_ctx,
899 VC_CONTAINER_TRACK_T *track,
900 char *attribute )
901{
902 VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module;
903 char *ptr = attribute;
904 char *payload_type;
905 char *mime_sub_type;
906 char *sample_rate;
907 char *full_mime_type;
908 char *channels;
909
910 /* rtpmap attribute format:
911 * <payload type> <MIME type>/<sample rate>[/<channels>]
912 * Payload type must match the one used in the media field */
913 payload_type = rtsp_parse_extract_ws(&ptr);
914 if (strcmp(payload_type, t_module->payload_type))
915 {
916 /* Ignore any unsupported secondary payload type attributes */
917 LOG_DEBUG(p_ctx, "RTSP: Secondary payload type attribute - not supported");
918 return VC_CONTAINER_SUCCESS;
919 }
920
921 mime_sub_type = rtsp_parse_extract(&ptr, slash_delimiter_fn, NULL);
922 if (!*mime_sub_type)
923 {
924 LOG_ERROR(p_ctx, "RTSP: rtpmap: MIME type missing");
925 return VC_CONTAINER_ERROR_FORMAT_INVALID;
926 }
927
928 sample_rate = rtsp_parse_extract(&ptr, slash_delimiter_fn, NULL);
929 if (!*sample_rate)
930 {
931 LOG_ERROR(p_ctx, "RTSP: rtpmap: sample rate missing");
932 return VC_CONTAINER_ERROR_FORMAT_INVALID;
933 }
934
935 full_mime_type = (char *)malloc(strlen(t_module->media_type) + strlen(mime_sub_type) + 2);
936 if (!full_mime_type)
937 {
938 LOG_ERROR(p_ctx, "RTSP: Failed to allocate space for full MIME type");
939 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
940 }
941 /* coverity[secure_coding] String has been allocated of the right size */
942 sprintf(full_mime_type, "%s/%s", t_module->media_type, mime_sub_type);
943 if (!vc_uri_add_query(t_module->reader_uri, MIME_TYPE_NAME, full_mime_type))
944 {
945 free(full_mime_type);
946 LOG_ERROR(p_ctx, "RTSP: Failed to add MIME type to URI");
947 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
948 }
949 free(full_mime_type);
950
951 if (!vc_uri_add_query(t_module->reader_uri, SAMPLE_RATE_NAME, sample_rate))
952 {
953 LOG_ERROR(p_ctx, "RTSP: Failed to add sample rate to URI");
954 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
955 }
956
957 /* Optional channels specifier */
958 channels = rtsp_parse_extract_ws(&ptr);
959 if (*channels)
960 {
961 if (!vc_uri_add_query(t_module->reader_uri, CHANNELS_NAME, channels))
962 {
963 LOG_ERROR(p_ctx, "RTSP: Failed to add channels to URI");
964 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
965 }
966 }
967
968 return VC_CONTAINER_SUCCESS;
969}
970
971/**************************************************************************//**
972 * Parse an fmtp attribute and store values in the related track.
973 *
974 * @param p_ctx The RTSP reader context.
975 * @param track The track relating to the fmtp.
976 * @param attribute The fmtp attribute value.
977 * @return The resulting status of the function.
978 */
979static VC_CONTAINER_STATUS_T rtsp_parse_fmtp_attribute( VC_CONTAINER_T *p_ctx,
980 VC_CONTAINER_TRACK_T *track,
981 char *attribute )
982{
983 VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module;
984 char *ptr = attribute;
985 char *payload_type;
986
987 /* fmtp attribute format:
988 * <payload type> <parameters>
989 * The payload type must match the first one in the media field, parameters
990 * are semi-colon separated and may have additional whitespace around them. */
991
992 payload_type = rtsp_parse_extract_ws(&ptr);
993 if (strcmp(payload_type, t_module->payload_type))
994 {
995 /* Ignore any unsupported secondary payload type attributes */
996 LOG_DEBUG(p_ctx, "RTSP: Secondary payload type attribute - not supported");
997 return VC_CONTAINER_SUCCESS;
998 }
999
1000 while (*ptr)
1001 {
1002 char *name;
1003 char *value;
1004
1005 /* Only add the parameter if the name was not empty. This avoids problems with
1006 * strings like ";;", ";" or ";=value;" */
1007 if (rtsp_parse_extract_parameter(&ptr, &name, &value))
1008 {
1009 if (!vc_uri_add_query(t_module->reader_uri, name, value))
1010 {
1011 if (value)
1012 LOG_ERROR(p_ctx, "RTSP: Failed to add <%s>=<%s> query to URI", name, value);
1013 else
1014 LOG_ERROR(p_ctx, "RTSP: Failed to add <%s> query to URI", name);
1015 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1016 }
1017 }
1018 }
1019
1020 return VC_CONTAINER_SUCCESS;
1021}
1022
1023/**************************************************************************//**
1024 * Merge base URI and relative URI strings into a merged URI string.
1025 * Always creates a new string, even if the relative URI is actually absolute.
1026 *
1027 * @param p_ctx The RTSP reader context.
1028 * @param base_uri_str The base URI string.
1029 * @param relative_uri_str The relative URI string.
1030 * @param p_merged_uri_str Pointer to where to put the merged string pointer.
1031 * @return The resulting status of the function.
1032 */
1033static VC_CONTAINER_STATUS_T rtsp_merge_uris( VC_CONTAINER_T *p_ctx,
1034 const char *base_uri_str,
1035 const char *relative_uri_str,
1036 char **p_merged_uri_str)
1037{
1038 VC_URI_PARTS_T *base_uri = NULL;
1039 VC_URI_PARTS_T *relative_uri = NULL;
1040 VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1041 uint32_t merged_size;
1042
1043 *p_merged_uri_str = NULL;
1044 relative_uri = vc_uri_create();
1045 if (!relative_uri) goto tidy_up;
1046 if (!vc_uri_parse(relative_uri, relative_uri_str))
1047 {
1048 status = VC_CONTAINER_ERROR_FORMAT_INVALID;
1049 goto tidy_up;
1050 }
1051
1052 if (vc_uri_scheme(relative_uri) != NULL)
1053 {
1054 /* URI is absolute, not relative, so return it as the merged URI */
1055 size_t len = strlen(relative_uri_str);
1056
1057 *p_merged_uri_str = (char *)malloc(len + 1);
1058 if (!*p_merged_uri_str) goto tidy_up;
1059
1060 strncpy(*p_merged_uri_str, relative_uri_str, len);
1061 status = VC_CONTAINER_SUCCESS;
1062 goto tidy_up;
1063 }
1064
1065 base_uri = vc_uri_create();
1066 if (!base_uri) goto tidy_up;
1067 if (!vc_uri_parse(base_uri, base_uri_str))
1068 {
1069 status = VC_CONTAINER_ERROR_FORMAT_INVALID;
1070 goto tidy_up;
1071 }
1072
1073 /* Build up merged URI in relative_uri, using base_uri as necessary */
1074 if (!vc_uri_merge(base_uri, relative_uri)) goto tidy_up;
1075
1076 merged_size = vc_uri_build(relative_uri, NULL, 0) + 1;
1077 *p_merged_uri_str = (char *)malloc(merged_size);
1078 if (!*p_merged_uri_str) goto tidy_up;
1079
1080 vc_uri_build(relative_uri, *p_merged_uri_str, merged_size);
1081
1082 status = VC_CONTAINER_SUCCESS;
1083
1084tidy_up:
1085 if (base_uri) vc_uri_release(base_uri);
1086 if (relative_uri) vc_uri_release(relative_uri);
1087 if (status != VC_CONTAINER_SUCCESS)
1088 LOG_ERROR(p_ctx, "RTSP: Error merging URIs: %d", (int)status);
1089 return status;
1090}
1091
1092/**************************************************************************//**
1093 * Parse a control attribute and store it as an absolute URI.
1094 *
1095 * @param p_ctx The RTSP reader context.
1096 * @param attribute The control attribute value.
1097 * @param base_uri_str The base URI string.
1098 * @param p_control_uri_str Pointer to where to put the control string pointer.
1099 * @return The resulting status of the function.
1100 */
1101static VC_CONTAINER_STATUS_T rtsp_parse_control_attribute( VC_CONTAINER_T *p_ctx,
1102 const char *attribute,
1103 const char *base_uri_str,
1104 char **p_control_uri_str)
1105{
1106 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
1107
1108 /* control attribute format:
1109 * <control URI>
1110 * The control URI is either absolute or relative to the base URI. If the
1111 * control URI is just an asterisk, the control URI matches the base URI. */
1112
1113 if (!*attribute || strcmp(attribute, "*") == 0)
1114 {
1115 size_t len = strlen(base_uri_str);
1116
1117 *p_control_uri_str = (char *)malloc(len + 1);
1118 if (!*p_control_uri_str)
1119 {
1120 LOG_ERROR(p_ctx, "RTSP: Failed to allocate control URI");
1121 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1122 }
1123 strncpy(*p_control_uri_str, base_uri_str, len);
1124 } else {
1125 status = rtsp_merge_uris(p_ctx, base_uri_str, attribute, p_control_uri_str);
1126 }
1127
1128 return status;
1129}
1130
1131/**************************************************************************//**
1132 * Open a reader for the track using the URI that has been generated.
1133 *
1134 * @param p_ctx The RTSP reader context.
1135 * @param t_module The track module for which a reader is needed.
1136 * @return The resulting status of the function.
1137 */
1138static VC_CONTAINER_STATUS_T rtsp_open_track_reader( VC_CONTAINER_T *p_ctx,
1139 VC_CONTAINER_TRACK_MODULE_T *t_module )
1140{
1141 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
1142 uint32_t uri_buffer_size;
1143 char *uri_buffer;
1144
1145 uri_buffer_size = vc_uri_build(t_module->reader_uri, NULL, 0) + 1;
1146 uri_buffer = (char *)malloc(uri_buffer_size);
1147 if (!uri_buffer)
1148 {
1149 LOG_ERROR(p_ctx, "RTSP: Failed to build RTP URI");
1150 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1151 }
1152 vc_uri_build(t_module->reader_uri, uri_buffer, uri_buffer_size);
1153
1154 t_module->reader = vc_container_open_reader(uri_buffer, &status, NULL, NULL);
1155 free(uri_buffer);
1156
1157 return status;
1158}
1159
1160/**************************************************************************//**
1161 * Open a reader for the track using the network URI that has been generated.
1162 *
1163 * @param p_ctx The RTSP reader context.
1164 * @param t_module The track module for which a reader is needed.
1165 * @return The resulting status of the function.
1166 */
1167static VC_CONTAINER_STATUS_T rtsp_open_network_reader( VC_CONTAINER_T *p_ctx,
1168 VC_CONTAINER_TRACK_MODULE_T *t_module )
1169{
1170 char port[PORT_BUFFER_SIZE] = {0};
1171
1172 if (!t_module->rtp_port)
1173 {
1174 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1175
1176 t_module->rtp_port = module->next_rtp_port;
1177 if (t_module->rtp_port > LAST_DYNAMIC_PORT)
1178 {
1179 LOG_ERROR(p_ctx, "RTSP: Out of dynamic ports");
1180 return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
1181 }
1182
1183 module->next_rtp_port += 2;
1184 }
1185
1186 snprintf(port, sizeof(port)-1, "%hu", t_module->rtp_port);
1187 if (!vc_uri_set_port(t_module->reader_uri, port))
1188 {
1189 LOG_ERROR(p_ctx, "RTSP: Failed to set track reader URI port");
1190 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1191 }
1192
1193 return rtsp_open_track_reader(p_ctx, t_module);
1194}
1195
1196/**************************************************************************//**
1197 * Open a reader for the track using the file URI that has been generated.
1198 *
1199 * @param p_ctx The RTSP reader context.
1200 * @param t_module The track module for which a reader is needed.
1201 * @return The resulting status of the function.
1202 */
1203static VC_CONTAINER_STATUS_T rtsp_open_file_reader( VC_CONTAINER_T *p_ctx,
1204 VC_CONTAINER_TRACK_MODULE_T *t_module )
1205{
1206 VC_CONTAINER_STATUS_T status;
1207 VC_URI_PARTS_T *rtsp_uri = NULL;
1208 const char *rtsp_path;
1209 int len;
1210 char *new_path = NULL;
1211 char *extension;
1212
1213 /* Use the RTSP URI's path, with the extension changed to ".t<track>.pkt"
1214 * where <track> is the zero-based media item number. */
1215
1216 rtsp_uri = vc_uri_create();
1217 if (!rtsp_uri)
1218 {
1219 LOG_ERROR(p_ctx, "RTSP: Failed to create RTSP URI");
1220 status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1221 goto tidy_up;
1222 }
1223
1224 if (!vc_uri_parse(rtsp_uri, p_ctx->priv->io->uri))
1225 {
1226 LOG_ERROR(p_ctx, "RTSP: Failed to parse RTSP URI <%s>", p_ctx->priv->io->uri);
1227 status = VC_CONTAINER_ERROR_FORMAT_INVALID;
1228 goto tidy_up;
1229 }
1230
1231 rtsp_path = vc_uri_path(rtsp_uri);
1232 if (!rtsp_path || !*rtsp_path)
1233 {
1234 LOG_ERROR(p_ctx, "RTSP: RTSP URI path missing <%s>", p_ctx->priv->io->uri);
1235 status = VC_CONTAINER_ERROR_FORMAT_INVALID;
1236 goto tidy_up;
1237 }
1238
1239 len = strlen(rtsp_path);
1240 new_path = (char *)calloc(1, len + RTP_PATH_EXTRA + 1);
1241 if (!rtsp_uri)
1242 {
1243 LOG_ERROR(p_ctx, "RTSP: Failed to create buffer for RTP path");
1244 status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1245 goto tidy_up;
1246 }
1247
1248 strncpy(new_path, rtsp_path, len);
1249 extension = strrchr(new_path, '.'); /* Find extension, to replace it */
1250 if (!extension)
1251 extension = new_path + strlen(new_path); /* No extension, so append instead */
1252
1253 snprintf(extension, len + RTP_PATH_EXTRA - (extension - new_path),
1254 RTP_PATH_EXTENSION_FORMAT, p_ctx->priv->module->media_item);
1255 if (!vc_uri_set_path(t_module->reader_uri, new_path))
1256 {
1257 LOG_ERROR(p_ctx, "RTSP: Failed to store RTP path <%s>", new_path);
1258 status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1259 goto tidy_up;
1260 }
1261
1262 free(new_path);
1263 vc_uri_release(rtsp_uri);
1264
1265 return rtsp_open_track_reader(p_ctx, t_module);
1266
1267tidy_up:
1268 if (new_path) free(new_path);
1269 if (rtsp_uri) vc_uri_release(rtsp_uri);
1270 return status;
1271}
1272
1273/**************************************************************************//**
1274 * Copy track information from the encapsulated track reader's track to the
1275 * RTSP track.
1276 *
1277 * @param p_ctx The RTSP reader context.
1278 * @param track The RTSP track requiring its information to be filled in.
1279 * @return The resulting status of the function.
1280 */
1281static VC_CONTAINER_STATUS_T rtsp_copy_track_data_from_reader( VC_CONTAINER_T *p_ctx,
1282 VC_CONTAINER_TRACK_T *track )
1283{
1284 VC_CONTAINER_T *reader = track->priv->module->reader;
1285 VC_CONTAINER_ES_FORMAT_T *src_format, *dst_format;
1286
1287 if (reader->tracks_num != 1)
1288 {
1289 LOG_ERROR(p_ctx, "RTSP: Expected track reader to have one track, has %d", reader->tracks_num);
1290 return VC_CONTAINER_ERROR_FORMAT_INVALID;
1291 }
1292
1293 if (reader->tracks[0]->meta_num)
1294 {
1295 LOG_DEBUG(p_ctx, "RTSP: Track reader has meta data - not supported");
1296 }
1297
1298 src_format = reader->tracks[0]->format;
1299 dst_format = track->format;
1300
1301 /* Copy fields individually to avoid problems with pointers (type and extradata). */
1302 dst_format->es_type = src_format->es_type;
1303 dst_format->codec = src_format->codec;
1304 dst_format->codec_variant = src_format->codec_variant;
1305 *dst_format->type = *src_format->type;
1306 dst_format->bitrate = src_format->bitrate;
1307 memcpy(dst_format->language, src_format->language, sizeof(dst_format->language));
1308 dst_format->group_id = src_format->group_id;
1309 dst_format->flags = src_format->flags;
1310
1311 if (src_format->extradata)
1312 {
1313 VC_CONTAINER_STATUS_T status;
1314 uint32_t extradata_size = src_format->extradata_size;
1315
1316 status = vc_container_track_allocate_extradata(p_ctx, track, extradata_size);
1317 if (status != VC_CONTAINER_SUCCESS)
1318 return status;
1319
1320 memcpy(dst_format->extradata, src_format->extradata, extradata_size);
1321 dst_format->extradata_size = extradata_size;
1322 }
1323
1324 track->is_enabled = reader->tracks[0]->is_enabled;
1325
1326 return VC_CONTAINER_SUCCESS;
1327}
1328
1329/**************************************************************************//**
1330 * Finalise the creation of the RTSP track by opening its reader and filling in
1331 * the track information.
1332 *
1333 * @param p_ctx The RTSP reader context.
1334 * @param track The RTSP track being finalised.
1335 * @return The resulting status of the function.
1336 */
1337static VC_CONTAINER_STATUS_T rtsp_complete_track( VC_CONTAINER_T *p_ctx,
1338 VC_CONTAINER_TRACK_T *track )
1339{
1340 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1341 VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module;
1342 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
1343
1344 if (!t_module->control_uri)
1345 {
1346 LOG_ERROR(p_ctx, "RTSP: Track control URI is missing");
1347 return VC_CONTAINER_ERROR_FORMAT_INVALID;
1348 }
1349
1350 if (module->uri_has_network_info)
1351 {
1352 int ii;
1353
1354 if (!vc_uri_set_host(t_module->reader_uri, ""))
1355 {
1356 LOG_ERROR(p_ctx, "RTSP: Failed to set track reader URI host");
1357 return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1358 }
1359
1360 status = rtsp_open_network_reader(p_ctx, t_module);
1361
1362 for (ii = 0; status == VC_CONTAINER_ERROR_URI_OPEN_FAILED && ii < DYNAMIC_PORT_ATTEMPTS_MAX; ii++)
1363 {
1364 /* Reset port to pick up next dynamic port */
1365 t_module->rtp_port = 0;
1366 status = rtsp_open_network_reader(p_ctx, t_module);
1367 }
1368
1369 /* Change I/O to non-blocking, so that tracks can be polled */
1370 if (status == VC_CONTAINER_SUCCESS)
1371 status = vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, 0);
1372 /* Set a large read buffer, to avoid dropping bursts of large packets (e.g. hi-def video) */
1373 if (status == VC_CONTAINER_SUCCESS)
1374 status = vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE, UDP_READ_BUFFER_SIZE);
1375 } else {
1376 status = rtsp_open_file_reader(p_ctx, t_module);
1377 }
1378
1379 vc_uri_release(t_module->reader_uri);
1380 t_module->reader_uri = NULL;
1381
1382 if (status == VC_CONTAINER_SUCCESS)
1383 status = rtsp_copy_track_data_from_reader(p_ctx, track);
1384
1385 return status;
1386}
1387
1388/**************************************************************************//**
1389 * Returns whether the given character is an attribute name delimiter or not.
1390 *
1391 * @param char_to_test The character under test.
1392 * @return True if the character is an attribute name delimiter, false if not.
1393 */
1394static int attribute_name_delimiter_fn(int char_to_test)
1395{
1396 return (char_to_test == ':');
1397}
1398
1399/**************************************************************************//**
1400 * Create RTSP tracks from media fields in SDP formatted data.
1401 *
1402 * @param p_ctx The RTSP reader context.
1403 * @param sdp_buffer The SDP data.
1404 * @param base_uri The RTSP base URI.
1405 * @return The resulting status of the function.
1406 */
1407static VC_CONTAINER_STATUS_T rtsp_create_tracks_from_sdp( VC_CONTAINER_T *p_ctx,
1408 char *sdp_buffer,
1409 char *base_uri )
1410{
1411 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
1412 VC_CONTAINER_TRACK_T *track = NULL;
1413 char *session_base_uri = base_uri;
1414 char *ptr;
1415 char *next_line_ptr;
1416 char *attribute;
1417
1418 for (ptr = sdp_buffer; status == VC_CONTAINER_SUCCESS && *ptr; ptr = next_line_ptr)
1419 {
1420 /* Find end of line */
1421 char field = *ptr;
1422
1423 next_line_ptr = ptr;
1424 while (*next_line_ptr && *next_line_ptr != '\n')
1425 next_line_ptr++;
1426
1427 /* Terminate line */
1428 if (*next_line_ptr)
1429 *next_line_ptr++ = '\0';
1430
1431 /* The format of the line has to be "<field>=<value>" where <field> is a single
1432 * character. Ignore anything else. */
1433 if (ptr[1] != '=')
1434 continue;
1435 ptr = rtsp_trim(ptr + 2);
1436
1437 switch (field)
1438 {
1439 case 'm':
1440 /* Start of media item */
1441 if (track)
1442 {
1443 /* Finish previous track */
1444 status = rtsp_complete_track(p_ctx, track);
1445 track = NULL;
1446 p_ctx->priv->module->media_item++;
1447 if (status != VC_CONTAINER_SUCCESS)
1448 break;
1449 }
1450
1451 status = rtsp_create_track_for_media_field(p_ctx, ptr, &track);
1452 break;
1453 case 'a': /* Attribute (either session or media level) */
1454 /* Attributes of the form "a=<name>:<value>" */
1455 attribute = rtsp_parse_extract(&ptr, attribute_name_delimiter_fn, NULL);
1456
1457 if (track)
1458 {
1459 /* Media level attribute */
1460
1461 /* Look for known attributes */
1462 if (strcmp(attribute, "rtpmap") == 0)
1463 status = rtsp_parse_rtpmap_attribute(p_ctx, track, ptr);
1464 else if (strcmp(attribute, "fmtp") == 0)
1465 status = rtsp_parse_fmtp_attribute(p_ctx, track, ptr);
1466 else if (strcmp(attribute, "control") == 0)
1467 {
1468 char **track_control_uri = &track->priv->module->control_uri;
1469
1470 if (*track_control_uri)
1471 {
1472 free(*track_control_uri);
1473 *track_control_uri = NULL;
1474 }
1475 status = rtsp_parse_control_attribute(p_ctx, ptr, session_base_uri, track_control_uri);
1476 }
1477 /* Any other attributes are ignored */
1478 } else {
1479 /* Session level attribute */
1480 if (strcmp(attribute, "control") == 0)
1481 {
1482 /* Only need to change the session_base_uri if it differs from the base URI */
1483 ptr = rtsp_trim(ptr);
1484 if (session_base_uri != base_uri)
1485 {
1486 free(session_base_uri);
1487 session_base_uri = base_uri;
1488 }
1489 if (strcmp(ptr, base_uri) != 0)
1490 status = rtsp_parse_control_attribute(p_ctx, ptr, base_uri, &session_base_uri);
1491 }
1492 }
1493 break;
1494 default: /* Ignore any other field names */
1495 ;
1496 }
1497 }
1498
1499 if (session_base_uri && session_base_uri != base_uri)
1500 free(session_base_uri);
1501
1502 /* Having no media fields is an error, since there will be nothing to play back */
1503 if (status == VC_CONTAINER_SUCCESS)
1504 {
1505 if (!p_ctx->tracks_num)
1506 status = VC_CONTAINER_ERROR_FORMAT_INVALID;
1507 else if (track)
1508 {
1509 /* Finish final track */
1510 status = rtsp_complete_track(p_ctx, track);
1511 p_ctx->priv->module->media_item++;
1512 }
1513 }
1514
1515 return status;
1516}
1517
1518/**************************************************************************//**
1519 * Create RTSP tracks from the response to a DESCRIBE request.
1520 * The response must have already been filled into the comms buffer and header
1521 * list.
1522 *
1523 * @param p_ctx The RTSP reader context.
1524 * @return The resulting status of the function.
1525 */
1526static VC_CONTAINER_STATUS_T rtsp_create_tracks_from_response( VC_CONTAINER_T *p_ctx )
1527{
1528 VC_CONTAINERS_LIST_T *header_list = p_ctx->priv->module->header_list;
1529 RTSP_HEADER_T header;
1530 char *base_uri;
1531 char *content;
1532
1533 header.name = CONTENT_PSEUDOHEADER_NAME;
1534 if (!vc_containers_list_find_entry(header_list, &header))
1535 {
1536 LOG_ERROR(p_ctx, "RTSP: Content missing");
1537 return VC_CONTAINER_ERROR_FORMAT_INVALID;
1538 }
1539 content = header.value;
1540
1541 /* The control URI may be relative to a base URI which is the first of these
1542 * that is available:
1543 * 1. Content-Base header
1544 * 2. Content-Location header
1545 * 3. Request URI
1546 */
1547 header.name = CONTENT_BASE_NAME;
1548 if (vc_containers_list_find_entry(header_list, &header))
1549 base_uri = header.value;
1550 else {
1551 header.name = CONTENT_LOCATION_NAME;
1552 if (vc_containers_list_find_entry(header_list, &header))
1553 base_uri = header.value;
1554 else
1555 base_uri = p_ctx->priv->io->uri;
1556 }
1557
1558 return rtsp_create_tracks_from_sdp(p_ctx, content, base_uri);
1559}
1560
1561/**************************************************************************//**
1562 * Header comparison function.
1563 * Compare two header structures and return whether the first is less than,
1564 * equal to or greater than the second.
1565 *
1566 * @param first The first structure to be compared.
1567 * @param second The second structure to be compared.
1568 * @return Negative if first is less than second, positive if first is greater
1569 * and zero if they are equal.
1570 */
1571static int rtsp_header_comparator(const RTSP_HEADER_T *first, const RTSP_HEADER_T *second)
1572{
1573 return strcasecmp(first->name, second->name);
1574}
1575
1576/**************************************************************************//**
1577 * Make a DESCRIBE request to the server and create tracks from the response.
1578 *
1579 * @param p_ctx The RTSP reader context.
1580 * @return The resulting status of the function.
1581 */
1582static VC_CONTAINER_STATUS_T rtsp_describe( VC_CONTAINER_T *p_ctx )
1583{
1584 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
1585
1586 /* Send DESCRIBE request and get response */
1587 status = rtsp_send_describe_request(p_ctx);
1588 if (status != VC_CONTAINER_SUCCESS) return status;
1589 status = rtsp_read_response(p_ctx);
1590 if (status != VC_CONTAINER_SUCCESS) return status;
1591
1592 /* Create tracks from SDP content */
1593 status = rtsp_create_tracks_from_response(p_ctx);
1594
1595 return status;
1596}
1597
1598/**************************************************************************//**
1599 * Make a SETUP request to the server and get session from response.
1600 *
1601 * @param p_ctx The RTSP reader context.
1602 * @param t_module The track module to be set up.
1603 * @return The resulting status of the function.
1604 */
1605static VC_CONTAINER_STATUS_T rtsp_setup( VC_CONTAINER_T *p_ctx,
1606 VC_CONTAINER_TRACK_MODULE_T *t_module )
1607{
1608 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
1609 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1610 const char *session_header;
1611 size_t session_header_len;
1612
1613 status = rtsp_send_setup_request(p_ctx, t_module);
1614 if (status != VC_CONTAINER_SUCCESS) return status;
1615 status = rtsp_read_response(p_ctx);
1616 if (status != VC_CONTAINER_SUCCESS) return status;
1617
1618 session_header = rtsp_get_session_header(module->header_list);
1619 session_header_len = strlen(session_header);
1620 if (session_header_len > SESSION_HEADER_LENGTH_MAX) return VC_CONTAINER_ERROR_FORMAT_INVALID;
1621
1622 t_module->session_header = (char *)malloc(session_header_len + 1);
1623 if (!t_module->session_header) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1624 strncpy(t_module->session_header, session_header, session_header_len);
1625
1626 return status;
1627}
1628
1629/**************************************************************************//**
1630 * Make a SETUP request to the server and get session from response.
1631 *
1632 * @param p_ctx The RTSP reader context.
1633 * @param t_module The track module to be set up.
1634 * @return The resulting status of the function.
1635 */
1636static VC_CONTAINER_STATUS_T rtsp_play( VC_CONTAINER_T *p_ctx,
1637 VC_CONTAINER_TRACK_MODULE_T *t_module )
1638{
1639 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
1640 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1641
1642 status = rtsp_send_play_request(p_ctx, t_module);
1643 if (status != VC_CONTAINER_SUCCESS) return status;
1644 status = rtsp_read_response(p_ctx);
1645 if (status != VC_CONTAINER_SUCCESS) return status;
1646
1647 rtsp_store_rtp_info(module->header_list, t_module);
1648
1649 return status;
1650}
1651
1652/**************************************************************************//**
1653 * Blocking read/skip data from a container.
1654 * Can also be used to query information about the next block of data.
1655 *
1656 * @pre The container is set to non-blocking.
1657 * @post The container is set to non-blocking.
1658 *
1659 * @param p_ctx The reader context.
1660 * @param p_packet The container packet information, or NULL.
1661 * @param flags The container read flags.
1662 * @return The resulting status of the function.
1663 */
1664static VC_CONTAINER_STATUS_T rtsp_blocking_track_read(VC_CONTAINER_T *p_ctx,
1665 VC_CONTAINER_PACKET_T *p_packet,
1666 uint32_t flags)
1667{
1668 VC_CONTAINER_STATUS_T status;
1669
1670 status = vc_container_read(p_ctx, p_packet, flags);
1671
1672 /* The ..._ABORTED status corresponds to a timeout waiting for data */
1673 if (status == VC_CONTAINER_ERROR_ABORTED)
1674 {
1675 /* So switch to blocking temporarily to wait for some */
1676 (void)vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, VC_CONTAINER_READ_TIMEOUT_BLOCK);
1677 status = vc_container_read(p_ctx, p_packet, flags);
1678 (void)vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, 0);
1679 }
1680
1681 return status;
1682}
1683
1684/**************************************************************************//**
1685 * Update the cached packet info blocks for all tracks.
1686 * If one or more of the tracks has data, set the current track to the one with
1687 * the earliest decode timestamp.
1688 *
1689 * @pre The track readers must not block when data is requested from them.
1690 *
1691 * @param p_ctx The RTSP reader context.
1692 * @return The resulting status of the function.
1693 */
1694static VC_CONTAINER_STATUS_T rtsp_update_track_info( VC_CONTAINER_T *p_ctx )
1695{
1696 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
1697 uint32_t tracks_num = p_ctx->tracks_num;
1698 uint32_t track_idx;
1699 int64_t earliest_dts = MAXIMUM_INT64;
1700 VC_CONTAINER_TRACK_MODULE_T *earliest_track = NULL;
1701
1702 /* Reset current track to unknown */
1703 p_ctx->priv->module->current_track = NULL;
1704
1705 /* Collect each track's info and return the one with earliest timestamp. */
1706 for (track_idx = 0; track_idx < tracks_num; track_idx++)
1707 {
1708 VC_CONTAINER_TRACK_MODULE_T *t_module = p_ctx->tracks[track_idx]->priv->module;
1709 VC_CONTAINER_PACKET_T *info = &t_module->info;
1710
1711 /* If this track has no data available, request more */
1712 if (!info->size)
1713 {
1714 /* This is a non-blocking read, so status will be ..._ABORTED if nothing available */
1715 status = vc_container_read(t_module->reader, info, VC_CONTAINER_READ_FLAG_INFO);
1716 /* Adjust track index to be the RTSP index instead of the RTP one */
1717 info->track = track_idx;
1718 }
1719
1720 if (status == VC_CONTAINER_SUCCESS)
1721 {
1722 if (info->dts < earliest_dts)
1723 {
1724 earliest_dts = info->dts;
1725 earliest_track = t_module;
1726 }
1727 }
1728 else if (status != VC_CONTAINER_ERROR_ABORTED)
1729 {
1730 /* Not a time-out failure, so abort */
1731 return status;
1732 }
1733 }
1734
1735 p_ctx->priv->module->current_track = earliest_track;
1736
1737 return VC_CONTAINER_SUCCESS;
1738}
1739
1740/*****************************************************************************
1741Functions exported as part of the Container Module API
1742 *****************************************************************************/
1743
1744/**************************************************************************//**
1745 * Read/skip data from the container.
1746 * Can also be used to query information about the next block of data.
1747 *
1748 * @param p_ctx The reader context.
1749 * @param p_packet The container packet information, or NULL.
1750 * @param flags The container read flags.
1751 * @return The resulting status of the function.
1752 */
1753static VC_CONTAINER_STATUS_T rtsp_reader_read( VC_CONTAINER_T *p_ctx,
1754 VC_CONTAINER_PACKET_T *p_packet,
1755 uint32_t flags )
1756{
1757 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1758 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
1759 VC_CONTAINER_TRACK_MODULE_T *current_track = module->current_track;
1760 VC_CONTAINER_PACKET_T *info;
1761
1762 if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK)
1763 {
1764 vc_container_assert(p_packet);
1765 vc_container_assert(p_packet->track < p_ctx->tracks_num);
1766 current_track = p_ctx->tracks[p_packet->track]->priv->module;
1767 module->current_track = current_track;
1768
1769 if (!current_track->info.size)
1770 {
1771 status = rtsp_blocking_track_read(current_track->reader, &current_track->info, VC_CONTAINER_READ_FLAG_INFO);
1772 if (status != VC_CONTAINER_SUCCESS)
1773 goto error;
1774 }
1775 }
1776 else if (!current_track || !current_track->info.size)
1777 {
1778 status = rtsp_update_track_info(p_ctx);
1779 if (status != VC_CONTAINER_SUCCESS)
1780 goto error;
1781
1782 while (!module->current_track)
1783 {
1784 /* Check RTSP stream to see if it has closed */
1785 status = rtsp_read_response(p_ctx);
1786 if (status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_ABORTED)
1787 {
1788 /* No data from any track yet, so keep checking */
1789 status = rtsp_update_track_info(p_ctx);
1790 }
1791 if (status != VC_CONTAINER_SUCCESS)
1792 goto error;
1793 }
1794
1795 current_track = module->current_track;
1796 }
1797
1798 info = &current_track->info;
1799 vc_container_assert(info->size);
1800
1801 if (flags & VC_CONTAINER_READ_FLAG_INFO)
1802 {
1803 vc_container_assert(p_packet);
1804 memcpy(p_packet, info, sizeof(*info));
1805 } else {
1806 status = rtsp_blocking_track_read(current_track->reader, p_packet, flags);
1807 if (status != VC_CONTAINER_SUCCESS)
1808 goto error;
1809
1810 if (p_packet)
1811 {
1812 p_packet->track = info->track;
1813
1814 if (flags & VC_CONTAINER_READ_FLAG_SKIP)
1815 {
1816 info->size = 0;
1817 } else {
1818 vc_container_assert(info->size >= p_packet->size);
1819 info->size -= p_packet->size;
1820 }
1821 } else {
1822 info->size = 0;
1823 }
1824 }
1825
1826 if (p_packet)
1827 {
1828 /* Adjust timestamps to be relative to zero */
1829 if (!module->ts_base)
1830 module->ts_base = p_packet->dts;
1831 p_packet->dts -= module->ts_base;
1832 p_packet->pts -= module->ts_base;
1833 }
1834
1835error:
1836 STREAM_STATUS(p_ctx) = status;
1837 return status;
1838}
1839
1840/**************************************************************************//**
1841 * Seek over data in the container.
1842 *
1843 * @param p_ctx The reader context.
1844 * @param p_offset The seek offset.
1845 * @param mode The seek mode.
1846 * @param flags The seek flags.
1847 * @return The resulting status of the function.
1848 */
1849static VC_CONTAINER_STATUS_T rtsp_reader_seek( VC_CONTAINER_T *p_ctx,
1850 int64_t *p_offset,
1851 VC_CONTAINER_SEEK_MODE_T mode,
1852 VC_CONTAINER_SEEK_FLAGS_T flags)
1853{
1854 VC_CONTAINER_PARAM_UNUSED(p_ctx);
1855 VC_CONTAINER_PARAM_UNUSED(p_offset);
1856 VC_CONTAINER_PARAM_UNUSED(mode);
1857 VC_CONTAINER_PARAM_UNUSED(flags);
1858
1859 /* RTSP is a non-seekable container */
1860 return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
1861}
1862
1863/**************************************************************************//**
1864 * Close the container.
1865 *
1866 * @param p_ctx The reader context.
1867 * @return The resulting status of the function.
1868 */
1869static VC_CONTAINER_STATUS_T rtsp_reader_close( VC_CONTAINER_T *p_ctx )
1870{
1871 VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
1872 unsigned int i;
1873
1874 for(i = 0; i < p_ctx->tracks_num; i++)
1875 {
1876 VC_CONTAINER_TRACK_MODULE_T *t_module = p_ctx->tracks[i]->priv->module;
1877
1878 if (t_module->control_uri && t_module->session_header)
1879 {
1880 /* Send the teardown message and wait for a response, although it
1881 * isn't important whether it was successful or not. */
1882 if (rtsp_send_teardown_request(p_ctx, t_module) == VC_CONTAINER_SUCCESS)
1883 (void)rtsp_read_response(p_ctx);
1884 }
1885
1886 if (t_module->reader)
1887 vc_container_close(t_module->reader);
1888 if (t_module->reader_uri)
1889 vc_uri_release(t_module->reader_uri);
1890 if (t_module->control_uri)
1891 free(t_module->control_uri);
1892 if (t_module->session_header)
1893 free(t_module->session_header);
1894 vc_container_free_track(p_ctx, p_ctx->tracks[i]); /* Also need to close track's reader */
1895 }
1896 p_ctx->tracks = NULL;
1897 p_ctx->tracks_num = 0;
1898 if (module)
1899 {
1900 if (module->comms_buffer)
1901 free(module->comms_buffer);
1902 if (module->header_list)
1903 vc_containers_list_destroy(module->header_list);
1904 free(module);
1905 }
1906 p_ctx->priv->module = 0;
1907 return VC_CONTAINER_SUCCESS;
1908}
1909
1910/**************************************************************************//**
1911 * Open the container.
1912 * Uses the I/O URI and/or data to configure the container.
1913 *
1914 * @param p_ctx The reader context.
1915 * @return The resulting status of the function.
1916 */
1917VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T *p_ctx )
1918{
1919 VC_CONTAINER_MODULE_T *module = 0;
1920 VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
1921 uint32_t ii;
1922
1923 /* Check the URI scheme looks valid */
1924 if (!vc_uri_scheme(p_ctx->priv->uri) ||
1925 (strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTSP_SCHEME) &&
1926 strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTSP_PKT_SCHEME)))
1927 return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
1928
1929 /* Allocate our context */
1930 if ((module = (VC_CONTAINER_MODULE_T *)malloc(sizeof(VC_CONTAINER_MODULE_T))) == NULL)
1931 {
1932 status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
1933 goto error;
1934 }
1935
1936 memset(module, 0, sizeof(*module));
1937 p_ctx->priv->module = module;
1938 p_ctx->tracks = module->tracks;
1939 module->next_rtp_port = FIRST_DYNAMIC_PORT;
1940 module->cseq_value = 0;
1941 module->uri_has_network_info =
1942 (strncasecmp(p_ctx->priv->io->uri, RTSP_NETWORK_URI_START, RTSP_NETWORK_URI_START_LENGTH) == 0);
1943 module->comms_buffer = (char *)calloc(1, COMMS_BUFFER_SIZE+1);
1944 if (!module->comms_buffer) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
1945
1946 /* header_list will contain pointers into the response_buffer, so take care in re-use */
1947 module->header_list = vc_containers_list_create(HEADER_LIST_INITIAL_CAPACITY, sizeof(RTSP_HEADER_T),
1948 (VC_CONTAINERS_LIST_COMPARATOR_T)rtsp_header_comparator);
1949 if (!module->header_list) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
1950
1951 status = rtsp_describe(p_ctx);
1952 for (ii = 0; status == VC_CONTAINER_SUCCESS && ii < p_ctx->tracks_num; ii++)
1953 status = rtsp_setup(p_ctx, p_ctx->tracks[ii]->priv->module);
1954 for (ii = 0; status == VC_CONTAINER_SUCCESS && ii < p_ctx->tracks_num; ii++)
1955 status = rtsp_play(p_ctx, p_ctx->tracks[ii]->priv->module);
1956 if (status != VC_CONTAINER_SUCCESS)
1957 goto error;
1958
1959 /* Set the RTSP stream to block briefly, to allow polling for closure as well as to avoid spinning CPU */
1960 vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS, DATA_UNAVAILABLE_READ_TIMEOUT_MS);
1961
1962 p_ctx->priv->pf_close = rtsp_reader_close;
1963 p_ctx->priv->pf_read = rtsp_reader_read;
1964 p_ctx->priv->pf_seek = rtsp_reader_seek;
1965
1966 if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) goto error;
1967 return VC_CONTAINER_SUCCESS;
1968
1969error:
1970 if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_EOS)
1971 status = VC_CONTAINER_ERROR_FORMAT_INVALID;
1972 LOG_DEBUG(p_ctx, "error opening RTSP stream (%i)", status);
1973 rtsp_reader_close(p_ctx);
1974 return status;
1975}
1976
1977/********************************************************************************
1978 Entrypoint function
1979 ********************************************************************************/
1980
1981#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
1982# pragma weak reader_open rtsp_reader_open
1983#endif
1984