How does a "Proxy" work in DBus Bluez API? - bluetooth

I'm confused on how a proxy works in BlueZ DBus API. Currently, I'm writing a function that should read the characteristic value of a specific service.
In these function:
static void print_string(const char *str, void *user_data)
{
printf("%s\n", str);
}
void bt_shell_hexdump(const unsigned char *buf, size_t len)
{
util_hexdump(' ', buf, len, print_string, NULL);
}
static void read_reply(DBusMessage *message, void *user_data)
{
DBusError error;
DBusMessageIter iter, array;
uint8_t *value;
int len;
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE)
{
printf("Failed to read: %s\n", error.name);
dbus_error_free(&error);
return;
}
dbus_message_iter_init(message, &iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
{
printf("Invalid response to read\n");
return;
}
dbus_message_iter_recurse(&iter, &array);
dbus_message_iter_get_fixed_array(&array, &value, &len);
if (len < 0)
{
printf("Unable to parse value\n");
return;
}
bt_shell_hexdump(value, len);
return;
}
static void read_setup(DBusMessageIter *iter, void *user_data)
{
DBusMessageIter dict;
uint16_t *offset = user_data;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_STRING_AS_STRING
DBUS_TYPE_VARIANT_AS_STRING
DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
&dict);
g_dbus_dict_append_entry(&dict, "offset", DBUS_TYPE_UINT16, offset);
dbus_message_iter_close_container(iter, &dict);
}
static void read_attribute(GDBusProxy *proxy, uint16_t offset)
{
if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup, read_reply, &offset, NULL) == FALSE)
{
printf("Failed to read\n");
return;
}
printf("Attempting to read %s\n", g_dbus_proxy_get_path(proxy));
}
void gatt_read_attribute(void)
{
GDBusProxy *proxy;
const char *iface;
int offset = 0;
iface = g_dbus_proxy_get_interface(proxy);
printf("Iface: %s\n", iface);
if (!strcmp(iface, "org.bluez.GattCharacteristic1") || !strcmp(iface, "org.bluez.GattDescriptor1"))
{
read_attribute(proxy, offset);
return;
}
return;
}
I setup the proxy but reading the "iface" value returns NULL. Is there a way for me to set the specific interface I want the proxy for?
Can someone explain how these proxy's work?
I'm new to BlueZ DBus API. The code snippet is from BlueZ client.
Thank you

Related

How do i print the char value from file c program?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 128
const char *FILE_GAME_DATA_PATH = "./game.txt";
struct game_tag
{
char gname[20];
struct game_tag *next;
} * head;
//struct game_tag g;
typedef struct game_tag GAME;
//file functions
void read_file();
//util functions.
int menu();
void print_game(GAME *game);
void release(GAME *data);
//core
void display_game();
void quite();
//link
int count_elements(GAME *elements);
int count();
int main(void)
{
int selected;
read_file();
while (1)
{
selected = menu();
switch (selected)
{
case 1:
display_game();
break;
default:
printf("cannot find your option!");
break;
}
}
}
void display_game()
{
read_file();
GAME *game = head;
if(game == NULL)
{
printf("\nNo Game!\n");
return;
}
print_game(game);
}
void print_game(GAME *game)
{
int records_count = 0;
printf("\n========== GAME ==========\n");
while(game != NULL)
{
printf("\n");
printf("Game Name: %s\n ", game->gname);
game = game->next;
records_count++;
}
printf("\nRecords: %d has been loaded successfully!\n", records_count);
release(game);
}
int menu()
{
printf("\n(1) Display Game details\n");
int choosen;
printf("\nEnter your option: ");
scanf("%d", &choosen);
return choosen;
}
void add_game(char game_name[20])
{
GAME *temp, *iterator;
temp = (struct game_tag *)malloc(sizeof(struct game_tag));
GAME info;
memcpy(info.gname, game_name, 20);
//temp = head;
iterator = head;
if (head == NULL)
{
head = temp;
head->next = NULL;
}
else
{
while (iterator->next != NULL)
{
iterator = iterator->next;
}
temp->next = NULL;
iterator->next = temp;
}
}
void read_file()
{
if(head != NULL)
{
GAME *temp;
while(head != NULL)
{
temp = head;
head = head->next;
free(temp);
}
}
FILE *file;
file = fopen(FILE_GAME_DATA_PATH, "r");
if(file == NULL)
{
printf("Cannot read file: %s", FILE_GAME_DATA_PATH);
exit(EXIT_FAILURE);
}
char game_name[20];
int i;
while(!feof(file))
{
char no[BUFFER_SIZE];
fgets(game_name, sizeof(game_name), file);
i=0;
while(game_name[i] != '\0')
{
i++;
}
game_name[i] = '\0';
add_game(game_name);
}
fclose(file);
}
void quite()
{
printf("\nGoodbye!");
exit(EXIT_SUCCESS);
}
void release(GAME *data)
{
if (data == NULL)
{
return;
}
// free the nodes
// because it can be use in memory
// we need to clear it first
// before we re-initailize the new data
GAME *temp;
while (data != NULL)
{
temp = data;
data = data->next;
free(temp);
}
}
(1) At this point, I have created the main function
(2) I'm trying to print the value from txt file which is char value that less than 20byte
(3) My error is it read the file properly, but it returns the garbage value
!Error I'm concern that my read_file function doesn't read txt proper or there an error in my condition statement

libuv based server crash when there are two connections back to back

