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?
Related
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 trying to read from a web camera in synchronous mode using Microsoft Media Foundation:
#include <iostream>
#include <vector>
#include <sstream>
#include <atlbase.h>
#include <mfidl.h>
#include <Mfapi.h>
#include <Mfreadwrite.h>
#pragma comment(lib,"Mfplat.lib")
#pragma comment(lib,"Mf.lib")
#pragma comment(lib,"Mfreadwrite.lib")
#pragma comment(lib,"mfuuid.lib")
#pragma comment(lib,"shlwapi.lib")
int main()
{
HRESULT hr;
DWORD dwCoInit = COINIT_MULTITHREADED;
hr = CoInitializeEx(NULL, dwCoInit);
if (FAILED(hr))
return -1;
hr = MFStartup(MF_VERSION);
if (FAILED(hr))
return -2;
CComBSTR name("M034-WDR");
CComPtr<IMFMediaSource> pMediaSource;
CComPtr<IMFSourceReader> pSourceReader;
CComPtr<IMFMediaType> pMediaType;
// Find all camera sources:
CComPtr<IMFAttributes> pAttributes;
MFCreateAttributes(&pAttributes, 1);
pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); // camera source
CComHeapPtr<IMFActivate*> ppActivates;
UINT32 nActivateCount = 0;
MFEnumDeviceSources(pAttributes, &ppActivates, &nActivateCount);
// Loop over the camera sources, find the one with correct name:
CComPtr<IMFActivate> pActivate;
for (UINT32 nIndex = 0; nIndex < nActivateCount; nIndex++) {
CComHeapPtr<WCHAR> name_i;
UINT32 length;
ppActivates[nIndex]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &name_i, &length);
if (wcscmp(name_i, name) == 0) {
pActivate = ppActivates[nIndex];
}
reinterpret_cast<CComPtr<IMFActivate>&>(ppActivates[nIndex]).Release();
}
if (!pActivate) { // camera with name not found
return -3;
}
IMFMediaSource* source;
hr = pActivate->ActivateObject(__uuidof(IMFMediaSource), (VOID**)&source);
if (FAILED(hr))
return -4;
pMediaSource = source;
hr = MFCreateSourceReaderFromMediaSource(pMediaSource, NULL, &pSourceReader);
if (FAILED(hr))
return -5;
IMFMediaType* mediaType;
hr = pSourceReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, &mediaType);
if (FAILED(hr))
return -6;
unsigned int width, height;
hr = MFGetAttributeSize(mediaType, MF_MT_FRAME_SIZE, &width, &height);
if (FAILED(hr))
return -7;
while (true) {
CComPtr<IMFSample> pSample;
DWORD dwControlFlags = 0;
DWORD* pdwActualStreamIndex = NULL;
DWORD pdwStreamFlags;
LONGLONG timestamp;
OutputDebugStringA("MF:Before ReadSample()");
hr = pSourceReader->ReadSample(0,//dwStreamIndex,
dwControlFlags,
pdwActualStreamIndex,
&pdwStreamFlags,
×tamp,
&pSample);
OutputDebugStringA("MF:After ReadSample()");
if (FAILED(hr)) {
std::stringstream ss;
ss << "MF:ReadSample() failed. hr = " << std::hex << hr << ".";
OutputDebugStringA(ss.str().c_str());
}
if (pSample == NULL) {
OutputDebugStringA("MF:ReadSample(): got null sample.");
}
}
}
The code above works fine on my laptop with one such webcam.
On another PC with another such webcam however; it runs fine for ca 1 minute before it starts to fail:
00000920 54.00323105 [7008] MF:Before ReadSample()
00000921 54.00324631 [7008] MF:After ReadSample()
00000922 54.00325012 [7008] MF:ReadSample() failed. hr = c00d3ea2.
00000923 54.00326538 [7008] MF:ReadSample(): got null sample.
Looking up the error code:
C:\temp>Err_6.4.5.exe c00d3ea2
# for hex 0xc00d3ea2 / decimal -1072873822
MF_E_VIDEO_RECORDING_DEVICE_INVALIDATED mferror.h
# The video recording device is no longer present.%0
Anyways the Microsoft Camera application also experiences problems. Every minute ca. the image goes black for 1 sec ca. before the camera appears to be restarted.
How can I restart my code like Microsoft Camera does?
Is there a possibility to set the padding for encryption with AES128 in Poco Crypto? I can't find any option for this.
std::string Crypto::Encrypt(const std::string &input, const std::string &key)
{
Poco::Crypto::Cipher::ByteVec iv { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Poco::Crypto::Cipher::ByteVec key2 {key.begin(), key.end()};
Poco::Crypto::Cipher::Ptr pCipher = Poco::Crypto::CipherFactory::defaultFactory()
.createCipher(Poco::Crypto::CipherKey("aes128", key2, iv));
std::string output = pCipher->encryptString(input);
return std::move(output);
}
In simple OpenSSL I have this option:
EVP_CIPHER_CTX *ctx;
EVP_CIPHER_CTX_set_padding(ctx, 0)
By default padding is enabled.
From comment in Crypto/include/Poco/Crypto/CryptoTransform.h or https://pocoproject.org/docs/Poco.Crypto.CryptoTransform.html#230, encryption operations are padded by default.
...
virtual int setPadding(int padding);
/// Enables or disables padding. By default encryption operations are padded using standard block
/// padding and the padding is checked and removed when decrypting. If the padding parameter is zero then
/// no padding is performed, the total amount of data encrypted or decrypted must then be a multiple of
/// the block size or an error will occur.
...
If you want to change padding option, you should override createEncryptor(), creatoreDecryptor() in Poco::Crypto::Cipher like below
class CipherWithPadding : public Poco::Crypto::Cipher
{
public:
// by default no padding, for more information refer to CryptoTransform::setPadding
CipherWithPadding(Poco::Crypto::Cipher::Ptr cipher_impl, int padding = 0)
: cipher_(cipher_impl)
, padding_(padding)
{
}
virtual ~CipherWithPadding()
{
}
virtual const std::string& name() const
{
return cipher_->name();
}
virtual Poco::Crypto::CryptoTransform* createEncryptor() override
{
auto ptransform = cipher_->createEncryptor();
if (ptransform)
ptransform->setPadding(padding_);
return ptransform;
}
virtual Poco::Crypto::CryptoTransform* createDecryptor() override
{
auto ptransform = cipher_->createDecryptor();
if (ptransform)
ptransform->setPadding(padding_);
return ptransform;
}
protected:
int padding_;
Poco::Crypto::Cipher::Ptr cipher_;
protected:
CipherWithPadding();
private:
CipherWithPadding(const CipherWithPadding&);
CipherWithPadding& operator= (const CipherWithPadding&);
};
Then your function should be like below
std::string Crypto::Encrypt(const std::string &input, const std::string &key)
{
Poco::Crypto::Cipher::ByteVec iv { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Poco::Crypto::Cipher::ByteVec key2 {key.begin(), key.end()};
CipherWithPadding cipher(Poco::Crypto::CipherFactory::defaultFactory()
.createCipher(Poco::Crypto::CipherKey("aes128", key2, iv)));
std::string output = cipher.encryptString(input);
return std::move(output);
}
I'm trying to get my IMFSinkWriter's Finalize() call to be asynchronous.
So I created this
class MyMFSinkWriterCallback : public IMFSinkWriterCallback
{
public:
MyMFSinkWriterCallback();
virtual ~MyMFSinkWriterCallback();
// IMFSinkWriterCallback methods
STDMETHODIMP OnFinalize(HRESULT hrStatus);
STDMETHODIMP OnMarker(DWORD dwStreamIndex, LPVOID pvContext);
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
private:
long m_nRefCount;
};
HRESULT MyMFSinkWriterCallback::OnFinalize(HRESULT hrStatus)
{
OutputDebugStringA("MyMFSinkWriterCallback::OnFinalize\n");
return S_OK;
}
And I installed it like this
hr = pAttributes->SetUnknown(MF_SINK_WRITER_ASYNC_CALLBACK, new MyMFSinkWriterCallback());
if (SUCCEEDED(hr))
{
hr = MFCreateSinkWriterFromURL(L"C:\test.mp4", NULL, pAttributes, &MySinkWriter);
}
QueryInterface, AddRef, and Release are all being called but when I end my capture like this
OutputDebugStringA("MySinkWriter->Finalize START\n");
HRESULT hr = MySinkWriter->Finalize();
OutputDebugStringA("MySinkWriter->Finalize END\n");
All I see in my output log is
MySinkWriter->Finalize START
MySinkWriter->Finalize END
I never see
MyMFSinkWriterCallback::OnFinalize
Perhaps you don't let time to "OnFinalize" to execute. Here is a working sample code (based on Sink Writer Tutorial) :
#include <Windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <Mfreadwrite.h>
#include <mferror.h>
#include <Shlwapi.h>
#include <new>
#pragma comment(lib, "mfreadwrite")
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfuuid")
#pragma comment(lib, "Shlwapi")
template <class T> void SafeRelease(T **ppT){
if(*ppT){
(*ppT)->Release();
*ppT = NULL;
}
}
// Format constants
const UINT32 VIDEO_WIDTH = 640;
const UINT32 VIDEO_HEIGHT = 480;
const UINT32 VIDEO_FPS = 30;
const UINT64 VIDEO_FRAME_DURATION = 10 * 1000 * 1000 / VIDEO_FPS;
const UINT32 VIDEO_BIT_RATE = 800000;
const GUID VIDEO_ENCODING_FORMAT = MFVideoFormat_WMV3;
const GUID VIDEO_INPUT_FORMAT = MFVideoFormat_RGB32;
const UINT32 VIDEO_PELS = VIDEO_WIDTH * VIDEO_HEIGHT;
const UINT32 VIDEO_FRAME_COUNT = 20 * VIDEO_FPS;
// Buffer to hold the video frame data.
DWORD videoFrameBuffer[VIDEO_PELS];
class CMFSinkWriterCallback : public IMFSinkWriterCallback{
public:
CMFSinkWriterCallback(HANDLE hFinalizeEvent) : m_nRefCount(1), m_hFinalizeEvent(hFinalizeEvent){}
virtual ~CMFSinkWriterCallback(){}
// IMFSinkWriterCallback methods
STDMETHODIMP OnFinalize(HRESULT hrStatus){
OutputDebugString(L"CMFSinkWriterCallback::OnFinalize\n");
if(m_hFinalizeEvent != NULL){
SetEvent(m_hFinalizeEvent);
}
return hrStatus;
}
STDMETHODIMP OnMarker(DWORD dwStreamIndex, LPVOID pvContext){
return S_OK;
}
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void** ppv){
static const QITAB qit[] = {
QITABENT(CMFSinkWriterCallback, IMFSinkWriterCallback),
{0}
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) AddRef(){
return InterlockedIncrement(&m_nRefCount);
}
STDMETHODIMP_(ULONG) Release(){
ULONG refCount = InterlockedDecrement(&m_nRefCount);
if(refCount == 0){
delete this;
}
return refCount;
}
private:
volatile long m_nRefCount;
HANDLE m_hFinalizeEvent;
};
HRESULT InitializeSinkWriter(IMFSinkWriter **ppWriter, DWORD *pStreamIndex, IMFSinkWriterCallback* pCallBack){
*ppWriter = NULL;
*pStreamIndex = NULL;
IMFAttributes *pAttributes = NULL;
IMFSinkWriter *pSinkWriter = NULL;
IMFMediaType *pMediaTypeOut = NULL;
IMFMediaType *pMediaTypeIn = NULL;
DWORD streamIndex = 0;
// Create the empty attribute store.
HRESULT hr = MFCreateAttributes(&pAttributes, 1);
if(SUCCEEDED(hr)){
hr = pAttributes->SetUnknown(MF_SINK_WRITER_ASYNC_CALLBACK, pCallBack);
}
if(SUCCEEDED(hr)){
hr = MFCreateSinkWriterFromURL(L"output.wmv", NULL, pAttributes, &pSinkWriter);
}
// Set the output media type.
if(SUCCEEDED(hr)){
hr = MFCreateMediaType(&pMediaTypeOut);
}
if(SUCCEEDED(hr)){
hr = pMediaTypeOut->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
}
if(SUCCEEDED(hr)){
hr = pMediaTypeOut->SetGUID(MF_MT_SUBTYPE, VIDEO_ENCODING_FORMAT);
}
if(SUCCEEDED(hr)){
hr = pMediaTypeOut->SetUINT32(MF_MT_AVG_BITRATE, VIDEO_BIT_RATE);
}
if(SUCCEEDED(hr)){
hr = pMediaTypeOut->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
}
if(SUCCEEDED(hr)){
hr = MFSetAttributeSize(pMediaTypeOut, MF_MT_FRAME_SIZE, VIDEO_WIDTH, VIDEO_HEIGHT);
}
if(SUCCEEDED(hr)){
hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_FRAME_RATE, VIDEO_FPS, 1);
}
if(SUCCEEDED(hr)){
hr = MFSetAttributeRatio(pMediaTypeOut, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
}
if(SUCCEEDED(hr)){
hr = pSinkWriter->AddStream(pMediaTypeOut, &streamIndex);
}
// Set the input media type.
if(SUCCEEDED(hr)){
hr = MFCreateMediaType(&pMediaTypeIn);
}
if(SUCCEEDED(hr)){
hr = pMediaTypeIn->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
}
if(SUCCEEDED(hr)){
hr = pMediaTypeIn->SetGUID(MF_MT_SUBTYPE, VIDEO_INPUT_FORMAT);
}
if(SUCCEEDED(hr)){
hr = pMediaTypeIn->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
}
if(SUCCEEDED(hr)){
hr = MFSetAttributeSize(pMediaTypeIn, MF_MT_FRAME_SIZE, VIDEO_WIDTH, VIDEO_HEIGHT);
}
if(SUCCEEDED(hr)){
hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_FRAME_RATE, VIDEO_FPS, 1);
}
if(SUCCEEDED(hr)){
hr = MFSetAttributeRatio(pMediaTypeIn, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
}
if(SUCCEEDED(hr)){
hr = pSinkWriter->SetInputMediaType(streamIndex, pMediaTypeIn, NULL);
}
// Tell the sink writer to start accepting data.
if(SUCCEEDED(hr)){
hr = pSinkWriter->BeginWriting();
}
// Return the pointer to the caller.
if(SUCCEEDED(hr)){
*ppWriter = pSinkWriter;
(*ppWriter)->AddRef();
*pStreamIndex = streamIndex;
}
SafeRelease(&pAttributes);
SafeRelease(&pSinkWriter);
SafeRelease(&pMediaTypeOut);
SafeRelease(&pMediaTypeIn);
return hr;
}
HRESULT WriteFrame(IMFSinkWriter *pWriter, DWORD streamIndex, const LONGLONG& rtStart){
IMFSample *pSample = NULL;
IMFMediaBuffer *pBuffer = NULL;
const LONG cbWidth = 4 * VIDEO_WIDTH;
const DWORD cbBuffer = cbWidth * VIDEO_HEIGHT;
BYTE *pData = NULL;
// Create a new memory buffer.
HRESULT hr = MFCreateMemoryBuffer(cbBuffer, &pBuffer);
// Lock the buffer and copy the video frame to the buffer.
if(SUCCEEDED(hr)){
hr = pBuffer->Lock(&pData, NULL, NULL);
}
if(SUCCEEDED(hr)){
hr = MFCopyImage(pData, cbWidth, (BYTE*)videoFrameBuffer, cbWidth, cbWidth, VIDEO_HEIGHT);
}
if(pBuffer){
pBuffer->Unlock();
}
// Set the data length of the buffer.
if(SUCCEEDED(hr)){
hr = pBuffer->SetCurrentLength(cbBuffer);
}
// Create a media sample and add the buffer to the sample.
if(SUCCEEDED(hr)){
hr = MFCreateSample(&pSample);
}
if(SUCCEEDED(hr)){
hr = pSample->AddBuffer(pBuffer);
}
// Set the time stamp and the duration.
if(SUCCEEDED(hr)){
hr = pSample->SetSampleTime(rtStart);
}
if(SUCCEEDED(hr)){
hr = pSample->SetSampleDuration(VIDEO_FRAME_DURATION);
}
// Send the sample to the Sink Writer.
if(SUCCEEDED(hr)){
hr = pWriter->WriteSample(streamIndex, pSample);
}
SafeRelease(&pSample);
SafeRelease(&pBuffer);
return hr;
}
void main(){
HANDLE hFinalizeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(hFinalizeEvent == NULL)
return;
// Set all pixels to green
for(DWORD i = 0; i < VIDEO_PELS; ++i){
videoFrameBuffer[i] = 0x000000FF;
}
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if(SUCCEEDED(hr)){
hr = MFStartup(MF_VERSION);
if(SUCCEEDED(hr)){
IMFSinkWriterCallback* pCallBack = NULL;
IMFSinkWriter *pSinkWriter = NULL;
DWORD stream;
pCallBack = new (std::nothrow)CMFSinkWriterCallback(hFinalizeEvent);
hr = pCallBack ? S_OK : E_POINTER;
if(SUCCEEDED(hr)){
hr = InitializeSinkWriter(&pSinkWriter, &stream, pCallBack);
}
if(SUCCEEDED(hr)){
// Send frames to the sink writer.
LONGLONG rtStart = 0;
for(DWORD i = 0; i < VIDEO_FRAME_COUNT; ++i){
hr = WriteFrame(pSinkWriter, stream, rtStart);
if(FAILED(hr)){
break;
}
rtStart += VIDEO_FRAME_DURATION;
}
}
if(SUCCEEDED(hr)){
hr = pSinkWriter->Finalize();
WaitForSingleObject(hFinalizeEvent, INFINITE);
}
SafeRelease(&pCallBack);
SafeRelease(&pSinkWriter);
MFShutdown();
}
CoUninitialize();
}
CloseHandle(hFinalizeEvent);
}
I am trying to create a cl_mem using clCreateImage but the program keeps crashing. I am following my book as close as possible but it's been a pretty bump road so far.
#include "stdafx.h"
#include <iostream>
#include <CL\cl.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
cl_int status;
cl_platform_id platform;
status = clGetPlatformIDs(1, &platform, NULL);
cl_device_id device;
clGetDeviceIDs(platform, CL_DEVICE_TYPE_ALL, 1, &device, NULL);
cl_context_properties props[3] = { CL_CONTEXT_PLATFORM, (cl_context_properties) (platform), 0 };
cl_context context = clCreateContext(props, 1, &device, NULL, NULL, &status);
cl_image_desc desc;
desc.image_type = CL_MEM_OBJECT_IMAGE2D;
desc.image_width = 100;
desc.image_height = 100;
desc.image_depth = 0;
desc.image_array_size = 0;
desc.image_row_pitch = 0;
desc.image_slice_pitch = 0;
desc.num_mip_levels = 0;
desc.num_samples = 0;
desc.buffer = NULL;
cl_image_format format;
format.image_channel_order = CL_R;
format.image_channel_data_type = CL_FLOAT;
// crashes on the next line with -- Unhandled exception at 0x72BCC9F1 in Convolution.exe: 0xC0000005: Access violation executing location 0x00000000.
cl_mem d_inputImage = clCreateImage(context, CL_MEM_READ_ONLY, &format, &desc, NULL, &status);
// never gets here
cout << "--->"; int exit; cin >> exit;
return 0;
}
clCreateImage has the following parameters:
cl_mem clCreateImage ( cl_context context,
cl_mem_flags flags,
const cl_image_format *image_format,
const cl_image_desc *image_desc,
void *host_ptr,
cl_int *errcode_ret)
In the doc page there is no mention that "host_ptr" may be NULL. Try with a valid pointer there. This is different from clCrateBuffer where a NULL pointer is allowed. However in CreateBuffer there is also no mention to that case, but I do know that it works. So it may be a driver/library bug.
Since it is fairly clear that the OpenCL library is trying to access a NULL pointer location as this error code states :Access violation executing location 0x00000000 I recomend to first try with that.
I think it's easier to code ignoring the usaless paremeters as follow:
cl_image_desc desc;
memset(&desc, '\0', sizeof(cl_image_desc));
desc.image_type = CL_MEM_OBJECT_IMAGE2D;
desc.image_width = width;
desc.image_height = height;
desc.mem_object= NULL; // or someBuf;
Additionally, the "host_ptr" can be NULL.
The common errors are usually the wrong image format which isn't supported by device, the wrong size and wrong version.