diff options
Diffstat (limited to 'src/modules/module-stream-restore.c')
| -rw-r--r-- | src/modules/module-stream-restore.c | 205 | 
1 files changed, 158 insertions, 47 deletions
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index f2982315..fe30e295 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -87,10 +87,12 @@ struct userdata {  };  struct entry { -    char device[PA_NAME_MAX];      pa_channel_map channel_map; -    pa_cvolume volume; +    char device[PA_NAME_MAX]; +    pa_cvolume relative_volume; +    pa_cvolume absolute_volume;      pa_bool_t muted:1; +    pa_bool_t device_valid:1, relative_volume_valid:1, absolute_volume_valid:1, muted_valid:1;  }; @@ -153,7 +155,9 @@ static struct entry* read_entry(struct userdata *u, const char *name) {          goto fail;      if (data.dsize != sizeof(struct entry)) { -        pa_log_warn("Database contains entry for stream %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); +        /* This is probably just a database upgrade, hence let's not +         * consider this more than a debug message */ +        pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));          goto fail;      } @@ -164,18 +168,19 @@ static struct entry* read_entry(struct userdata *u, const char *name) {          goto fail;      } -    if (!(pa_cvolume_valid(&e->volume))) { -        pa_log_warn("Invalid volume stored in database for stream %s", name); +    if (!(pa_channel_map_valid(&e->channel_map))) { +        pa_log_warn("Invalid channel map stored in database for stream %s", name);          goto fail;      } -    if (!(pa_channel_map_valid(&e->channel_map))) { -        pa_log_warn("Invalid channel map stored in database for stream %s", name); +    if (e->device_valid && !pa_namereg_is_valid_name(e->device)) { +        pa_log_warn("Invalid device name stored in database for stream %s", name);          goto fail;      } -    if (e->volume.channels != e->channel_map.channels) { -        pa_log_warn("Volume and channel map don't match in database entry for stream %s", name); +    if ((e->relative_volume_valid && (!pa_cvolume_valid(&e->relative_volume) || e->relative_volume.channels != e->channel_map.channels)) || +        (e->absolute_volume_valid && (!pa_cvolume_valid(&e->absolute_volume) || e->absolute_volume.channels != e->channel_map.channels))) { +        pa_log_warn("Invalid volume stored in database for stream %s", name);          goto fail;      } @@ -213,6 +218,33 @@ static void trigger_save(struct userdata *u) {      u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);  } +static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { +    pa_cvolume t; + +    pa_assert(a); +    pa_assert(b); + +    if (a->device_valid != b->device_valid || +        (a->device_valid && strncmp(a->device, b->device, sizeof(a->device)))) +        return FALSE; + +    if (a->muted_valid != b->muted_valid || +        (a->muted && (a->muted != b->muted))) +        return FALSE; + +    t = b->relative_volume; +    if (a->relative_volume_valid != b->relative_volume_valid || +        (a->relative_volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->relative_volume))) +        return FALSE; + +    t = b->absolute_volume; +    if (a->absolute_volume_valid != b->absolute_volume_valid || +        (a->absolute_volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->absolute_volume))) +        return FALSE; + +    return TRUE; +} +  static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {      struct userdata *u = userdata;      struct entry entry, *old; @@ -240,9 +272,23 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3              return;          entry.channel_map = sink_input->channel_map; -        entry.volume = *pa_sink_input_get_volume(sink_input); + +        if (sink_input->sink->flags & PA_SINK_FLAT_VOLUME) { +            entry.absolute_volume = *pa_sink_input_get_volume(sink_input); +            entry.absolute_volume_valid = sink_input->save_volume; + +            pa_sw_cvolume_divide(&entry.relative_volume, &entry.absolute_volume, pa_sink_get_volume(sink_input->sink, FALSE)); +            entry.relative_volume_valid = sink_input->save_volume; +        } else { +            entry.relative_volume = *pa_sink_input_get_volume(sink_input); +            entry.relative_volume_valid = sink_input->save_volume; +        } +          entry.muted = pa_sink_input_get_mute(sink_input); +        entry.muted_valid = sink_input->save_muted; +          pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device)); +        entry.device_valid = sink_input->save_sink;      } else {          pa_source_output *source_output; @@ -255,19 +301,15 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3          if (!(name = get_name(source_output->proplist, "source-output")))              return; -        /* The following fields are filled in to make the entry valid -         * according to read_entry(). They are otherwise useless */          entry.channel_map = source_output->channel_map; -        pa_cvolume_reset(&entry.volume, entry.channel_map.channels); +          pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device)); +        entry.device_valid = source_output->save_source;      }      if ((old = read_entry(u, name))) { -        if (pa_cvolume_equal(pa_cvolume_remap(&old->volume, &old->channel_map, &entry.channel_map), &entry.volume) && -            !old->muted == !entry.muted && -            strncmp(old->device, entry.device, sizeof(entry.device)) == 0) { - +        if (entries_equal(old, &entry)) {              pa_xfree(old);              pa_xfree(name);              return; @@ -297,20 +339,25 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n      pa_assert(new_data); +    if (!u->restore_device) +        return PA_HOOK_OK; +      if (!(name = get_name(new_data->proplist, "sink-input")))          return PA_HOOK_OK;      if ((e = read_entry(u, name))) {          pa_sink *s; -        if (u->restore_device && -            (s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK))) { +        if (e->device_valid) { -            if (!new_data->sink) { -                pa_log_info("Restoring device for stream %s.", name); -                new_data->sink = s; -            } else -                pa_log_info("Not restore device for stream %s, because already set.", name); +            if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK))) { +                if (!new_data->sink) { +                    pa_log_info("Restoring device for stream %s.", name); +                    new_data->sink = s; +                    new_data->save_sink = TRUE; +                } else +                    pa_log_info("Not restore device for stream %s, because already set.", name); +            }          }          pa_xfree(e); @@ -327,6 +374,9 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu      pa_assert(new_data); +    if (!u->restore_volume && !u->restore_muted) +        return PA_HOOK_OK; +      if (!(name = get_name(new_data->proplist, "sink-input")))          return PA_HOOK_OK; @@ -335,16 +385,37 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu          if (u->restore_volume) {              if (!new_data->virtual_volume_is_set) { -                pa_log_info("Restoring volume for sink input %s.", name); -                pa_sink_input_new_data_set_virtual_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map)); +                pa_cvolume v; +                pa_cvolume_init(&v); + +                if (new_data->sink->flags & PA_SINK_FLAT_VOLUME) { + +                    if (e->absolute_volume_valid && +                        e->device_valid && +                        pa_streq(new_data->sink->name, e->device)) +                        v = e->absolute_volume; +                    else if (e->relative_volume_valid) { +                        pa_cvolume t = new_data->sink->virtual_volume; +                        pa_sw_cvolume_multiply(&v, &e->relative_volume, pa_cvolume_remap(&t, &new_data->sink->channel_map, &e->channel_map)); +                    } + +                } else if (e->relative_volume_valid) +                    v = e->relative_volume; + +                if (v.channels > 0) { +                    pa_log_info("Restoring volume for sink input %s.", name); +                    pa_sink_input_new_data_set_virtual_volume(new_data, pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map)); +                    new_data->save_volume = TRUE; +                }              } else                  pa_log_debug("Not restoring volume for sink input %s, because already set.", name);          } -        if (u->restore_muted) { +        if (u->restore_muted && e->muted_valid) {              if (!new_data->muted_is_set) {                  pa_log_info("Restoring mute state for sink input %s.", name);                  pa_sink_input_new_data_set_muted(new_data, e->muted); +                new_data->save_muted = TRUE;              } else                  pa_log_debug("Not restoring mute state for sink input %s, because already set.", name);          } @@ -363,21 +434,27 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou      pa_assert(new_data); +    if (!u->restore_device) +        return PA_HOOK_OK; + +    if (new_data->direct_on_input) +        return PA_HOOK_OK; +      if (!(name = get_name(new_data->proplist, "source-output")))          return PA_HOOK_OK;      if ((e = read_entry(u, name))) {          pa_source *s; -        if (u->restore_device && -            !new_data->direct_on_input && -            (s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE))) { - -            if (!new_data->source) { -                pa_log_info("Restoring device for stream %s.", name); -                new_data->source = s; -            } else -                pa_log_info("Not restoring device for stream %s, because already set", name); +        if (e->device_valid) { +            if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE))) { +                if (!new_data->source) { +                    pa_log_info("Restoring device for stream %s.", name); +                    new_data->source = s; +                    new_data->save_source = TRUE; +                } else +                    pa_log_info("Not restoring device for stream %s, because already set", name); +            }          }          pa_xfree(e); @@ -431,18 +508,37 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {          }          if (u->restore_volume) { -            pa_cvolume v = e->volume; -            pa_log_info("Restoring volume for sink input %s.", name); -            pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map)); +            pa_cvolume v; +            pa_cvolume_init(&v); + +            if (si->sink->flags & PA_SINK_FLAT_VOLUME) { + +                if (e->absolute_volume_valid && +                    e->device_valid && +                    pa_streq(e->device, si->sink->name)) +                    v = e->absolute_volume; +                else if (e->relative_volume_valid) { +                    pa_cvolume t = si->sink->virtual_volume; +                    pa_sw_cvolume_multiply(&v, &e->relative_volume, pa_cvolume_remap(&t, &si->sink->channel_map, &e->channel_map)); +                } +            } else if (e->relative_volume_valid) +                v = e->relative_volume; + +            if (v.channels > 0) { +                pa_log_info("Restoring volume for sink input %s.", name); +                pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map), TRUE); +            }          } -        if (u->restore_muted) { +        if (u->restore_muted && +            e->muted_valid) {              pa_log_info("Restoring mute state for sink input %s.", name);              pa_sink_input_set_mute(si, e->muted, TRUE);          }          if (u->restore_device && -            (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) { +            e->device_valid && +            (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SINK))) {              pa_log_info("Restoring device for stream %s.", name);              pa_sink_input_move_to(si, s, TRUE); @@ -462,6 +558,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {          }          if (u->restore_device && +            e->device_valid &&              (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {              pa_log_info("Restoring device for stream %s.", name); @@ -548,11 +645,13 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio                  pa_xfree(key.dptr);                  if ((e = read_entry(u, name))) { +                    pa_cvolume r; +                      pa_tagstruct_puts(reply, name);                      pa_tagstruct_put_channel_map(reply, &e->channel_map); -                    pa_tagstruct_put_cvolume(reply, &e->volume); -                    pa_tagstruct_puts(reply, e->device); -                    pa_tagstruct_put_boolean(reply, e->muted); +                    pa_tagstruct_put_cvolume(reply, e->relative_volume_valid ? &e->relative_volume : pa_cvolume_init(&r)); +                    pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL); +                    pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE);                      pa_xfree(e);                  } @@ -592,16 +691,28 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio                  if (pa_tagstruct_gets(t, &name) < 0 ||                      pa_tagstruct_get_channel_map(t, &entry.channel_map) || -                    pa_tagstruct_get_cvolume(t, &entry.volume) < 0 || +                    pa_tagstruct_get_cvolume(t, &entry.relative_volume) < 0 ||                      pa_tagstruct_gets(t, &device) < 0 ||                      pa_tagstruct_get_boolean(t, &muted) < 0)                      goto fail; -                if (entry.channel_map.channels != entry.volume.channels) +                entry.absolute_volume_valid = FALSE; +                entry.relative_volume_valid = entry.relative_volume.channels > 0; + +                if (entry.relative_volume_valid && +                    entry.channel_map.channels != entry.relative_volume.channels)                      goto fail;                  entry.muted = muted; -                pa_strlcpy(entry.device, device, sizeof(entry.device)); +                entry.muted_valid = TRUE; + +                if (device) +                    pa_strlcpy(entry.device, device, sizeof(entry.device)); +                entry.device_valid = !!entry.device[0]; + +                if (entry.device_valid && +                    !pa_namereg_is_valid_name(entry.device)) +                    goto fail;                  key.dptr = (void*) name;                  key.dsize = (int) strlen(name);  | 