Doing some research on high performance TCP server (not necessarily for HTTP) for testing purpose. Followed some sample code and created a libuv based TCP server, however the following sample code will crash with error SIGPIPE on the line uv_write(req, stream, buffer, 1, on_write);. Here is the console output:
client=0x562529924700
1writing to 0x562529924700
2writing to 0x562529924700
wrote.
1writing to 0x562529924700
Note that the tcp client closes the first tcp connection with TCP RESET. Wireshark shows that the TCP client has established the second tcp connection and sent the request, but the tcp server somehow didn't print something similar to the line client=0x562529924700 to indicate that it has accept the second connection.
Any ideas would be appreciated.
Here is the server code
//from https://gist.github.com/Jxck/4305806
// https://nikhilm.github.io/uvbook/networking.html
//api guide http://docs.libuv.org/en/v1.x/
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
#include <string.h>
#include <unistd.h>
#define DEFAULT_PORT 8080
#define DEFAULT_BACKLOG 1000
uv_loop_t *loop;
int cnt = 0;
char *str = "HTTP/1.1 200 Ok\r\nContent-Length: 7\r\n\r\n%07d\n";
void on_write(uv_write_t* req, int status)
{
if (status) {
perror( "uv_write error ");
return;
}
printf("wrote.\n");
free(req);
//uv_close((uv_handle_t*)req->handle, on_close);
}
void write2(uv_stream_t* stream, char *data, int len2) {
uv_buf_t buffer[] = {
{.base = data, .len = len2}
};
uv_write_t *req = malloc(sizeof(uv_write_t));
printf("1writing to %p\n", stream);
uv_write(req, stream, buffer, 1, on_write);
printf("2writing to %p\n", stream);
}
void on_close(uv_handle_t* handle)
{
//printf("closed.");
}
void echo_read(uv_stream_t *sock, ssize_t nread, const uv_buf_t *buf) {
if (nread == -1) {
fprintf(stderr, "error echo_read");
return;
} else if (nread == 0) {
uv_close((uv_handle_t*)sock, on_close);
return;
}
char respMsg[52];
sprintf(respMsg, str, cnt++);
//snprintf(&respMsg[41], 6,"%6d", cnt++);
write2(sock, respMsg, strlen(respMsg));
//printf("result: %s\n", buf->base);
}
static void my_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
buf->base = malloc(suggested_size);
buf->len = suggested_size;
}
void on_new_connection(uv_stream_t *server, int status) {
if (status < 0) {
fprintf(stderr, "New connection error %s\n", uv_strerror(status));
// error!
return;
}
uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t));
printf("client=%p\n", client);
uv_tcp_init(loop, client);
if (uv_accept(server, (uv_stream_t*) client) == 0) {
uv_read_start((uv_stream_t*) client, my_alloc_cb, echo_read);
}
else {
printf("failed to accept\n");
uv_close((uv_handle_t*) client, NULL);
}
}
int main() {
loop = uv_default_loop();
uv_tcp_t server;
uv_tcp_init(loop, &server);
struct sockaddr_in addr;
uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr);
int r;
r = uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
r = uv_listen((uv_stream_t*) &server, DEFAULT_BACKLOG, on_new_connection);
if (r) {
fprintf(stderr, "Listen error %s\n", uv_strerror(r));
return 1;
}
return uv_run(loop, UV_RUN_DEFAULT);
}
Turned out the issue is on echo_read(). When the TCP connection is terminated with tcp reset, nread is not -1, it's some negative number (-4095 in my case).
Was able to take care of it using nread < 0 instead of nread == -1.
void echo_read(uv_stream_t *sock, ssize_t nread, const uv_buf_t *buf) {
if (nread == -1) {
fprintf(stderr, "error echo_read");
return;
} else if (nread == 0) {
uv_close((uv_handle_t*)sock, on_close);
return;
}

bluez pairing before exchange

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.

How to directly deliver packets to the L4 layer in the NF_IP_PRE_ROUTING hook

I want to directly deliver some packets to the L4 layer when the
packets get to the hook of NF_IP_PRE_ROUTING. I use the use
ip_local_deliver() function. However, it does not work. May I know how
I can make it work. Thank you!
Best Regards,
Lawrence
Thanks for the suggestion! My code is in the following:
const char* hooks[] = {"NF_IP_PRE_ROUTING"};
unsigned int
header(unsigned int hooknum,
struct sk_buff* skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff*))
{
struct sk_buff* nskb;
struct iphdr *iph = NULL;
nskb = skb;
if(nskb==NULL)
{
printk("%s\n", "*skb is NULL");
return NF_ACCEPT;
}
iph = ip_hdr(nskb);
if(iph == NULL)
{
printk("%s\n", "*iph is NULL");
return NF_ACCEPT;
}
if ((iph->protocol == IPPROTO_UDP) || (iph->protocol == IPPROTO_ICMP)){
ip_local_deliver(nskb);
printk("------delivered --------\n");
return NF_STOLEN;
}
return NF_ACCEPT;
}
static struct nf_hook_ops header_ops[] = {
{
{
.hook = header,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = 0, //NF_IP_PRE_ROUTING,
.priority = NF_IP_PRI_FIRST,
},
};
static int __init init(void)
{
int ret;
ret = nf_register_hooks(header_ops, ARRAY_SIZE(header_ops));
if (ret < 0) {
printk("http detect:can't register header_ops detect hook!\n");
return ret;
}
printk("insmod header_ops detect module\n");
return 0;
}
static void __exit fini(void)
{
nf_unregister_hooks(header_ops, ARRAY_SIZE(header_ops));
printk("remove header_ops detect module.\n");
}
module_init(init);
module_exit(fini);

Using callback function in a directshow filter cause memory leaks

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.

Resources