struct ast_websocket *websocket;
struct ast_format *native_format;
struct ast_codec *native_codec;
- struct ast_format *slin_format;
- struct ast_codec *slin_codec;
struct ast_channel *channel;
struct ast_timer *timer;
- struct ast_frame silence;
- struct ast_trans_pvt *translator;
AST_LIST_HEAD(, ast_frame) frame_queue;
pthread_t outbound_read_thread;
size_t bytes_read;
(_res); \
})
-static void set_channel_format(struct websocket_pvt * instance,
- struct ast_format *fmt)
-{
- if (ast_format_cmp(ast_channel_rawreadformat(instance->channel), fmt)
- == AST_FORMAT_CMP_NOT_EQUAL) {
- ast_channel_set_rawreadformat(instance->channel, fmt);
- ast_set_read_format(instance->channel, ast_channel_readformat(instance->channel));
- ast_debug(4, "Switching readformat to %s\n", ast_format_get_name(fmt));
- }
-}
-
/*
* Reminder... This function gets called by webchan_read which is
* triggered by the channel timer firing. It always gets called
{
struct websocket_pvt *instance = NULL;
struct ast_frame *native_frame = NULL;
- struct ast_frame *slin_frame = NULL;
int fdno = ast_channel_fdno(ast);
instance = ast_channel_tech_pvt(ast);
native_frame = dequeue_frame(instance);
/*
- * No frame when the timer fires means we have to create and
- * return a silence frame in its place.
+ * No frame when the timer fires means we have to return a null frame in its place.
*/
if (!native_frame) {
- ast_debug(5, "%s: WebSocket read timer fired with no frame available. Returning silence.\n", ast_channel_name(ast));
- set_channel_format(instance, instance->slin_format);
- slin_frame = ast_frdup(&instance->silence);
- return slin_frame;
- }
-
- /*
- * If we're in passthrough mode or the frame length is already optimal_frame_size,
- * we can just return it.
- */
- if (instance->passthrough || native_frame->datalen == instance->optimal_frame_size) {
- set_channel_format(instance, instance->native_format);
- return native_frame;
- }
-
- /*
- * If we're here, we have a short frame that we need to pad
- * with silence.
- */
-
- if (instance->translator) {
- slin_frame = ast_translate(instance->translator, native_frame, 0);
- if (!slin_frame) {
- ast_log(LOG_WARNING, "%s: Failed to translate %d byte frame\n",
- ast_channel_name(ast), native_frame->datalen);
- return NULL;
- }
- ast_frame_free(native_frame, 0);
- } else {
- /*
- * If there was no translator then the native format
- * was already slin.
- */
- slin_frame = native_frame;
- }
-
- set_channel_format(instance, instance->slin_format);
-
- /*
- * So now we have an slin frame but it's probably still short
- * so we create a new data buffer with the correct length
- * which is filled with zeros courtesy of ast_calloc.
- * We then copy the short frame data into the new buffer
- * and set the offset to AST_FRIENDLY_OFFSET so that
- * the core can read the data without any issues.
- * If the original frame data was mallocd, we need to free the old
- * data buffer so we don't leak memory and we need to set
- * mallocd to AST_MALLOCD_DATA so that the core knows
- * it needs to free the new data buffer when it's done.
- */
-
- if (slin_frame->datalen != instance->silence.datalen) {
- char *old_data = slin_frame->data.ptr;
- int old_len = slin_frame->datalen;
- int old_offset = slin_frame->offset;
- ast_debug(4, "%s: WebSocket read short frame. Expected %d got %d. Filling with silence\n",
- ast_channel_name(ast), instance->silence.datalen,
- slin_frame->datalen);
-
- slin_frame->data.ptr = ast_calloc(1, instance->silence.datalen + AST_FRIENDLY_OFFSET);
- if (!slin_frame->data.ptr) {
- ast_frame_free(slin_frame, 0);
- return NULL;
- }
- slin_frame->data.ptr += AST_FRIENDLY_OFFSET;
- slin_frame->offset = AST_FRIENDLY_OFFSET;
- memcpy(slin_frame->data.ptr, old_data, old_len);
- if (slin_frame->mallocd & AST_MALLOCD_DATA) {
- ast_free(old_data - old_offset);
- }
- slin_frame->mallocd |= AST_MALLOCD_DATA;
- slin_frame->datalen = instance->silence.datalen;
- slin_frame->samples = instance->silence.samples;
+ ast_debug(4, "%s: WebSocket read timer fired with no frame available. Returning NULL frame.\n",
+ ast_channel_name(ast));
+ return &ast_null_frame;
}
- return slin_frame;
+ return native_frame;
}
static int queue_frame_from_buffer(struct websocket_pvt *instance,
ao2_cleanup(instance->native_format);
instance->native_format = NULL;
- ao2_cleanup(instance->slin_codec);
- instance->slin_codec = NULL;
-
- ao2_cleanup(instance->slin_format);
- instance->slin_format = NULL;
-
- if (instance->silence.data.ptr) {
- ast_free(instance->silence.data.ptr);
- instance->silence.data.ptr = NULL;
- }
-
- if (instance->translator) {
- ast_translator_free_path(instance->translator);
- instance->translator = NULL;
- }
-
if (instance->leftover_data) {
ast_free(instance->leftover_data);
instance->leftover_data = NULL;
return ao2_bump(instance);
}
-static int set_instance_translator(struct websocket_pvt *instance)
-{
- if (ast_format_cache_is_slinear(instance->native_format)) {
- instance->slin_format = ao2_bump(instance->native_format);
- instance->slin_codec = ast_format_get_codec(instance->slin_format);
- return 0;
- }
-
- instance->slin_format = ao2_bump(ast_format_cache_get_slin_by_rate(instance->native_codec->sample_rate));
- if (!instance->slin_format) {
- ast_log(LOG_ERROR, "%s: Unable to get slin format for rate %d\n",
- ast_channel_name(instance->channel), instance->native_codec->sample_rate);
- return -1;
- }
- ast_debug(3, "%s: WebSocket channel slin format '%s' Sample rate: %d ptime: %dms\n",
- ast_channel_name(instance->channel), ast_format_get_name(instance->slin_format),
- ast_format_get_sample_rate(instance->slin_format),
- ast_format_get_default_ms(instance->slin_format));
-
- instance->translator = ast_translator_build_path(instance->slin_format, instance->native_format);
- if (!instance->translator) {
- ast_log(LOG_ERROR, "%s: Unable to build translator path from '%s' to '%s'\n",
- ast_channel_name(instance->channel), ast_format_get_name(instance->native_format),
- ast_format_get_name(instance->slin_format));
- return -1;
- }
-
- instance->slin_codec = ast_format_get_codec(instance->slin_format);
- return 0;
-}
-
-static int set_instance_silence_frame(struct websocket_pvt *instance)
-{
- instance->silence.frametype = AST_FRAME_VOICE;
- instance->silence.datalen =
- (instance->slin_codec->default_ms * instance->slin_codec->minimum_bytes) / instance->slin_codec->minimum_ms;
- instance->silence.samples = instance->silence.datalen / sizeof(uint16_t);
- /*
- * Even though we'll calloc the data pointer, we don't mark it as
- * mallocd because this frame will be around for a while and we don't
- * want it accidentally freed before we're done with it.
- */
- instance->silence.mallocd = 0;
- instance->silence.offset = 0;
- instance->silence.src = __PRETTY_FUNCTION__;
- instance->silence.subclass.format = instance->slin_format;
- instance->silence.data.ptr = ast_calloc(1, instance->silence.datalen);
- if (!instance->silence.data.ptr) {
- return -1;
- }
-
- return 0;
-}
-
static int set_channel_timer(struct websocket_pvt *instance)
{
int rate = 0;
instance->channel = ao2_bump(chan);
ast_channel_tech_set(instance->channel, &websocket_tech);
- if (set_instance_translator(instance) != 0) {
- goto failure;
- }
-
- if (set_instance_silence_frame(instance) != 0) {
- goto failure;
- }
-
/* If the application's media direction is 'both' or 'out', we need the channel timer. */
if (instance->media_direction != WEBCHAN_MEDIA_DIRECTION_IN
&& set_channel_timer(instance) != 0) {