As far as I understand, in BLE, UUIDs are universal IDs that serve to uniquely identify a BLE attribute. They can be 2, 4 or 128 bytes. When they are 2 or 4, the remaining bits until 128 are filled with a standard BLE base UUID: 0000-1000-8000-00805f9b34fb. Is this correct?
Having UUIDs, why are handles needed? From what I have seen in my example, they are displayed as part of the attribute 'identifier', e.g.
# bluetoothctl capture
Primary Service (Handle 0x0920)
/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0028
000000ff-0000-1000-8000-00805f9b34fb
Unknown
According to the logs I'm printing (which I will show below), the handle is 28. Is this correct? If so, what is the meaning of that 'Handle 0x0920'?
I have implemented a GATT server example in an ESP32 board. For now, I'm printing some logs to verify that the numbers I see (UUIDs, handles...) match those I see when connecting with bluetoothctl. Here are the logs:
I (1009) ESP32-DHT11: Service (inst. ID 0, uuid ff) created
I (1009) ESP32-DHT11: Service (inst. = 0, uuid = ff) started --> handle = 28
I (1009) ESP32-DHT11: Charac. (uuid ff01) added to service ff
I (1019) ESP32-DHT11: Charac. ff01 --> handle = 2a
I (1019) ESP32-DHT11: Descriptor (uuid 2902) added to service ff
I (1029) ESP32-DHT11: Charac. descr. 2902 --> handle = 2b
I (1029) ESP32-DHT11: Adv. parmeters set successfully
And here is the complete bluetoothctl capture:
Primary Service (Handle 0x0009)
/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0001
00001801-0000-1000-8000-00805f9b34fb
Generic Attribute Profile
Characteristic (Handle 0xae24)
/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0001/char0002
00002a05-0000-1000-8000-00805f9b34fb
Service Changed
Descriptor (Handle 0x0015)
/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0001/char0002/desc0004
00002902-0000-1000-8000-00805f9b34fb
Client Characteristic Configuration
Primary Service (Handle 0x0920)
/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0028
000000ff-0000-1000-8000-00805f9b34fb
Unknown
Characteristic (Handle 0x6c84)
/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0028/char0029
0000ff01-0000-1000-8000-00805f9b34fb
Unknown
Descriptor (Handle 0x0015)
/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0028/char0029/desc002b
00002902-0000-1000-8000-00805f9b34fb
Client Characteristic Configuration
I believe the first three attributes belong to the GAP layer and therefore can be ignored. Let's focus in the 3 next. As you can see, the UUIDs in my logs match those in the bluetoothctl capture. If we consider handles the last part of the attributes identifiers, all of them match save one (/org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0028/char0029 --> 0x29, which should be 0x2a according to my logs) Why does this happen?
What are exactly the path-like 'identifiers'? (e.g. /org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0028/char0029)
Code
The source code for this is quite long, so I attach it here at the end:
main.c
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_bt.h"
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_gatt_common_api.h"
#include "sdkconfig.h"
#include "app.h"
#define SENSOR_NAME "ESP32-DHT11"
#define TAG SENSOR_NAME
#define ARG_UNUSED(arg) ((void)arg)
static int16_t temp, hum;
static esp_attr_value_t sensor_data = {
.attr_max_len = (uint16_t)sizeof(temp),
.attr_len = (uint16_t)sizeof(temp),
.attr_value = (uint8_t*)(&temp),
};
static void gap_handler(esp_gap_ble_cb_event_t event,
esp_ble_gap_cb_param_t* param)
{
switch (event) {
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
esp_ble_gap_start_advertising(&adv_params);
break;
default:
break;
}
}
// TODO service_def is global and opaque.
static uint32_t short_uuid(esp_bt_uuid_t uuid)
{
switch (uuid.len) {
case 2:
return uuid.uuid.uuid16;
case 4:
return uuid.uuid.uuid32;
default:
return (uint32_t)-1;
}
}
/**
* #brief Handle for the event REG. Triggered when an app. (a.k.a. profile) is
* registered.
*
*/
static esp_err_t gatts_register_evt_handler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param)
{
esp_err_t rc = esp_ble_gatts_create_service(gatts_if,
&service_def.service_id,
GATT_HANDLE_COUNT);
if (rc != ESP_OK) {
return rc;
}
ESP_LOGI(TAG,
"Service (inst. ID %x, uuid %x) created",
service_def.service_id.id.inst_id,
short_uuid(service_def.service_id.id.uuid));
return rc;
}
/**
* #brief Handle for the event CREATE (triggered after creating a GATTS
* service). Stores the handle of the previously created service, starts it and
* adds a characteristic to it.
*
*/
static esp_err_t gatts_create_evt_handler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param)
{
ARG_UNUSED(event);
ARG_UNUSED(gatts_if);
service_def.service_handle = param->create.service_handle;
esp_err_t rc = esp_ble_gatts_start_service(service_def.service_handle);
if (rc != ESP_OK) {
return rc;
}
ESP_LOGI(TAG,
"Service (inst. = %x, uuid = %x) started --> handle = %x",
service_def.service_id.id.inst_id,
short_uuid(service_def.service_id.id.uuid),
service_def.service_handle);
rc = esp_ble_gatts_add_char(service_def.service_handle,
&service_def.char_uuid,
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
ESP_GATT_CHAR_PROP_BIT_READ
| ESP_GATT_CHAR_PROP_BIT_NOTIFY,
&sensor_data,
NULL);
if (rc != ESP_OK) {
return rc;
}
ESP_LOGI(TAG,
"Charac. (uuid %x) added to service %x",
short_uuid(service_def.char_uuid),
short_uuid(service_def.service_id.id.uuid));
return rc;
}
/**
* #brief Handles the event ADD_CHAR, which is triggered after a characteristic
* has been added successfully. When a charac. is added, its handle is generated
* (at runtime). This function gets it and stores it. Also, it adds a charac.
* descriptor to the service.
*
*/
static esp_err_t gatts_add_char_evt_handler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param)
{
ARG_UNUSED(event);
ARG_UNUSED(gatts_if);
service_def.char_handle = param->add_char.attr_handle;
ESP_LOGI(TAG,
"Charac. %x --> handle = %x",
short_uuid(service_def.char_uuid),
service_def.char_handle);
esp_err_t rc = esp_ble_gatts_add_char_descr(service_def.service_handle,
&service_def.descr_uuid,
ESP_GATT_PERM_READ
| ESP_GATT_PERM_WRITE,
NULL,
NULL);
if (rc != ESP_OK) {
return rc;
}
ESP_LOGI(TAG,
"Descriptor (uuid %x) added to service %x",
short_uuid(service_def.descr_uuid),
short_uuid(service_def.service_id.id.uuid));
return rc;
}
/**
* #brief Handles the event ADD_CHAR, which is triggered after a descriptor
* has been added successfully. It gets and stores such descriptor handle and
* overrides the default advertising data.
*
*/
static esp_err_t gatts_add_char_descr_evt_handler(
esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param)
{
ARG_UNUSED(event);
ARG_UNUSED(gatts_if);
service_def.descr_handle = param->add_char_descr.attr_handle;
ESP_LOGI(TAG,
"Charac. descr. %x --> handle = %x",
short_uuid(service_def.descr_uuid),
service_def.descr_handle);
esp_err_t rc = esp_ble_gap_config_adv_data(&adv_data);
if (rc != ESP_OK) {
return rc;
}
ESP_LOGI(TAG, "Adv. parmeters set successfully");
return rc;
}
/**
* #brief Handles the event CONNECT, which is triggered by a host connection.
* Updates the current connection params. with those from the incoming
* connection. Also, stores the incoming GATTS interface ID (which signals
* which service is being read) and connection ID.
*
* #todo Does gatts_if actually designate the app./profile rather than the
* service?
*
*/
static esp_err_t gatts_connect_evt_handler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param)
{
ARG_UNUSED(event);
update_conn_params(param->connect.remote_bda);
service_def.gatts_if = gatts_if;
service_def.client_write_conn = param->write.conn_id;
ESP_LOGI(TAG,
"Host connected, GATTS if. ID = %x, conn. ID = %x",
service_def.gatts_if,
service_def.client_write_conn);
return ESP_OK;
}
/**
* #brief Handles the event READ, which is triggered by a host read.
*
*/
static esp_err_t gatts_read_evt_handler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param)
{
ARG_UNUSED(event);
ESP_LOGI(TAG, "Read on %x detected", param->read.handle);
esp_gatt_rsp_t rsp = {0};
rsp.attr_value.handle = param->read.handle;
rsp.attr_value.len = sensor_data.attr_len;
memcpy(rsp.attr_value.value, sensor_data.attr_value, sensor_data.attr_len);
return esp_ble_gatts_send_response(gatts_if,
param->read.conn_id,
param->read.trans_id,
ESP_GATT_OK,
&rsp);
}
/**
* #brief GATT event handler.
*
* #see esp_ble_gatts_app_register
*/
static void gatt_handler(esp_gatts_cb_event_t event,
esp_gatt_if_t gatts_if,
esp_ble_gatts_cb_param_t* param)
{
esp_err_t rc = ESP_OK;
switch (event) {
case ESP_GATTS_REG_EVT:
rc = gatts_register_evt_handler(event, gatts_if, param);
break;
case ESP_GATTS_CREATE_EVT:
rc = gatts_create_evt_handler(event, gatts_if, param);
break;
case ESP_GATTS_ADD_CHAR_EVT:
rc = gatts_add_char_evt_handler(event, gatts_if, param);
break;
case ESP_GATTS_ADD_CHAR_DESCR_EVT:
rc = gatts_add_char_descr_evt_handler(event, gatts_if, param);
break;
case ESP_GATTS_CONNECT_EVT: {
rc = gatts_connect_evt_handler(event, gatts_if, param);
break;
}
case ESP_GATTS_READ_EVT:
rc = gatts_read_evt_handler(event, gatts_if, param);
break;
case ESP_GATTS_WRITE_EVT: {
ESP_LOGI(TAG,
"ESP_GATTS_WRITE_EVT %x %x",
service_def.descr_handle,
param->write.handle);
if (service_def.descr_handle == param->write.handle) {
uint16_t descr_value =
param->write.value[1] << 8 | param->write.value[0];
if (descr_value != 0x0000) {
ESP_LOGI(TAG, "notify enable");
esp_ble_gatts_send_indicate(gatts_if,
param->write.conn_id,
service_def.char_handle,
sensor_data.attr_len,
sensor_data.attr_value,
false);
} else {
ESP_LOGI(TAG, "notify disable");
}
esp_ble_gatts_send_response(gatts_if,
param->write.conn_id,
param->write.trans_id,
ESP_GATT_OK,
NULL);
} else {
esp_ble_gatts_send_response(gatts_if,
param->write.conn_id,
param->write.trans_id,
ESP_GATT_WRITE_NOT_PERMIT,
NULL);
}
break;
}
case ESP_GATTS_DISCONNECT_EVT:
service_def.gatts_if = 0;
esp_ble_gap_start_advertising(&adv_params);
break;
default:
break;
}
if (rc != ESP_OK) {
ESP_LOGE(TAG, "GATTS error %d", rc);
}
}
static int16_t asd = 0;
static int dht_read_data(int16_t* hum, int16_t* temp)
{
if (asd == 255) {
asd = 0;
} else {
asd++;
}
*hum = asd;
*temp = 2 * asd;
return ESP_OK;
}
static void read_temp_task(void* arg)
{
while (1) {
vTaskDelay(2000 / portTICK_PERIOD_MS);
if (dht_read_data(&hum, &temp) == ESP_OK) {
temp /= 10;
if (service_def.gatts_if > 0) {
esp_ble_gatts_send_indicate(service_def.gatts_if,
service_def.client_write_conn,
service_def.char_handle,
sensor_data.attr_len,
sensor_data.attr_value,
false);
}
} else {
ESP_LOGE(TAG, "DHT11 read failed");
}
}
}
void app_main(void)
{
init_service_def();
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES
|| ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ESP_ERROR_CHECK(nvs_flash_init());
}
esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
esp_bt_controller_init(&bt_cfg);
esp_bt_controller_enable(ESP_BT_MODE_BLE);
esp_bluedroid_init();
esp_bluedroid_enable();
esp_ble_gap_register_callback(gap_handler);
esp_ble_gatts_register_callback(gatt_handler);
esp_ble_gatts_app_register(0);
xTaskCreate(read_temp_task,
"temp",
configMINIMAL_STACK_SIZE * 3,
NULL,
5,
NULL);
}
app.c
#include "app.h"
#include <string.h>
static uint8_t adv_service_uuid128[32] = {
0xfb,
0x34,
0x9b,
0x5f,
0x80,
0x00,
0x00,
0x80,
0x00,
0x10,
0x00,
0x00,
0xFF,
0x00,
0x00,
0x00,
};
esp_ble_adv_data_t adv_data = {
.set_scan_rsp = false,
.include_name = true,
.include_txpower = false,
.min_interval = 0x0006,
.max_interval = 0x0010,
.appearance = 0x00,
.manufacturer_len = 0,
.p_manufacturer_data = NULL,
.service_data_len = 0,
.p_service_data = NULL,
.service_uuid_len = sizeof(adv_service_uuid128),
.p_service_uuid = adv_service_uuid128,
.flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};
esp_ble_adv_params_t adv_params = {
.adv_int_min = 0x20,
.adv_int_max = 0x40,
.adv_type = ADV_TYPE_IND,
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
.channel_map = ADV_CHNL_ALL,
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};
service_info_t service_def;
void init_service_def(void)
{
service_def.service_id.is_primary = true;
service_def.service_id.id.inst_id = 0x00;
service_def.service_id.id.uuid.len = ESP_UUID_LEN_16;
service_def.service_id.id.uuid.uuid.uuid16 = GATT_SERVICE_UUID;
service_def.char_uuid.len = ESP_UUID_LEN_16;
service_def.char_uuid.uuid.uuid16 = GATT_CHARACTERISTIC_UUID;
service_def.descr_uuid.len = ESP_UUID_LEN_16;
service_def.descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
service_def.gatts_if = 0;
}
void update_conn_params(esp_bd_addr_t remote_bda)
{
esp_ble_conn_update_params_t conn_params = {0};
memcpy(conn_params.bda, remote_bda, sizeof(esp_bd_addr_t));
conn_params.latency = 0;
conn_params.max_int = 0x20;
conn_params.min_int = 0x10;
conn_params.timeout = 400;
esp_ble_gap_update_conn_params(&conn_params);
}
app.h
#ifndef gattex_app_h_
#define gattex_app_h_
#include <stdint.h>
#include <stddef.h>
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#define GATT_SERVICE_UUID 0x00FF
#define GATT_CHARACTERISTIC_UUID 0xFF01
#define GATT_HANDLE_COUNT 4
typedef struct
{
uint16_t service_handle;
esp_gatt_srvc_id_t service_id;
uint16_t char_handle;
esp_bt_uuid_t char_uuid;
uint16_t descr_handle;
esp_bt_uuid_t descr_uuid;
esp_gatt_if_t gatts_if;
uint16_t client_write_conn;
} service_info_t;
extern esp_ble_adv_data_t adv_data;
extern esp_ble_adv_params_t adv_params;
extern service_info_t service_def;
void init_service_def(void);
void update_conn_params(esp_bd_addr_t remote_bda);
#endif
The UUID (e.g. 00001801-0000-1000-8000-00805f9b34fb) is the identifier of the GATT service/characteristic etc. This is used for identification GATT entries at a high level. There is a reserved range for Bluetooth SIG adopted UUIDs. Outside of that range can be used for Custom UUIDs
The handle (e.g. 0x0009) is the identifier for that specific entry in the local GATT database that gets built during service discovery. For example, there might a characteristic UUID used under multiple GATT services. However each database entry will have a different handle.
bluetoothctl communicates with the Bluetooth Daemon using D-Bus for inter process communication (IPC). For D-Bus there are three bits of information that are required: the D-Bus Service Name (org.bluez), the D-Bus interface (e.g. org.bluez.GattService1) and the D-Bus object path (e.g. /org/bluez/hci0/dev_0C_B8_15_F6_61_3E/service0001). The BlueZ API for GATT is documented at: https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/gatt-api.txt
The LE attribute data is stored as a sequence of handles with data as in this example:
Handle
0001 UUID = 2800 (Primary service) Value = 1800 (UUID of the primary service)
The attributes that belong to this primary service follow:
0002 UUID = 2803 (Characteristic info) Value = Handle (0003) permissions and UUID of the following characteristic
0003 UUID = as in previous handle Value = Value of the characteristic
0004 UUID = 2803 (Characteristic info) Value = Handle (0005) permissions and UUID of the following characteristic
0005 UUID = as in previous handle Value = Value of the characteristic
Some characteristics are followed by a descriptor (e.g. notify enable)
0006 UUID = 2803 (Characteristic info) Value = Handle (0007) permissions and UUID of the following characteristic
0007 UUID = as in previous handle Value = Value of the characteristic
0008 UUID = 2902 (Descriptor for notify enable) Value = 00 00 (notify off) or 00 01 (notify on)
There might then be another primary service
0009 UUID = 2800 (Primary service) Value = 1801 (UUID of the primary service)
etc....
A client only needs the value handle (e.g. 0003 above) to read/write the characteristic. If it does not know the handle, it must read this database to find the handle from the UUID. It will read all the handles looking for UUID=2803 entries. A 2-byte UUID (e.g. 1801) may also appear as a 16-byte standard value with the 2 bytes at bytes 3/4: 00001801-0000-1000-8000-00805F9B34FB.
Normally, the client will read the database on connection and set up the dbus objects using the handles it finds. So the first characteristic in the example is a member of the primary service with handle 0001, and has an info handle of 0002, so it will appear as ..../service0001/char0002. It will read and write using handle 0003. The other handles listed first (like Characteristic (Handle 0xae24)) seem to invented by bluetoothctl for its own purposes and are unknown to the LE device, so confusion is created by two types of handle.
I am writing a class which has the following structure:
Header File:
#pragma once
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
class VirtualButton : public QWidget {
Q_OBJECT
public:
VirtualButton( QWidget *parent );
private:
static QMap<unsigned int, QColor> ColorKeyMap;
static QList<unsigned int> goodKeys;
};
CppFile
#include "VirtualButton.hpp"
QMap<unsigned int, QColor> VirtualButton::ColorKeyMap = QMap<unsigned int, QColor>();
ColorKeyMap[ 23 ] = QColor( 0xff, 0x00, 0xff );
QList<unsigned int> VirtualButton::goodKeys = QList<unsigned int>() << 50 << 62 << 37 << 133 << 64 << 108 << 135 << 109;
VirtualButton::VirtualButton( QWidget *parent ) : QWidget( parent ) {
setFixedSize( 48, 48 );
};
int main( int argv, char **argv ) {
QApplication app( argc, argv );
VirtualButton *btn = new VirtualButton();
btn->show();
return app.exec()
}
When I compile this code I get the following error:
VirtualKeyboard.cpp:4:1: error: ‘ColorKeyMap’ does not name a type; did you mean ‘QColormap’?
ColorKeyMap[ 23 ] = QColor( 0xff, 0x00, 0xff );
^~~~~~~~~~~
QColormap
Why does this error come up? I can see that goodKeys has no issue, but ColorKeyMap does. Is this because QColor should not be used outside the class?
There were a couple of mistakes in your code.
The main problem is that you want to modify for a certain *key, namely 23 the value of color, namely QColor( 0xff, 0x00, 0xff );
The problem is although you can initialize a private static member outside of your class like you did, you cannot modify it later. You cannot even read it later outside the class. You can only access it in the future through a member function of the class VirtualButton.
Fortunately, there is work around. You can use a static function (namely initColorKeyMap()) to initialize your static member ColorKeyMap, as shown in the code below.
virtualbutton.h
#pragma once
#include <QtWidgets/QtWidgets>
#include <QMap>
#include <QList>
class VirtualButton : public QWidget {
Q_OBJECT
public:
VirtualButton( QWidget *parent );
private:
static QMap<unsigned int, QColor> initColorKeyMap();
static QMap<unsigned int, QColor> ColorKeyMap;
static QList<unsigned int> goodKeys;
};
virtualbutton.cpp
#include "virtualbutton.h"
QMap<unsigned int, QColor> VirtualButton::initColorKeyMap() {
QMap<unsigned int, QColor> temp = QMap<unsigned int, QColor>();
temp[ 23 ] = QColor( 0xff, 0x00, 0xff );
return temp;
}
QMap<unsigned int, QColor> VirtualButton::ColorKeyMap = VirtualButton::initColorKeyMap();
QList<unsigned int> VirtualButton::goodKeys = QList<unsigned int>() << 50 << 62 << 37 << 133 << 64 << 108 << 135 << 109;
VirtualButton::VirtualButton( QWidget *parent ) : QWidget( parent ) {
setFixedSize( 48, 48 );
}
There were a couple of mistakes/mistmatches in your main() also, but I guess they were just smaller side problems and not your main concern. Anyways, here is the main.cpp also with minor corrections and some additional code. (With added coded, I wanted to test your Button class, until I realized that it has not been derived from QPushButton and so I can't show a VirtualButton on the widget. Just realized it later. Anyways, it had no bearing on the real problem at hand. The code compiles with a correctly initialized ColorKeyMap)
main.cpp
#include "virtualbutton.h"
#include <QtCore>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget window;
window.resize(320, 240);
window.setWindowTitle(QApplication::translate("childwidget", "Child widget"));
window.show();
// QPushButton *button = new QPushButton(
// QApplication::translate("childwidget", "Press me"), &window);
VirtualButton *button = new VirtualButton(&window);
// button->move(100, 100);
button->show();
return app.exec();
}
Other than that the code compiles without a problem.
I'm using Windows Media Foundation WMV encoder on Win10 64bit. While it can be used to encode correctly, I failed to set VBR quality.
Below is the sample code
const PROPERTYKEY MFPKEY_VBRENABLED = { { 0xe48d9459, 0x6abe, 0x4eb5, { 0x92, 0x11, 0x60, 0x8, 0xc, 0x1a, 0xb9, 0x84 } }, 0x14 };
const PROPERTYKEY MFPKEY_DESIRED_VBRQUALITY = { { 0x6dbdf03b, 0xb05c, 0x4a03, { 0x8e, 0xc1, 0xbb, 0xe6, 0x3d, 0xb1, 0x0c, 0xb4 } }, 0x00 + 25 };
CLSID* pCLSIDs = NULL; // Pointer to an array of CLISDs. UINT32 nCount = 0;
MFT_REGISTER_TYPE_INFO encoderInfo; encoderInfo.guidMajorType = MFMediaType_Video;
encoderInfo.guidSubtype = MFVideoFormat_WMV3;
HRESULT hr = fpMFTEnum(MFT_CATEGORY_VIDEO_ENCODER, 0, NULL, &encoderInfo, NULL, &pCLSIDs, &nCount);
if (FAILED(hr) || (nCount == 0)) {} ciEncoder.CreateObject(pCLSIDs[0], IID_IMFTransform);
if (ciEncoder.IsInvalid()) {}
LComInterface<IPropertyStore> ciPropertyStore; // WMV Encoder codec setting interface
hr = ciEncoder->QueryInterface(IID_IPropertyStore, (void**)ciPropertyStore.GetAssignablePtrRef());
if (SUCCEEDED(hr)) {
PROPVARIANT propVal;
propVal.vt = VT_BOOL;
propVal.boolVal = VARIANT_TRUE;
hr = ciPropertyStore->SetValue(MFPKEY_VBRENABLED, propVal);
propVal.vt = VT_UI4;
propVal.ulVal = 90;
hr = ciPropertyStore->SetValue(MFPKEY_DESIRED_VBRQUALITY, propVal);
While ciPropertyStore->SetValue(MFPKEY_VBRENABLED, propVal) returns S_OK,
ciPropertyStore->SetValue(MFPKEY_DESIRED_VBRQUALITY, propVal) failed and hr = "The property ID does not match any property supported by the transform"
Thanks
I've just found the root cause: seems I should use MFPKEY_VBRQUALITY instead of MFPKEY_DESIRED_VBRQUALITY
in https://msdn.microsoft.com/en-us/library/windows/desktop/dd206749%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 seems MFPKEY_VBRQUALITY is for video, MFPKEY_DESIRED_VBRQUALITY is for audio?
I'm working with serial communication, and I receive 32bit integers in a QByteArray, packed in 4 separate bytes (little-endian).
I attempt to unpack the value from the 4 bytes using QByteArray::toLong() but it fails the conversion and returns the wrong number:
quint8 packed_bytes[] { 0x12, 0x34, 0x56, 0x78 };
QByteArray packed_array { QByteArray(reinterpret_cast<char*>(packed_bytes),
sizeof(packed_bytes)) };
bool isConversionOK;
qint64 unpacked_value { packed_array.toLong(&isConversionOK) };
// At this point:
// unpacked_value == 0
// isConversionOK == false
The expected unpacked_value is 0x78563412 (little-endian unpacking). Why is the conversion failing?
You can use a QDataStream to read binary data.
quint8 packed_bytes[] { 0x12, 0x34, 0x56, 0x78 };
QByteArray packed_array { QByteArray(reinterpret_cast<char*>(packed_bytes), sizeof(packed_bytes)) };
QDataStream stream(packed_array);
stream.setByteOrder(QDataStream::LittleEndian);
int result;
stream >> result;
qDebug() << QString::number(result,16);
toLong() converts a char * digits string to long. Not bytes. And your values likely don't make the up the string "0x78563412" or its decimal equivalent. Hence the 0 result.
If you need the byte values interpreted as long you can do something like:
long value;
value == *((long*)packed_bytes.data());
Or to access an array of bytes as long array:
long * values;
values == (long*)packed_bytes.data();
values[0]; // contains first long
values[1]; // contains second long
...
Don't know whether my examples work out of the box but it should make clear the principle.
Check out this example:
char bytes[] = {255, 0};
QByteArray b(bytes, 2);
QByteArray c("255");
qDebug() << b.toShort() << c.toShort();
qDebug() << *((short*)b.data()) << *((short*)c.data());
the output is:
0 255
255 13618
You may need to change the byte order depending on the endianess. But it does what you need.
you can build your qint64 with bit manipulators:
#include <QtGlobal>
#include <QByteArray>
#include <QDebug>
int main()
{
quint8 packed_bytes[] { 0x12, 0x34, 0x56, 0x78 };
QByteArray packed_array { QByteArray(reinterpret_cast<char*>(packed_bytes),
sizeof(packed_bytes)) };
qint64 unpacked_value = 0;
unpacked_value |= packed_array.at(0) |
packed_array.at(1) << 8 |
packed_array.at(2) << 16 |
packed_array.at(3) << 24;
qDebug() << QString("0x%1").arg(unpacked_value, 0, 16);
}
Here's a generic solution for converting a QByteArray to "some other type" (such as what is specifically asked in the question) by running it through a QDataStream (as done by the accepted answer).
DISCLAIMER: I am only advocating for using this in a private implementation. I am aware there are many ways one could abuse the
macro!
Using this macro, you can easily produce many conversion functions such as the examples I've provided. Defining a series of such functions in this way may be useful if you need to pull a variety of types out of a stream. Obviously, you could tweak the macro for your use case, the point is the pattern can remain basically same and be put in a macro like this.
#define byteArrayToType( data, order, type ) \
QDataStream stream( data ); \
stream.setByteOrder( order ); \
type t; \
stream >> t; \
return t;
Example functions, which simply wrap the macro:
16 bit, signed
qint16 toQInt16( const QByteArray &data,
const QDataStream::ByteOrder order=QDataStream::BigEndian )
{ byteArrayToType( data, order, qint16 ) }
32 bit, signed
qint32 toQInt32( const QByteArray &data,
const QDataStream::ByteOrder order=QDataStream::BigEndian )
{ byteArrayToType( data, order, qint32 ) }
64 bit, signed
qint64 toQInt64( const QByteArray &data,
const QDataStream::ByteOrder order=QDataStream::BigEndian )
{ byteArrayToType( data, order, qint64 ) }
Cast the Byte array to the required format and use the built-in function qFromBigEndian or qFromLittleEndian to set the Byte order. Example code is shown below,
QByteArray byteArray("\x45\x09\x03\x00");
quint32 myValue = qFromBigEndian<quint32>(byteArray);
qDebug() << "Hex value: " << QString("0x%1").arg(myValue, 8, 16, QLatin1Char( '0' ));
myValue holds the converted value.
Don't forget to include the header file <QtEndian>