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

Commit c04978f3 authored by Pavlin Radoslavov's avatar Pavlin Radoslavov
Browse files

Add a mechanism for Audio Feeding Parameters negotiation

Previously, the Audio Feeding Parameters between the Media Framework
and A2DP were hard-coded: 44.1 KHz sample rate, 16-bits per sample, Stereo.
Now the Media Framework queries A2DP, and uses the returned values.

Bug: b/30958229
Test: Manual testing: A2DP streaming to headphones. TestTracker/68727
Change-Id: I70b90d2961ceb9efcd7021d2e12c240fe531ee1c
parent 09bc5b47
Loading
Loading
Loading
Loading
+238 −39
Original line number Original line Diff line number Diff line
@@ -93,7 +93,7 @@ struct a2dp_audio_device {


struct a2dp_config {
struct a2dp_config {
    uint32_t                rate;
    uint32_t                rate;
    uint32_t                channel_flags;
    uint32_t                channel_mask;
    int                     format;
    int                     format;
};
};


@@ -170,14 +170,36 @@ static void ts_log(UNUSED_ATTR const char *tag, UNUSED_ATTR int val, struct time
    }
    }
}
}


static int calc_audiotime(struct a2dp_config cfg, int bytes)
static int calc_audiotime_usec(struct a2dp_config cfg, int bytes)
{
{
    int chan_count = popcount(cfg.channel_flags);
    int chan_count = audio_channel_count_from_out_mask(cfg.channel_mask);
    int bytes_per_sample;


    ASSERTC(cfg.format == AUDIO_FORMAT_PCM_16_BIT,
    switch (cfg.format) {
            "unsupported sample sz", cfg.format);
    case AUDIO_FORMAT_PCM_8_BIT:
      bytes_per_sample = 1;
      break;
    case AUDIO_FORMAT_PCM_16_BIT:
      bytes_per_sample = 2;
      break;
    case AUDIO_FORMAT_PCM_24_BIT_PACKED:
      bytes_per_sample = 3;
      break;
    case AUDIO_FORMAT_PCM_8_24_BIT:
      bytes_per_sample = 4;
      break;
    case AUDIO_FORMAT_PCM_32_BIT:
      bytes_per_sample = 4;
      break;
    default:
      ASSERTC(false, "unsupported sample format", cfg.format);
      bytes_per_sample = 2;
      break;
    }


    return (int)(((int64_t)bytes * (1000000 / (chan_count * 2))) / cfg.rate);
    return (int)(((int64_t)bytes *
                  (USEC_PER_SEC / (chan_count * bytes_per_sample))) /
                 cfg.rate);
}
}


