From patchwork Thu May 15 08:29:15 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacob Erlbeck X-Patchwork-Id: 349113 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from ganesha.gnumonks.org (ganesha.gnumonks.org [IPv6:2001:780:45:1d:225:90ff:fe52:c662]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id A6BD5140083 for ; Thu, 15 May 2014 18:52:04 +1000 (EST) Received: from localhost ([127.0.0.1] helo=ganesha.gnumonks.org) by ganesha.gnumonks.org with esmtp (Exim 4.72) (envelope-from ) id 1WkrOL-0000kw-M0; Thu, 15 May 2014 10:51:53 +0200 Received: from mail.sysmocom.de ([144.76.43.93]) by ganesha.gnumonks.org with esmtp (Exim 4.72) (envelope-from ) id 1WkrDo-0007xQ-7Z for openbsc@lists.osmocom.org; Thu, 15 May 2014 10:41:09 +0200 Received: from sysmocom-tmp.home (24-134-58-61-dynip.superkabel.de [24.134.58.61]) by mail.sysmocom.de (Postfix) with ESMTPSA id 4E35858F15; Thu, 15 May 2014 08:40:54 +0000 (UTC) From: Jacob Erlbeck To: openbsc@lists.osmocom.org Subject: [PATCH 07/11] mgcp: Add packet size (ptime) conversion Date: Thu, 15 May 2014 10:29:15 +0200 Message-Id: <1400142559-26788-7-git-send-email-jerlbeck@sysmocom.de> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1400142559-26788-1-git-send-email-jerlbeck@sysmocom.de> References: <1399891147-31419-1-git-send-email-jerlbeck@sysmocom.de> <1400142559-26788-1-git-send-email-jerlbeck@sysmocom.de> X-Spam-Score: 0.2 (/) X-Spam-Report: SpamASsassin versoin 3.3.1 on ganesha.gnumonks.org summary: Content analysis details: (0.2 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.1 TW_CF BODY: Odd Letter Triples with CF 0.1 TW_MG BODY: Odd Letter Triples with MG 0.1 TW_GC BODY: Odd Letter Triples with GC Cc: Jacob Erlbeck X-BeenThere: openbsc@lists.osmocom.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Development of the OpenBSC GSM base station controller List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: openbsc-bounces@lists.osmocom.org Errors-To: openbsc-bounces@lists.osmocom.org The current transcoder implemenation always does a 1:1 recoding concerning the duration of a packet. So RTP timestamps and sequence numbers are not modified. This is not sufficient in some cases, e.g. when the BTS does only allow for a single fixed ptime. This patch decouples encoding from decoding and moves the decoded samples to the state structure so that samples can be combined or drain according to the packaging of incoming and outgoing packets. This patch incorporates parts of Holger's experimental fixes in 0e669e05^..9eba68f9. Ticket: OW#1111 Sponsored-by: On-Waves ehf --- openbsc/contrib/testconv/testconv_main.c | 52 +++++-- openbsc/include/openbsc/mgcp.h | 5 +- openbsc/include/openbsc/mgcp_internal.h | 3 +- openbsc/src/libmgcp/mgcp_network.c | 30 +++- openbsc/src/libmgcp/mgcp_protocol.c | 15 +- openbsc/src/libmgcp/mgcp_vty.c | 22 +++ openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c | 232 +++++++++++++++++++--------- 7 files changed, 262 insertions(+), 97 deletions(-) diff --git a/openbsc/contrib/testconv/testconv_main.c b/openbsc/contrib/testconv/testconv_main.c index c2785f2..aee7304 100644 --- a/openbsc/contrib/testconv/testconv_main.c +++ b/openbsc/contrib/testconv/testconv_main.c @@ -38,10 +38,10 @@ int mgcp_get_trans_frame_size(void *state_, int nsamples, int dst); int main(int argc, char **argv) { - char buf[4096] = {0}; + char buf[4096] = {0x80, 0}; int cc, rc; - struct mgcp_rtp_end dst_end = {0}; - struct mgcp_rtp_end src_end = {0}; + struct mgcp_rtp_end *dst_end; + struct mgcp_rtp_end *src_end; struct mgcp_trunk_config tcfg = {{0}}; struct mgcp_endpoint endp = {0}; struct mgcp_process_rtp_state *state; @@ -52,39 +52,63 @@ int main(int argc, char **argv) tcfg.endpoints = &endp; tcfg.number_endpoints = 1; endp.tcfg = &tcfg; + mgcp_free_endp(&endp); + + dst_end = &endp.bts_end; + src_end = &endp.net_end; if (argc <= 2) errx(1, "Usage: {gsm|g729|pcma|l16} {gsm|g729|pcma|l16}"); - if ((src_end.payload_type = audio_name_to_type(argv[1])) == -1) + if ((src_end->payload_type = audio_name_to_type(argv[1])) == -1) errx(1, "invalid input format '%s'", argv[1]); - if ((dst_end.payload_type = audio_name_to_type(argv[2])) == -1) + if ((dst_end->payload_type = audio_name_to_type(argv[2])) == -1) errx(1, "invalid output format '%s'", argv[2]); - rc = mgcp_transcoding_setup(&endp, &dst_end, &src_end); + rc = mgcp_transcoding_setup(&endp, dst_end, src_end); if (rc < 0) errx(1, "setup failed: %s", strerror(-rc)); - state = dst_end.rtp_process_data; + state = dst_end->rtp_process_data; OSMO_ASSERT(state != NULL); in_size = mgcp_transcoding_get_frame_size(state, 160, 0); OSMO_ASSERT(sizeof(buf) >= in_size + 12); + buf[1] = src_end->payload_type; + *(uint16_t*)(buf+2) = htons(1); + *(uint32_t*)(buf+4) = htonl(0); + *(uint32_t*)(buf+8) = htonl(0xaabbccdd); + while ((cc = read(0, buf + 12, in_size))) { + int cont; + int len; + if (cc != in_size) err(1, "read"); cc += 12; /* include RTP header */ - rc = mgcp_transcoding_process_rtp(&endp, &dst_end, - buf, &cc, sizeof(buf)); - if (rc < 0) - errx(1, "processing failed: %s", strerror(-rc)); + len = cc; + + do { + cont = mgcp_transcoding_process_rtp(&endp, dst_end, + buf, &len, sizeof(buf)); + if (cont == -EAGAIN) { + fprintf(stderr, "Got EAGAIN\n"); + break; + } + + if (cont < 0) + errx(1, "processing failed: %s", strerror(-cont)); + + len -= 12; /* ignore RTP header */ + + if (write(1, buf + 12, len) != len) + err(1, "write"); - cc -= 12; /* ignore RTP header */ - if (write(1, buf + 12, cc) != cc) - err(1, "write"); + len = cont; + } while (len > 0); } return 0; } diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h index b595aba..eb64e32 100644 --- a/openbsc/include/openbsc/mgcp.h +++ b/openbsc/include/openbsc/mgcp.h @@ -87,7 +87,8 @@ typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int stat typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg); typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone); -typedef int (*mgcp_processing)(struct mgcp_rtp_end *dst_end, +typedef int (*mgcp_processing)(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, char *data, int *len, int buf_size); typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, @@ -181,6 +182,8 @@ struct mgcp_config { struct mgcp_port_range transcoder_ports; int endp_dscp; + int bts_force_ptime; + mgcp_change change_cb; mgcp_policy policy_cb; mgcp_reset reset_cb; diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h index e74b9fa..c7bc2a8 100644 --- a/openbsc/include/openbsc/mgcp_internal.h +++ b/openbsc/include/openbsc/mgcp_internal.h @@ -90,6 +90,7 @@ struct mgcp_rtp_end { char *audio_name; char *subtype_name; int output_enabled; + int force_output_ptime; /* RTP patching */ int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ @@ -202,7 +203,7 @@ void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *, uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *); /* payload processing default functions */ -int mgcp_rtp_processing_default(struct mgcp_rtp_end *dst_end, +int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, char *data, int *len, int buf_size); int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c index dcbb97a..42f0381 100644 --- a/openbsc/src/libmgcp/mgcp_network.c +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -348,7 +348,7 @@ static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp, return timestamp_error; } -int mgcp_rtp_processing_default(struct mgcp_rtp_end *dst_end, +int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, char *data, int *len, int buf_size) { return 0; @@ -622,12 +622,28 @@ static int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, if (!rtp_end->output_enabled) rtp_end->dropped_packets += 1; else if (is_rtp) { - mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, buf, rc); - endp->cfg->rtp_processing_cb(rtp_end, buf, &rc, RTP_BUF_SIZE); - forward_data(rtp_end->rtp.fd, &endp->taps[tap_idx], buf, rc); - return mgcp_udp_send(rtp_end->rtp.fd, - &rtp_end->addr, - rtp_end->rtp_port, buf, rc); + int cont; + int nbytes = 0; + int len = rc; + mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, buf, len); + do { + cont = endp->cfg->rtp_processing_cb(endp, rtp_end, + buf, &len, RTP_BUF_SIZE); + if (cont < 0) + break; + + forward_data(rtp_end->rtp.fd, &endp->taps[tap_idx], + buf, len); + rc = mgcp_udp_send(rtp_end->rtp.fd, + &rtp_end->addr, + rtp_end->rtp_port, buf, len); + + if (rc <= 0) + return rc; + nbytes += rc; + len = cont; + } while (len > 0); + return nbytes; } else if (!tcfg->omit_rtcp) { return mgcp_udp_send(rtp_end->rtcp.fd, &rtp_end->addr, diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c index f0d94c8..a903e3c 100644 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -604,6 +604,12 @@ static int set_audio_info(void *ctx, struct mgcp_rtp_end *rtp, rtp->channels = channels; rtp->subtype_name = talloc_strdup(ctx, audio_codec); rtp->audio_name = talloc_strdup(ctx, audio_name); + + if (!strcmp(audio_codec, "G729")) { + rtp->frame_duration_num = 10; + rtp->frame_duration_den = 1000; + } + if (channels != 1) LOGP(DMGCP, LOGL_NOTICE, "Channels != 1 in SDP: '%s'\n", audio_name); @@ -916,11 +922,16 @@ mgcp_header_done: set_audio_info(p->cfg, &endp->bts_end, tcfg->audio_payload, tcfg->audio_name); endp->bts_end.fmtp_extra = talloc_strdup(tcfg->endpoints, tcfg->audio_fmtp_extra); - if (have_sdp) { + if (have_sdp) parse_sdp_data(&endp->net_end, p); - setup_rtp_processing(endp); + + if (p->cfg->bts_force_ptime) { + endp->bts_end.packet_duration_ms = p->cfg->bts_force_ptime; + endp->bts_end.force_output_ptime = 1; } + setup_rtp_processing(endp); + /* policy CB */ if (p->cfg->policy_cb) { int rc; diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c index 953d34b..f1afa95 100644 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ b/openbsc/src/libmgcp/mgcp_vty.c @@ -362,6 +362,26 @@ ALIAS_DEPRECATED(cfg_mgcp_rtp_ip_dscp, cfg_mgcp_rtp_ip_tos_cmd, RTP_STR "Apply IP_TOS to the audio stream\n" "The DSCP value\n") +#define FORCE_PTIME_STR "Force a fixed ptime for packets sent to the BTS" +DEFUN(cfg_mgcp_rtp_force_ptime, + cfg_mgcp_rtp_force_ptime_cmd, + "rtp force-ptime (10|20|40)", + RTP_STR FORCE_PTIME_STR + "The required ptime (packet duration) in ms\n") +{ + g_cfg->bts_force_ptime = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_no_rtp_force_ptime, + cfg_mgcp_no_rtp_force_ptime_cmd, + "no rtp force-ptime", + NO_STR RTP_STR FORCE_PTIME_STR) +{ + g_cfg->bts_force_ptime = 0; + return CMD_SUCCESS; +} + DEFUN(cfg_mgcp_sdp_fmtp_extra, cfg_mgcp_sdp_fmtp_extra_cmd, "sdp audio fmtp-extra .NAME", @@ -1084,6 +1104,8 @@ int mgcp_vty_init(void) install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_base_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_tos_cmd); + install_element(MGCP_NODE, &cfg_mgcp_rtp_force_ptime_cmd); + install_element(MGCP_NODE, &cfg_mgcp_no_rtp_force_ptime_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_cmd); install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_once_cmd); install_element(MGCP_NODE, &cfg_mgcp_no_rtp_keepalive_cmd); diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c index 67e7e52..edd3178 100644 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c +++ b/openbsc/src/osmo-bsc_mgcp/mgcp_transcode.c @@ -1,5 +1,4 @@ /* - * (C) 2014 by Sysmocom s.f.m.c. GmbH * (C) 2014 by On-Waves * All Rights Reserved * @@ -22,7 +21,8 @@ #include #include -#include "bscconfig.h" + +#include "../../bscconfig.h" #include "g711common.h" #include @@ -70,6 +70,14 @@ struct mgcp_process_rtp_state { } dst; size_t dst_frame_size; size_t dst_samples_per_frame; + int dst_packet_duration; + + int is_running; + uint16_t next_seq; + uint32_t next_time; + int16_t samples[10*160]; + size_t sample_cnt; + size_t sample_offs; }; int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst) @@ -302,6 +310,9 @@ int mgcp_transcoding_setup(struct mgcp_endpoint *endp, break; } + if (dst_end->force_output_ptime) + state->dst_packet_duration = mgcp_rtp_packet_duration(endp, dst_end); + LOGP(DMGCP, LOGL_INFO, "Initialized RTP processing on: 0x%x " "conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n", @@ -330,44 +341,21 @@ void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, *audio_name = endp->net_end.audio_name; } - -int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end, - char *data, int *len, int buf_size) +static int decode_audio(struct mgcp_process_rtp_state *state, + uint8_t **src, size_t *nbytes) { - struct mgcp_process_rtp_state *state = dst_end->rtp_process_data; - size_t rtp_hdr_size = 12; - char *payload_data = data + rtp_hdr_size; - int payload_len = *len - rtp_hdr_size; - size_t sample_cnt = 0; - size_t sample_idx; - int16_t samples[10*160]; - uint8_t *src = (uint8_t *)payload_data; - uint8_t *dst = (uint8_t *)payload_data; - size_t nbytes = payload_len; - size_t frame_remainder; - - if (!state) - return 0; - - if (state->src_fmt == state->dst_fmt) - return 0; - - /* TODO: check payload type (-> G.711 comfort noise) */ - - /* Decode src into samples */ - while (nbytes >= state->src_frame_size) { - if (sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(samples)) { + while (*nbytes >= state->src_frame_size) { + if (state->sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(state->samples)) { LOGP(DMGCP, LOGL_ERROR, "Sample buffer too small: %d > %d.\n", - sample_cnt + state->src_samples_per_frame, - ARRAY_SIZE(samples)); + state->sample_cnt + state->src_samples_per_frame, + ARRAY_SIZE(state->samples)); return -ENOSPC; } switch (state->src_fmt) { case AF_GSM: if (gsm_decode(state->src.gsm_handle, - (gsm_byte *)src, samples + sample_cnt) < 0) { + (gsm_byte *)*src, state->samples + state->sample_cnt) < 0) { LOGP(DMGCP, LOGL_ERROR, "Failed to decode GSM.\n"); return -EINVAL; @@ -375,54 +363,44 @@ int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, break; #ifdef HAVE_BCG729 case AF_G729: - bcg729Decoder(state->src.g729_dec, src, 0, samples + sample_cnt); + bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt); break; #endif case AF_PCMA: - alaw_decode(src, samples + sample_cnt, + alaw_decode(*src, state->samples + state->sample_cnt, state->src_samples_per_frame); break; case AF_S16: - memmove(samples + sample_cnt, src, + memmove(state->samples + state->sample_cnt, *src, state->src_frame_size); break; case AF_L16: - l16_decode(src, samples + sample_cnt, + l16_decode(*src, state->samples + state->sample_cnt, state->src_samples_per_frame); break; default: break; } - src += state->src_frame_size; - nbytes -= state->src_frame_size; - sample_cnt += state->src_samples_per_frame; - } - - /* Add silence if necessary */ - frame_remainder = sample_cnt % state->dst_samples_per_frame; - if (frame_remainder) { - size_t silence = state->dst_samples_per_frame - frame_remainder; - if (sample_cnt + silence > ARRAY_SIZE(samples)) { - LOGP(DMGCP, LOGL_ERROR, - "Sample buffer too small for silence: %d > %d.\n", - sample_cnt + silence, - ARRAY_SIZE(samples)); - return -ENOSPC; - } - - while (silence > 0) { - samples[sample_cnt] = 0; - sample_cnt += 1; - silence -= 1; - } + *src += state->src_frame_size; + *nbytes -= state->src_frame_size; + state->sample_cnt += state->src_samples_per_frame; } + return 0; +} +static int encode_audio(struct mgcp_process_rtp_state *state, + uint8_t *dst, size_t buf_size, size_t max_samples) +{ + int nbytes = 0; + size_t nsamples = 0; /* Encode samples into dst */ - sample_idx = 0; - nbytes = 0; - while (sample_idx + state->dst_samples_per_frame <= sample_cnt) { + while (nsamples + state->dst_samples_per_frame <= max_samples) { if (nbytes + state->dst_frame_size > buf_size) { - LOGP(DMGCP, LOGL_ERROR, + if (nbytes > 0) + break; + + /* Not even one frame fits into the buffer */ + LOGP(DMGCP, LOGL_INFO, "Encoding (RTP) buffer too small: %d > %d.\n", nbytes + state->dst_frame_size, buf_size); return -ENOSPC; @@ -430,23 +408,24 @@ int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, switch (state->dst_fmt) { case AF_GSM: gsm_encode(state->dst.gsm_handle, - samples + sample_idx, dst); + state->samples + state->sample_offs, dst); break; #ifdef HAVE_BCG729 case AF_G729: bcg729Encoder(state->dst.g729_enc, - samples + sample_idx, dst); + state->samples + state->sample_offs, dst); break; #endif case AF_PCMA: - alaw_encode(samples + sample_idx, dst, + alaw_encode(state->samples + state->sample_offs, dst, state->src_samples_per_frame); break; case AF_S16: - memmove(dst, samples + sample_idx, state->dst_frame_size); + memmove(dst, state->samples + state->sample_offs, + state->dst_frame_size); break; case AF_L16: - l16_encode(samples + sample_idx, dst, + l16_encode(state->samples + state->sample_offs, dst, state->src_samples_per_frame); break; default: @@ -454,12 +433,121 @@ int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, } dst += state->dst_frame_size; nbytes += state->dst_frame_size; - sample_idx += state->dst_samples_per_frame; + state->sample_offs += state->dst_samples_per_frame; + nsamples += state->dst_samples_per_frame; } + state->sample_cnt -= nsamples; + return nbytes; +} - *len = rtp_hdr_size + nbytes; - /* Patch payload type */ - data[1] = (data[1] & 0x80) | (dst_end->payload_type & 0x7f); +int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, + struct mgcp_rtp_end *dst_end, + char *data, int *len, int buf_size) +{ + struct mgcp_process_rtp_state *state = dst_end->rtp_process_data; + size_t rtp_hdr_size = 12; + char *payload_data = data + rtp_hdr_size; + int payload_len = *len - rtp_hdr_size; + // size_t sample_idx; + uint8_t *src = (uint8_t *)payload_data; + uint8_t *dst = (uint8_t *)payload_data; + size_t nbytes = payload_len; + // size_t frame_remainder; + size_t nsamples; + size_t max_samples; + uint32_t ts_no; + int rc; - return 0; + if (!state) + return 0; + + if (state->src_fmt == state->dst_fmt) { + if (!state->dst_packet_duration) + return 0; + + /* TODO: repackage without transcoding */ + } + + /* If the remaining samples do not fit into a fixed ptime, + * a) discard them, if the next packet is much later + * b) add silence and * send it, if the current packet is not + * yet too late + * c) append the sample data, if the timestamp matches exactly + */ + + /* TODO: check payload type (-> G.711 comfort noise) */ + + if (payload_len > 0) { + ts_no = ntohl(*(uint32_t*)(data+4)); + if (!state->is_running) + state->next_seq = ntohs(*(uint32_t*)(data+4)); + + state->is_running = 1; + + if (state->sample_cnt > 0) { + int32_t delta = ts_no - state->next_time; + /* TODO: check sequence? reordering? packet loss? */ + + if (delta > state->sample_cnt) + /* There is a time gap between the last packet + * and the current one. Just discard the + * partial data that is left in the buffer. + * TODO: This can be improved by adding silence + * instead if the delta is small enough. + */ + state->sample_cnt = 0; + else if (delta < 0) { + LOGP(DMGCP, LOGL_NOTICE, + "RTP time jumps backwards, delta = %d, " + "discarding buffered samples\n", + delta); + state->sample_cnt = 0; + state->sample_offs = 0; + return -EAGAIN; + } + + /* Make sure the samples start without offset */ + fprintf(stderr, "Moving %d samples to buffer start (offset %d)\n", state->sample_cnt, state->sample_offs); + if (state->sample_offs && state->sample_cnt) + memmove(&state->samples[0], + &state->samples[state->sample_offs], + state->sample_cnt * sizeof(state->samples[0])); + } + + state->sample_offs = 0; + + /* Append decoded audio to samples */ + decode_audio(state, &src, &nbytes); + + if (nbytes > 0) + LOGP(DMGCP, LOGL_NOTICE, + "Skipped audio frame in RTP packet: %d octets\n", + nbytes); + } else + ts_no = state->next_time; + + if (state->sample_cnt < state->dst_packet_duration) + return -EAGAIN; + + max_samples = + state->dst_packet_duration ? + state->dst_packet_duration : state->sample_cnt; + + nsamples = state->sample_cnt; + + rc = encode_audio(state, dst, buf_size, max_samples); + if (rc <= 0) + return rc; + + nsamples -= state->sample_cnt; + fprintf(stderr, "Wrote %d samples to buffer (offset %d)\n", nsamples, state->sample_offs); + + *len = rtp_hdr_size + rc; + *(uint16_t*)(data+2) = htonl(state->next_seq); + *(uint32_t*)(data+4) = htonl(ts_no); + + state->next_seq += 1; + state->next_time = ts_no + nsamples; + + return nsamples ? rtp_hdr_size : 0; }