schreibfaul1 / ESP32-audioI2S

Play mp3 files from SD via I2S
GNU General Public License v3.0
1.12k stars 289 forks source link

Play streaming audio.connecttohost() with authorization: bearer #825

Closed VaAndCob closed 2 weeks ago

VaAndCob commented 2 months ago

How can I play streaming with request header "Authorization: Bearer {accesstoken}" ? audio.connecttohost(url, accesstoken) something like this

schreibfaul1 commented 2 months ago

Nobody has asked about this yet. I can add that, but I don't have the opportunity to test it.

VaAndCob commented 2 months ago

Nobody has asked about this yet. I can add that, but I don't have the opportunity to test it.

Firstly, I have to thank you a lot for this library, it makes a lot easier for many applications. this is what I added to audio.h and audio.ccp (by reuse and modify your sketch)

// LINE voice message
bool Audio::lineVoiceMessage(String messageId, const char* token) {

  String host_chr = "https://api-data.line.me/v2/bot/message/" + messageId + "/content";
    // Now you can convert 'host' to a C-string if needed
    const char* host = host_chr.c_str();
     xSemaphoreTake(mutex_playAudioData, portMAX_DELAY);

    if(host == NULL) {
        AUDIO_INFO("Hostaddress is empty");
        stopSong();
        xSemaphoreGive(mutex_playAudioData);
        return false;
    }

    uint16_t lenHost = strlen(host);

    if(lenHost >= 512 - 10) {
        AUDIO_INFO("Hostaddress is too long");
        stopSong();
        xSemaphoreGive(mutex_playAudioData);
        return false;
    }

    /**/
    int   idx = indexOf(host, "http");
    char* l_host = (char*)malloc(lenHost + 10);
    if(idx < 0) {
        strcpy(l_host, "http://");
        strcat(l_host, host);
    }                                      // amend "http;//" if not found
    else { strcpy(l_host, (host + idx)); } // trim left if necessary

    char* h_host = NULL; // pointer of l_host without http:// or https://
    if(startsWith(l_host, "https")) h_host = strdup(l_host + 8);
    else h_host = strdup(l_host + 7);

    // initializationsequence
    int16_t  pos_slash;     // position of "/" in hostname
    int16_t  pos_colon;     // position of ":" in hostname
    int16_t  pos_ampersand; // position of "&" in hostname
    uint16_t port = 80;     // port number

    // In the URL there may be an extension, like noisefm.ru:8000/play.m3u&t=.m3u
    pos_slash = indexOf(h_host, "/", 0);
    pos_colon = indexOf(h_host, ":", 0);
    if(isalpha(h_host[pos_colon + 1])) pos_colon = -1; // no portnumber follows
    pos_ampersand = indexOf(h_host, "&", 0);

    char* hostwoext = NULL; // "skonto.ls.lv:8002" in "skonto.ls.lv:8002/mp3"
    char* extension = NULL; // "/mp3" in "skonto.ls.lv:8002/mp3"

    if(pos_slash > 1) {
        hostwoext = (char*)malloc(pos_slash + 1);
        memcpy(hostwoext, h_host, pos_slash);
        hostwoext[pos_slash] = '\0';
        uint16_t extLen = urlencode_expected_len(h_host + pos_slash);
        extension = (char*)malloc(extLen + 20);
        memcpy(extension, h_host + pos_slash, extLen);
        urlencode(extension, extLen, true);
    }
    else { // url has no extension
        hostwoext = strdup(h_host);
        extension = strdup("/");
    }

    if((pos_colon >= 0) && ((pos_ampersand == -1) || (pos_ampersand > pos_colon))) {
        port = atoi(h_host + pos_colon + 1); // Get portnumber as integer
        hostwoext[pos_colon] = '\0';         // Host without portnumber
    }

    setDefaults(); // no need to stop clients if connection is established (default is true)

    if(startsWith(l_host, "https")) m_f_ssl = true;
    else m_f_ssl = false;

    //  AUDIO_INFO("Connect to \"%s\" on port %d, extension \"%s\"", hostwoext, port, extension);
    // optional basic authorization
    uint16_t auth = strlen(token);
    char     authorization[auth + 1];
    authorization[0] = '\0';
    strcat(authorization, token);

    //  AUDIO_INFO("Connect to \"%s\" on port %d, extension \"%s\"", hostwoext, port, extension);

    char rqh[strlen(h_host) + strlen(authorization) + 220]; // http request header
    rqh[0] = '\0';

    strcat(rqh, "GET ");
    strcat(rqh, extension);
    strcat(rqh, " HTTP/1.1\r\n");
    strcat(rqh, "Host: ");
    strcat(rqh, hostwoext);
    strcat(rqh, "\r\n");

    if(auth > 0) {
        strcat(rqh, "Authorization: Bearer ");
        strcat(rqh, authorization);
        strcat(rqh, "\r\n");
    }
    strcat(rqh, "Content-Type: application/json\r\n\r\n"); 

  Serial.println(rqh);
    bool res = true; // no need to reconnect if connection exists

    if(m_f_ssl) {
        _client = static_cast<WiFiClient*>(&clientsecure);
        if(port == 80) port = 443;
    }
    else { _client = static_cast<WiFiClient*>(&client); }

    uint32_t t = millis();

    AUDIO_INFO("connect to: \"%s\" on port %d path \"%s\"", hostwoext, port, extension);

    _client->setTimeout(m_f_ssl ? m_timeout_ms_ssl : m_timeout_ms);
    res = _client->connect(hostwoext, port);
    if(res) {
        uint32_t dt = millis() - t;
        strcpy(m_lastHost, l_host);
        AUDIO_INFO("%s has been established in %lu ms, free Heap: %lu bytes", m_f_ssl ? "SSL" : "Connection", (long unsigned int)dt, (long unsigned int)ESP.getFreeHeap());
        m_f_running = true;
    }
    m_expectedCodec = CODEC_NONE;
    m_expectedPlsFmt = FORMAT_NONE;

    if(res) {
        Serial.println("Host connected");
    //    log_i("connecttohost(): %s", rqh);
        _client->print(rqh);
        if(endsWith(extension, ".mp3" )) m_expectedCodec  = CODEC_MP3;
        if(endsWith(extension, ".aac" )) m_expectedCodec  = CODEC_AAC;
        if(endsWith(extension, ".wav" )) m_expectedCodec  = CODEC_WAV;
        if(endsWith(extension, ".m4a" )) m_expectedCodec  = CODEC_M4A;
        if(endsWith(extension, ".ogg" )) m_expectedCodec  = CODEC_OGG;
        if(endsWith(extension, ".flac")) m_expectedCodec  = CODEC_FLAC;
        if(endsWith(extension, "-flac")) m_expectedCodec  = CODEC_FLAC;
        if(endsWith(extension, ".opus")) m_expectedCodec  = CODEC_OPUS;
        if(endsWith(extension, "/opus")) m_expectedCodec  = CODEC_OPUS;
        if(endsWith(extension, ".asx" )) m_expectedPlsFmt = FORMAT_ASX;
        if(endsWith(extension, ".m3u" )) m_expectedPlsFmt = FORMAT_M3U;
        if(endsWith(extension, ".pls" )) m_expectedPlsFmt = FORMAT_PLS;
        if(endsWith(extension, ".m3u8")) {
            m_expectedPlsFmt = FORMAT_M3U8;
            if(audio_lasthost) audio_lasthost(host);
        }
        setDatamode(HTTP_RESPONSE_HEADER); // Handle header
        m_streamType = ST_WEBSTREAM;
    }
    else {
        Serial.println("Request failed");
        AUDIO_INFO("Request %s failed!", l_host);
        if(audio_showstation) audio_showstation("");
        if(audio_showstreamtitle) audio_showstreamtitle("");
        if(audio_icydescription) audio_icydescription("");
        if(audio_icyurl) audio_icyurl("");
        m_lastHost[0] = 0;
    }
    if(hostwoext) {
        free(hostwoext);
        hostwoext = NULL;
    }
    if(extension) {
        free(extension);
        extension = NULL;
    }
    if(l_host) {
        free(l_host);
        l_host = NULL;
    }
    if(h_host) {
        free(h_host);
        h_host = NULL;
    }
    xSemaphoreGive(mutex_playAudioData);
    return res;
}