/*****************************************************************************
/*****************************************************************************
@@ -396,27 +418,121 @@ static int check_a2dp_ready(struct a2dp_stream_common *common)
    return 0;
    return 0;
}
}


static int a2dp_read_audio_config(struct a2dp_stream_common *common)
static int a2dp_read_input_audio_config(struct a2dp_stream_common *common)
{
{
    uint32_t sample_rate;
    tA2DP_SAMPLE_RATE sample_rate;
    uint8_t channel_count;
    tA2DP_CHANNEL_COUNT channel_count;


    if (a2dp_command(common, A2DP_CTRL_GET_AUDIO_CONFIG) < 0)
    if (a2dp_command(common, A2DP_CTRL_GET_INPUT_AUDIO_CONFIG) < 0)
    {
    {
        ERROR("check a2dp ready failed");
        ERROR("get a2dp input audio config failed");
        return -1;
        return -1;
    }
    }


    if (a2dp_ctrl_receive(common, &sample_rate, 4) < 0)
    if (a2dp_ctrl_receive(common, &sample_rate, sizeof(tA2DP_SAMPLE_RATE)) < 0)
        return -1;
        return -1;
    if (a2dp_ctrl_receive(common, &channel_count, 1) < 0)
    if (a2dp_ctrl_receive(common, &channel_count,
                          sizeof(tA2DP_CHANNEL_COUNT)) < 0) {
        return -1;
        return -1;
    }


    common->cfg.channel_flags = (channel_count == 1 ? AUDIO_CHANNEL_IN_MONO : AUDIO_CHANNEL_IN_STEREO);
    switch (sample_rate) {
    common->cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
    case 44100:
    case 48000:
      common->cfg.rate = sample_rate;
      common->cfg.rate = sample_rate;
      break;
    default:
      ERROR("Invalid sample rate: %" PRIu32, sample_rate);
      return -1;
    }


    INFO("got config %d %d", common->cfg.format, common->cfg.rate);
    switch (channel_count) {
    case 1:
      common->cfg.channel_mask = AUDIO_CHANNEL_IN_MONO;
      break;
    case 2:
      common->cfg.channel_mask = AUDIO_CHANNEL_IN_STEREO;
      break;
    default:
      ERROR("Invalid channel count: %" PRIu32, channel_count);
      return -1;
    }

    // TODO: For now input audio format is always hard-coded as PCM 16-bit
    common->cfg.format = AUDIO_FORMAT_PCM_16_BIT;

    INFO("got input audio config %d %d", common->cfg.format, common->cfg.rate);

    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;

    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)
        return -1;
    if (a2dp_ctrl_receive(common, &channel_count,
                          sizeof(tA2DP_CHANNEL_COUNT)) < 0) {
        return -1;
    }
    if (a2dp_ctrl_receive(common, &bits_per_sample,
                          sizeof(tA2DP_BITS_PER_SAMPLE)) < 0) {
        return -1;
    }

    // Check the sample rate
    switch (sample_rate) {
    case 44100:
    case 48000:
    case 88200:
    case 96000:
      common->cfg.rate = sample_rate;
      break;
    default:
      ERROR("Invalid sample rate: %" PRIu32, sample_rate);
      return -1;
    }

    // Check the channel count
    switch (channel_count) {
    case 1:
      common->cfg.channel_mask = AUDIO_CHANNEL_OUT_MONO;
      break;
    case 2:
      common->cfg.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
      break;
    default:
      ERROR("Invalid channel count: %" PRIu8, channel_count);
      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;
      break;
    case 32:
      common->cfg.format = AUDIO_FORMAT_PCM_32_BIT;
      break;
    default:
      ERROR("Invalid bits per sample: %" PRIu8, bits_per_sample);
      return -1;
    }

    INFO("got output audio config: sample_rate=%u channel_count=%u "
         "bits_per_sample=%u", sample_rate, channel_count, bits_per_sample);


    return 0;
    return 0;
}
}
@@ -617,7 +733,7 @@ finish: ;


    // If send didn't work out, sleep to emulate write delay.
    // If send didn't work out, sleep to emulate write delay.
    if (sent == -1) {
    if (sent == -1) {
        const int us_delay = calc_audiotime(out->common.cfg, bytes);
        const int us_delay = calc_audiotime_usec(out->common.cfg, bytes);
        DEBUG("emulate a2dp write delay (%d us)", us_delay);
        DEBUG("emulate a2dp write delay (%d us)", us_delay);
        usleep(us_delay);
        usleep(us_delay);
    }
    }
@@ -639,12 +755,6 @@ static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)


    DEBUG("out_set_sample_rate : %" PRIu32, rate);
    DEBUG("out_set_sample_rate : %" PRIu32, rate);


    if (rate != AUDIO_STREAM_DEFAULT_RATE)
    {
        ERROR("only rate %d supported", AUDIO_STREAM_DEFAULT_RATE);
        return -1;
    }

    out->common.cfg.rate = rate;
    out->common.cfg.rate = rate;


    return 0;
    return 0;
@@ -669,9 +779,9 @@ static uint32_t out_get_channels(const struct audio_stream *stream)
{
{
    struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream;
    struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream;


    DEBUG("channels 0x%" PRIx32, out->common.cfg.channel_flags);
    DEBUG("channels 0x%" PRIx32, out->common.cfg.channel_mask);


    return out->common.cfg.channel_flags;
    return out->common.cfg.channel_mask;
}
}


static audio_format_t out_get_format(const struct audio_stream *stream)
static audio_format_t out_get_format(const struct audio_stream *stream)
@@ -716,7 +826,7 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
{
{
    struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream;
    struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream;


    INFO("state %d", out->common.state);
    INFO("state %d kvpairs %s", out->common.state, kvpairs);


    std::unordered_map<std::string, std::string> params =
    std::unordered_map<std::string, std::string> params =
            hash_map_utils_new_from_string_params(kvpairs);
            hash_map_utils_new_from_string_params(kvpairs);
@@ -756,14 +866,101 @@ static int out_set_parameters(struct audio_stream *stream, const char *kvpairs)
    return status;
    return status;
}
}


static char * out_get_parameters(UNUSED_ATTR const struct audio_stream *stream,
static char * out_get_parameters(const struct audio_stream *stream,
                                 UNUSED_ATTR const char *keys)
                                 const char *keys)
{
{
    FNLOG();
    FNLOG();


    /* add populating param here */
    struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream;

    std::unordered_map<std::string, std::string> params =
            hash_map_utils_new_from_string_params(keys);
    std::unordered_map<std::string, std::string> return_params;


    if (params.empty())
      return strdup("");
      return strdup("");

    pthread_mutex_lock(&out->common.lock);

    if (a2dp_read_output_audio_config(&out->common) < 0) {
        ERROR("a2dp_read_output_audio_config failed");
        goto done;
    }

    // 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 (!param.empty()) {
        return_params[AUDIO_PARAMETER_STREAM_SUP_FORMATS] = param;
      }
    }

    // Add the channel mask
    if (params.find(AUDIO_PARAMETER_STREAM_SUP_CHANNELS) != 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 (!param.empty()) {
        return_params[AUDIO_PARAMETER_STREAM_SUP_CHANNELS] = param;
      }
    }

    // Add the sample rate
    if (params.find(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES) != 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 (!param.empty()) {
        return_params[AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES] = param;
      }
    }

