pschatzmann / ESP32-A2DP

A Simple ESP32 Bluetooth A2DP Library (to implement a Music Receiver or Sender) that supports Arduino, PlatformIO and Espressif IDF
Apache License 2.0
1.55k stars 261 forks source link

Double metadata notifications response on provoked track change #561

Closed martinroger closed 1 month ago

martinroger commented 1 month ago

Problem Description

When running any of the basic examples of the A2DP Sink that queries metada update notifications, I noticed that sometimes - and mostly when - doing a track change either directly on the TG or via the CT (a2dp_sink.next()), I would get double and even triple notifications with the new song metadata in a short timeframe, as if _av_newtrack() got called twice by two different places (I couldn't find evidence of that, even going to DEBUG level ESP messages).

The expected behaviour would be to get a single response metadata notification when changing tracks either via the CT, or directly on the TG (e.g. pressing the next button on the phone).

The current work-around is to implement some basic data-debouncing and compares, since these 2-3 responses are usually fired very rapidly together.

I noticed that sometimes between those duplicate metadata responses, the track duration attribute isn't updated on the first 1-2 responses but is updated on the third. This makes me think that this may be also possibly related to the type of TG device responding. Could this be just a quirk of how AVRCP is sometimes implemented in the wild and not an actual issue ?

Device Description

NodeMCU 32S, which is based on a WROOM-32. I also observed the same behaviour on a Sparkfun Thing Plus which I believe to run the WROOM-32E. It is connected to an external UDA1334A DAC, but it also shows the issue with the i2s pointing to the internal DAC.

Sketch

//bt_music_receiver_with_metadata.ino exhibits this behaviour, apparently related to the metadata callback being called twice :

void avrc_metadata_callback(uint8_t id, const uint8_t *text) {
  Serial.printf("==> AVRC metadata rsp: attribute id 0x%x, %s\n", id, text);
  if (id == ESP_AVRC_MD_ATTR_PLAYING_TIME) {
    uint32_t playtime = String((char*)text).toInt();
    Serial.printf("==> Playing time is %d ms (%d seconds)\n", playtime, (int)round(playtime/1000.0));
  }
}

Other Steps to Reproduce

I tried :

Provide your Version of the EP32 Arduino Core (or the IDF Version)

IDF 6.7.0, Arduino Core 2.0.16, ESP32-A2DP 1.8.1+sha.b0ad45d

I have checked existing issues, discussions and online documentation

pschatzmann commented 1 month ago

I think the application works as designed: I am just passing all the events that are received to the callback and I guess that they have implemented it that way to make sure that no events gets lost.

Usually this is not a problem because the events are used to update the information locally.

If your application logic relies on some unique events, you will need to implement your own logic