Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit faf04238 authored by Jakub Tyszkowski's avatar Jakub Tyszkowski
Browse files

LeAudio: Add missing direction contexts for metadata passing

The group and device APIs were getting single combined stream context type
which caused unnecessary context mixing for a single direction in a
stream. With this change, the groups and the devices always get the metadata
for each direction but at the end, the ASEs will get only the proper direction
metadata when stream is enabled (or metadata updated).

This also solves the problem of the missing reconfiguration context, i.e. which
direction metadata should be passed to the group and device after
previous stream gets stopped.

Last but not least, the mixed content can force additional volume level
changes if the remote device keeps each stream type volume on its end
and restores them when the context changes (instead of realying on the
central device to restore the proper volume).

Bug: 247659576
Tag: #feature
Test: atest BluetoothInstrumentationTests
Test: atest --host bluetooth_le_audio_client_test bluetooth_le_audio_test --no-bazel-mode
Change-Id: I37b0175a55c787e18547b9612a142bfaae11b678
parent 41b7d6b4
Loading
Loading
Loading
Loading
+130 −135
Original line number Diff line number Diff line
@@ -314,8 +314,7 @@ class LeAudioClientImpl : public LeAudioClient {

    LOG_DEBUG("new_configuration_context= %s",
              ToString(new_configuration_context).c_str());
    ReconfigureOrUpdateMetadata(group, new_configuration_context,
                                metadata_context_types_.sink);
    ReconfigureOrUpdateMetadata(group, new_configuration_context);
  }

  void StartVbcCloseTimeout() {
@@ -792,13 +791,12 @@ class LeAudioClientImpl : public LeAudioClient {
    return AudioContexts(LeAudioContextType::UNSPECIFIED);
  }

  bool GroupStream(const int group_id, LeAudioContextType context_type,
                   AudioContexts metadata_context_type) {
  bool GroupStream(
      const int group_id, LeAudioContextType context_type,
      const BidirectionalPair<AudioContexts>& metadata_context_types) {
    LeAudioDeviceGroup* group = aseGroups_.FindById(group_id);
    auto final_context_type = context_type;

    auto adjusted_metadata_context_type =
        ChooseMetadataContextType(metadata_context_type);
    DLOG(INFO) << __func__;
    if (context_type >= LeAudioContextType::RFU) {
      LOG(ERROR) << __func__ << ", stream context type is not supported: "
@@ -851,17 +849,21 @@ class LeAudioClientImpl : public LeAudioClient {
          bluetooth::common::time_get_os_boottime_us();
    }

    BidirectionalPair<std::vector<uint8_t>> ccids = {
        .sink = ContentControlIdKeeper::GetInstance()->GetAllCcids(
            metadata_context_types.sink),
        .source = ContentControlIdKeeper::GetInstance()->GetAllCcids(
            metadata_context_types.source)};
    bool result = groupStateMachine_->StartStream(
        group, final_context_type, adjusted_metadata_context_type,
        ContentControlIdKeeper::GetInstance()->GetAllCcids(
            adjusted_metadata_context_type));
        group, final_context_type, metadata_context_types, ccids);

    return result;
  }

  void GroupStream(const int group_id, const uint16_t context_type) override {
    GroupStream(group_id, LeAudioContextType(context_type),
                AudioContexts(context_type));
    BidirectionalPair<AudioContexts> initial_contexts = {
        AudioContexts(context_type), AudioContexts(context_type)};
    GroupStream(group_id, LeAudioContextType(context_type), initial_contexts);
  }

  void GroupSuspend(const int group_id) override {
@@ -3239,7 +3241,7 @@ class LeAudioClientImpl : public LeAudioClient {
      return true;
    }
    return GroupStream(active_group_id_, configuration_context_type_,
                       get_bidirectional(metadata_context_types_));
                       metadata_context_types_);
  }

  void OnAudioSuspend() {
@@ -3707,40 +3709,14 @@ class LeAudioClientImpl : public LeAudioClient {
              ToString(group->GetState()).c_str(),
              ToString(group->GetTargetState()).c_str());

    auto new_metadata_context_types_ = AudioContexts();

    /* If the local sink is started, ready to start or any direction is
     * reconfiguring to start sit remote source configuration, then take
     * into the account current context type. If the metadata seem
     * invalid, keep the old one, but verify against the availability.
     * Otherwise start empty and add the tracks contexts.
     */
    auto is_releasing_for_reconfiguration =
        (((audio_receiver_state_ == AudioState::RELEASING) ||
          (audio_sender_state_ == AudioState::RELEASING)) &&
         group->IsPendingConfiguration() &&
         IsDirectionAvailableForCurrentConfiguration(
             group, le_audio::types::kLeAudioDirectionSource));
    if (is_releasing_for_reconfiguration ||
        (audio_receiver_state_ == AudioState::STARTED) ||
        (audio_receiver_state_ == AudioState::READY_TO_START)) {
      LOG_DEBUG("Other direction is streaming. Taking its contexts %s",
                ToString(metadata_context_types_.source).c_str());
      new_metadata_context_types_ =
          ChooseMetadataContextType(metadata_context_types_.source);

    } else if (source_metadata.empty()) {
      LOG_DEBUG("Not a valid sink metadata update. Keeping the old contexts");
      new_metadata_context_types_ &= group->GetAvailableContexts();

    } else {
      LOG_DEBUG("No other direction is streaming. Start with empty contexts.");
    }
    // Make sure the other direction is tested against the available contexts
    metadata_context_types_.source &= group->GetAvailableContexts();

    /* Set the remote sink metadata context from the playback tracks metadata */
    metadata_context_types_.sink = GetAllowedAudioContextsFromSourceMetadata(
        source_metadata, group->GetAvailableContexts());
    new_metadata_context_types_ |= metadata_context_types_.sink;
    metadata_context_types_.sink =
        ChooseMetadataContextType(metadata_context_types_.sink);

    if (stack_config_get_interface()
            ->get_pts_force_le_audio_multiple_contexts_metadata()) {
@@ -3764,19 +3740,47 @@ class LeAudioClientImpl : public LeAudioClient {
      LOG_DEBUG("new_configuration_context= %s.",
                ToString(new_configuration_context).c_str());
      GroupStream(active_group_id_, new_configuration_context,
                  metadata_context_types_.sink);
                  metadata_context_types_);
      return;
    }

    if (new_metadata_context_types_.none()) {
      LOG_WARN("invalid/unknown context metadata, using 'UNSPECIFIED' instead");
      new_metadata_context_types_ =
    // We expect at least some context when remote sink gets enabled
    if (metadata_context_types_.sink.none()) {
      LOG_WARN(
          "invalid/unknown sink context metadata, using 'UNSPECIFIED' instead");
      metadata_context_types_.sink =
          AudioContexts(LeAudioContextType::UNSPECIFIED);
    }

    /* Choose the right configuration context */
    /* Start with only this direction context metadata */
    auto configuration_context_candidates = metadata_context_types_.sink;

    /* If the local sink is started, ready to start or any direction is
     * reconfiguring when the remote sink configuration is active, then take
     * into the account current context type for this direction when
     * configuration context is selected.
     */
    auto is_releasing_for_reconfiguration =
        (((audio_receiver_state_ == AudioState::RELEASING) ||
          (audio_sender_state_ == AudioState::RELEASING)) &&
         group->IsPendingConfiguration() &&
         IsDirectionAvailableForCurrentConfiguration(
             group, le_audio::types::kLeAudioDirectionSource));
    if (is_releasing_for_reconfiguration ||
        (audio_receiver_state_ == AudioState::STARTED) ||
        (audio_receiver_state_ == AudioState::READY_TO_START)) {
      LOG_DEBUG("Other direction is streaming. Taking its contexts %s",
                ToString(metadata_context_types_.source).c_str());
      configuration_context_candidates =
          ChooseMetadataContextType(get_bidirectional(metadata_context_types_));
    }
    LOG_DEBUG("configuration_context_candidates= %s",
              ToString(configuration_context_candidates).c_str());

    auto new_configuration_context =
        ChooseConfigurationContextType(new_metadata_context_types_);
        ChooseConfigurationContextType(configuration_context_candidates);
    LOG_DEBUG("new_configuration_context= %s",
              ToString(new_configuration_context).c_str());

    /* For the following contexts we don't actually need HQ audio:
     * LeAudioContextType::NOTIFICATIONS
@@ -3791,19 +3795,21 @@ class LeAudioClientImpl : public LeAudioClient {
        LeAudioContextType::NOTIFICATIONS | LeAudioContextType::SOUNDEFFECTS |
        LeAudioContextType::INSTRUCTIONAL | LeAudioContextType::ALERTS |
        LeAudioContextType::EMERGENCYALARM;
    if ((new_metadata_context_types_ & ~no_reconfigure_contexts).none() &&
    if ((configuration_context_candidates & ~no_reconfigure_contexts).none() &&
        IsDirectionAvailableForCurrentConfiguration(
            group, le_audio::types::kLeAudioDirectionSink)) {
      LOG_INFO(
          "There is no need to reconfigure for the sonification events. Keep "
          "the configuration unchanged.");
          "There is no need to reconfigure for the sonification events, "
          "staying with the existing configuration context of %s",
          ToString(configuration_context_type_).c_str());
      new_configuration_context = configuration_context_type_;
    }

    LOG_DEBUG("new_configuration_context= %s",
              ToString(new_configuration_context).c_str());
    ReconfigureOrUpdateMetadata(group, new_configuration_context,
                                std::move(new_metadata_context_types_));
    LOG_DEBUG("metadata_context_types_.sink= %s",
              ToString(metadata_context_types_.sink).c_str());
    LOG_DEBUG("metadata_context_types_.source= %s",
              ToString(metadata_context_types_.source).c_str());
    ReconfigureOrUpdateMetadata(group, new_configuration_context);
  }

  void OnLocalAudioSinkMetadataUpdate(
@@ -3824,39 +3830,14 @@ class LeAudioClientImpl : public LeAudioClient {
              ToString(group->GetState()).c_str(),
              ToString(group->GetTargetState()).c_str());

    auto new_metadata_context_types = AudioContexts();

    /* If the local source is started, ready to start or any direction is
     * reconfiguring to start sit remote sink configuration, then take
     * into the account current context type. If the metadata seem
     * invalid, keep the old one, but verify against the availability.
     * Otherwise start empty and add the tracks contexts.
     */
    auto is_releasing_for_reconfiguration =
        (((audio_receiver_state_ == AudioState::RELEASING) ||
          (audio_sender_state_ == AudioState::RELEASING)) &&
         group->IsPendingConfiguration() &&
         IsDirectionAvailableForCurrentConfiguration(
             group, le_audio::types::kLeAudioDirectionSink));
    if (is_releasing_for_reconfiguration ||
        (audio_sender_state_ == AudioState::STARTED) ||
        (audio_sender_state_ == AudioState::READY_TO_START)) {
      LOG_DEBUG("Other direction is streaming. Taking its contexts %s",
                ToString(metadata_context_types_.sink).c_str());
      new_metadata_context_types =
          ChooseMetadataContextType(metadata_context_types_.sink);

    } else if (sink_metadata.empty()) {
      LOG_DEBUG("Not a valid sink metadata update. Keeping the old contexts");
      new_metadata_context_types &= group->GetAvailableContexts();

    } else {
      LOG_DEBUG("No other direction is streaming. Start with empty contexts.");
    }
    // Make sure the other direction is tested against the available contexts
    metadata_context_types_.sink &= group->GetAvailableContexts();

    /* Set remote source metadata context from the recording tracks metadata */
    metadata_context_types_.source = GetAllowedAudioContextsFromSinkMetadata(
        sink_metadata, group->GetAvailableContexts());
    metadata_context_types_.source =
        ChooseMetadataContextType(metadata_context_types_.source);

    /* Make sure we have CONVERSATIONAL when in a call */
    if (in_call_) {
@@ -3865,44 +3846,67 @@ class LeAudioClientImpl : public LeAudioClient {
          AudioContexts(LeAudioContextType::CONVERSATIONAL);
    }

    /* Append the remote source context types */
    new_metadata_context_types |= metadata_context_types_.source;

    if (stack_config_get_interface()
            ->get_pts_force_le_audio_multiple_contexts_metadata()) {
      // Use common audio stream contexts exposed by the PTS
      new_metadata_context_types = AudioContexts(0xFFFF);
      metadata_context_types_.source = AudioContexts(0xFFFF);
      for (auto device = group->GetFirstDevice(); device != nullptr;
           device = group->GetNextDevice(device)) {
        new_metadata_context_types &= device->GetAvailableContexts();
        metadata_context_types_.source &= device->GetAvailableContexts();
      }
      if (new_metadata_context_types.value() == 0xFFFF) {
        new_metadata_context_types =
      if (metadata_context_types_.source.value() == 0xFFFF) {
        metadata_context_types_.source =
            AudioContexts(LeAudioContextType::UNSPECIFIED);
      }
      LOG_WARN("Overriding new_metadata_context_types with: %su",
               new_metadata_context_types.to_string().c_str());
      LOG_WARN("Overriding metadata_context_types_.source with: %su",
               metadata_context_types_.source.to_string().c_str());

      /* Choose the right configuration context */
      const auto new_configuration_context =
          ChooseConfigurationContextType(new_metadata_context_types);
          ChooseConfigurationContextType(metadata_context_types_.source);

      LOG_DEBUG("new_configuration_context= %s.",
                ToString(new_configuration_context).c_str());
      new_metadata_context_types.set(new_configuration_context);
      metadata_context_types_.source.set(new_configuration_context);
    }

    if (new_metadata_context_types.none()) {
      LOG_WARN("invalid/unknown context metadata, using 'UNSPECIFIED' instead");
      new_metadata_context_types =
    // We expect at least some context when remote source gets enabled
    if (metadata_context_types_.source.none()) {
      LOG_WARN(
          "invalid/unknown source context metadata, using 'UNSPECIFIED' "
          "instead");
      metadata_context_types_.source =
          AudioContexts(LeAudioContextType::UNSPECIFIED);
    }

    /* Start with only this direction context metadata */
    auto configuration_context_candidates = metadata_context_types_.source;

    /* If the local source is started, ready to start or any direction is
     * reconfiguring when the remote sink configuration is active, then take
     * into the account current context type for this direction when
     * configuration context is selected.
     */
    auto is_releasing_for_reconfiguration =
        (((audio_receiver_state_ == AudioState::RELEASING) ||
          (audio_sender_state_ == AudioState::RELEASING)) &&
         group->IsPendingConfiguration() &&
         IsDirectionAvailableForCurrentConfiguration(
             group, le_audio::types::kLeAudioDirectionSink));
    if (is_releasing_for_reconfiguration ||
        (audio_sender_state_ == AudioState::STARTED) ||
        (audio_sender_state_ == AudioState::READY_TO_START)) {
      configuration_context_candidates =
          ChooseMetadataContextType(get_bidirectional(metadata_context_types_));
      LOG_DEBUG("Other direction is streaming. Taking its contexts %s",
                ToString(metadata_context_types_.sink).c_str());
    }
    LOG_DEBUG("configuration_context_candidates= %s",
              ToString(configuration_context_candidates).c_str());

    /* Choose the right configuration context */
    const auto new_configuration_context =
        ChooseConfigurationContextType(new_metadata_context_types);
    LOG_DEBUG("new_configuration_context= %s",
              ToString(new_configuration_context).c_str());
    auto new_configuration_context =
        ChooseConfigurationContextType(configuration_context_candidates);

    /* Do nothing if audio source is not valid for the new configuration */
    const auto is_audio_source_context =
@@ -3931,27 +3935,22 @@ class LeAudioClientImpl : public LeAudioClient {
          "context in %s. Not switching to %s right now.",
          ToString(configuration_context_type_).c_str(),
          ToString(new_configuration_context).c_str());
      return;
      new_configuration_context = configuration_context_type_;
    }

    ReconfigureOrUpdateMetadata(group, new_configuration_context,
                                std::move(new_metadata_context_types));
    LOG_DEBUG("metadata_context_types_.sink= %s",
              ToString(metadata_context_types_.sink).c_str());
    LOG_DEBUG("metadata_context_types_.source= %s",
              ToString(metadata_context_types_.source).c_str());
    ReconfigureOrUpdateMetadata(group, new_configuration_context);
  }

  void ReconfigureOrUpdateMetadata(LeAudioDeviceGroup* group,
                                   LeAudioContextType new_configuration_context,
                                   AudioContexts new_metadata_context_types) {
  void ReconfigureOrUpdateMetadata(
      LeAudioDeviceGroup* group, LeAudioContextType new_configuration_context) {
    if (new_configuration_context != configuration_context_type_) {
      LOG_DEBUG(
          "Changing configuration context from %s to %s, new "
          "metadata_contexts: %s",
      LOG_DEBUG("Changing configuration context from %s to %s",
                ToString(configuration_context_type_).c_str(),
          ToString(new_configuration_context).c_str(),
          ToString(new_metadata_context_types).c_str());
      // TODO: This should also cache the combined metadata context for the
      //       reconfiguration, so that once the group reaches IDLE state and
      //       is about to reconfigure, we would know if we reconfigure with
      //       sink or source or both metadata.
                ToString(new_configuration_context).c_str());
      if (SetConfigurationAndStopStreamWhenNeeded(group,
                                                  new_configuration_context)) {
        return;
@@ -3960,13 +3959,13 @@ class LeAudioClientImpl : public LeAudioClient {

    if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
      LOG_DEBUG(
          "The %s configuration did not change. Changing only the metadata "
          "contexts from %s to %s",
          "The %s configuration did not change. Updating the metadata to "
          "sink=%s, source=%s",
          ToString(configuration_context_type_).c_str(),
          ToString(get_bidirectional(metadata_context_types_)).c_str(),
          ToString(new_metadata_context_types).c_str());
      GroupStream(group->group_id_, new_configuration_context,
                  new_metadata_context_types);
          ToString(metadata_context_types_.sink).c_str(),
          ToString(metadata_context_types_.source).c_str());
      GroupStream(group->group_id_, configuration_context_type_,
                  metadata_context_types_);
    }
  }

@@ -4336,18 +4335,14 @@ class LeAudioClientImpl : public LeAudioClient {
      case GroupStreamStatus::IDLE: {
        if (group && group->IsPendingConfiguration()) {
          SuspendedForReconfiguration();
          // TODO: It is not certain to which directions we will
          //       reconfigure. We would have know the exact
          //       configuration but this is yet to be selected or have
          //       the metadata cached from earlier when reconfiguration
          //       was scheduled.
          auto adjusted_metedata_context_type = ChooseMetadataContextType(
              get_bidirectional(metadata_context_types_));
          BidirectionalPair<std::vector<uint8_t>> ccids = {
              .sink = ContentControlIdKeeper::GetInstance()->GetAllCcids(
                  metadata_context_types_.sink),
              .source = ContentControlIdKeeper::GetInstance()->GetAllCcids(
                  metadata_context_types_.source)};
          if (groupStateMachine_->ConfigureStream(
                  group, configuration_context_type_,
                  adjusted_metedata_context_type,
                  ContentControlIdKeeper::GetInstance()->GetAllCcids(
                      adjusted_metedata_context_type))) {
                  group, configuration_context_type_, metadata_context_types_,
                  ccids)) {
            /* If configuration succeed wait for new status. */
            return;
          }
+50 −50

File changed.

Preview size limit exceeded, changes collapsed.

+34 −20
Original line number Diff line number Diff line
@@ -163,18 +163,23 @@ class LeAudioDevice {
                             codec_capability_setting);
  uint8_t GetLc3SupportedChannelCount(uint8_t direction);
  uint8_t GetPhyBitmask(void);
  bool ConfigureAses(const le_audio::set_configurations::SetConfiguration& ent,
  bool ConfigureAses(
      const le_audio::set_configurations::SetConfiguration& ent,
      types::LeAudioContextType context_type,
      uint8_t* number_of_already_active_group_ase,
                     types::AudioLocations& group_snk_audio_locations,
                     types::AudioLocations& group_src_audio_locations,
                     bool reconnect, types::AudioContexts metadata_context_type,
                     const std::vector<uint8_t>& ccid_list);
      types::BidirectionalPair<types::AudioLocations>&
          group_audio_locations_out,
      const types::BidirectionalPair<types::AudioContexts>&
          metadata_context_types,
      const types::BidirectionalPair<std::vector<uint8_t>>& ccid_lists,
      bool reuse_cis_id);
  void SetSupportedContexts(types::AudioContexts snk_contexts,
                            types::AudioContexts src_contexts);
  types::AudioContexts GetAvailableContexts(
  inline types::AudioContexts GetAvailableContexts(
      int direction = (types::kLeAudioDirectionSink |
                       types::kLeAudioDirectionSource));
                       types::kLeAudioDirectionSource)) {
    return avail_contexts_.get(direction);
  }
  types::AudioContexts SetAvailableContexts(types::AudioContexts snk_cont_val,
                                            types::AudioContexts src_cont_val);
  void DeactivateAllAses(void);
@@ -186,8 +191,9 @@ class LeAudioDevice {
  void DisconnectAcl(void);
  std::vector<uint8_t> GetMetadata(types::AudioContexts context_type,
                                   const std::vector<uint8_t>& ccid_list);
  bool IsMetadataChanged(types::AudioContexts context_type,
                         const std::vector<uint8_t>& ccid_list);
  bool IsMetadataChanged(
      const types::BidirectionalPair<types::AudioContexts>& context_types,
      const types::BidirectionalPair<std::vector<uint8_t>>& ccid_lists);

 private:
  types::BidirectionalPair<types::AudioContexts> avail_contexts_;
@@ -245,7 +251,10 @@ class LeAudioDeviceGroup {
        transport_latency_mtos_us_(0),
        transport_latency_stom_us_(0),
        configuration_context_type_(types::LeAudioContextType::UNINITIALIZED),
        metadata_context_type_(types::LeAudioContextType::UNINITIALIZED),
        metadata_context_type_({.sink = types::AudioContexts(
                                    types::LeAudioContextType::UNINITIALIZED),
                                .source = types::AudioContexts(
                                    types::LeAudioContextType::UNINITIALIZED)}),
        group_available_contexts_(types::LeAudioContextType::UNINITIALIZED),
        pending_group_available_contexts_change_(
            types::LeAudioContextType::UNINITIALIZED),
@@ -296,8 +305,10 @@ class LeAudioDeviceGroup {
  void CigAssignCisConnHandlesToAses(void);
  void CigUnassignCis(LeAudioDevice* leAudioDevice);
  bool Configure(types::LeAudioContextType context_type,
                 types::AudioContexts metadata_context_type,
                 std::vector<uint8_t> ccid_list = {});
                 const types::BidirectionalPair<types::AudioContexts>&
                     metadata_context_types,
                 types::BidirectionalPair<std::vector<uint8_t>> ccid_lists = {
                     .sink = {}, .source = {}});
  uint32_t GetSduInterval(uint8_t direction);
  uint8_t GetSCA(void);
  uint8_t GetPacking(void);
@@ -325,8 +336,9 @@ class LeAudioDeviceGroup {
  std::optional<LeAudioCodecConfiguration> GetCodecConfigurationByDirection(
      types::LeAudioContextType group_context_type, uint8_t direction) const;
  bool IsContextSupported(types::LeAudioContextType group_context_type);
  bool IsMetadataChanged(types::AudioContexts group_context_type,
                         const std::vector<uint8_t>& ccid_list);
  bool IsMetadataChanged(
      const types::BidirectionalPair<types::AudioContexts>& context_types,
      const types::BidirectionalPair<std::vector<uint8_t>>& ccid_lists);
  void CreateStreamVectorForOffloader(uint8_t direction);
  void StreamOffloaderUpdated(uint8_t direction);

@@ -363,7 +375,8 @@ class LeAudioDeviceGroup {
    return configuration_context_type_;
  }

  inline types::AudioContexts GetMetadataContexts(void) const {
  inline types::BidirectionalPair<types::AudioContexts> GetMetadataContexts()
      const {
    return metadata_context_type_;
  }

@@ -386,8 +399,9 @@ class LeAudioDeviceGroup {
  bool ConfigureAses(
      const set_configurations::AudioSetConfiguration* audio_set_conf,
      types::LeAudioContextType context_type,
      types::AudioContexts metadata_context_type,
      const std::vector<uint8_t>& ccid_list);
      const types::BidirectionalPair<types::AudioContexts>&
          metadata_context_types,
      const types::BidirectionalPair<std::vector<uint8_t>>& ccid_lists);
  bool IsConfigurationSupported(
      const set_configurations::AudioSetConfiguration* audio_set_configuration,
      types::LeAudioContextType context_type,
@@ -396,7 +410,7 @@ class LeAudioDeviceGroup {

  /* Current configuration and metadata context types */
  types::LeAudioContextType configuration_context_type_;
  types::AudioContexts metadata_context_type_;
  types::BidirectionalPair<types::AudioContexts> metadata_context_type_;

  /* Mask of contexts that the whole group can handle at it's current state
   * It's being updated each time group members connect, disconnect or their
+31 −17

File changed.

Preview size limit exceeded, changes collapsed.

+102 −85

File changed.

Preview size limit exceeded, changes collapsed.

Loading