diff --git a/Makefile b/Makefile index 3564c2192..18591f768 100644 --- a/Makefile +++ b/Makefile @@ -8,9 +8,10 @@ all: $(MAKE) -C daemon $(MAKE) -C recording-daemon $(MAKE) -C iptables-extension + $(MAKE) -C t with-kernel: - $(MAKE) + $(MAKE) all $(MAKE) -C kernel-module distclean clean: @@ -18,6 +19,7 @@ distclean clean: $(MAKE) -C recording-daemon clean $(MAKE) -C iptables-extension clean $(MAKE) -C kernel-module clean + $(MAKE) -C t clean rm -rf project.tgz cov-int .DEFAULT: @@ -25,6 +27,7 @@ distclean clean: $(MAKE) -C recording-daemon $@ $(MAKE) -C iptables-extension $@ $(MAKE) -C kernel-module $@ + $(MAKE) -C t coverity: cov-build --dir cov-int $(MAKE) diff --git a/daemon/codec.c b/daemon/codec.c index 1b5bf5197..fc3b79dfa 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -478,6 +478,7 @@ struct rtp_payload_type *codec_make_payload_type(const str *codec_str, struct ca ret->channels = channels; ret->bitrate = bitrate; ret->ptime = ptime; + ret->format_parameters = STR_EMPTY; const codec_def_t *def = codec_find(&ret->encoding, 0); ret->codec_def = def; @@ -490,6 +491,8 @@ struct rtp_payload_type *codec_make_payload_type(const str *codec_str, struct ca ret->channels = def->default_channels; if (!ret->ptime) ret->ptime = def->default_ptime; + if ((!ret->format_parameters.s || !ret->format_parameters.s[0]) && def->default_fmtp) + str_init(&ret->format_parameters, (char *) def->default_fmtp); if (def->init) def->init(ret); @@ -527,7 +530,6 @@ struct rtp_payload_type *codec_make_payload_type(const str *codec_str, struct ca str_init(&ret->encoding_with_params, full_encoding); str_init(&ret->encoding_parameters, params); - ret->format_parameters = STR_EMPTY; __rtp_payload_type_dup(media->call, ret); @@ -587,14 +589,14 @@ static struct ssrc_entry *__ssrc_handler_new(void *p) { ch->encoder = encoder_new(); if (!ch->encoder) goto err; - if (encoder_config(ch->encoder, h->dest_pt.codec_def, + if (encoder_config_fmtp(ch->encoder, h->dest_pt.codec_def, h->dest_pt.bitrate ? : h->dest_pt.codec_def->default_bitrate, ch->ptime, - &enc_format, &ch->encoder_format)) + &enc_format, &ch->encoder_format, &h->dest_pt.format_parameters)) goto err; - ch->decoder = decoder_new_fmt(h->source_pt.codec_def, h->source_pt.clock_rate, h->source_pt.channels, - &ch->encoder_format); + ch->decoder = decoder_new_fmtp(h->source_pt.codec_def, h->source_pt.clock_rate, h->source_pt.channels, + &ch->encoder_format, &h->source_pt.format_parameters); if (!ch->decoder) goto err; @@ -649,6 +651,7 @@ static int __packet_encoded(encoder_t *enc, void *u1, void *u2) { while (1) { // figure out how big of a buffer we need unsigned int payload_len = MAX(enc->avpkt.size, ch->bytes_per_packet); + payload_len += 16; // extra room for certain protocols, e.g. AMR framing unsigned int pkt_len = sizeof(struct rtp_header) + payload_len + RTP_BUFFER_TAIL_ROOM; // prepare our buffers char *buf = malloc(pkt_len); @@ -661,7 +664,7 @@ static int __packet_encoded(encoder_t *enc, void *u1, void *u2) { if (in_pkt) ilog(LOG_DEBUG, "Adding %i bytes to packetizer", in_pkt->size); int ret = ch->handler->dest_pt.codec_def->packetizer(in_pkt, - ch->sample_buffer, &inout); + ch->sample_buffer, &inout, enc); if (G_UNLIKELY(ret == -1)) { // nothing diff --git a/lib/bitstr.h b/lib/bitstr.h new file mode 100644 index 000000000..ad530c5bd --- /dev/null +++ b/lib/bitstr.h @@ -0,0 +1,79 @@ +#ifndef _BITSTR_H_ +#define _BITSTR_H_ + +#include "str.h" +#include + +struct bitstr_s { + str s; + unsigned int bit_offset; // leading consumed bits +}; +typedef struct bitstr_s bitstr; + +INLINE void bitstr_init(bitstr *b, const str *s) { + b->s = *s; + b->bit_offset = 0; +} + +INLINE int bitstr_shift_ret(bitstr *b, unsigned int bits, str *ret) { + if (!bits) + return 0; + // check if we have enough + if (bits > b->s.len * 8 - b->bit_offset) + return -1; + + unsigned int to_copy = (bits + b->bit_offset + 7) / 8; + + if (ret) { + assert(ret->len >= to_copy); + ret->len = to_copy; + memcpy(ret->s, b->s.s, to_copy); + unsigned char *ret_s = (unsigned char *) ret->s; // avoid bitshifts on signed chars + + // we have to bit-shift the entire string if there was a leading offset + if (b->bit_offset) { + unsigned int left = bits; + unsigned int c = 0; + while (b->bit_offset + left > 8) { + // enough to fill one output byte from two consecutive input bytes + ret_s[c] <<= b->bit_offset; + ret_s[c] |= ret_s[c + 1] >> (8 - b->bit_offset); + if (left <= 8) { + // final trailing bits overlapping bytes: truncate + ret_s[c] &= 0xff << (8 - left); + left = 0; + ret->len--; + } + else + left -= 8; + c++; + } + if (left) { + // last byte has the remainder + ret_s[c] <<= b->bit_offset; + ret_s[c] &= 0xff << (8 - left); + } + } + else { + // truncate last byte if needed + unsigned int bits_left = bits % 8; + if (bits_left) + ret_s[to_copy - 1] &= 0xff << (8 - bits_left); + } + } + + b->bit_offset += bits; + unsigned int int_bytes = b->bit_offset / 8; + int shift_ret = str_shift(&b->s, int_bytes); + assert(shift_ret == 0); + b->bit_offset -= int_bytes * 8; + + return 0; +} + +INLINE int bitstr_shift(bitstr *b, unsigned int bits) { + return bitstr_shift_ret(b, bits, NULL); +} + + +#endif diff --git a/lib/codeclib.c b/lib/codeclib.c index f820de992..c8b594c80 100644 --- a/lib/codeclib.c +++ b/lib/codeclib.c @@ -13,6 +13,7 @@ #include "loglib.h" #include "resample.h" #include "rtplib.h" +#include "bitstr.h" @@ -34,18 +35,24 @@ static packetizer_f packetizer_passthrough; // pass frames as they arrive in AVPackets static packetizer_f packetizer_samplestream; // flat stream of samples +static packetizer_f packetizer_amr; static format_init_f opus_init; -static set_options_f opus_set_options; +static set_enc_options_f opus_set_enc_options; + +static set_enc_options_f amr_set_enc_options; +static set_dec_options_f amr_set_dec_options; static void avc_def_init(codec_def_t *); -static const char *avc_decoder_init(decoder_t *); +static const char *avc_decoder_init(decoder_t *, const str *); static int avc_decoder_input(decoder_t *dec, const str *data, GQueue *out); static void avc_decoder_close(decoder_t *); -static const char *avc_encoder_init(encoder_t *enc); +static const char *avc_encoder_init(encoder_t *enc, const str *); static int avc_encoder_input(encoder_t *enc, AVFrame **frame); static void avc_encoder_close(encoder_t *enc); +static int amr_decoder_input(decoder_t *dec, const str *data, GQueue *out); + @@ -58,15 +65,24 @@ static const codec_type_t codec_type_avcodec = { .encoder_input = avc_encoder_input, .encoder_close = avc_encoder_close, }; +static const codec_type_t codec_type_amr = { + .def_init = avc_def_init, + .decoder_init = avc_decoder_init, + .decoder_input = amr_decoder_input, + .decoder_close = avc_decoder_close, + .encoder_init = avc_encoder_init, + .encoder_input = avc_encoder_input, + .encoder_close = avc_encoder_close, +}; #ifdef HAVE_BCG729 static packetizer_f packetizer_g729; // aggregate some frames into packets static void bcg729_def_init(codec_def_t *); -static const char *bcg729_decoder_init(decoder_t *); +static const char *bcg729_decoder_init(decoder_t *, const str *); static int bcg729_decoder_input(decoder_t *dec, const str *data, GQueue *out); static void bcg729_decoder_close(decoder_t *); -static const char *bcg729_encoder_init(encoder_t *enc); +static const char *bcg729_encoder_init(encoder_t *enc, const str *); static int bcg729_encoder_input(encoder_t *enc, AVFrame **frame); static void bcg729_encoder_close(encoder_t *enc); @@ -212,7 +228,7 @@ static codec_def_t __codec_defs[] = { .media_type = MT_AUDIO, .codec_type = &codec_type_avcodec, .init = opus_init, - .set_options = opus_set_options, + .set_enc_options = opus_set_enc_options, }, { .rtpname = "vorbis", @@ -294,9 +310,12 @@ static codec_def_t __codec_defs[] = { .default_channels = 1, .default_bitrate = 6600, .default_ptime = 20, - .packetizer = packetizer_passthrough, + .default_fmtp = "octet-align=1", + .packetizer = packetizer_amr, .media_type = MT_AUDIO, - .codec_type = &codec_type_avcodec, + .codec_type = &codec_type_amr, + .set_enc_options = amr_set_enc_options, + .set_dec_options = amr_set_dec_options, }, { .rtpname = "AMR-WB", @@ -306,9 +325,12 @@ static codec_def_t __codec_defs[] = { .default_channels = 1, .default_bitrate = 14250, .default_ptime = 20, - .packetizer = packetizer_passthrough, + .default_fmtp = "octet-align=1", + .packetizer = packetizer_amr, .media_type = MT_AUDIO, - .codec_type = &codec_type_avcodec, + .codec_type = &codec_type_amr, + .set_enc_options = amr_set_enc_options, + .set_dec_options = amr_set_dec_options, }, // pseudo-codecs { @@ -368,17 +390,21 @@ enum media_type codec_get_type(const str *type) { -static const char *avc_decoder_init(decoder_t *ret) { - AVCodec *codec = ret->def->decoder; +static const char *avc_decoder_init(decoder_t *dec, const str *fmtp) { + AVCodec *codec = dec->def->decoder; if (!codec) return "codec not supported"; - ret->u.avc.avcctx = avcodec_alloc_context3(codec); - if (!ret->u.avc.avcctx) + dec->u.avc.avcctx = avcodec_alloc_context3(codec); + if (!dec->u.avc.avcctx) return "failed to alloc codec context"; - ret->u.avc.avcctx->channels = ret->in_format.channels; - ret->u.avc.avcctx->sample_rate = ret->in_format.clockrate; - int i = avcodec_open2(ret->u.avc.avcctx, codec, NULL); + dec->u.avc.avcctx->channels = dec->in_format.channels; + dec->u.avc.avcctx->sample_rate = dec->in_format.clockrate; + + if (dec->def->set_dec_options) + dec->def->set_dec_options(dec, fmtp); + + int i = avcodec_open2(dec->u.avc.avcctx, codec, NULL); if (i) return "failed to open codec context"; @@ -392,6 +418,12 @@ static const char *avc_decoder_init(decoder_t *ret) { decoder_t *decoder_new_fmt(const codec_def_t *def, int clockrate, int channels, const format_t *resample_fmt) { + return decoder_new_fmtp(def, clockrate, channels, resample_fmt, NULL); +} + +decoder_t *decoder_new_fmtp(const codec_def_t *def, int clockrate, int channels, const format_t *resample_fmt, + const str *fmtp) +{ const char *err; decoder_t *ret = NULL; @@ -412,7 +444,7 @@ decoder_t *decoder_new_fmt(const codec_def_t *def, int clockrate, int channels, if (resample_fmt) ret->out_format = *resample_fmt; - err = def->codec_type->decoder_init(ret); + err = def->codec_type->decoder_init(ret, fmtp); if (err) goto err; @@ -876,7 +908,7 @@ encoder_t *encoder_new() { return ret; } -static const char *avc_encoder_init(encoder_t *enc) { +static const char *avc_encoder_init(encoder_t *enc, const str *fmtp) { enc->u.avc.codec = enc->def->encoder; if (!enc->u.avc.codec) return "output codec not found"; @@ -911,8 +943,8 @@ static const char *avc_encoder_init(encoder_t *enc) { enc->samples_per_frame = enc->u.avc.avcctx->frame_size; enc->samples_per_packet = enc->samples_per_frame; - if (enc->def->set_options) - enc->def->set_options(enc); + if (enc->def->set_enc_options) + enc->def->set_enc_options(enc, fmtp); int i = avcodec_open2(enc->u.avc.avcctx, enc->u.avc.codec, NULL); if (i) @@ -923,6 +955,12 @@ static const char *avc_encoder_init(encoder_t *enc) { int encoder_config(encoder_t *enc, const codec_def_t *def, int bitrate, int ptime, const format_t *requested_format, format_t *actual_format) +{ + return encoder_config_fmtp(enc, def, bitrate, ptime, requested_format, actual_format, NULL); +} + +int encoder_config_fmtp(encoder_t *enc, const codec_def_t *def, int bitrate, int ptime, + const format_t *requested_format, format_t *actual_format, const str *fmtp) { const char *err; @@ -941,7 +979,7 @@ int encoder_config(encoder_t *enc, const codec_def_t *def, int bitrate, int ptim enc->ptime = ptime / def->clockrate_mult; enc->bitrate = bitrate; - err = def->codec_type->encoder_init(enc); + err = def->codec_type->encoder_init(enc, fmtp); if (err) goto err; @@ -1133,7 +1171,7 @@ int encoder_input_fifo(encoder_t *enc, AVFrame *frame, } -static int packetizer_passthrough(AVPacket *pkt, GString *buf, str *output) { +static int packetizer_passthrough(AVPacket *pkt, GString *buf, str *output, encoder_t *enc) { if (!pkt) return -1; assert(output->len >= pkt->size); @@ -1144,7 +1182,7 @@ static int packetizer_passthrough(AVPacket *pkt, GString *buf, str *output) { // returns: -1 = not enough data, nothing returned; 0 = returned a packet; // 1 = returned a packet and there's more -static int packetizer_samplestream(AVPacket *pkt, GString *buf, str *input_output) { +static int packetizer_samplestream(AVPacket *pkt, GString *buf, str *input_output, encoder_t *enc) { // avoid moving buffers around if possible: // most common case: new input packet has just enough (or more) data as what we need if (G_LIKELY(pkt && buf->len == 0 && pkt->size >= input_output->len)) { @@ -1167,6 +1205,9 @@ static int packetizer_samplestream(AVPacket *pkt, GString *buf, str *input_outpu } + + + static void opus_init(struct rtp_payload_type *pt) { if (pt->clock_rate != 48000) { ilog(LOG_WARN, "Opus is only supported with a clock rate of 48 kHz"); @@ -1216,7 +1257,7 @@ static void opus_init(struct rtp_payload_type *pt) { ilog(LOG_DEBUG, "Using default bitrate of %i bps for %i-channel Opus", pt->bitrate, pt->channels); } -static void opus_set_options(encoder_t *enc) { +static void opus_set_enc_options(encoder_t *enc, const str *fmtp) { int ret; if (enc->ptime) if ((ret = av_opt_set_int(enc->u.avc.avcctx, "frame_duration", enc->ptime, 0))) @@ -1226,6 +1267,252 @@ static void opus_set_options(encoder_t *enc) { + + +#define AMR_FT_TYPES 14 +const static unsigned int amr_bits_per_frame[AMR_FT_TYPES] = { + 95, // 4.75 kbit/s // 0 + 103, // 5.15 kbit/s // 1 + 118, // 5.90 kbit/s // 2 + 134, // 6.70 kbit/s // 3 + 148, // 7.40 kbit/s // 4 + 159, // 7.95 kbit/s // 5 + 204, // 10.2 kbit/s // 6 + 244, // 12.2 kbit/s // 7 + 40, // comfort noise // 8 + 40, // comfort noise // 9 + 40, // comfort noise // 10 + 40, // comfort noise // 11 + 0, // invalid // 12 + 0, // invalid // 13 +}; +const static unsigned int amr_wb_bits_per_frame[AMR_FT_TYPES] = { + 132, // 6.60 kbit/s // 0 + 177, // 8.85 kbit/s // 1 + 253, // 12.65 kbit/s // 2 + 285, // 14.25 kbit/s // 3 + 317, // 15.85 kbit/s // 4 + 365, // 18.25 kbit/s // 5 + 397, // 19.85 kbit/s // 6 + 461, // 23.05 kbit/s // 7 + 477, // 23.85 kbit/s // 8 + 40, // comfort noise // 9 + 0, // invalid // 10 + 0, // invalid // 11 + 0, // invalid // 12 + 0, // invalid // 13 +}; +static void amr_set_encdec_options(codec_options_t *opts, const str *fmtp, const codec_def_t *def) { + if (!strcmp(def->rtpname, "AMR")) + opts->amr.bits_per_frame = amr_bits_per_frame; + else + opts->amr.bits_per_frame = amr_wb_bits_per_frame; + + if (!fmtp || !fmtp->s) + return; + + // semicolon-separated key=value + str s = *fmtp; + str token, key; + while (str_token_sep(&token, &s, ';') == 0) { + if (str_token(&key, &token, '=')) + continue; + if (!str_cmp(&key, "octet-align")) { + if (token.len == 1 && token.s[0] == '1') + opts->amr.octet_aligned = 1; + } + else if (!str_cmp(&key, "crc")) { + if (token.len == 1 && token.s[0] == '1') { + opts->amr.octet_aligned = 1; + opts->amr.crc = 1; + } + } + else if (!str_cmp(&key, "robust-sorting")) { + if (token.len == 1 && token.s[0] == '1') { + opts->amr.octet_aligned = 1; + opts->amr.robust_sorting = 1; + } + } + else if (!str_cmp(&key, "interleaving")) { + opts->amr.octet_aligned = 1; + opts->amr.interleaving = str_to_i(&token, 0); + } + // XXX other options + } +} +static void amr_set_enc_options(encoder_t *enc, const str *fmtp) { + amr_set_encdec_options(&enc->codec_options, fmtp, enc->def); +} +static void amr_set_dec_options(decoder_t *dec, const str *fmtp) { + amr_set_encdec_options(&dec->codec_options, fmtp, dec->def); +} + +static int amr_decoder_input(decoder_t *dec, const str *data, GQueue *out) { + const char *err = NULL; + + if (!data || !data->s) + goto err; + + bitstr d; + bitstr_init(&d, data); + + GQueue toc = G_QUEUE_INIT; + unsigned int ill = 0, ilp = 0; + + unsigned char cmr_chr[2]; + str cmr = STR_CONST_INIT_BUF(cmr_chr); + err = "no CMR"; + if (bitstr_shift_ret(&d, 4, &cmr)) + goto err; + // XXX handle CMR? + + if (dec->codec_options.amr.octet_aligned) { + if (bitstr_shift(&d, 4)) + goto err; + + if (dec->codec_options.amr.interleaving) { + unsigned char ill_ilp_chr[2]; + str ill_ilp = STR_CONST_INIT_BUF(ill_ilp_chr); + err = "no ILL/ILP"; + if (bitstr_shift_ret(&d, 8, &ill_ilp)) + goto err; + ill = ill_ilp_chr[0] >> 4; + ilp = ill_ilp_chr[0] & 0xf; + } + } + + err = "ILP > ILL"; + if (ilp > ill) + goto err; + err = "interleaving unimplemented"; + if (ill) + goto err; + + // TOC + int num_crcs = 0; + while (1) { + unsigned char toc_byte[2]; + str toc_entry = STR_CONST_INIT_BUF(toc_byte); + err = "missing TOC entry"; + if (bitstr_shift_ret(&d, 6, &toc_entry)) + goto err; + + if (dec->codec_options.amr.octet_aligned) + if (bitstr_shift(&d, 2)) + goto err; + + unsigned char ft = (toc_byte[0] >> 3) & 0xf; + if (ft != 14 && ft != 15) { + num_crcs++; + err = "invalid frame type"; + if (ft >= AMR_FT_TYPES) + goto err; + if (dec->codec_options.amr.bits_per_frame[ft] == 0) + goto err; + } + + g_queue_push_tail(&toc, GUINT_TO_POINTER(toc_byte[0])); + + // no F bit = last TOC entry + if (!(toc_byte[0] & 0x80)) + break; + } + + if (dec->codec_options.amr.crc) { + // CRCs is one byte per frame + err = "missing CRC entry"; + if (bitstr_shift(&d, num_crcs * 8)) + goto err; + // XXX use/check CRCs + } + + while (toc.length) { + unsigned char toc_byte = GPOINTER_TO_UINT(g_queue_pop_head(&toc)); + unsigned char ft = (toc_byte >> 3) & 0xf; + if (ft >= AMR_FT_TYPES) // invalid + continue; + + unsigned int bits = dec->codec_options.amr.bits_per_frame[ft]; + + // AMR encoder expects an octet aligned TOC byte plus the payload + unsigned char frame_buf[(bits + 7) / 8 + 1 + 1]; + str frame = STR_CONST_INIT_BUF(frame_buf); + str_shift(&frame, 1); + err = "short frame"; + if (bitstr_shift_ret(&d, bits, &frame)) + goto err; + + // add TOC byte + str_shift(&frame, -1); + frame.s[0] = toc_byte & 0x7c; // strip F bit, keep FT and Q, zero padding (01111100) + + if (dec->codec_options.amr.octet_aligned && (bits % 8) != 0) { + unsigned int padding_bits = 8 - (bits % 8); + if (bitstr_shift(&d, padding_bits)) + goto err; + } + + err = "failed to decode AMR data"; + if (avc_decoder_input(dec, &frame, out)) + goto err; + } + + return 0; + +err: + if (err) + ilog(LOG_WARN, "Error unpacking AMR packet: %s", err); + + return -1; +} +static int packetizer_amr(AVPacket *pkt, GString *buf, str *output, encoder_t *enc) { + assert(pkt->size >= 1); + + // CMR + TOC byte (already included) + optional ILL/ILP + optional CRC + payload + assert(output->len >= pkt->size + 3); + + unsigned char toc = pkt->data[0]; + unsigned char ft = (toc >> 3) & 0xf; + assert(ft <= 13); + unsigned int bits = enc->codec_options.amr.bits_per_frame[ft]; + assert(bits != 0); + + unsigned char *s = (unsigned char *) output->s; // for safe bit shifting + + s[0] = '\xf0'; // no CMR req (4 bits) + + if (enc->codec_options.amr.octet_aligned) { + unsigned int offset = 1; // CMR byte + if (enc->codec_options.amr.interleaving) + s[offset++] = 0; // no interleaving + if (enc->codec_options.amr.crc) + s[offset++] = 0; // not implemented + memcpy(s + offset, pkt->data, pkt->size); + output->len = pkt->size + offset; + return 0; + } + + // bit shift TOC byte in (6 bits) + s[0] |= pkt->data[0] >> 4; + s[1] = (pkt->data[0] & 0x0c) << 4; + + // bit shift payload in (shifted by 4+6 = 10 bits = 1 byte + 2 bits + for (int i = 1; i < pkt->size; i++) { + s[i] |= pkt->data[i] >> 2; + s[i+1] = pkt->data[i] << 6; + } + + // is the last byte just padding? + bits += 4 + 6; // CMR and TOC + unsigned int bytes = (bits + 7) / 8; + output->len = bytes; + + return 0; +} + + + + #ifdef HAVE_BCG729 static void bcg729_def_init(codec_def_t *def) { // test init @@ -1241,7 +1528,7 @@ static void bcg729_def_init(codec_def_t *def) { } } -static const char *bcg729_decoder_init(decoder_t *dec) { +static const char *bcg729_decoder_init(decoder_t *dec, const str *fmtp) { dec->u.bcg729 = initBcg729DecoderChannel(); if (!dec->u.bcg729) return "failed to initialize bcg729"; @@ -1285,7 +1572,7 @@ static void bcg729_decoder_close(decoder_t *dec) { dec->u.bcg729 = NULL; } -static const char *bcg729_encoder_init(encoder_t *enc) { +static const char *bcg729_encoder_init(encoder_t *enc, const str *fmtp) { enc->u.bcg729 = initBcg729EncoderChannel(0); // no VAD if (!enc->u.bcg729) return "failed to initialize bcg729"; @@ -1329,13 +1616,13 @@ static void bcg729_encoder_close(encoder_t *enc) { enc->u.bcg729 = NULL; } -static int packetizer_g729(AVPacket *pkt, GString *buf, str *input_output) { +static int packetizer_g729(AVPacket *pkt, GString *buf, str *input_output, encoder_t *enc) { // how many frames do we want? int want_frames = input_output->len / 10; // easiest case: we only want one frame. return what we got if (want_frames == 1 && pkt) - return packetizer_passthrough(pkt, buf, input_output); + return packetizer_passthrough(pkt, buf, input_output, enc); // any other case, we go through our buffer str output = *input_output; // remaining output buffer diff --git a/lib/codeclib.h b/lib/codeclib.h index bf059e29c..f87f796fc 100644 --- a/lib/codeclib.h +++ b/lib/codeclib.h @@ -29,6 +29,7 @@ struct resample_s; struct seq_packet_s; struct packet_sequencer_s; struct rtp_payload_type; +union codec_options_u; typedef struct codec_type_s codec_type_t; typedef struct decoder_s decoder_t; @@ -37,10 +38,12 @@ typedef struct format_s format_t; typedef struct resample_s resample_t; typedef struct seq_packet_s seq_packet_t; typedef struct packet_sequencer_s packet_sequencer_t; +typedef union codec_options_u codec_options_t; -typedef int packetizer_f(AVPacket *, GString *, str *); +typedef int packetizer_f(AVPacket *, GString *, str *, encoder_t *); typedef void format_init_f(struct rtp_payload_type *); -typedef void set_options_f(encoder_t *); +typedef void set_enc_options_f(encoder_t *, const str *); +typedef void set_dec_options_f(decoder_t *, const str *); @@ -55,15 +58,26 @@ enum media_type { struct codec_type_s { void (*def_init)(codec_def_t *); - const char *(*decoder_init)(decoder_t *); + const char *(*decoder_init)(decoder_t *, const str *); int (*decoder_input)(decoder_t *, const str *data, GQueue *); void (*decoder_close)(decoder_t *); - const char *(*encoder_init)(encoder_t *); + const char *(*encoder_init)(encoder_t *, const str *); int (*encoder_input)(encoder_t *, AVFrame **); void (*encoder_close)(encoder_t *); }; +union codec_options_u { + struct { + int interleaving; + int octet_aligned:1; + int crc:1; + int robust_sorting:1; + + const unsigned int *bits_per_frame; + } amr; +}; + struct codec_def_s { const char * const rtpname; int clockrate_mult; @@ -73,13 +87,15 @@ struct codec_def_s { int default_channels; const int default_bitrate; int default_ptime; + const char *default_fmtp; packetizer_f * const packetizer; const int bits_per_sample; const enum media_type media_type; // codec-specific callbacks format_init_f *init; - set_options_f *set_options; + set_enc_options_f *set_enc_options; + set_dec_options_f *set_dec_options; // filled in by codeclib_init() str rtpname_str; @@ -107,6 +123,7 @@ struct resample_s { struct decoder_s { const codec_def_t *def; + codec_options_t codec_options; format_t in_format, out_format; @@ -135,6 +152,7 @@ struct encoder_s { actual_format; const codec_def_t *def; + codec_options_t codec_options; union { struct { @@ -177,6 +195,8 @@ enum media_type codec_get_type(const str *type); decoder_t *decoder_new_fmt(const codec_def_t *def, int clockrate, int channels, const format_t *resample_fmt); +decoder_t *decoder_new_fmtp(const codec_def_t *def, int clockrate, int channels, const format_t *resample_fmt, + const str *fmtp); void decoder_close(decoder_t *dec); int decoder_input_data(decoder_t *dec, const str *data, unsigned long ts, int (*callback)(decoder_t *, AVFrame *, void *u1, void *u2), void *u1, void *u2); @@ -185,6 +205,8 @@ int decoder_input_data(decoder_t *dec, const str *data, unsigned long ts, encoder_t *encoder_new(); int encoder_config(encoder_t *enc, const codec_def_t *def, int bitrate, int ptime, const format_t *requested_format, format_t *actual_format); +int encoder_config_fmtp(encoder_t *enc, const codec_def_t *def, int bitrate, int ptime, + const format_t *requested_format, format_t *actual_format, const str *fmtp); void encoder_close(encoder_t *); void encoder_free(encoder_t *); int encoder_input_data(encoder_t *enc, AVFrame *frame, diff --git a/lib/common.Makefile b/lib/common.Makefile index cc83fc998..bb204a0f2 100644 --- a/lib/common.Makefile +++ b/lib/common.Makefile @@ -12,7 +12,7 @@ debug: dep: .depend clean: - rm -f $(OBJS) $(TARGET) $(LIBSRCS) .depend core core.* + rm -f $(OBJS) $(TARGET) $(LIBSRCS) $(ADD_CLEAN) .depend core core.* rm -f fix_frame_channel_layout.h fix_frame_channel_layout-test.[co] .depend: $(SRCS) $(LIBSRCS) Makefile @@ -22,7 +22,7 @@ install: $(OBJS): Makefile -$(LIBSRCS): +$(LIBSRCS): $(patsubst %,../lib/%,$(LIBSRCS)) rm -f "$@" echo '/******** GENERATED FILE ********/' > "$@" cat ../lib/"$@" >> "$@" diff --git a/lib/loglib.h b/lib/loglib.h index 04ebbea7b..d5a2edeef 100644 --- a/lib/loglib.h +++ b/lib/loglib.h @@ -44,6 +44,8 @@ void __ilog_np(int prio, const char *format, ...) __attribute__ ((format (printf INLINE int get_log_level(void) { + if (!rtpe_common_config_ptr) + return 8; return g_atomic_int_get(&rtpe_common_config_ptr->log_level); } diff --git a/lib/str.h b/lib/str.h index a53866032..a2f552c4e 100644 --- a/lib/str.h +++ b/lib/str.h @@ -27,6 +27,8 @@ typedef struct _str str; #define STR_NULL ((str) { NULL, 0 }) #define STR_EMPTY ((str) { "", 0 }) #define STR_CONST_INIT(str) { str, sizeof(str)-1 } +#define STR_CONST_INIT_LEN(str, len) { str, len } +#define STR_CONST_INIT_BUF(buf) { (char *) &buf, sizeof(buf) } @@ -60,6 +62,8 @@ INLINE str *str_chunk_insert(GStringChunk *c, const str *s); INLINE int str_shift(str *s, int len); /* eats the supplied string from the beginning of s. returns -1 if string head doesn't match */ INLINE int str_shift_cmp(str *s, const char *); +/* shifts the string by given length and returns the shifted part. returns -1 if string is too short */ +INLINE int str_shift_ret(str *s, int len, str *ret); /* binary compares str object with memory chunk of equal size */ INLINE int str_memcmp(const str *s, void *m); /* locate a substring within a string, returns character index or -1 */ @@ -113,8 +117,13 @@ INLINE char *str_end(const str *s) { return s->s + s->len; } INLINE int str_shift(str *s, int len) { + return str_shift_ret(s, len, NULL); +} +INLINE int str_shift_ret(str *s, int len, str *ret) { if (s->len < len) return -1; + if (ret) + str_init_len(ret, s->s, len); s->s += len; s->len -= len; return 0; diff --git a/t/.gitignore b/t/.gitignore new file mode 100644 index 000000000..08cb90708 --- /dev/null +++ b/t/.gitignore @@ -0,0 +1,13 @@ +*.o +bitstr-test +amr-encode-test +amr-decode-test +core +.depend +auxlib.c +codeclib.c +fix_frame_channel_layout.h +loglib.c +resample.c +rtplib.c +str.c diff --git a/t/.ycm_extra_conf.py b/t/.ycm_extra_conf.py new file mode 100644 index 000000000..92f5a5a15 --- /dev/null +++ b/t/.ycm_extra_conf.py @@ -0,0 +1,111 @@ +import os +import ycm_core +from clang_helpers import PrepareClangFlags + +# Set this to the absolute path to the folder (NOT the file!) containing the +# compile_commands.json file to use that instead of 'flags'. See here for +# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html +# Most projects will NOT need to set this to anything; you can just change the +# 'flags' list of compilation flags. Notice that YCM itself uses that approach. +compilation_database_folder = '' + +# These are the compilation flags that will be used in case there's no +# compilation database set. +flags = [ + '-g', + '-Wall', + '-pthread', + '-fno-strict-aliasing', + '-I/usr/include/glib-2.0', + '-I/usr/lib/x86_64-linux-gnu/glib-2.0/include', + '-I/usr/include/json-glib-1.0', + '-pthread', + '-I../kernel-module/', + '-I../lib/', + '-D_GNU_SOURCE', + '-D__DEBUG=1', + '-D__YCM=1', + '-DRTPENGINE_VERSION="dummy"', + '-DRE_PLUGIN_DIR="/usr/lib/rtpengine"', + '-DWITH_IPTABLES_OPTION', + '-DWITH_TRANSCODING', + '-DHAVE_BCG729', + '-O2', + '-fstack-protector', + '--param=ssp-buffer-size=4', + '-Wformat', + '-Werror=format-security', + '-D_FORTIFY_SOURCE=2', + # THIS IS IMPORTANT! Without a "-std=" flag, clang won't + # know which language to use when compiling headers. So it will guess. + # Badly. So C++ headers will be compiled as C headers. + # You don't want that so ALWAYS specify + # a "-std=". + # For a C project, you would set this to something like 'c99' instead of + # 'c++11'. + '-std=c99', + # ...and the same thing goes for the magic -x option which specifies the + # language that the files to be compiled are written in. This is mostly + # relevant for c++ headers. + # For a C project, you would set this to 'c' instead of 'c++'. + '-x', + 'c', +] + +if compilation_database_folder: + database = ycm_core.CompilationDatabase(compilation_database_folder) +else: + database = None + + +def DirectoryOfThisScript(): + return os.path.dirname(os.path.abspath(__file__)) + + +def MakeRelativePathsInFlagsAbsolute(flags, working_directory): + if not working_directory: + return flags + new_flags = [] + make_next_absolute = False + path_flags = ['-isystem', '-I', '-iquote', '--sysroot='] + for flag in flags: + new_flag = flag + + if make_next_absolute: + make_next_absolute = False + if not flag.startswith('/'): + new_flag = os.path.join(working_directory, flag) + + for path_flag in path_flags: + if flag == path_flag: + make_next_absolute = True + break + + if flag.startswith(path_flag): + path = flag[len(path_flag):] + new_flag = path_flag + os.path.join(working_directory, path) + break + + if new_flag: + new_flags.append(new_flag) + return new_flags + + +def FlagsForFile(filename): + if database: + # Bear in mind that compilation_info.compiler_flags_ does NOT return a + # python list, but a "list-like" StringVec object + compilation_info = database.GetCompilationInfoForFile(filename) + final_flags = PrepareClangFlags( + MakeRelativePathsInFlagsAbsolute( + compilation_info.compiler_flags_, + compilation_info.compiler_working_dir_), + filename) + else: + relative_to = DirectoryOfThisScript() + final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to) + + return { + 'flags': final_flags, + 'do_cache': True + } diff --git a/t/Makefile b/t/Makefile new file mode 100644 index 000000000..76d2e851a --- /dev/null +++ b/t/Makefile @@ -0,0 +1,62 @@ +TARGET= unit-tests + +with_transcoding ?= yes + +CFLAGS= -g -Wall -pthread -fno-strict-aliasing +CFLAGS+= -std=c99 +CFLAGS+= $(shell pkg-config --cflags glib-2.0) +CFLAGS+= $(shell pkg-config --cflags gthread-2.0) +CFLAGS+= $(shell pkg-config --cflags openssl) +CFLAGS+= -I. -I../lib/ +CFLAGS+= -D_GNU_SOURCE +ifeq ($(with_transcoding),yes) +CFLAGS+= $(shell pkg-config --cflags libavcodec) +CFLAGS+= $(shell pkg-config --cflags libavformat) +CFLAGS+= $(shell pkg-config --cflags libavutil) +CFLAGS+= $(shell pkg-config --cflags libswresample) +CFLAGS+= $(shell pkg-config --cflags libavfilter) +CFLAGS+= -DWITH_TRANSCODING +else +CFLAGS+= -DWITHOUT_CODECLIB +endif + +LDFLAGS+= $(shell pkg-config --libs glib-2.0) +LDFLAGS+= $(shell pkg-config --libs gthread-2.0) +LDFLAGS+= $(shell pkg-config --libs libcrypto) +LDFLAGS+= $(shell pkg-config --libs openssl) +ifeq ($(with_transcoding),yes) +LDFLAGS+= $(shell pkg-config --libs libavcodec) +LDFLAGS+= $(shell pkg-config --libs libavformat) +LDFLAGS+= $(shell pkg-config --libs libavutil) +LDFLAGS+= $(shell pkg-config --libs libswresample) +LDFLAGS+= $(shell pkg-config --libs libavfilter) +endif + +SRCS= bitstr-test.c amr-decode-test.c amr-encode-test.c +LIBSRCS= loglib.c auxlib.c str.c rtplib.c +ifeq ($(with_transcoding),yes) +LIBSRCS+= codeclib.c resample.c +endif +OBJS= $(SRCS:.c=.o) $(LIBSRCS:.c=.o) + +include ../lib/common.Makefile + +include .depend + +.PHONY: unit-tests + +TESTS= bitstr-test +ifeq ($(with_transcoding),yes) +TESTS+= amr-decode-test amr-encode-test +endif + +ADD_CLEAN= $(TESTS) + +unit-tests: $(TESTS) + for x in $(TESTS); do echo testing: $$x; ./$$x || exit 1; done + +bitstr-test: bitstr-test.o + +amr-decode-test: amr-decode-test.o codeclib.o str.o auxlib.o resample.o rtplib.o loglib.o + +amr-encode-test: amr-encode-test.o codeclib.o str.o auxlib.o resample.o rtplib.o loglib.o diff --git a/t/amr-decode-test.c b/t/amr-decode-test.c new file mode 100644 index 000000000..8b3f3fb57 --- /dev/null +++ b/t/amr-decode-test.c @@ -0,0 +1,106 @@ +#include "codeclib.h" +#include "str.h" +#include + +static void hexdump(const unsigned char *buf, int len) { + for (int i = 0; i < len; i++) + printf("%02x", buf[i]); + printf("\n"); +} + +static int frame_cb(decoder_t *dec, AVFrame *frame, void *u1, void *u2) { + char **expect = u1; + int *expect_len = u2; + assert(expect); + assert(expect_len); + assert(*expect); + if (*expect_len != frame->linesize[0] + || memcmp(frame->data[0], *expect, *expect_len)) + { + printf( + "packet content mismatch\n" + "expected %i bytes, received %i bytes\n" + "expected:\n", + *expect_len, frame->linesize[0]); + hexdump((unsigned char *) *expect, *expect_len); + printf("received:\n"); + hexdump((unsigned char *) frame->data[0], frame->linesize[0]); + exit(1); + } + *expect = NULL; + *expect_len = 0; + return 0; +} + +static void do_test_amr_xx(const char *file, int line, + char *fmtp_s, char *data_s, int data_len, char *expect_s, int expect_len, + char *codec, int clockrate) +{ + printf("running test %s:%i\n", file, line); + str codec_name; + str_init(&codec_name, codec); + const codec_def_t *def = codec_find(&codec_name, MT_AUDIO); + assert(def); + if (!def->support_encoding || !def->support_decoding) { + printf("AMR not fully supported - skipping test\n"); + exit(0); + } + const format_t fmt = { .clockrate = clockrate, .channels = 1, .format = AV_SAMPLE_FMT_S16}; + str fmtp_str, *fmtp = NULL; + if (fmtp_s) { + str_init(&fmtp_str, fmtp_s); + fmtp = &fmtp_str; + } + decoder_t *d = decoder_new_fmtp(def, clockrate, 1, &fmt, fmtp); + assert(d); + const str data = { data_s, data_len }; + int ret = decoder_input_data(d, &data, 1, frame_cb, &expect_s, &expect_len); + assert(!ret); + assert(expect_s == NULL); + decoder_close(d); + printf("test ok: %s:%i\n", file, line); +} + +static void do_test_amr_wb(const char *file, int line, + char *fmtp_s, char *data_s, int data_len, char *expect_s, int expect_len) +{ + do_test_amr_xx(file, line, fmtp_s, data_s, data_len, expect_s, expect_len, + "AMR-WB", 16000); +} +static void do_test_amr_nb(const char *file, int line, + char *fmtp_s, char *data_s, int data_len, char *expect_s, int expect_len) +{ + do_test_amr_xx(file, line, fmtp_s, data_s, data_len, expect_s, expect_len, + "AMR", 8000); +} + +#define do_test_wb(in, out, fmt) \ + do_test_amr_wb(__FILE__, __LINE__, fmt, in, sizeof(in)-1, out, sizeof(out)-1) +#define do_test_nb(in, out, fmt) \ + do_test_amr_nb(__FILE__, __LINE__, fmt, in, sizeof(in)-1, out, sizeof(out)-1) + +int main() { + codeclib_init(0); + + do_test_wb( + "\xf0\x44\xf1\x46\x18\x1d\xd1\x57\x23\x13\x42\xf0\x00\x0c\x50\x33\xdd\xff\x0b\x99\x89\x2c\x68\x52\xf8\xf8\xd9\x59\x16\xd7\x45\xe7\x01\xec\x1f\xfe\x5b\xc6\xf9\x01\xa4\xb5\xe0\x6c\x91\x41\xfe\x52\x2c\xce\x44\xbb\x5a\xdf\x76\x29\xf8\xdb\xca\x18\xd6\x50", + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\xff\xff\x02\x00\xff\xff\xff\xff\x02\x00\xfd\xff\x03\x00\x00\x00\xfd\xff\x04\x00\xfc\xff\x02\x00\x01\x00\xfd\xff\x03\x00\xfe\xff\x01\x00\x01\x00\xff\xff\x01\x00\xff\xff\x01\x00\xff\xff\x00\x00\x01\x00\xff\xff\x00\x00\x00\x00\xff\xff\x00\x00\x01\x00\xfe\xff\x02\x00\xff\xff\xff\xff\x03\x00\xfd\xff\x03\x00\xff\xff\xff\xff\x03\x00\xfe\xff\x01\x00\x00\x00\xff\xff\x02\x00\xfe\xff\x01\x00\x00\x00\xff\xff\x02\x00\xfe\xff\x01\x00\x00\x00\xff\xff\x04\x00\xfe\xff\xfd\xff\x00\x00\x02\x00\xfe\xff\xf8\xff\x01\x00\x04\x00\xff\xff\xff\xff\xfc\xff\x06\x00\x00\x00\xf8\xff\x11\x00\x09\x00\x06\x00\x3f\x00\x37\x00\xf9\xff\x11\x00\x4e\x00\x34\x00\xf4\xff\x17\x00\x5d\x00\x31\x00\xe0\xff\x0b\x00\x71\x00\x42\x00\xd3\xff\x09\x00\x74\x00\x3c\x00\xc8\xff\x03\x00\x78\x00\x35\x00\xbc\xff\xff\xff\x7a\x00\x2e\x00\xb0\xff\x00\x00\x77\x00\x26\x00\xaa\xff\xfc\xff\x78\x00\x1c\x00\xa3\xff\xfe\xff\x72\x00\x14\x00\xa0\xff\xfc\xff\x6f\x00\x09\x00\x8e\xff\xfc\xff\x72\x00\xff\xff\x89\xff\xff\xff\x7e\x00\xfe\xff\x7b\xff\x19\x00\xa9\x00\xfa\xff\x62\xff\x14\x00\xae\x00\xf5\xff\x54\xff\x16\x00\xb6\x00\xe8\xff\x3f\xff\x0b\x00\xb9\x00\xee\xff\x34\xff\xfd\xff\xb8\x00\xe9\xff\x2d\xff\x00\x00\xb8\x00\xe4\xff\x2c\xff\xff\xff\xb9\x00\xdf\xff\x25\xff\xff\xff\xb2\x00\xda\xff\x28\xff\xfc\xff\xae\x00\xd6\xff\x2a\xff\xff\xff\xa5\x00\xd8\xff\x30\xff\xfc\xff\xa1\x00\xd5\xff\x35\xff\xf9\xff\x97\x00\xd4\xff\x37\xff\xfa\xff\x92\x00\xcd\xff\x38\xff\xfe\xff\x8e\x00\xcb\xff\x3e\xff\xfe\xff\x88\x00\xcc\xff\x40\xff\xfa\xff\x89\x00\xcf\xff\x41\xff\xfa\xff\x87\x00\xd0\xff\x44\xff\xfa\xff\x89\x00\xd6\xff\x48\xff\xf9\xff\x88\x00\xdd\xff\x4d\xff\xf2\xff\x81\x00\xde\xff\x54\xff\xf4\xff\x7b\x00\xde\xff\x5c\xff\xf6\xff\x73\x00\xe0\xff\x65\xff\xf6\xff\x6d\x00\xe0\xff\x6f\xff\xf7\xff\x63\x00\xe0\xff\x78\xff\xf7\xff\x5d\x00\xde\xff\x76\xff\xf9\xff\x60\x00\xdf\xff\x7f\xff\xfb\xff\x5c\x00\xe8\xff\x85\xff\xfb\xff\x60\x00\xea\xff\x87\xff\xfe\xff\x63\x00\xee\xff\x8b\xff\x00\x00\x64\x00\xf3\xff\x8d\xff\xfe\xff\x66\x00\xf7\xff\x8f\xff\xfd\xff\x68\x00\xf9\xff\x8c\xff\xfd\xff\x6d\x00\xfc\xff\x8c\xff\xfd\xff\x71\x00\xff\xff\x89\xff\xfe\xff\x75\x00\x02\x00\x88\xff\xfc\xff\x78\x00\x03\x00\x87\xff\xfd\xff\x7b\x00\x03\x00\x86\xff\x00\x00\x7e\x00\x03\x00\x84\xff\xfe\xff\x81\x00\x07\x00\x82\xff\x01\x00\x84\x00\x03\x00\x82\xff\x05\x00\x88\x00\x05\x00\x81\xff\x04\x00\x88\x00\x05\x00\x80\xff\x05\x00\x8a\x00\x05\x00", + "octet-align=1"); + + do_test_wb( + "\xf4\x7c\x51\x86\x07\x74\x55\xc8\xc4\xd0\xbc\x00\x03\x14\x0c\xf7\x7f\xc2\xe6\x62\x4b\x1a\x14\xbe\x3e\x36\x56\x45\xb5\xd1\x79\xc0\x7b\x07\xff\x96\xf1\xbe\x40\x69\x2d\x78\x1b\x24\x50\x7f\x94\x8b\x33\x91\x2e\xd6\xb7\xdd\x8a\x7e\x36\xf2\x86\x35\x94", + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\xff\xff\x02\x00\xff\xff\xff\xff\x02\x00\xfd\xff\x03\x00\x00\x00\xfd\xff\x04\x00\xfc\xff\x02\x00\x01\x00\xfd\xff\x03\x00\xfe\xff\x01\x00\x01\x00\xff\xff\x01\x00\xff\xff\x01\x00\xff\xff\x00\x00\x01\x00\xff\xff\x00\x00\x00\x00\xff\xff\x00\x00\x01\x00\xfe\xff\x02\x00\xff\xff\xff\xff\x03\x00\xfd\xff\x03\x00\xff\xff\xff\xff\x03\x00\xfe\xff\x01\x00\x00\x00\xff\xff\x02\x00\xfe\xff\x01\x00\x00\x00\xff\xff\x02\x00\xfe\xff\x01\x00\x00\x00\xff\xff\x04\x00\xfe\xff\xfd\xff\x00\x00\x02\x00\xfe\xff\xf8\xff\x01\x00\x04\x00\xff\xff\xff\xff\xfc\xff\x06\x00\x00\x00\xf8\xff\x11\x00\x09\x00\x06\x00\x3f\x00\x37\x00\xf9\xff\x11\x00\x4e\x00\x34\x00\xf4\xff\x17\x00\x5d\x00\x31\x00\xe0\xff\x0b\x00\x71\x00\x42\x00\xd3\xff\x09\x00\x74\x00\x3c\x00\xc8\xff\x03\x00\x78\x00\x35\x00\xbc\xff\xff\xff\x7a\x00\x2e\x00\xb0\xff\x00\x00\x77\x00\x26\x00\xaa\xff\xfc\xff\x78\x00\x1c\x00\xa3\xff\xfe\xff\x72\x00\x14\x00\xa0\xff\xfc\xff\x6f\x00\x09\x00\x8e\xff\xfc\xff\x72\x00\xff\xff\x89\xff\xff\xff\x7e\x00\xfe\xff\x7b\xff\x19\x00\xa9\x00\xfa\xff\x62\xff\x14\x00\xae\x00\xf5\xff\x54\xff\x16\x00\xb6\x00\xe8\xff\x3f\xff\x0b\x00\xb9\x00\xee\xff\x34\xff\xfd\xff\xb8\x00\xe9\xff\x2d\xff\x00\x00\xb8\x00\xe4\xff\x2c\xff\xff\xff\xb9\x00\xdf\xff\x25\xff\xff\xff\xb2\x00\xda\xff\x28\xff\xfc\xff\xae\x00\xd6\xff\x2a\xff\xff\xff\xa5\x00\xd8\xff\x30\xff\xfc\xff\xa1\x00\xd5\xff\x35\xff\xf9\xff\x97\x00\xd4\xff\x37\xff\xfa\xff\x92\x00\xcd\xff\x38\xff\xfe\xff\x8e\x00\xcb\xff\x3e\xff\xfe\xff\x88\x00\xcc\xff\x40\xff\xfa\xff\x89\x00\xcf\xff\x41\xff\xfa\xff\x87\x00\xd0\xff\x44\xff\xfa\xff\x89\x00\xd6\xff\x48\xff\xf9\xff\x88\x00\xdd\xff\x4d\xff\xf2\xff\x81\x00\xde\xff\x54\xff\xf4\xff\x7b\x00\xde\xff\x5c\xff\xf6\xff\x73\x00\xe0\xff\x65\xff\xf6\xff\x6d\x00\xe0\xff\x6f\xff\xf7\xff\x63\x00\xe0\xff\x78\xff\xf7\xff\x5d\x00\xde\xff\x76\xff\xf9\xff\x60\x00\xdf\xff\x7f\xff\xfb\xff\x5c\x00\xe8\xff\x85\xff\xfb\xff\x60\x00\xea\xff\x87\xff\xfe\xff\x63\x00\xee\xff\x8b\xff\x00\x00\x64\x00\xf3\xff\x8d\xff\xfe\xff\x66\x00\xf7\xff\x8f\xff\xfd\xff\x68\x00\xf9\xff\x8c\xff\xfd\xff\x6d\x00\xfc\xff\x8c\xff\xfd\xff\x71\x00\xff\xff\x89\xff\xfe\xff\x75\x00\x02\x00\x88\xff\xfc\xff\x78\x00\x03\x00\x87\xff\xfd\xff\x7b\x00\x03\x00\x86\xff\x00\x00\x7e\x00\x03\x00\x84\xff\xfe\xff\x81\x00\x07\x00\x82\xff\x01\x00\x84\x00\x03\x00\x82\xff\x05\x00\x88\x00\x05\x00\x81\xff\x04\x00\x88\x00\x05\x00\x80\xff\x05\x00\x8a\x00\x05\x00", + NULL); + + do_test_wb( + "\xf4\x7c\x51\x86\x07\x74\x55\xc8\xc4\xd0\xbc\x00\x03\x14\x0c\xf7\x7f\xc2\xe6\x62\x4b\x1a\x14\xbe\x3e\x36\x56\x45\xb5\xd1\x79\xc0\x7b\x07\xff\x96\xf1\xbe\x40\x69\x2d\x78\x1b\x24\x50\x7f\x94\x8b\x33\x91\x2e\xd6\xb7\xdd\x8a\x7e\x36\xf2\x86\x35\x94", + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\xff\xff\x02\x00\xff\xff\xff\xff\x02\x00\xfd\xff\x03\x00\x00\x00\xfd\xff\x04\x00\xfc\xff\x02\x00\x01\x00\xfd\xff\x03\x00\xfe\xff\x01\x00\x01\x00\xff\xff\x01\x00\xff\xff\x01\x00\xff\xff\x00\x00\x01\x00\xff\xff\x00\x00\x00\x00\xff\xff\x00\x00\x01\x00\xfe\xff\x02\x00\xff\xff\xff\xff\x03\x00\xfd\xff\x03\x00\xff\xff\xff\xff\x03\x00\xfe\xff\x01\x00\x00\x00\xff\xff\x02\x00\xfe\xff\x01\x00\x00\x00\xff\xff\x02\x00\xfe\xff\x01\x00\x00\x00\xff\xff\x04\x00\xfe\xff\xfd\xff\x00\x00\x02\x00\xfe\xff\xf8\xff\x01\x00\x04\x00\xff\xff\xff\xff\xfc\xff\x06\x00\x00\x00\xf8\xff\x11\x00\x09\x00\x06\x00\x3f\x00\x37\x00\xf9\xff\x11\x00\x4e\x00\x34\x00\xf4\xff\x17\x00\x5d\x00\x31\x00\xe0\xff\x0b\x00\x71\x00\x42\x00\xd3\xff\x09\x00\x74\x00\x3c\x00\xc8\xff\x03\x00\x78\x00\x35\x00\xbc\xff\xff\xff\x7a\x00\x2e\x00\xb0\xff\x00\x00\x77\x00\x26\x00\xaa\xff\xfc\xff\x78\x00\x1c\x00\xa3\xff\xfe\xff\x72\x00\x14\x00\xa0\xff\xfc\xff\x6f\x00\x09\x00\x8e\xff\xfc\xff\x72\x00\xff\xff\x89\xff\xff\xff\x7e\x00\xfe\xff\x7b\xff\x19\x00\xa9\x00\xfa\xff\x62\xff\x14\x00\xae\x00\xf5\xff\x54\xff\x16\x00\xb6\x00\xe8\xff\x3f\xff\x0b\x00\xb9\x00\xee\xff\x34\xff\xfd\xff\xb8\x00\xe9\xff\x2d\xff\x00\x00\xb8\x00\xe4\xff\x2c\xff\xff\xff\xb9\x00\xdf\xff\x25\xff\xff\xff\xb2\x00\xda\xff\x28\xff\xfc\xff\xae\x00\xd6\xff\x2a\xff\xff\xff\xa5\x00\xd8\xff\x30\xff\xfc\xff\xa1\x00\xd5\xff\x35\xff\xf9\xff\x97\x00\xd4\xff\x37\xff\xfa\xff\x92\x00\xcd\xff\x38\xff\xfe\xff\x8e\x00\xcb\xff\x3e\xff\xfe\xff\x88\x00\xcc\xff\x40\xff\xfa\xff\x89\x00\xcf\xff\x41\xff\xfa\xff\x87\x00\xd0\xff\x44\xff\xfa\xff\x89\x00\xd6\xff\x48\xff\xf9\xff\x88\x00\xdd\xff\x4d\xff\xf2\xff\x81\x00\xde\xff\x54\xff\xf4\xff\x7b\x00\xde\xff\x5c\xff\xf6\xff\x73\x00\xe0\xff\x65\xff\xf6\xff\x6d\x00\xe0\xff\x6f\xff\xf7\xff\x63\x00\xe0\xff\x78\xff\xf7\xff\x5d\x00\xde\xff\x76\xff\xf9\xff\x60\x00\xdf\xff\x7f\xff\xfb\xff\x5c\x00\xe8\xff\x85\xff\xfb\xff\x60\x00\xea\xff\x87\xff\xfe\xff\x63\x00\xee\xff\x8b\xff\x00\x00\x64\x00\xf3\xff\x8d\xff\xfe\xff\x66\x00\xf7\xff\x8f\xff\xfd\xff\x68\x00\xf9\xff\x8c\xff\xfd\xff\x6d\x00\xfc\xff\x8c\xff\xfd\xff\x71\x00\xff\xff\x89\xff\xfe\xff\x75\x00\x02\x00\x88\xff\xfc\xff\x78\x00\x03\x00\x87\xff\xfd\xff\x7b\x00\x03\x00\x86\xff\x00\x00\x7e\x00\x03\x00\x84\xff\xfe\xff\x81\x00\x07\x00\x82\xff\x01\x00\x84\x00\x03\x00\x82\xff\x05\x00\x88\x00\x05\x00\x81\xff\x04\x00\x88\x00\x05\x00\x80\xff\x05\x00\x8a\x00\x05\x00", + ""); + + do_test_nb( + "\xf0\x3c\x53\xff\x3a\xe8\x30\x41\xa5\xa8\xa4\x1d\x2f\xf2\x03\x60\x35\xc0\x00\x07\xc5\x53\xf4\xbc\x98\x00\x01\x14\x2f\xf0\x00\x0f\x70", + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\xff\xf8\xff\x10\x00\x00\x00\xe8\xff\xf8\xff\x28\x00\x00\x00\xc8\xff\x00\x00\x38\x00\xf8\xff\xb0\xff\x08\x00\x48\x00\xf0\xff\xa8\xff\x08\x00\x60\x00\xf8\xff\x98\xff\x08\x00\x88\x00\xf0\xff\x88\xff\x10\x00\x88\x00\xe8\xff\x70\xff\x18\x00\x80\x00\xd8\xff\x70\xff\x08\x00\x78\x00\xd8\xff\x88\xff\x08\x00\x78\x00\xe8\xff\x90\xff\x10\x00\x70\x00\xe0\xff\x90\xff\x08\x00\x68\x00\xf8\xff\x88\xff\x00\x00\x80\x00\xf8\xff\x88\xff\x00\x00\x80\x00\xf8\xff\x78\xff\x08\x00\x80\x00\xe8\xff\x78\xff\x08\x00\x78\x00\xe0\xff\x88\xff\x10\x00\x70\x00\xe8\xff\x90\xff\x10\x00\x68\x00\xe0\xff\x98\xff\x08\x00\x70\x00\xe8\xff\xa8\xff\xf8\xff\x70\x00\xf0\xff\x98\xff\xf8\xff\x68\x00\x00\x00\x80\xff\xf8\xff\x68\x00\xf8\xff\x90\xff\x00\x00\x70\x00\xf8\xff\x90\xff\x00\x00\x78\x00\xf0\xff\x88\xff\x00\x00\x80\x00\xf8\xff\x80\xff\x00\x00\x88\x00\xf8\xff\x80\xff\xf8\xff\x88\x00\xf8\xff\x70\xff\x00\x00\x80\x00\xf0\xff\x80\xff\xf8\xff\x80\x00\xe8\xff\x80\xff\xf0\xff\x88\x00", + "octet-align=1"); + + return 0; +} diff --git a/t/amr-encode-test.c b/t/amr-encode-test.c new file mode 100644 index 000000000..e526745aa --- /dev/null +++ b/t/amr-encode-test.c @@ -0,0 +1,147 @@ +#include "codeclib.h" +#include "str.h" +#include + +static void hexdump(const unsigned char *buf, int len) { + for (int i = 0; i < len; i++) + printf("%02x", buf[i]); + printf("\n"); +} + +static int dec_cb(encoder_t *e, void *u1, void *u2) { + char **expect = u1; + int *expect_len = u2; + assert(expect); + assert(expect_len); + assert(*expect); + + GString *buf = g_string_new(""); + int plen = 256; + char payload[plen]; + str inout = { payload, plen }; + e->def->packetizer(&e->avpkt, buf, &inout, e); + + if (inout.len != *expect_len + || memcmp(inout.s, *expect, *expect_len)) + { + printf( + "packet content mismatch\n" + "expected %i bytes, received %i bytes\n" + "expected:\n", + *expect_len, inout.len); + hexdump((unsigned char *) *expect, *expect_len); + printf("received:\n"); + hexdump((unsigned char *) inout.s, inout.len); + exit(1); + } + + *expect = NULL; + *expect_len = 0; + + g_string_free(buf, TRUE); + return 0; +} + +static void do_test_amr_xx(const char *file, int line, + char *fmtp_s, char *data_s, int data_len, char *expect_s, int expect_len, + int bitrate, char *codec, int clockrate) +{ + printf("running test %s:%i\n", file, line); + str codec_name; + str_init(&codec_name, codec); + const codec_def_t *def = codec_find(&codec_name, MT_AUDIO); + assert(def); + if (!def->support_encoding || !def->support_decoding) { + printf("AMR not fully supported - skipping test\n"); + exit(0); + } + const format_t fmt = { .clockrate = clockrate, .channels = 1, .format = 0 }; + str fmtp_str, *fmtp = NULL; + char *fmtp_buf = NULL; + if (fmtp_s) { + fmtp_buf = strdup(fmtp_s); + str_init(&fmtp_str, fmtp_buf); + fmtp = &fmtp_str; + } + encoder_t *e = encoder_new(); + assert(e); + format_t actual_fmt; + int ret = encoder_config_fmtp(e, def, bitrate, 20, &fmt, &actual_fmt, fmtp); + assert(actual_fmt.clockrate == clockrate); + assert(actual_fmt.channels == 1); + assert(actual_fmt.format == AV_SAMPLE_FMT_S16); + + AVFrame *frame = av_frame_alloc(); + assert(frame); + frame->nb_samples = 20 * clockrate / 1000; + frame->format = actual_fmt.format; + frame->sample_rate = actual_fmt.clockrate; + frame->channel_layout = av_get_default_channel_layout(actual_fmt.channels); + ret = av_frame_get_buffer(frame, 0); + assert(ret >= 0); + + assert(data_len == frame->nb_samples * 2); + memcpy(frame->data[0], data_s, data_len); + + ret = encoder_input_data(e, frame, dec_cb, &expect_s, &expect_len); + assert(!ret); + assert(expect_s == NULL); + + encoder_free(e); + free(fmtp_buf); + + printf("test ok: %s:%i\n", file, line); +} + +static void do_test_amr_wb(const char *file, int line, + char *fmtp_s, char *data_s, int data_len, char *expect_s, int expect_len, + int bitrate) +{ + do_test_amr_xx(file, line, fmtp_s, data_s, data_len, expect_s, expect_len, bitrate, + "AMR-WB", 16000); +} +static void do_test_amr_nb(const char *file, int line, + char *fmtp_s, char *data_s, int data_len, char *expect_s, int expect_len, + int bitrate) +{ + do_test_amr_xx(file, line, fmtp_s, data_s, data_len, expect_s, expect_len, bitrate, + "AMR", 8000); +} + +#define do_test_wb(in, out, fmt, bitrate) \ + do_test_amr_wb(__FILE__, __LINE__, fmt, in, sizeof(in)-1, out, sizeof(out)-1, bitrate) +#define do_test_nb(in, out, fmt, bitrate) \ + do_test_amr_nb(__FILE__, __LINE__, fmt, in, sizeof(in)-1, out, sizeof(out)-1, bitrate) + +#define test_320_samples_16_bits \ + "\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01" +#define test_160_samples_16_bits \ + "\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01\x00\x00\x01\x00\x01\x00\x01\x01" + +int main() { + codeclib_init(0); + + do_test_wb( + test_320_samples_16_bits, + "\xf0\x44\xf1\x46\x18\x1d\xd1\x57\x23\x13\x42\xf0\x00\x0c\x50\x33\xdd\xff\x0b\x99\x89\x2c\x68\x52\xf8\xf8\xd9\x59\x16\xd7\x45\xe7\x01\xec\x1f\xfe\x5b\xc6\xf9\x01\xa4\xb5\xe0\x6c\x91\x41\xfe\x52\x2c\xce\x44\xbb\x5a\xdf\x76\x29\xf8\xdb\xca\x18\xd6\x50", + "octet-align=1", + 23850); + do_test_wb( + test_320_samples_16_bits, + "\xf0\x00\x44\xf1\x46\x18\x1d\xd1\x57\x23\x13\x42\xf0\x00\x0c\x50\x33\xdd\xff\x0b\x99\x89\x2c\x68\x52\xf8\xf8\xd9\x59\x16\xd7\x45\xe7\x01\xec\x1f\xfe\x5b\xc6\xf9\x01\xa4\xb5\xe0\x6c\x91\x41\xfe\x52\x2c\xce\x44\xbb\x5a\xdf\x76\x29\xf8\xdb\xca\x18\xd6\x50", + "octet-align=1;interleaving=4", + 23850); + do_test_wb( + test_320_samples_16_bits, + "\xf4\x7c\x51\x86\x07\x74\x55\xc8\xc4\xd0\xbc\x00\x03\x14\x0c\xf7\x7f\xc2\xe6\x62\x4b\x1a\x14\xbe\x3e\x36\x56\x45\xb5\xd1\x79\xc0\x7b\x07\xff\x96\xf1\xbe\x40\x69\x2d\x78\x1b\x24\x50\x7f\x94\x8b\x33\x91\x2e\xd6\xb7\xdd\x8a\x7e\x36\xf2\x86\x35\x94", + NULL, + 23850); + + do_test_nb( + test_160_samples_16_bits, + "\xf0\x3c\x53\xff\x3a\xe8\x30\x41\xa5\xa8\xa4\x1d\x2f\xf2\x03\x60\x35\xc0\x00\x07\xc5\x53\xf4\xbc\x98\x00\x01\x14\x2f\xf0\x00\x0f\x70", + "octet-align=1", + 12200); + + return 0; +} diff --git a/t/bitstr-test.c b/t/bitstr-test.c new file mode 100644 index 000000000..68008d5c8 --- /dev/null +++ b/t/bitstr-test.c @@ -0,0 +1,204 @@ +#include +#include +#include +#include +#include "bitstr.h" +#include "str.h" + +#define do_test_ret(retval, args...) do { \ + int r = do_test(args); \ + if (r != retval) \ + err("didn't run all tests!\n"); \ + } while (0) +#define test1(input, shift_len, output, result) \ + do_test_ret(1, input, sizeof(input)-1, __FILE__, __LINE__, shift_len, output, sizeof(output)-1, \ + result, 0) +#define test2(input, shift_len1, output1, result1, shift_len2, output2, result2) \ + do_test_ret(2, input, sizeof(input)-1, __FILE__, __LINE__, \ + shift_len1, output1, sizeof(output1)-1, result1, \ + shift_len2, output2, sizeof(output2)-1, result2, \ + 0) +#define test3(input, \ + shift_len1, output1, result1, \ + shift_len2, output2, result2, \ + shift_len3, output3, result3) \ + do_test_ret(3, input, sizeof(input)-1, __FILE__, __LINE__, \ + shift_len1, output1, sizeof(output1)-1, result1, \ + shift_len2, output2, sizeof(output2)-1, result2, \ + shift_len3, output3, sizeof(output3)-1, result3, \ + 0) + +#define err(fmt...) do { \ + fprintf(stderr, fmt); \ + exit(1); \ + } while (0) + +int do_test(const char *input, unsigned int input_len, + const char *file, unsigned int line, + ...) +{ + char in_buf[input_len]; + memcpy(in_buf, input, input_len); + str inp; + str_init_len(&inp, in_buf, input_len); + bitstr inp_bs; + bitstr_init(&inp_bs, &inp); + + va_list ap; + va_start(ap, line); + int argc = 0; + + while (1) { + unsigned int shift_len = va_arg(ap, unsigned int); + if (!shift_len) + break; + const char *output = va_arg(ap, const char *); + unsigned int output_len = va_arg(ap, unsigned int); + int result = va_arg(ap, int); + + char out_buf[output_len+1]; + str outp = STR_CONST_INIT_BUF(out_buf); + + int ret; + if (output) + ret = bitstr_shift_ret(&inp_bs, shift_len, &outp); + else + ret = bitstr_shift(&inp_bs, shift_len); + + if (ret != result) + err("ERROR return %i instead of %i (%s:%i arg %i)\n", + ret, result, file, line, argc); + if (ret == 0 && output) { + if (outp.len != output_len) + err("ERROR output len %i instead of %i (%s:%i arg %i)\n", + outp.len, output_len, file, line, argc); + if (memcmp(outp.s, output, output_len)) + err("ERROR output string mismatch (%s:%i arg %i)\n", + file, line, argc); + } +// if (inp.len != remainder_len) +// err("ERROR remainder len %i instead of %i (%s:%i arg %i)\n", +// inp.len, remainder_len, file, line, argc); + + printf("test ok: %s:%i arg %i\n", file, line, argc); + argc++; + } + + return argc; +} + +int main() { + test1("\x81", 8, "\x81", 0); + test2("\x81", 8, "\x81", 0, 1, "", -1); + test2("\x81", 8, "\x81", 0, 1, NULL, -1); + + test1("\x81", 7, "\x80", 0); + test2("\x81", 7, "\x80", 0, 1, "\x80", 0); + test3("\x81", 7, "\x80", 0, 1, "\x80", 0, 1, "", -1); + test3("\x81", 7, "\x80", 0, 1, NULL, 0, 1, "", -1); + test3("\x81", 7, "\x80", 0, 1, "\x80", 0, 1, NULL, -1); + test3("\x81", 7, "\x80", 0, 1, NULL, 0, 1, NULL, -1); + test2("\x81", 7, "\x80", 0, 2, "", -1); + test2("\x81", 7, "\x80", 0, 2, NULL, -1); + + test1("\x82", 7, "\x82", 0); + test2("\x82", 7, "\x82", 0, 1, "\x00", 0); + test2("\x82", 7, NULL, 0, 1, "\x00", 0); + test3("\x82", 7, "\x82", 0, 1, "\x00", 0, 1, "", -1); + test3("\x82", 7, "\x82", 0, 1, NULL, 0, 1, "", -1); + test3("\x82", 7, "\x82", 0, 1, "\x00", 0, 1, NULL, -1); + test3("\x82", 7, "\x82", 0, 1, NULL, 0, 1, NULL, -1); + test2("\x82", 7, "\x82", 0, 2, "", -1); + test2("\x82", 7, "\x82", 0, 2, NULL, -1); + + test1("\x83", 7, "\x82", 0); + test2("\x83", 7, "\x82", 0, 1, "\x80", 0); + test2("\x83", 7, NULL, 0, 1, "\x80", 0); + test3("\x83", 7, "\x82", 0, 1, "\x80", 0, 1, "", -1); + test3("\x83", 7, "\x82", 0, 1, NULL, 0, 1, "", -1); + test2("\x83", 7, "\x82", 0, 2, "", -1); + + test1("\x81", 1, "\x80", 0); + test2("\x81", 1, "\x80", 0, 7, "\x02", 0); + test3("\x81", 1, "\x80", 0, 7, "\x02", 0, 1, "", -1); + test3("\x81", 1, NULL, 0, 7, "\x02", 0, 1, "", -1); + test3("\x81", 1, "\x80", 0, 7, NULL, 0, 1, "", -1); + test3("\x81", 1, NULL, 0, 7, NULL, 0, 1, "", -1); + + test1("\xff", 1, "\x80", 0); + test2("\xff", 1, "\x80", 0, 5, "\xf8", 0); + test3("\xff", 1, "\x80", 0, 5, "\xf8", 0, 2, "\xc0", 0); + test3("\xff", 1, NULL, 0, 5, "\xf8", 0, 2, "\xc0", 0); + test3("\xff", 1, "\x80", 0, 5, NULL, 0, 2, "\xc0", 0); + test3("\xff", 1, NULL, 0, 5, NULL, 0, 2, "\xc0", 0); + test3("\xff", 1, "\x80", 0, 5, "\xf8", 0, 3, "", -1); + test2("\xff", 1, "\x80", 0, 7, "\xfe", 0); + test3("\xff", 1, "\x80", 0, 7, "\xfe", 0, 1, "", -1); + test3("\xff", 1, NULL, 0, 7, "\xfe", 0, 1, "", -1); + test3("\xff", 1, "\x80", 0, 7, NULL, 0, 1, "", -1); + test3("\xff", 1, NULL, 0, 7, NULL, 0, 1, "", -1); + + test1("J76x", 8, "J", 0); + + test2("J76x", 8, "J", 0, 8, "7", 0); + test3("J76x", 8, "J", 0, 8, "7", 0, 7, "6", 0); + test3("J76x", 8, "J", 0, 8, "7", 0, 14, "6x", 0); + test3("J76x", 8, "J", 0, 8, "7", 0, 16, "6x", 0); + test3("J76x", 8, "J", 0, 8, "7", 0, 17, "", -1); + + test2("J76x", 8, "J", 0, 12, "70", 0); + test3("J76x", 8, "J", 0, 12, "70", 0, 3, "`", 0); + test3("J76x", 8, "J", 0, 12, "70", 0, 6, "d", 0); + test3("J76x", 8, "J", 0, 12, "70", 0, 8, "g", 0); + test3("J76x", 8, "J", 0, 12, "70", 0, 12, "g\x80", 0); + test3("J76x", 8, NULL, 0, 12, "70", 0, 12, "g\x80", 0); + test3("J76x", 8, "J", 0, 12, NULL, 0, 12, "g\x80", 0); + test3("J76x", 8, NULL, 0, 12, NULL, 0, 12, "g\x80", 0); + test3("J76x", 8, "J", 0, 12, "70", 0, 13, "", -1); + + test2("J76x", 8, "J", 0, 14, "74", 0); + test3("J76x", 8, "J", 0, 14, "74", 0, 5, "\x98", 0); + test3("J76x", 8, NULL, 0, 14, "74", 0, 5, "\x98", 0); + test3("J76x", 8, "J", 0, 14, NULL, 0, 5, "\x98", 0); + test3("J76x", 8, NULL, 0, 14, NULL, 0, 5, "\x98", 0); + test3("J76x", 8, "J", 0, 14, "74", 0, 8, "\x9e", 0); + test3("J76x", 8, NULL, 0, 14, "74", 0, 8, "\x9e", 0); + test3("J76x", 8, "J", 0, 14, NULL, 0, 8, "\x9e", 0); + test3("J76x", 8, NULL, 0, 14, NULL, 0, 8, "\x9e", 0); + + test1("J76x", 12, "J0", 0); + test2("J76x", 12, "J0", 0, 3, "`", 0); + test3("J76x", 12, "J0", 0, 3, "`", 0, 3, "\x80", 0); + test3("J76x", 12, "J0", 0, 3, "`", 0, 6, "\x98", 0); + + test2("J76x", 12, "J0", 0, 4, "p", 0); + test2("J76x", 12, "J0", 0, 4, "p", 0); + test3("J76x", 12, "J0", 0, 4, "p", 0, 3, "\x20", 0); + test3("J76x", 12, "J0", 0, 4, "p", 0, 6, "\x34", 0); + + test2("J76x", 12, "J0", 0, 6, "p", 0); + test2("J76x", 12, "J0", 0, 6, "p", 0); + test3("J76x", 12, "J0", 0, 6, "p", 0, 3, "\xc0", 0); + test3("J76x", 12, "J0", 0, 6, "p", 0, 6, "\xd8", 0); + + test2("J76x", 12, "J0", 0, 8, "s", 0); + test2("J76x", 12, "J0", 0, 8, "s", 0); + test3("J76x", 12, "J0", 0, 8, "s", 0, 3, "\x60", 0); + test3("J76x", 12, "J0", 0, 8, "s", 0, 6, "\x64", 0); + + test2("J76x", 12, "J0", 0, 11, "s`", 0); + test2("J76x", 12, "J0", 0, 11, "s`", 0); + test3("J76x", 12, "J0", 0, 11, "s`", 0, 3, "\x20", 0); + test3("J76x", 12, "J0", 0, 11, "s`", 0, 6, "\x3c", 0); + + test2("J76x", 12, "J0", 0, 18, "sg\x80", 0); + test2("J76x", 12, "J0", 0, 18, "sg\x80", 0); + test3("J76x", 12, "J0", 0, 18, "sg\x80", 0, 2, "\x00", 0); + test3("J76x", 12, "J0", 0, 18, NULL, 0, 2, "\x00", 0); + test3("J76x", 12, NULL, 0, 18, "sg\x80", 0, 2, "\x00", 0); + test3("J76x", 12, NULL, 0, 18, NULL, 0, 2, "\x00", 0); + test3("J76x", 12, "J0", 0, 18, "sg\x80", 0, 3, NULL, -1); + + // non octet aligned AMR + test3("\xf0\xde\xc0\x81\xc0\x08\xa9\xbc\x06\x33\x53\x14\x69\xdd\x3d\x2e\xa9\x8f\x81\xee\x2e\x09\x08\x80\xca\x05\x1e\x91\x00\x10\x00\x00\xca\x05\x20\x91\x00\x10\x00\x00\xca\x05\x22\x91\x00\x10\x00\x00\xca\x05\x24\x91\x00\x10\x00\x00\xca\x05\x26\x91\x00\x10", 4, "\xf0", 0, 6, "\x0c", 0, 177, "\x7b\x02\x07\x00\x22\xa6\xf0\x18\xcd\x4c\x51\xa7\x74\xf4\xba\xa6\x3e\x07\xb8\xb8\x24\x22\x00", 0); +} diff --git a/t/log.h b/t/log.h new file mode 100644 index 000000000..38b8d34c8 --- /dev/null +++ b/t/log.h @@ -0,0 +1,7 @@ +#ifndef __LOG_H__ +#define __LOG_H__ + +#include "loglib.h" +#define __ilog(prio, fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__) + +#endif