and this is the result, I think it works but the download audio file (.m4a) doesn't play.

GET /v2/bot/message/524574436284957095/content HTTP/1.1 Host: api-data.line.me Authorization: Bearer eFC+Tj5hJrxLMxHDo6vL Content-Type: application/json

info connect to: "api-data.line.me" on port 443 path "/v2/bot/message/524574436284957095/content" info SSL has been established in 1809 ms, free Heap: 96532 bytes Host connected info AACDecoder has been initialized, free Heap: 97748 bytes , free stack 2668 DWORDs info unknown Audio Type 0 info max bitrate: 1073743106 info avr bitrate: 336070145 info unknown ObjectType e, stop info Sampling Frequency: 44100 info ch; 1, bps: 16, sr: 16000

I have downloaded this audio.m4a file to my PC and it can be played with a music player, I also tried saving this file to the SD card and playing it with the SD card, but it still did not play.

I'm unsure if the audio file has missing info, etc. I also attached an example m4a file in case you will be kind enough to test it. audio.zip

Thank you.

schreibfaul1 commented 2 months ago

The audio.m4a file is within the specification, but differs from the "usual" m4a files when analysing the m4a header. I have extended the routine for reading the m4a header. The file can now be played.

VaAndCob commented 2 months ago

Thank you so much, it works for now.. I download m4a file to littleFS and playback. but the problem is the speed is slower than usual. it's like 0.5 time speed or lower

