/* * OpenMRCP - Open Source Media Resource Control Protocol Stack * Copyright (C) 2007, Cepstral LLC * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * Author(s): * Arsen Chaloyan * * Contributor(s): * */ #include "jitter_buffer.h" #define MAX_DISSECT_FRAME_COUNT 20 struct jitter_buffer_t { jitter_buffer_config_t *config; apr_byte_t *raw_data; media_frame_t *frames; apr_size_t frame_count; apr_size_t frame_ts; apr_size_t playout_delay_ts; apr_byte_t write_sync; int write_ts_offset; apr_size_t write_ts; apr_size_t read_ts; apr_pool_t *pool; }; jitter_buffer_t* jitter_buffer_create(jitter_buffer_config_t *jb_config, apr_size_t sampling_rate, apr_pool_t *pool) { size_t i; size_t frame_size; jitter_buffer_t *jb = apr_palloc(pool,sizeof(jitter_buffer_t)); jb->config = jb_config; jb->frame_ts = CODEC_FRAME_TIME_BASE * sampling_rate / 1000; frame_size = /*pcm16*/2 * jb->frame_ts; jb->frame_count = jb->config->max_playout_delay / CODEC_FRAME_TIME_BASE; jb->raw_data = apr_palloc(pool,frame_size*jb->frame_count); jb->frames = apr_palloc(pool,sizeof(media_frame_t)*jb->frame_count); for(i=0; iframe_count; i++) { media_frame_init(&jb->frames[i]); jb->frames[i].codec_frame.buffer = jb->raw_data + i*frame_size; } jb->playout_delay_ts = jb->config->initial_playout_delay * sampling_rate / 1000; jb->write_sync = 1; jb->write_ts_offset = 0; jb->write_ts = jb->read_ts = 0; return jb; } apr_status_t jitter_buffer_destroy(jitter_buffer_t *jb) { return APR_SUCCESS; } apr_status_t jitter_buffer_restart(jitter_buffer_t *jb) { jb->write_sync = 1; jb->write_ts_offset = 0; jb->write_ts = jb->read_ts; return APR_SUCCESS; } static APR_INLINE media_frame_t* jitter_buffer_frame_get(jitter_buffer_t *jb, apr_size_t ts) { apr_size_t index = (ts / jb->frame_ts) % jb->frame_count; return &jb->frames[index]; } static APR_INLINE jb_result_t jitter_buffer_write_prepare(jitter_buffer_t *jb, apr_uint32_t ts, apr_size_t *write_ts) { jb_result_t result = JB_OK; if(jb->write_sync) { jb->write_ts_offset = ts - jb->write_ts; jb->write_sync = 0; } *write_ts = ts - jb->write_ts_offset + jb->playout_delay_ts; if(*write_ts % jb->frame_ts != 0) { /* not frame alligned */ return JB_DISCARD_NOT_ALLIGNED; } if(*write_ts >= jb->write_ts) { if(*write_ts - jb->write_ts > jb->frame_ts) { /* gap */ } /* normal write */ } else { if(*write_ts >= jb->read_ts) { /* backward write */ } else { /* too late */ result = JB_DISCARD_TOO_LATE; } } return result; } jb_result_t jitter_buffer_write(jitter_buffer_t *jb, codec_handle_t *codec, void *buffer, apr_size_t size, apr_uint32_t ts) { media_frame_t *media_frame; codec_frame_t frames[MAX_DISSECT_FRAME_COUNT]; apr_size_t frame_count; apr_size_t i; apr_size_t write_ts; jb_result_t result = jitter_buffer_write_prepare(jb,ts,&write_ts); if(result != JB_OK) { return result; } /* dissect codec frames */ frame_count = codec->manipulator->dissect( codec, buffer, size, frames, MAX_DISSECT_FRAME_COUNT); if(frame_count + (write_ts - jb->read_ts)/jb->frame_ts > jb->frame_count) { /* too early */ apr_size_t count = (write_ts - jb->read_ts)/jb->frame_ts; result = JB_DISCARD_TOO_EARLY; if(jb->frame_count > count) { frame_count = jb->frame_count - count; } else { frame_count = 0; } } for(i=0; imanipulator->decode(codec,&frames[i],&media_frame->codec_frame) == APR_SUCCESS) { media_frame->type |= MEDIA_FRAME_TYPE_AUDIO; } write_ts += jb->frame_ts; } if(write_ts > jb->write_ts) { jb->write_ts = write_ts; } return result; } jb_result_t jitter_buffer_write_named_event(jitter_buffer_t *jb, named_event_frame_t *named_event, apr_uint32_t ts) { return JB_OK; } apr_status_t jitter_buffer_read(jitter_buffer_t *jb, media_frame_t *media_frame) { media_frame_t *src_media_frame = jitter_buffer_frame_get(jb,jb->read_ts); if(jb->write_ts > jb->read_ts) { /* normal read */ media_frame->type = src_media_frame->type; if(media_frame->type & MEDIA_FRAME_TYPE_AUDIO) { media_frame->codec_frame.size = src_media_frame->codec_frame.size; memcpy(media_frame->codec_frame.buffer,src_media_frame->codec_frame.buffer,media_frame->codec_frame.size); } if(media_frame->type & MEDIA_FRAME_TYPE_EVENT) { media_frame->event_frame = src_media_frame->event_frame; } } else { /* underflow */ media_frame->type = MEDIA_FRAME_TYPE_NONE; jb->write_ts += jb->frame_ts; } src_media_frame->type = MEDIA_FRAME_TYPE_NONE; jb->read_ts += jb->frame_ts; return APR_SUCCESS; }