I am working on a project of sound source localization and want to lit an individual ws2812 Led in the direction of the sound source.
I came across an expansion software package x-cube-memsmic1 (https://www.st.com/en/embedded-software/x-cube-memsmic1.html) which uses Acoustic SL library to estimate the angle of the sound source and send the angle along with some other things to PC via USB. The description says that the audio_application.c and usbd_audio_if.c contains some functions to get the angle from the source and send it.
Now I don't know which function does this and how?
Can someone tell me how can I get that specific function so I can put it in main loop?
Thanks
Viki
#include "audio_application.h"
#include "acoustic_sl.h"
uint16_t PDM_Buffer[((((AUDIO_IN_CHANNELS * AUDIO_IN_SAMPLING_FREQUENCY) / 1000) * MAX_DECIMATION_FACTOR) / 16)* N_MS ];
/* Private variables ---------------------------------------------------------*/
/*Handler and Config structure for Source Localization*/
AcousticSL_Handler_t libSoundSourceLoc_Handler_Instance;
AcousticSL_Config_t libSoundSourceLoc_Config_Instance;
volatile int16_t SOURCE_Angle_Value = 0;
volatile uint16_t SOURCE_Enable_Value = 1;
extern UART_HandleTypeDef UartHandle;
volatile int32_t result[2];
static uint16_t PCM_Buffer[AUDIO_IN_CHANNELS * AUDIO_IN_SAMPLING_FREQUENCY / 1000];
void CCA02M2_AUDIO_IN_HalfTransfer_CallBack(uint32_t Instance)
{
UNUSED(Instance);
AudioProcess();
}
void CCA02M2_AUDIO_IN_TransferComplete_CallBack(uint32_t Instance)
{
UNUSED(Instance);
AudioProcess();
}
void AudioProcess(void)
{
if (CCA02M2_AUDIO_IN_PDMToPCM(CCA02M2_AUDIO_INSTANCE, (uint16_t *)PDM_Buffer, PCM_Buffer) != BSP_ERROR_NONE)
{
Error_Handler();
}
if (AcousticSL_Data_Input((int16_t *)&PCM_Buffer[BOTTOM_LEFT_MIC], (int16_t *)&PCM_Buffer[TOP_RIGHT_MIC],
(int16_t *)&PCM_Buffer[BOTTOM_RIGHT_MIC], (int16_t *)&PCM_Buffer[TOP_LEFT_MIC], &libSoundSourceLoc_Handler_Instance) == 1U)
{
SW_Task2_Start(); /*Localization Processing Task*/
}
Send_Audio_to_USB((int16_t *)PCM_Buffer, AUDIO_IN_SAMPLING_FREQUENCY / 1000 * AUDIO_IN_CHANNELS);
}
void Audio_Libraries_Init(void)
{
__IO uint32_t error_value = 0;
/* Enable CRC peripheral to unlock the library */
__CRC_CLK_ENABLE();
/*Setup Source Localization static parameters*/
libSoundSourceLoc_Handler_Instance.channel_number = 4;
libSoundSourceLoc_Handler_Instance.M12_distance = DIAGONAL;
libSoundSourceLoc_Handler_Instance.M34_distance = DIAGONAL;
libSoundSourceLoc_Handler_Instance.sampling_frequency = AUDIO_IN_SAMPLING_FREQUENCY;
libSoundSourceLoc_Handler_Instance.algorithm = ACOUSTIC_SL_ALGORITHM_GCCP;
libSoundSourceLoc_Handler_Instance.ptr_M1_channels = 4;
libSoundSourceLoc_Handler_Instance.ptr_M2_channels = 4;
libSoundSourceLoc_Handler_Instance.ptr_M3_channels = 4;
libSoundSourceLoc_Handler_Instance.ptr_M4_channels = 4;
libSoundSourceLoc_Handler_Instance.samples_to_process = 512;
(void)AcousticSL_getMemorySize(&libSoundSourceLoc_Handler_Instance);
libSoundSourceLoc_Handler_Instance.pInternalMemory = (uint32_t *)malloc(libSoundSourceLoc_Handler_Instance.internal_memory_size);
error_value += AcousticSL_Init(&libSoundSourceLoc_Handler_Instance);
/*Setup Source Localization dynamic parameters*/
libSoundSourceLoc_Config_Instance.resolution = 10;
libSoundSourceLoc_Config_Instance.threshold = 24;
error_value += AcousticSL_setConfig(&libSoundSourceLoc_Handler_Instance, &libSoundSourceLoc_Config_Instance);
/*Error Management*/
if (error_value != 0U)
{
Error_Handler();
}
/*Malloc Failure*/
if (libSoundSourceLoc_Handler_Instance.pInternalMemory == NULL)
{
Error_Handler();
}
}
void SW_IRQ_Tasks_Init(void)
{
HAL_NVIC_SetPriority((IRQn_Type)EXTI1_IRQn, 0x0D, 0);
HAL_NVIC_EnableIRQ((IRQn_Type)EXTI1_IRQn);
HAL_NVIC_SetPriority((IRQn_Type)EXTI2_IRQn, 0x0E, 0);
HAL_NVIC_EnableIRQ((IRQn_Type)EXTI2_IRQn);
}
void SW_Task1_Callback(void)
{
}
void SW_Task2_Callback(void)
{
(void)AcousticSL_Process((int32_t *)&result, &libSoundSourceLoc_Handler_Instance);
if (result[0] == ACOUSTIC_SL_NO_AUDIO_DETECTED)
{
result[0] = -1;
}
if (result[0] != -1)
{
char output[4];
int32_t n = sprintf(output, "%li", (int32_t)result[0]);
(void)HAL_UART_Transmit(&UartHandle, (uint8_t *)" ", 4, 0xFFFF);
(void)HAL_UART_Transmit(&UartHandle, (uint8_t *)"\r", 1, 0xFFFF);
(void)HAL_UART_Transmit(&UartHandle, (uint8_t *)output, (uint16_t)n, 0xFFFF);
}
}
void SW_Task1_Start(void)
{
HAL_NVIC_SetPendingIRQ(EXTI1_IRQn);
}
void SW_Task2_Start(void)
{
HAL_NVIC_SetPendingIRQ(EXTI2_IRQn);
}
void Error_Handler(void)
{
while (1);
}
I'm using code from An Introduction to Bluetooth Programming ► Chapter 4. Bluetooth programming in C with BlueZ4.2. ► RFCOMM sockets to send messages between two Raspberry Pi.
However, if I don't make the pairing between two devices through the bluetoothctl, I can't use the client because it gives me error:
uh oh: Invalid exchange.
Can you give me some hints on how can I make the pair through the C code? I need to use this "automatically" without need to pairing through the bluetoothctl before the C code.
Before getting into my answer, I am not sure how to achieve this using "libbluetooth" API's. But my below answer is based on DBUS API using GDBUS. This should most likely work with any recent bluez (with bluetoothd) running.
Note, with Bluez5 it's recommended to use DBUS API's.
To brief, you need to develop an Agent which accepts the Pairing request automatically, assuming "Confirmation" agent here. Please refer agent capabilities here.
With recent bluez version (atleast 5.47+), we have a new API "ConnectDevice" which can be used to connect device without scanning/discovery. From your question, I understand that you are trying to communicate between two RPi's, so you can find the BT address for both the bluetooth controllers. With BT address in places,
/*
* bluez_adapter_connect.c - Connect with device without StartDiscovery
* - This example registers an agen with NoInputOutput capability for the purpose of
* auto pairing
* - Use ConnectDevice method to connect with device using provided MAC address
* - Usual signal subscription to get the details of the connected device
* - Introduced new signal handler to exit the program gracefully
*
* Note: As "ConnectDevice" is new API and in experimental state (but mostly stable)
* one need to use "-E" option when starting "bluetoothd". Systems running systemd can
* edit /lib/systemd/system/bluetooth.service in ExecStart option
*
* When this API is useful?
* - When you already have the MAC address of end bluetooth Device to connect with, then
* you don't need to scan for the device (with or without filter) and connect it.
* - StartDiscovery + Pair + Connect => ConnectDevice
*
* How you will have MAC address before scanning?
* - When you have other communication (wired or wireless) medium to exchange the MAC address
* - For example, NFC OOB can be used to exchange the MAC address
* - Testing Bluetooth with same device (MAC address known)
*
* - Here Agent capability is registered as "NoInputOutput" for experimental purpose only, in
* real world scenario, Pair + Connect involves real Agents.
* - Also note, bluez_agent_call_method and bluez_adapter_call_method are two different methods doing
* the same work with difference in interface name and object path. This exist just to make the
* understanding more clear.
*
* gcc `pkg-config --cflags glib-2.0 gio-2.0` -Wall -Wextra -o ./bin/bluez_adapter_connect ./bluez_adapter_connect.c `pkg-config --libs glib-2.0 gio-2.0`
*/
#include <glib.h>
#include <gio/gio.h>
#include <signal.h>
GMainLoop *loop;
GDBusConnection *con;
static void bluez_property_value(const gchar *key, GVariant *value)
{
const gchar *type = g_variant_get_type_string(value);
g_print("\t%s : ", key);
switch(*type) {
case 'o':
case 's':
g_print("%s\n", g_variant_get_string(value, NULL));
break;
case 'b':
g_print("%d\n", g_variant_get_boolean(value));
break;
case 'u':
g_print("%d\n", g_variant_get_uint32(value));
break;
case 'a':
/* TODO Handling only 'as', but not array of dicts */
if(g_strcmp0(type, "as"))
break;
g_print("\n");
const gchar *uuid;
GVariantIter i;
g_variant_iter_init(&i, value);
while(g_variant_iter_next(&i, "s", &uuid))
g_print("\t\t%s\n", uuid);
break;
default:
g_print("Other\n");
break;
}
}
typedef void (*method_cb_t)(GObject *, GAsyncResult *, gpointer);
static int bluez_adapter_call_method(const char *method, GVariant *param, method_cb_t method_cb)
{
g_dbus_connection_call(con,
"org.bluez",
/* TODO Find the adapter path runtime */
"/org/bluez/hci0",
"org.bluez.Adapter1",
method,
param,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
method_cb,
(void *)method);
return 0;
}
static void bluez_result_async_cb(GObject *con,
GAsyncResult *res,
gpointer data)
{
const gchar *key = (gchar *)data;
GVariant *result = NULL;
GError *error = NULL;
result = g_dbus_connection_call_finish((GDBusConnection *)con, res, &error);
if(error != NULL) {
g_print("Unable to get result: %s\n", error->message);
return;
}
if(result) {
result = g_variant_get_child_value(result, 0);
bluez_property_value(key, result);
}
g_variant_unref(result);
}
static void bluez_device_appeared(GDBusConnection *sig,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
(void)sig;
(void)sender_name;
(void)object_path;
(void)interface;
(void)signal_name;
(void)user_data;
GVariantIter *interfaces;
const char *object;
const gchar *interface_name;
GVariant *properties;
g_variant_get(parameters, "(&oa{sa{sv}})", &object, &interfaces);
while(g_variant_iter_next(interfaces, "{&s#a{sv}}", &interface_name, &properties)) {
if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
g_print("[ %s ]\n", object);
const gchar *property_name;
GVariantIter i;
GVariant *prop_val;
g_variant_iter_init(&i, properties);
while(g_variant_iter_next(&i, "{&sv}", &property_name, &prop_val))
bluez_property_value(property_name, prop_val);
g_variant_unref(prop_val);
}
g_variant_unref(properties);
}
return;
}
#define BT_ADDRESS_STRING_SIZE 18
static void bluez_device_disappeared(GDBusConnection *sig,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
(void)sig;
(void)sender_name;
(void)object_path;
(void)interface;
(void)signal_name;
GVariantIter *interfaces;
const char *object;
const gchar *interface_name;
char address[BT_ADDRESS_STRING_SIZE] = {'\0'};
g_variant_get(parameters, "(&oas)", &object, &interfaces);
while(g_variant_iter_next(interfaces, "s", &interface_name)) {
if(g_strstr_len(g_ascii_strdown(interface_name, -1), -1, "device")) {
int i;
char *tmp = g_strstr_len(object, -1, "dev_") + 4;
for(i = 0; *tmp != '\0'; i++, tmp++) {
if(*tmp == '_') {
address[i] = ':';
continue;
}
address[i] = *tmp;
}
g_print("\nDevice %s removed\n", address);
g_main_loop_quit((GMainLoop *)user_data);
}
}
return;
}
static void bluez_signal_adapter_changed(GDBusConnection *conn,
const gchar *sender,
const gchar *path,
const gchar *interface,
const gchar *signal,
GVariant *params,
void *userdata)
{
(void)conn;
(void)sender;
(void)path;
(void)interface;
(void)userdata;
GVariantIter *properties = NULL;
GVariantIter *unknown = NULL;
const char *iface;
const char *key;
GVariant *value = NULL;
const gchar *signature = g_variant_get_type_string(params);
if(strcmp(signature, "(sa{sv}as)") != 0) {
g_print("Invalid signature for %s: %s != %s", signal, signature, "(sa{sv}as)");
goto done;
}
g_variant_get(params, "(&sa{sv}as)", &iface, &properties, &unknown);
while(g_variant_iter_next(properties, "{&sv}", &key, &value)) {
if(!g_strcmp0(key, "Powered")) {
if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
g_print("Invalid argument type for %s: %s != %s", key,
g_variant_get_type_string(value), "b");
goto done;
}
g_print("Adapter is Powered \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
}
if(!g_strcmp0(key, "Discovering")) {
if(!g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
g_print("Invalid argument type for %s: %s != %s", key,
g_variant_get_type_string(value), "b");
goto done;
}
g_print("Adapter scan \"%s\"\n", g_variant_get_boolean(value) ? "on" : "off");
}
}
done:
if(properties != NULL)
g_variant_iter_free(properties);
if(value != NULL)
g_variant_unref(value);
}
static int bluez_adapter_set_property(const char *prop, GVariant *value)
{
GVariant *result;
GError *error = NULL;
result = g_dbus_connection_call_sync(con,
"org.bluez",
"/org/bluez/hci0",
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new("(ssv)", "org.bluez.Adapter1", prop, value),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if(error != NULL)
return 1;
g_variant_unref(result);
return 0;
}
static int bluez_adapter_connect_device(char **argv)
{
int rc;
GVariantBuilder *b = g_variant_builder_new(G_VARIANT_TYPE_VARDICT);
g_variant_builder_add(b, "{sv}", "Address", g_variant_new_string(argv[1]));
GVariant *device_dict = g_variant_builder_end(b);
g_variant_builder_unref(b);
rc = bluez_adapter_call_method("ConnectDevice",
g_variant_new_tuple(&device_dict, 1),
bluez_result_async_cb);
if(rc) {
g_print("Not able to call ConnectDevice\n");
return 1;
}
return 0;
}
#define AGENT_PATH "/org/bluez/AutoPinAgent"
static int bluez_agent_call_method(const gchar *method, GVariant *param)
{
GVariant *result;
GError *error = NULL;
result = g_dbus_connection_call_sync(con,
"org.bluez",
"/org/bluez",
"org.bluez.AgentManager1",
method,
param,
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if(error != NULL) {
g_print("Register %s: %s\n", AGENT_PATH, error->message);
return 1;
}
g_variant_unref(result);
return 0;
}
static int bluez_register_autopair_agent(void)
{
int rc;
rc = bluez_agent_call_method("RegisterAgent", g_variant_new("(os)", AGENT_PATH, "NoInputNoOutput"));
if(rc)
return 1;
rc = bluez_agent_call_method("RequestDefaultAgent", g_variant_new("(o)", AGENT_PATH));
if(rc) {
bluez_agent_call_method("UnregisterAgent", g_variant_new("(o)", AGENT_PATH));
return 1;
}
return 0;
}
static void cleanup_handler(int signo)
{
if (signo == SIGINT) {
g_print("received SIGINT\n");
g_main_loop_quit(loop);
}
}
int main(int argc, char **argv)
{
int rc;
guint prop_changed;
guint iface_added;
guint iface_removed;
if(signal(SIGINT, cleanup_handler) == SIG_ERR)
g_print("can't catch SIGINT\n");
con = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL);
if(con == NULL) {
g_print("Not able to get connection to system bus\n");
return 1;
}
loop = g_main_loop_new(NULL, FALSE);
prop_changed = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
NULL,
"org.bluez.Adapter1",
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_signal_adapter_changed,
NULL,
NULL);
iface_added = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.ObjectManager",
"InterfacesAdded",
NULL,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_device_appeared,
loop,
NULL);
iface_removed = g_dbus_connection_signal_subscribe(con,
"org.bluez",
"org.freedesktop.DBus.ObjectManager",
"InterfacesRemoved",
NULL,
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
bluez_device_disappeared,
loop,
NULL);
rc = bluez_adapter_set_property("Powered", g_variant_new("b", TRUE));
if(rc) {
g_print("Not able to enable the adapter\n");
goto fail;
}
rc = bluez_register_autopair_agent();
if(rc) {
g_print("Not able to register default autopair agent\n");
goto fail;
}
if(argc == 2) {
rc = bluez_adapter_connect_device(argv);
if(rc)
goto fail;
}
g_main_loop_run(loop);
rc = bluez_adapter_set_property("Powered", g_variant_new("b", FALSE));
if(rc)
g_print("Not able to disable the adapter\n");
fail:
g_dbus_connection_signal_unsubscribe(con, prop_changed);
g_dbus_connection_signal_unsubscribe(con, iface_added);
g_dbus_connection_signal_unsubscribe(con, iface_removed);
g_object_unref(con);
return 0;
}
you should be able to use the above program to connect the device. Here in this example, the agent is registered as "NoInputOutput" capability, something like bluetooth headphones, so that no pairing response is required.
But you should modify this example to client side (assuming this example going to run in RPi 1 as server, modify this to accept request in client side RPi 2).
You can find the detailed explanation about this example here and also some relevant GDBUS based examples here.
I am writing kqueue hooks for a character device that allows a client to block waiting for an EVFILT_READ. If I set my read filters code to always return one the kevent will return instantly. However, if the filter returns one at some later point in time nothing unblocks. For the following code the printf "After" never happens and in the filter code I can trivially get "filter_Read return 1" (immediately followed by a return 0)
Device (relevant excerpt)
static int
lowmem_filter_read(struct knote *kn, long hint)
{
mtx_assert(&lowmem_mtx, MA_OWNED);
if(manual_alert){
manual_alert=0;
printf("filter_Read return 1\n");
return 1;
}
printf("filter_Read return 0\n");
return 0;
}
static void
lowmem_filter_detach(struct knote *kn)
{
mtx_assert(&lowmem_mtx, MA_OWNED);
knlist_remove(&kl, kn, 0);
}
static struct filterops lowmem_filtops_read = {
.f_isfd = 1,
.f_detach = lowmem_filter_detach,
.f_event = lowmem_filter_read,
};
static int
lowmem_kqfilter(struct cdev *dev, struct knote *kn)
{
int err = EINVAL;
/* Figure out who needs service */
lowmem_lock();
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &lowmem_filtops_read;
knlist_add(&kl, kn, 1);
err = 0;
break;
default:
err = EOPNOTSUPP;
break;
}
lowmem_unlock();
return (err);
}
Client:
struct kevent ev;
struct timespec nullts = {0,0};
int fd=0;
int main(int argc, char **argv){
fd = open("/dev/lowmem", O_RDWR | O_NONBLOCK);
int kq=kqueue();
EV_SET(&ev,fd,EVFILT_READ, EV_ADD,0,0,NULL);
kevent(kq,&ev,1,NULL,0,&nullts);
for(;;){
printf("Starting\n");
int n=kevent(kq,NULL,0,&ev,1,NULL);
printf("After\n");
if(n>0){
printf("Something happened ev.fflags=%i\n",(int)ev.fflags);
}
}
return 0;
}
I am quite new to std::thread and I quickly realized that creating them is quite costly at least on my computer running W7.
So I decided to create my threads and send jobs to it using that piece of sample code: http://en.cppreference.com/w/cpp/thread/condition_variable
My code runs well no crash however I didn't notice much performance increase so I measured the difference between the time the job finishes and the time the job is detected finished by the main thread ( see WaitUntilJobFinished() ) I noticed that on some rare occasions the time difference was over 2 milliseconds
Does anyone see anything wrong with the code?
Code:
class CJobParameters
{
public:
};
typedef void (*CJobFunc)( const CJobParameters * );
class CThread
{
public:
void Start();
void WaitUntilJobDone();
void StartJob( CJobFunc inJobFunc, const CJobParameters * inJobParameters );
std::thread m_stdThread;
CJobFunc m_jobFunc = nullptr;
const CJobParameters * m_jobParameters = nullptr;
//std::atomic<bool> m_jobDone = true;
std::mutex m_mutex;
std::condition_variable m_cv;
__int64 m_jobDoneAt = 0;
__int64 m_threadJoinedAt = 0;
__int64 m_lostTime = 0;
};
class CThreads
{
public:
static void Start();
static CThread threadArray[ JOB_COUNT ];
};
void ThreadMain( CThread * inThread )
{
while ( true )
{
std::unique_lock<std::mutex> lk( inThread->m_mutex );
inThread->m_cv.wait(lk, [ inThread ]{return inThread->m_jobParameters != nullptr;});
if ( inThread->m_jobFunc )
{
(*inThread->m_jobFunc)( inThread->m_jobParameters );
inThread->m_jobFunc = nullptr;
inThread->m_jobParameters = nullptr;
inThread->m_jobDoneAt = COSToolbox::QuerySystemTime2();
}
lk.unlock();
inThread->m_cv.notify_one();
std::this_thread::sleep_for( std::chrono::nanoseconds(0) );
}
}
void CThread::StartJob( CJobFunc inJobFunc, const CJobParameters * inJobParameters )
{
std::lock_guard<std::mutex> lk( m_mutex );
m_jobFunc = inJobFunc;
m_jobParameters = inJobParameters;
m_cv.notify_one();
}
void CThread::Start()
{
m_stdThread = std::thread( ThreadMain, this );
}
void CThread::WaitUntilJobDone()
{
std::unique_lock<std::mutex> lk( m_mutex );
m_cv.wait(lk, [ this ]{return this->m_jobParameters == nullptr;});
m_threadJoinedAt = COSToolbox::QuerySystemTime2();
m_lostTime = m_threadJoinedAt - m_jobDoneAt;
LOG_INFO( "Thread joined with %f ms lost", (Float32)m_lostTime / 1000 );
}
CThread CThreads::threadArray[ JOB_COUNT ];
void CThreads::Start()
{
for ( Int32 i = 0; i < JOB_COUNT; ++i )
{
threadArray[i].Start();
}
}
void MyJobFunc( const CJobParameters * jobParameters )
{
// do job here
}
void main()
{
CThreads::Start();
while(true)
{
CJobParameters jobParametersArray[ JOB_COUNT ];
for ( Int32 i = 0; i < JOB_COUNT; ++i )
{
CThread & thread = CThreads::threadArray[ i ];
CJobParameters& jobParameters = jobParametersArray[ i ];
jobParameters.m_ // Fill in params
thread.StartJob( &MyJobFunc, &jobParameters );
}
for ( Int32 i = 0; i < JOB_COUNT; ++i )
{
CThread & thread = CThreads::threadArray[ i ];
// Prints 2 ms sometimes whith i = 0
thread.WaitUntilJobDone();
}
}
}
Two things:
You are yielding your processor time unconditionally, and on some older versions of windows, you yield the entire process, not just thread:
std::this_thread::sleep_for( std::chrono::nanoseconds(0) );
this yield is unnecessary. I suspect the reason you're doing it is that without it you were getting a spin loop, resulting from the fact that you are both reading and writing to a single condition variable.
You need two condition variables, one for work pending and one for work done. Typically the listener will pass the condition variable or a struct containing it as a parameter to the thread function, allowing you to pass a single condition variable from your dispatcher.
I am using third party API which I get streams from a callback function
int OnNewImage(BYTE *pData, int nLen)
When I run a simple C++ sample program from console
int continue = 1;
int OnNewImage(BYTE *pData, int nLen)
{
std::cout << "On new image is called" << std::endl;
return continue;
}
int main()
{
// This will block
int result = DownloadStream(/*params*/...,OnNewImage /*callbackfunction*/);
return 0;
}
I get no memory leaks.[ memory does not increase ]
But When I use this callback function in a directshow filter, it
produce memory leaks.[ memory increase regularly]
What may cause this? And how can I fix it? Any ideas?
UPDATE: My DirectShow Filter structure
What I do:
Basically
I get streams at "unsigned __stdcall DVRStreamThread(LPVOID
pvParam)" function which call back OnNewImage
Then i insert frames into my queue inside that callback[OnNewImage]
Finally At FillBuffer I consume frames from queue.
It is am h264 stream. I can able to set simple graph like this
MySourceFilter ---> H264 Decoder ---> Video Renderer
Here is my FilterSourceCode:
Well I have a simple queue which i insert incoming frames then consume:
SynchronisedQueue
template <typename T>
class SynchronisedQueue
{
public:
void Enqueue(const T& data)
{
boost::unique_lock<boost::mutex> lock(queueMutex);
dataQueue.push(data);
conditionVariable.notify_one();
}
T Dequeue()
{
boost::unique_lock<boost::mutex> lock(queueMutex);
while (dataQueue.size()==0)
{
conditionVariable.wait(lock);
}
T result=dataQueue.front(); dataQueue.pop();
return result;
}
int Size()
{
boost::unique_lock<boost::mutex> lock(queueMutex);
int size = dataQueue.size();
return size;
}
private:
std::queue<T> dataQueue;
boost::mutex queueMutex;
boost::condition_variable conditionVariable;
};
Then My Filter:
DvrSourceFilter [ header]
#define DVRSourceFilterName L"DVRDirectShowFilter"
#include <streams.h>
#include <process.h>
#include <MyDvrApi.h>
#include "SynchronisedQueue.h"
// {F89A85DA-F77C-4d44-893B-CCA43A49E7EF}
DEFINE_GUID(CLSID_DVRSourceFilter,
0xf89a85da, 0xf77c, 0x4d44, 0x89, 0x3b, 0xcc, 0xa4, 0x3a, 0x49, 0xe7, 0xef);
class DECLSPEC_UUID("34363248-0000-0010-8000-00AA00389B71") Subtype_H264;
class DVRSourceFilter;
using namespace std;
/*
* **********************
* DVRPin
* **********************
*/
class DVRPin : public CSourceStream
{
public:
DVRPin(HRESULT *phr, DVRSourceFilter *pFilter);
~DVRPin();
// Override the version that offers exactly one media type
HRESULT GetMediaType(CMediaType *pMediaType);
HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pRequest);
HRESULT FillBuffer(IMediaSample *pSample);
static int OnNewImage(void *pUser, BYTE *pData, int nLen, int nCh, int tMts, int nType, void *returnHandle);
// Setters
void SetDvrIp(char* dvrIp);
void SetDvrPort( int dvrPort);
void SetDvrUserName( char * userName);
void SetDvrPassword(char* password);
void SetStartTime(int startTime);
void SetMilliSecond(int milliSecond);
void SetChannelNumber(int channelNumber);
void SetSize(int width, int height);
// Getters
char* GetDvrIp();
int GetDvrPort();
char* GetDvrUserName();
char* GetDvrPassword();
int GetStartTime();
int GetMilliSecond();
int GetChannelNumber();
int GetMode();
public:
char* dvrIp;
int dvrPort;
char* userName;
char* password;
int startTime;
int milliSecond;
int channelNumber;
BITMAPINFOHEADER m_bmpInfo;
BYTE* m_RGB24Buffer;
DWORD m_RGB24BufferSize;
bool streamCompleted;
int hDecHandle;
HANDLE m_hDVRStreamThreadHandle;
unsigned int m_dwThreadID;
SynchronisedQueue<std::vector<BYTE>> IncomingFramesQueue;
protected:
virtual HRESULT OnThreadCreate();
virtual HRESULT OnThreadDestroy();
virtual HRESULT DoBufferProcessingLoop();
};
/*
* **********************
* DVRSourceFilter
* *********************
*
*/
class DVRSourceFilter : public CSource
{
public:
DECLARE_IUNKNOWN;
static CUnknown * WINAPI CreateInstance(IUnknown *pUnk, HRESULT *phr);
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv);
void SetDVRLiveParameters(char* dvrIP, int dvrPort, char* userName, char* password, int channelNumber, int width, int height);
private:
DVRSourceFilter(IUnknown *pUnk, HRESULT *phr);
~DVRSourceFilter();
private:
DVRPin *m_pPin;
};
DvrSourceFilter [implementation]
#include "DvrSourceFilter.h"
unsigned __stdcall DVRStreamThread(LPVOID pvParam)
{
DVRPin* streamReader = (DVRPin*)pvParam;
int channelBits = 1 << (streamReader->channelNumber - 1);
streamReader->m_RGB24BufferSize = streamReader->m_bmpInfo.biWidth * streamReader->m_bmpInfo.biHeight * 3;
streamReader->m_RGB24Buffer = (BYTE*)malloc(streamReader->m_RGB24BufferSize);
DownloadStream((LPCTSTR)streamReader->dvrIp,
streamReader->dvrPort , (LPCTSTR)streamReader->userName ,
(LPCTSTR)streamReader->password , channelBits, channelBits,
streamReader->startTime, streamReader->milliSecond,
streamReader->OnNewImage, (void*)streamReader);
streamReader->startTime = -2; // End Of Stream
return 0;
}
/*
* ******************
* DVRPin Class
* ******************
*/
DVRPin::DVRPin(HRESULT *phr, DVRSourceFilter *pFilter)
: CSourceStream(NAME("DVR Source Bitmap"), phr, pFilter, L"Out")
{
m_bmpInfo.biSize = sizeof(BITMAPINFOHEADER);
m_bmpInfo.biCompression = BI_RGB;
m_bmpInfo.biBitCount = 24;
m_bmpInfo.biPlanes = 1;
m_bmpInfo.biClrImportant = 0;
m_bmpInfo.biClrUsed = 0;
m_bmpInfo.biXPelsPerMeter = 0;
m_bmpInfo.biYPelsPerMeter = 0;
hDecHandle = 0;
m_RGB24Buffer = NULL;
m_RGB24BufferSize = 0;
streamCompleted = false;
startTime = -1; // Live Stream
*phr = S_OK;
}
DVRPin::~DVRPin()
{
}
int DVRPin::OnNewImage(void *pUser, BYTE *pData, int nLen, int nCh, int tMts, int nType, void *returnHandle)
{
DVRPin* reader = (DVRPin*)pUser;
if(reader->streamCompleted)
{
return false;
}
if(pData)
{
std::vector<BYTE> vecFrame(pData, pData + nLen/sizeof(pData[0]));
reader->IncomingFramesQueue.Enqueue(vecFrame);
}
return !reader->streamCompleted;
}
HRESULT DVRPin::OnThreadCreate()
{
m_hDVRStreamThreadHandle =
(HANDLE)_beginthreadex(NULL, 0, &DVRStreamThread, (void*)this, 0, &m_dwThreadID);
return S_OK;
}
HRESULT DVRPin::OnThreadDestroy() {
streamCompleted = true;
_endthreadex(0);
CloseHandle(m_hDVRStreamThreadHandle);
return S_OK;
}
HRESULT DVRPin::GetMediaType(CMediaType *pMediaType)
{
CAutoLock cAutoLock(m_pFilter->pStateLock());
CheckPointer(pMediaType, E_POINTER);
VIDEOINFOHEADER* pvi = (VIDEOINFOHEADER*)pMediaType->AllocFormatBuffer(sizeof(VIDEOINFOHEADER));
if (pvi == 0)
return(E_OUTOFMEMORY);
ZeroMemory(pvi, pMediaType->cbFormat);
pvi->bmiHeader = m_bmpInfo;
pvi->bmiHeader.biSizeImage = GetBitmapSize(&pvi->bmiHeader);
SetRectEmpty(&(pvi->rcSource));
SetRectEmpty(&(pvi->rcTarget));
pMediaType->SetType(&MEDIATYPE_Video);
pMediaType->SetFormatType(&FORMAT_VideoInfo);
pMediaType->SetTemporalCompression(FALSE);
// Work out the GUID for the subtype from the header info.
const GUID SubTypeGUID = __uuidof(Subtype_H264);//GetBitmapSubtype(&pvi->bmiHeader);
pMediaType->SetSubtype(&SubTypeGUID);
pMediaType->SetSampleSize(pvi->bmiHeader.biSizeImage);
return S_OK;
}
HRESULT DVRPin::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pRequest)
{
HRESULT hr;
CAutoLock cAutoLock(m_pFilter->pStateLock());
CheckPointer(pAlloc, E_POINTER);
CheckPointer(pRequest, E_POINTER);
VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER*) m_mt.Format();
if (pRequest->cBuffers == 0)
{
pRequest->cBuffers = 2;
}
pRequest->cbBuffer = pvi->bmiHeader.biSizeImage;
ALLOCATOR_PROPERTIES Actual;
hr = pAlloc->SetProperties(pRequest, &Actual);
if (FAILED(hr))
{
return hr;
}
if (Actual.cbBuffer < pRequest->cbBuffer)
{
return E_FAIL;
}
return S_OK;
}
HRESULT DVRPin::FillBuffer(IMediaSample *pSample)
{
if(!streamCompleted)
{
CAutoLock cAutoLock(m_pLock);
HRESULT hr;
BYTE* pData = NULL;
hr = pSample->GetPointer(&pData);
if(FAILED(hr))
{
pSample->Release();
return hr;
}
if(IncomingFramesQueue.Size() <= 0) {
return S_OK;
}
vector<BYTE> data = IncomingFramesQueue.Dequeue();
int dataSize = (int)data.size();
if(dataSize <= 0 || dataSize > 1000000)
{
return S_OK;
}
memcpy(pData, &data[0], dataSize);
hr = pSample->SetActualDataLength(dataSize);
if(FAILED(hr))
{
pSample->Release();
return hr;
}
hr = pSample->SetSyncPoint(TRUE);
if(FAILED(hr))
{
pSample->Release();
return hr;
}
pSample->Release();
}
return S_OK;
}
HRESULT DVRPin::DoBufferProcessingLoop() {
Command com;
REFERENCE_TIME rtNow = 0L;
REFERENCE_TIME rtAdvise = 0L;
OnThreadStartPlay();
do {
while (!streamCompleted && !CheckRequest(&com)) {
IncomingFramesQueue.WaitUntilHaveElements();
IMediaSample *pSample;
HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,FALSE);
if (FAILED(hr)) {
continue; // go round again. Perhaps the error will go away
// or the allocator is decommited & we will be asked to
// exit soon.
}
hr = FillBuffer(pSample);
if (hr == S_OK) {
Deliver(pSample);
} else if (hr == S_FALSE) {
pSample->Release();
DeliverEndOfStream();
return S_FALSE;
} else {
// Log Error
}
pSample->Release();
}
if (com == CMD_RUN || com == CMD_PAUSE)
com = GetRequest(); // throw command away
else if (com != CMD_STOP)
{
// Log Error
}
} while (!streamCompleted && com != CMD_STOP);
return S_OK;
}
void DVRPin::SetDvrIp( char* dvrIp )
{
this->dvrIp = dvrIp;
}
void DVRPin::SetDvrPort( int dvrPort )
{
this->dvrPort = dvrPort;
}
void DVRPin::SetDvrUserName( char * userName )
{
this->userName = userName;
}
void DVRPin::SetDvrPassword( char* password )
{
this->password = password;
}
void DVRPin::SetStartTime( int startTime )
{
this->startTime = startTime;
}
void DVRPin::SetMilliSecond( int milliSecond )
{
this->milliSecond = milliSecond;
}
void DVRPin::SetSize(int width, int height) {
m_bmpInfo.biWidth = width;
m_bmpInfo.biHeight = height;
m_bmpInfo.biSizeImage = GetBitmapSize(&m_bmpInfo);
}
char* DVRPin::GetDvrIp()
{
return dvrIp;
}
int DVRPin::GetDvrPort()
{
return dvrPort;
}
char* DVRPin::GetDvrUserName()
{
return userName;
}
char* DVRPin::GetDvrPassword()
{
return password;
}
int DVRPin::GetStartTime()
{
return startTime;
}
int DVRPin::GetMilliSecond()
{
return milliSecond;
}
void DVRPin::SetChannelNumber( int channelNumber )
{
this->channelNumber = channelNumber;
}
int DVRPin::GetChannelNumber()
{
return channelNumber;
}
/*
* ****************************
* DVRSourceFilter Class
* ***************************
*/
DVRSourceFilter::DVRSourceFilter(IUnknown *pUnk, HRESULT *phr)
: CSource(NAME("DVRSourceBitmap"), pUnk, CLSID_DVRSourceFilter)
{
// The pin magically adds itself to our pin array.
m_pPin = new DVRPin(phr, this);
// Just for test at graph studio
SetDVRLiveParameters("192.168.3.151", 7000, "admin", "000000", 3, 352, 288);
if (phr)
{
if (m_pPin == NULL)
*phr = E_OUTOFMEMORY;
else
*phr = S_OK;
}
}
DVRSourceFilter::~DVRSourceFilter()
{
delete m_pPin;
}
CUnknown * WINAPI DVRSourceFilter::CreateInstance(IUnknown *pUnk, HRESULT *phr)
{
DVRSourceFilter *pNewFilter = new DVRSourceFilter(pUnk, phr);
if (phr)
{
if (pNewFilter == NULL)
*phr = E_OUTOFMEMORY;
else
*phr = S_OK;
}
return pNewFilter;
}
STDMETHODIMP DVRSourceFilter::NonDelegatingQueryInterface( REFIID riid, void **ppv )
{
return CSource::NonDelegatingQueryInterface(riid, ppv);
}
void DVRSourceFilter::SetDVRLiveParameters( char* dvrIP, int dvrPort, char* userName, char* password, int channelNumber, int width, int height )
{
m_pPin->SetDvrIp(dvrIP);
m_pPin->SetDvrPort(dvrPort);
m_pPin->SetDvrUserName(userName);
m_pPin->SetDvrPassword(password);
m_pPin->SetChannelNumber(channelNumber);
m_pPin->SetStartTime(-1);// Live Stream
m_pPin->SetMilliSecond(0);
m_pPin->SetSize(width, height);
}
...
To make directshow Filter simple [ to understand memory leak source], just implement OnNewImage function and FillBufferFunction as "dummy", but still has memory leak:
int DVRPin::OnNewImage(void *pUser, BYTE *pData, int nLen, int nCh, int tMts, int nType, void *returnHandle)
{
return 1; // for not to end call back
}
HRESULT DVRPin::FillBuffer(IMediaSample *pSample)
{
pSample->Release();
return S_OK;
}
In DVRStreamThread, you have:
streamReader->m_RGB24Buffer = (BYTE*)malloc(streamReader->m_RGB24BufferSize);
But I don't see a matching call to free() anywhere. When your DVRPin object is deleted, you will have to explicitly free the data pointed to by its m_RGB24Buffer member.
First thing I see, is that your destructors are not virtual. This might be a cause of leaks when release is not taking place when inheritance is used. See related article about the necessity of virtual destructors.