forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1158 lines
44 KiB
1158 lines
44 KiB
/* |
|
Copyright (c) 2012, Broadcom Europe Ltd |
|
All rights reserved. |
|
|
|
Redistribution and use in source and binary forms, with or without |
|
modification, are permitted provided that the following conditions are met: |
|
* Redistributions of source code must retain the above copyright |
|
notice, this list of conditions and the following disclaimer. |
|
* Redistributions in binary form must reproduce the above copyright |
|
notice, this list of conditions and the following disclaimer in the |
|
documentation and/or other materials provided with the distribution. |
|
* Neither the name of the copyright holder nor the |
|
names of its contributors may be used to endorse or promote products |
|
derived from this software without specific prior written permission. |
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
|
|
#define CONTAINER_IS_BIG_ENDIAN |
|
//#define ENABLE_CONTAINERS_LOG_FORMAT |
|
//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE |
|
#define CONTAINER_HELPER_LOG_INDENT(a) 0 |
|
#include "containers/core/containers_private.h" |
|
#include "containers/core/containers_io_helpers.h" |
|
#include "containers/core/containers_utils.h" |
|
#include "containers/core/containers_uri.h" |
|
#include "containers/core/containers_logging.h" |
|
#include "containers/core/containers_bits.h" |
|
#include "containers/core/containers_list.h" |
|
|
|
#include "rtp_priv.h" |
|
#include "rtp_mpeg4.h" |
|
#include "rtp_h264.h" |
|
|
|
#ifdef _DEBUG |
|
/* Validates static sorted lists are correctly constructed */ |
|
#define RTP_DEBUG 1 |
|
#endif |
|
|
|
/****************************************************************************** |
|
Configurable defines and constants. |
|
******************************************************************************/ |
|
|
|
/** Maximum size of an RTP packet */ |
|
#define MAXIMUM_PACKET_SIZE 2048 |
|
|
|
/** Maximum number of RTP packets that can be missed without restarting. */ |
|
#define MAX_DROPOUT 3000 |
|
/** Maximum number of out of sequence RTP packets that are accepted. */ |
|
#define MAX_MISORDER 0 |
|
/** Minimum number of sequential packets required for an acceptable connection |
|
* when restarting. */ |
|
#define MIN_SEQUENTIAL 2 |
|
|
|
/****************************************************************************** |
|
Defines and constants. |
|
******************************************************************************/ |
|
|
|
#define RTP_SCHEME "rtp:" |
|
|
|
/** The RTP PKT scheme is used with test pkt files */ |
|
#define RTP_PKT_SCHEME "rtppkt:" |
|
|
|
/** \name RTP URI parameter names |
|
* @{ */ |
|
#define PAYLOAD_TYPE_NAME "rtppt" |
|
#define MIME_TYPE_NAME "mime-type" |
|
#define CHANNELS_NAME "channels" |
|
#define RATE_NAME "rate" |
|
#define SSRC_NAME "ssrc" |
|
#define SEQ_NAME "seq" |
|
/* @} */ |
|
|
|
/** A sentinel codec that is not supported */ |
|
#define UNSUPPORTED_CODEC VC_FOURCC(0,0,0,0) |
|
|
|
/** Evaluates to true if the given payload type is in the supported static audio range. */ |
|
#define IS_STATIC_AUDIO_TYPE(PT) ((PT) < countof(static_audio_payload_types)) |
|
|
|
/** Payload type number for the first static video type */ |
|
#define FIRST_STATIC_VIDEO_TYPE 24 |
|
/** Evaluates to true if the given payload type is in the supported static video range. */ |
|
#define IS_STATIC_VIDEO_TYPE(PT) ((PT) >= FIRST_STATIC_VIDEO_TYPE && \ |
|
(PT) < (FIRST_STATIC_VIDEO_TYPE + countof(static_video_payload_types))) |
|
|
|
/** Evaluates to true if the given payload type is in the dynamic range. */ |
|
#define IS_DYNAMIC_TYPE(PT) ((PT) >= 96 && (PT) < 128) |
|
|
|
/** All sequence numbers are modulo this value. */ |
|
#define RTP_SEQ_MOD (1 << 16) |
|
|
|
/** All the static video payload types use a 90kHz timestamp clock */ |
|
#define STATIC_VIDEO_TIMESTAMP_CLOCK 90000 |
|
|
|
/** Number of microseconds in a second, used to convert RTP timestamps to microseconds */ |
|
#define MICROSECONDS_PER_SECOND 1000000 |
|
|
|
/****************************************************************************** |
|
Type definitions |
|
******************************************************************************/ |
|
|
|
/** \name MIME type parameter handlers |
|
* Function prototypes for payload parameter handlers */ |
|
/* @{ */ |
|
static VC_CONTAINER_STATUS_T audio_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); |
|
static VC_CONTAINER_STATUS_T l8_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); |
|
static VC_CONTAINER_STATUS_T l16_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params); |
|
/* @} */ |
|
|
|
/** \name MIME type payload handlers */ |
|
/* @{ */ |
|
static VC_CONTAINER_STATUS_T l16_payload_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, |
|
VC_CONTAINER_PACKET_T *p_packet, uint32_t flags); |
|
/* @} */ |
|
|
|
/** Static audio payload type data */ |
|
typedef struct audio_payload_type_data_tag |
|
{ |
|
VC_CONTAINER_FOURCC_T codec; /**< FourCC codec for this payload type */ |
|
uint32_t channels; /**< Number of audio channels */ |
|
uint32_t sample_rate; /**< Sample rate */ |
|
uint32_t bits_per_sample; /**< Bits per sample, or 1 if not applicable */ |
|
PARAMETER_HANDLER_T param_handler; /**< Optional parameter handler */ |
|
PAYLOAD_HANDLER_T payload_handler; /**< Optional payload handler */ |
|
} AUDIO_PAYLOAD_TYPE_DATA_T; |
|
|
|
/** The details for the statically defined audio payload types from RFC3551 */ |
|
static AUDIO_PAYLOAD_TYPE_DATA_T static_audio_payload_types[] = |
|
{ |
|
{ VC_CONTAINER_CODEC_MULAW, 1, 8000, 8, audio_parameter_handler, NULL }, /* 0 - PCMU */ |
|
{ UNSUPPORTED_CODEC }, /* 1 - reserved */ |
|
{ UNSUPPORTED_CODEC }, /* 2 - reserved */ |
|
{ UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 3 - GSM */ |
|
{ UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 4 - G723 */ |
|
{ UNSUPPORTED_CODEC, 1, 8000, 4, NULL, NULL }, /* 5 - DVI4 */ |
|
{ UNSUPPORTED_CODEC, 1, 16000, 4, NULL, NULL }, /* 6 - DVI4 */ |
|
{ UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 7 - LPC */ |
|
{ VC_CONTAINER_CODEC_ALAW, 1, 8000, 8, audio_parameter_handler, NULL }, /* 8 - PCMA */ |
|
{ UNSUPPORTED_CODEC, 1, 8000, 8, NULL, NULL }, /* 9 - G722 */ |
|
{ VC_CONTAINER_CODEC_PCM_SIGNED, 2, 44100, 16, audio_parameter_handler, l16_payload_handler }, /* 10 - L16 */ |
|
{ VC_CONTAINER_CODEC_PCM_SIGNED, 1, 44100, 16, audio_parameter_handler, l16_payload_handler }, /* 11 - L16 */ |
|
{ VC_CONTAINER_CODEC_QCELP, 1, 8000, 16, NULL, NULL }, /* 12 - QCELP */ |
|
{ UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 13 - CN */ |
|
{ VC_CONTAINER_CODEC_MPGA, 1, 90000, 1, NULL, NULL }, /* 14 - MPA */ |
|
{ UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 15 - G728 */ |
|
{ UNSUPPORTED_CODEC, 1, 11025, 4, NULL, NULL }, /* 16 - DVI4 */ |
|
{ UNSUPPORTED_CODEC, 1, 22050, 4, NULL, NULL }, /* 17 - DVI4 */ |
|
{ UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 18 - G729 */ |
|
}; |
|
|
|
/** Static video payload type data */ |
|
typedef struct video_payload_type_data_tag |
|
{ |
|
VC_CONTAINER_FOURCC_T codec; /**< FourCC codec for this payload type */ |
|
PARAMETER_HANDLER_T param_handler; /**< Optional parameter handler */ |
|
PAYLOAD_HANDLER_T payload_handler; /**< Optional payload handler */ |
|
} VIDEO_PAYLOAD_TYPE_DATA_T; |
|
|
|
/** The details for the statically defined video payload types from RFC3551 */ |
|
static VIDEO_PAYLOAD_TYPE_DATA_T static_video_payload_types[] = |
|
{ |
|
{ UNSUPPORTED_CODEC }, /* 24 - unassigned */ |
|
{ UNSUPPORTED_CODEC }, /* 25 - CelB */ |
|
{ UNSUPPORTED_CODEC }, /* 26 - JPEG */ |
|
{ UNSUPPORTED_CODEC }, /* 27 - unassigned */ |
|
{ UNSUPPORTED_CODEC }, /* 28 - nv */ |
|
{ UNSUPPORTED_CODEC }, /* 29 - unassigned */ |
|
{ UNSUPPORTED_CODEC }, /* 30 - unassigned */ |
|
{ UNSUPPORTED_CODEC }, /* 31 - H261 */ |
|
{ VC_CONTAINER_CODEC_MP2V, NULL, NULL }, /* 32 - MPV */ |
|
{ UNSUPPORTED_CODEC }, /* 33 - MP2T */ |
|
{ VC_CONTAINER_CODEC_H263, NULL, NULL } /* 34 - H263 */ |
|
}; |
|
|
|
/** MIME type details */ |
|
typedef struct mime_type_data_tag |
|
{ |
|
const char *name; /**< Name of MIME type */ |
|
VC_CONTAINER_ES_TYPE_T es_type; /**< Elementary stream type */ |
|
VC_CONTAINER_FOURCC_T codec; /**< Codec to be used */ |
|
PARAMETER_HANDLER_T param_handler; /**< Parameter handler for this MIME type */ |
|
} MIME_TYPE_DATA_T; |
|
|
|
/** Comparator for MIME type details. */ |
|
static int mime_type_data_comparator(const MIME_TYPE_DATA_T *a, const MIME_TYPE_DATA_T *b); |
|
|
|
/** Dynamic audio payload details |
|
* Note: case-insensitive sort by name */ |
|
static MIME_TYPE_DATA_T dynamic_mime_details[] = { |
|
{ "audio/l16", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_PCM_SIGNED, l16_parameter_handler }, |
|
{ "audio/l8", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_PCM_SIGNED, l8_parameter_handler }, |
|
{ "audio/mpeg4-generic", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MP4A, mp4_parameter_handler }, |
|
{ "video/h264", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264, h264_parameter_handler }, |
|
{ "video/mpeg4-generic", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V, mp4_parameter_handler }, |
|
}; |
|
|
|
/** Sorted list of dynamic MIME type details */ |
|
VC_CONTAINERS_STATIC_LIST(dynamic_mime, dynamic_mime_details, mime_type_data_comparator); |
|
|
|
/** RTP reader data. */ |
|
typedef struct VC_CONTAINER_MODULE_T |
|
{ |
|
VC_CONTAINER_TRACK_T *track; |
|
} VC_CONTAINER_MODULE_T; |
|
|
|
/****************************************************************************** |
|
Function prototypes |
|
******************************************************************************/ |
|
VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T * ); |
|
|
|
/****************************************************************************** |
|
Local Functions |
|
******************************************************************************/ |
|
|
|
/**************************************************************************//** |
|
* Parameter comparison function. |
|
* Compare two parameter structures and return whether the first is less than, |
|
* equal to or greater than the second. |
|
* |
|
* @param first The first structure to be compared. |
|
* @param second The second structure to be compared. |
|
* @return Negative if first is less than second, positive if first is greater |
|
* and zero if they are equal. |
|
*/ |
|
static int parameter_comparator(const PARAMETER_T *first, const PARAMETER_T *second) |
|
{ |
|
return strcasecmp(first->name, second->name); |
|
} |
|
|
|
/**************************************************************************//** |
|
* Creates and populates a parameter list from a URI structure. |
|
* The list does not copy the parameter strings themselves, so the URI structure |
|
* must be retained (and its parameters unmodified) while the list is in use. |
|
* |
|
* @param uri The URI containing the parameters. |
|
* @return List created from the parameters of the URI, or NULL on error. |
|
*/ |
|
static VC_CONTAINERS_LIST_T *fill_parameter_list(VC_URI_PARTS_T *uri) |
|
{ |
|
uint32_t num_parameters = vc_uri_num_queries(uri); |
|
VC_CONTAINERS_LIST_T *parameters; |
|
uint32_t ii; |
|
|
|
parameters = vc_containers_list_create(num_parameters, sizeof(PARAMETER_T), (VC_CONTAINERS_LIST_COMPARATOR_T)parameter_comparator); |
|
if (!parameters) |
|
return NULL; |
|
|
|
for (ii = 0; ii < num_parameters; ii++) |
|
{ |
|
PARAMETER_T param; |
|
|
|
vc_uri_query(uri, ii, ¶m.name, ¶m.value); |
|
if (!vc_containers_list_insert(parameters, ¶m, false)) |
|
{ |
|
vc_containers_list_destroy(parameters); |
|
return NULL; |
|
} |
|
} |
|
|
|
#ifdef RTP_DEBUG |
|
vc_containers_list_validate(parameters); |
|
#endif |
|
|
|
return parameters; |
|
} |
|
|
|
/**************************************************************************//** |
|
* Decodes a static audio payload type into track information. |
|
* The static parameters may be overridden by URI parameters. |
|
* |
|
* @param p_ctx The reader context. |
|
* @param track The track to be populated. |
|
* @param param_list The URI parameter list. |
|
* @param payload_type The static payload type. |
|
* @return The resulting status of the function. |
|
*/ |
|
static VC_CONTAINER_STATUS_T decode_static_audio_type(VC_CONTAINER_T *p_ctx, |
|
VC_CONTAINER_TRACK_T *track, |
|
const VC_CONTAINERS_LIST_T *param_list, |
|
uint32_t payload_type) |
|
{ |
|
VC_CONTAINER_ES_FORMAT_T *format = track->format; |
|
AUDIO_PAYLOAD_TYPE_DATA_T *data = &static_audio_payload_types[payload_type]; |
|
|
|
VC_CONTAINER_PARAM_UNUSED(p_ctx); |
|
VC_CONTAINER_PARAM_UNUSED(param_list); |
|
|
|
vc_container_assert(payload_type < countof(static_audio_payload_types)); |
|
|
|
if (data->codec == UNSUPPORTED_CODEC) |
|
return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
|
|
|
format->es_type = VC_CONTAINER_ES_TYPE_AUDIO; |
|
format->codec = data->codec; |
|
format->type->audio.channels = data->channels; |
|
format->type->audio.sample_rate = data->sample_rate; |
|
format->type->audio.bits_per_sample = data->bits_per_sample; |
|
format->type->audio.block_align = data->channels * BITS_TO_BYTES(data->bits_per_sample); |
|
track->priv->module->timestamp_clock = format->type->audio.sample_rate; |
|
track->priv->module->payload_handler = data->payload_handler; |
|
|
|
if (data->param_handler) |
|
data->param_handler(p_ctx, track, param_list); |
|
|
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
/**************************************************************************//** |
|
* Decodes a static video payload type into track information. |
|
* The static parameters may be overridden by URI parameters. |
|
* |
|
* @param p_ctx The reader context. |
|
* @param track The track to be populated. |
|
* @param param_list The URI parameter list. |
|
* @param payload_type The static payload type. |
|
* @return The resulting status of the function. |
|
*/ |
|
static VC_CONTAINER_STATUS_T decode_static_video_type(VC_CONTAINER_T *p_ctx, |
|
VC_CONTAINER_TRACK_T *track, |
|
const VC_CONTAINERS_LIST_T *param_list, |
|
uint32_t payload_type) |
|
{ |
|
VC_CONTAINER_ES_FORMAT_T *format = track->format; |
|
VIDEO_PAYLOAD_TYPE_DATA_T *data = &static_video_payload_types[payload_type - FIRST_STATIC_VIDEO_TYPE]; |
|
|
|
VC_CONTAINER_PARAM_UNUSED(p_ctx); |
|
VC_CONTAINER_PARAM_UNUSED(param_list); |
|
|
|
vc_container_assert(payload_type >= FIRST_STATIC_VIDEO_TYPE); |
|
vc_container_assert(payload_type < FIRST_STATIC_VIDEO_TYPE + countof(static_video_payload_types)); |
|
|
|
if (data->codec == UNSUPPORTED_CODEC) |
|
return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
|
|
|
format->es_type = VC_CONTAINER_ES_TYPE_VIDEO; |
|
format->codec = data->codec; |
|
track->priv->module->timestamp_clock = STATIC_VIDEO_TIMESTAMP_CLOCK; |
|
track->priv->module->payload_handler = data->payload_handler; |
|
|
|
if (data->param_handler) |
|
data->param_handler(p_ctx, track, param_list); |
|
|
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
/**************************************************************************//** |
|
* Compare two MIME type structures and return whether the first is less than, |
|
* equal to or greater than the second. |
|
* |
|
* @param a The first parameter structure to be compared. |
|
* @param b The second parameter structure to be compared. |
|
* @return Negative if a is less than b, positive if a is greater and zero if |
|
* they are equal. |
|
*/ |
|
static int mime_type_data_comparator(const MIME_TYPE_DATA_T *a, const MIME_TYPE_DATA_T *b) |
|
{ |
|
return strcasecmp(a->name, b->name); |
|
} |
|
|
|
/**************************************************************************//** |
|
* Generic audio parameter handler. |
|
* Updates the track information with generic audio parameters, such as "rate" |
|
* and "channels". |
|
* |
|
* @param p_ctx The reader context. |
|
* @param track The track to be updated. |
|
* @param params The URI parameter list. |
|
* @return The resulting status of the function. |
|
*/ |
|
static VC_CONTAINER_STATUS_T audio_parameter_handler(VC_CONTAINER_T *p_ctx, |
|
VC_CONTAINER_TRACK_T *track, |
|
const VC_CONTAINERS_LIST_T *params) |
|
{ |
|
VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; |
|
|
|
VC_CONTAINER_PARAM_UNUSED(p_ctx); |
|
|
|
/* See RFC3555. Generic audio parameters that can override static payload |
|
* type defaults. */ |
|
if (rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate)) |
|
track->priv->module->timestamp_clock = audio->sample_rate; |
|
if (rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels)) |
|
audio->block_align = audio->channels * BITS_TO_BYTES(audio->bits_per_sample); |
|
|
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
/**************************************************************************//** |
|
* L8 specific audio parameter handler. |
|
* Updates the track information with audio parameters needed by the audio/L8 |
|
* MIME type. For example, the "rate" parameter is mandatory. |
|
* |
|
* @param p_ctx The reader context. |
|
* @param track The track to be updated. |
|
* @param params The URI parameter list. |
|
* @return The resulting status of the function. |
|
*/ |
|
static VC_CONTAINER_STATUS_T l8_parameter_handler(VC_CONTAINER_T *p_ctx, |
|
VC_CONTAINER_TRACK_T *track, |
|
const VC_CONTAINERS_LIST_T *params) |
|
{ |
|
VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; |
|
|
|
VC_CONTAINER_PARAM_UNUSED(p_ctx); |
|
|
|
/* See RFC3555, section 4.1.14, for parameter names and details. */ |
|
if (!rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate)) |
|
return VC_CONTAINER_ERROR_FORMAT_INVALID; |
|
if (!rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels)) |
|
audio->channels = 1; |
|
audio->bits_per_sample = 8; |
|
audio->block_align = audio->channels; |
|
track->priv->module->timestamp_clock = audio->sample_rate; |
|
|
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
/**************************************************************************//** |
|
* L16 specific audio parameter handler. |
|
* Updates the track information with audio parameters needed by the audio/L16 |
|
* MIME type. For example, the "rate" parameter is mandatory. |
|
* |
|
* @param p_ctx The reader context. |
|
* @param track The track to be updated. |
|
* @param params The URI parameter list. |
|
* @return The resulting status of the function. |
|
*/ |
|
static VC_CONTAINER_STATUS_T l16_parameter_handler(VC_CONTAINER_T *p_ctx, |
|
VC_CONTAINER_TRACK_T *track, |
|
const VC_CONTAINERS_LIST_T *params) |
|
{ |
|
VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio; |
|
|
|
VC_CONTAINER_PARAM_UNUSED(p_ctx); |
|
|
|
/* See RFC3555, section 4.1.15, for parameter names and details. */ |
|
if (!rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate)) |
|
return VC_CONTAINER_ERROR_FORMAT_INVALID; |
|
if (!rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels)) |
|
audio->channels = 1; |
|
audio->bits_per_sample = 16; |
|
audio->block_align = audio->channels * 2; |
|
track->priv->module->timestamp_clock = audio->sample_rate; |
|
track->priv->module->payload_handler = l16_payload_handler; |
|
|
|
/* TODO: add support for "channel-order" to set channel mapping */ |
|
|
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
/**************************************************************************//** |
|
* Decode a dynamic payload type from parameters. |
|
* Populates the track information with data for supported dynamic media types. |
|
* |
|
* @param p_ctx The reader context. |
|
* @param track The track to be updated. |
|
* @param param_list The URI parameter list. |
|
* @return The resulting status of the function. |
|
*/ |
|
static VC_CONTAINER_STATUS_T decode_dynamic_type(VC_CONTAINER_T *p_ctx, |
|
VC_CONTAINER_TRACK_T *track, |
|
const VC_CONTAINERS_LIST_T *param_list) |
|
{ |
|
VC_CONTAINER_ES_FORMAT_T *format = track->format; |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
PARAMETER_T mime_type; |
|
MIME_TYPE_DATA_T mime_details; |
|
|
|
/* Get MIME type parameter */ |
|
mime_type.name = MIME_TYPE_NAME; |
|
if (!vc_containers_list_find_entry(param_list, &mime_type)) |
|
return VC_CONTAINER_ERROR_FORMAT_INVALID; |
|
|
|
#ifdef RTP_DEBUG |
|
vc_containers_list_validate(&dynamic_mime); |
|
#endif |
|
|
|
/* Look up MIME type to see if it can be handled */ |
|
mime_details.name = mime_type.value; |
|
if (!vc_containers_list_find_entry(&dynamic_mime, &mime_details)) |
|
return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
|
|
|
format->codec = mime_details.codec; |
|
format->es_type = mime_details.es_type; |
|
|
|
/* Default number of channels for audio is one */ |
|
if (mime_details.es_type == VC_CONTAINER_ES_TYPE_AUDIO) |
|
format->type->audio.channels = 1; |
|
|
|
/* Lete MIME type specific parameter handler deal with any other parameters */ |
|
status = mime_details.param_handler(p_ctx, track, param_list); |
|
|
|
/* Ensure that the sample rate has been set for audio formats */ |
|
if (mime_details.es_type == VC_CONTAINER_ES_TYPE_AUDIO && !format->type->audio.sample_rate) |
|
return VC_CONTAINER_ERROR_FORMAT_INVALID; |
|
|
|
return status; |
|
} |
|
|
|
/**************************************************************************//** |
|
* Decode the RTP payload type. |
|
* Populates track information with data from static tables and the URI |
|
* parameter list, according to the payload and MIME types. |
|
* |
|
* @param p_ctx The reader context. |
|
* @param track The track to be updated. |
|
* @param params The URI parameter list. |
|
* @return The resulting status of the function. |
|
*/ |
|
static VC_CONTAINER_STATUS_T decode_payload_type(VC_CONTAINER_T *p_ctx, |
|
VC_CONTAINER_TRACK_T *track, |
|
const VC_CONTAINERS_LIST_T *param_list, |
|
uint32_t payload_type) |
|
{ |
|
VC_CONTAINER_TRACK_MODULE_T *module = track->priv->module; |
|
VC_CONTAINER_STATUS_T status; |
|
|
|
if (IS_STATIC_AUDIO_TYPE(payload_type)) |
|
status = decode_static_audio_type(p_ctx, track, param_list, payload_type); |
|
else if (IS_STATIC_VIDEO_TYPE(payload_type)) |
|
status = decode_static_video_type(p_ctx, track, param_list, payload_type); |
|
else if (IS_DYNAMIC_TYPE(payload_type)) |
|
status = decode_dynamic_type(p_ctx, track, param_list); |
|
else |
|
status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
|
|
|
module->payload_type = (uint8_t)payload_type; |
|
|
|
return status; |
|
} |
|
|
|
/**************************************************************************//** |
|
* Initialises the RTP sequence number algorithm with a new sequence number. |
|
* |
|
* @param t_module The track module. |
|
* @param seq The new sequence number. |
|
*/ |
|
static void init_sequence_number(VC_CONTAINER_TRACK_MODULE_T *t_module, |
|
uint16_t seq) |
|
{ |
|
t_module->base_seq = seq; |
|
t_module->max_seq_num = seq; |
|
t_module->bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */ |
|
t_module->received = 0; |
|
} |
|
|
|
/**************************************************************************//** |
|
* Checks whether the sequence number for a packet is acceptable or not. |
|
* The packet will be unacceptable if it is out of sequence by some degree, or |
|
* if the packet sequence is still being established. |
|
* |
|
* @param t_module The track module. |
|
* @param seq The new sequence number. |
|
* @return True if the sequence number indicates the packet is acceptable |
|
*/ |
|
static bool update_sequence_number(VC_CONTAINER_TRACK_MODULE_T *t_module, |
|
uint16_t seq) |
|
{ |
|
uint16_t udelta = seq - t_module->max_seq_num; |
|
|
|
/* NOTE: This source is derived from the example code in RFC3550, section A.1 */ |
|
|
|
/* Source is not valid until MIN_SEQUENTIAL packets with |
|
* sequential sequence numbers have been received. */ |
|
if (t_module->probation) |
|
{ |
|
/* packet is in sequence */ |
|
if (seq == t_module->max_seq_num + 1) |
|
{ |
|
t_module->probation--; |
|
t_module->max_seq_num = seq; |
|
LOG_INFO(0, "RTP: Probation, %u more packet(s) to go at 0x%4.4hx", t_module->probation, seq); |
|
|
|
if (!t_module->probation) |
|
{ |
|
init_sequence_number(t_module, seq); |
|
t_module->received++; |
|
return 1; |
|
} |
|
} else { |
|
t_module->probation = MIN_SEQUENTIAL - 1; |
|
t_module->max_seq_num = seq; |
|
LOG_INFO(0, "RTP: Probation reset, wait for %u packet(s) at 0x%4.4hx", t_module->probation, seq); |
|
} |
|
return 0; |
|
} else if (udelta < MAX_DROPOUT) |
|
{ |
|
if (!udelta) |
|
{ |
|
/* Duplicate packet, drop it */ |
|
LOG_INFO(0, "RTP: Drop duplicate packet at 0x%4.4hx", seq); |
|
return 0; |
|
} |
|
if (udelta > 1) |
|
{ |
|
LOG_INFO(0, "RTP: Jumped by %hu packets to 0x%4.4hx", udelta, seq); |
|
} |
|
/* in order, with permissible gap */ |
|
t_module->max_seq_num = seq; |
|
} else |
|
#if (MAX_MISORDER != 0) |
|
/* When MAX_MISORDER is zero, always treat as out of order */ |
|
if (udelta <= RTP_SEQ_MOD - MAX_MISORDER) |
|
#endif |
|
{ |
|
/* the sequence number made a very large jump */ |
|
if (seq == t_module->bad_seq) |
|
{ |
|
LOG_INFO(0, "RTP: Misorder restart at 0x%4.4hx", seq); |
|
/* Two sequential packets -- assume that the other side |
|
* restarted without telling us so just re-sync |
|
* (i.e., pretend this was the first packet). */ |
|
init_sequence_number(t_module, seq); |
|
} else { |
|
LOG_INFO(0, "RTP: Misorder at 0x%4.4hx, expected 0x%4.4hx", seq, t_module->max_seq_num); |
|
t_module->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1); |
|
return 0; |
|
} |
|
} |
|
#if (MAX_MISORDER != 0) |
|
else { |
|
/* duplicate or reordered packet */ |
|
|
|
/* TODO: handle out of order packets */ |
|
} |
|
#endif |
|
t_module->received++; |
|
return 1; |
|
} |
|
|
|
/**************************************************************************//** |
|
* Extract the fields of an RTP packet and validate it. |
|
* |
|
* @param p_ctx The reader context. |
|
* @param t_module The track module. |
|
* @return True if successful, false if there were not enough bits in the |
|
* packet or the packet was invalid. |
|
*/ |
|
static void decode_rtp_packet_header(VC_CONTAINER_T *p_ctx, |
|
VC_CONTAINER_TRACK_MODULE_T *t_module) |
|
{ |
|
VC_CONTAINER_BITS_T *payload = &t_module->payload; |
|
uint32_t version, has_padding, has_extension, csrc_count, has_marker; |
|
uint32_t payload_type, ssrc; |
|
uint16_t seq_num; |
|
|
|
/* Break down fixed header area into component parts */ |
|
version = BITS_READ_U32(p_ctx, payload, 2, "Version"); |
|
has_padding = BITS_READ_U32(p_ctx, payload, 1, "Has padding"); |
|
has_extension = BITS_READ_U32(p_ctx, payload, 1, "Has extension"); |
|
csrc_count = BITS_READ_U32(p_ctx, payload, 4, "CSRC count"); |
|
has_marker = BITS_READ_U32(p_ctx, payload, 1, "Has marker"); |
|
payload_type = BITS_READ_U32(p_ctx, payload, 7, "Payload type"); |
|
seq_num = BITS_READ_U16(p_ctx, payload, 16, "Sequence number"); |
|
t_module->timestamp = BITS_READ_U32(p_ctx, payload, 32, "Timestamp"); |
|
ssrc = BITS_READ_U32(p_ctx, payload, 32, "SSRC"); |
|
|
|
/* If there was only a partial header, abort immediately */ |
|
if (!BITS_VALID(p_ctx, payload)) |
|
return; |
|
|
|
/* Validate version, payload type, sequence number and SSRC, if set */ |
|
if (version != 2 || payload_type != t_module->payload_type) |
|
{ |
|
BITS_INVALIDATE(p_ctx, payload); |
|
return; |
|
} |
|
if (BIT_IS_SET(t_module->flags, TRACK_SSRC_SET) && (ssrc != t_module->expected_ssrc)) |
|
{ |
|
LOG_DEBUG(p_ctx, "RTP: Unexpected SSRC (0x%8.8X)", ssrc); |
|
BITS_INVALIDATE(p_ctx, payload); |
|
return; |
|
} |
|
|
|
/* Check sequence number indicates packet is usable */ |
|
if (!update_sequence_number(t_module, seq_num)) |
|
{ |
|
BITS_INVALIDATE(p_ctx, payload); |
|
return; |
|
} |
|
|
|
/* Adjust to account for padding, CSRCs and extension */ |
|
if (has_padding) |
|
{ |
|
VC_CONTAINER_BITS_T bit_stream; |
|
uint32_t available = BITS_BYTES_AVAILABLE(p_ctx, payload); |
|
uint8_t padding; |
|
|
|
BITS_COPY_STREAM(p_ctx, &bit_stream, payload); |
|
/* The last byte of the payload is the number of padding bytes, including itself */ |
|
BITS_SKIP_BYTES(p_ctx, &bit_stream, available - 1, "Skip to padding length"); |
|
padding = BITS_READ_U8(p_ctx, &bit_stream, 8, "Padding length"); |
|
|
|
BITS_REDUCE_BYTES(p_ctx, payload, padding, "Remove padding"); |
|
} |
|
|
|
/* Each CSRC is 32 bits, so shift count up to skip the right number of bits */ |
|
BITS_SKIP(p_ctx, payload, csrc_count << 5, "CSRC section"); |
|
|
|
if (has_extension) |
|
{ |
|
uint32_t extension_bits; |
|
|
|
/* Extension header is 16-bit ID (which isn't needed), then 16-bit length in 32-bit words */ |
|
BITS_SKIP(p_ctx, payload, 16, "Extension ID"); |
|
extension_bits = BITS_READ_U32(p_ctx, payload, 16, "Extension length") << 5; |
|
BITS_SKIP(p_ctx, payload, extension_bits, "Extension data"); |
|
} |
|
|
|
/* Record whether or not this RTP packet had the marker bit set */ |
|
if (has_marker) |
|
SET_BIT(t_module->flags, TRACK_HAS_MARKER); |
|
else |
|
CLEAR_BIT(t_module->flags, TRACK_HAS_MARKER); |
|
|
|
/* If it hasn't been set independently, use the first timestamp as a baseline */ |
|
if (!t_module->timestamp_base) |
|
t_module->timestamp_base = t_module->timestamp; |
|
t_module->timestamp -= t_module->timestamp_base; |
|
} |
|
|
|
/**************************************************************************//** |
|
* Generic payload handler. |
|
* Copies/skips data verbatim from the packet payload. |
|
* |
|
* @param p_ctx The reader context. |
|
* @param track The track being read. |
|
* @param p_packet The container packet information, or NULL. |
|
* @param flags The container read flags. |
|
* @return The resulting status of the function. |
|
*/ |
|
static VC_CONTAINER_STATUS_T generic_payload_handler(VC_CONTAINER_T *p_ctx, |
|
VC_CONTAINER_TRACK_T *track, |
|
VC_CONTAINER_PACKET_T *p_packet, |
|
uint32_t flags) |
|
{ |
|
VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module; |
|
VC_CONTAINER_BITS_T *payload = &t_module->payload; |
|
uint32_t size; |
|
|
|
VC_CONTAINER_PARAM_UNUSED(p_ctx); |
|
|
|
if (!p_packet) |
|
{ |
|
/* Skip the rest of this RTP packet */ |
|
BITS_INVALIDATE(p_ctx, payload); |
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
/* Copy as much as possible into the client packet buffer */ |
|
size = BITS_BYTES_AVAILABLE(p_ctx, payload); |
|
|
|
if (flags & VC_CONTAINER_READ_FLAG_SKIP) |
|
BITS_SKIP_BYTES(p_ctx, payload, size, "Packet data"); |
|
else { |
|
if (!(flags & VC_CONTAINER_READ_FLAG_INFO)) |
|
{ |
|
if (size > p_packet->buffer_size) |
|
size = p_packet->buffer_size; |
|
|
|
BITS_COPY_BYTES(p_ctx, payload, size, p_packet->data, "Packet data"); |
|
} |
|
p_packet->size = size; |
|
} |
|
|
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
/**************************************************************************//** |
|
* L16 payload handler. |
|
* Copies/skips data from the packet payload. On copy, swaps consecutive bytes |
|
* in the data in order to get expected byte order. |
|
* |
|
* @param p_ctx The reader context. |
|
* @param track The track being read. |
|
* @param p_packet The container packet information, or NULL. |
|
* @param flags The container read flags. |
|
* @return The resulting status of the function. |
|
*/ |
|
static VC_CONTAINER_STATUS_T l16_payload_handler(VC_CONTAINER_T *p_ctx, |
|
VC_CONTAINER_TRACK_T *track, |
|
VC_CONTAINER_PACKET_T *p_packet, |
|
uint32_t flags) |
|
{ |
|
VC_CONTAINER_STATUS_T status; |
|
|
|
/* Most aspects are handled adequately by the generic handler */ |
|
status = generic_payload_handler(p_ctx, track, p_packet, flags); |
|
if (status != VC_CONTAINER_SUCCESS) |
|
return status; |
|
|
|
if (p_packet && !(flags & (VC_CONTAINER_READ_FLAG_SKIP | VC_CONTAINER_READ_FLAG_INFO))) |
|
{ |
|
uint8_t *ptr, *end_ptr; |
|
|
|
/* Ensure packet size is even */ |
|
p_packet->size &= ~1; |
|
|
|
/* Swap bytes of each sample, to get host order instead of network order */ |
|
for (ptr = p_packet->data, end_ptr = ptr + p_packet->size; ptr < end_ptr; ptr += 2) |
|
{ |
|
uint8_t high_byte = ptr[0]; |
|
ptr[0] = ptr[1]; |
|
ptr[1] = high_byte; |
|
} |
|
} |
|
|
|
return status; |
|
} |
|
|
|
|
|
/***************************************************************************** |
|
Utility functions for use by RTP payload handlers |
|
*****************************************************************************/ |
|
|
|
/**************************************************************************//** |
|
* Gets the value of a parameter as an unsigned 32-bit decimal integer. |
|
* |
|
* @param param_list The URI parameter list. |
|
* @param name The name of the parameter. |
|
* @param value Pointer to the variable to receive the value. |
|
* @return True if the parameter value was read and stored correctly, false |
|
* otherwise. |
|
*/ |
|
bool rtp_get_parameter_u32(const VC_CONTAINERS_LIST_T *param_list, |
|
const char *name, |
|
uint32_t *value) |
|
{ |
|
PARAMETER_T param; |
|
|
|
param.name = name; |
|
if (vc_containers_list_find_entry(param_list, ¶m) && param.value) |
|
{ |
|
char *end; |
|
|
|
*value = strtoul(param.value, &end, 10); |
|
return (end != param.value) && (*end == '\0'); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
/**************************************************************************//** |
|
* Gets the value of a parameter as an unsigned 32-bit hexadecimal integer. |
|
* |
|
* @param param_list The URI parameter list. |
|
* @param name The name of the parameter. |
|
* @param value Pointer to the variable to receive the value. |
|
* @return True if the parameter value was read and stored correctly, false |
|
* otherwise. |
|
*/ |
|
bool rtp_get_parameter_x32(const VC_CONTAINERS_LIST_T *param_list, |
|
const char *name, |
|
uint32_t *value) |
|
{ |
|
PARAMETER_T param; |
|
|
|
param.name = name; |
|
if (vc_containers_list_find_entry(param_list, ¶m) && param.value) |
|
{ |
|
char *end; |
|
|
|
*value = strtoul(param.value, &end, 16); |
|
return (end != param.value) && (*end == '\0'); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
/***************************************************************************** |
|
Functions exported as part of the Container Module API |
|
*****************************************************************************/ |
|
|
|
/**************************************************************************//** |
|
* Read/skip data from the container. |
|
* Can also be used to query information about the next block of data. |
|
* |
|
* @param p_ctx The reader context. |
|
* @param p_packet The container packet information, or NULL. |
|
* @param flags The container read flags. |
|
* @return The resulting status of the function. |
|
*/ |
|
static VC_CONTAINER_STATUS_T rtp_reader_read( VC_CONTAINER_T *p_ctx, |
|
VC_CONTAINER_PACKET_T *p_packet, |
|
uint32_t flags ) |
|
{ |
|
VC_CONTAINER_TRACK_T *track; |
|
VC_CONTAINER_TRACK_MODULE_T *t_module; |
|
VC_CONTAINER_STATUS_T status; |
|
|
|
if((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) && p_packet->track) |
|
return VC_CONTAINER_ERROR_INVALID_ARGUMENT; |
|
|
|
track = p_ctx->tracks[0]; |
|
t_module = track->priv->module; |
|
|
|
CLEAR_BIT(t_module->flags, TRACK_NEW_PACKET); |
|
|
|
while (!BITS_AVAILABLE(p_ctx, &t_module->payload)) |
|
{ |
|
uint32_t bytes_read; |
|
|
|
/* No data left from last RTP packet, get another one */ |
|
bytes_read = READ_BYTES(p_ctx, t_module->buffer, MAXIMUM_PACKET_SIZE); |
|
if (!bytes_read) |
|
return STREAM_STATUS(p_ctx); |
|
|
|
BITS_INIT(p_ctx, &t_module->payload, t_module->buffer, bytes_read); |
|
|
|
decode_rtp_packet_header(p_ctx, t_module); |
|
SET_BIT(t_module->flags, TRACK_NEW_PACKET); |
|
} |
|
|
|
if (p_packet) |
|
{ |
|
uint32_t timestamp_top = t_module->timestamp >> 30; |
|
|
|
/* Determine whether timestamp has wrapped forwards or backwards around zero */ |
|
if ((timestamp_top == 0) && (t_module->last_timestamp_top == 3)) |
|
t_module->timestamp_wraps++; |
|
else if ((timestamp_top == 3) && (t_module->last_timestamp_top == 0)) |
|
t_module->timestamp_wraps--; |
|
t_module->last_timestamp_top = timestamp_top; |
|
|
|
p_packet->dts = p_packet->pts = ((int64_t)t_module->timestamp_wraps << 32) | t_module->timestamp; |
|
p_packet->track = 0; |
|
p_packet->flags = 0; |
|
} |
|
|
|
status = t_module->payload_handler(p_ctx, track, p_packet, flags); |
|
if (p_packet && status == VC_CONTAINER_SUCCESS) |
|
{ |
|
/* Adjust timestamps from RTP clock rate to microseconds */ |
|
p_packet->pts = p_packet->pts * MICROSECONDS_PER_SECOND / t_module->timestamp_clock; |
|
p_packet->dts = p_packet->dts * MICROSECONDS_PER_SECOND / t_module->timestamp_clock; |
|
} |
|
|
|
STREAM_STATUS(p_ctx) = status; |
|
return status; |
|
} |
|
|
|
/**************************************************************************//** |
|
* Seek over data in the container. |
|
* |
|
* @param p_ctx The reader context. |
|
* @param p_offset The seek offset. |
|
* @param mode The seek mode. |
|
* @param flags The seek flags. |
|
* @return The resulting status of the function. |
|
*/ |
|
static VC_CONTAINER_STATUS_T rtp_reader_seek( VC_CONTAINER_T *p_ctx, |
|
int64_t *p_offset, |
|
VC_CONTAINER_SEEK_MODE_T mode, |
|
VC_CONTAINER_SEEK_FLAGS_T flags) |
|
{ |
|
VC_CONTAINER_PARAM_UNUSED(p_ctx); |
|
VC_CONTAINER_PARAM_UNUSED(p_offset); |
|
VC_CONTAINER_PARAM_UNUSED(mode); |
|
VC_CONTAINER_PARAM_UNUSED(flags); |
|
|
|
/* RTP is a non-seekable container */ |
|
return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; |
|
} |
|
|
|
/**************************************************************************//** |
|
* Apply a control operation to the container. |
|
* |
|
* @param p_ctx The reader context. |
|
* @param operation The control operation. |
|
* @param args Optional additional arguments for the operation. |
|
* @return The resulting status of the function. |
|
*/ |
|
static VC_CONTAINER_STATUS_T rtp_reader_control( VC_CONTAINER_T *p_ctx, |
|
VC_CONTAINER_CONTROL_T operation, |
|
va_list args) |
|
{ |
|
VC_CONTAINER_STATUS_T status; |
|
VC_CONTAINER_TRACK_MODULE_T *t_module = p_ctx->tracks[0]->priv->module; |
|
|
|
switch (operation) |
|
{ |
|
case VC_CONTAINER_CONTROL_SET_TIMESTAMP_BASE: |
|
{ |
|
t_module->timestamp_base = va_arg(args, uint32_t); |
|
if (!t_module->timestamp_base) |
|
t_module->timestamp_base = 1; /* Zero is used to mean "not set" */ |
|
status = VC_CONTAINER_SUCCESS; |
|
} |
|
break; |
|
case VC_CONTAINER_CONTROL_SET_NEXT_SEQUENCE_NUMBER: |
|
{ |
|
init_sequence_number(t_module, (uint16_t)va_arg(args, uint32_t)); |
|
t_module->probation = 0; |
|
status = VC_CONTAINER_SUCCESS; |
|
} |
|
break; |
|
case VC_CONTAINER_CONTROL_SET_SOURCE_ID: |
|
{ |
|
t_module->expected_ssrc = va_arg(args, uint32_t); |
|
SET_BIT(t_module->flags, TRACK_SSRC_SET); |
|
status = VC_CONTAINER_SUCCESS; |
|
} |
|
break; |
|
default: |
|
status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; |
|
} |
|
|
|
return status; |
|
} |
|
|
|
/**************************************************************************//** |
|
* Close the container. |
|
* |
|
* @param p_ctx The reader context. |
|
* @return The resulting status of the function. |
|
*/ |
|
static VC_CONTAINER_STATUS_T rtp_reader_close( VC_CONTAINER_T *p_ctx ) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = p_ctx->priv->module; |
|
|
|
vc_container_assert(p_ctx->tracks_num < 2); |
|
|
|
if (p_ctx->tracks_num) |
|
{ |
|
void *payload_extra; |
|
|
|
vc_container_assert(module); |
|
vc_container_assert(module->track); |
|
vc_container_assert(module->track->priv); |
|
vc_container_assert(module->track->priv->module); |
|
|
|
payload_extra = module->track->priv->module->extra; |
|
if (payload_extra) |
|
free(payload_extra); |
|
vc_container_free_track(p_ctx, module->track); |
|
} |
|
p_ctx->tracks = NULL; |
|
p_ctx->tracks_num = 0; |
|
if (module) free(module); |
|
p_ctx->priv->module = 0; |
|
return VC_CONTAINER_SUCCESS; |
|
} |
|
|
|
/**************************************************************************//** |
|
* Open the container. |
|
* Uses the I/O URI and/or data to configure the container. |
|
* |
|
* @param p_ctx The reader context. |
|
* @return The resulting status of the function. |
|
*/ |
|
VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T *p_ctx ) |
|
{ |
|
VC_CONTAINER_MODULE_T *module = 0; |
|
VC_CONTAINER_TRACK_T *track = 0; |
|
VC_CONTAINER_TRACK_MODULE_T *t_module = 0; |
|
VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS; |
|
VC_CONTAINERS_LIST_T *parameters = NULL; |
|
uint32_t payload_type; |
|
uint32_t initial_seq_num; |
|
|
|
/* Check the URI scheme looks valid */ |
|
if (!vc_uri_scheme(p_ctx->priv->uri) || |
|
(strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTP_SCHEME) && |
|
strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTP_PKT_SCHEME))) |
|
return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; |
|
|
|
/* Make the query/parameter list more easily searchable */ |
|
parameters = fill_parameter_list(p_ctx->priv->uri); |
|
if (!parameters) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } |
|
|
|
/* Payload type parameter is mandatory and must fit in 7 bits */ |
|
if (!rtp_get_parameter_u32(parameters, PAYLOAD_TYPE_NAME, &payload_type) || payload_type > 127) |
|
{ |
|
status = VC_CONTAINER_ERROR_FORMAT_INVALID; |
|
goto error; |
|
} |
|
|
|
/* Allocate our context */ |
|
module = (VC_CONTAINER_MODULE_T *)malloc(sizeof(VC_CONTAINER_MODULE_T)); |
|
if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; } |
|
|
|
memset(module, 0, sizeof(*module)); |
|
p_ctx->priv->module = module; |
|
p_ctx->tracks = &module->track; |
|
|
|
/* Allocate the track, including space for reading an RTP packet on the end */ |
|
track = vc_container_allocate_track(p_ctx, sizeof(VC_CONTAINER_TRACK_MODULE_T) + MAXIMUM_PACKET_SIZE); |
|
if (!track) |
|
{ |
|
status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; |
|
goto error; |
|
} |
|
module->track = track; |
|
p_ctx->tracks_num = 1; |
|
t_module = track->priv->module; |
|
|
|
/* Initialise the track data */ |
|
t_module->buffer = (uint8_t *)(t_module + 1); |
|
status = decode_payload_type(p_ctx, track, parameters, payload_type); |
|
if (status != VC_CONTAINER_SUCCESS) |
|
goto error; |
|
|
|
vc_container_assert(t_module->timestamp_clock != 0); |
|
|
|
/* Default to a generic, unstructured payload handler */ |
|
if (!t_module->payload_handler) |
|
t_module->payload_handler = generic_payload_handler; |
|
|
|
if (rtp_get_parameter_x32(parameters, SSRC_NAME, &t_module->expected_ssrc)) |
|
SET_BIT(t_module->flags, TRACK_SSRC_SET); |
|
|
|
t_module->probation = MIN_SEQUENTIAL; |
|
if (rtp_get_parameter_u32(parameters, SEQ_NAME, &initial_seq_num)) |
|
{ |
|
/* If an initial sequence number is provided, avoid probation period */ |
|
t_module->max_seq_num = (uint16_t)initial_seq_num; |
|
t_module->probation = 0; |
|
} |
|
|
|
track->is_enabled = true; |
|
|
|
vc_containers_list_destroy(parameters); |
|
|
|
p_ctx->priv->pf_close = rtp_reader_close; |
|
p_ctx->priv->pf_read = rtp_reader_read; |
|
p_ctx->priv->pf_seek = rtp_reader_seek; |
|
p_ctx->priv->pf_control = rtp_reader_control; |
|
|
|
return VC_CONTAINER_SUCCESS; |
|
|
|
error: |
|
if (parameters) vc_containers_list_destroy(parameters); |
|
if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_EOS) |
|
status = VC_CONTAINER_ERROR_FORMAT_INVALID; |
|
LOG_DEBUG(p_ctx, "error opening RTP (%i)", status); |
|
rtp_reader_close(p_ctx); |
|
return status; |
|
} |
|
|
|
/******************************************************************************** |
|
Entrypoint function |
|
********************************************************************************/ |
|
|
|
#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__) |
|
# pragma weak reader_open rtp_reader_open |
|
#endif
|
|
|