Merge commit 'zecke/fixes/mgcp-transcoding'

Include the last bits of fixes for the transcoding code. These fixes
and the appropriate mgcp config are known to work.
diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h
index d4d6140..1790f84 100644
--- a/openbsc/include/openbsc/mgcp.h
+++ b/openbsc/include/openbsc/mgcp.h
@@ -87,6 +87,12 @@
 typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg);
 typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone);
 
+/**
+ * Return:
+ *   <  0 in case no audio was processed
+ *   >= 0 in case audio was processed. The remaining payload
+ *   length will be returned.
+ */
 typedef int (*mgcp_processing)(struct mgcp_endpoint *endp,
 			       struct mgcp_rtp_end *dst_end,
 			       char *data, int *len, int buf_size);
diff --git a/openbsc/src/libmgcp/mgcp_transcode.c b/openbsc/src/libmgcp/mgcp_transcode.c
index 296020c..4d4cec8 100644
--- a/openbsc/src/libmgcp/mgcp_transcode.c
+++ b/openbsc/src/libmgcp/mgcp_transcode.c
@@ -428,10 +428,12 @@
 
 	if (payload_len > 0) {
 		ts_no = ntohl(*(uint32_t*)(data+4));
-		if (!state->is_running)
+		if (!state->is_running) {
 			state->next_seq = ntohs(*(uint16_t*)(data+2));
+			state->next_time = ts_no;
+			state->is_running = 1;
+		}
 
-		state->is_running = 1;
 
 		if (state->sample_cnt > 0) {
 			int32_t delta = ts_no - state->next_time;
@@ -448,6 +450,7 @@
 					"0x%x dropping sample buffer due delta=%d sample_cnt=%d\n",
 					ENDPOINT_NUMBER(endp), delta, state->sample_cnt);
 				state->sample_cnt = 0;
+				state->next_time = ts_no;
 			} else if (delta < 0) {
 				LOGP(DMGCP, LOGL_NOTICE,
 				     "RTP time jumps backwards, delta = %d, "
@@ -488,7 +491,14 @@
 	nsamples = state->sample_cnt;
 
 	rc = encode_audio(state, dst, buf_size, max_samples);
-	if (rc <= 0)
+	/*
+	 * There were no samples to encode?
+	 * TODO: how does this work for comfort noise?
+	 */
+	if (rc == 0)
+		return -ENOMSG;
+	/* Any other error during the encoding */
+	if (rc < 0)
 		return rc;
 
 	nsamples -= state->sample_cnt;
diff --git a/openbsc/tests/mgcp/mgcp_transcoding_test.c b/openbsc/tests/mgcp/mgcp_transcoding_test.c
index 404268a..2e857a1 100644
--- a/openbsc/tests/mgcp/mgcp_transcoding_test.c
+++ b/openbsc/tests/mgcp/mgcp_transcoding_test.c
@@ -120,6 +120,24 @@
 		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
 		"\xD5\xA5\xA3\xA5\xD5\x25\x23\x25\xD5\xA5\xA3\xA5\xD5\x25\x23\x25"
 	},
+	/* RTP: SeqNo=26527, TS=232640 */
+	{0.020000, 92,
+		"\x80\x08\x67\x9f\x00\x03\x8c\xc0\x04\xaa\x67\x9f\xd5\xd5\xd5\xd5"
+		"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
+		"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
+		"\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5\xd5"
+		"\xd5\xd5\xd5\xd5\xd5\xd5\x55\x55\xd5\xd5\x55\x55\xd5\xd5\x55\x55"
+		"\xd5\xd5\xd5\x55\x55\xd5\xd5\xd5\x55\x55\xd5\xd5"
+	},
+	/* RTP: SeqNo=26528, TS=232720 */
+	{0.020000, 92,
+		"\x80\x08\x67\xa0\x00\x03\x8d\x10\x04\xaa\x67\x9f\x55\xd5\xd5\x55"
+		"\xd5\x55\xd5\xd5\xd5\x55\xd5\x55\xd5\xd5\x55\xd5\x55\xd5\x55\xd5"
+		"\x55\x55\xd5\x55\xd5\xd5\x55\x55\x55\x55\x55\xd5\xd5\x55\xd5\xd5"
+		"\xd5\x55\xd5\xd5\xd5\x55\x54\x55\xd5\xd5\x55\xd5\xd5\xd5\xd5\x55"
+		"\x54\x55\xd5\x55\xd5\x55\x55\x55\x55\x55\xd5\xd5\xd5\xd5\xd5\xd4"
+		"\xd5\x54\x55\xd5\xd4\xd5\x54\xd5\x55\xd5\xd5\xd5"
+	},
 };
 
 
