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

Commit 655fb1b9 authored by Pavlin Radoslavov's avatar Pavlin Radoslavov
Browse files

Add a mechanism for configuring the A2DP Source codecs

* Codec config internal abstraction:
 - Add new classes A2dpCodecConfig and A2dpCodecs that (will)
   encapsulate all codec-related APIs
 - Add unit tests for the above two classes
 - Add method A2dpCodecConfig.buildCodecConfig(), and use it when
   creating the codec configuration instead of A2DP_InitSource2SinkCodec().
   The new method can build the codec config by taking into account
   optional user codec-related configuration preferences.
 - Use the A2DP codec config API from the hardware/libhardware bt_av.h API
 - Replace enum tA2DP_CODEC_SEP_INDEX with btav_a2dp_codec_index_t
   from the bt_av.h API
 - Move codec-specific functions from stack/include/a2dp_api.h
   and stack/a2dp/a2dp_api.cc to stack/include/a2dp_codec_api.h
   and stack/a2dp/a2dp_codec_config.cc
 - Create a new base class A2dpCodecConfig() to hold some of the
   codec-related state, and implement the corresponding A2dpCodecConfigSbc
   and A2dpCodecConfigSbcSink derived classes.
 - Move A2DP spec-related constants from stack/include/a2dp_api.h
   to stack/include/a2dp_constants.h
 - Move A2DP-related error codes from stack/include/a2dp_api.h
   to stack/include/a2dp_error_codes.h
 - Move A2DP SBC spec-related constants from stack/include/a2dp_sbc.h to
   stack/include/a2dp_sbc_constants.h

* Implement the backend mechanism for handling user (re)configuration of
  A2DP Source codecs as requested via the JNI API calls.
  Also, any codec changes are reported back via JNI API callbacks.
  The current audio parameter selection (sample rate, bits per
  sample, channel mode - mono/stereo) is as follows:
  - If the user selected parameters are acceptable (based on
    local codec capability and the remote Sink capability),
    those parameters are used.
  - Else if the Audio HAL's requested parameters are acceptable,
    those are used.
  - Else if the default settings are acceptable, those are used.
  - Else use the best match among the local and the remote device's
    capabilities.

* Update the mechanism for handling OTA configuration requests from the
  remote Sink device.
  - The OTA prefered codec configuration is ignored if the current
  codec configuration contains explicit user configuration, or if the
  codec configuration for the same codec contains explicit user
  configuration.

* Refactor the Audio HAL <-> Bluetooth audio parameter negotiation
  mechanism:
  The new mechanism gives some flexibility to the Media Framework to
  choose the appropriate audio format (sample rate, bits per sample,
  and channel mode - mono/stereo), and at the same time allows
  the Bluetooth stack to double-check / overwrite the choice.
 - out_get_parameters() on the Audio HAL side asks the Bluetooth stack
   for all currently supported formats (for the current codec),
   and returns them to the Media Framework: sample rate, bits per sample,
   and channel mode (mono/stereo).
 - The first time adev_open_output_stream() is called on the Audio HAL,
   it asks the Bluetooth stack about the audio format currently selected
   by the Bluetooth stack (based on codec negotiation with the Sink device,
   and User Configuration).
 - The second time adev_open_output_stream() is called on the Audio HAL,
   its "config" will eventually contain the audio format selected
   internally by the Media Framework. That audio format is sent to the
   Bluetooth stack.
   If that format is acceptable to the Bluetooth stack, the Bluetooth
   stack will reconfigure itself internally, and will respond back with
   those values. Otherwise, it will respond back with the values that
   should be used instead.

* Misc other fixes and refactoring:
 - Fix the BTA handling of A2DP codec reconfiguration
 - Fix a bug in the implementation of A2DP_BitsSet(), and add the
   approriate unit test. Also, fix the code that was using this function
   incorrectly.
 - The SBC encoder is compiled as a separate library
 - Replace leftover usage of "false" with "FALSE" for macros, and
   vice-versa for variable values.

Test: A2DP streaming to headsets, TestPlans/71390
Bug: 30958229
Change-Id: I440b6126e2250e33b0075f9789dd93154c007c2b
parent 94a2b0c2
Loading
Loading
Loading
Loading
+309 −86
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@
#include <mutex>

