#include "pa.h" uint8_t * tag_uint32(Pulse_Tag *tag, uint32_t val) { uint8_t *ret; ret = tag->data + tag->size; *ret = PA_TAG_U32; val = htonl(val); memcpy(ret + 1, &val, sizeof(val)); ret += PA_TAG_SIZE_U32; tag->size = ret - tag->data; return ret; } uint8_t * untag_uint32(Pulse_Tag *tag, uint32_t *val) { uint8_t *ret; ret = tag->data + tag->size; if ((*ret != PA_TAG_U32) && (*ret != PA_TAG_VOLUME)) return 0; memcpy(val, ret + 1, sizeof(uint32_t)); *val = ntohl(*val); ret += PA_TAG_SIZE_U32; tag->size = ret - tag->data; return ret; } uint8_t * tag_bool(Pulse_Tag *tag, Eina_Bool val) { uint8_t *ret; ret = tag->data + tag->size; *ret = (uint8_t)(val ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE); ret += PA_TAG_SIZE_BOOLEAN; tag->size = ret - tag->data; return ret; } uint8_t * untag_bool(Pulse_Tag *tag, Eina_Bool *val) { uint8_t *ret; ret = tag->data + tag->size; if ((*ret != PA_TAG_BOOLEAN_TRUE) && (*ret != PA_TAG_BOOLEAN_FALSE)) return 0; *val = !!(*ret == PA_TAG_BOOLEAN_TRUE) ? EINA_TRUE : EINA_FALSE; ret += PA_TAG_SIZE_BOOLEAN; tag->size = ret - tag->data; return ret; } uint8_t * tag_string(Pulse_Tag *tag, const char *val) { uint8_t *ret; ret = tag->data + tag->size; if (val) { *ret = PA_TAG_STRING; strcpy((char*)ret + 1, val); ret += PA_TAG_SIZE_STRING + strlen(val); tag->size = ret - tag->data; } else *ret = PA_TAG_STRING_NULL, tag->size++; return ret; } uint8_t * untag_string(Pulse_Tag *tag, const char **val) { uint8_t *ret; ret = tag->data + tag->size; switch (*ret) { case PA_TAG_STRING: eina_stringshare_replace(val, (char*)ret + 1); ret += PA_TAG_SIZE_STRING + strlen(*val); break; case PA_TAG_STRING_NULL: *val = NULL; ret += PA_TAG_SIZE_STRING_NULL; break; default: return 0; } tag->size = ret - tag->data; return ret; } uint8_t * untag_sample(Pulse_Tag *tag, pa_sample_spec *spec) { uint8_t *ret; ret = tag->data + tag->size; if (*ret != PA_TAG_SAMPLE_SPEC) return 0; spec->format = ret[1]; spec->channels = ret[2]; memcpy(&spec->rate, ret + 3, sizeof(spec->rate)); spec->rate = ntohl(spec->rate); ret += PA_TAG_SIZE_SAMPLE_SPEC, tag->size += PA_TAG_SIZE_SAMPLE_SPEC; return ret; } uint8_t * untag_channel_map(Pulse_Tag *tag, pa_channel_map *map) { uint8_t *ret; unsigned int x; ret = tag->data + tag->size; if (*ret != PA_TAG_CHANNEL_MAP) return 0; map->channels = ret[1]; if (map->channels > PA_CHANNELS_MAX) return 0; if (map->channels + 2 + tag->size > tag->dsize) return 0; for (ret += 2, x = 0; x < map->channels; ret++, x++) map->map[x] = (int8_t)*ret; tag->size = ret - tag->data; return ret; } uint8_t * untag_usec(Pulse_Tag *tag, uint64_t *val) { uint8_t *ret; uint32_t tmp; ret = tag->data + tag->size; if (*ret != PA_TAG_USEC) return 0; memcpy(&tmp, ret + 1, 4); *val = (uint64_t)ntohl(tmp) << 32; memcpy(&tmp, ret + 5, 4); *val |= (uint64_t)ntohl(tmp); ret += PA_TAG_SIZE_USEC; tag->size = ret - tag->data; return ret; } uint8_t * tag_arbitrary(Pulse_Tag *tag, const void *val, uint32_t size) { uint8_t *ret; uint32_t tmp; ret = tag->data + tag->size; *ret = PA_TAG_ARBITRARY; tmp = htonl(size); memcpy(ret + 1, &tmp, sizeof(uint32_t)); memcpy(ret + PA_TAG_SIZE_U32, val, size); ret += PA_TAG_SIZE_ARBITRARY + size; tag->size = ret - tag->data; return ret; } uint8_t * untag_arbitrary(Pulse_Tag *tag, Eina_Binbuf **val) { uint8_t *ret; uint32_t len; if (!untag_uint32(tag, &len)) return 0; ret = tag->data + tag->size; if (*ret != PA_TAG_ARBITRARY) return 0; ret += PA_TAG_SIZE_ARBITRARY; *val = eina_binbuf_new(); eina_binbuf_append_length(*val, ret, len); ret += len; tag->size = ret - tag->data; return ret; } uint8_t * tag_simple_init(Pulse *conn, Pulse_Tag *tag, uint32_t val, PA_Tag type) { switch (type) { case PA_TAG_U32: tag_uint32(tag, val); return tag_uint32(tag, conn->tag_count++); default: break; } return NULL; } static Eina_Bool tag_proplist_foreach(const Eina_Hash *h __UNUSED__, const char *key, const char *val, Pulse_Tag *tag) { size_t size; tag_string(tag, key); size = strlen(val) + 1; tag_uint32(tag, size); tag_arbitrary(tag, val, size); return EINA_TRUE; } uint8_t * tag_proplist(Pulse_Tag *tag) { uint8_t *ret; ret = tag->data + tag->size; *ret = PA_TAG_PROPLIST, tag->size++; eina_hash_foreach(tag->props, (Eina_Hash_Foreach)tag_proplist_foreach, tag); return tag_string(tag, NULL); } uint8_t * untag_proplist(Pulse_Tag *tag, Eina_Hash **props) { uint8_t *ret; ret = tag->data + tag->size; if (*ret != PA_TAG_PROPLIST) return 0; *props = eina_hash_string_superfast_new((Eina_Free_Cb)eina_binbuf_free); for (++tag->size; *ret != PA_TAG_STRING_NULL && tag->size < tag->dsize - 1;) { const char *key = NULL; Eina_Binbuf *val; EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &key), error); EINA_SAFETY_ON_FALSE_GOTO(untag_arbitrary(tag, &val), error); #if 0 { char buf[128]; snprintf(buf, sizeof(buf), "key='%%s', val='%%.%zus'", eina_binbuf_length_get(val)); DBG(buf, key, eina_binbuf_string_get(val)); } #endif eina_hash_add(*props, key, val); eina_stringshare_del(key); ret = tag->data + tag->size; } tag->size++; return ++ret; error: eina_hash_free(*props); return 0; } uint8_t * tag_volume(Pulse_Tag *tag, uint8_t channels, double vol) { uint32_t pa_vol; uint8_t *ret, x; if (vol <= 0.0) pa_vol = PA_VOLUME_MUTED; else pa_vol = ((vol * PA_VOLUME_NORM) - (PA_VOLUME_NORM / 2)) / 100; pa_vol = htonl(pa_vol); ret = tag->data + tag->size; *ret++ = PA_TAG_CVOLUME; *ret++ = channels; for (x = 0; x < channels; x++, ret += sizeof(pa_vol)) memcpy(ret, &pa_vol, sizeof(pa_vol)); tag->size = ret - tag->data; return ret; } uint8_t * tag_cvol(Pulse_Tag *tag, pa_cvolume *c) { uint8_t *ret, x; uint32_t pa_vol; ret = tag->data + tag->size; *ret++ = PA_TAG_CVOLUME; *ret++ = c->channels; for (x = 0; x < c->channels; x++, ret += sizeof(pa_vol)) { pa_vol = htonl(c->values[x]); memcpy(ret, &pa_vol, sizeof(pa_vol)); } tag->size = ret - tag->data; return ret; } uint8_t * untag_cvol(Pulse_Tag *tag, pa_cvolume *cvol) { uint32_t pa_vol; uint8_t *ret, x; ret = tag->data + tag->size; if (*ret != PA_TAG_CVOLUME) return 0; cvol->channels = ret[1]; for (x = 0, ret += 2; x < cvol->channels; x++, ret += sizeof(pa_vol)) { memcpy(&pa_vol, ret, sizeof(pa_vol)); cvol->values[x] = ntohl(pa_vol); } tag->size = ret - tag->data; return ret; } void tag_finish(Pulse_Tag *tag) { EINA_SAFETY_ON_NULL_RETURN(tag); tag->header[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1); tag->header[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(tag->dsize); } void pulse_tag_free(Pulse_Tag *tag) { if (!tag) return; free(tag->data); if (tag->props) eina_hash_free(tag->props); free(tag); }