My ultimate goal is to stream live audio to IBM Watson STT but right now I'm trying to stream an audio file to IBM Watson STT using libCURL in C++.
I was able to post a short audio file to IBM Watson STT using this simple code (for anyone interested):
code-listing-1:
#include <cstdio>
#include <curl/curl.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdlib>
int main(void)
{
// reading the audio file to memory
std::ifstream file("audio-file.ogg", std::ios::binary | std::ios::ate);
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> log(size);
if(!file.read(log.data(), size)) {
exit(-1);
}
// copying it to char buffer
char * data = new char[log.size()];
for(unsigned int i = 0; i < log.size(); i++) data[i] = log[i];
// doing the thing
struct curl_slist *hs = NULL;
CURL *curl = curl_easy_init();
CURLcode res;
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "https://api.{location}.speech-to-text.watson.cloud.ibm.com/instances/{instance_id}/v1/recognize");
hs = curl_slist_append(hs, "Content-Type: audio/ogg");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hs);
/* get verbose debug output please */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
/* allow whatever auth the server speaks */
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_easy_setopt(curl, CURLOPT_USERPWD, "apikey:{MY_API_KEY}");
/* size of the POST data */
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, log.size());
/* pass in a pointer to the data - libcurl will not copy */
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
res = curl_easy_perform(curl);
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
}
return 0;
}
this works perfectly for short audio file (I tried 7 seconds audio files) but really long audio don't seem to work (I tried 4+ minutes audio).
Now I'm trying to stream the audio instead. I'm thinking I can stream longer audio files that way (even though that's not really what I want to do).
According to the doc I should use the Transfer-Encoding: chunked header and depending of the file I should mention mention a specific Content-Type or Content-Type: application/octet-stream
So I came up with the following code based on libcurl Transfer Encoding example:
code-listing-2:
#include <cstdio>
#include <curl/curl.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdlib>
struct WriteThis {
const char *readptr;
size_t sizeleft;
};
static size_t read_callback(char *dest, size_t size, size_t nmemb, void *userp)
{
struct WriteThis *wt = (struct WriteThis *)userp;
size_t buffer_size = size*nmemb;
if(wt->sizeleft) {
/* copy as much as possible from the source to the destination */
size_t copy_this_much = wt->sizeleft;
if(copy_this_much > buffer_size)
copy_this_much = buffer_size;
memcpy(dest, wt->readptr, copy_this_much);
wt->readptr += copy_this_much;
wt->sizeleft -= copy_this_much;
return copy_this_much; /* we copied this many bytes */
}
return 0; /* no more data left to deliver */
}
int main(void)
{
CURL *curl;
CURLcode res;
struct WriteThis wt;
// reading the audio file to memory
std::ifstream file("audio-file.ogg", std::ios::binary | std::ios::ate);
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> log(size);
if(!file.read(log.data(), size)) {
exit(-1);
}
// copying it to char buffer
char * data = new char[log.size()];
for(unsigned int i = 0; i < log.size(); i++) data[i] = log[i];
// doing the thing
wt.readptr = data;
wt.sizeleft = log.size();
/* In windows, this will init the winsock stuff */
res = curl_global_init(CURL_GLOBAL_DEFAULT);
/* Check for errors */
if(res != CURLE_OK) {
fprintf(stderr, "curl_global_init() failed: %s\n",
curl_easy_strerror(res));
return 1;
}
/* get a curl handle */
curl = curl_easy_init();
if(curl) {
/* First set the URL that is about to receive our POST. */
curl_easy_setopt(curl, CURLOPT_URL, "https://api.{location}.speech-to-text.watson.cloud.ibm.com/instances/{instance_id}/v1/recognize");
/* Now specify we want to POST data */
curl_easy_setopt(curl, CURLOPT_POST, 1L);
/* we want to use our own read function */
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
/* pointer to pass to our read function */
curl_easy_setopt(curl, CURLOPT_READDATA, &wt);
/* get verbose debug output please */
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
/* allow whatever auth the server speaks */
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_easy_setopt(curl, CURLOPT_USERPWD, "apikey:{MY_API_KEY}");
struct curl_slist *chunk = NULL;
chunk = curl_slist_append(chunk, "Content-Type: application/octet-stream");
chunk = curl_slist_append(chunk, "Transfer-Encoding: chunked");
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}
This code, unfortunately, doesn't work. I get a 401 Error and the libcurl response/log is as follow (only writing the response part) :
- old SSL session ID is stale, removing < HTTP/2 401
< strict-transport-security: max-age=31536000; includeSubDomains;
< www-authenticate: Basic realm="IBM Watson Gateway(Log-in)"
< content-length: 164
< content-type: application/json
< x-dp-watson-tran-id: 47f2d5e9-a720-4114-885c-8c8804317c1b
< x-request-id: 47f2d5e9-a720-4114-885c-8c8804317c1b
< x-global-transaction-id: 47f2d5e9-a720-4114-885c-8c8804317c1b
< server: watson-gateway
< x-edgeconnect-midmile-rtt: 79
< x-edgeconnect-origin-mex-latency: 230
< date: Tue, 10 Aug 2021 11:51:46 GMT
- necessary data rewind wasn't possible
- stopped the pause stream!
- Connection #0 to host api.eu-gb.speech-to-text.watson.cloud.ibm.com left intact curl_easy_perform() failed: Send failed since rewinding of the data stream failed
What should I write to stream a buffered audio file to IBM Watson STT (and therefore will be able to stream live audio next).
Thanks in advance!