done:
    pthread_mutex_unlock(&out->common.lock);

    std::string result;
    for (const auto& ptr : return_params) {
      result += ptr.first + "=" + ptr.second + ";";
    }

    INFO("get parameters result = %s", result.c_str());

    return strdup(result.c_str());
}
}


static uint32_t out_get_latency(const struct audio_stream_out *stream)
static uint32_t out_get_latency(const struct audio_stream_out *stream)
@@ -884,7 +1081,7 @@ static uint32_t in_get_channels(const struct audio_stream *stream)
    struct a2dp_stream_in *in = (struct a2dp_stream_in *)stream;
    struct a2dp_stream_in *in = (struct a2dp_stream_in *)stream;


    FNLOG();
    FNLOG();
    return in->common.cfg.channel_flags;
    return in->common.cfg.channel_mask;
}
}


static audio_format_t in_get_format(UNUSED_ATTR const struct audio_stream *stream)
static audio_format_t in_get_format(UNUSED_ATTR const struct audio_stream *stream)
@@ -1001,7 +1198,7 @@ static ssize_t in_read(struct audio_stream_in *stream, void* buffer,
error:
error:
    pthread_mutex_unlock(&in->common.lock);
    pthread_mutex_unlock(&in->common.lock);
    memset(buffer, 0, bytes);
    memset(buffer, 0, bytes);
    us_delay = calc_audiotime(in->common.cfg, bytes);
    us_delay = calc_audiotime_usec(in->common.cfg, bytes);
    DEBUG("emulate a2dp read delay (%d us)", us_delay);
    DEBUG("emulate a2dp read delay (%d us)", us_delay);


    usleep(us_delay);
    usleep(us_delay);
@@ -1072,9 +1269,11 @@ static int adev_open_output_stream(struct audio_hw_device *dev,
    /* initialize a2dp specifics */
    /* initialize a2dp specifics */
    a2dp_stream_common_init(&out->common);
    a2dp_stream_common_init(&out->common);


    out->common.cfg.channel_flags = AUDIO_STREAM_DEFAULT_CHANNEL_FLAG;
    if (a2dp_read_output_audio_config(&out->common) < 0) {
    out->common.cfg.format = AUDIO_STREAM_DEFAULT_FORMAT;
        ERROR("a2dp_read_output_audio_config failed");
    out->common.cfg.rate = AUDIO_STREAM_DEFAULT_RATE;
        ret = -1;
        goto err_open;
    }


   /* set output config values */
   /* set output config values */
   if (config)
   if (config)
@@ -1265,8 +1464,8 @@ static int adev_open_input_stream(struct audio_hw_device *dev,
        goto err_open;
        goto err_open;
    }
    }


    if (a2dp_read_audio_config(&in->common) < 0) {
    if (a2dp_read_input_audio_config(&in->common) < 0) {
        ERROR("a2dp_read_audio_config failed (%s)", strerror(errno));
        ERROR("a2dp_read_input_audio_config failed (%s)", strerror(errno));
        ret = -1;
        ret = -1;
        goto err_open;
        goto err_open;
    }
    }
+7 −5
Original line number Original line Diff line number Diff line
@@ -27,6 +27,8 @@
#ifndef AUDIO_A2DP_HW_H
#ifndef AUDIO_A2DP_HW_H
#define AUDIO_A2DP_HW_H
#define AUDIO_A2DP_HW_H


#include <stdint.h>

/*****************************************************************************
/*****************************************************************************
 *  Constants & Macros
 *  Constants & Macros
 *****************************************************************************/
 *****************************************************************************/
@@ -35,10 +37,6 @@
#define A2DP_CTRL_PATH "/data/misc/bluedroid/.a2dp_ctrl"
#define A2DP_CTRL_PATH "/data/misc/bluedroid/.a2dp_ctrl"
#define A2DP_DATA_PATH "/data/misc/bluedroid/.a2dp_data"
#define A2DP_DATA_PATH "/data/misc/bluedroid/.a2dp_data"


#define AUDIO_STREAM_DEFAULT_RATE          44100
#define AUDIO_STREAM_DEFAULT_FORMAT        AUDIO_FORMAT_PCM_16_BIT
#define AUDIO_STREAM_DEFAULT_CHANNEL_FLAG  AUDIO_CHANNEL_OUT_STEREO

// AUDIO_STREAM_OUTPUT_BUFFER_SZ controls the size of the audio socket buffer.
// AUDIO_STREAM_OUTPUT_BUFFER_SZ controls the size of the audio socket buffer.
// If one assumes the write buffer is always full during normal BT playback,
// If one assumes the write buffer is always full during normal BT playback,
// then increasing this value increases our playback latency.
// then increasing this value increases our playback latency.
@@ -83,7 +81,8 @@ typedef enum {
    A2DP_CTRL_CMD_START,
    A2DP_CTRL_CMD_START,
    A2DP_CTRL_CMD_STOP,
    A2DP_CTRL_CMD_STOP,
    A2DP_CTRL_CMD_SUSPEND,
    A2DP_CTRL_CMD_SUSPEND,
    A2DP_CTRL_GET_AUDIO_CONFIG,
    A2DP_CTRL_GET_INPUT_AUDIO_CONFIG,
    A2DP_CTRL_GET_OUTPUT_AUDIO_CONFIG,
    A2DP_CTRL_CMD_OFFLOAD_START,
    A2DP_CTRL_CMD_OFFLOAD_START,
} tA2DP_CTRL_CMD;
} tA2DP_CTRL_CMD;


