summaryrefslogtreecommitdiffstats
path: root/plugins/codecs/amrnb/amrdecode.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 20:34:10 +0000
commite4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch)
tree68cb5ef9081156392f1dd62a00c6ccc1451b93df /plugins/codecs/amrnb/amrdecode.c
parentInitial commit. (diff)
downloadwireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz
wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins/codecs/amrnb/amrdecode.c')
-rw-r--r--plugins/codecs/amrnb/amrdecode.c274
1 files changed, 274 insertions, 0 deletions
diff --git a/plugins/codecs/amrnb/amrdecode.c b/plugins/codecs/amrnb/amrdecode.c
new file mode 100644
index 00000000..1cceca3c
--- /dev/null
+++ b/plugins/codecs/amrnb/amrdecode.c
@@ -0,0 +1,274 @@
+/* amrdecode.c
+ * AMR codec
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+
+#include <wireshark.h>
+
+#include "wsutil/codecs.h"
+#include "ws_attributes.h"
+
+#include <opencore-amrnb/interf_dec.h>
+
+void codec_register_amr(void);
+
+static void *
+codec_amr_init(codec_context_t *ctx _U_)
+{
+ void *state;
+ state = Decoder_Interface_init();
+
+ return state;
+}
+
+static void
+codec_amr_release(codec_context_t *state)
+{
+ Decoder_Interface_exit(state->priv);
+}
+
+static unsigned
+codec_amr_get_channels(codec_context_t *ctx _U_)
+{
+ return 1;
+}
+
+static unsigned
+codec_amr_get_frequency(codec_context_t *ctx _U_)
+{
+ return 8000;
+}
+
+/* RTP doesn't allow the other SID types */
+static const uint8_t speech_bits[16] = {95,103,118,134,148,159,204,244,39, 0, 0, 0, 0, 0, 0, 0};
+/* The number of speech bits rounded up to bytes */
+static const uint8_t block_size[16] = {12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0};
+
+static const uint8_t bit_mask8[] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };
+
+/* Retrieve no_of_bits (<= 8) from in, starting at bit_offset.
+ * Does not do bounds checking.
+ */
+static uint8_t
+get_bits8(uint8_t *in, unsigned bit_offset, const unsigned no_of_bits)
+{
+ uint8_t ret;
+ unsigned octet_offset = bit_offset >> 3;
+ unsigned bits_in_first_octet = 8 - (bit_offset % 8);
+ if (bits_in_first_octet >= no_of_bits) {
+ ret = in[octet_offset] >> (bits_in_first_octet - no_of_bits) & bit_mask8[no_of_bits];
+ } else {
+ unsigned left_shift = no_of_bits - bits_in_first_octet;
+ ret = (in[octet_offset] << left_shift) & bit_mask8[no_of_bits];
+ ret |= (in[octet_offset + 1] >> (8 - left_shift));
+ }
+ return ret;
+}
+
+static size_t
+codec_amr_decode_one(void *state, const void *input, size_t inputSizeBytes,
+ void *output, size_t *outputSizeBytes)
+{
+ uint8_t *in;
+ int mode;
+ unsigned packet_size;
+ packet_size = 2; /* CMR + TOC */
+
+ /* 160 samples per frame, two byte per frame, 20ms */
+ *outputSizeBytes = 160 * 2;
+
+ /* If no room for CMR + TOC, insert silence */
+ if (packet_size > inputSizeBytes) {
+ memset(output, 0, 160 * 2);
+ return *outputSizeBytes;
+ }
+
+ in = (uint8_t*)input + 1;
+ mode = (in[0] >> 3) & 0x0F;
+ packet_size += block_size[mode];
+
+ /* If the size is screwed up, insert silence */
+ if (packet_size > inputSizeBytes) {
+ memset(output, 0, 160 * 2);
+ return *outputSizeBytes;
+ }
+
+ /* XXX: The last parameter is the BFI - we could invert the
+ * Q-bit and pass it in, which might be better?
+ */
+ Decoder_Interface_Decode(state, in, (short *)output, 0);
+ return *outputSizeBytes;
+}
+
+static size_t
+codec_amr_decode_many(void *state, const void *input, size_t inputSizeBytes,
+ void *output, size_t *outputSizeBytes, unsigned frames)
+{
+ int mode;
+ unsigned packet_size = 1; /* CMR */
+
+ *outputSizeBytes = 160 * 2 * frames;
+
+ uint8_t *toc = (uint8_t *)input + 1;
+ uint8_t *speech = toc + frames;
+ uint8_t in[32];
+
+ for (unsigned i = 0; i < frames; i++) {
+ mode = (toc[i] >> 3) & 0x0F;
+ packet_size += block_size[mode] + 1; /* include the TOC */
+
+ /* If the size is screwed up, insert silence */
+ if (packet_size > inputSizeBytes) {
+ memset(output, 0, 160 * 2 * (frames - i));
+ return *outputSizeBytes;
+ }
+
+ /* OpenCORE-AMR ignores the F-bit (which is why we're doing
+ * this memcpy) but might as well clear it.
+ */
+ in[0] = toc[i] & 0x7F;
+ memcpy(&in[1], speech, block_size[mode]);
+ /* XXX: The last parameter is the BFI - we could invert the
+ * Q-bit and pass it in, which might be better?
+ */
+ Decoder_Interface_Decode(state, in, (short *)output, 0);
+ speech += block_size[mode];
+ output = (uint8_t *)output + 160 * 2;
+ }
+
+ return *outputSizeBytes;
+}
+
+static size_t
+codec_amr_decode_oa(codec_context_t *ctx, const void *input,
+ size_t inputSizeBytes, void *output, size_t *outputSizeBytes)
+{
+ bool f_bit;
+ unsigned frames = 0;
+ void *state = ctx->priv;
+
+ /* First byte is CMR, second is the Payload TOC */
+ if (inputSizeBytes < 2) {
+ frames = 1;
+ } else {
+ uint8_t *in = (uint8_t *)input;
+ do {
+ f_bit = (in[++frames] >> 7) & 0x01;
+ } while (f_bit && inputSizeBytes > frames + 1);
+ }
+
+ /* 160 samples per frame, two byte per frame, 20ms */
+ if (!output || !outputSizeBytes)
+ return 160 * 2 * frames;
+
+ if (frames == 1) {
+ return codec_amr_decode_one(state, input, inputSizeBytes, output, outputSizeBytes);
+ } else {
+ return codec_amr_decode_many(state, input, inputSizeBytes, output, outputSizeBytes, frames);
+ }
+}
+
+static size_t
+codec_amr_decode(codec_context_t *ctx, const void *input,
+ size_t inputSizeBytes, void *output, size_t *outputSizeBytes)
+{
+ bool f_bit;
+ unsigned frames = 0;
+ void *state = ctx->priv;
+
+ if (ctx->fmtp_map) {
+ const char* octet_align = (const char *)wmem_map_lookup(ctx->fmtp_map, "octet-align");
+ /* There's a few other lesser used options like "crc", "interleaving",
+ * and "robust-sorting" that can change how it should be decoded.
+ * (All of them imply octet-aligned.) Ideally we'd handle them too.
+ */
+ if (g_strcmp0(octet_align, "1") == 0) {
+ return codec_amr_decode_oa(ctx, input, inputSizeBytes, output, outputSizeBytes);
+ }
+ }
+
+ unsigned bit_offset = 4;
+ uint8_t *in = (uint8_t *)input;
+ /* Per RFC 4867, if the format parameters don't indicate octet-align,
+ * bandwidth-efficient mode is used. (For Decode As, we'll pass in
+ * the value of the dissector's prefs.)
+ * OpenCORE-AMR's interface only supports octet-aligned mode, so we
+ * have to align the data. (From the source, the decode also supports
+ * IF2, except that there's no way to access that from the interface.)
+ */
+ /* First byte is CMR, second is the Payload TOC */
+ if (inputSizeBytes < 2) {
+ frames = 1;
+ } else {
+ do {
+ f_bit = get_bits8(in, bit_offset, 1);
+ bit_offset += 6;
+ frames++;
+ } while (f_bit && inputSizeBytes > (bit_offset / 8));
+ }
+
+ /* 160 samples per frame, two byte per frame, 20ms */
+ if (!output || !outputSizeBytes)
+ return 160 * 2 * frames;
+
+ *outputSizeBytes = 160 * 2 * frames;
+ /* bit_offset is now where the speech bits begin */
+ unsigned toc_offset = 5; /* Mode start */
+ uint8_t aligned[32];
+ int mode;
+ for (unsigned i = 0; i < frames; ++i) {
+ mode = get_bits8(in, toc_offset, 4);
+
+ /* If the size is screwed up, insert silence */
+ if ((bit_offset + speech_bits[mode] + 7) / 8 > inputSizeBytes) {
+ memset(output, 0, 160 * 2 * (frames - i));
+ return *outputSizeBytes;
+ }
+
+ memset(aligned, 0, 32);
+ aligned[0] = mode << 3;
+ for (unsigned j = 0; j < speech_bits[mode] / 8; ++j) {
+ aligned[1 + j] = get_bits8(in, bit_offset, 8);
+ bit_offset += 8;
+ }
+ if (speech_bits[mode] % 8) {
+ aligned[1 + block_size[mode]] = get_bits8(in, bit_offset, speech_bits[mode] % 8);
+ }
+ /* Padding might be different. */
+
+ /* XXX: The last parameter is the BFI - we could invert the
+ * Q-bit and pass it in, which might be better?
+ */
+ Decoder_Interface_Decode(state, aligned, (short *)output, 0);
+ output = (uint8_t *)output + 160 * 2;
+ }
+
+ return *outputSizeBytes;
+}
+
+void
+codec_register_amr(void)
+{
+ register_codec("AMR", codec_amr_init, codec_amr_release,
+ codec_amr_get_channels, codec_amr_get_frequency, codec_amr_decode);
+}
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */