mirror of
https://github.com/jambonz/freeswitch-modules.git
synced 2026-01-25 02:08:27 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3f50aaf0d7 | |||
| 0bdb4a1e32 |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
In order to have a concrete record that your contribution is intentional and you agree to license it under the same terms as the project's license, we've adopted the same lightweight approach that the Linux Kernel submitting patches process, Docker, and many other projects use: the [Developer Certificate of Origin](https://developercertificate.org/), or DCO. This is a simple declaration that you wrote the contribution or otherwise have the right to contribute it to this project.
|
In order to have a concrete record that your contribution is intentional and you agree to license it under the same terms as [the project's license](./COPYING.md), we've adopted the same lightweight approach that the Linux Kernel submitting patches process, Docker, and many other projects use: the [Developer Certificate of Origin](https://developercertificate.org/), or DCO. This is a simple declaration that you wrote the contribution or otherwise have the right to contribute it to this project.
|
||||||
|
|
||||||
If you agree to this for your contribution, then all that's needed is to include the line in your commit or pull request comment:
|
If you agree to this for your contribution, then all that's needed is to include the line in your commit or pull request comment:
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,14 @@ under different conditions.
|
|||||||
1. MIT License:
|
1. MIT License:
|
||||||
- This license applies only when the software is used exclusively in conjunction with a [jambonz](https://jambonz.org) voice server.
|
- This license applies only when the software is used exclusively in conjunction with a [jambonz](https://jambonz.org) voice server.
|
||||||
Specifically, that means all of the following conditions must be true:
|
Specifically, that means all of the following conditions must be true:
|
||||||
|
|
||||||
i) The software is compiled and dynamically loaded into a running Freeswitch process
|
i) The software is compiled and dynamically loaded into a running Freeswitch process
|
||||||
that is dedicated to providing services to a jambonz server or cluster.
|
that is dedicated to providing services to a jambonz server or cluster.
|
||||||
|
|
||||||
ii) All incoming calls to the Freeswitch instance running this software are
|
ii) All incoming calls to the Freeswitch instance running this software are
|
||||||
controlled via an outbound ESL socket connection from Freeswitch
|
controlled via an outbound ESL socket connection from Freeswitch
|
||||||
to a Node.js application running the [jambonz feature server](https://github.com/jambonz/jambonz-feature-server) application.
|
to a Node.js application running the [jambonz feature server](https://github.com/jambonz/jambonz-feature-server) application.
|
||||||
|
|
||||||
- For the full terms of the MIT License, see the [`LICENSE_MIT`](LICENSE_MIT) file.
|
- For the full terms of the MIT License, see the [`LICENSE_MIT`](LICENSE_MIT) file.
|
||||||
|
|
||||||
2. AGPL Version 3.0:
|
2. AGPL Version 3.0:
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
Under specific conditions that are described [here](https://github.com/jambonz/freeswitch-modules/blob/main/COPYING), this software is licensed under the MIT License below.
|
Under specific conditions that are described here: https://github.com/jambonz/freeswitch-modules/blob/main/COPYING.md, this software is licensed under the MIT License below.
|
||||||
|
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ A collection of Freeswitch modules intended for use with a [jambonz](https://jam
|
|||||||
|
|
||||||
This software is available under a dual-licensing scheme. For specific use in a standalone [jambonz](https://jambonz.org) deployment, the [MIT License](./LICENSE_MIT) applies. For all other uses, the software is licensed for use under the [AGPL Version 3.0 license](./LICENSE_AGPL-3.0).
|
This software is available under a dual-licensing scheme. For specific use in a standalone [jambonz](https://jambonz.org) deployment, the [MIT License](./LICENSE_MIT) applies. For all other uses, the software is licensed for use under the [AGPL Version 3.0 license](./LICENSE_AGPL-3.0).
|
||||||
|
|
||||||
Please review [COPYING](./COPYING) for detailed information in order to assess whether your intended usage meets the specific conditions that allow for usage under the MIT License.
|
Please review [COPYING](./COPYING.md) for detailed information in order to assess whether your intended usage meets the specific conditions that allow for usage under the MIT License.
|
||||||
|
|
||||||
## Contributing to the software
|
## Contributing to the software
|
||||||
|
|
||||||
If you wish to contribute changes, please review [our rules](./CONTRIBUTING.md).
|
If you wish to contribute changes, please review [our rules](./CONTRIBUTING.md) governing contributions.
|
||||||
@@ -71,7 +71,7 @@ int AudioPipe::lws_callback(struct lws *wsi,
|
|||||||
lwsl_err("AudioPipe::lws_service_thread LWS_CALLBACK_CLIENT_CONNECTION_ERROR: %s, response status %d\n", in ? (char *)in : "(null)", rc);
|
lwsl_err("AudioPipe::lws_service_thread LWS_CALLBACK_CLIENT_CONNECTION_ERROR: %s, response status %d\n", in ? (char *)in : "(null)", rc);
|
||||||
if (ap) {
|
if (ap) {
|
||||||
ap->m_state = LWS_CLIENT_FAILED;
|
ap->m_state = LWS_CLIENT_FAILED;
|
||||||
ap->m_callback(ap->m_uuid.c_str(), AudioPipe::CONNECT_FAIL, (char *) in, ap->isFinished());
|
ap->m_callback(ap->m_uuid.c_str(), ap->m_bugname.c_str(), deepgram::AudioPipe::CONNECT_FAIL, (char *) in, ap->isFinished());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
lwsl_err("AudioPipe::lws_service_thread LWS_CALLBACK_CLIENT_CONNECTION_ERROR unable to find wsi %p..\n", wsi);
|
lwsl_err("AudioPipe::lws_service_thread LWS_CALLBACK_CLIENT_CONNECTION_ERROR unable to find wsi %p..\n", wsi);
|
||||||
@@ -82,11 +82,12 @@ int AudioPipe::lws_callback(struct lws *wsi,
|
|||||||
case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
case LWS_CALLBACK_CLIENT_ESTABLISHED:
|
||||||
{
|
{
|
||||||
AudioPipe* ap = findAndRemovePendingConnect(wsi);
|
AudioPipe* ap = findAndRemovePendingConnect(wsi);
|
||||||
|
|
||||||
if (ap) {
|
if (ap) {
|
||||||
*ppAp = ap;
|
*ppAp = ap;
|
||||||
ap->m_vhd = vhd;
|
ap->m_vhd = vhd;
|
||||||
ap->m_state = LWS_CLIENT_CONNECTED;
|
ap->m_state = LWS_CLIENT_CONNECTED;
|
||||||
ap->m_callback(ap->m_uuid.c_str(), AudioPipe::CONNECT_SUCCESS, NULL, ap->isFinished());
|
ap->m_callback(ap->m_uuid.c_str(), ap->m_bugname.c_str(), deepgram::AudioPipe::CONNECT_SUCCESS, NULL, ap->isFinished());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
lwsl_err("AudioPipe::lws_service_thread LWS_CALLBACK_CLIENT_ESTABLISHED %s unable to find wsi %p..\n", ap->m_uuid.c_str(), wsi);
|
lwsl_err("AudioPipe::lws_service_thread LWS_CALLBACK_CLIENT_ESTABLISHED %s unable to find wsi %p..\n", ap->m_uuid.c_str(), wsi);
|
||||||
@@ -96,6 +97,7 @@ int AudioPipe::lws_callback(struct lws *wsi,
|
|||||||
case LWS_CALLBACK_CLIENT_CLOSED:
|
case LWS_CALLBACK_CLIENT_CLOSED:
|
||||||
{
|
{
|
||||||
AudioPipe* ap = *ppAp;
|
AudioPipe* ap = *ppAp;
|
||||||
|
|
||||||
if (!ap) {
|
if (!ap) {
|
||||||
lwsl_err("AudioPipe::lws_service_thread LWS_CALLBACK_CLIENT_CLOSED %s unable to find wsi %p..\n", ap->m_uuid.c_str(), wsi);
|
lwsl_err("AudioPipe::lws_service_thread LWS_CALLBACK_CLIENT_CLOSED %s unable to find wsi %p..\n", ap->m_uuid.c_str(), wsi);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -104,12 +106,12 @@ int AudioPipe::lws_callback(struct lws *wsi,
|
|||||||
// closed by us
|
// closed by us
|
||||||
|
|
||||||
lwsl_debug("%s socket closed by us\n", ap->m_uuid.c_str());
|
lwsl_debug("%s socket closed by us\n", ap->m_uuid.c_str());
|
||||||
ap->m_callback(ap->m_uuid.c_str(), AudioPipe::CONNECTION_CLOSED_GRACEFULLY, NULL, ap->isFinished());
|
ap->m_callback(ap->m_uuid.c_str(), ap->m_bugname.c_str(), deepgram::AudioPipe::CONNECTION_CLOSED_GRACEFULLY, NULL, ap->isFinished());
|
||||||
}
|
}
|
||||||
else if (ap->m_state == LWS_CLIENT_CONNECTED) {
|
else if (ap->m_state == LWS_CLIENT_CONNECTED) {
|
||||||
// closed by far end
|
// closed by far end
|
||||||
lwsl_info("%s socket closed by far end\n", ap->m_uuid.c_str());
|
lwsl_info("%s socket closed by far end\n", ap->m_uuid.c_str());
|
||||||
ap->m_callback(ap->m_uuid.c_str(), AudioPipe::CONNECTION_DROPPED, NULL, ap->isFinished());
|
ap->m_callback(ap->m_uuid.c_str(), ap->m_bugname.c_str(), deepgram::AudioPipe::CONNECTION_DROPPED, NULL, ap->isFinished());
|
||||||
}
|
}
|
||||||
ap->m_state = LWS_CLIENT_DISCONNECTED;
|
ap->m_state = LWS_CLIENT_DISCONNECTED;
|
||||||
ap->setClosed();
|
ap->setClosed();
|
||||||
@@ -125,6 +127,7 @@ int AudioPipe::lws_callback(struct lws *wsi,
|
|||||||
case LWS_CALLBACK_CLIENT_RECEIVE:
|
case LWS_CALLBACK_CLIENT_RECEIVE:
|
||||||
{
|
{
|
||||||
AudioPipe* ap = *ppAp;
|
AudioPipe* ap = *ppAp;
|
||||||
|
|
||||||
if (!ap) {
|
if (!ap) {
|
||||||
lwsl_err("AudioPipe::lws_service_thread LWS_CALLBACK_CLIENT_RECEIVE %s unable to find wsi %p..\n", ap->m_uuid.c_str(), wsi);
|
lwsl_err("AudioPipe::lws_service_thread LWS_CALLBACK_CLIENT_RECEIVE %s unable to find wsi %p..\n", ap->m_uuid.c_str(), wsi);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -171,7 +174,7 @@ int AudioPipe::lws_callback(struct lws *wsi,
|
|||||||
if (lws_is_final_fragment(wsi)) {
|
if (lws_is_final_fragment(wsi)) {
|
||||||
if (nullptr != ap->m_recv_buf) {
|
if (nullptr != ap->m_recv_buf) {
|
||||||
std::string msg((char *)ap->m_recv_buf, ap->m_recv_buf_ptr - ap->m_recv_buf);
|
std::string msg((char *)ap->m_recv_buf, ap->m_recv_buf_ptr - ap->m_recv_buf);
|
||||||
ap->m_callback(ap->m_uuid.c_str(), AudioPipe::MESSAGE, msg.c_str(), ap->isFinished());
|
ap->m_callback(ap->m_uuid.c_str(), ap->m_bugname.c_str(), deepgram::AudioPipe::MESSAGE, msg.c_str(), ap->isFinished());
|
||||||
if (nullptr != ap->m_recv_buf) free(ap->m_recv_buf);
|
if (nullptr != ap->m_recv_buf) free(ap->m_recv_buf);
|
||||||
}
|
}
|
||||||
ap->m_recv_buf = ap->m_recv_buf_ptr = nullptr;
|
ap->m_recv_buf = ap->m_recv_buf_ptr = nullptr;
|
||||||
@@ -184,6 +187,7 @@ int AudioPipe::lws_callback(struct lws *wsi,
|
|||||||
case LWS_CALLBACK_CLIENT_WRITEABLE:
|
case LWS_CALLBACK_CLIENT_WRITEABLE:
|
||||||
{
|
{
|
||||||
AudioPipe* ap = *ppAp;
|
AudioPipe* ap = *ppAp;
|
||||||
|
|
||||||
if (!ap) {
|
if (!ap) {
|
||||||
lwsl_err("AudioPipe::lws_service_thread LWS_CALLBACK_CLIENT_WRITEABLE %s unable to find wsi %p..\n", ap->m_uuid.c_str(), wsi);
|
lwsl_err("AudioPipe::lws_service_thread LWS_CALLBACK_CLIENT_WRITEABLE %s unable to find wsi %p..\n", ap->m_uuid.c_str(), wsi);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -251,7 +255,6 @@ static const lws_retry_bo_t retry = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct lws_context *AudioPipe::context = nullptr;
|
struct lws_context *AudioPipe::context = nullptr;
|
||||||
std::string AudioPipe::protocolName;
|
|
||||||
std::mutex AudioPipe::mutex_connects;
|
std::mutex AudioPipe::mutex_connects;
|
||||||
std::mutex AudioPipe::mutex_disconnects;
|
std::mutex AudioPipe::mutex_disconnects;
|
||||||
std::mutex AudioPipe::mutex_writes;
|
std::mutex AudioPipe::mutex_writes;
|
||||||
@@ -424,7 +427,7 @@ void AudioPipe::initialize(int loglevel, log_emit_function logger) {
|
|||||||
|
|
||||||
lwsl_notice("AudioPipe::initialize starting\n");
|
lwsl_notice("AudioPipe::initialize starting\n");
|
||||||
std::lock_guard<std::mutex> lock(mapMutex);
|
std::lock_guard<std::mutex> lock(mapMutex);
|
||||||
std::thread t(&AudioPipe::lws_service_thread);
|
std::thread t(&deepgram::AudioPipe::lws_service_thread);
|
||||||
stopFlag = false;
|
stopFlag = false;
|
||||||
t.detach();
|
t.detach();
|
||||||
}
|
}
|
||||||
@@ -437,9 +440,9 @@ bool AudioPipe::deinitialize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// instance members
|
// instance members
|
||||||
AudioPipe::AudioPipe(const char* uuid, const char* host, unsigned int port, const char* path,
|
AudioPipe::AudioPipe(const char* uuid, const char* bugname, const char* host, unsigned int port, const char* path,
|
||||||
size_t bufLen, size_t minFreespace, const char* apiKey, notifyHandler_t callback) :
|
size_t bufLen, size_t minFreespace, const char* apiKey, notifyHandler_t callback) :
|
||||||
m_uuid(uuid), m_host(host), m_port(port), m_path(path), m_finished(false),
|
m_uuid(uuid), m_host(host), m_port(port), m_path(path), m_finished(false), m_bugname(bugname),
|
||||||
m_audio_buffer_min_freespace(minFreespace), m_audio_buffer_max_len(bufLen), m_gracefulShutdown(false),
|
m_audio_buffer_min_freespace(minFreespace), m_audio_buffer_max_len(bufLen), m_gracefulShutdown(false),
|
||||||
m_audio_buffer_write_offset(LWS_PRE), m_recv_buf(nullptr), m_recv_buf_ptr(nullptr),
|
m_audio_buffer_write_offset(LWS_PRE), m_recv_buf(nullptr), m_recv_buf_ptr(nullptr),
|
||||||
m_state(LWS_CLIENT_IDLE), m_wsi(nullptr), m_vhd(nullptr), m_apiKey(apiKey), m_callback(callback) {
|
m_state(LWS_CLIENT_IDLE), m_wsi(nullptr), m_vhd(nullptr), m_apiKey(apiKey), m_callback(callback) {
|
||||||
@@ -468,7 +471,6 @@ bool AudioPipe::connect_client(struct lws_per_vhost_data *vhd) {
|
|||||||
i.host = i.address;
|
i.host = i.address;
|
||||||
i.origin = i.address;
|
i.origin = i.address;
|
||||||
i.ssl_connection = LCCSCF_USE_SSL;
|
i.ssl_connection = LCCSCF_USE_SSL;
|
||||||
//i.protocol = protocolName.c_str();
|
|
||||||
i.pwsi = &(m_wsi);
|
i.pwsi = &(m_wsi);
|
||||||
|
|
||||||
m_state = LWS_CLIENT_CONNECTING;
|
m_state = LWS_CLIENT_CONNECTING;
|
||||||
|
|||||||
@@ -13,133 +13,130 @@
|
|||||||
|
|
||||||
namespace deepgram {
|
namespace deepgram {
|
||||||
|
|
||||||
class AudioPipe {
|
class AudioPipe {
|
||||||
public:
|
public:
|
||||||
enum LwsState_t {
|
enum LwsState_t {
|
||||||
LWS_CLIENT_IDLE,
|
LWS_CLIENT_IDLE,
|
||||||
LWS_CLIENT_CONNECTING,
|
LWS_CLIENT_CONNECTING,
|
||||||
LWS_CLIENT_CONNECTED,
|
LWS_CLIENT_CONNECTED,
|
||||||
LWS_CLIENT_FAILED,
|
LWS_CLIENT_FAILED,
|
||||||
LWS_CLIENT_DISCONNECTING,
|
LWS_CLIENT_DISCONNECTING,
|
||||||
LWS_CLIENT_DISCONNECTED
|
LWS_CLIENT_DISCONNECTED
|
||||||
|
};
|
||||||
|
enum NotifyEvent_t {
|
||||||
|
CONNECT_SUCCESS,
|
||||||
|
CONNECT_FAIL,
|
||||||
|
CONNECTION_DROPPED,
|
||||||
|
CONNECTION_CLOSED_GRACEFULLY,
|
||||||
|
MESSAGE
|
||||||
|
};
|
||||||
|
typedef void (*log_emit_function)(int level, const char *line);
|
||||||
|
typedef void (*notifyHandler_t)(const char *sessionId, const char* bugname, NotifyEvent_t event, const char* message, bool finished);
|
||||||
|
|
||||||
|
struct lws_per_vhost_data {
|
||||||
|
struct lws_context *context;
|
||||||
|
struct lws_vhost *vhost;
|
||||||
|
const struct lws_protocols *protocol;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void initialize(int loglevel, log_emit_function logger);
|
||||||
|
static bool deinitialize();
|
||||||
|
static bool lws_service_thread();
|
||||||
|
|
||||||
|
// constructor
|
||||||
|
AudioPipe(const char* uuid, const char* bugname, const char* host, unsigned int port, const char* path,
|
||||||
|
size_t bufLen, size_t minFreespace, const char* apiKey, notifyHandler_t callback);
|
||||||
|
~AudioPipe();
|
||||||
|
|
||||||
|
LwsState_t getLwsState(void) { return m_state; }
|
||||||
|
std::string& getApiKey(void) {
|
||||||
|
return m_apiKey;
|
||||||
|
}
|
||||||
|
void connect(void);
|
||||||
|
void bufferForSending(const char* text);
|
||||||
|
size_t binarySpaceAvailable(void) {
|
||||||
|
return m_audio_buffer_max_len - m_audio_buffer_write_offset;
|
||||||
|
}
|
||||||
|
size_t binaryMinSpace(void) {
|
||||||
|
return m_audio_buffer_min_freespace;
|
||||||
|
}
|
||||||
|
char * binaryWritePtr(void) {
|
||||||
|
return (char *) m_audio_buffer + m_audio_buffer_write_offset;
|
||||||
|
}
|
||||||
|
void binaryWritePtrAdd(size_t len) {
|
||||||
|
m_audio_buffer_write_offset += len;
|
||||||
|
}
|
||||||
|
void binaryWritePtrResetToZero(void) {
|
||||||
|
m_audio_buffer_write_offset = 0;
|
||||||
|
}
|
||||||
|
void lockAudioBuffer(void) {
|
||||||
|
m_audio_mutex.lock();
|
||||||
|
}
|
||||||
|
void unlockAudioBuffer(void) ;
|
||||||
|
|
||||||
|
void close() ;
|
||||||
|
void finish();
|
||||||
|
void waitForClose();
|
||||||
|
void setClosed() { m_promise.set_value(); }
|
||||||
|
bool isFinished() { return m_finished;}
|
||||||
|
|
||||||
|
// no default constructor or copying
|
||||||
|
AudioPipe() = delete;
|
||||||
|
AudioPipe(const AudioPipe&) = delete;
|
||||||
|
void operator=(const AudioPipe&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static int lws_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len);
|
||||||
|
static struct lws_context *context;
|
||||||
|
static std::mutex mutex_connects;
|
||||||
|
static std::mutex mutex_disconnects;
|
||||||
|
static std::mutex mutex_writes;
|
||||||
|
static std::list<AudioPipe*> pendingConnects;
|
||||||
|
static std::list<AudioPipe*> pendingDisconnects;
|
||||||
|
static std::list<AudioPipe*> pendingWrites;
|
||||||
|
static log_emit_function logger;
|
||||||
|
|
||||||
|
static std::mutex mapMutex;
|
||||||
|
static bool stopFlag;
|
||||||
|
|
||||||
|
static AudioPipe* findAndRemovePendingConnect(struct lws *wsi);
|
||||||
|
static AudioPipe* findPendingConnect(struct lws *wsi);
|
||||||
|
static void addPendingConnect(AudioPipe* ap);
|
||||||
|
static void addPendingDisconnect(AudioPipe* ap);
|
||||||
|
static void addPendingWrite(AudioPipe* ap);
|
||||||
|
static void processPendingConnects(lws_per_vhost_data *vhd);
|
||||||
|
static void processPendingDisconnects(lws_per_vhost_data *vhd);
|
||||||
|
static void processPendingWrites(void);
|
||||||
|
|
||||||
|
bool connect_client(struct lws_per_vhost_data *vhd);
|
||||||
|
|
||||||
|
LwsState_t m_state;
|
||||||
|
std::string m_uuid;
|
||||||
|
std::string m_host;
|
||||||
|
unsigned int m_port;
|
||||||
|
std::string m_path;
|
||||||
|
std::string m_metadata;
|
||||||
|
std::mutex m_text_mutex;
|
||||||
|
std::mutex m_audio_mutex;
|
||||||
|
int m_sslFlags;
|
||||||
|
struct lws *m_wsi;
|
||||||
|
uint8_t *m_audio_buffer;
|
||||||
|
size_t m_audio_buffer_max_len;
|
||||||
|
size_t m_audio_buffer_write_offset;
|
||||||
|
size_t m_audio_buffer_min_freespace;
|
||||||
|
uint8_t* m_recv_buf;
|
||||||
|
uint8_t* m_recv_buf_ptr;
|
||||||
|
size_t m_recv_buf_len;
|
||||||
|
struct lws_per_vhost_data* m_vhd;
|
||||||
|
notifyHandler_t m_callback;
|
||||||
|
log_emit_function m_logger;
|
||||||
|
std::string m_apiKey;
|
||||||
|
bool m_gracefulShutdown;
|
||||||
|
bool m_finished;
|
||||||
|
std::string m_bugname;
|
||||||
|
std::promise<void> m_promise;
|
||||||
};
|
};
|
||||||
enum NotifyEvent_t {
|
|
||||||
CONNECT_SUCCESS,
|
|
||||||
CONNECT_FAIL,
|
|
||||||
CONNECTION_DROPPED,
|
|
||||||
CONNECTION_CLOSED_GRACEFULLY,
|
|
||||||
MESSAGE
|
|
||||||
};
|
|
||||||
typedef void (*log_emit_function)(int level, const char *line);
|
|
||||||
typedef void (*notifyHandler_t)(const char *sessionId, NotifyEvent_t event, const char* message, bool finished);
|
|
||||||
|
|
||||||
struct lws_per_vhost_data {
|
|
||||||
struct lws_context *context;
|
|
||||||
struct lws_vhost *vhost;
|
|
||||||
const struct lws_protocols *protocol;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void initialize(int loglevel, log_emit_function logger);
|
|
||||||
static bool deinitialize();
|
|
||||||
static bool lws_service_thread();
|
|
||||||
|
|
||||||
// constructor
|
|
||||||
AudioPipe(const char* uuid, const char* host, unsigned int port, const char* path,
|
|
||||||
size_t bufLen, size_t minFreespace, const char* apiKey, notifyHandler_t callback);
|
|
||||||
~AudioPipe();
|
|
||||||
|
|
||||||
LwsState_t getLwsState(void) { return m_state; }
|
|
||||||
std::string& getApiKey(void) {
|
|
||||||
return m_apiKey;
|
|
||||||
}
|
|
||||||
void connect(void);
|
|
||||||
void bufferForSending(const char* text);
|
|
||||||
size_t binarySpaceAvailable(void) {
|
|
||||||
return m_audio_buffer_max_len - m_audio_buffer_write_offset;
|
|
||||||
}
|
|
||||||
size_t binaryMinSpace(void) {
|
|
||||||
return m_audio_buffer_min_freespace;
|
|
||||||
}
|
|
||||||
char * binaryWritePtr(void) {
|
|
||||||
return (char *) m_audio_buffer + m_audio_buffer_write_offset;
|
|
||||||
}
|
|
||||||
void binaryWritePtrAdd(size_t len) {
|
|
||||||
m_audio_buffer_write_offset += len;
|
|
||||||
}
|
|
||||||
void binaryWritePtrResetToZero(void) {
|
|
||||||
m_audio_buffer_write_offset = 0;
|
|
||||||
}
|
|
||||||
void lockAudioBuffer(void) {
|
|
||||||
m_audio_mutex.lock();
|
|
||||||
}
|
|
||||||
void unlockAudioBuffer(void) ;
|
|
||||||
|
|
||||||
void close() ;
|
|
||||||
void finish();
|
|
||||||
void waitForClose();
|
|
||||||
void setClosed() { m_promise.set_value(); }
|
|
||||||
bool isFinished() { return m_finished;}
|
|
||||||
|
|
||||||
// no default constructor or copying
|
|
||||||
AudioPipe() = delete;
|
|
||||||
AudioPipe(const AudioPipe&) = delete;
|
|
||||||
void operator=(const AudioPipe&) = delete;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
static int lws_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len);
|
|
||||||
static unsigned int nchild;
|
|
||||||
static struct lws_context *context;
|
|
||||||
static unsigned int numContexts;
|
|
||||||
static std::string protocolName;
|
|
||||||
static std::mutex mutex_connects;
|
|
||||||
static std::mutex mutex_disconnects;
|
|
||||||
static std::mutex mutex_writes;
|
|
||||||
static std::list<AudioPipe*> pendingConnects;
|
|
||||||
static std::list<AudioPipe*> pendingDisconnects;
|
|
||||||
static std::list<AudioPipe*> pendingWrites;
|
|
||||||
static log_emit_function logger;
|
|
||||||
|
|
||||||
static std::mutex mapMutex;
|
|
||||||
static bool stopFlag;
|
|
||||||
|
|
||||||
static AudioPipe* findAndRemovePendingConnect(struct lws *wsi);
|
|
||||||
static AudioPipe* findPendingConnect(struct lws *wsi);
|
|
||||||
static void addPendingConnect(AudioPipe* ap);
|
|
||||||
static void addPendingDisconnect(AudioPipe* ap);
|
|
||||||
static void addPendingWrite(AudioPipe* ap);
|
|
||||||
static void processPendingConnects(lws_per_vhost_data *vhd);
|
|
||||||
static void processPendingDisconnects(lws_per_vhost_data *vhd);
|
|
||||||
static void processPendingWrites(void);
|
|
||||||
|
|
||||||
bool connect_client(struct lws_per_vhost_data *vhd);
|
|
||||||
|
|
||||||
LwsState_t m_state;
|
|
||||||
std::string m_uuid;
|
|
||||||
std::string m_host;
|
|
||||||
unsigned int m_port;
|
|
||||||
std::string m_path;
|
|
||||||
std::string m_metadata;
|
|
||||||
std::mutex m_text_mutex;
|
|
||||||
std::mutex m_audio_mutex;
|
|
||||||
int m_sslFlags;
|
|
||||||
struct lws *m_wsi;
|
|
||||||
uint8_t *m_audio_buffer;
|
|
||||||
size_t m_audio_buffer_max_len;
|
|
||||||
size_t m_audio_buffer_write_offset;
|
|
||||||
size_t m_audio_buffer_min_freespace;
|
|
||||||
uint8_t* m_recv_buf;
|
|
||||||
uint8_t* m_recv_buf_ptr;
|
|
||||||
size_t m_recv_buf_len;
|
|
||||||
struct lws_per_vhost_data* m_vhd;
|
|
||||||
notifyHandler_t m_callback;
|
|
||||||
log_emit_function m_logger;
|
|
||||||
std::string m_apiKey;
|
|
||||||
bool m_gracefulShutdown;
|
|
||||||
bool m_finished;
|
|
||||||
std::string m_bugname;
|
|
||||||
std::promise<void> m_promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace deepgram
|
} // namespace deepgram
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -265,11 +265,12 @@ namespace {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void eventCallback(const char* sessionId, deepgram::AudioPipe::NotifyEvent_t event, const char* message, bool finished) {
|
static void eventCallback(const char* sessionId, const char* bugname,
|
||||||
|
deepgram::AudioPipe::NotifyEvent_t event, const char* message, bool finished) {
|
||||||
switch_core_session_t* session = switch_core_session_locate(sessionId);
|
switch_core_session_t* session = switch_core_session_locate(sessionId);
|
||||||
if (session) {
|
if (session) {
|
||||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||||
switch_media_bug_t *bug = (switch_media_bug_t*) switch_channel_get_private(channel, MY_BUG_NAME);
|
switch_media_bug_t *bug = (switch_media_bug_t*) switch_channel_get_private(channel, bugname);
|
||||||
if (bug) {
|
if (bug) {
|
||||||
private_t* tech_pvt = (private_t*) switch_core_media_bug_get_user_data(bug);
|
private_t* tech_pvt = (private_t*) switch_core_media_bug_get_user_data(bug);
|
||||||
if (tech_pvt) {
|
if (tech_pvt) {
|
||||||
@@ -343,6 +344,7 @@ namespace {
|
|||||||
tech_pvt->channels = channels;
|
tech_pvt->channels = channels;
|
||||||
tech_pvt->id = ++idxCallCount;
|
tech_pvt->id = ++idxCallCount;
|
||||||
tech_pvt->buffer_overrun_notified = 0;
|
tech_pvt->buffer_overrun_notified = 0;
|
||||||
|
strncpy(tech_pvt->bugname, bugname, MAX_BUG_LEN);
|
||||||
|
|
||||||
size_t buflen = LWS_PRE + (FRAME_SIZE_8000 * desiredSampling / 8000 * channels * 1000 / RTP_PACKETIZATION_PERIOD * nAudioBufferSecs);
|
size_t buflen = LWS_PRE + (FRAME_SIZE_8000 * desiredSampling / 8000 * channels * 1000 / RTP_PACKETIZATION_PERIOD * nAudioBufferSecs);
|
||||||
|
|
||||||
@@ -353,7 +355,7 @@ namespace {
|
|||||||
return SWITCH_STATUS_FALSE;
|
return SWITCH_STATUS_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
deepgram::AudioPipe* ap = new deepgram::AudioPipe(tech_pvt->sessionId, tech_pvt->host, tech_pvt->port, tech_pvt->path,
|
deepgram::AudioPipe* ap = new deepgram::AudioPipe(tech_pvt->sessionId, bugname, tech_pvt->host, tech_pvt->port, tech_pvt->path,
|
||||||
buflen, read_impl.decoded_bytes_per_packet, apiKey, eventCallback);
|
buflen, read_impl.decoded_bytes_per_packet, apiKey, eventCallback);
|
||||||
if (!ap) {
|
if (!ap) {
|
||||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error allocating AudioPipe\n");
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error allocating AudioPipe\n");
|
||||||
@@ -400,7 +402,8 @@ extern "C" {
|
|||||||
switch_status_t dg_transcribe_init() {
|
switch_status_t dg_transcribe_init() {
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "mod_deepgram_transcribe: audio buffer (in secs): %d secs\n", nAudioBufferSecs);
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "mod_deepgram_transcribe: audio buffer (in secs): %d secs\n", nAudioBufferSecs);
|
||||||
|
|
||||||
int logs = LLL_ERR | LLL_WARN | LLL_NOTICE || LLL_INFO | LLL_PARSER | LLL_HEADER | LLL_EXT | LLL_CLIENT | LLL_LATENCY | LLL_DEBUG ;
|
int logs = LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||||
|
// | LLL_INFO | LLL_PARSER | LLL_HEADER | LLL_EXT | LLL_CLIENT | LLL_LATENCY | LLL_DEBUG ;
|
||||||
|
|
||||||
deepgram::AudioPipe::initialize(logs, lws_logger);
|
deepgram::AudioPipe::initialize(logs, lws_logger);
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "AudioPipe::initialize completed\n");
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "AudioPipe::initialize completed\n");
|
||||||
@@ -455,7 +458,7 @@ extern "C" {
|
|||||||
|
|
||||||
switch_status_t dg_transcribe_session_stop(switch_core_session_t *session,int channelIsClosing, char* bugname) {
|
switch_status_t dg_transcribe_session_stop(switch_core_session_t *session,int channelIsClosing, char* bugname) {
|
||||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||||
switch_media_bug_t *bug = (switch_media_bug_t*) switch_channel_get_private(channel, MY_BUG_NAME);
|
switch_media_bug_t *bug = (switch_media_bug_t*) switch_channel_get_private(channel, bugname);
|
||||||
if (!bug) {
|
if (!bug) {
|
||||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "dg_transcribe_session_stop: no bug - websocket conection already closed\n");
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "dg_transcribe_session_stop: no bug - websocket conection already closed\n");
|
||||||
return SWITCH_STATUS_FALSE;
|
return SWITCH_STATUS_FALSE;
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ static switch_bool_t capture_callback(switch_media_bug_t *bug, void *user_data,
|
|||||||
{
|
{
|
||||||
private_t *tech_pvt = (private_t*) switch_core_media_bug_get_user_data(bug);
|
private_t *tech_pvt = (private_t*) switch_core_media_bug_get_user_data(bug);
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Got SWITCH_ABC_TYPE_CLOSE.\n");
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Got SWITCH_ABC_TYPE_CLOSE.\n");
|
||||||
|
|
||||||
dg_transcribe_session_stop(session, 1, tech_pvt->bugname);
|
dg_transcribe_session_stop(session, 1, tech_pvt->bugname);
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Finished SWITCH_ABC_TYPE_CLOSE.\n");
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Finished SWITCH_ABC_TYPE_CLOSE.\n");
|
||||||
}
|
}
|
||||||
@@ -74,7 +73,7 @@ static switch_status_t start_capture(switch_core_session_t *session, switch_medi
|
|||||||
void *pUserData;
|
void *pUserData;
|
||||||
uint32_t samples_per_second;
|
uint32_t samples_per_second;
|
||||||
|
|
||||||
if (switch_channel_get_private(channel, MY_BUG_NAME)) {
|
if (switch_channel_get_private(channel, bugname)) {
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "removing bug from previous transcribe\n");
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "removing bug from previous transcribe\n");
|
||||||
do_stop(session, bugname);
|
do_stop(session, bugname);
|
||||||
}
|
}
|
||||||
@@ -94,7 +93,7 @@ static switch_status_t start_capture(switch_core_session_t *session, switch_medi
|
|||||||
if ((status = switch_core_media_bug_add(session, "dg_transcribe", NULL, capture_callback, pUserData, 0, flags, &bug)) != SWITCH_STATUS_SUCCESS) {
|
if ((status = switch_core_media_bug_add(session, "dg_transcribe", NULL, capture_callback, pUserData, 0, flags, &bug)) != SWITCH_STATUS_SUCCESS) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
switch_channel_set_private(channel, MY_BUG_NAME, bug);
|
switch_channel_set_private(channel, bugname, bug);
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "added media bug for dg transcribe\n");
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "added media bug for dg transcribe\n");
|
||||||
|
|
||||||
return SWITCH_STATUS_SUCCESS;
|
return SWITCH_STATUS_SUCCESS;
|
||||||
@@ -105,13 +104,16 @@ static switch_status_t do_stop(switch_core_session_t *session, char* bugname)
|
|||||||
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
switch_status_t status = SWITCH_STATUS_SUCCESS;
|
||||||
|
|
||||||
switch_channel_t *channel = switch_core_session_get_channel(session);
|
switch_channel_t *channel = switch_core_session_get_channel(session);
|
||||||
switch_media_bug_t *bug = switch_channel_get_private(channel, MY_BUG_NAME);
|
switch_media_bug_t *bug = switch_channel_get_private(channel, bugname);
|
||||||
|
|
||||||
if (bug) {
|
if (bug) {
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Received user command command to stop transcribe.\n");
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Received user command command to stop transcribe.\n");
|
||||||
status = dg_transcribe_session_stop(session, 0, bugname);
|
status = dg_transcribe_session_stop(session, 0, bugname);
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "stopped transcribe.\n");
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "stopped transcribe.\n");
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "do_stop: did not find bugname %s.\n", bugname);
|
||||||
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@@ -141,7 +143,7 @@ SWITCH_STANDARD_API(dg_transcribe_function)
|
|||||||
if ((lsession = switch_core_session_locate(argv[0]))) {
|
if ((lsession = switch_core_session_locate(argv[0]))) {
|
||||||
if (!strcasecmp(argv[1], "stop")) {
|
if (!strcasecmp(argv[1], "stop")) {
|
||||||
char *bugname = argc > 2 ? argv[2] : MY_BUG_NAME;
|
char *bugname = argc > 2 ? argv[2] : MY_BUG_NAME;
|
||||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "stop transcribing\n");
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "stop transcribing %s\n", bugname);
|
||||||
status = do_stop(lsession, bugname);
|
status = do_stop(lsession, bugname);
|
||||||
} else if (!strcasecmp(argv[1], "start")) {
|
} else if (!strcasecmp(argv[1], "start")) {
|
||||||
char* lang = argv[2];
|
char* lang = argv[2];
|
||||||
@@ -151,7 +153,8 @@ SWITCH_STANDARD_API(dg_transcribe_function)
|
|||||||
flags |= SMBF_WRITE_STREAM ;
|
flags |= SMBF_WRITE_STREAM ;
|
||||||
flags |= SMBF_STEREO;
|
flags |= SMBF_STEREO;
|
||||||
}
|
}
|
||||||
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "start transcribing %s %s\n", lang, interim ? "interim": "complete");
|
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "start transcribing %s %s %s\n",
|
||||||
|
lang, interim ? "interim": "complete", bugname);
|
||||||
status = start_capture(lsession, flags, lang, interim, bugname);
|
status = start_capture(lsession, flags, lang, interim, bugname);
|
||||||
}
|
}
|
||||||
switch_core_session_rwunlock(lsession);
|
switch_core_session_rwunlock(lsession);
|
||||||
|
|||||||
Reference in New Issue
Block a user