@@ -94,6 +93,9 @@ typedef enum {
    A2DP_CTRL_ACK_UNSUPPORTED
    A2DP_CTRL_ACK_UNSUPPORTED
} tA2DP_CTRL_ACK;
} tA2DP_CTRL_ACK;


typedef uint32_t tA2DP_SAMPLE_RATE;
typedef uint8_t tA2DP_CHANNEL_COUNT;
typedef uint8_t tA2DP_BITS_PER_SAMPLE;


/*****************************************************************************
/*****************************************************************************
 *  Type definitions for callback functions
 *  Type definitions for callback functions
+2 −1
Original line number Original line Diff line number Diff line
@@ -28,7 +28,8 @@ const char* audio_a2dp_hw_dump_ctrl_event(tA2DP_CTRL_CMD event)
        CASE_RETURN_STR(A2DP_CTRL_CMD_START)
        CASE_RETURN_STR(A2DP_CTRL_CMD_START)
        CASE_RETURN_STR(A2DP_CTRL_CMD_STOP)
        CASE_RETURN_STR(A2DP_CTRL_CMD_STOP)
        CASE_RETURN_STR(A2DP_CTRL_CMD_SUSPEND)
        CASE_RETURN_STR(A2DP_CTRL_CMD_SUSPEND)
        CASE_RETURN_STR(A2DP_CTRL_GET_AUDIO_CONFIG)
        CASE_RETURN_STR(A2DP_CTRL_GET_INPUT_AUDIO_CONFIG)
        CASE_RETURN_STR(A2DP_CTRL_GET_OUTPUT_AUDIO_CONFIG)
        CASE_RETURN_STR(A2DP_CTRL_CMD_OFFLOAD_START)
        CASE_RETURN_STR(A2DP_CTRL_CMD_OFFLOAD_START)
    default:
    default:
        break;
        break;
+32 −0
Original line number Original line Diff line number Diff line
@@ -961,6 +961,38 @@ void bta_av_co_audio_encoder_init(tA2DP_ENCODER_INIT_PARAMS* p_init_params) {
  mutex_global_unlock();
  mutex_global_unlock();
}
}


void bta_av_co_get_encoder_feeding_parameters(
    tA2DP_FEEDING_PARAMS* p_feeding_params) {
  /* Protect access to bta_av_co_cb.codec_config */
  mutex_global_lock();

  const uint8_t* p_codec_info = bta_av_co_cb.codec_config;
  int sample_rate = A2DP_GetTrackSampleRate(p_codec_info);
  if (sample_rate > 0) {
    p_feeding_params->sample_rate = sample_rate;
  } else {
    APPL_TRACE_ERROR("%s: invalid sample rate for codec %s: %d", __func__,
                     A2DP_CodecName(p_codec_info), sample_rate);
  }
  int channel_count = A2DP_GetTrackChannelCount(p_codec_info);
  if (channel_count > 0) {
    p_feeding_params->channel_count = channel_count;
  } else {
    APPL_TRACE_ERROR("%s: invalid channel count for codec %s: %d", __func__,
                     A2DP_CodecName(p_codec_info), channel_count);
  }
  int bits_per_sample = A2DP_GetTrackBitsPerSample(p_codec_info);
  if (bits_per_sample > 0) {
    p_feeding_params->bits_per_sample = bits_per_sample;
  } else {
    APPL_TRACE_ERROR("%s: invalid bits per sample for codec %s: %d", __func__,
                     A2DP_CodecName(p_codec_info), bits_per_sample);
  }

  /* Protect access to bta_av_co_cb.codec_config */
  mutex_global_unlock();
}