@@ -221,8 +239,9 @@
 	cont = mgcp_transcoding_process_rtp(endp, dst_end,
 					    buf, &len, sizeof(buf));
 	if (cont < 0) {
-		printf("processing failed: %s", strerror(-cont));
-		abort();
+		printf("Nothing encoded due: %s\n", strerror(-cont));
+		talloc_free(ctx);
+		return -1;
 	}
 
 	if (len < 24) {
@@ -296,6 +315,124 @@
 	talloc_free(ctx);
 }
 
+static void test_transcode_result(void)
+{
+	char buf[4096];
+	int len, res;
+	void *ctx;
+	struct mgcp_endpoint *endp;
+	struct mgcp_process_rtp_state *state;
+
+	{
+		/* from GSM to PCMA and same ptime */
+		given_configured_endpoint(160, 0, "gsm", "pcma", &ctx, &endp);
+		state = endp->bts_end.rtp_process_data;
+
+		/* result */
+		len = audio_packets_gsm[0].len;
+		memcpy(buf, audio_packets_gsm[0].data, len);
+		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
+		OSMO_ASSERT(res == sizeof(struct rtp_hdr));
+		OSMO_ASSERT(state->sample_cnt == 0);
+
+		len = res;
+		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
+		OSMO_ASSERT(res == -ENOMSG);
+
+		talloc_free(ctx);
+	}
+
+	{
+		/* from GSM to PCMA and same ptime */
+		given_configured_endpoint(160, 160, "gsm", "pcma", &ctx, &endp);
+		state = endp->bts_end.rtp_process_data;
+
+		/* result */
+		len = audio_packets_gsm[0].len;
+		memcpy(buf, audio_packets_gsm[0].data, len);
+		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
+		OSMO_ASSERT(res == sizeof(struct rtp_hdr));
+		OSMO_ASSERT(state->sample_cnt == 0);
+
+		len = res;
+		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
+		OSMO_ASSERT(res == -EAGAIN);
+
+		talloc_free(ctx);
+	}
+
+	{
+		/* from PCMA to GSM and wrong different ptime */
+		given_configured_endpoint(80, 160, "pcma", "gsm", &ctx, &endp);
+		state = endp->bts_end.rtp_process_data;
+
+		/* Add the first sample */
+		len = audio_packets_pcma[1].len;
+		memcpy(buf, audio_packets_pcma[1].data, len);
+		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
+		OSMO_ASSERT(state->sample_cnt == 80);
+		OSMO_ASSERT(state->next_time == 232640);
+		OSMO_ASSERT(res < 0);
+
+		/* Add the second sample and it should be consumable */
+		len = audio_packets_pcma[2].len;
+		memcpy(buf, audio_packets_pcma[2].data, len);
+		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
+		OSMO_ASSERT(state->sample_cnt == 0);
+		OSMO_ASSERT(state->next_time == 232640 + 80 + 160);
+		OSMO_ASSERT(res == sizeof(struct rtp_hdr));
+
+		talloc_free(ctx);
+	}
+
+	{
+		/* from PCMA to GSM with a big time jump */
+		struct rtp_hdr *hdr;
+		uint32_t ts;
+
+		given_configured_endpoint(80, 160, "pcma", "gsm", &ctx, &endp);
+		state = endp->bts_end.rtp_process_data;
+
+		/* Add the first sample */
+		len = audio_packets_pcma[1].len;
+		memcpy(buf, audio_packets_pcma[1].data, len);
+		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
+		OSMO_ASSERT(state->sample_cnt == 80);
+		OSMO_ASSERT(state->next_time == 232640);
+		OSMO_ASSERT(state->next_seq == 26527);
+		OSMO_ASSERT(res < 0);
+
+		/* Add a skip to the packet to force a 'resync' */
+		len = audio_packets_pcma[2].len;
+		memcpy(buf, audio_packets_pcma[2].data, len);
+		hdr = (struct rtp_hdr *) &buf[0];
+		/* jump the time and add alignment error */
+		ts = ntohl(hdr->timestamp) + 123 * 80 + 2;
+		hdr->timestamp = htonl(ts);
+		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
+		OSMO_ASSERT(res < 0);
+		OSMO_ASSERT(state->sample_cnt == 80);
+		OSMO_ASSERT(state->next_time == ts);
+		OSMO_ASSERT(state->next_seq == 26527);
+		/* TODO: this can create alignment errors */
+
+
+		/* Now attempt to consume 160 samples */
+		len = audio_packets_pcma[2].len;
+		memcpy(buf, audio_packets_pcma[2].data, len);
+		hdr = (struct rtp_hdr *) &buf[0];
+		ts += 80;
+		hdr->timestamp = htonl(ts);
+		res = mgcp_transcoding_process_rtp(endp, &endp->bts_end, buf, &len, ARRAY_SIZE(buf));
+		OSMO_ASSERT(res == 12);
+		OSMO_ASSERT(state->sample_cnt == 0);
+		OSMO_ASSERT(state->next_time == ts + 160);
+		OSMO_ASSERT(state->next_seq == 26528);
+
+		talloc_free(ctx);
+	}
+}
+
 static int test_repacking(int in_samples, int out_samples, int no_transcode)
 {
 	char buf[4096] = {0x80, 0};
@@ -378,6 +515,7 @@
 
 int main(int argc, char **argv)
 {
+	int rc;
 	osmo_init_logging(&log_info);
 
 	printf("=== Transcoding Good Cases ===\n");
@@ -413,19 +551,22 @@
 	printf("=== Transcoding Bad Cases ===\n");
 
 	printf("Invalid size:\n");
-	transcode_test("gsm", "pcma",
+	rc = transcode_test("gsm", "pcma",
 		       (uint8_t *)audio_packets_gsm_invalid_size[0].data,
 		       audio_packets_gsm_invalid_size[0].len);
+	OSMO_ASSERT(rc < 0);
 
 	printf("Invalid data:\n");
-	transcode_test("gsm", "pcma",
+	rc = transcode_test("gsm", "pcma",
 		       (uint8_t *)audio_packets_gsm_invalid_data[0].data,
 		       audio_packets_gsm_invalid_data[0].len);
+	OSMO_ASSERT(rc < 0);
 
 	printf("Invalid payload type:\n");
-	transcode_test("gsm", "pcma",
+	rc = transcode_test("gsm", "pcma",
 		       (uint8_t *)audio_packets_gsm_invalid_ptype[0].data,
 		       audio_packets_gsm_invalid_ptype[0].len);
+	OSMO_ASSERT(rc == 0);
 
 	printf("=== Repacking ===\n");
 
@@ -440,6 +581,7 @@
 	test_repacking(160, 100, 0);
 	test_repacking(160, 100, 1);
 	test_rtp_seq_state();
+	test_transcode_result();
 
 	return 0;
 }
diff --git a/openbsc/tests/mgcp/mgcp_transcoding_test.ok b/openbsc/tests/mgcp/mgcp_transcoding_test.ok
index e06b0e1..7c1c8ce 100644
--- a/openbsc/tests/mgcp/mgcp_transcoding_test.ok
+++ b/openbsc/tests/mgcp/mgcp_transcoding_test.ok
@@ -144,19 +144,11 @@
 Invalid size:
 == Transcoding test ==
 converting gsm -> pcma
-encoded:
-    80 03 00 01 00 00 00 a0 11 22 33 44 d4 7c e3 e9 
-    62 50 39 f0 f8 b4 68 ea 6c 0e 81 1b 56 2a d5 bc 
-    69 9c d1 f0 66 7a ec 49 7a 
-counted: 0
+Nothing encoded due: No message of desired type
 Invalid data:
 == Transcoding test ==
 converting gsm -> pcma
-encoded:
-    80 03 00 01 00 00 00 a0 11 22 33 44 ee ee ee ee 
-    ee ee ee ee ee ee ee ee ee ee ee ee ee ee ee ee 
-    ee ee ee ee ee ee ee ee ee ee ee ee ee 
-counted: 0
+Nothing encoded due: No message of desired type
 Invalid payload type:
 == Transcoding test ==
 converting gsm -> pcma