#include <hardware/audio.h>
#include <hardware/bt_av.h>
#include <hardware/hardware.h>
#include <system/audio.h>

@@ -326,7 +327,7 @@ static int skt_disconnect(int fd) {
 ****************************************************************************/

static int a2dp_ctrl_receive(struct a2dp_stream_common* common, void* buffer,
                             int length) {
                             size_t length) {
  ssize_t ret;
  int i;

@@ -336,18 +337,18 @@ static int a2dp_ctrl_receive(struct a2dp_stream_common* common, void* buffer,
      break;
    }
    if (ret == 0) {
      ERROR("ack failed: peer closed");
      ERROR("receive control data failed: peer closed");
      break;
    }
    if (errno != EWOULDBLOCK && errno != EAGAIN) {
      ERROR("ack failed: error(%s)", strerror(errno));
      ERROR("receive control data failed: error(%s)", strerror(errno));
      break;
    }
    if (i == (CTRL_CHAN_RETRY_COUNT - 1)) {
      ERROR("ack failed: max retry count");
      ERROR("receive control data failed: max retry count");
      break;
    }
    INFO("ack failed (%s), retrying", strerror(errno));
    INFO("receive control data failed (%s), retrying", strerror(errno));
  }
  if (ret <= 0) {
    skt_disconnect(common->ctrl_fd);
@@ -356,6 +357,48 @@ static int a2dp_ctrl_receive(struct a2dp_stream_common* common, void* buffer,
  return ret;
}

// Sends control info for stream |common|. The data to send is stored in
// |buffer| and has size |length|.
// On success, returns the number of octets sent, otherwise -1.
static int a2dp_ctrl_send(struct a2dp_stream_common* common, const void* buffer,
                          size_t length) {
  ssize_t sent;
  size_t remaining = length;
  int i;

  if (length == 0) return 0;  // Nothing to do

  for (i = 0;; i++) {
    OSI_NO_INTR(sent = send(common->ctrl_fd, buffer, remaining, MSG_NOSIGNAL));
    if (sent == static_cast<ssize_t>(remaining)) {
      remaining = 0;
      break;
    }
    if (sent > 0) {
      buffer = (static_cast<const char*>(buffer) + sent);
      remaining -= sent;
      continue;
    }
    if (sent < 0) {
      if (errno != EWOULDBLOCK && errno != EAGAIN) {
        ERROR("send control data failed: error(%s)", strerror(errno));
        break;
      }
      INFO("send control data failed (%s), retrying", strerror(errno));
    }
    if (i >= (CTRL_CHAN_RETRY_COUNT - 1)) {
      ERROR("send control data failed: max retry count");
      break;
    }
  }
  if (remaining > 0) {
    skt_disconnect(common->ctrl_fd);
    common->ctrl_fd = AUDIO_SKT_DISCONNECTED;
    return -1;
  }
  return length;
}

static int a2dp_command(struct a2dp_stream_common* common, tA2DP_CTRL_CMD cmd) {
  char ack;

@@ -452,73 +495,201 @@ static int a2dp_read_input_audio_config(struct a2dp_stream_common* common) {
  return 0;
}

static int a2dp_read_output_audio_config(struct a2dp_stream_common* common) {
  tA2DP_SAMPLE_RATE sample_rate;
  tA2DP_CHANNEL_COUNT channel_count;
  tA2DP_BITS_PER_SAMPLE bits_per_sample;
static int a2dp_read_output_audio_config(
    struct a2dp_stream_common* common, btav_a2dp_codec_config_t* codec_config,
    btav_a2dp_codec_config_t* codec_capability, bool update_stream_config) {
  struct a2dp_config stream_config;

  if (a2dp_command(common, A2DP_CTRL_GET_OUTPUT_AUDIO_CONFIG) < 0) {
    ERROR("get a2dp output audio config failed");
    return -1;
  }

  if (a2dp_ctrl_receive(common, &sample_rate, sizeof(tA2DP_SAMPLE_RATE)) < 0)
  // Receive the current codec config
  if (a2dp_ctrl_receive(common, &codec_config->sample_rate,
                        sizeof(btav_a2dp_codec_sample_rate_t)) < 0) {
    return -1;
  if (a2dp_ctrl_receive(common, &channel_count, sizeof(tA2DP_CHANNEL_COUNT)) <
      0) {
  }
  if (a2dp_ctrl_receive(common, &codec_config->bits_per_sample,
                        sizeof(btav_a2dp_codec_bits_per_sample_t)) < 0) {
    return -1;
  }
  if (a2dp_ctrl_receive(common, &bits_per_sample,
                        sizeof(tA2DP_BITS_PER_SAMPLE)) < 0) {
  if (a2dp_ctrl_receive(common, &codec_config->channel_mode,
                        sizeof(btav_a2dp_codec_channel_mode_t)) < 0) {
    return -1;
  }

  // Check the sample rate
  switch (sample_rate) {
  // Receive the current codec capability
  if (a2dp_ctrl_receive(common, &codec_capability->sample_rate,
                        sizeof(btav_a2dp_codec_sample_rate_t)) < 0) {
    return -1;
  }
  if (a2dp_ctrl_receive(common, &codec_capability->bits_per_sample,
                        sizeof(btav_a2dp_codec_bits_per_sample_t)) < 0) {
    return -1;
  }
  if (a2dp_ctrl_receive(common, &codec_capability->channel_mode,
                        sizeof(btav_a2dp_codec_channel_mode_t)) < 0) {
    return -1;
  }

  // Check the codec config sample rate
  switch (codec_config->sample_rate) {
    case BTAV_A2DP_CODEC_SAMPLE_RATE_44100:
      stream_config.rate = 44100;
      break;
    case BTAV_A2DP_CODEC_SAMPLE_RATE_48000:
      stream_config.rate = 48000;
      break;
    case BTAV_A2DP_CODEC_SAMPLE_RATE_88200:
      stream_config.rate = 88200;
      break;
    case BTAV_A2DP_CODEC_SAMPLE_RATE_96000:
      stream_config.rate = 96000;
      break;
    case BTAV_A2DP_CODEC_SAMPLE_RATE_176400:
      stream_config.rate = 176400;
      break;
    case BTAV_A2DP_CODEC_SAMPLE_RATE_192000:
      stream_config.rate = 192000;
      break;
    case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE:
    default:
      ERROR("Invalid sample rate: 0x%x", codec_config->sample_rate);
      return -1;
  }

  // Check the codec config bits per sample
  switch (codec_config->bits_per_sample) {
    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16:
      stream_config.format = AUDIO_FORMAT_PCM_16_BIT;
      break;
    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24:
      stream_config.format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
      break;
    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32:
      stream_config.format = AUDIO_FORMAT_PCM_32_BIT;
      break;
    case BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE:
    default:
      ERROR("Invalid bits per sample: 0x%x", codec_config->bits_per_sample);
      return -1;
  }

  // Check the codec config channel mode
  switch (codec_config->channel_mode) {
    case BTAV_A2DP_CODEC_CHANNEL_MODE_MONO:
      stream_config.channel_mask = AUDIO_CHANNEL_OUT_MONO;
      break;
    case BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO:
      stream_config.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
      break;
    case BTAV_A2DP_CODEC_CHANNEL_MODE_NONE:
    default:
      ERROR("Invalid channel mode: 0x%x", codec_config->channel_mode);
      return -1;
  }

  // Update the output stream configuration
  if (update_stream_config) {
    common->cfg.rate = stream_config.rate;
    common->cfg.channel_mask = stream_config.channel_mask;
    common->cfg.format = stream_config.format;
  }

  INFO(
      "got output codec capability: sample_rate=0x%x bits_per_sample=0x%x "
      "channel_mode=0x%x",
      codec_capability->sample_rate, codec_capability->bits_per_sample,
      codec_capability->channel_mode);

  return 0;
}

static int a2dp_write_output_audio_config(struct a2dp_stream_common* common) {
  btav_a2dp_codec_config_t codec_config;

  if (a2dp_command(common, A2DP_CTRL_SET_OUTPUT_AUDIO_CONFIG) < 0) {
    ERROR("set a2dp output audio config failed");
    return -1;
  }

  codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_NONE;
  codec_config.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_NONE;
  codec_config.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_NONE;

  switch (common->cfg.rate) {
    case 44100:
      codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_44100;
      break;
    case 48000:
      codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_48000;
      break;
    case 88200:
      codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_88200;
      break;
    case 96000:
      common->cfg.rate = sample_rate;
      codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_96000;
      break;
    case 176400:
      codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_176400;
      break;
    case 192000:
      codec_config.sample_rate = BTAV_A2DP_CODEC_SAMPLE_RATE_192000;
      break;
    default:
      ERROR("Invalid sample rate: %" PRIu32, sample_rate);
      ERROR("Invalid sample rate: %" PRIu32, common->cfg.rate);
      return -1;
  }

  // Check the channel count
  switch (channel_count) {
    case 1:
      common->cfg.channel_mask = AUDIO_CHANNEL_OUT_MONO;
  switch (common->cfg.format) {
    case AUDIO_FORMAT_PCM_16_BIT:
      codec_config.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16;
      break;
    case 2:
      common->cfg.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
    case AUDIO_FORMAT_PCM_24_BIT_PACKED:
      codec_config.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24;
      break;
    case AUDIO_FORMAT_PCM_32_BIT:
      codec_config.bits_per_sample = BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32;
      break;
    case AUDIO_FORMAT_PCM_8_24_BIT:
    // FALLTHROUGH
    // All 24-bit audio is expected in AUDIO_FORMAT_PCM_24_BIT_PACKED format
    default:
      ERROR("Invalid channel count: %" PRIu8, channel_count);
      ERROR("Invalid audio format: 0x%x", common->cfg.format);
      return -1;
  }

  // Check the bits per sample
  switch (bits_per_sample) {
    case 16:
      common->cfg.format = AUDIO_FORMAT_PCM_16_BIT;
      break;
    case 24:
      common->cfg.format = AUDIO_FORMAT_PCM_24_BIT_PACKED;
  switch (common->cfg.channel_mask) {
    case AUDIO_CHANNEL_OUT_MONO:
      codec_config.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_MONO;
      break;
    case 32:
      common->cfg.format = AUDIO_FORMAT_PCM_32_BIT;
    case AUDIO_CHANNEL_OUT_STEREO:
      codec_config.channel_mode = BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO;
      break;
    default:
      ERROR("Invalid bits per sample: %" PRIu8, bits_per_sample);
      ERROR("Invalid channel mask: 0x%x", common->cfg.channel_mask);
      return -1;
  }

  // Send the current codec config that has been selected by us
  if (a2dp_ctrl_send(common, &codec_config.sample_rate,
                     sizeof(btav_a2dp_codec_sample_rate_t)) < 0)
    return -1;
  if (a2dp_ctrl_send(common, &codec_config.bits_per_sample,
                     sizeof(btav_a2dp_codec_bits_per_sample_t)) < 0) {
    return -1;
  }
  if (a2dp_ctrl_send(common, &codec_config.channel_mode,
                     sizeof(btav_a2dp_codec_channel_mode_t)) < 0) {
    return -1;
  }

  INFO(
      "got output audio config: sample_rate=%u channel_count=%u "
      "bits_per_sample=%u",
      sample_rate, channel_count, bits_per_sample);
      "sent output codec config: sample_rate=0x%x bits_per_sample=0x%x "
      "channel_mode=0x%x",
      codec_config.sample_rate, codec_config.bits_per_sample,
      codec_config.channel_mode);

  return 0;
}
@@ -826,6 +997,9 @@ static char* out_get_parameters(const struct audio_stream* stream,
                                const char* keys) {
  FNLOG();

  btav_a2dp_codec_config_t codec_config;
  btav_a2dp_codec_config_t codec_capability;

  struct a2dp_stream_out* out = (struct a2dp_stream_out*)stream;

  std::unordered_map<std::string, std::string> params =
@@ -836,7 +1010,9 @@ static char* out_get_parameters(const struct audio_stream* stream,

  std::lock_guard<std::recursive_mutex> lock(*out->common.mutex);

  if (a2dp_read_output_audio_config(&out->common) < 0) {
  if (a2dp_read_output_audio_config(&out->common, &codec_config,
                                    &codec_capability,
                                    false /* update_stream_config */) < 0) {
    ERROR("a2dp_read_output_audio_config failed");
    goto done;
  }
@@ -844,63 +1020,80 @@ static char* out_get_parameters(const struct audio_stream* stream,
  // Add the format
  if (params.find(AUDIO_PARAMETER_STREAM_SUP_FORMATS) != params.end()) {
    std::string param;
    switch (out->common.cfg.format) {
      case AUDIO_FORMAT_PCM_16_BIT:
        param = "AUDIO_FORMAT_PCM_16_BIT";
        break;
      case AUDIO_FORMAT_PCM_24_BIT_PACKED:
        param = "AUDIO_FORMAT_PCM_24_BIT_PACKED";
        break;
      case AUDIO_FORMAT_PCM_8_24_BIT:
        param = "AUDIO_FORMAT_PCM_8_24_BIT";
        break;
      case AUDIO_FORMAT_PCM_32_BIT:
        param = "AUDIO_FORMAT_PCM_32_BIT";
        break;
      default:
        ERROR("Invalid audio format: 0x%x", out->common.cfg.format);
        break;
    if (codec_capability.bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_16) {
      if (!param.empty()) param += "|";
      param += "AUDIO_FORMAT_PCM_16_BIT";
    }
    if (codec_capability.bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_24) {
      if (!param.empty()) param += "|";
      param += "AUDIO_FORMAT_PCM_24_BIT_PACKED";
    }
    if (!param.empty()) {
    if (codec_capability.bits_per_sample & BTAV_A2DP_CODEC_BITS_PER_SAMPLE_32) {
      if (!param.empty()) param += "|";
      param += "AUDIO_FORMAT_PCM_32_BIT";
    }
    if (param.empty()) {
      ERROR("Invalid codec capability bits_per_sample=0x%x",
            codec_capability.bits_per_sample);
      goto done;
    } else {
      return_params[AUDIO_PARAMETER_STREAM_SUP_FORMATS] = param;
    }
  }

  // Add the channel mask
  if (params.find(AUDIO_PARAMETER_STREAM_SUP_CHANNELS) != params.end()) {
  // Add the sample rate
  if (params.find(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES) != params.end()) {
    std::string param;
    switch (out->common.cfg.channel_mask) {
      case AUDIO_CHANNEL_OUT_MONO:
        param = "AUDIO_CHANNEL_OUT_MONO";
        break;
      case AUDIO_CHANNEL_OUT_STEREO:
        param = "AUDIO_CHANNEL_OUT_STEREO";
        break;
      default:
        ERROR("Invalid channel mask: 0x%x", out->common.cfg.channel_mask);
        break;
    if (codec_capability.sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_44100) {
      if (!param.empty()) param += "|";
      param += "44100";
    }
    if (!param.empty()) {
      return_params[AUDIO_PARAMETER_STREAM_SUP_CHANNELS] = param;
    if (codec_capability.sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_48000) {
      if (!param.empty()) param += "|";
      param += "48000";
    }
    if (codec_capability.sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_88200) {
      if (!param.empty()) param += "|";
      param += "88200";
    }
    if (codec_capability.sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_96000) {
      if (!param.empty()) param += "|";
      param += "96000";
    }
    if (codec_capability.sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_176400) {
      if (!param.empty()) param += "|";
      param += "176400";
    }
    if (codec_capability.sample_rate & BTAV_A2DP_CODEC_SAMPLE_RATE_192000) {
      if (!param.empty()) param += "|";
      param += "192000";
    }
    if (param.empty()) {
      ERROR("Invalid codec capability sample_rate=0x%x",
            codec_capability.sample_rate);
      goto done;
    } else {
      return_params[AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES] = param;
    }
  }

  // Add the sample rate
  if (params.find(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES) != params.end()) {
  // Add the channel mask
  if (params.find(AUDIO_PARAMETER_STREAM_SUP_CHANNELS) != params.end()) {
    std::string param;
    switch (out->common.cfg.rate) {
      case 44100:
      case 48000:
      case 88200:
      case 96000:
        param = std::to_string(out->common.cfg.rate);
        break;
      default:
        ERROR("Invalid sample rate: %d", out->common.cfg.rate);
        break;
    if (codec_capability.channel_mode & BTAV_A2DP_CODEC_CHANNEL_MODE_MONO) {
      if (!param.empty()) param += "|";
      param += "AUDIO_CHANNEL_OUT_MONO";
    }
    if (!param.empty()) {
      return_params[AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES] = param;
    if (codec_capability.channel_mode & BTAV_A2DP_CODEC_CHANNEL_MODE_STEREO) {
      if (!param.empty()) param += "|";
      param += "AUDIO_CHANNEL_OUT_STEREO";
    }
    if (param.empty()) {
      ERROR("Invalid codec capability channel_mode=0x%x",
            codec_capability.channel_mode);
      goto done;
    } else {
      return_params[AUDIO_PARAMETER_STREAM_SUP_CHANNELS] = param;
    }
  }

@@ -1187,19 +1380,49 @@ static int adev_open_output_stream(struct audio_hw_device* dev,
  /* initialize a2dp specifics */
  a2dp_stream_common_init(&out->common);

  if (a2dp_read_output_audio_config(&out->common) < 0) {
  // Make sure we always have the feeding parameters configured
  btav_a2dp_codec_config_t codec_config;
  btav_a2dp_codec_config_t codec_capability;
  if (a2dp_read_output_audio_config(&out->common, &codec_config,
                                    &codec_capability,
                                    true /* update_stream_config */) < 0) {
    ERROR("a2dp_read_output_audio_config failed");
    ret = -1;
    goto err_open;
  }

  /* set output config values */
  if (config) {
  if (config != nullptr) {
    // Try to use the config parameters and send it to the remote side
    // TODO: Shall we use out_set_format() and similar?
    if (config->format != 0) out->common.cfg.format = config->format;
    if (config->sample_rate != 0) out->common.cfg.rate = config->sample_rate;
    if (config->channel_mask != 0)
      out->common.cfg.channel_mask = config->channel_mask;
    if ((out->common.cfg.format != 0) || (out->common.cfg.rate != 0) ||
        (out->common.cfg.channel_mask != 0)) {
      if (a2dp_write_output_audio_config(&out->common) < 0) {
        ERROR("a2dp_write_output_audio_config failed");
        ret = -1;
        goto err_open;
      }
      // Read again and make sure we use the same parameters as the remote side
      if (a2dp_read_output_audio_config(&out->common, &codec_config,
                                        &codec_capability,
                                        true /* update_stream_config */) < 0) {
        ERROR("a2dp_read_output_audio_config failed");
        ret = -1;
        goto err_open;
      }
    }
    config->format = out_get_format((const struct audio_stream*)&out->stream);
    config->sample_rate =
        out_get_sample_rate((const struct audio_stream*)&out->stream);
    config->channel_mask =
        out_get_channels((const struct audio_stream*)&out->stream);

    INFO("Output stream config: format=0x%x sample_rate=%d channel_mask=0x%x",
         config->format, config->sample_rate, config->channel_mask);
  }
  *stream_out = &out->stream;
  a2dp_dev->output = out;
+1 −0
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ typedef enum {
  A2DP_CTRL_CMD_SUSPEND,
  A2DP_CTRL_GET_INPUT_AUDIO_CONFIG,
  A2DP_CTRL_GET_OUTPUT_AUDIO_CONFIG,
  A2DP_CTRL_SET_OUTPUT_AUDIO_CONFIG,
  A2DP_CTRL_CMD_OFFLOAD_START,
} tA2DP_CTRL_CMD;

+1 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ const char* audio_a2dp_hw_dump_ctrl_event(tA2DP_CTRL_CMD event) {
    CASE_RETURN_STR(A2DP_CTRL_CMD_SUSPEND)
    CASE_RETURN_STR(A2DP_CTRL_GET_INPUT_AUDIO_CONFIG)
    CASE_RETURN_STR(A2DP_CTRL_GET_OUTPUT_AUDIO_CONFIG)
    CASE_RETURN_STR(A2DP_CTRL_SET_OUTPUT_AUDIO_CONFIG)
    CASE_RETURN_STR(A2DP_CTRL_CMD_OFFLOAD_START)
    default:
      break;
+41 −20
Original line number Diff line number Diff line
@@ -248,7 +248,7 @@ tAVDT_CTRL_CBACK* const bta_av_dt_cback[] = {bta_av_stream0_cback,
 * Returns          void
 **********************************************/
static uint8_t bta_av_get_scb_handle(tBTA_AV_SCB* p_scb, uint8_t local_sep) {
  for (int i = 0; i < A2DP_CODEC_SEP_INDEX_MAX; i++) {
  for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
    if ((p_scb->seps[i].tsep == local_sep) &&
        A2DP_CodecTypeEquals(p_scb->seps[i].codec_info,
                             p_scb->cfg.codec_info)) {
@@ -270,7 +270,7 @@ static uint8_t bta_av_get_scb_handle(tBTA_AV_SCB* p_scb, uint8_t local_sep) {
 **********************************************/
static uint8_t bta_av_get_scb_sep_type(tBTA_AV_SCB* p_scb,
                                       uint8_t tavdt_handle) {
  for (int i = 0; i < A2DP_CODEC_SEP_INDEX_MAX; i++) {
  for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
    if (p_scb->seps[i].av_handle == tavdt_handle) return (p_scb->seps[i].tsep);
  }
  APPL_TRACE_DEBUG("%s: handle %d not found", __func__, tavdt_handle)
@@ -731,7 +731,7 @@ static void bta_av_a2dp_sdp_cback(bool found, tA2DP_Service* p_service) {
static void bta_av_adjust_seps_idx(tBTA_AV_SCB* p_scb, uint8_t avdt_handle) {
  APPL_TRACE_DEBUG("%s: codec: %s", __func__,
                   A2DP_CodecName(p_scb->cfg.codec_info));
  for (int i = 0; i < A2DP_CODEC_SEP_INDEX_MAX; i++) {
  for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
    APPL_TRACE_DEBUG("%s: av_handle: %d codec: %s", __func__,
                     p_scb->seps[i].av_handle,
                     A2DP_CodecName(p_scb->seps[i].codec_info));
@@ -1041,7 +1041,7 @@ void bta_av_cleanup(tBTA_AV_SCB* p_scb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
  p_scb->skip_sdp = false;
  if (p_scb->deregistring) {
    /* remove stream */
    for (int i = 0; i < A2DP_CODEC_SEP_INDEX_MAX; i++) {
    for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) {
      if (p_scb->seps[i].av_handle) AVDT_RemoveStream(p_scb->seps[i].av_handle);
      p_scb->seps[i].av_handle = 0;
    }
@@ -1923,7 +1923,7 @@ void bta_av_str_stopped(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
  BT_HDR* p_buf;
  uint8_t policy = HCI_ENABLE_SNIFF_MODE;

  APPL_TRACE_ERROR("%s: audio_open_cnt=%d, p_data %x", __func__,
  APPL_TRACE_ERROR("%s: audio_open_cnt=%d, p_data %p", __func__,
                   bta_av_cb.audio_open_cnt, p_data);

  bta_sys_idle(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->peer_addr);
@@ -1985,8 +1985,11 @@ void bta_av_str_stopped(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
    suspend_rsp.initiator = true;
    APPL_TRACE_EVENT("%s: status %d", __func__, suspend_rsp.status);

    /* send STOP_EVT event only if not in reconfiguring state */
    if (p_scb->state != BTA_AV_RCFG_SST) {
    // Send STOP_EVT event only if not in reconfiguring state.
    // However, we should send STOP_EVT if we are reconfiguring when taking
    // the Close->Configure->Open->Start path.
    if (p_scb->state != BTA_AV_RCFG_SST ||
        (p_data && p_data->api_stop.reconfig_stop)) {
      (*bta_av_cb.p_cback)(BTA_AV_STOP_EVT, (tBTA_AV*)&suspend_rsp);
    }
  }
@@ -2026,34 +2029,51 @@ void bta_av_reconfig(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
  p_scb->rcfg_idx = p_rcfg->sep_info_idx;
  p_scb->p_cap->psc_mask = p_scb->cur_psc_mask;

  /* if the requested index differs from the current one, we can only close/open
   */
  if ((p_scb->rcfg_idx == p_scb->sep_info_idx) && (p_rcfg->suspend) &&
      (p_scb->recfg_sup) && (p_scb->suspend_sup)) {
  // If the requested SEP index is same as the current one, then we
  // can Suspend->Reconfigure->Start.
  // Otherwise, we have to Close->Configure->Open->Start or
  // Close->Configure->Open for streams that are / are not started.
  if ((p_scb->rcfg_idx == p_scb->sep_info_idx) && p_rcfg->suspend &&
      p_scb->recfg_sup && p_scb->suspend_sup) {
    if (p_scb->started) {
      // Suspend->Reconfigure->Start
      stop.flush = false;
      stop.suspend = true;
      stop.reconfig_stop = false;
      bta_av_str_stopped(p_scb, (tBTA_AV_DATA*)&stop);
    } else {
      // Reconfigure
      APPL_TRACE_DEBUG("%s: reconfig", __func__);
      AVDT_ReconfigReq(p_scb->avdt_handle, p_scb->p_cap);
      p_scb->p_cap->psc_mask = p_scb->cur_psc_mask;
    }
  } else {
    /* close the stream */
    APPL_TRACE_DEBUG("%s: close/open num_protect: %d", __func__,
    // Close the stream first, and then Configure it
    APPL_TRACE_DEBUG("%s: Close/Open started: %d state: %d num_protect: %d",
                     __func__, p_scb->started, p_scb->state,
                     p_cfg->num_protect);
    if (p_scb->started) {
      // Close->Configure->Open->Start
      if ((p_scb->rcfg_idx != p_scb->sep_info_idx) && p_scb->recfg_sup) {
        // Make sure we trigger STOP_EVT when taking the longer road to
        // reconfiguration, otherwise we don't call Start.
        stop.flush = false;
        stop.suspend = false;
        stop.reconfig_stop = true;
        bta_av_str_stopped(p_scb, (tBTA_AV_DATA*)&stop);
      } else {
        bta_av_str_stopped(p_scb, NULL);
      }
      p_scb->started = false;

      /* drop the buffers queued in L2CAP */
    } else {
      // Close->Configure->Open
      bta_av_str_stopped(p_scb, NULL);
    }
    // Drop the buffers queued in L2CAP
    L2CA_FlushChannel(p_scb->l2c_cid, L2CAP_FLUSH_CHANS_ALL);

    AVDT_CloseReq(p_scb->avdt_handle);
  }
}
}

/*******************************************************************************
 *
@@ -2144,7 +2164,6 @@ void bta_av_data_path(tBTA_AV_SCB* p_scb, UNUSED_ATTR tBTA_AV_DATA* p_data) {
 ******************************************************************************/
void bta_av_start_ok(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
  tBTA_AV_START start;
  tBTA_AV_API_STOP stop;
  bool initiator = false;
  bool suspend = false;
  uint16_t flush_to;
@@ -2290,6 +2309,7 @@ void bta_av_start_ok(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
    (*bta_av_cb.p_cback)(BTA_AV_START_EVT, (tBTA_AV*)&start);

    if (suspend) {
      tBTA_AV_API_STOP stop;
      p_scb->role |= BTA_AV_ROLE_SUSPEND;
      p_scb->cong = true; /* do not allow the media data to go through */
      /* do not duplicate the media packets to this channel */
@@ -2297,6 +2317,7 @@ void bta_av_start_ok(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
      p_scb->co_started = false;
      stop.flush = false;
      stop.suspend = true;
      stop.reconfig_stop = false;
      bta_av_ssm_execute(p_scb, BTA_AV_AP_STOP_EVT, (tBTA_AV_DATA*)&stop);
    }
  }
+1 −0
Original line number Diff line number Diff line
@@ -277,6 +277,7 @@ void BTA_AvStop(bool suspend) {
  p_buf->hdr.event = BTA_AV_API_STOP_EVT;
  p_buf->flush = true;
  p_buf->suspend = suspend;
  p_buf->reconfig_stop = false;

  bta_sys_sendmsg(p_buf);
}
Loading