const tA2DP_ENCODER_INTERFACE* bta_av_co_get_encoder_interface(void) {
const tA2DP_ENCODER_INTERFACE* bta_av_co_get_encoder_interface(void) {
  /* Protect access to bta_av_co_cb.codec_config */
  /* Protect access to bta_av_co_cb.codec_config */
  mutex_global_lock();
  mutex_global_lock();
+2 −2
Original line number Original line Diff line number Diff line
@@ -53,10 +53,10 @@ bool btif_a2dp_sink_startup(void);
void btif_a2dp_sink_shutdown(void);
void btif_a2dp_sink_shutdown(void);


// Get the audio sample rate for the A2DP Sink module.
// Get the audio sample rate for the A2DP Sink module.
uint32_t btif_a2dp_sink_get_sample_rate(void);
tA2DP_SAMPLE_RATE btif_a2dp_sink_get_sample_rate(void);


// Get the audio channel count for the A2DP Sink module.
// Get the audio channel count for the A2DP Sink module.
uint8_t btif_a2dp_sink_get_channel_count(void);
tA2DP_CHANNEL_COUNT btif_a2dp_sink_get_channel_count(void);


// Update the decoder for the A2DP Sink module.
// Update the decoder for the A2DP Sink module.
// |p_codec_info| contains the new codec information.
// |p_codec_info| contains the new codec information.
Loading