Downloaded 1024 bytes... Downloaded 1024 bytes... Downloaded 1024 bytes... Downloaded 1024 bytes... Downloaded 534 bytes... File '/voicemessage.m4a' saved, size: 9750 bytes Playing the audio file... info buffers freed, free Heap: 93868 bytes info Reading file: "/voicemessage.m4a" info AACDecoder has been initialized, free Heap: 92952 bytes , free stack 2908 DWORDs info ch; 1, bps: 16, sr: 16000 info AudioType: MPEG4 / Audio info max bitrate: 16384 info avg bitrate: 16384 info AudioObjectType: AAC Low Complexity info Sampling Frequency: 16000 info Channel Configurations: front-center info AAC FrameLength: 1024 bytes info ch; 1, bps: 16, sr: 16000 info Audio-Length: 8705 info Content-Length: 9750 info stream ready info syncword found at pos 0 info Channels: 1 info SampleRate: 16000 info BitsPerSample: 16 info BitRate: 16375 info Closing audio file "voicemessage.m4a" voicemessage.m4a info End of file "voicemessage.m4a"

VaAndCob commented 2 months ago

oh and if possible you can add a function to play stream from https get with authorization: bearer would be very appreaciate

schreibfaul1 commented 2 months ago

Can you please attach the file, the last one was too short to recognise. Maybe there is a problem with mono? I don't want to write a separate function for the "bearer", maybe we can send the token as a user in connecttohost()?

VaAndCob commented 2 months ago

Can you please attach the file, the last one was too short to recognise. Maybe there is a problem with mono? I don't want to write a separate function for the "bearer", maybe we can send the token as a user in connecttohost()?

that would be great, just adding a Bearer parameter into this function connecttohost() is ok.

the m4a file is Stereo, Sampling rate 32000Hz, 32 bps here is an example of voice sound I downloaded from Line application. whenever I download from Line server and save to LittleFS as .m4a file extension, then play with audio.connecttoFS function. the sound volume is low and the speed is slow, (sounds like a monster LOL)

voice_703585.m4a.zip

one more question. Is it capable of playing m4a streaming from the host? Any limitation?

thank :)

VaAndCob commented 2 months ago

one more thing, I notice the m4a information from success play file vs no sound file as below. SUCCESS PLAYED

File '/voicemessage.m4a' saved, size: 11626 bytes Playing the audio file... info PSRAM found, inputBufferSize: 638965 bytes info buffers freed, free Heap: 94252 bytes info Reading file: "/voicemessage.m4a" info AACDecoder has been initialized, free Heap: 93336 bytes , free stack 3004 DWORDs info ch; 2, bps: 16, sr: 16000 info AudioType: MPEG4 / Audio info max bitrate: 16295 info avg bitrate: 16295 info AudioObjectType: AAC Low Complexity info Sampling Frequency: 16000 info Channel Configurations: front-center info AAC FrameLength: 1024 bytes info ch; 2, bps: 16, sr: 16000 info Audio-Length: 10506 info Content-Length: 11634 info stream ready info syncword found at pos 0 info Channels: 1 info SampleRate: 16000 info BitsPerSample: 16 info BitRate: 23875 info Closing audio file "voicemessage.m4a" voicemessage.m4a info End of file "voicemessage.m4a"

//---------------------- NO SOUND

File '/voicemessage.m4a' saved, size: 16007 bytes Playing the audio file... info buffers freed, free Heap: 89492 bytes info Reading file: "/voicemessage.m4a" info AACDecoder has been initialized, free Heap: 88576 bytes , free stack 3004 DWORDs info ch; 1, bps: 16, sr: 16000 info AudioType: MPEG4 / Audio info max bitrate: 16384 info avg bitrate: 16384 info AudioObjectType: AAC Low Complexity info Sampling Frequency: 16000 info Channel Configurations: front-center info AAC FrameLength: 1024 bytes info ch; 1, bps: 16, sr: 16000

//---------------- So I guessed it the m4a file itself that I downloaded from Line server that might be missing information to get the encoder play it correctly..

github-actions[bot] commented 1 month ago

This issue is stale because it has been open for 30 days with no activity.

github-actions[bot] commented 2 weeks ago

This issue was closed because it has been inactive for 14 days since being marked as stale.