From 0f2f469cc04d64b635fcead4f554962d5fb3fd25 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Mon, 6 Feb 2023 12:40:54 +0100 Subject: [PATCH 1/8] merge rem into re --- CMakeLists.txt | 57 +++ include/rem.h | 25 ++ include/rem_aac.h | 15 + include/rem_au.h | 22 ++ include/rem_aubuf.h | 104 +++++ include/rem_auconv.h | 13 + include/rem_audio.h | 21 + include/rem_aufile.h | 26 ++ include/rem_auframe.h | 48 +++ include/rem_aulevel.h | 13 + include/rem_aumix.h | 35 ++ include/rem_auresamp.h | 34 ++ include/rem_autone.h | 10 + include/rem_avc.h | 23 ++ include/rem_dsp.h | 137 +++++++ include/rem_dtmf.h | 21 + include/rem_fir.h | 15 + include/rem_flv.h | 56 +++ include/rem_g711.h | 77 ++++ include/rem_goertzel.h | 32 ++ include/rem_vid.h | 152 +++++++ include/rem_vidconv.h | 11 + include/rem_video.h | 11 + include/rem_vidmix.h | 40 ++ rem/aac/aac.c | 69 ++++ rem/au/fmt.c | 40 ++ rem/au/util.c | 23 ++ rem/aubuf/ajb.c | 365 +++++++++++++++++ rem/aubuf/ajb.h | 21 + rem/aubuf/aubuf.c | 567 ++++++++++++++++++++++++++ rem/auconv/auconv.c | 140 +++++++ rem/aufile/aufile.c | 230 +++++++++++ rem/aufile/aufile.h | 27 ++ rem/aufile/wave.c | 205 ++++++++++ rem/auframe/auframe.c | 116 ++++++ rem/aulevel/aulevel.c | 138 +++++++ rem/aumix/aumix.c | 532 +++++++++++++++++++++++++ rem/auresamp/resamp.c | 340 ++++++++++++++++ rem/autone/tone.c | 98 +++++ rem/avc/config.c | 102 +++++ rem/dtmf/dec.c | 193 +++++++++ rem/fir/fir.c | 67 ++++ rem/g711/g711.c | 853 ++++++++++++++++++++++++++++++++++++++++ rem/goertzel/goertzel.c | 60 +++ rem/vid/draw.c | 250 ++++++++++++ rem/vid/fmt.c | 40 ++ rem/vid/frame.c | 467 ++++++++++++++++++++++ rem/vidconv/vconv.c | 826 ++++++++++++++++++++++++++++++++++++++ rem/vidmix/vidmix.c | 731 ++++++++++++++++++++++++++++++++++ 49 files changed, 7498 insertions(+) create mode 100644 include/rem.h create mode 100644 include/rem_aac.h create mode 100644 include/rem_au.h create mode 100644 include/rem_aubuf.h create mode 100644 include/rem_auconv.h create mode 100644 include/rem_audio.h create mode 100644 include/rem_aufile.h create mode 100644 include/rem_auframe.h create mode 100644 include/rem_aulevel.h create mode 100644 include/rem_aumix.h create mode 100644 include/rem_auresamp.h create mode 100644 include/rem_autone.h create mode 100644 include/rem_avc.h create mode 100644 include/rem_dsp.h create mode 100644 include/rem_dtmf.h create mode 100644 include/rem_fir.h create mode 100644 include/rem_flv.h create mode 100644 include/rem_g711.h create mode 100644 include/rem_goertzel.h create mode 100644 include/rem_vid.h create mode 100644 include/rem_vidconv.h create mode 100644 include/rem_video.h create mode 100644 include/rem_vidmix.h create mode 100644 rem/aac/aac.c create mode 100644 rem/au/fmt.c create mode 100644 rem/au/util.c create mode 100644 rem/aubuf/ajb.c create mode 100644 rem/aubuf/ajb.h create mode 100644 rem/aubuf/aubuf.c create mode 100644 rem/auconv/auconv.c create mode 100644 rem/aufile/aufile.c create mode 100644 rem/aufile/aufile.h create mode 100644 rem/aufile/wave.c create mode 100644 rem/auframe/auframe.c create mode 100644 rem/aulevel/aulevel.c create mode 100644 rem/aumix/aumix.c create mode 100644 rem/auresamp/resamp.c create mode 100644 rem/autone/tone.c create mode 100644 rem/avc/config.c create mode 100644 rem/dtmf/dec.c create mode 100644 rem/fir/fir.c create mode 100644 rem/g711/g711.c create mode 100644 rem/goertzel/goertzel.c create mode 100644 rem/vid/draw.c create mode 100644 rem/vid/fmt.c create mode 100644 rem/vid/frame.c create mode 100644 rem/vidconv/vconv.c create mode 100644 rem/vidmix/vidmix.c diff --git a/CMakeLists.txt b/CMakeLists.txt index d0834e88c..1cba8165e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,7 @@ include(CheckCCompilerFlag) # Options # +option(USE_REM "Enable Librem" ON) option(USE_BFCP "Enable BFCP" ON) option(USE_JBUF "Enable JBUF" ON) option(USE_PCP "Enable PCP" ON) @@ -174,6 +175,32 @@ set(HEADERS include/re_websock.h ) +set(REM_HEADERS + include/rem_aac.h + include/rem_aubuf.h + include/rem_auconv.h + include/rem_audio.h + include/rem_aufile.h + include/rem_auframe.h + include/rem_au.h + include/rem_aulevel.h + include/rem_aumix.h + include/rem_auresamp.h + include/rem_autone.h + include/rem_avc.h + include/rem_dsp.h + include/rem_dtmf.h + include/rem_fir.h + include/rem_flv.h + include/rem_g711.h + include/rem_goertzel.h + include/rem.h + include/rem_vidconv.h + include/rem_video.h + include/rem_vid.h + include/rem_vidmix.h +) + if(USE_UNIXSOCK) list(APPEND HEADERS include/re_unixsock.h @@ -371,6 +398,32 @@ set(SRCS src/websock/websock.c ) +set(REM_SRCS + rem/aac/aac.c + rem/au/fmt.c + rem/au/util.c + rem/aubuf/aubuf.c + rem/aubuf/ajb.c + rem/auconv/auconv.c + rem/aufile/aufile.c + rem/aufile/wave.c + rem/auframe/auframe.c + rem/aulevel/aulevel.c + rem/aumix/aumix.c + rem/auresamp/resamp.c + rem/autone/tone.c + rem/avc/config.c + rem/dtmf/dec.c + rem/fir/fir.c + rem/g711/g711.c + rem/goertzel/goertzel.c + rem/vid/draw.c + rem/vid/fmt.c + rem/vid/frame.c + rem/vidconv/vconv.c + rem/vidmix/vidmix.c +) + if(USE_UNIXSOCK) list(APPEND SRCS src/unixsock/unixsock.c @@ -534,6 +587,10 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Android") ) endif() +if(USE_REM) + list(APPEND SRCS ${REM_SRCS}) +endif() + ############################################################################## # diff --git a/include/rem.h b/include/rem.h new file mode 100644 index 000000000..3a67b595b --- /dev/null +++ b/include/rem.h @@ -0,0 +1,25 @@ +/** + * @file rem.h Wrapper for librem headers + * + * Copyright (C) 2010 Creytiv.com + */ + +#ifndef REM_H__ +#define REM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +#include "rem_audio.h" +#include "rem_video.h" +#include "rem_dsp.h" +#include "rem_flv.h" + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/rem_aac.h b/include/rem_aac.h new file mode 100644 index 000000000..f6e9b2ce6 --- /dev/null +++ b/include/rem_aac.h @@ -0,0 +1,15 @@ +/** + * @file rem_aac.h Advanced Audio Coding + * + * Copyright (C) 2010 Creytiv.com + */ + + +/** Defines the AAC header */ +struct aac_header { + unsigned sample_rate; /**< Audio sample rate in [Hz] */ + unsigned channels; /**< Number of audio channels */ + unsigned frame_size; /**< Frame size, 960 or 1024 bits */ +}; + +int aac_header_decode(struct aac_header *hdr, const uint8_t *p, size_t len); diff --git a/include/rem_au.h b/include/rem_au.h new file mode 100644 index 000000000..8174acb93 --- /dev/null +++ b/include/rem_au.h @@ -0,0 +1,22 @@ +/** + * @file rem_au.h Basic audio types + * + * Copyright (C) 2010 Creytiv.com + */ + + +/** Audio formats */ +enum aufmt { + AUFMT_S16LE, /**< Signed 16-bit PCM */ + AUFMT_S32LE, /**< Signed 32-bit PCM */ + AUFMT_PCMA, /**< G.711 A-law */ + AUFMT_PCMU, /**< G.711 U-law */ + AUFMT_FLOAT, /**< Float 32 bit (CPU endian) */ + AUFMT_S24_3LE,/**< Signed 24bit Little Endian in 3bytes format */ + AUFMT_RAW, /**< RAW PCM */ +}; + +size_t aufmt_sample_size(enum aufmt fmt); +const char *aufmt_name(enum aufmt fmt); + +uint32_t calc_nsamp(uint32_t srate, uint8_t channels, uint16_t ptime); diff --git a/include/rem_aubuf.h b/include/rem_aubuf.h new file mode 100644 index 000000000..32ed40677 --- /dev/null +++ b/include/rem_aubuf.h @@ -0,0 +1,104 @@ +/** + * @file rem_aubuf.h Audio Buffer + * + * Copyright (C) 2010 Creytiv.com + */ +struct aubuf; + +enum aubuf_mode { + AUBUF_FIXED, + AUBUF_ADAPTIVE +}; + +int aubuf_alloc(struct aubuf **abp, size_t min_sz, size_t max_sz); +void aubuf_set_live(struct aubuf *ab, bool live); +void aubuf_set_mode(struct aubuf *ab, enum aubuf_mode mode); +void aubuf_set_silence(struct aubuf *ab, double silence); +int aubuf_resize(struct aubuf *ab, size_t min_sz, size_t max_sz); +int aubuf_write_auframe(struct aubuf *ab, const struct auframe *af); +int aubuf_append_auframe(struct aubuf *ab, struct mbuf *mb, + const struct auframe *af); +void aubuf_read_auframe(struct aubuf *ab, struct auframe *af); +void aubuf_sort_auframe(struct aubuf *ab); +int aubuf_get(struct aubuf *ab, uint32_t ptime, uint8_t *p, size_t sz); +void aubuf_flush(struct aubuf *ab); +int aubuf_debug(struct re_printf *pf, const struct aubuf *ab); +size_t aubuf_cur_size(const struct aubuf *ab); +void aubuf_drop_auframe(struct aubuf *ab, const struct auframe *af); + + +static inline int aubuf_append(struct aubuf *ab, struct mbuf *mb) +{ + return aubuf_append_auframe(ab, mb, NULL); +} + + +static inline int aubuf_get_samp(struct aubuf *ab, uint32_t ptime, + int16_t *sampv, size_t sampc) +{ + return aubuf_get(ab, ptime, (uint8_t *)sampv, sampc * 2); +} + + +#ifndef __cplusplus +static inline int aubuf_write(struct aubuf *ab, const uint8_t *p, size_t sz) +{ + struct auframe af = { + .fmt = AUFMT_RAW, + .srate = 0, + .sampv = (uint8_t *)p, + .sampc = sz, + .timestamp = 0, + .level = AULEVEL_UNDEF + }; + + return aubuf_write_auframe(ab, &af); +} + + +static inline int aubuf_write_samp(struct aubuf *ab, const int16_t *sampv, + size_t sampc) +{ + struct auframe af = { + .fmt = AUFMT_S16LE, + .srate = 0, + .sampv = (uint8_t *)sampv, + .sampc = sampc, + .timestamp = 0, + .level = AULEVEL_UNDEF + }; + + return aubuf_write_auframe(ab, &af); +} + + +static inline void aubuf_read(struct aubuf *ab, uint8_t *p, size_t sz) +{ + struct auframe af = { + .fmt = AUFMT_RAW, + .srate = 0, + .sampv = p, + .sampc = sz, + .timestamp = 0, + .level = AULEVEL_UNDEF + }; + + aubuf_read_auframe(ab, &af); +} + + +static inline void aubuf_read_samp(struct aubuf *ab, int16_t *sampv, + size_t sampc) +{ + struct auframe af = { + .fmt = AUFMT_S16LE, + .srate = 0, + .sampv = (uint8_t *)sampv, + .sampc = sampc, + .timestamp = 0, + .level = AULEVEL_UNDEF + }; + + aubuf_read_auframe(ab, &af); +} +#endif diff --git a/include/rem_auconv.h b/include/rem_auconv.h new file mode 100644 index 000000000..ef11f136c --- /dev/null +++ b/include/rem_auconv.h @@ -0,0 +1,13 @@ +/** + * @file rem_auconv.h Audio sample format conversion + * + * Copyright (C) 2010 Creytiv.com + */ + + +void auconv_from_s16(enum aufmt dst_fmt, void *dst_sampv, + const int16_t *src_sampv, size_t sampc); +void auconv_to_s16(int16_t *dst_sampv, enum aufmt src_fmt, + void *src_sampv, size_t sampc); +void auconv_to_float(float *dst_sampv, enum aufmt src_fmt, + const void *src_sampv, size_t sampc); diff --git a/include/rem_audio.h b/include/rem_audio.h new file mode 100644 index 000000000..c02c147f9 --- /dev/null +++ b/include/rem_audio.h @@ -0,0 +1,21 @@ +/** + * @file rem_audio.h Wrapper for all Audio header files + * + * Copyright (C) 2010 Creytiv.com + */ + + +#include "rem_au.h" +#include "rem_aulevel.h" +#include "rem_auframe.h" +#include "rem_aubuf.h" +#include "rem_auconv.h" +#include "rem_aufile.h" +#include "rem_autone.h" +#include "rem_aumix.h" +#include "rem_dtmf.h" +#include "rem_fir.h" +#include "rem_goertzel.h" +#include "rem_auresamp.h" +#include "rem_g711.h" +#include "rem_aac.h" diff --git a/include/rem_aufile.h b/include/rem_aufile.h new file mode 100644 index 000000000..fdd7482b7 --- /dev/null +++ b/include/rem_aufile.h @@ -0,0 +1,26 @@ +/** + * @file rem_aufile.h Audio File interface + * + * Copyright (C) 2010 Creytiv.com + */ + + +/** Audio file mode */ +enum aufile_mode { + AUFILE_READ, + AUFILE_WRITE, +}; + +/** Audio file parameters */ +struct aufile_prm { + uint32_t srate; + uint8_t channels; + enum aufmt fmt; +}; + +struct aufile; + +int aufile_open(struct aufile **afp, struct aufile_prm *prm, + const char *filename, enum aufile_mode mode); +int aufile_read(struct aufile *af, uint8_t *p, size_t *sz); +int aufile_write(struct aufile *af, const uint8_t *p, size_t sz); diff --git a/include/rem_auframe.h b/include/rem_auframe.h new file mode 100644 index 000000000..d4fc472d4 --- /dev/null +++ b/include/rem_auframe.h @@ -0,0 +1,48 @@ +/* + * Audio frame + */ + +#define AUDIO_TIMEBASE 1000000U + +/** + * Defines a frame of audio samples + */ +struct auframe { + enum aufmt fmt; /**< Sample format (enum aufmt) */ + uint32_t srate; /**< Samplerate */ + void *sampv; /**< Audio samples (must be mem_ref'd) */ + size_t sampc; /**< Total number of audio samples */ + uint64_t timestamp; /**< Timestamp in AUDIO_TIMEBASE units */ + double level; /**< Audio level in dBov */ + uint16_t id; /**< Frame/Channel identifier */ + uint8_t ch; /**< Channels */ + uint8_t padding[5]; +}; + +void auframe_init(struct auframe *af, enum aufmt fmt, void *sampv, + size_t sampc, uint32_t srate, uint8_t ch); + +/** + * Update an audio frame + * + * @param af Audio frame + * @param sampv Audio samples + * @param sampc Total number of audio samples + * @param timestamp Timestamp in AUDIO_TIMEBASE units + */ +static inline void auframe_update(struct auframe *af, void *sampv, + size_t sampc, uint64_t timestamp) +{ + if (!af) + return; + + af->sampv = sampv; + af->sampc = sampc; + af->timestamp = timestamp; + af->level = AULEVEL_UNDEF; +} + +size_t auframe_size(const struct auframe *af); +void auframe_mute(struct auframe *af); +double auframe_level(struct auframe *af); +uint64_t auframe_bytes_to_timestamp(const struct auframe *af, size_t n); diff --git a/include/rem_aulevel.h b/include/rem_aulevel.h new file mode 100644 index 000000000..838f34bba --- /dev/null +++ b/include/rem_aulevel.h @@ -0,0 +1,13 @@ + + +/* + * Audio-level + */ + + +#define AULEVEL_UNDEF (-128.0) +#define AULEVEL_MIN (-96.0) +#define AULEVEL_MAX (0.0) + + +double aulevel_calc_dbov(int fmt, const void *sampv, size_t sampc); diff --git a/include/rem_aumix.h b/include/rem_aumix.h new file mode 100644 index 000000000..0f17db0a3 --- /dev/null +++ b/include/rem_aumix.h @@ -0,0 +1,35 @@ +/** + * @file rem_aumix.h Audio Mixer + * + * Copyright (C) 2010 Creytiv.com + */ + +struct aumix; +struct aumix_source; + +/** + * Audio mixer frame handler + * + * @param sampv Buffer with audio samples + * @param sampc Number of samples + * @param arg Handler argument + */ +typedef void (aumix_frame_h)(const int16_t *sampv, size_t sampc, void *arg); +typedef void (aumix_record_h)(struct auframe *af); +typedef void (aumix_read_h)(struct auframe *af, void *arg); + +int aumix_alloc(struct aumix **mixp, uint32_t srate, + uint8_t ch, uint32_t ptime); +void aumix_recordh(struct aumix *mix, aumix_record_h *recordh); +int aumix_playfile(struct aumix *mix, const char *filepath); +uint32_t aumix_source_count(const struct aumix *mix); +int aumix_source_alloc(struct aumix_source **srcp, struct aumix *mix, + aumix_frame_h *fh, void *arg); +void aumix_source_set_id(struct aumix_source *src, uint16_t id); +void aumix_source_enable(struct aumix_source *src, bool enable); +void aumix_source_mute(struct aumix_source *src, bool mute); +int aumix_source_put(struct aumix_source *src, const int16_t *sampv, + size_t sampc); +void aumix_source_readh(struct aumix_source *src, aumix_read_h *readh); +void aumix_source_flush(struct aumix_source *src); +int aumix_debug(struct re_printf *pf, struct aumix *mix); diff --git a/include/rem_auresamp.h b/include/rem_auresamp.h new file mode 100644 index 000000000..70b408605 --- /dev/null +++ b/include/rem_auresamp.h @@ -0,0 +1,34 @@ +/** + * @file rem_auresamp.h Audio Resampling + * + * Copyright (C) 2010 Creytiv.com + */ + +/** + * Defines the audio resampler handler + * + * @param outv Output samples + * @param inv Input samples + * @param inc Number of input samples + * @param ratio Resample ratio + */ +typedef void (auresamp_h)(int16_t *outv, const int16_t *inv, + size_t inc, unsigned ratio); + +/** Defines the resampler state */ +struct auresamp { + struct fir fir; /**< FIR filter state */ + auresamp_h *resample; /**< Resample handler */ + const int16_t *tapv; /**< FIR filter taps */ + size_t tapc; /**< FIR filter tap count */ + uint32_t orate, irate; /**< Input/output sample rate */ + unsigned och, ich; /**< Input/output channel count */ + unsigned ratio; /**< Resample ratio */ + bool up; /**< Up/down sample flag */ +}; + +void auresamp_init(struct auresamp *rs); +int auresamp_setup(struct auresamp *rs, uint32_t irate, unsigned ich, + uint32_t orate, unsigned och); +int auresamp(struct auresamp *rs, int16_t *outv, size_t *outc, + const int16_t *inv, size_t inc); diff --git a/include/rem_autone.h b/include/rem_autone.h new file mode 100644 index 000000000..23ba65bbf --- /dev/null +++ b/include/rem_autone.h @@ -0,0 +1,10 @@ +/** + * @file rem_autone.h Audio Tones + * + * Copyright (C) 2010 Creytiv.com + */ + + +int autone_sine(struct mbuf *mb, uint32_t srate, + uint32_t f1, int l1, uint32_t f2, int l2); +int autone_dtmf(struct mbuf *mb, uint32_t srate, int digit); diff --git a/include/rem_avc.h b/include/rem_avc.h new file mode 100644 index 000000000..b795d04c3 --- /dev/null +++ b/include/rem_avc.h @@ -0,0 +1,23 @@ +/** + * @file rem_avc.h Advanced Video Coding + * + * Copyright (C) 2010 Creytiv.com + */ + + +struct avc_config { + uint8_t profile_ind; + uint8_t profile_compat; + uint8_t level_ind; + uint16_t sps_len; + uint8_t sps[256]; + uint16_t pps_len; + uint8_t pps[64]; +}; + + +int avc_config_encode(struct mbuf *mb, uint8_t profile_ind, + uint8_t profile_compat, uint8_t level_ind, + uint16_t sps_length, const uint8_t *sps, + uint16_t pps_length, const uint8_t *pps); +int avc_config_decode(struct avc_config *conf, struct mbuf *mb); diff --git a/include/rem_dsp.h b/include/rem_dsp.h new file mode 100644 index 000000000..f9ede9f6d --- /dev/null +++ b/include/rem_dsp.h @@ -0,0 +1,137 @@ +/** + * @file rem_dsp.h DSP routines + * + * Copyright (C) 2010 Creytiv.com + */ + + +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif + +#define INT15_MAX 0x3fff +#define INT15_MIN (-INT15_MAX - 1) + +#ifndef INT16_MAX +#define INT16_MAX 0x7fff +#endif + +#ifndef INT16_MIN +#define INT16_MIN (-INT16_MAX - 1) +#endif + + +/* todo: check which preprocessor macros to use */ + + +#if defined (HAVE_ARMV6) || defined (HAVE_NEON) + +static inline uint8_t saturate_u8(int32_t a) +{ + uint8_t r; + __asm__ __volatile__ ("usat %0, #8, %1" : "=r"(r) : "r"(a)); + return r; +} + +static inline int16_t saturate_s15(int32_t a) +{ + __asm__ __volatile__ ("ssat %0, #15, %1 \n\t" + : "+r"(a) + : "r"(a) + ); + + return a; +} + +static inline int16_t saturate_s16(int32_t a) +{ + __asm__ __volatile__ ("ssat %0, #16, %1 \n\t" + : "+r"(a) + : "r"(a) + ); + + return a; +} + +static inline int16_t saturate_add16(int32_t a, int32_t b) +{ + __asm__ __volatile__ ("add %0, %1, %2 \n\t" + "ssat %0, #16, %0 \n\t" + :"+r"(a) + :"r"(a), "r"(b) + ); + return a; +} + +static inline int16_t saturate_sub16(int32_t a, int32_t b) +{ + __asm__ __volatile__ ("sub %0, %1, %2 \n\t" + "ssat %0, #16, %0 \n\t" + :"+r"(a) + :"r"(a), "r"(b) + ); + return a; +} + + +#else + + +static inline uint8_t saturate_u8(int32_t a) +{ + return (a > (int32_t)UINT8_MAX) ? UINT8_MAX : ((a < 0) ? 0 : a); +} + +static inline int16_t saturate_s15(int32_t a) +{ + if (a > INT15_MAX) + return INT15_MAX; + else if (a < INT15_MIN) + return INT15_MIN; + else + return a; +} + +static inline int16_t saturate_s16(int32_t a) +{ + if (a > INT16_MAX) + return INT16_MAX; + else if (a < INT16_MIN) + return INT16_MIN; + else + return a; +} + +static inline int16_t saturate_add16(int32_t a, int32_t b) +{ + return saturate_s16(a + b); +} + + +static inline int16_t saturate_sub16(int32_t a, int32_t b) +{ + return saturate_s16(a - b); +} + + +#endif + + +#ifdef HAVE_NEON +static inline int ABS32(int a) +{ + int r; + __asm__ __volatile__ ("vmov.s32 d0[0], %1 \t\n" + "vabs.s32 d0, d0 \t\n" + "vmov.s32 %0, d0[0] \t\n" + : "=r"(r) + : "r"(a) + ); + return a; +} +#else +static inline int ABS32(int a) +{ + return a > 0 ? a : -a; +} +#endif diff --git a/include/rem_dtmf.h b/include/rem_dtmf.h new file mode 100644 index 000000000..f96f11b6c --- /dev/null +++ b/include/rem_dtmf.h @@ -0,0 +1,21 @@ +/** + * @file rem_dtmf.h DTMF Decoder + * + * Copyright (C) 2010 Creytiv.com + */ + +struct dtmf_dec; + +/** + * Defines the DTMF decode handler + * + * @param digit Decoded DTMF digit + * @param arg Handler argument + */ +typedef void (dtmf_dec_h)(char digit, void *arg); + + +int dtmf_dec_alloc(struct dtmf_dec **decp, unsigned srate, unsigned ch, + dtmf_dec_h *dech, void *arg); +void dtmf_dec_reset(struct dtmf_dec *dec, unsigned srate, unsigned ch); +void dtmf_dec_probe(struct dtmf_dec *dec, const int16_t *sampv, size_t sampc); diff --git a/include/rem_fir.h b/include/rem_fir.h new file mode 100644 index 000000000..02dfc94d3 --- /dev/null +++ b/include/rem_fir.h @@ -0,0 +1,15 @@ +/** + * @file rem_fir.h Finite Impulse Response (FIR) functions + * + * Copyright (C) 2010 Creytiv.com + */ + +/** Defines the fir filter state */ +struct fir { + int16_t history[256]; /**< Previous samples */ + unsigned index; /**< Sample index */ +}; + +void fir_reset(struct fir *fir); +void fir_filter(struct fir *fir, int16_t *outv, const int16_t *inv, size_t inc, + unsigned ch, const int16_t *tapv, size_t tapc); diff --git a/include/rem_flv.h b/include/rem_flv.h new file mode 100644 index 000000000..abc8338ca --- /dev/null +++ b/include/rem_flv.h @@ -0,0 +1,56 @@ +/** + * @file rem_flv.h Flash Video File Format + * + * Copyright (C) 2010 Creytiv.com + */ + + +/* + * Audio + */ + +enum flv_aucodec { + FLV_AUCODEC_PCM = 0, + FLV_AUCODEC_MP3 = 2, + FLV_AUCODEC_PCM_LE = 3, + FLV_AUCODEC_ALAW = 7, + FLV_AUCODEC_ULAW = 8, + FLV_AUCODEC_AAC = 10, +}; + +enum flv_srate { + FLV_SRATE_5500HZ = 0, + FLV_SRATE_11000HZ = 1, + FLV_SRATE_22000HZ = 2, + FLV_SRATE_44000HZ = 3, +}; + +enum flv_aac_packet_type { + FLV_AAC_SEQUENCE_HEADER = 0, + FLV_AAC_RAW = 1, +}; + + +/* + * Video + */ + +enum flv_vidframe { + FLV_VIDFRAME_KEY = 1, + FLV_VIDFRAME_INTER = 2, + FLV_VIDFRAME_DISP_INTER = 3, + FLV_VIDFRAME_GENERATED_KEY = 4, + FLV_VIDFRAME_VIDEO_INFO_CMD = 5, +}; + +enum flv_vidcodec { + FLV_VIDCODEC_H263 = 2, + FLV_VIDCODEC_H264 = 7, + FLV_VIDCODEC_MPEG4 = 9, +}; + +enum flv_avc_packet_type { + FLV_AVC_SEQUENCE = 0, + FLV_AVC_NALU = 1, + FLV_AVC_EOS = 2, +}; diff --git a/include/rem_g711.h b/include/rem_g711.h new file mode 100644 index 000000000..b093aab66 --- /dev/null +++ b/include/rem_g711.h @@ -0,0 +1,77 @@ +/** + * @file rem_g711.h Interface to G.711 codec + * + * Copyright (C) 2010 Creytiv.com + */ + + +extern const uint8_t g711_l2u[4096]; +extern const uint8_t g711_l2A[2048]; +extern const int16_t g711_u2l[256]; +extern const int16_t g711_A2l[256]; + + +/** + * Encode one 16-bit PCM sample to U-law format + * + * @param l Signed PCM sample + * + * @return U-law byte + */ +static inline uint8_t g711_pcm2ulaw(int16_t lx) +{ + int32_t l = lx; + const uint8_t mask = (l < 0) ? 0x7f : 0xff; + if (l < 0) + l = -l; + if (l < 4) + return 0xff & mask; + l -= 4; + l >>= 3; + + return g711_l2u[l] & mask; +} + + +/** + * Encode one 16-bit PCM sample to A-law format + * + * @param l Signed PCM sample + * + * @return A-law byte + */ +static inline uint8_t g711_pcm2alaw(int16_t l) +{ + const uint8_t mask = (l < 0) ? 0x7f : 0xff; + if (l < 0) + l = ~l; + l >>= 4; + + return g711_l2A[l] & mask; +} + + +/** + * Decode one U-law sample to 16-bit PCM sample + * + * @param u U-law byte + * + * @return Signed PCM sample + */ +static inline int16_t g711_ulaw2pcm(uint8_t u) +{ + return g711_u2l[u]; +} + + +/** + * Decode one A-law sample to 16-bit PCM sample + * + * @param A A-law byte + * + * @return Signed PCM sample + */ +static inline int16_t g711_alaw2pcm(uint8_t a) +{ + return g711_A2l[a]; +} diff --git a/include/rem_goertzel.h b/include/rem_goertzel.h new file mode 100644 index 000000000..c9ff2e6d6 --- /dev/null +++ b/include/rem_goertzel.h @@ -0,0 +1,32 @@ +/** + * @file rem_goertzel.h Goertzel algorithm + * + * Copyright (C) 2010 Creytiv.com + */ + +/** Defines the goertzel algorithm state */ +struct goertzel { + double q1; /**< current state */ + double q2; /**< previous state */ + double coef; /**< coefficient */ +}; + + +void goertzel_init(struct goertzel *g, double freq, unsigned srate); +void goertzel_reset(struct goertzel *g); +double goertzel_result(struct goertzel *g); + + +/** + * Process sample + * + * @param g Goertzel state + * @param samp Sample value + */ +static inline void goertzel_update(struct goertzel *g, int16_t samp) +{ + double q0 = g->coef*g->q1 - g->q2 + (double)samp; + + g->q2 = g->q1; + g->q1 = q0; +} diff --git a/include/rem_vid.h b/include/rem_vid.h new file mode 100644 index 000000000..4103477c0 --- /dev/null +++ b/include/rem_vid.h @@ -0,0 +1,152 @@ +/** + * @file rem_vid.h Basic video types + * + * Copyright (C) 2010 Creytiv.com + */ + + +/** Pixel format */ +enum vidfmt { + VID_FMT_YUV420P = 0, /* planar YUV 4:2:0 12bpp */ + VID_FMT_YUYV422, /* packed YUV 4:2:2 16bpp */ + VID_FMT_UYVY422, /* packed YUV 4:2:2 16bpp */ + VID_FMT_RGB32, /* packed RGBA 8:8:8:8 32bpp (native endian) */ + VID_FMT_ARGB, /* packed RGBA 8:8:8:8 32bpp (big endian) */ + VID_FMT_RGB565, /* packed RGB 5:6:5 16bpp (native endian) */ + VID_FMT_NV12, /* planar YUV 4:2:0 12bpp UV interleaved */ + VID_FMT_NV21, /* planar YUV 4:2:0 12bpp VU interleaved */ + VID_FMT_YUV444P, /* planar YUV 4:4:4 24bpp */ + VID_FMT_YUV422P, /* planar YUV 4:2:2 16bpp */ + /* marker */ + VID_FMT_N +}; + +/** Video pixel format component description */ +struct vidfmt_compdesc { + unsigned plane_index:2; + unsigned step:3; +}; + +/** Video pixel format description */ +struct vidfmt_desc { + const char *name; + uint8_t planes; + uint8_t compn; + struct vidfmt_compdesc compv[4]; +}; + +/** Video orientation */ +enum vidorient { + VIDORIENT_PORTRAIT, + VIDORIENT_PORTRAIT_UPSIDEDOWN, + VIDORIENT_LANDSCAPE_LEFT, + VIDORIENT_LANDSCAPE_RIGHT, +}; + +/** Video size */ +struct vidsz { + unsigned w; /**< Width */ + unsigned h; /**< Height */ +}; + +/** Video frame */ +struct vidframe { + uint8_t *data[4]; /**< Video planes */ + uint16_t linesize[4]; /**< Array of line-sizes */ + struct vidsz size; /**< Frame resolution */ + enum vidfmt fmt; /**< Video pixel format */ +}; + +/** Video point */ +struct vidpt { + unsigned x; /**< X position */ + unsigned y; /**< Y position */ +}; + +/** Video rectangle */ +struct vidrect { + unsigned x; /**< X position */ + unsigned y; /**< Y position */ + unsigned w; /**< Width */ + unsigned h; /**< Height */ +}; + +static inline bool vidsz_cmp(const struct vidsz *a, const struct vidsz *b) +{ + if (!a || !b) + return false; + + if (a == b) + return true; + + return a->w == b->w && a->h == b->h; +} + + +static inline bool vidrect_cmp(const struct vidrect *a, + const struct vidrect *b) +{ + if (!a || !b) + return false; + + if (a == b) + return true; + + return a->x == b->x && a->y == b->y && a->w == b->w && a->h == b->h; +} + + +static inline int rgb2y(uint8_t r, uint8_t g, uint8_t b) +{ + return ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16; +} + + +static inline int rgb2u(uint8_t r, uint8_t g, uint8_t b) +{ + return ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128; +} + + +static inline int rgb2v(uint8_t r, uint8_t g, uint8_t b) +{ + return ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128; +} + + +size_t vidframe_size(enum vidfmt fmt, const struct vidsz *sz); +void vidframe_init(struct vidframe *vf, enum vidfmt fmt, + const struct vidsz *sz, void *data[4], + unsigned linesize[4]); +void vidframe_init_buf(struct vidframe *vf, enum vidfmt fmt, + const struct vidsz *sz, uint8_t *buf); +int vidframe_alloc(struct vidframe **vfp, enum vidfmt fmt, + const struct vidsz *sz); +void vidframe_fill(struct vidframe *vf, uint32_t r, uint32_t g, uint32_t b); +void vidframe_copy(struct vidframe *dst, const struct vidframe *src); + + +const char *vidfmt_name(enum vidfmt fmt); + + +static inline bool vidframe_isvalid(const struct vidframe *f) +{ + return f ? f->data[0] != NULL : false; +} + + +extern const struct vidfmt_desc vidfmt_descv[VID_FMT_N]; + + +/* draw */ +void vidframe_draw_point(struct vidframe *f, unsigned x, unsigned y, + uint8_t r, uint8_t g, uint8_t b); +void vidframe_draw_hline(struct vidframe *f, + unsigned x0, unsigned y0, unsigned w, + uint8_t r, uint8_t g, uint8_t b); +void vidframe_draw_vline(struct vidframe *f, + unsigned x0, unsigned y0, unsigned h, + uint8_t r, uint8_t g, uint8_t b); +void vidframe_draw_rect(struct vidframe *f, + unsigned x0, unsigned y0, unsigned w, unsigned h, + uint8_t r, uint8_t g, uint8_t b); diff --git a/include/rem_vidconv.h b/include/rem_vidconv.h new file mode 100644 index 000000000..94fee1d65 --- /dev/null +++ b/include/rem_vidconv.h @@ -0,0 +1,11 @@ +/** + * @file rem_vidconv.h Video colorspace conversion + * + * Copyright (C) 2010 Creytiv.com + */ + + +void vidconv(struct vidframe *dst, const struct vidframe *src, + struct vidrect *r); +void vidconv_aspect(struct vidframe *dst, const struct vidframe *src, + struct vidrect *r); diff --git a/include/rem_video.h b/include/rem_video.h new file mode 100644 index 000000000..73b4c7722 --- /dev/null +++ b/include/rem_video.h @@ -0,0 +1,11 @@ +/** + * @file rem_video.h Wrapper for all Video header files + * + * Copyright (C) 2010 Creytiv.com + */ + + +#include "rem_vid.h" +#include "rem_vidmix.h" +#include "rem_vidconv.h" +#include "rem_avc.h" diff --git a/include/rem_vidmix.h b/include/rem_vidmix.h new file mode 100644 index 000000000..d806c4b72 --- /dev/null +++ b/include/rem_vidmix.h @@ -0,0 +1,40 @@ +/** + * @file rem_vidmix.h Video Mixer + * + * Copyright (C) 2010 Creytiv.com + */ + + +struct vidmix; +struct vidmix_source; + +/** + * Video mixer frame handler + * + * @param ts Timestamp + * @param frame Video frame + * @param arg Handler argument + */ +typedef void (vidmix_frame_h)(uint64_t ts, const struct vidframe *frame, + void *arg); + +int vidmix_alloc(struct vidmix **mixp); +int vidmix_source_alloc(struct vidmix_source **srcp, struct vidmix *mix, + const struct vidsz *sz, unsigned fps, bool content, + vidmix_frame_h *fh, void *arg); +bool vidmix_source_isenabled(const struct vidmix_source *src); +bool vidmix_source_isrunning(const struct vidmix_source *src); +void *vidmix_source_get_focus(const struct vidmix_source *src); +void vidmix_source_enable(struct vidmix_source *src, bool enable); +int vidmix_source_start(struct vidmix_source *src); +void vidmix_source_stop(struct vidmix_source *src); +int vidmix_source_set_size(struct vidmix_source *src, const struct vidsz *sz); +void vidmix_source_set_rate(struct vidmix_source *src, unsigned fps); +void vidmix_source_set_content_hide(struct vidmix_source *src, bool hide); +void vidmix_source_toggle_selfview(struct vidmix_source *src); +void vidmix_source_set_focus(struct vidmix_source *src, + const struct vidmix_source *focus_src, + bool focus_full); +void vidmix_source_set_focus_idx(struct vidmix_source *src, unsigned pidx); +void vidmix_source_put(struct vidmix_source *src, + const struct vidframe *frame); diff --git a/rem/aac/aac.c b/rem/aac/aac.c new file mode 100644 index 000000000..7a0eccfda --- /dev/null +++ b/rem/aac/aac.c @@ -0,0 +1,69 @@ +/** + * @file aac.c Advanced Audio Coding + * + * Copyright (C) 2018 Creytiv.com + */ + +#include +#include + + +/* + * Ref https://wiki.multimedia.cx/index.php/MPEG-4_Audio + */ + +enum { + OBJECT_TYPE_AAC_LC = 2 +}; + + +static const unsigned aac_sample_rates[13] = { + 96000, 88200, 64000, 48000, 44100, 32000, + 24000, 22050, 16000, 12000, 11025, 8000, 7350 +}; + + +static const unsigned aac_channels[8] = { + 0, 1, 2, 3, 4, 5, 6, 8 +}; + + +/** + * Decode an AAC header + * + * @param hdr Decoded AAC header + * @param p Packet to decode + * @param len Packet length + * + * @return 0 if success, otherwise errorcode + */ +int aac_header_decode(struct aac_header *hdr, const uint8_t *p, size_t len) +{ + uint8_t object_type; + uint8_t srate_index; + uint8_t channel_index; + + if (!hdr || !p || len<2) + return EINVAL; + + object_type = (p[0] >> 3) & 0x1f; + + if (object_type != OBJECT_TYPE_AAC_LC) + return EBADMSG; + + srate_index = (p[0] & 0x07) << 1; + srate_index |= (p[1] & 0x80) >> 7; + + channel_index = (p[1] >> 3) & 0xf; + + if (srate_index >= RE_ARRAY_SIZE(aac_sample_rates)) + return ENOTSUP; + if (channel_index >= RE_ARRAY_SIZE(aac_channels)) + return ENOTSUP; + + hdr->sample_rate = aac_sample_rates[srate_index]; + hdr->channels = aac_channels[channel_index]; + hdr->frame_size = ((p[1] >> 2) & 1) ? 960 : 1024; + + return 0; +} diff --git a/rem/au/fmt.c b/rem/au/fmt.c new file mode 100644 index 000000000..230d654ad --- /dev/null +++ b/rem/au/fmt.c @@ -0,0 +1,40 @@ +/** + * @file au/fmt.c Audio formats + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include + + +/* Number of bytes per sample */ +size_t aufmt_sample_size(enum aufmt fmt) +{ + switch (fmt) { + + case AUFMT_S16LE: return sizeof(int16_t); + case AUFMT_S32LE: return sizeof(int32_t); + case AUFMT_RAW: return 1; + case AUFMT_PCMA: return 1; + case AUFMT_PCMU: return 1; + case AUFMT_FLOAT: return sizeof(float); + case AUFMT_S24_3LE: return 3; + default: return 0; + } +} + + +const char *aufmt_name(enum aufmt fmt) +{ + switch (fmt) { + + case AUFMT_S16LE: return "S16LE"; + case AUFMT_S32LE: return "S32LE"; + case AUFMT_PCMA: return "PCMA"; + case AUFMT_PCMU: return "PCMU"; + case AUFMT_FLOAT: return "FLOAT"; + case AUFMT_S24_3LE: return "S24_3LE"; + case AUFMT_RAW: return "RAW"; + default: return "???"; + } +} diff --git a/rem/au/util.c b/rem/au/util.c new file mode 100644 index 000000000..dddf19ce4 --- /dev/null +++ b/rem/au/util.c @@ -0,0 +1,23 @@ +/** + * @file util.c Audio utility functions + * + * Copyright (C) 2022 Commend.com - c.spielberger@commend.com + */ + +#include +#include + + +/** + * Calculate number of samples from sample rate, channels and packet time + * + * @param srate Sample rate in [Hz] + * @param channels Number of channels + * @param ptime Packet time in [ms] + * + * @return Number of samples + */ +uint32_t calc_nsamp(uint32_t srate, uint8_t channels, uint16_t ptime) +{ + return srate * channels * ptime / 1000; +} diff --git a/rem/aubuf/ajb.c b/rem/aubuf/ajb.c new file mode 100644 index 000000000..349058859 --- /dev/null +++ b/rem/aubuf/ajb.c @@ -0,0 +1,365 @@ +/** + * @file ajb.c Adaptive Jitter Buffer algorithm + * + * Copyright (C) 2022 Commend.com - c.spielberger@commend.com + */ +#include +#include +#include +#include +#include +#include +#include "ajb.h" + +#define DEBUG_LEVEL 5 + +/** + * @brief The adaptive jitter computation is done by means of an exponential + * moving average (EMA). + *j_i = j_{i-1} + a (c - j_{i-1}) + * + * Where $a$ ist the EMA coefficient and $c$ is the current value. + */ +enum { + JITTER_EMA_COEFF = 512, /* Divisor for jitter EMA coefficient */ + JITTER_UP_SPEED = 64, /* 64 times faster up than down */ + BUFTIME_EMA_COEFF = 128, /* Divisor for Buftime EMA coeff. */ + BUFTIME_LO = 125, /* 125% of jitter */ + BUFTIME_HI = 175, /* 175% of jitter */ + SKEW_MAX = 10, /* Max skew in [ms] */ +}; + + +/** Adaptive jitter buffer statistics */ +struct ajb { + int32_t jitter; /**< Jitter in [us] */ + mtx_t *lock; + + uint64_t ts; /**< previous timestamp */ + uint64_t ts0; /**< reference timestamp */ + uint64_t tr0; /**< reference time of arrival */ + uint64_t tr00; /**< arrival of first packet */ +#if DEBUG_LEVEL >= 6 + struct { + int32_t d; + uint32_t buftime; + uint32_t bufmin; + uint32_t bufmax; + enum ajb_state as; + } plot; + + char buf[136]; /**< Buffer for trace */ +#endif + + enum ajb_state as; /**< computed jitter buffer state */ + + int32_t avbuftime; /**< average buffered time [us] */ + bool started; /**< Started flag */ + size_t wish_sz; /**< Wish size of buffer [Bytes] */ + struct auframe af; /**< Audio frame of last ajb_get() */ + uint32_t dropped; /**< Dropped audio frames counter */ + double silence; /**< Silence audio level */ +}; + + +static void destructor(void *arg) +{ + struct ajb *ajb = arg; + + mem_deref(ajb->lock); +#if DEBUG_LEVEL >= 6 + (void)re_trace_close(); +#endif +} + + +#if DEBUG_LEVEL >= 6 +static void plot_ajb(struct ajb *ajb, uint64_t tr) +{ + uint32_t treal; + + if (!ajb->tr00) + ajb->tr00 = tr; + + treal = (uint32_t) (tr - ajb->tr00); + re_snprintf(ajb->buf, sizeof(ajb->buf), + "%s, 0x%p, %u, %i, %u, %u, %u, %i, %i, %u", + __func__, /* row 1 - grep */ + ajb, /* row 2 - grep optional */ + treal, /* row 3 - plot x-axis */ + ajb->plot.d, /* row 4 - plot */ + ajb->jitter, /* row 5 - plot */ + ajb->plot.buftime, /* row 6 - plot */ + ajb->avbuftime, /* row 7 - plot */ + ajb->plot.bufmin, /* row 8 - plot */ + ajb->plot.bufmax, /* row 9 - plot */ + ajb->plot.as); /* row 10 - plot */ + re_trace_event("ajb", "plot", 'P', NULL, 0, RE_TRACE_ARG_STRING_COPY, + "line", ajb->buf); +} +#endif + + +#if DEBUG_LEVEL >= 6 +void plot_underrun(struct ajb *ajb) +{ + uint64_t tr; + uint32_t treal; + if (!ajb) + return; + + tr = tmr_jiffies(); + if (!ajb->tr00) + ajb->tr00 = tr; + + treal = (uint32_t) (tr - ajb->tr00); + re_snprintf(ajb->buf, sizeof(ajb->buf), "%s, 0x%p, %u, %i", + __func__, /* row 1 - grep */ + ajb, /* row 2 - grep optional */ + treal, /* row 3 - plot optional */ + 1); /* row 4 - plot */ + re_trace_event("ajb", "plot", 'U', NULL, 0, RE_TRACE_ARG_STRING_COPY, + "line", ajb->buf); +} +#else +void plot_underrun(struct ajb *ajb) +{ + (void)ajb; +} +#endif + + +/** + * Initializes the adaptive jitter buffer statistics + * + * @param silence Silence audio level + * @param wish_sz Wish size of buffer [Bytes] + * + * @return ajb Adaptive jitter buffer statistics + */ +struct ajb *ajb_alloc(double silence, size_t wish_sz) +{ + struct ajb *ajb; + int err; + + ajb = mem_zalloc(sizeof(*ajb), destructor); + if (!ajb) + return NULL; + + err = mutex_alloc(&ajb->lock); + if (err) + goto out; + + ajb->ts0 = 0; + ajb->tr0 = 0; + ajb->as = AJB_GOOD; + ajb->silence = silence; + ajb->wish_sz = wish_sz; +#if DEBUG_LEVEL >= 6 + (void)re_trace_init("ajb.json"); +#endif + +out: + if (err) + ajb = mem_deref(ajb); + + return ajb; +} + + +void ajb_reset(struct ajb *ajb) +{ + if (!ajb) + return; + + mtx_lock(ajb->lock); + ajb->ts = 0; + ajb->ts0 = 0; + ajb->tr0 = 0; + + /* We start with wish size. */ + ajb->started = false; + ajb->as = AJB_GOOD; + mtx_unlock(ajb->lock); +} + + +/** + * Computes the jitter for audio frame arrival. + * + * @param ajb Adaptive jitter buffer statistics + * @param af Audio frame + * @param cur_sz Current aubuf size + */ +void ajb_calc(struct ajb *ajb, const struct auframe *af, size_t cur_sz) +{ + uint64_t tr; /**< Real time in [us] */ + uint32_t buftime, bufmax, bufmin; /**< Buffer time in [us] */ + uint32_t bufwish; /**< Buffer wish time in [us] */ + int32_t d; /**< Time shift in [us] */ + int32_t da; /**< Absolut time shift in [us] */ + int32_t s; /**< EMA coefficient */ + uint64_t ts; /**< Time stamp */ + uint64_t ds; /**< Time stamp duration */ + uint32_t ptime; /**< Packet time [us] */ + size_t szdiv; + + if (!ajb || !af || !af->srate) + return; + + mtx_lock(ajb->lock); + ts = af->timestamp; + tr = tmr_jiffies_usec(); + if (!ajb->ts0) + goto out; + + ds = ts - ajb->ts0; + d = (int32_t) (int64_t) ( (tr - ajb->tr0) - ds ); + da = abs(d); + + szdiv = af->srate * af->ch * aufmt_sample_size(af->fmt) / 1000; + buftime = (uint32_t) (cur_sz * 1000 / szdiv); + bufwish = (uint32_t) (ajb->wish_sz * 1000 / szdiv); + if (ajb->started) { + ajb->avbuftime += ((int32_t) buftime - ajb->avbuftime) / + BUFTIME_EMA_COEFF; + if (ajb->avbuftime < 0) + ajb->avbuftime = 0; + } + else { + /* Directly after "filling" of aubuf compute a good start value + * fitting to wish size. */ + ajb->avbuftime = buftime; + ajb->jitter = ajb->avbuftime * 100 * 2 / + (BUFTIME_LO + BUFTIME_HI); + ajb->started = true; + } + + s = da > ajb->jitter ? JITTER_UP_SPEED : 1; + + ajb->jitter += (da - ajb->jitter) * s / JITTER_EMA_COEFF; + if (ajb->jitter < 0) + ajb->jitter = 0; + + bufmin = (uint32_t) ajb->jitter * BUFTIME_LO / 100; + bufmax = (uint32_t) ajb->jitter * BUFTIME_HI / 100; + + ptime = (uint32_t) (af->sampc * AUDIO_TIMEBASE / (af->srate * af->ch)); + bufmin = MAX(bufmin, ptime * 2 / 3); + if (bufwish >= ptime) + bufmin = MAX(bufmin, bufwish - ptime / 3); + + bufmax = MAX(bufmax, bufmin + 7 * ptime / 6); + + /* reset time base if a frame is missing or skew is too high */ + if (ts - ajb->ts > ptime || da > SKEW_MAX * 1000) + ajb->ts0 = 0; + + if ((uint32_t) ajb->avbuftime < bufmin) + ajb->as = AJB_LOW; + else if ((uint32_t) ajb->avbuftime > bufmax) + ajb->as = AJB_HIGH; + else + ajb->as = AJB_GOOD; + +#if DEBUG_LEVEL >= 6 + ajb->plot.d = d; + ajb->plot.buftime = buftime; + ajb->plot.bufmin = bufmin; + ajb->plot.bufmax = bufmax; + plot_ajb(ajb, tr / 1000); +#endif +out: + ajb->ts = ts; + if (!ajb->ts0) { + ajb->ts0 = ts; + ajb->tr0 = tr; + } + mtx_unlock(ajb->lock); +} + + +void ajb_set_ts0(struct ajb *ajb, uint64_t timestamp) +{ + if (!ajb) + return; + + mtx_lock(ajb->lock); + ajb->ts = timestamp; + ajb->ts0 = timestamp; + ajb->tr0 = tmr_jiffies_usec(); + mtx_unlock(ajb->lock); +} + + +/** + * Get the state of the Adaptive Jitter Buffer + * + * @param ajb Adaptive Jitter Buffer state + * @param af Audio frame + * + * @return Computed jitter buffer state + */ +enum ajb_state ajb_get(struct ajb *ajb, struct auframe *af) +{ + enum ajb_state as = AJB_GOOD; + uint32_t ptime; /**< Packet time [us] */ + + if (!ajb || !af || !af->srate || !af->sampc) + return AJB_GOOD; + + mtx_lock(ajb->lock); + ajb->af = *af; + + /* ptime in [us] */ + ptime = (uint32_t) (af->sampc * AUDIO_TIMEBASE / (af->srate * af->ch)); + if (!ajb->avbuftime) + goto out; + + if (ajb->as == AJB_GOOD || + (ajb->silence < 0. && auframe_level(af) > ajb->silence)) + goto out; + + as = ajb->as; + if (as == AJB_HIGH) { + /* early adjustment of avbuftime */ + ajb->avbuftime -= ptime; + ajb->as = AJB_GOOD; +#if DEBUG_LEVEL >= 6 + ajb->plot.as = AJB_HIGH; + plot_ajb(ajb, tmr_jiffies()); + ajb->plot.as = AJB_GOOD; +#endif + } + else if (as == AJB_LOW) { + /* early adjustment */ + ajb->avbuftime += ptime; + ajb->as = AJB_GOOD; +#if DEBUG_LEVEL >= 6 + ajb->plot.as = AJB_LOW; + plot_ajb(ajb, tmr_jiffies()); + ajb->plot.as = AJB_GOOD; +#endif + } + +out: + mtx_unlock(ajb->lock); + return as; +} + + +int32_t ajb_debug(const struct ajb *ajb) +{ + int32_t jitter; + + if (!ajb) + return 0; + + mtx_lock(ajb->lock); + jitter = ajb->jitter; + mtx_unlock(ajb->lock); + re_printf(" ajb jitter: %d, ajb avbuftime: %d\n", jitter / 1000, + ajb->avbuftime); + + return jitter; +} diff --git a/rem/aubuf/ajb.h b/rem/aubuf/ajb.h new file mode 100644 index 000000000..1a5c5dd47 --- /dev/null +++ b/rem/aubuf/ajb.h @@ -0,0 +1,21 @@ +/** + * @file ajb.h Adaptive Jitter Buffer interface + * + * Copyright (C) 2022 Commend.com - c.spielberger@commend.com + */ + +enum ajb_state { + AJB_GOOD = 0, + AJB_LOW, + AJB_HIGH, +}; + +struct ajb; + +struct ajb *ajb_alloc(double silence, size_t wish_sz); +void ajb_reset(struct ajb *ajb); +void ajb_calc(struct ajb *ajb, const struct auframe *af, size_t sampc); +enum ajb_state ajb_get(struct ajb *ajb, struct auframe *af); +int32_t ajb_debug(const struct ajb *ajb); +void plot_underrun(struct ajb *ajb); +void ajb_set_ts0(struct ajb *ajb, uint64_t timestamp); diff --git a/rem/aubuf/aubuf.c b/rem/aubuf/aubuf.c new file mode 100644 index 000000000..4ef2dd96e --- /dev/null +++ b/rem/aubuf/aubuf.c @@ -0,0 +1,567 @@ +/** + * @file aubuf.c Audio Buffer + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include +#include +#include +#include "ajb.h" + + +#define AUBUF_DEBUG 0 + + +/** Locked audio-buffer with almost zero-copy */ +struct aubuf { + struct list afl; + mtx_t *lock; + size_t wish_sz; + size_t cur_sz; + size_t max_sz; + size_t fill_sz; /**< To fill size */ + size_t pkt_sz; /**< Packet size */ + size_t wr_sz; /**< Written size */ + bool started; + uint64_t ts; + +#if AUBUF_DEBUG + struct { + size_t or; + size_t ur; + } stats; +#endif + enum aubuf_mode mode; + struct ajb *ajb; /**< Adaptive jitter buffer statistics */ + double silence; /**< Silence volume in negative [dB] */ + bool live; /**< Live stream switch */ +}; + + +struct frame { + struct le le; + struct mbuf *mb; + struct auframe af; +}; + + +static void frame_destructor(void *arg) +{ + struct frame *f = arg; + + list_unlink(&f->le); + mem_deref(f->mb); +} + + +static void aubuf_destructor(void *arg) +{ + struct aubuf *ab = arg; + + list_flush(&ab->afl); + mem_deref(ab->lock); + mem_deref(ab->ajb); +} + + +static void read_auframe(struct aubuf *ab, struct auframe *af) +{ + struct le *le = ab->afl.head; + size_t sample_size = aufmt_sample_size(af->fmt); + size_t sz = auframe_size(af); + uint8_t *p = af->sampv; + + while (le) { + struct frame *f = le->data; + size_t n; + + le = le->next; + + n = min(mbuf_get_left(f->mb), sz); + + (void)mbuf_read_mem(f->mb, p, n); + ab->cur_sz -= n; + + af->id = f->af.id; + af->srate = f->af.srate; + af->ch = f->af.ch; + af->timestamp = f->af.timestamp; + + if (!mbuf_get_left(f->mb)) { + mem_deref(f); + } + else if (af->srate && af->ch && sample_size) { + + f->af.timestamp += + auframe_bytes_to_timestamp(&f->af, n); + } + + if (n == sz) + break; + + p += n; + sz -= n; + } +} + + +/** + * Allocate a new audio buffer + * + * @param abp Pointer to allocated audio buffer + * @param min_sz Minimum buffer size + * @param max_sz Maximum buffer size (0 for no max size) + * + * @return 0 for success, otherwise error code + */ +int aubuf_alloc(struct aubuf **abp, size_t min_sz, size_t max_sz) +{ + struct aubuf *ab; + int err; + + if (!abp) + return EINVAL; + + ab = mem_zalloc(sizeof(*ab), aubuf_destructor); + if (!ab) + return ENOMEM; + + err = mutex_alloc(&ab->lock); + if (err) + goto out; + + ab->wish_sz = min_sz; + ab->max_sz = max_sz; + ab->fill_sz = min_sz; + ab->live = true; + + out: + if (err) + mem_deref(ab); + else + *abp = ab; + + return err; +} + + +/** + * Sets the live stream flag on/off. If activated the audio buffer drops old + * frames on first read to keep the latency under `min_sz` bytes on startup. + * Default: `live` is true. + * + * @param ab Audio buffer + * @param live Live flag + */ +void aubuf_set_live(struct aubuf *ab, bool live) +{ + if (!ab) + return; + + ab->live = live; +} + + +void aubuf_set_mode(struct aubuf *ab, enum aubuf_mode mode) +{ + if (!ab) + return; + + ab->mode = mode; +} + + +/** + * Sets the volume level for silence + * + * @param ab Audio buffer + * @param silence Volume level in negative [dB] + */ +void aubuf_set_silence(struct aubuf *ab, double silence) +{ + if (!ab) + return; + + ab->silence = silence; +} + + +/** + * Resize audio buffer (flushes aubuf) + * + * @param ab Audio buffer + * @param min_sz Minimum buffer size + * @param max_sz Maximum buffer size (0 for no max size) + * + * @return 0 for success, otherwise error code + */ +int aubuf_resize(struct aubuf *ab, size_t min_sz, size_t max_sz) +{ + if (!ab) + return EINVAL; + + mtx_lock(ab->lock); + ab->wish_sz = min_sz; + ab->max_sz = max_sz; + mtx_unlock(ab->lock); + + aubuf_flush(ab); + + return 0; +} + + +static bool frame_less_equal(struct le *le1, struct le *le2, void *arg) +{ + struct frame *frame1 = le1->data; + struct frame *frame2 = le2->data; + (void)arg; + + return frame1->af.timestamp <= frame2->af.timestamp; +} + + +/** + * Append a PCM-buffer to the end of the audio buffer + * + * @param ab Audio buffer + * @param mb Mbuffer with PCM samples + * @param af Audio frame (optional) + * + * @return 0 for success, otherwise error code + */ +int aubuf_append_auframe(struct aubuf *ab, struct mbuf *mb, + const struct auframe *af) +{ + struct frame *f; + size_t sz; + + if (!ab || !mb) + return EINVAL; + + f = mem_zalloc(sizeof(*f), frame_destructor); + if (!f) + return ENOMEM; + + f->mb = mem_ref(mb); + if (af) + f->af = *af; + + sz = mbuf_get_left(mb); + + mtx_lock(ab->lock); + ab->pkt_sz = sz; + if (ab->fill_sz >= ab->pkt_sz) + ab->fill_sz -= ab->pkt_sz; + + if (!f->af.timestamp && f->af.srate && f->af.ch) { + f->af.timestamp = + auframe_bytes_to_timestamp(&f->af, ab->wr_sz); + } + + list_insert_sorted(&ab->afl, frame_less_equal, NULL, &f->le, f); + ab->cur_sz += sz; + ab->wr_sz += sz; + + if (ab->max_sz && ab->cur_sz > ab->max_sz) { +#if AUBUF_DEBUG + ++ab->stats.or; + (void)re_printf("aubuf: %p overrun (cur=%zu/%zu)\n", + ab, ab->cur_sz, ab->max_sz); +#endif + f = list_ledata(ab->afl.head); + if (f) { + ab->cur_sz -= mbuf_get_left(f->mb); + mem_deref(f); + } + } + + mtx_unlock(ab->lock); + return 0; +} + + +/** + * Write PCM samples to the audio buffer + * + * @param ab Audio buffer + * @param af Audio frame + * + * @return 0 for success, otherwise error code + */ +int aubuf_write_auframe(struct aubuf *ab, const struct auframe *af) +{ + struct mbuf *mb; + size_t sz; + size_t sample_size; + bool ajb; + int err; + + if (!ab || !af) + return EINVAL; + sample_size = aufmt_sample_size(af->fmt); + if (sample_size) + sz = af->sampc * aufmt_sample_size(af->fmt); + else + sz = af->sampc; + + mb = mbuf_alloc(sz); + + if (!mb) + return ENOMEM; + + (void)mbuf_write_mem(mb, af->sampv, sz); + mb->pos = 0; + + err = aubuf_append_auframe(ab, mb, af); + + mtx_lock(ab->lock); + mem_deref(mb); + ajb = !ab->fill_sz && ab->ajb; + mtx_unlock(ab->lock); + + if (ajb) + ajb_calc(ab->ajb, af, ab->cur_sz); + + return err; +} + + +/** + * Read PCM samples from the audio buffer. If there is not enough data + * in the audio buffer, silence will be read. + * + * @param ab Audio buffer + * @param af Audio frame (af.sampv, af.sampc and af.fmt needed) + */ +void aubuf_read_auframe(struct aubuf *ab, struct auframe *af) +{ + size_t sz; + bool filling; + enum ajb_state as; + bool drop; + + if (!ab || !af) + return; + + sz = auframe_size(af); + if (!ab->ajb && ab->mode == AUBUF_ADAPTIVE) + ab->ajb = ajb_alloc(ab->silence, ab->wish_sz); + + mtx_lock(ab->lock); + as = ajb_get(ab->ajb, af); + if (as == AJB_LOW) { +#if AUBUF_DEBUG + (void)re_printf("aubuf: inc buffer due to high jitter\n"); + ajb_debug(ab->ajb); +#endif + goto out; + } + + if (ab->fill_sz || ab->cur_sz < sz) { +#if AUBUF_DEBUG + if (!ab->fill_sz) { + ++ab->stats.ur; + (void)re_printf("aubuf: %p underrun " + "(cur=%zu, sz=%zu)\n", + ab, ab->cur_sz, sz); + fflush(stdout); + plot_underrun(ab->ajb); + } +#endif + if (!ab->fill_sz) + ajb_set_ts0(ab->ajb, 0); + + filling = ab->fill_sz > 0; + memset(af->sampv, 0, sz); + if (filling) + goto out; + else + ab->fill_sz = ab->wish_sz; + } + + /* on first read drop old frames */ + drop = ab->live && !ab->started && ab->wish_sz; + while (drop && ab->cur_sz > ab->wish_sz) { + struct frame *f = list_ledata(ab->afl.head); + if (f) { + ab->cur_sz -= mbuf_get_left(f->mb); + mem_deref(f); + } + } + + ab->started = true; + read_auframe(ab, af); + if (as == AJB_HIGH) { +#if AUBUF_DEBUG + (void)re_printf("aubuf: drop a frame to reduce latency\n"); + ajb_debug(ab->ajb); +#endif + read_auframe(ab, af); + } + + out: + + if (ab->fill_sz && ab->fill_sz < ab->pkt_sz) { + if (ab->fill_sz >= sz) + ab->fill_sz -= sz; + else + ab->fill_sz = 0; + } + + mtx_unlock(ab->lock); +} + + +/** + * Timed read PCM samples from the audio buffer. If there is not enough data + * in the audio buffer, silence will be read. + * + * @param ab Audio buffer + * @param ptime Packet time in [ms] + * @param p Buffer where PCM samples are read into + * @param sz Number of bytes to read + * + * @note This does the same as aubuf_read() except that it also takes + * timing into consideration. + * + * @return 0 if valid PCM was read, ETIMEDOUT if no PCM is ready yet + */ +int aubuf_get(struct aubuf *ab, uint32_t ptime, uint8_t *p, size_t sz) +{ + uint64_t now; + int err = 0; + + if (!ab || !ptime) + return EINVAL; + + mtx_lock(ab->lock); + + now = tmr_jiffies(); + if (!ab->ts) + ab->ts = now; + + if (now < ab->ts) { + err = ETIMEDOUT; + goto out; + } + + ab->ts += ptime; + + out: + mtx_unlock(ab->lock); + + if (!err) + aubuf_read(ab, p, sz); + + return err; +} + + +/** + * Flush the audio buffer + * + * @param ab Audio buffer + */ +void aubuf_flush(struct aubuf *ab) +{ + if (!ab) + return; + + mtx_lock(ab->lock); + + list_flush(&ab->afl); + ab->fill_sz = ab->wish_sz; + ab->cur_sz = 0; + ab->wr_sz = 0; + ab->ts = 0; + + mtx_unlock(ab->lock); + ajb_reset(ab->ajb); +} + + +/** + * Audio buffer debug handler, use with fmt %H + * + * @param pf Print function + * @param ab Audio buffer + * + * @return 0 if success, otherwise errorcode + */ +int aubuf_debug(struct re_printf *pf, const struct aubuf *ab) +{ + int err; + + if (!ab) + return 0; + + mtx_lock(ab->lock); + err = re_hprintf(pf, "wish_sz=%zu cur_sz=%zu fill_sz=%zu", + ab->wish_sz, ab->cur_sz, ab->fill_sz); + +#if AUBUF_DEBUG + err |= re_hprintf(pf, " [overrun=%zu underrun=%zu]", + ab->stats.or, ab->stats.ur); +#endif + + mtx_unlock(ab->lock); + + return err; +} + + +/** + * Get the current number of bytes in the audio buffer + * + * @param ab Audio buffer + * + * @return Number of bytes in the audio buffer + */ +size_t aubuf_cur_size(const struct aubuf *ab) +{ + size_t sz; + + if (!ab) + return 0; + + mtx_lock(ab->lock); + sz = ab->cur_sz; + mtx_unlock(ab->lock); + + return sz; +} + + +/** + * Reorder aubuf by auframe->timestamp + * + * @param ab Audio buffer + */ +void aubuf_sort_auframe(struct aubuf *ab) +{ + if (!ab) + return; + + list_sort(&ab->afl, frame_less_equal, NULL); +} + + +/** + * This function is for reporting that the given audio frame was dropped. Its + * timestamp is used to reset the ajb structure to avoid a jump of the computed + * jitter value + * + * @param ab Audio buffer + * @param af Audio frame + */ +void aubuf_drop_auframe(struct aubuf *ab, const struct auframe *af) +{ + if (!ab) + return; + + ajb_set_ts0(ab->ajb, af->timestamp); +} diff --git a/rem/auconv/auconv.c b/rem/auconv/auconv.c new file mode 100644 index 000000000..24fac45ca --- /dev/null +++ b/rem/auconv/auconv.c @@ -0,0 +1,140 @@ +/** + * @file auconv.c Audio sample format converter + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include + + +static inline float ausamp_short2float(int16_t in) +{ + float out; + + out = (float) (in / (1.0 * 0x8000)); + + return out; +} + + +static inline int16_t ausamp_float2short(float in) +{ + double value; + int16_t out; + + value = in * (8.0 * 0x10000000); + + if (value >= (1.0 * 0x7fffffff)) { + out = 32767; + } + else if (value <= (-8.0 * 0x10000000)) { + out = -32768; + } + else + out = (short) (lrint (value) >> 16); + + return out; +} + + +void auconv_from_s16(enum aufmt dst_fmt, void *dst_sampv, + const int16_t *src_sampv, size_t sampc) +{ + float *f; + uint8_t *b; + size_t i; + + if (!dst_sampv || !src_sampv || !sampc) + return; + + switch (dst_fmt) { + + case AUFMT_FLOAT: + f = dst_sampv; + for (i=0; i> 8; + b[3*i+1] = s & 0xff; + b[3*i+0] = 0; + } + break; + + default: + (void)re_fprintf(stderr, "auconv: sample format %d (%s)" + " not supported\n", + dst_fmt, aufmt_name(dst_fmt)); + return; + } +} + + +void auconv_to_s16(int16_t *dst_sampv, enum aufmt src_fmt, + void *src_sampv, size_t sampc) +{ + float *f; + uint8_t *b; + size_t i; + + if (!dst_sampv || !src_sampv || !sampc) + return; + + switch (src_fmt) { + + case AUFMT_FLOAT: + f = src_sampv; + for (i=0; i +#include +#include +#include +#include "aufile.h" + + +/** Audio file state */ +struct aufile { + struct aufile_prm prm; + enum aufile_mode mode; + size_t datasize; + size_t nread; + size_t nwritten; + FILE *f; +}; + + +static int wavfmt_to_aufmt(enum wavfmt fmt, uint16_t bps) +{ + switch (fmt) { + + case WAVE_FMT_PCM: + if (bps != 16) + return -1; + + return AUFMT_S16LE; + + case WAVE_FMT_ALAW: + if (bps != 8) + return -1; + + return AUFMT_PCMA; + + case WAVE_FMT_ULAW: + if (bps != 8) + return -1; + + return AUFMT_PCMU; + + default: + return -1; + } +} + + +static enum wavfmt aufmt_to_wavfmt(enum aufmt fmt) +{ + switch (fmt) { + + case AUFMT_S16LE: return WAVE_FMT_PCM; + case AUFMT_PCMA: return WAVE_FMT_ALAW; + case AUFMT_PCMU: return WAVE_FMT_ULAW; + default: return -1; + } +} + + +static uint16_t aufmt_to_bps(enum aufmt fmt) +{ + switch (fmt) { + + case AUFMT_S16LE: return 16; + case AUFMT_PCMA: return 8; + case AUFMT_PCMU: return 8; + default: return 0; + } +} + + +static void destructor(void *arg) +{ + struct aufile *af = arg; + + if (!af->f) + return; + + /* Update WAV header in write-mode */ + if (af->mode == AUFILE_WRITE && af->nwritten > 0) { + + rewind(af->f); + + (void)wav_header_encode(af->f, aufmt_to_wavfmt(af->prm.fmt), + af->prm.channels, af->prm.srate, + aufmt_to_bps(af->prm.fmt), + af->nwritten); + } + + (void)fclose(af->f); +} + + +/** + * Open a WAVE file for reading or writing + * + * Supported formats: 16-bit PCM, A-law, U-law + * + * @param afp Pointer to allocated Audio file + * @param prm Audio format of the file + * @param filename Filename of the WAV-file to load + * @param mode Read or write mode + * + * @return 0 if success, otherwise errorcode + */ +int aufile_open(struct aufile **afp, struct aufile_prm *prm, + const char *filename, enum aufile_mode mode) +{ + struct wav_fmt fmt; + struct aufile *af; + int aufmt; + int err; + + if (!afp || !filename || (mode == AUFILE_WRITE && !prm)) + return EINVAL; + + af = mem_zalloc(sizeof(*af), destructor); + if (!af) + return ENOMEM; + + af->mode = mode; + + af->f = fopen(filename, mode == AUFILE_READ ? "rb" : "wb"); + if (!af->f) { + err = errno; + goto out; + } + + switch (mode) { + + case AUFILE_READ: + err = wav_header_decode(&fmt, &af->datasize, af->f); + if (err) + goto out; + + aufmt = wavfmt_to_aufmt(fmt.format, fmt.bps); + if (aufmt < 0) { + err = ENOSYS; + goto out; + } + + if (prm) { + prm->srate = fmt.srate; + prm->channels = (uint8_t)fmt.channels; + prm->fmt = aufmt; + } + break; + + case AUFILE_WRITE: + af->prm = *prm; + + err = wav_header_encode(af->f, aufmt_to_wavfmt(prm->fmt), + prm->channels, prm->srate, + aufmt_to_bps(prm->fmt), 0); + break; + + default: + err = ENOSYS; + break; + } + + out: + if (err) + mem_deref(af); + else + *afp = af; + + return err; +} + + +/** + * Read PCM-samples from a WAV file + * + * @param af Audio-file + * @param p Read buffer + * @param sz Size of buffer, on return contains actual read + * + * @return 0 if success, otherwise errorcode + */ +int aufile_read(struct aufile *af, uint8_t *p, size_t *sz) +{ + size_t n; + + if (!af || !p || !sz || af->mode != AUFILE_READ) + return EINVAL; + + if (af->nread >= af->datasize) { + *sz = 0; + return 0; + } + + n = min(*sz, af->datasize - af->nread); + + n = fread(p, 1, n, af->f); + if (ferror(af->f)) + return errno; + + *sz = n; + af->nread += n; + + return 0; +} + + +/** + * Write PCM-samples to a WAV file + * + * @param af Audio-file + * @param p Write buffer + * @param sz Size of buffer + * + * @return 0 if success, otherwise errorcode + */ +int aufile_write(struct aufile *af, const uint8_t *p, size_t sz) +{ + if (!af || !p || !sz || af->mode != AUFILE_WRITE) + return EINVAL; + + if (1 != fwrite(p, sz, 1, af->f)) + return ferror(af->f); + + af->nwritten += sz; + + return 0; +} diff --git a/rem/aufile/aufile.h b/rem/aufile/aufile.h new file mode 100644 index 000000000..6a78cd5a0 --- /dev/null +++ b/rem/aufile/aufile.h @@ -0,0 +1,27 @@ +/** + * @file aufile.h Audio File -- internal API + * + * Copyright (C) 2010 Creytiv.com + */ + + +enum wavfmt { + WAVE_FMT_PCM = 0x0001, + WAVE_FMT_ALAW = 0x0006, + WAVE_FMT_ULAW = 0x0007, +}; + +/** WAVE format sub-chunk */ +struct wav_fmt { + uint16_t format; + uint16_t channels; + uint32_t srate; + uint32_t byterate; + uint16_t block_align; + uint16_t bps; + uint16_t extra; +}; + +int wav_header_encode(FILE *f, uint16_t format, uint16_t channels, + uint32_t srate, uint16_t bps, size_t bytes); +int wav_header_decode(struct wav_fmt *fmt, size_t *datasize, FILE *f); diff --git a/rem/aufile/wave.c b/rem/aufile/wave.c new file mode 100644 index 000000000..c10c5e85a --- /dev/null +++ b/rem/aufile/wave.c @@ -0,0 +1,205 @@ +/** + * @file wave.c WAVE format encoding and decoding + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include +#include "aufile.h" + + +enum { + WAVE_FMT_SIZE = 16 +}; + + +/** WAV-file chunk */ +struct wav_chunk { + uint8_t id[4]; + uint32_t size; +}; + + +static int write_u16(FILE *f, uint16_t v) +{ + v = sys_htols(v); + + if (1 != fwrite(&v, sizeof(v), 1, f)) + return ferror(f); + + return 0; +} + + +static int write_u32(FILE *f, uint32_t v) +{ + v = sys_htoll(v); + + if (1 != fwrite(&v, sizeof(v), 1, f)) + return ferror(f); + + return 0; +} + + +static int read_u16(FILE *f, uint16_t *v) +{ + uint16_t vle; + + if (1 != fread(&vle, sizeof(vle), 1, f)) + return ferror(f); + + *v = sys_ltohs(vle); + + return 0; +} + + +static int read_u32(FILE *f, uint32_t *v) +{ + uint32_t vle; + + if (1 != fread(&vle, sizeof(vle), 1, f)) + return ferror(f); + + *v = sys_ltohl(vle); + + return 0; + +} + + +static int chunk_encode(FILE *f, const char *id, size_t sz) +{ + if (1 != fwrite(id, 4, 1, f)) + return ferror(f); + + return write_u32(f, (uint32_t)sz); +} + + +static int chunk_decode(struct wav_chunk *chunk, FILE *f) +{ + if (1 != fread(chunk->id, sizeof(chunk->id), 1, f)) + return ferror(f); + + return read_u32(f, &chunk->size); +} + + +int wav_header_encode(FILE *f, uint16_t format, uint16_t channels, + uint32_t srate, uint16_t bps, size_t bytes) +{ + int err; + + err = chunk_encode(f, "RIFF", 36 + bytes); + if (err) + return err; + + if (1 != fwrite("WAVE", 4, 1, f)) + return ferror(f); + + err = chunk_encode(f, "fmt ", WAVE_FMT_SIZE); + if (err) + return err; + + err = write_u16(f, format); + err |= write_u16(f, channels); + err |= write_u32(f, srate); + err |= write_u32(f, srate * channels * bps / 8); + err |= write_u16(f, channels * bps / 8); + err |= write_u16(f, bps); + if (err) + return err; + + return chunk_encode(f, "data", bytes); +} + + +int wav_header_decode(struct wav_fmt *fmt, size_t *datasize, FILE *f) +{ + struct wav_chunk header, format, chunk; + uint8_t rifftype[4]; /* "WAVE" */ + int err = 0; + + err = chunk_decode(&header, f); + if (err) + return err; + + if (memcmp(header.id, "RIFF", 4)) { + (void)re_fprintf(stderr, "aufile: expected RIFF (%b)\n", + header.id, sizeof(header.id)); + return EBADMSG; + } + + if (1 != fread(rifftype, sizeof(rifftype), 1, f)) + return ferror(f); + + if (memcmp(rifftype, "WAVE", 4)) { + (void)re_fprintf(stderr, "aufile: expected WAVE (%b)\n", + rifftype, sizeof(rifftype)); + return EBADMSG; + } + + err = chunk_decode(&format, f); + if (err) + return err; + + if (memcmp(format.id, "fmt ", 4)) { + (void)re_fprintf(stderr, "aufile: expected fmt (%b)\n", + format.id, sizeof(format.id)); + return EBADMSG; + } + + if (format.size < WAVE_FMT_SIZE) + return EBADMSG; + + err = read_u16(f, &fmt->format); + err |= read_u16(f, &fmt->channels); + err |= read_u32(f, &fmt->srate); + err |= read_u32(f, &fmt->byterate); + err |= read_u16(f, &fmt->block_align); + err |= read_u16(f, &fmt->bps); + if (err) + return err; + + /* skip any extra bytes */ + if (format.size >= (WAVE_FMT_SIZE + 2)) { + + err = read_u16(f, &fmt->extra); + if (err) + return err; + + if (fmt->extra > 0) { + if (fseek(f, fmt->extra, SEEK_CUR)) + return errno; + } + } + + /* fast forward to "data" chunk */ + for (;;) { + + err = chunk_decode(&chunk, f); + if (err) + return err; + + if (chunk.size > header.size) { + (void)re_fprintf(stderr, "chunk size too large" + " (%u > %u)\n", + chunk.size, header.size); + return EBADMSG; + } + + if (0 == memcmp(chunk.id, "data", 4)) { + *datasize = chunk.size; + break; + } + + if (fseek(f, chunk.size, SEEK_CUR) < 0) + return errno; + } + + return 0; +} diff --git a/rem/auframe/auframe.c b/rem/auframe/auframe.c new file mode 100644 index 000000000..60d1b2de2 --- /dev/null +++ b/rem/auframe/auframe.c @@ -0,0 +1,116 @@ +/** + * @file auframe.c Audio frame + * + * Copyright (C) 2010 - 2020 Alfred E. Heggestad + */ + +#include +#include +#include +#include +#include + + +/** + * Initialize an audio frame + * + * @param af Audio frame + * @param fmt Sample format (enum aufmt) + * @param sampv Audio samples + * @param sampc Total number of audio samples + * @param srate Samplerate + * @param ch Channels + */ +void auframe_init(struct auframe *af, enum aufmt fmt, void *sampv, + size_t sampc, uint32_t srate, uint8_t ch) +{ + if (!af) + return; + + if (0 == aufmt_sample_size(fmt)) { + re_printf("auframe: init: unsupported sample format %d (%s)\n", + fmt, aufmt_name(fmt)); + } + + memset(af, 0, sizeof(*af)); + + af->fmt = fmt; + af->sampv = sampv; + af->sampc = sampc; + af->srate = srate; + af->level = AULEVEL_UNDEF; + af->ch = ch; + af->id = 0; +} + + +/** + * Get the size of an audio frame + * + * @param af Audio frame + * + * @return Number of bytes + */ +size_t auframe_size(const struct auframe *af) +{ + size_t sz; + + if (!af) + return 0; + + sz = aufmt_sample_size(af->fmt); + if (sz == 0) { + re_printf("auframe: size: illegal format %d (%s)\n", + af->fmt, aufmt_name(af->fmt)); + sz = 1; + } + + return af->sampc * sz; +} + + +/** + * Silence all samples in an audio frame + * + * @param af Audio frame + */ +void auframe_mute(struct auframe *af) +{ + if (!af) + return; + + memset(af->sampv, 0, auframe_size(af)); +} + + +/** + * Get audio level (only calculated once) + * + * @note Set af->level = AULEVEL_UNDEF to force re-calculation + * + * @param af Audio frame + * + * @return Audio level expressed in dBov on success and AULEVEL_UNDEF on error + */ +double auframe_level(struct auframe *af) +{ + if (!af) + return AULEVEL_UNDEF; + + if (af->fmt == AUFMT_RAW) + return AULEVEL_UNDEF; + + if (af->level == AULEVEL_UNDEF) + af->level = aulevel_calc_dbov(af->fmt, af->sampv, af->sampc); + + return af->level; +} + + +uint64_t auframe_bytes_to_timestamp(const struct auframe *af, size_t n) +{ + size_t sample_size = aufmt_sample_size(af->fmt); + + return ((uint64_t) n) * AUDIO_TIMEBASE / + (af->srate * af->ch * sample_size); +} diff --git a/rem/aulevel/aulevel.c b/rem/aulevel/aulevel.c new file mode 100644 index 000000000..832bc19ce --- /dev/null +++ b/rem/aulevel/aulevel.c @@ -0,0 +1,138 @@ +/** + * @file aulevel/aulevel.c Audio level + * + * Copyright (C) 2017 Creytiv.com + */ + +#include +#include +#include + + +/** + * Generic routine to calculate RMS (Root-Mean-Square) from + * a set of signed 16-bit values + * + * \verbatim + + .--------------- + | N-1 + | ----. + | \ + | \ 2 + | | s[n] + | / + | / + _ | ----' + \ | n=0 + \ | ------------ + \| N + + \endverbatim + * + * @param data Array of signed 16-bit values + * @param len Number of values + * + * @return RMS value from 0 to 32768 + */ +static double calc_rms_s16(const int16_t *data, size_t len) +{ + int64_t sum = 0; + size_t i; + + if (!data || !len) + return .0; + + for (i = 0; i < len; i++) { + sum += data[i] * data[i]; + } + + return sqrt(sum / (double)len); +} + + +static double calc_rms_s32(const int32_t *data, size_t len) +{ + double sum = 0; + size_t i; + + if (!data || !len) + return .0; + + for (i = 0; i < len; i++) { + const double sample = data[i]; + + sum += sample * sample; + } + + return sqrt(sum / (double)len); +} + + +static double calc_rms_float(const float *data, size_t len) +{ + double sum = 0; + size_t i; + + if (!data || !len) + return .0; + + for (i = 0; i < len; i++) { + const double sample = data[i]; + + sum += sample * sample; + } + + return sqrt(sum / (double)len); +} + + +/** + * Calculate the audio level in dBov from a set of audio samples. + * dBov is the level, in decibels, relative to the overload point + * of the system + * + * @param fmt Sample format (enum aufmt) + * @param sampv Audio samples + * @param sampc Number of audio samples + * + * @return Audio level expressed in dBov on success and AULEVEL_UNDEF on error + */ +double aulevel_calc_dbov(int fmt, const void *sampv, size_t sampc) +{ + static const double peak_s16 = 32767.0; + static const double peak_s32 = 2147483647.0; + double rms, dbov; + + if (!sampv || !sampc) + return AULEVEL_UNDEF; + + switch (fmt) { + + case AUFMT_S16LE: + rms = calc_rms_s16(sampv, sampc) / peak_s16; + break; + + case AUFMT_S32LE: + rms = calc_rms_s32(sampv, sampc) / peak_s32; + break; + + case AUFMT_FLOAT: + rms = calc_rms_float(sampv, sampc) / 1.0; + break; + + default: + re_printf("aulevel: sample format not supported (%s)\n", + aufmt_name(fmt)); + return AULEVEL_UNDEF; + } + + dbov = 20 * log10(rms); + + if (dbov < AULEVEL_MIN) + dbov = AULEVEL_MIN; + else if (dbov > AULEVEL_MAX) + dbov = AULEVEL_MAX; + + return dbov; +} diff --git a/rem/aumix/aumix.c b/rem/aumix/aumix.c new file mode 100644 index 000000000..4ac348bb1 --- /dev/null +++ b/rem/aumix/aumix.c @@ -0,0 +1,532 @@ +/** + * @file aumix.c Audio Mixer + * + * Copyright (C) 2010 Creytiv.com + */ + +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include + + +/** Defines an Audio mixer */ +struct aumix { + mtx_t mutex; + cnd_t cond; + struct list srcl; + thrd_t thread; + struct aufile *af; + uint32_t ptime; + uint32_t frame_size; + uint32_t srate; + uint8_t ch; + aumix_record_h *recordh; + bool run; +}; + +/** Defines an Audio mixer source */ +struct aumix_source { + struct le le; + struct auframe af; + int16_t *frame; + struct aubuf *aubuf; + struct aumix *mix; + aumix_frame_h *fh; + aumix_read_h *readh; + void *arg; + bool muted; +}; + + +static void dummy_frame_handler(const int16_t *sampv, size_t sampc, void *arg) +{ + (void)sampv; + (void)sampc; + (void)arg; +} + + +static void destructor(void *arg) +{ + struct aumix *mix = arg; + + if (mix->run) { + + mtx_lock(&mix->mutex); + mix->run = false; + cnd_signal(&mix->cond); + mtx_unlock(&mix->mutex); + + thrd_join(mix->thread, NULL); + } + + mem_deref(mix->af); +} + + +static void source_destructor(void *arg) +{ + struct aumix_source *src = arg; + + if (src->le.list) { + mtx_lock(&src->mix->mutex); + list_unlink(&src->le); + mtx_unlock(&src->mix->mutex); + } + + mem_deref(src->aubuf); + mem_deref(src->frame); + mem_deref(src->mix); +} + + +static int aumix_thread(void *arg) +{ + uint8_t *silence, *frame, *base_frame; + struct aumix *mix = arg; + int16_t *mix_frame; + uint64_t ts = 0; + + silence = mem_zalloc(mix->frame_size*2, NULL); + frame = mem_alloc(mix->frame_size*2, NULL); + mix_frame = mem_alloc(mix->frame_size*2, NULL); + + if (!silence || !frame || !mix_frame) + goto out; + + mtx_lock(&mix->mutex); + + while (mix->run) { + + struct le *le; + uint64_t now; + + if (!mix->srcl.head) { + mix->af = mem_deref(mix->af); + cnd_wait(&mix->cond, &mix->mutex); + ts = 0; + } + else { + mtx_unlock(&mix->mutex); + sys_usleep(4000); + mtx_lock(&mix->mutex); + } + + now = tmr_jiffies(); + if (!ts) + ts = now; + + if (ts > now) + continue; + + if (mix->af) { + + size_t n = mix->frame_size*2; + + if (aufile_read(mix->af, frame, &n) || n == 0) { + mix->af = mem_deref(mix->af); + base_frame = silence; + } + else if (n < mix->frame_size*2) { + memset(frame + n, 0, mix->frame_size*2 - n); + mix->af = mem_deref(mix->af); + base_frame = frame; + } + else { + base_frame = frame; + } + } + else { + base_frame = silence; + } + + for (le = mix->srcl.head; le; le = le->next) { + + struct aumix_source *src = le->data; + + if (src->muted) + continue; + + if (src->readh) + src->readh(&src->af, src->arg); + else + aubuf_read_auframe(src->aubuf, &src->af); + + if (mix->recordh) + mix->recordh(&src->af); + } + + for (le = mix->srcl.head; le; le = le->next) { + + struct aumix_source *src = le->data; + struct le *cle; + + memcpy(mix_frame, base_frame, mix->frame_size * 2); + + LIST_FOREACH(&mix->srcl, cle) + { + + struct aumix_source *csrc = cle->data; + int32_t sample; + + /* skip self */ + if (csrc == src) + continue; + + if (csrc->muted) + continue; + + for (size_t i = 0; i < mix->frame_size; i++) { + sample = mix_frame[i] + csrc->frame[i]; + + /* soft clipping */ + if (sample >= 32767) + sample = 32767; + if (sample <= -32767) + sample = -32767; + + mix_frame[i] = (int16_t)sample; + } + } + + src->fh(mix_frame, mix->frame_size, src->arg); + } + + ts += mix->ptime; + } + + mtx_unlock(&mix->mutex); + + out: + mem_deref(mix_frame); + mem_deref(silence); + mem_deref(frame); + + return 0; +} + + +/** + * Allocate a new Audio mixer + * + * @param mixp Pointer to allocated audio mixer + * @param srate Sample rate in [Hz] + * @param ch Number of channels + * @param ptime Packet time in [ms] + * + * @return 0 for success, otherwise error code + */ +int aumix_alloc(struct aumix **mixp, uint32_t srate, + uint8_t ch, uint32_t ptime) +{ + struct aumix *mix; + int err; + + if (!mixp || !srate || !ch || !ptime) + return EINVAL; + + mix = mem_zalloc(sizeof(*mix), destructor); + if (!mix) + return ENOMEM; + + mix->ptime = ptime; + mix->frame_size = srate * ch * ptime / 1000; + mix->srate = srate; + mix->ch = ch; + mix->recordh = NULL; + + err = mtx_init(&mix->mutex, mtx_plain) != thrd_success; + if (err) { + err = ENOMEM; + goto out; + } + + err = cnd_init(&mix->cond) != thrd_success; + if (err) { + err = ENOMEM; + goto out; + } + + mix->run = true; + + err = thread_create_name(&mix->thread, "aumix", aumix_thread, mix); + if (err) { + mix->run = false; + goto out; + } + + out: + if (err) + mem_deref(mix); + else + *mixp = mix; + + return err; +} + + +/** + * Add record handler + * + * @param mix Audio mixer + * @param recordh Record Handler + */ +void aumix_recordh(struct aumix *mix, aumix_record_h *recordh) +{ + if (!mix) + return; + + mtx_lock(&mix->mutex); + mix->recordh = recordh; + mtx_unlock(&mix->mutex); +} + + +/** + * Load audio file for mixer announcements + * + * @param mix Audio mixer + * @param filepath Filename of audio file with complete path + * + * @return 0 for success, otherwise error code + */ +int aumix_playfile(struct aumix *mix, const char *filepath) +{ + struct aufile_prm prm; + struct aufile *af; + int err; + + if (!mix || !filepath) + return EINVAL; + + err = aufile_open(&af, &prm, filepath, AUFILE_READ); + if (err) + return err; + + if (prm.fmt != AUFMT_S16LE || prm.srate != mix->srate || + prm.channels != mix->ch) { + mem_deref(af); + return EINVAL; + } + + mtx_lock(&mix->mutex); + mem_deref(mix->af); + mix->af = af; + mtx_unlock(&mix->mutex); + + return 0; +} + + +/** + * Count number of audio sources in the audio mixer + * + * @param mix Audio mixer + * + * @return Number of audio sources + */ +uint32_t aumix_source_count(const struct aumix *mix) +{ + if (!mix) + return 0; + + return list_count(&mix->srcl); +} + + +/** + * Allocate an audio mixer source + * + * @param srcp Pointer to allocated audio source + * @param mix Audio mixer + * @param fh Mixer frame handler + * @param arg Handler argument + * + * @return 0 for success, otherwise error code + */ +int aumix_source_alloc(struct aumix_source **srcp, struct aumix *mix, + aumix_frame_h *fh, void *arg) +{ + struct aumix_source *src; + size_t sz; + int err; + + if (!srcp || !mix) + return EINVAL; + + src = mem_zalloc(sizeof(*src), source_destructor); + if (!src) + return ENOMEM; + + src->mix = mem_ref(mix); + src->fh = fh ? fh : dummy_frame_handler; + src->arg = arg; + src->muted = false; + + sz = mix->frame_size*2; + + src->frame = mem_alloc(sz, NULL); + if (!src->frame) { + err = ENOMEM; + goto out; + } + + auframe_init(&src->af, AUFMT_S16LE, src->frame, mix->frame_size, + mix->srate, mix->ch); + + err = aubuf_alloc(&src->aubuf, sz * 6, sz * 12); + if (err) + goto out; + + out: + if (err) + mem_deref(src); + else + *srcp = src; + + return err; +} + + +/** + * Add source read handler (alternative to aumix_source_put) + * + * @param src Audio mixer source + * @param readh Read Handler + */ +void aumix_source_readh(struct aumix_source *src, aumix_read_h *readh) +{ + if (!src || !src->mix) + return; + + mtx_lock(&src->mix->mutex); + src->readh = readh; + mtx_unlock(&src->mix->mutex); +} + + +/** + * Mute/unmute aumix source + * + * @param src Audio mixer source + * @param mute True to mute, false to unmute + */ +void aumix_source_mute(struct aumix_source *src, bool mute) +{ + if (!src) + return; + + src->muted = mute; +} + + +/** + * Enable/disable aumix source + * + * @param src Audio mixer source + * @param enable True to enable, false to disable + */ +void aumix_source_enable(struct aumix_source *src, bool enable) +{ + struct aumix *mix; + + if (!src) + return; + + if (src->le.list && enable) + return; + + if (!src->le.list && !enable) + return; + + mix = src->mix; + + mtx_lock(&mix->mutex); + + if (enable) { + list_append(&mix->srcl, &src->le, src); + cnd_signal(&mix->cond); + } + else { + list_unlink(&src->le); + } + + mtx_unlock(&mix->mutex); +} + + +/** + * Write PCM samples for a given source to the audio mixer + * + * @param src Audio mixer source + * @param sampv PCM samples + * @param sampc Number of samples + * + * @return 0 for success, otherwise error code + */ +int aumix_source_put(struct aumix_source *src, const int16_t *sampv, + size_t sampc) +{ + if (!src || !sampv) + return EINVAL; + + return aubuf_write_samp(src->aubuf, sampv, sampc); +} + + +/** + * Flush the audio buffer of a given audio mixer source + * + * @param src Audio mixer source + */ +void aumix_source_flush(struct aumix_source *src) +{ + if (!src) + return; + + aubuf_flush(src->aubuf); +} + + +/** + * Audio mixer debug handler + * + * @param pf Print function + * @param mix Audio mixer + * + * @return 0 if success, otherwise errorcode + */ +int aumix_debug(struct re_printf *pf, struct aumix *mix) +{ + struct le *le; + int err = 0; + + if (!pf || !mix) + return EINVAL; + + re_hprintf(pf, "aumix debug:\n"); + mtx_lock(&mix->mutex); + LIST_FOREACH(&mix->srcl, le) + { + struct aumix_source *src = le->data; + re_hprintf(pf, "\tsource: %p muted=%d ", src, src->muted); + err = aubuf_debug(pf, src->aubuf); + if (err) + goto out; + re_hprintf(pf, "\n"); + } + +out: + mtx_unlock(&mix->mutex); + return err; +} diff --git a/rem/auresamp/resamp.c b/rem/auresamp/resamp.c new file mode 100644 index 000000000..0a04a608b --- /dev/null +++ b/rem/auresamp/resamp.c @@ -0,0 +1,340 @@ +/** + * @file resamp.c Audio Resampler + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include +#include + + +/* 48kHz sample-rate, 4kHz cutoff (pass 0-3kHz, stop 5-24kHz) */ +static const int16_t fir_48_4[] = { + 62, -176, -329, -556, -802, -1005, -1090, -985, + -636, -23, 826, 1837, 2894, 3859, 4595, 4994, + 4994, 4595, 3859, 2894, 1837, 826, -23, -636, + -985, -1090, -1005, -802, -556, -329, -176, 62 +}; + +/* 48kHz sample-rate, 8kHz cutoff (pass 0-7kHz, stop 9-24kHz) */ +static const int16_t fir_48_8[] = { + 238, 198, -123, -738, -1268, -1204, -380, 714, + 1164, 376, -1220, -2206, -1105, 2395, 6909, 10069, + 10069, 6909, 2395, -1105, -2206, -1220, 376, 1164, + 714, -380, -1204, -1268, -738, -123, 198, 238 +}; + +/* 16kHz sample-rate, 4kHz cutoff and 32kHz sample-rate, 8 kHz cutoff */ +static const int16_t fir_16_4[] = { + 22, 60, -41, -157, -9, 322, 195, -490, -613, 539, 1362, -229, + -2657, -1101, 6031, 13167, 13167, 6031, -1101, -2657, -229, + 1362, 539, -613, -490, 195, 322, -9, -157, -41, 60, 22 +}; + +static void upsample_mono2mono(int16_t *outv, const int16_t *inv, + size_t inc, unsigned ratio) +{ + unsigned i; + + while (inc >= 1) { + + for (i=0; i= 1) { + + for (i=0; i= 2) { + + const int16_t s = inv[0]/2 + inv[1]/2; + + for (i=0; i= 2) { + + for (i=0; i= ratio) { + + *outv++ = *inv; + + inv += ratio; + inc -= ratio; + } +} + + +static void downsample_mono2stereo(int16_t *outv, const int16_t *inv, + size_t inc, unsigned ratio) +{ + while (inc >= ratio) { + + *outv++ = *inv; + *outv++ = *inv; + + inv += ratio; + inc -= ratio; + } +} + + +static void downsample_stereo2mono(int16_t *outv, const int16_t *inv, + size_t inc, unsigned ratio) +{ + ratio *= 2; + + while (inc >= ratio) { + + *outv++ = inv[0]/2 + inv[1]/2; + + inv += ratio; + inc -= ratio; + } +} + + +static void downsample_stereo2stereo(int16_t *outv, const int16_t *inv, + size_t inc, unsigned ratio) +{ + ratio *= 2; + + while (inc >= ratio) { + + *outv++ = inv[0]; + *outv++ = inv[1]; + + inv += ratio; + inc -= ratio; + } +} + + +/** + * Initialize a resampler object + * + * @param rs Resampler to initialize + */ +void auresamp_init(struct auresamp *rs) +{ + if (!rs) + return; + + memset(rs, 0, sizeof(*rs)); + fir_reset(&rs->fir); +} + + +/** + * Configure a resampler object + * + * @note The sample rate ratio must be an integer + * + * @param rs Resampler + * @param irate Input sample rate + * @param ich Input channel count + * @param orate Output sample rate + * @param och Output channel count + * + * @return 0 if success, otherwise error code + */ +int auresamp_setup(struct auresamp *rs, uint32_t irate, unsigned ich, + uint32_t orate, unsigned och) +{ + if (!rs || !irate || !ich || !orate || !och) + return EINVAL; + + if (orate == irate && och == ich) { + auresamp_init(rs); + return 0; + } + + if (orate >= irate) { + + if (orate % irate) + return ENOTSUP; + + if (ich == 1 && och == 1) + rs->resample = upsample_mono2mono; + else if (ich == 1 && och == 2) + rs->resample = upsample_mono2stereo; + else if (ich == 2 && och == 1) + rs->resample = upsample_stereo2mono; + else if (ich == 2 && och == 2) + rs->resample = upsample_stereo2stereo; + else + return ENOTSUP; + + if (!rs->up || orate != rs->orate || och != rs->och) + fir_reset(&rs->fir); + + rs->ratio = orate / irate; + rs->up = true; + + if (orate == irate) { + rs->tapv = NULL; + rs->tapc = 0; + } + else if (orate == 48000 && irate == 16000) { + rs->tapv = fir_48_8; + rs->tapc = RE_ARRAY_SIZE(fir_48_8); + } + else if ((orate == 16000 && irate == 8000) || + (orate == 32000 && irate == 16000)) { + rs->tapv = fir_16_4; + rs->tapc = RE_ARRAY_SIZE(fir_16_4); + } + else { + rs->tapv = fir_48_4; + rs->tapc = RE_ARRAY_SIZE(fir_48_4); + } + } + else { + if (irate % orate) + return ENOTSUP; + + if (ich == 1 && och == 1) + rs->resample = downsample_mono2mono; + else if (ich == 1 && och == 2) + rs->resample = downsample_mono2stereo; + else if (ich == 2 && och == 1) + rs->resample = downsample_stereo2mono; + else if (ich == 2 && och == 2) + rs->resample = downsample_stereo2stereo; + else + return ENOTSUP; + + if (rs->up || irate != rs->irate || ich != rs->ich) + fir_reset(&rs->fir); + + rs->ratio = irate / orate; + rs->up = false; + + if (irate == 48000 && orate == 16000) { + rs->tapv = fir_48_8; + rs->tapc = RE_ARRAY_SIZE(fir_48_8); + } + else if ((irate == 16000 && orate == 8000) || + (irate == 32000 && orate == 16000)) { + rs->tapv = fir_16_4; + rs->tapc = RE_ARRAY_SIZE(fir_16_4); + } + else { + rs->tapv = fir_48_4; + rs->tapc = RE_ARRAY_SIZE(fir_48_4); + } + } + + rs->orate = orate; + rs->och = och; + rs->irate = irate; + rs->ich = ich; + + return 0; +} + + +/** + * Resample + * + * @note When downsampling, the input count must be divisible by rate ratio + * + * @param rs Resampler + * @param outv Output samples + * @param outc Output sample count (in/out) + * @param inv Input samples + * @param inc Input sample count + * + * @return 0 if success, otherwise error code + */ +int auresamp(struct auresamp *rs, int16_t *outv, size_t *outc, + const int16_t *inv, size_t inc) +{ + size_t incc, outcc; + + if (!rs || !rs->resample || !outv || !outc || !inv) + return EINVAL; + + incc = inc / rs->ich; + + if (rs->up) { + outcc = incc * rs->ratio; + + if (*outc < outcc * rs->och) + return ENOMEM; + + rs->resample(outv, inv, inc, rs->ratio); + + *outc = outcc * rs->och; + + if (rs->tapv) + fir_filter(&rs->fir, outv, outv, *outc, rs->och, + rs->tapv, rs->tapc); + } + else { + outcc = incc / rs->ratio; + + if (*outc < outcc * rs->och || *outc < inc) + return ENOMEM; + + fir_filter(&rs->fir, outv, inv, inc, rs->ich, + rs->tapv, rs->tapc); + + rs->resample(outv, outv, inc, rs->ratio); + + *outc = outcc * rs->och; + } + + return 0; +} diff --git a/rem/autone/tone.c b/rem/autone/tone.c new file mode 100644 index 000000000..1f8db111a --- /dev/null +++ b/rem/autone/tone.c @@ -0,0 +1,98 @@ +/** + * @file tone.c Audio Tones + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include + + +#define SCALE (32767) +#define DTMF_AMP (5) + +#if !defined (M_PI) +#define M_PI 3.14159265358979323846264338327 +#endif + + +static inline uint32_t digit2lo(int digit) +{ + switch (digit) { + + case '1': case '2': case '3': case 'A': return 697; + case '4': case '5': case '6': case 'B': return 770; + case '7': case '8': case '9': case 'C': return 852; + case '*': case '0': case '#': case 'D': return 941; + default: return 0; + } +} + + +static inline uint32_t digit2hi(int digit) +{ + switch (digit) { + + case '1': case '4': case '7': case '*': return 1209; + case '2': case '5': case '8': case '0': return 1336; + case '3': case '6': case '9': case '#': return 1477; + case 'A': case 'B': case 'C': case 'D': return 1633; + default: return 0; + } +} + + +/** + * Generate a dual-tone sine wave into a PCM buffer + * + * @param mb Buffer for PCM samples + * @param srate Sample rate in [Hz] + * @param f1 Frequency number one + * @param l1 Level of f1 from 0-100 + * @param f2 Frequency number two + * @param l2 Level of f2 from 0-100 + * + * @return 0 for success, otherwise error code + */ +int autone_sine(struct mbuf *mb, uint32_t srate, + uint32_t f1, int l1, uint32_t f2, int l2) +{ + double d1, d2; + uint32_t i; + int err = 0; + + if (!mb || !srate) + return EINVAL; + + d1 = 1.0f * f1 / srate; + d2 = 1.0f * f2 / srate; + + for (i=0; i +#include +#include +#include +#include +#include +#include + + +#define AVC_CONFIG_VERSION 1 +#define SPS_MASK 0xe0 + + +int avc_config_encode(struct mbuf *mb, uint8_t profile_ind, + uint8_t profile_compat, uint8_t level_ind, + uint16_t sps_length, const uint8_t *sps, + uint16_t pps_length, const uint8_t *pps) +{ + int err; + + if (!mb || !sps || !pps) + return EINVAL; + + err = mbuf_write_u8(mb, AVC_CONFIG_VERSION); + + err |= mbuf_write_u8(mb, profile_ind); + err |= mbuf_write_u8(mb, profile_compat); + err |= mbuf_write_u8(mb, level_ind); + + err |= mbuf_write_u8(mb, 0xfc | (4-1)); + + /* SPS */ + err |= mbuf_write_u8(mb, SPS_MASK | 1); + err |= mbuf_write_u16(mb, htons(sps_length)); + err |= mbuf_write_mem(mb, sps, sps_length); + + /* PPS */ + err |= mbuf_write_u8(mb, 1); + err |= mbuf_write_u16(mb, htons(pps_length)); + err |= mbuf_write_mem(mb, pps, pps_length); + + return err; +} + + +int avc_config_decode(struct avc_config *conf, struct mbuf *mb) +{ + uint8_t version, length_size, count; + + if (!conf || !mb) + return EINVAL; + + if (mbuf_get_left(mb) < 5) + return EBADMSG; + + version = mbuf_read_u8(mb); + conf->profile_ind = mbuf_read_u8(mb); + conf->profile_compat = mbuf_read_u8(mb); + conf->level_ind = mbuf_read_u8(mb); + length_size = mbuf_read_u8(mb) & 0x03; + + if (version != AVC_CONFIG_VERSION || length_size != 3) + return EPROTO; + + /* SPS */ + if (mbuf_get_left(mb) < 3) + return EBADMSG; + + count = mbuf_read_u8(mb) & 0x1f; + conf->sps_len = ntohs(mbuf_read_u16(mb)); + + if (count != 1 || conf->sps_len > sizeof(conf->sps)) + return EOVERFLOW; + + if (mbuf_get_left(mb) < conf->sps_len) + return EBADMSG; + + mbuf_read_mem(mb, conf->sps, conf->sps_len); + + /* PPS */ + if (mbuf_get_left(mb) < 3) + return EBADMSG; + + count = mbuf_read_u8(mb); + conf->pps_len = ntohs(mbuf_read_u16(mb)); + + if (count != 1 || conf->pps_len > sizeof(conf->pps)) + return EOVERFLOW; + + if (mbuf_get_left(mb) < conf->pps_len) + return EBADMSG; + + mbuf_read_mem(mb, conf->pps, conf->pps_len); + + return 0; +} diff --git a/rem/dtmf/dec.c b/rem/dtmf/dec.c new file mode 100644 index 000000000..9e6525e30 --- /dev/null +++ b/rem/dtmf/dec.c @@ -0,0 +1,193 @@ +/** + * @file dtmf/dec.c DTMF Decoder + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include + + +#define BLOCK_SIZE 102 /* At 8kHz sample rate */ +#define THRESHOLD 16439.10631 /* -42dBm0 / bsize^2 */ +#define NORMAL_TWIST 6.309573 /* 8dB */ +#define REVERSE_TWIST 2.511886 /* 4dB */ +#define RELATIVE_KEY 6.309573 /* 8dB */ +#define RELATIVE_SUM 0.822243 /* -0.85dB */ + + +static const double fx[4] = { 1209.0, 1336.0, 1477.0, 1633.0 }; +static const double fy[4] = { 697.0, 770.0, 852.0, 941.0 }; + +static const char keyv[4][4] = {{'1', '2', '3', 'A'}, + {'4', '5', '6', 'B'}, + {'7', '8', '9', 'C'}, + {'*', '0', '#', 'D'}}; + + +struct dtmf_dec { + struct goertzel gx[4], gy[4]; + dtmf_dec_h *dech; + void *arg; + double threshold; + double energy; + double efac; + unsigned bsize; + unsigned bidx; + char digit, digit1; +}; + + +static char decode_digit(struct dtmf_dec *dec) +{ + unsigned i, x = 0, y = 0; + double ex[4], ey[4]; + + for (i=0; i<4; i++) { + + ex[i] = goertzel_result(&dec->gx[i]); + ey[i] = goertzel_result(&dec->gy[i]); + + if (ex[i] > ex[x]) + x = i; + + if (ey[i] > ey[y]) + y = i; + } + + if (ex[x] < dec->threshold || + ey[y] < dec->threshold) + return 0; + + if (ex[x] > ey[y] * NORMAL_TWIST || + ey[y] > ex[x] * REVERSE_TWIST) + return 0; + + for (i=0; i<4; i++) { + + if ((i != x && ex[i] * RELATIVE_KEY > ex[x]) || + (i != y && ey[i] * RELATIVE_KEY > ey[y])) + return 0; + } + + if ((ex[x] + ey[y]) < dec->efac * dec->energy) + return 0; + + return keyv[y][x]; +} + + +/** + * Allocate a DTMF decoder instance + * + * @param decp Pointer to allocated decoder + * @param srate Sample rate + * @param ch Number of channels + * @param dech Decode handler + * @param arg Handler argument + * + * @return 0 if success, otherwise errorcode + */ +int dtmf_dec_alloc(struct dtmf_dec **decp, unsigned srate, unsigned ch, + dtmf_dec_h *dech, void *arg) +{ + struct dtmf_dec *dec; + + if (!decp || !dech || !srate || !ch) + return EINVAL; + + dec = mem_zalloc(sizeof(*dec), NULL); + if (!dec) + return ENOMEM; + + dtmf_dec_reset(dec, srate, ch); + + dec->dech = dech; + dec->arg = arg; + + *decp = dec; + + return 0; +} + + +/** + * Reset and configure DTMF decoder state + * + * @param dec DTMF decoder + * @param srate Sample rate + * @param ch Number of channels + */ +void dtmf_dec_reset(struct dtmf_dec *dec, unsigned srate, unsigned ch) +{ + unsigned i; + + if (!dec || !srate || !ch) + return; + + srate *= ch; + + for (i=0; i<4; i++) { + goertzel_init(&dec->gx[i], fx[i], srate); + goertzel_init(&dec->gy[i], fy[i], srate); + } + + dec->bsize = (BLOCK_SIZE * srate) / 8000; + dec->threshold = THRESHOLD * dec->bsize * dec->bsize; + dec->efac = RELATIVE_SUM * dec->bsize; + + dec->energy = 0.0; + dec->bidx = 0; + dec->digit = 0; + dec->digit1 = 0; +} + + +/** + * Decode DTMF from input audio samples + * + * @param dec DTMF decoder + * @param sampv Buffer with audio samples + * @param sampc Number of samples + */ +void dtmf_dec_probe(struct dtmf_dec *dec, const int16_t *sampv, size_t sampc) +{ + size_t i; + + if (!dec || !sampv) + return; + + for (i=0; igx[j], sampv[i]); + goertzel_update(&dec->gy[j], sampv[i]); + } + + dec->energy += sampv[i] * sampv[i]; + + if (++dec->bidx < dec->bsize) + continue; + + digit0 = decode_digit(dec); + + if (digit0 != dec->digit && dec->digit1 != dec->digit) { + + dec->digit = digit0; + + if (digit0 != dec->digit1) + dec->digit = 0; + + if (dec->digit) + dec->dech(dec->digit, dec->arg); + } + + dec->digit1 = digit0; + dec->energy = 0.0; + dec->bidx = 0; + } +} diff --git a/rem/fir/fir.c b/rem/fir/fir.c new file mode 100644 index 000000000..d8460d8cc --- /dev/null +++ b/rem/fir/fir.c @@ -0,0 +1,67 @@ +/** + * @file fir.c FIR -- Finite Impulse Response + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include + + +/** + * Reset the FIR-filter + * + * @param fir FIR-filter state + */ +void fir_reset(struct fir *fir) +{ + if (!fir) + return; + + memset(fir, 0, sizeof(*fir)); +} + + +/** + * Process samples with the FIR filter + * + * @note product of channel and tap-count must be power of two + * + * @param fir FIR filter + * @param outv Output samples + * @param inv Input samples + * @param inc Number of samples + * @param ch Number of channels + * @param tapv Filter taps + * @param tapc Number of taps + */ +void fir_filter(struct fir *fir, int16_t *outv, const int16_t *inv, size_t inc, + unsigned ch, const int16_t *tapv, size_t tapc) +{ + const unsigned hmask = (ch * (unsigned)tapc) - 1; + + if (!fir || !outv || !inv || !ch || !tapv || !tapc) + return; + + if (hmask >= RE_ARRAY_SIZE(fir->history) || hmask & (hmask+1)) + return; + + while (inc--) { + + int64_t acc = 0; + unsigned i, j; + + fir->history[fir->index & hmask] = *inv++; + + for (i=0, j=fir->index++; ihistory[j & hmask] * tapv[i]; + + if (acc > 0x3fffffff) + acc = 0x3fffffff; + else if (acc < -0x40000000) + acc = -0x40000000; + + *outv++ = (int16_t)(acc>>15); + } +} diff --git a/rem/g711/g711.c b/rem/g711/g711.c new file mode 100644 index 000000000..1ad25b205 --- /dev/null +++ b/rem/g711/g711.c @@ -0,0 +1,853 @@ +/** + * @file g711.c G.711 codec + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include + + +const uint8_t g711_l2u[4096] = { + 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, + 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, 0xef, + 0xef, 0xee, 0xee, 0xed, 0xed, 0xec, 0xec, 0xeb, + 0xeb, 0xea, 0xea, 0xe9, 0xe9, 0xe8, 0xe8, 0xe7, + 0xe7, 0xe6, 0xe6, 0xe5, 0xe5, 0xe4, 0xe4, 0xe3, + 0xe3, 0xe2, 0xe2, 0xe1, 0xe1, 0xe0, 0xe0, 0xdf, + 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdc, 0xdb, + 0xdb, 0xdb, 0xdb, 0xda, 0xda, 0xda, 0xda, 0xd9, + 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7, + 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd6, 0xd5, + 0xd5, 0xd5, 0xd5, 0xd4, 0xd4, 0xd4, 0xd4, 0xd3, + 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1, + 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xd0, 0xcf, + 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xcf, 0xce, + 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xce, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcc, + 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcb, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xca, + 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xca, 0xc9, + 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc9, 0xc8, + 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc8, 0xc7, + 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc7, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc5, + 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc5, 0xc4, + 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc4, 0xc3, + 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc2, + 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc1, + 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0xc0, + 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, +}; + +const uint8_t g711_l2A[2048] = { + 0xd5, 0xd4, 0xd7, 0xd6, 0xd1, 0xd0, 0xd3, 0xd2, + 0xdd, 0xdc, 0xdf, 0xde, 0xd9, 0xd8, 0xdb, 0xda, + 0xc5, 0xc4, 0xc7, 0xc6, 0xc1, 0xc0, 0xc3, 0xc2, + 0xcd, 0xcc, 0xcf, 0xce, 0xc9, 0xc8, 0xcb, 0xca, + 0xf5, 0xf5, 0xf4, 0xf4, 0xf7, 0xf7, 0xf6, 0xf6, + 0xf1, 0xf1, 0xf0, 0xf0, 0xf3, 0xf3, 0xf2, 0xf2, + 0xfd, 0xfd, 0xfc, 0xfc, 0xff, 0xff, 0xfe, 0xfe, + 0xf9, 0xf9, 0xf8, 0xf8, 0xfb, 0xfb, 0xfa, 0xfa, + 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, + 0xe7, 0xe7, 0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, + 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, + 0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, + 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, + 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, + 0xeb, 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, + 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, + 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x94, + 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, + 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, + 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, + 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, 0x92, + 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, 0x9d, + 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, 0x9c, + 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, + 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, + 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, + 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, 0x9b, + 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, 0x9a, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, 0x8d, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, 0x8c, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, 0x8f, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, 0x8e, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, 0x89, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, 0x8a, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, 0xb5, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, 0xb7, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, 0xb6, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, 0xb3, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, 0xb8, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, 0xba, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, 0xa5, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, 0xa4, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, 0xa7, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, 0xa1, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, 0xa3, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, 0xa2, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, 0xad, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, 0xac, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, 0xae, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, 0xa9, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, 0xa8, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, +}; + +const int16_t g711_u2l[256] = { + -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, + -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, + -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, + -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, -2, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 2, +}; + +const int16_t g711_A2l[256] = { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736, + -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784, + -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368, + -3776, -3648, -4032, -3904, -3264, -3136, -3520, -3392, + -22016,-20992,-24064,-23040,-17920,-16896,-19968,-18944, + -30208,-29184,-32256,-31232,-26112,-25088,-28160,-27136, + -11008,-10496,-12032,-11520, -8960, -8448, -9984, -9472, + -15104,-14592,-16128,-15616,-13056,-12544,-14080,-13568, + -344, -328, -376, -360, -280, -264, -312, -296, + -472, -456, -504, -488, -408, -392, -440, -424, + -88, -72, -120, -104, -24, -8, -56, -40, + -216, -200, -248, -232, -152, -136, -184, -168, + -1376, -1312, -1504, -1440, -1120, -1056, -1248, -1184, + -1888, -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, -592, + -944, -912, -1008, -976, -816, -784, -880, -848, + 5504, 5248, 6016, 5760, 4480, 4224, 4992, 4736, + 7552, 7296, 8064, 7808, 6528, 6272, 7040, 6784, + 2752, 2624, 3008, 2880, 2240, 2112, 2496, 2368, + 3776, 3648, 4032, 3904, 3264, 3136, 3520, 3392, + 22016, 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, 27136, + 11008, 10496, 12032, 11520, 8960, 8448, 9984, 9472, + 15104, 14592, 16128, 15616, 13056, 12544, 14080, 13568, + 344, 328, 376, 360, 280, 264, 312, 296, + 472, 456, 504, 488, 408, 392, 440, 424, + 88, 72, 120, 104, 24, 8, 56, 40, + 216, 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, 1184, + 1888, 1824, 2016, 1952, 1632, 1568, 1760, 1696, + 688, 656, 752, 720, 560, 528, 624, 592, + 944, 912, 1008, 976, 816, 784, 880, 848, +}; diff --git a/rem/goertzel/goertzel.c b/rem/goertzel/goertzel.c new file mode 100644 index 000000000..0e42ed4a7 --- /dev/null +++ b/rem/goertzel/goertzel.c @@ -0,0 +1,60 @@ +/** + * @file goertzel.c Goertzel algorithm + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include + + +#define PI 3.14159265358979323846264338327 + + +/** + * Initialize goertzel state + * + * @param g Goertzel state + * @param freq Target frequency + * @param srate Sample rate + */ +void goertzel_init(struct goertzel *g, double freq, unsigned srate) +{ + g->q1 = 0.0; + g->q2 = 0.0; + g->coef = 2.0 * cos(2.0 * PI * (freq/(double)srate)); +} + + +/** + * Reset goertzel state + * + * @param g Goertzel state + */ +void goertzel_reset(struct goertzel *g) +{ + g->q1 = 0.0; + g->q2 = 0.0; +} + + +/** + * Calculate result and reset state + * + * @param g Goertzel state + * + * @return Result value + */ +double goertzel_result(struct goertzel *g) +{ + double res; + + goertzel_update(g, 0); + + res = g->q1*g->q1 + g->q2*g->q2 - g->q1*g->q2*g->coef; + + goertzel_reset(g); + + return res * 2.0; +} diff --git a/rem/vid/draw.c b/rem/vid/draw.c new file mode 100644 index 000000000..ec7737595 --- /dev/null +++ b/rem/vid/draw.c @@ -0,0 +1,250 @@ +/** + * @file draw.c Video Frame primitive drawing routines + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include + + +/** + * Draw a pixel to a video frame + * + * @param f Video frame + * @param x Pixel X-position + * @param y Pixel Y-position + * @param r Red color component + * @param g Green color component + * @param b Blue color component + */ +void vidframe_draw_point(struct vidframe *f, unsigned x, unsigned y, + uint8_t r, uint8_t g, uint8_t b) +{ + uint8_t *yp, *up, *vp; + uint32_t *p; + size_t uv_offset; + + if (!f) + return; + + if (x >= f->size.w || y >= f->size.h) + return; + + switch (f->fmt) { + + case VID_FMT_YUV420P: + yp = f->data[0] + f->linesize[0] * y + x; + up = f->data[1] + f->linesize[1] * (y/2) + x/2; + vp = f->data[2] + f->linesize[2] * (y/2) + x/2; + + yp[0] = rgb2y(r, g, b); + up[0] = rgb2u(r, g, b); + vp[0] = rgb2v(r, g, b); + break; + + case VID_FMT_YUYV422: + uv_offset = (f->linesize[0] * y + x * 2) & ~3; + + yp = f->data[0] + uv_offset; + + yp[0] = rgb2y(r, g, b); + yp[1] = rgb2u(r, g, b); + yp[2] = rgb2y(r, g, b); + yp[3] = rgb2v(r, g, b); + break; + + case VID_FMT_YUV444P: + yp = f->data[0] + f->linesize[0] * y + x; + up = f->data[1] + f->linesize[1] * y + x; + vp = f->data[2] + f->linesize[2] * y + x; + + yp[0] = rgb2y(r, g, b); + up[0] = rgb2u(r, g, b); + vp[0] = rgb2v(r, g, b); + break; + + case VID_FMT_RGB32: + p = (void *)(f->data[0] + f->linesize[0] * y + x*4); + + *p = (uint32_t)r << 16 | (uint32_t)g << 8 | b; + break; + + case VID_FMT_NV12: + uv_offset = (f->linesize[1] * (y/2) + x) & ~1; + + yp = f->data[0] + f->linesize[0] * y + x; + up = f->data[1] + uv_offset; + vp = f->data[1] + uv_offset + 1; + + yp[0] = rgb2y(r, g, b); + up[0] = rgb2u(r, g, b); + vp[0] = rgb2v(r, g, b); + break; + + case VID_FMT_NV21: + uv_offset = (f->linesize[1] * (y/2) + x) & ~1; + + yp = f->data[0] + f->linesize[0] * y + x; + up = f->data[1] + uv_offset + 1; + vp = f->data[1] + uv_offset; + + yp[0] = rgb2y(r, g, b); + up[0] = rgb2u(r, g, b); + vp[0] = rgb2v(r, g, b); + break; + + case VID_FMT_YUV422P: + yp = f->data[0] + f->linesize[0] * y + x; + up = f->data[1] + f->linesize[1] * y + x/2; + vp = f->data[2] + f->linesize[2] * y + x/2; + + yp[0] = rgb2y(r, g, b); + up[0] = rgb2u(r, g, b); + vp[0] = rgb2v(r, g, b); + break; + + default: + (void)re_fprintf(stderr, "vidframe_draw_point:" + " unsupported format %s\n", + vidfmt_name(f->fmt)); + break; + } +} + + +/** + * Draw a horizontal line + * + * @param f Video frame + * @param x0 Origin X-position + * @param y0 Origin Y-position + * @param w Line width + * @param r Red color component + * @param g Green color component + * @param b Blue color component + */ +void vidframe_draw_hline(struct vidframe *f, + unsigned x0, unsigned y0, unsigned w, + uint8_t r, uint8_t g, uint8_t b) +{ + uint8_t y, u, v; + uint8_t *p; + size_t offset; + + if (!f) + return; + + if (x0 >= f->size.w || y0 >= f->size.h) + return; + + w = min(w, f->size.w-x0); + + y = rgb2y(r, g, b); + u = rgb2u(r, g, b); + v = rgb2v(r, g, b); + + switch (f->fmt) { + + case VID_FMT_YUV420P: + memset(f->data[0] + y0 *f->linesize[0] + x0, y, w); + memset(f->data[1] + (y0/2)*f->linesize[1] + x0/2, u, w/2); + memset(f->data[2] + (y0/2)*f->linesize[2] + x0/2, v, w/2); + break; + + case VID_FMT_YUV444P: + memset(f->data[0] + y0*f->linesize[0] + x0, y, w); + memset(f->data[1] + y0*f->linesize[1] + x0, u, w); + memset(f->data[2] + y0*f->linesize[2] + x0, v, w); + break; + + case VID_FMT_YUYV422: + offset = (y0*f->linesize[0] + x0) & ~3; + p = f->data[0] + offset; + + for (unsigned x=0; xlinesize[1] * (y0/2) + x0) & ~1; + p = f->data[1] + offset; + + memset(f->data[0] + y0 *f->linesize[0] + x0, y, w); + + for (unsigned x=0; xdata[0] + y0*f->linesize[0] + x0, y, w); + memset(f->data[1] + y0*f->linesize[1] + x0, u, w); + memset(f->data[2] + y0*f->linesize[2] + x0, v, w); + break; + + default: + (void)re_fprintf(stderr, "vidframe_draw_hline:" + " unsupported format %s\n", + vidfmt_name(f->fmt)); + break; + } +} + + +/** + * Draw a vertical line + * + * @param f Video frame + * @param x0 Origin X-position + * @param y0 Origin Y-position + * @param h Line height + * @param r Red color component + * @param g Green color component + * @param b Blue color component + */ +void vidframe_draw_vline(struct vidframe *f, + unsigned x0, unsigned y0, unsigned h, + uint8_t r, uint8_t g, uint8_t b) +{ + if (!f) + return; + + while (h--) { + vidframe_draw_point(f, x0, y0++, r, g, b); + } +} + + +/** + * Draw a rectangle + * + * @param f Video frame + * @param x0 Origin X-position + * @param y0 Origin Y-position + * @param w Rectangle width + * @param h Rectangle height + * @param r Red color component + * @param g Green color component + * @param b Blue color component + */ +void vidframe_draw_rect(struct vidframe *f, unsigned x0, unsigned y0, + unsigned w, unsigned h, + uint8_t r, uint8_t g, uint8_t b) +{ + if (!f) + return; + + vidframe_draw_hline(f, x0, y0, w, r, g, b); + vidframe_draw_hline(f, x0, y0+h-1, w, r, g, b); + vidframe_draw_vline(f, x0, y0, h, r, g, b); + vidframe_draw_vline(f, x0+w-1, y0, h, r, g, b); +} diff --git a/rem/vid/fmt.c b/rem/vid/fmt.c new file mode 100644 index 000000000..bcf86bd20 --- /dev/null +++ b/rem/vid/fmt.c @@ -0,0 +1,40 @@ +/** + * @file vid/fmt.c Video Formats + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include + + +/** Video format description table */ +const struct vidfmt_desc vidfmt_descv[VID_FMT_N] = { + + {"yuv420p", 3, 3, { {0, 1}, {1, 1}, {2, 1}, {0, 0} } }, + {"yuyv422", 1, 3, { {0, 2}, {0, 4}, {0, 4}, {0, 0} } }, + {"uyvy422", 1, 3, { {0, 2}, {0, 4}, {0, 4}, {0, 0} } }, + {"rgb32", 1, 4, { {0, 4}, {0, 4}, {0, 4}, {0, 4} } }, + {"argb", 1, 4, { {0, 4}, {0, 4}, {0, 4}, {0, 4} } }, + {"rgb565", 1, 3, { {0, 2}, {0, 2}, {0, 2}, {0, 0} } }, + {"nv12", 3, 2, { {0, 1}, {1, 2}, {1, 2}, {0, 0} } }, + {"nv21", 3, 2, { {0, 1}, {1, 2}, {1, 2}, {0, 0} } }, + {"yuv444p", 3, 3, { {0, 1}, {1, 1}, {2, 1}, {0, 0} } }, + {"yuv422p", 3, 3, { {0, 1}, {1, 1}, {2, 1}, {0, 0} } }, +}; + + +/** + * Get the name of a video format + * + * @param fmt Video format + * + * @return Name of the video format + */ +const char *vidfmt_name(enum vidfmt fmt) +{ + if (fmt >= VID_FMT_N) + return "???"; + + return vidfmt_descv[fmt].name; +} diff --git a/rem/vid/frame.c b/rem/vid/frame.c new file mode 100644 index 000000000..4233d23e2 --- /dev/null +++ b/rem/vid/frame.c @@ -0,0 +1,467 @@ +/** + * @file frame.c Video Frame + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include + + +/** + * Get video frame buffer size + * + * @param fmt Video pixel format + * @param sz Size of video frame + * + * @return Number of bytes + */ +size_t vidframe_size(enum vidfmt fmt, const struct vidsz *sz) +{ + if (!sz) + return 0; + + switch (fmt) { + + case VID_FMT_YUV420P: return (size_t)sz->w * sz->h * 3 / 2; + case VID_FMT_YUYV422: return (size_t)sz->w * sz->h * 2; + case VID_FMT_UYVY422: return (size_t)sz->w * sz->h * 2; + case VID_FMT_RGB32: return (size_t)sz->w * sz->h * 4; + case VID_FMT_ARGB: return (size_t)sz->w * sz->h * 4; + case VID_FMT_RGB565: return (size_t)sz->w * sz->h * 2; + case VID_FMT_NV12: return (size_t)sz->w * sz->h * 3 / 2; + case VID_FMT_NV21: return (size_t)sz->w * sz->h * 3 / 2; + case VID_FMT_YUV444P: return (size_t)sz->w * sz->h * 3; + case VID_FMT_YUV422P: return (size_t)sz->w * sz->h * 2; + default: + return 0; + } +} + + +/** + * Initialize a video frame + * + * @param vf Video frame + * @param fmt Video pixel format + * @param sz Size of video frame + * @param data Pointer to video planes + * @param linesize Pointer to linesizes + */ +void vidframe_init(struct vidframe *vf, enum vidfmt fmt, + const struct vidsz *sz, void *data[4], unsigned linesize[4]) +{ + int i; + + if (!vf || !sz || !data || !linesize) + return; + + for (i=0; i<4; i++) { + vf->data[i] = data[i]; + vf->linesize[i] = linesize[i]; + } + + vf->size = *sz; + vf->fmt = fmt; +} + + +/** + * Initialize a video frame from a buffer + * + * @param vf Video frame + * @param fmt Video pixel format + * @param sz Size of video frame + * @param buf Frame buffer + */ +void vidframe_init_buf(struct vidframe *vf, enum vidfmt fmt, + const struct vidsz *sz, uint8_t *buf) +{ + unsigned w, h; + + if (!vf || !sz || !buf) + return; + + w = (sz->w + 1) >> 1; + h = (sz->h + 1) >> 1; + + unsigned w2 = (sz->w + 1) >> 1; + + memset(vf->linesize, 0, sizeof(vf->linesize)); + memset(vf->data, 0, sizeof(vf->data)); + + switch (fmt) { + + case VID_FMT_YUV420P: + vf->linesize[0] = sz->w; + vf->linesize[1] = w; + vf->linesize[2] = w; + + vf->data[0] = buf; + vf->data[1] = vf->data[0] + vf->linesize[0] * sz->h; + vf->data[2] = vf->data[1] + vf->linesize[1] * h; + break; + + case VID_FMT_YUYV422: + case VID_FMT_UYVY422: + vf->linesize[0] = sz->w * 2; + vf->data[0] = buf; + break; + + case VID_FMT_RGB32: + case VID_FMT_ARGB: + vf->linesize[0] = sz->w * 4; + vf->data[0] = buf; + break; + + case VID_FMT_RGB565: + vf->linesize[0] = sz->w * 2; + vf->data[0] = buf; + break; + + case VID_FMT_NV12: + case VID_FMT_NV21: + vf->linesize[0] = sz->w; + vf->linesize[1] = w*2; + + vf->data[0] = buf; + vf->data[1] = vf->data[0] + vf->linesize[0] * sz->h; + break; + + case VID_FMT_YUV444P: + vf->linesize[0] = sz->w; + vf->linesize[1] = sz->w; + vf->linesize[2] = sz->w; + + vf->data[0] = buf; + vf->data[1] = vf->data[0] + vf->linesize[0] * sz->h; + vf->data[2] = vf->data[1] + vf->linesize[1] * sz->h; + break; + + case VID_FMT_YUV422P: + vf->linesize[0] = sz->w; + vf->linesize[1] = w2; + vf->linesize[2] = w2; + + vf->data[0] = buf; + vf->data[1] = vf->data[0] + vf->linesize[0] * sz->h; + vf->data[2] = vf->data[1] + vf->linesize[1] * sz->h; + break; + + default: + (void)re_printf("vidframe: no fmt %s\n", vidfmt_name(fmt)); + return; + } + + vf->size = *sz; + vf->fmt = fmt; +} + + +/** + * Allocate an empty video frame + * + * @param vfp Pointer to allocated video frame + * @param fmt Video pixel format + * @param sz Size of video frame + * + * @return 0 for success, otherwise error code + */ +int vidframe_alloc(struct vidframe **vfp, enum vidfmt fmt, + const struct vidsz *sz) +{ + struct vidframe *vf; + + if (!sz || !sz->w || !sz->h) + return EINVAL; + + vf = mem_zalloc(sizeof(*vf) + vidframe_size(fmt, sz), NULL); + if (!vf) + return ENOMEM; + + vidframe_init_buf(vf, fmt, sz, (uint8_t *)(vf + 1)); + + *vfp = vf; + + return 0; +} + + +/** + * Fill a video frame with a nice color + * + * @param vf Video frame + * @param r Red color component + * @param g Green color component + * @param b Blue color component + */ +void vidframe_fill(struct vidframe *vf, uint32_t r, uint32_t g, uint32_t b) +{ + uint8_t *p; + size_t h; + unsigned i, x; + int u, v; + + if (!vf) + return; + + switch (vf->fmt) { + + case VID_FMT_YUV420P: + h = vf->size.h; + + memset(vf->data[0], rgb2y(r, g, b), h * vf->linesize[0]); + memset(vf->data[1], rgb2u(r, g, b), h/2 * vf->linesize[1]); + memset(vf->data[2], rgb2v(r, g, b), h/2 * vf->linesize[2]); + break; + + case VID_FMT_YUV444P: + h = vf->size.h; + + memset(vf->data[0], rgb2y(r, g, b), h * vf->linesize[0]); + memset(vf->data[1], rgb2u(r, g, b), h * vf->linesize[1]); + memset(vf->data[2], rgb2v(r, g, b), h * vf->linesize[2]); + break; + + case VID_FMT_RGB32: + p = vf->data[0]; + for (i=0; ilinesize[0] * vf->size.h; i+=4) { + *p++ = b; + *p++ = g; + *p++ = r; + *p++ = 0; + } + break; + + case VID_FMT_NV12: + case VID_FMT_NV21: + h = vf->size.h; + + if (vf->fmt == VID_FMT_NV12) { + u = rgb2u(r, g, b); + v = rgb2v(r, g, b); + } + else { + v = rgb2u(r, g, b); + u = rgb2v(r, g, b); + } + + memset(vf->data[0], rgb2y(r, g, b), h * vf->linesize[0]); + + p = vf->data[1]; + + for (h=0; hsize.h; h+=2) { + + for (x=0; xsize.w; x+=2) { + p[x ] = u; + p[x+1] = v; + } + + p += vf->linesize[1]; + } + break; + + case VID_FMT_YUV422P: + h = vf->size.h; + + memset(vf->data[0], rgb2y(r, g, b), h * vf->linesize[0]); + memset(vf->data[1], rgb2u(r, g, b), h * vf->linesize[1]); + memset(vf->data[2], rgb2v(r, g, b), h * vf->linesize[2]); + break; + + default: + (void)re_printf("vidfill: no fmt %s\n", vidfmt_name(vf->fmt)); + break; + } +} + + +/** + * Copy content between to equally sized video frames of same pixel format + * + * @param dst Destination frame + * @param src Source frame + */ +void vidframe_copy(struct vidframe *dst, const struct vidframe *src) +{ + const uint8_t *ds0, *ds1, *ds2; + unsigned lsd, lss, w, h, y; + unsigned lsd1, lss1; + unsigned lsd2, lss2; + uint8_t *dd0, *dd1, *dd2; + + if (!dst || !src) + return; + + if (!vidsz_cmp(&dst->size, &src->size)) + return; + + if (dst->fmt != src->fmt) + return; + + switch (dst->fmt) { + + case VID_FMT_YUV420P: + lsd = dst->linesize[0]; + lss = src->linesize[0]; + + dd0 = dst->data[0]; + dd1 = dst->data[1]; + dd2 = dst->data[2]; + + ds0 = src->data[0]; + ds1 = src->data[1]; + ds2 = src->data[2]; + + w = dst->size.w & ~1; + h = dst->size.h & ~1; + + for (y=0; ylinesize[0]; + lss = src->linesize[0]; + + dd0 = dst->data[0]; + dd1 = dst->data[1]; + dd2 = dst->data[2]; + + ds0 = src->data[0]; + ds1 = src->data[1]; + ds2 = src->data[2]; + + w = dst->size.w; + h = dst->size.h; + + for (y=0; ylinesize[0]; + lss = src->linesize[0]; + + dd0 = dst->data[0]; + dd1 = dst->data[1]; + + ds0 = src->data[0]; + ds1 = src->data[1]; + + w = dst->size.w & ~1; + h = dst->size.h & ~1; + + for (y=0; ylinesize[0]; + lss = src->linesize[0]; + lsd1 = dst->linesize[1]; + lss1 = src->linesize[1]; + lsd2 = dst->linesize[2]; + lss2 = src->linesize[2]; + + dd0 = dst->data[0]; + dd1 = dst->data[1]; + dd2 = dst->data[2]; + + ds0 = src->data[0]; + ds1 = src->data[1]; + ds2 = src->data[2]; + + w = dst->size.w & ~1; + h = dst->size.h & ~1; + + for (y=0; ylinesize[0]; + lss = src->linesize[0]; + + dd0 = dst->data[0]; + + ds0 = src->data[0]; + + w = dst->size.w & ~1; + h = dst->size.h & ~1; + + for (y=0; yfmt)); + break; + } +} diff --git a/rem/vidconv/vconv.c b/rem/vidconv/vconv.c new file mode 100644 index 000000000..b0a320786 --- /dev/null +++ b/rem/vidconv/vconv.c @@ -0,0 +1,826 @@ +/** + * @file vconv.c Video Conversion + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include +#include +#include + + +#if 0 + +/* + * The lookup tables are generated with the following code: + */ + +#define P 14 + +#define COEF_RV ((int32_t) (1.370705f * (float)(1 << P))) +#define COEF_GU ((int32_t) (-0.337633f * (float)(1 << P))) +#define COEF_GV ((int32_t) (-0.698001f * (float)(1 << P))) +#define COEF_BU ((int32_t) (1.732446f * (float)(1 << P))) + +#define ERV(a) (COEF_RV * ((a) - 128)) +#define EGU(a) (COEF_GU * ((a) - 128)) +#define EGV(a) (COEF_GV * ((a) - 128)) +#define EBU(a) (COEF_BU * ((a) - 128)) + + +int16_t CRV[256]; +int16_t CGU[256]; +int16_t CGV[256]; +int16_t CBU[256]; + + +static void init_table(void) +{ + int i; + + for (i = 0; i < 256; ++i) { + CRV[i] = ERV(i) >> P; + CGU[i] = EGU(i) >> P; + CGV[i] = EGV(i) >> P; + CBU[i] = EBU(i) >> P; + } +} +#endif + + +static const int16_t CRV[256] = { + -176,-175,-173,-172,-170,-169,-168,-166,-165,-164,-162,-161, + -159,-158,-157,-155,-154,-153,-151,-150,-149,-147,-146,-144, + -143,-142,-140,-139,-138,-136,-135,-133,-132,-131,-129,-128, + -127,-125,-124,-122,-121,-120,-118,-117,-116,-114,-113,-112, + -110,-109,-107,-106,-105,-103,-102,-101, -99, -98, -96, -95, + -94, -92, -91, -90, -88, -87, -85, -84, -83, -81, -80, -79, + -77, -76, -75, -73, -72, -70, -69, -68, -66, -65, -64, -62, + -61, -59, -58, -57, -55, -54, -53, -51, -50, -48, -47, -46, + -44, -43, -42, -40, -39, -38, -36, -35, -33, -32, -31, -29, + -28, -27, -25, -24, -22, -21, -20, -18, -17, -16, -14, -13, + -11, -10, -9, -7, -6, -5, -3, -2, 0, 1, 2, 4, + 5, 6, 8, 9, 10, 12, 13, 15, 16, 17, 19, 20, + 21, 23, 24, 26, 27, 28, 30, 31, 32, 34, 35, 37, + 38, 39, 41, 42, 43, 45, 46, 47, 49, 50, 52, 53, + 54, 56, 57, 58, 60, 61, 63, 64, 65, 67, 68, 69, + 71, 72, 74, 75, 76, 78, 79, 80, 82, 83, 84, 86, + 87, 89, 90, 91, 93, 94, 95, 97, 98, 100, 101, 102, + 104, 105, 106, 108, 109, 111, 112, 113, 115, 116, 117, 119, + 120, 121, 123, 124, 126, 127, 128, 130, 131, 132, 134, 135, + 137, 138, 139, 141, 142, 143, 145, 146, 148, 149, 150, 152, + 153, 154, 156, 157, 158, 160, 161, 163, 164, 165, 167, 168, + 169, 171, 172, 174}; + +static const int16_t CGU[256] = { + 43, 42, 42, 42, 41, 41, 41, 40, 40, 40, 39, 39, + 39, 38, 38, 38, 37, 37, 37, 36, 36, 36, 35, 35, + 35, 34, 34, 34, 33, 33, 33, 32, 32, 32, 31, 31, + 31, 30, 30, 30, 29, 29, 29, 28, 28, 28, 27, 27, + 27, 26, 26, 25, 25, 25, 24, 24, 24, 23, 23, 23, + 22, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, + 18, 18, 18, 17, 17, 17, 16, 16, 16, 15, 15, 15, + 14, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11, + 10, 10, 10, 9, 9, 9, 8, 8, 8, 7, 7, 7, + 6, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, + 2, 2, 2, 1, 1, 1, 0, 0, 0, -1, -1, -2, + -2, -2, -3, -3, -3, -4, -4, -4, -5, -5, -5, -6, + -6, -6, -7, -7, -7, -8, -8, -8, -9, -9, -9, -10, + -10, -10, -11, -11, -11, -12, -12, -12, -13, -13, -13, -14, + -14, -14, -15, -15, -15, -16, -16, -16, -17, -17, -17, -18, + -18, -18, -19, -19, -19, -20, -20, -20, -21, -21, -21, -22, + -22, -22, -23, -23, -23, -24, -24, -24, -25, -25, -25, -26, + -26, -26, -27, -27, -28, -28, -28, -29, -29, -29, -30, -30, + -30, -31, -31, -31, -32, -32, -32, -33, -33, -33, -34, -34, + -34, -35, -35, -35, -36, -36, -36, -37, -37, -37, -38, -38, + -38, -39, -39, -39, -40, -40, -40, -41, -41, -41, -42, -42, + -42, -43, -43, -43}; + +static const int16_t CGV[256] = { + 89, 88, 87, 87, 86, 85, 85, 84, 83, 83, 82, 81, + 80, 80, 79, 78, 78, 77, 76, 76, 75, 74, 73, 73, + 72, 71, 71, 70, 69, 69, 68, 67, 67, 66, 65, 64, + 64, 63, 62, 62, 61, 60, 60, 59, 58, 57, 57, 56, + 55, 55, 54, 53, 53, 52, 51, 50, 50, 49, 48, 48, + 47, 46, 46, 45, 44, 43, 43, 42, 41, 41, 40, 39, + 39, 38, 37, 36, 36, 35, 34, 34, 33, 32, 32, 31, + 30, 30, 29, 28, 27, 27, 26, 25, 25, 24, 23, 23, + 22, 21, 20, 20, 19, 18, 18, 17, 16, 16, 15, 14, + 13, 13, 12, 11, 11, 10, 9, 9, 8, 7, 6, 6, + 5, 4, 4, 3, 2, 2, 1, 0, 0, -1, -2, -3, + -3, -4, -5, -5, -6, -7, -7, -8, -9, -10, -10, -11, + -12, -12, -13, -14, -14, -15, -16, -17, -17, -18, -19, -19, + -20, -21, -21, -22, -23, -24, -24, -25, -26, -26, -27, -28, + -28, -29, -30, -31, -31, -32, -33, -33, -34, -35, -35, -36, + -37, -37, -38, -39, -40, -40, -41, -42, -42, -43, -44, -44, + -45, -46, -47, -47, -48, -49, -49, -50, -51, -51, -52, -53, + -54, -54, -55, -56, -56, -57, -58, -58, -59, -60, -61, -61, + -62, -63, -63, -64, -65, -65, -66, -67, -68, -68, -69, -70, + -70, -71, -72, -72, -73, -74, -74, -75, -76, -77, -77, -78, + -79, -79, -80, -81, -81, -82, -83, -84, -84, -85, -86, -86, + -87, -88, -88, -89}; + +static const int16_t CBU[256] = { + -222,-221,-219,-217,-215,-214,-212,-210,-208,-207,-205,-203, + -201,-200,-198,-196,-195,-193,-191,-189,-188,-186,-184,-182, + -181,-179,-177,-175,-174,-172,-170,-169,-167,-165,-163,-162, + -160,-158,-156,-155,-153,-151,-149,-148,-146,-144,-143,-141, + -139,-137,-136,-134,-132,-130,-129,-127,-125,-124,-122,-120, + -118,-117,-115,-113,-111,-110,-108,-106,-104,-103,-101, -99, + -98, -96, -94, -92, -91, -89, -87, -85, -84, -82, -80, -78, + -77, -75, -73, -72, -70, -68, -66, -65, -63, -61, -59, -58, + -56, -54, -52, -51, -49, -47, -46, -44, -42, -40, -39, -37, + -35, -33, -32, -30, -28, -26, -25, -23, -21, -20, -18, -16, + -14, -13, -11, -9, -7, -6, -4, -2, 0, 1, 3, 5, + 6, 8, 10, 12, 13, 15, 17, 19, 20, 22, 24, 25, + 27, 29, 31, 32, 34, 36, 38, 39, 41, 43, 45, 46, + 48, 50, 51, 53, 55, 57, 58, 60, 62, 64, 65, 67, + 69, 71, 72, 74, 76, 77, 79, 81, 83, 84, 86, 88, + 90, 91, 93, 95, 97, 98, 100, 102, 103, 105, 107, 109, + 110, 112, 114, 116, 117, 119, 121, 123, 124, 126, 128, 129, + 131, 133, 135, 136, 138, 140, 142, 143, 145, 147, 148, 150, + 152, 154, 155, 157, 159, 161, 162, 164, 166, 168, 169, 171, + 173, 174, 176, 178, 180, 181, 183, 185, 187, 188, 190, 192, + 194, 195, 197, 199, 200, 202, 204, 206, 207, 209, 211, 213, + 214, 216, 218, 220}; + + +static inline void yuv2rgb(uint8_t *rgb, uint8_t y, int ruv, int guv, int buv) +{ + *rgb++ = saturate_u8(y + buv); + *rgb++ = saturate_u8(y + guv); + *rgb++ = saturate_u8(y + ruv); + *rgb = 0; +} + + +static inline void yuv2rgb565(uint8_t *rgb, uint8_t y, + int ruv, int guv, int buv) +{ + int r = saturate_u8(y + ruv) >> 3; + int g = saturate_u8(y + guv) >> 2; + int b = saturate_u8(y + buv) >> 3; + + rgb[1] = r << 3 | g >> 3; + rgb[0] = g << 5 | b; +} + + +static inline void _yuv2rgb(uint8_t *rgb, uint8_t y, uint8_t u, uint8_t v) +{ + int ruv, guv, buv; + + ruv = CRV[v]; + guv = CGV[v] + CGU[u]; + buv = CBU[u]; + + yuv2rgb(rgb, y, ruv, guv, buv); +} + + +typedef void (line_h)(unsigned xoffs, unsigned width, double rw, + unsigned yd, unsigned ys, unsigned ys2, + uint8_t *dd0, uint8_t *dd1, uint8_t *dd2, + unsigned lsd, + const uint8_t *sd0, const uint8_t *sd1, + const uint8_t *sd2, unsigned lss); + + +static void yuv420p_to_yuv420p(unsigned xoffs, unsigned width, double rw, + unsigned yd, unsigned ys, unsigned ys2, + uint8_t *dd0, uint8_t *dd1, uint8_t *dd2, + unsigned lsd, + const uint8_t *ds0, const uint8_t *ds1, + const uint8_t *ds2, unsigned lss + ) +{ + unsigned x, xd, xs, xs2; + unsigned id, is; + + for (x=0; x>1) + (ys>>1)*lss/2; + + dd1[id] = ds1[is]; + dd2[id] = ds2[is]; + } +} + + +static void yuyv422_to_yuv420p(unsigned xoffs, unsigned width, double rw, + unsigned yd, unsigned ys, unsigned ys2, + uint8_t *dd0, uint8_t *dd1, uint8_t *dd2, + unsigned lsd, + const uint8_t *sd0, const uint8_t *sd1, + const uint8_t *sd2, unsigned lss + ) +{ + unsigned x, xd, xs; + unsigned id, is, is2; + + (void)sd1; + (void)sd2; + + for (x=0; x> 16, x0 >> 8, x0); + dd0[id+1] = rgb2y(x1 >> 16, x1 >> 8, x1); + dd0[id + lsd] = rgb2y(x2 >> 16, x2 >> 8, x2); + dd0[id+1 + lsd] = rgb2y(x3 >> 16, x3 >> 8, x3); + + id = xd/2 + yd*lsd/4; + + dd1[id] = rgb2u(x0 >> 16, x0 >> 8, x0); + dd2[id] = rgb2v(x0 >> 16, x0 >> 8, x0); + } +} + + +static void rgb32_to_yuv444p(unsigned xoffs, unsigned width, double rw, + unsigned yd, unsigned ys, unsigned ys2, + uint8_t *dd0, uint8_t *dd1, uint8_t *dd2, + unsigned lsd, + const uint8_t *ds0, const uint8_t *ds1, + const uint8_t *ds2, unsigned lss + ) +{ + unsigned x, xd, xs; + unsigned id; + + (void)ds1; + (void)ds2; + + for (x=0; x> 16, x0 >> 8, x0); + dd0[id + lsd] = rgb2y(x1 >> 16, x1 >> 8, x1); + + dd1[id] = rgb2u(x0 >> 16, x0 >> 8, x0); + dd1[id + lsd] = rgb2u(x1 >> 16, x1 >> 8, x1); + + dd2[id] = rgb2v(x0 >> 16, x0 >> 8, x0); + dd2[id + lsd] = rgb2v(x1 >> 16, x1 >> 8, x1); + } +} + + +static void yuv420p_to_rgb32(unsigned xoffs, unsigned width, double rw, + unsigned yd, unsigned ys, unsigned ys2, + uint8_t *dd0, uint8_t *dd1, uint8_t *dd2, + unsigned lsd, + const uint8_t *ds0, const uint8_t *ds1, + const uint8_t *ds2, unsigned lss) +{ + unsigned x, xd, xs, xs2; + unsigned id, is; + + (void)dd1; + (void)dd2; + + for (x=0; x>1) + (ys>>1)*lss/2; + + u = ds1[is]; + v = ds2[is]; + + ruv = CRV[v]; + guv = CGV[v] + CGU[u]; + buv = CBU[u]; + + yuv2rgb(&dd0[id], ds0[xs + ys*lss], ruv, guv, buv); + yuv2rgb(&dd0[id+4], ds0[xs2 + ys*lss], ruv, guv, buv); + yuv2rgb(&dd0[id + lsd], ds0[xs + ys2*lss], ruv, guv, buv); + yuv2rgb(&dd0[id+4 + lsd], ds0[xs2 + ys2*lss], ruv, guv, buv); + } +} + + +static void yuv420p_to_rgb565(unsigned xoffs, unsigned width, double rw, + unsigned yd, unsigned ys, unsigned ys2, + uint8_t *dd0, uint8_t *dd1, uint8_t *dd2, + unsigned lsd, + const uint8_t *ds0, const uint8_t *ds1, + const uint8_t *ds2, unsigned lss) +{ + unsigned x, xd, xs, xs2; + unsigned id, is; + + (void)dd1; + (void)dd2; + + for (x=0; x>1) + (ys>>1)*lss/2; + + u = ds1[is]; + v = ds2[is]; + + ruv = CRV[v]; + guv = CGV[v] + CGU[u]; + buv = CBU[u]; + + yuv2rgb565(&dd0[id], ds0[xs + ys*lss], ruv, guv,buv); + yuv2rgb565(&dd0[id+2], ds0[xs2 + ys*lss], ruv, guv,buv); + yuv2rgb565(&dd0[id + lsd], ds0[xs + ys2*lss], ruv, guv,buv); + yuv2rgb565(&dd0[id+2 + lsd], ds0[xs2 + ys2*lss], ruv, guv,buv); + } +} + + +static void nv12_to_yuv420p(unsigned xoffs, unsigned width, double rw, + unsigned yd, unsigned ys, unsigned ys2, + uint8_t *dd0, uint8_t *dd1, uint8_t *dd2, + unsigned lsd, + const uint8_t *ds0, const uint8_t *ds1, + const uint8_t *ds2, unsigned lss + ) +{ + unsigned x, xd, xs, xs2; + unsigned id, is; + + (void)ds2; + + for (x=0; x>1) + (yd>>1)*lsd/2; + is = xs/2 + ys*lss/4; + + dd1[id] = ds1[2*is]; + dd2[id] = ds1[2*is+1]; + } +} + + +static void yuv420p_to_nv12(unsigned xoffs, unsigned width, double rw, + unsigned yd, unsigned ys, unsigned ys2, + uint8_t *dd0, uint8_t *dd1, uint8_t *dd2, + unsigned lsd, + const uint8_t *ds0, const uint8_t *ds1, + const uint8_t *ds2, unsigned lss + ) +{ + unsigned x, xd, xs, xs2; + unsigned id, is; + + (void)dd2; + + for (x=0; x>1) + (ys>>1)*lss/2; + + dd1[2*id] = ds1[is]; + dd1[2*id+1] = ds2[is]; + } +} + + +static void nv21_to_yuv420p(unsigned xoffs, unsigned width, double rw, + unsigned yd, unsigned ys, unsigned ys2, + uint8_t *dd0, uint8_t *dd1, uint8_t *dd2, + unsigned lsd, + const uint8_t *ds0, const uint8_t *ds1, + const uint8_t *ds2, unsigned lss + ) +{ + unsigned x, xd, xs, xs2; + unsigned id, is; + + (void)ds2; + + for (x=0; x>1) + (ys>>1)*lss/2) & ~1; + + dd2[id] = ds1[2*is]; + dd1[id] = ds1[2*is+1]; + } +} + + +static void yuv444p_to_rgb32(unsigned xoffs, unsigned width, double rw, + unsigned yd, unsigned ys, unsigned ys2, + uint8_t *dd0, uint8_t *dd1, uint8_t *dd2, + unsigned lsd, + const uint8_t *ds0, const uint8_t *ds1, + const uint8_t *ds2, unsigned lss) +{ + unsigned x, xd, xs; + unsigned id; + unsigned is1, is2; + + (void)dd1; + (void)dd2; + + for (x=0; xfmt < MAX_SRC && dst->fmt < MAX_DST) { + + /* Lookup conversion function */ + lineh = conv_table[src->fmt][dst->fmt]; + } + if (!lineh) { + (void)re_printf("vidconv: no pixel converter found for" + " %s -> %s\n", vidfmt_name(src->fmt), + vidfmt_name(dst->fmt)); + return; + } + + if (r) { + r->x &= ~1; + r->y &= ~1; + r->w &= ~1; + r->h &= ~1; + + if ((r->x + r->w) > dst->size.w || + (r->y + r->h) > dst->size.h) { + (void)re_printf("vidconv: out of bounds (%u x %u)\n", + dst->size.w, dst->size.h); + return; + } + } + else { + rdst.x = rdst.y = 0; + rdst.w = dst->size.w & ~1; + rdst.h = dst->size.h & ~1; + r = &rdst; + } + + rw = (double)src->size.w / (double)r->w; + rh = (double)src->size.h / (double)r->h; + + lsd = dst->linesize[0]; + lss = src->linesize[0]; + + dd0 = dst->data[0]; + dd1 = dst->data[1]; + dd2 = dst->data[2]; + + ds0 = src->data[0]; + ds1 = src->data[1]; + ds2 = src->data[2]; + + for (y=0; yh; y+=2) { + + yd = y + r->y; + + ys = (unsigned)(y * rh); + ys2 = (unsigned)((y+1) * rh); + + lineh(r->x, r->w, rw, yd, ys, ys2, + dd0, dd1, dd2, lsd, + ds0, ds1, ds2, lss); + } +} + + +/** + * Same as vidconv(), but maintain source aspect ratio within bounds of r + * + * @param dst Destination video frame + * @param src Source video frame + * @param r Drawing area in destination frame + */ +void vidconv_aspect(struct vidframe *dst, const struct vidframe *src, + struct vidrect *r) +{ + struct vidsz asz; + double ar; + + ar = (double)src->size.w / (double)src->size.h; + + asz.w = r->w; + asz.h = r->h; + + r->w = (unsigned)min((double)asz.w, (double)asz.h * ar); + r->h = (unsigned)min((double)asz.h, (double)asz.w / ar); + r->x = r->x + (asz.w - r->w) / 2; + r->y = r->y + (asz.h - r->h) / 2; + + vidconv(dst, src, r); +} diff --git a/rem/vidmix/vidmix.c b/rem/vidmix/vidmix.c new file mode 100644 index 000000000..c603ccf41 --- /dev/null +++ b/rem/vidmix/vidmix.c @@ -0,0 +1,731 @@ +/** + * @file vidmix.c Video Mixer + * + * Copyright (C) 2010 Creytiv.com + */ + +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 +#ifdef HAVE_UNISTD_H +#include +#endif +#define __USE_UNIX98 1 +#include +#include +#include +#include +#include + +/* + * Clock-rate for video timestamp + */ +#define VIDEO_TIMEBASE 1000000U + + +struct vidmix { + mtx_t rwlock; + struct list srcl; + bool initialized; +}; + +struct vidmix_source { + struct le le; + thrd_t thread; + mtx_t mutex; + struct vidframe *frame_tx; + struct vidframe *frame_rx; + struct vidmix *mix; + vidmix_frame_h *fh; + void *arg; + void *focus; + bool content_hide; + bool focus_full; + unsigned fint; + bool selfview; + bool content; + bool clear; + bool run; +}; + + +static inline void source_mix_full(struct vidframe *mframe, + const struct vidframe *frame_src); + + +static inline void clear_frame(struct vidframe *vf) +{ + vidframe_fill(vf, 0, 0, 0); +} + + +static void clear_all(struct vidmix *mix) +{ + struct le *le; + + for (le=mix->srcl.head; le; le=le->next) { + + struct vidmix_source *src = le->data; + + src->clear = true; + } +} + + +static void destructor(void *arg) +{ + struct vidmix *mix = arg; + + if (mix->initialized) + mtx_destroy(&mix->rwlock); +} + + +static void source_destructor(void *arg) +{ + struct vidmix_source *src = arg; + + vidmix_source_stop(src); + + if (src->le.list) { + mtx_lock(&src->mix->rwlock); + list_unlink(&src->le); + clear_all(src->mix); + mtx_unlock(&src->mix->rwlock); + } + + mem_deref(src->frame_tx); + mem_deref(src->frame_rx); + mem_deref(src->mix); +} + + +static inline void source_mix(struct vidframe *mframe, + const struct vidframe *frame_src, + unsigned n, unsigned rows, unsigned idx, + bool focus, bool focus_this, bool focus_full) +{ + struct vidrect rect; + + if (!frame_src) + return; + + if (focus) { + + const unsigned nmin = focus_full ? 12 : 6; + + n = max((n+1), nmin)/2; + + if (focus_this) { + rect.w = mframe->size.w * (n-1) / n; + rect.h = mframe->size.h * (n-1) / n; + rect.x = 0; + rect.y = 0; + } + else { + rect.w = mframe->size.w / n; + rect.h = mframe->size.h / n; + + if (idx < n) { + rect.x = mframe->size.w - rect.w; + rect.y = rect.h * idx; + } + else if (idx < (n*2 - 1)) { + rect.x = rect.w * (n*2 - 2 - idx); + rect.y = mframe->size.h - rect.h; + } + else { + return; + } + } + } + else if (rows == 1) { + + source_mix_full(mframe, frame_src); + return; + } + else { + rect.w = mframe->size.w / rows; + rect.h = mframe->size.h / rows; + rect.x = rect.w * (idx % rows); + rect.y = rect.h * (idx / rows); + } + + vidconv_aspect(mframe, frame_src, &rect); +} + + +static inline void source_mix_full(struct vidframe *mframe, + const struct vidframe *frame_src) +{ + if (!frame_src) + return; + + if (vidsz_cmp(&mframe->size, &frame_src->size)) { + + vidframe_copy(mframe, frame_src); + } + else { + struct vidrect rect; + + rect.w = mframe->size.w; + rect.h = mframe->size.h; + rect.x = 0; + rect.y = 0; + + vidconv_aspect(mframe, frame_src, &rect); + } +} + + +static inline unsigned calc_rows(unsigned n) +{ + unsigned rows; + + for (rows=1;; rows++) + if (n <= (rows * rows)) + return rows; +} + + +static int vidmix_thread(void *arg) +{ + struct vidmix_source *src = arg; + struct vidmix *mix = src->mix; + uint64_t ts = tmr_jiffies_usec(); + + mtx_lock(&src->mutex); + + while (src->run) { + + unsigned n, rows, idx; + struct le *le; + uint64_t now; + + mtx_unlock(&src->mutex); + sys_usleep(4000); + mtx_lock(&src->mutex); + + now = tmr_jiffies_usec(); + + if (ts > now) + continue; + + if (!src->frame_tx) { + ts += src->fint; + continue; + } + + mtx_lock(&mix->rwlock); + + if (src->clear) { + clear_frame(src->frame_tx); + src->clear = false; + } + + for (le=mix->srcl.head, n=0; le; le=le->next) { + + const struct vidmix_source *lsrc = le->data; + + if (lsrc == src && !src->selfview) + continue; + + if (lsrc->content && src->content_hide) + continue; + + if (lsrc == src->focus && src->focus_full) + source_mix_full(src->frame_tx, lsrc->frame_rx); + + ++n; + } + + rows = calc_rows(n); + + for (le=mix->srcl.head, idx=0; le; le=le->next) { + + const struct vidmix_source *lsrc = le->data; + + if (lsrc == src && !src->selfview) + continue; + + if (lsrc->content && src->content_hide) + continue; + + if (lsrc == src->focus && src->focus_full) + continue; + + source_mix(src->frame_tx, lsrc->frame_rx, n, rows, idx, + src->focus != NULL, src->focus == lsrc, + src->focus_full); + + if (src->focus != lsrc) + ++idx; + } + + mtx_unlock(&mix->rwlock); + + src->fh(ts, src->frame_tx, src->arg); + + ts += src->fint; + } + + mtx_unlock(&src->mutex); + + return 0; +} + + +static int content_thread(void *arg) +{ + struct vidmix_source *src = arg; + struct vidmix *mix = src->mix; + uint64_t ts = tmr_jiffies_usec(); + + mtx_lock(&src->mutex); + + while (src->run) { + + struct le *le; + uint64_t now; + + mtx_unlock(&src->mutex); + sys_usleep(4000); + mtx_lock(&src->mutex); + + now = tmr_jiffies_usec(); + + if (ts > now) + continue; + + mtx_lock(&mix->rwlock); + + for (le=mix->srcl.head; le; le=le->next) { + + const struct vidmix_source *lsrc = le->data; + + if (!lsrc->content || !lsrc->frame_rx || lsrc == src) + continue; + + src->fh(ts, lsrc->frame_rx, src->arg); + break; + } + + mtx_unlock(&mix->rwlock); + + ts += src->fint; + } + + mtx_unlock(&src->mutex); + + return 0; +} + + +/** + * Allocate a new Video mixer + * + * @param mixp Pointer to allocated video mixer + * + * @return 0 for success, otherwise error code + */ +int vidmix_alloc(struct vidmix **mixp) +{ + struct vidmix *mix; + int err; + + if (!mixp) + return EINVAL; + + mix = mem_zalloc(sizeof(*mix), destructor); + if (!mix) + return ENOMEM; + + err = mtx_init(&mix->rwlock, mtx_plain) != thrd_success; + if (err) { + err = ENOMEM; + goto out; + } + + mix->initialized = true; + + out: + if (err) + mem_deref(mix); + else + *mixp = mix; + + return err; +} + + +/** + * Allocate a video mixer source + * + * @param srcp Pointer to allocated video source + * @param mix Video mixer + * @param sz Size of output video frame (optional) + * @param fps Output frame rate (frames per second) + * @param content True if source is of type content + * @param fh Mixer frame handler + * @param arg Handler argument + * + * @return 0 for success, otherwise error code + */ +int vidmix_source_alloc(struct vidmix_source **srcp, struct vidmix *mix, + const struct vidsz *sz, unsigned fps, bool content, + vidmix_frame_h *fh, void *arg) +{ + struct vidmix_source *src; + int err; + + if (!srcp || !mix || !fps || !fh) + return EINVAL; + + src = mem_zalloc(sizeof(*src), source_destructor); + if (!src) + return ENOMEM; + + src->mix = mem_ref(mix); + src->fint = VIDEO_TIMEBASE/fps; + src->content = content; + src->fh = fh; + src->arg = arg; + + err = mtx_init(&src->mutex, mtx_plain) != thrd_success; + if (err) { + err = ENOMEM; + goto out; + } + + if (sz) { + err = vidframe_alloc(&src->frame_tx, VID_FMT_YUV420P, sz); + if (err) + goto out; + + clear_frame(src->frame_tx); + } + + out: + if (err) + mem_deref(src); + else + *srcp = src; + + return err; +} + + +/** + * Check if vidmix source is enabled + * + * @param src Video mixer source + * + * @return true if enabled, otherwise false + */ +bool vidmix_source_isenabled(const struct vidmix_source *src) +{ + return src ? (src->le.list != NULL) : false; +} + + +/** + * Check if vidmix source is running + * + * @param src Video mixer source + * + * @return true if running, otherwise false + */ +bool vidmix_source_isrunning(const struct vidmix_source *src) +{ + return src ? src->run : false; +} + + +/** + * Get focus source + * + * @param src Video mixer source + * + * @return pointer of focused source or NULL if focus is not set + */ +void *vidmix_source_get_focus(const struct vidmix_source *src) +{ + return src ? src->focus : NULL; +} + + +/** + * Enable/disable vidmix source + * + * @param src Video mixer source + * @param enable True to enable, false to disable + */ +void vidmix_source_enable(struct vidmix_source *src, bool enable) +{ + if (!src) + return; + + if (src->le.list && enable) + return; + + if (!src->le.list && !enable) + return; + + mtx_lock(&src->mix->rwlock); + + if (enable) { + if (src->frame_rx) + clear_frame(src->frame_rx); + + list_append(&src->mix->srcl, &src->le, src); + } + else { + list_unlink(&src->le); + } + + clear_all(src->mix); + + mtx_unlock(&src->mix->rwlock); +} + + +/** + * Start vidmix source thread + * + * @param src Video mixer source + * + * @return 0 for success, otherwise error code + */ +int vidmix_source_start(struct vidmix_source *src) +{ + int err; + + if (!src) + return EINVAL; + + if (src->run) + return EALREADY; + + src->run = true; + + err = thread_create_name(&src->thread, "vidmix", + src->content ? content_thread : vidmix_thread, + src); + if (err) + src->run = false; + + return err; +} + + +/** + * Stop vidmix source thread + * + * @param src Video mixer source + */ +void vidmix_source_stop(struct vidmix_source *src) +{ + if (!src) + return; + + if (src->run) { + mtx_lock(&src->mutex); + src->run = false; + mtx_unlock(&src->mutex); + thrd_join(src->thread, NULL); + } +} + + +/** + * Set video mixer output frame size + * + * @param src Video mixer source + * @param sz Size of output video frame + * + * @return 0 for success, otherwise error code + */ +int vidmix_source_set_size(struct vidmix_source *src, const struct vidsz *sz) +{ + struct vidframe *frame; + int err; + + if (!src || !sz) + return EINVAL; + + if (src->frame_tx && vidsz_cmp(&src->frame_tx->size, sz)) + return 0; + + err = vidframe_alloc(&frame, VID_FMT_YUV420P, sz); + if (err) + return err; + + clear_frame(frame); + + mtx_lock(&src->mutex); + mem_deref(src->frame_tx); + src->frame_tx = frame; + mtx_unlock(&src->mutex); + + return 0; +} + + +/** + * Set video mixer output frame rate + * + * @param src Video mixer source + * @param fps Output frame rate (frames per second) + */ +void vidmix_source_set_rate(struct vidmix_source *src, unsigned fps) +{ + if (!src || !fps) + return; + + mtx_lock(&src->mutex); + src->fint = VIDEO_TIMEBASE/fps; + mtx_unlock(&src->mutex); +} + + +/** + * Set video mixer content hide + * + * @param src Video mixer source + * @param hide True to hide content, false to show + */ +void vidmix_source_set_content_hide(struct vidmix_source *src, bool hide) +{ + if (!src) + return; + + mtx_lock(&src->mutex); + src->content_hide = hide; + src->clear = true; + mtx_unlock(&src->mutex); +} + + +/** + * Toggle vidmix source selfview + * + * @param src Video mixer source + */ +void vidmix_source_toggle_selfview(struct vidmix_source *src) +{ + if (!src) + return; + + mtx_lock(&src->mutex); + src->selfview = !src->selfview; + src->clear = true; + mtx_unlock(&src->mutex); +} + + +/** + * Set focus on selected participant source + * + * @param src Video mixer source + * @param focus_src Video mixer source to focus, NULL to clear focus state + * @param focus_full Full focus + */ +void vidmix_source_set_focus(struct vidmix_source *src, + const struct vidmix_source *focus_src, + bool focus_full) +{ + if (!src) + return; + + mtx_lock(&src->mutex); + src->focus_full = focus_full; + src->focus = (void *)focus_src; + src->clear = true; + mtx_unlock(&src->mutex); +} + + +/** + * Set focus on selected participant + * + * @param src Video mixer source + * @param pidx Participant to focus, 0 to disable + */ +void vidmix_source_set_focus_idx(struct vidmix_source *src, unsigned pidx) +{ + bool focus_full = false; + void *focus = NULL; + + if (!src) + return; + + if (pidx > 0) { + + struct le *le; + unsigned i; + + mtx_lock(&src->mix->rwlock); + + for (le=src->mix->srcl.head, i=1; le; le=le->next) { + + const struct vidmix_source *lsrc = le->data; + + if (lsrc == src && !src->selfview) + continue; + + if (lsrc->content && src->content_hide) + continue; + + if (i++ == pidx) { + focus = (void *)lsrc; + break; + } + } + + mtx_unlock(&src->mix->rwlock); + } + + if (focus && focus == src->focus) + focus_full = !src->focus_full; + + mtx_lock(&src->mutex); + src->focus_full = focus_full; + src->focus = focus; + src->clear = true; + mtx_unlock(&src->mutex); +} + + +/** + * Put a video frame into the video mixer + * + * @param src Video source + * @param frame Video frame + */ +void vidmix_source_put(struct vidmix_source *src, const struct vidframe *frame) +{ + if (!src || !frame || frame->fmt != VID_FMT_YUV420P) + return; + + if (!src->frame_rx || !vidsz_cmp(&src->frame_rx->size, &frame->size)) { + + struct vidframe *frm; + int err; + + err = vidframe_alloc(&frm, VID_FMT_YUV420P, &frame->size); + if (err) + return; + + mtx_lock(&src->mix->rwlock); + + mem_deref(src->frame_rx); + src->frame_rx = frm; + + clear_all(src->mix); + + mtx_unlock(&src->mix->rwlock); + } + + mtx_lock(&src->mix->rwlock); + vidframe_copy(src->frame_rx, frame); + mtx_unlock(&src->mix->rwlock); +} From f2d5fc35ce69c10c5fe8a8d2849fa154dbc0362d Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Mon, 6 Feb 2023 13:02:27 +0100 Subject: [PATCH 2/8] ci: remove rem --- .github/workflows/build.yml | 8 -------- .github/workflows/coverage.yml | 10 +--------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 93eccbfaf..f181a6191 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,13 +58,6 @@ jobs: run: | cmake -B build && cmake --build build - - uses: sreimers/pr-dependency-action@v0.5 - with: - name: rem - repo: https://github.com/baresip/rem - secret: ${{ secrets.GITHUB_TOKEN }} - working-directory: '../.' - - uses: sreimers/pr-dependency-action@v0.5 with: name: retest @@ -75,5 +68,4 @@ jobs: - name: retest run: | cd .. - cmake -S rem -B rem/build && cmake --build rem/build cd retest && make && ./retest -r diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 7d9d682e7..86f7f2fd6 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -31,13 +31,6 @@ jobs: run: | cmake -B build -DCMAKE_C_FLAGS="--coverage" && cmake --build build -j - - uses: sreimers/pr-dependency-action@v0.5 - with: - name: rem - repo: https://github.com/baresip/rem - secret: ${{ secrets.GITHUB_TOKEN }} - working-directory: '../.' - - uses: sreimers/pr-dependency-action@v0.5 with: name: retest @@ -48,7 +41,6 @@ jobs: - name: retest run: | cd .. - cmake -S rem -B rem/build && cmake --build rem/build cd retest; cmake -B build -DCMAKE_EXE_LINKER_FLAGS="--coverage" && \ cmake --build build -j && \ ./build/retest -a -v && \ @@ -65,7 +57,7 @@ jobs: - name: coverage check run: | - min_cov="59.4" + min_cov="50.0" cov=$(~/.local/bin/gcovr -r . -s | grep lines | awk '{ print $2 }' | sed 's/%//') echo "Coverage: ${cov}% (min $min_cov%)" exit $(echo "$cov < $min_cov" | bc -l) From 60f9a8f4a5b4d51c8345cd0fb6be4b82a21f42ed Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Mon, 6 Feb 2023 13:53:45 +0100 Subject: [PATCH 3/8] ci: skip building librem --- .github/workflows/sanitizers.yml | 8 -------- .github/workflows/valgrind.yml | 8 -------- 2 files changed, 16 deletions(-) diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index fee98b971..31b4840fa 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -43,13 +43,6 @@ jobs: run: | cmake -B build -DHAVE_THREADS= && cmake --build build -j - - uses: sreimers/pr-dependency-action@v0.5 - with: - name: rem - repo: https://github.com/baresip/rem - secret: ${{ secrets.GITHUB_TOKEN }} - working-directory: '../.' - - uses: sreimers/pr-dependency-action@v0.5 with: name: retest @@ -60,5 +53,4 @@ jobs: - name: retest run: | cd .. - cmake -S rem -B rem/build && cmake --build rem/build -j cd retest && make && ./retest -riv diff --git a/.github/workflows/valgrind.yml b/.github/workflows/valgrind.yml index 3f4dd099c..f285a80e3 100644 --- a/.github/workflows/valgrind.yml +++ b/.github/workflows/valgrind.yml @@ -25,13 +25,6 @@ jobs: run: | cmake -B build && cmake --build build -j - - uses: sreimers/pr-dependency-action@v0.5 - with: - name: rem - repo: https://github.com/baresip/rem - secret: ${{ secrets.GITHUB_TOKEN }} - working-directory: '../.' - - uses: sreimers/pr-dependency-action@v0.5 with: name: retest @@ -42,7 +35,6 @@ jobs: - name: retest run: | cd .. - cmake -S rem -B rem/build && cmake --build rem/build cd retest cmake -B build && cmake --build build -j valgrind --leak-check=full --show-reachable=yes --error-exitcode=42 ./build/retest -r -v From 9a931378b5f6743f39982f31ef551350fe97d295 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Mon, 6 Feb 2023 14:02:42 +0100 Subject: [PATCH 4/8] ci: remove rem --- .github/workflows/mingw.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 3626637d8..afe144d48 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -22,12 +22,6 @@ jobs: - uses: actions/checkout@v3 # needed for pr checkout - - uses: sreimers/pr-dependency-action@v0.5 - with: - name: rem - repo: https://github.com/baresip/rem - secret: ${{ secrets.GITHUB_TOKEN }} - - uses: sreimers/pr-dependency-action@v0.5 with: name: retest @@ -37,7 +31,6 @@ jobs: - name: "clone baresip-win32 repo" run: | git clone https://github.com/baresip/baresip-win32.git - mv rem baresip-win32/ mv retest baresip-win32/ - uses: actions/checkout@v3 From 30120e7eb2f7424758803d3469ba0995a1f6f71b Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Tue, 7 Feb 2023 13:58:03 +0100 Subject: [PATCH 5/8] ci: test mingw repo patch --- .github/workflows/mingw.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index afe144d48..95bce87b1 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -28,11 +28,16 @@ jobs: repo: https://github.com/baresip/retest secret: ${{ secrets.GITHUB_TOKEN }} - - name: "clone baresip-win32 repo" - run: | - git clone https://github.com/baresip/baresip-win32.git + - uses: sreimers/pr-dependency-action@v0.5 + with: + name: baresip-win32 + repo: https://github.com/baresip/baresip-win32 + secret: ${{ secrets.GITHUB_TOKEN }} + + - name: "baresip-win32 repo" + run: | mv retest baresip-win32/ - + - uses: actions/checkout@v3 with: path: baresip-win32/re From 400607d48ce165e1c61d693cda5b81215740bf55 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Tue, 7 Feb 2023 14:01:14 +0100 Subject: [PATCH 6/8] ci: fix 2 spaces as YML indentation --- .github/workflows/mingw.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 95bce87b1..50e2930ad 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -35,7 +35,7 @@ jobs: secret: ${{ secrets.GITHUB_TOKEN }} - name: "baresip-win32 repo" - run: | + run: | mv retest baresip-win32/ - uses: actions/checkout@v3 From 70e453ddb337892555cad1021bb52537092116bc Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Sat, 11 Feb 2023 16:24:29 +0100 Subject: [PATCH 7/8] bump coverage to 57.0% --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 86f7f2fd6..eb04204b3 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -57,7 +57,7 @@ jobs: - name: coverage check run: | - min_cov="50.0" + min_cov="57.0" cov=$(~/.local/bin/gcovr -r . -s | grep lines | awk '{ print $2 }' | sed 's/%//') echo "Coverage: ${cov}% (min $min_cov%)" exit $(echo "$cov < $min_cov" | bc -l) From 876183449b477a6d327199b8f62344442329a4e6 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 08:38:49 +0100 Subject: [PATCH 8/8] link to libm --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5723760a..ad847996f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -624,6 +624,8 @@ if(WIN32) wsock32 ws2_32 ) +else() + list(APPEND LINKLIBS -lm) endif() if(UNIX)