Esp-idf: Bluetooth A2DP source: Data callback stops after a while. (IDFGH-1865)

Created on 16 Sep 2019  路  8Comments  路  Source: espressif/esp-idf

Environment

  • Development Kit: [ESP32-Wrover-Kit]
  • Kit version (for WroverKit): [v4]
  • Module or chip used: [ESP32-WROVER-B]
  • IDF version (run git describe --tags to find it):
    v4.0-dev-1416-gba0f4f17e
  • Build System: [Make]
  • Compiler version (run xtensa-esp32-elf-gcc --version to find it):
    xtensa-esp32-elf-gcc (crosstool-NG esp32-2019r1) 8.2.0
  • Operating System: [macOS]
  • Power Supply: [USB]

Problem Description

ESP32 receives audio data over WiFi and pushes it to a ringbuffer. The audio is then read from the ringbuffer in bt_app_a2d_data_cb. It all worked well before we upgraded to release/v4.0. The callback function stops working after this event:

_a2dp media start successfully._

Then the callback function stops being called. I am not sure what is the cause or if it is just coincidence that it happens on same time.

When i dont use the ringbuffers and just generate some random noise it works.

I made example application where the callback function stops working after it works properly for a few seconds.

_Callback function in example is same as we use on device with udp._

Expected Behavior

Should connect to the tested device and then send random noise to the a2dp sink device.

Actual Behavior

On succesfull connect sound is transfered but stops after about 5 seconds.

Steps to reproduce

Run the code below. Only thing to change in code is the name of device you want to connect. It is at same place as in example.

Code to reproduce this issue

See the commit.

Update the bluetooth settings in menuconfig

  • Component config ---> Bluetooth (enable)
  • Component config ---> Bluetooth controller ---> Bluetooth controller mode ---> Bluetooth dual mode
  • Component config ---> Bluedroid Enable ---> Classic Bluetooth (enable)
  • Component config ---> Bluedroid Enable ---> Classic Bluetooth ---> A2DP (enable)

Most helpful comment

@KollarRichard I'm also using a ring buffer to pass audio to a BT headset. I've used your example above and modified it to make it work with both current master and release/v4.0. Please, take a look and let me know if it helps.
See the diff with master

All 8 comments

@KollarRichard Thanks for reporting, we will look into. Thanks.

@KollarRichard

  1. When i dont use the ringbuffers and just generate some random noise, it works well, doesn't it?
  2. In bt_app_a2d_data_cb, when you call xRingbufferReceive, What is the last parameter you used? Suggesting to use 0.
  3. The a2dp_source demo is in a loop: connect -> paly -> stop -> disconnect -> connect. Pay attention to it.

@blueMoodBHD

  1. Correct. I even noticed that when I call just xRingbufferGetCurFreeSize it works for a while. Stops anyway but later.
  2. I use xRingbufferReceiveUpTo and which parameter you mean? There are more of them. Documentation
    edit: You meant thexTicksToWait as the last parameter. I noticed it just now.
  3. I modified it to be connected only, and even when was used as in demo it failed little bit before disconnect and then never connected again. The original demo without _ringbuffers_ worked well.

It seems like that there is something error about ringbuffer. Here are some considerations:

  1. Can't block in bt_app_a2d_data_cb, so xTicksToWait should be 0. And there should not be any other delays.
  2. After calling xRingbufferReceiveUpTo, A call to vRingbufferReturnItem is required after this to free up the date retrieved. even if the data you received is less than xMaxSize.
  1. Tried it with 0 but stopped working anyway.
  2. vRingbufferReturnItem is called.

Here is my code for callback function:

static int32_t bt_app_a2d_data_cb(uint8_t *data, int32_t len)
{

  if (len < 0 || data == NULL)
  {
    return 0;
  }

    size_t item_size;
    // size_t free_size = xRingbufferGetCurFreeSize(a2dp_input_buffer);
    // ESP_LOGE(BT_AV_TAG, "Waiting for bluetooth data, buffer free size: %d", free_size);
    char *item = (char *)xRingbufferReceiveUpTo(a2dp_input_buffer, &item_size, 0, len / 2);

    if (item != NULL)
    {
      if (item_size < (len / 2))
      {
        ESP_LOGI(BT_AV_TAG, "!!!!!Less items in buffer (%d) than required (%d)", item_size, len / 2);
      }
      // ESP_LOGI(BT_AV_TAG, "Received data for bluetooth: %d", item_size);
      for (int i = 0; i < item_size / 2; i++)
      {
        char v1 = item[2 * i];
        char v2 = item[2 * i + 1];

        data[4 * i] = v1;
        data[4 * i + 1] = v2;
        data[4 * i + 2] = v1;
        data[4 * i + 3] = v2;
      }

      vRingbufferReturnItem(a2dp_input_buffer, (void *)item);
      return len;
    }
    else
    {
      ESP_LOGE(BT_AV_TAG, "Received no data for bluetooth");
      return len;
    }
}

@KollarRichard I'm also using a ring buffer to pass audio to a BT headset. I've used your example above and modified it to make it work with both current master and release/v4.0. Please, take a look and let me know if it helps.
See the diff with master

Hi @KollarRichard

I have done some fix in your a2dp source example. And it runs well connected with another ESP32-WROVER-B board runs official a2dp_sink example.

The main fix is in the app_main(), I change the sequence of tasks.

In my opinion, AVRC and A2DP service should start up completely first, then other tasks based on these two function should be started up.

I attached the example, please take a look if the problem still exists. And pin me anytime if you have questions about the fix.

a2dp_source.zip

@KollarRichard Feel free to reopen if the issue still happens. Thanks.

Was this page helpful?
0 / 5 - 0 ratings