I am using OpenCv 2.4.8 on Linux, and obtaining a USB webcam (logitech C200) capture for further processing. I have determined that the capture uses cap_libv4.cpp (present in modlules/highgui) to capture the image. I had to modify the said file to add support for timestamps.
I have tried it with a number of webcams, and noticed it only gives correct timestamps if the flags field in the structure v4l2_buffer is 0. If it is equal to 2, which corresponds to V4L2_BUF_FLAG_TIMESTAMP MONOTONIC, it gives 0. I am wondering if it is a problem with my code modification, or with the libv4l drivers.
The function read_frame_v4l2 reads the buffer, and updates the timestamp. The timestamp can be read using the function icvGetPropertyCAM_V4L. The modified codes for these two functions are as below:
icvGetPropertyCAM_V4L:
static double icvGetPropertyCAM_V4L (CvCaptureCAM_V4L* capture,
int property_id ) {
char name[32];
int is_v4l2_device = 0;
/* initialize the control structure */
switch (property_id) {
case CV_CAP_PROP_FRAME_WIDTH:
case CV_CAP_PROP_FRAME_HEIGHT:
CLEAR (capture->form);
capture->form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl (capture->deviceHandle, VIDIOC_G_FMT, &capture->form)) {
/* display an error message, and return an error code */
perror ("VIDIOC_G_FMT");
if (v4l1_ioctl (capture->deviceHandle, VIDIOCGWIN, &capture->captureWindow) < 0) {
fprintf (stderr, "HIGHGUI ERROR: V4L: Unable to determine size of incoming image\n");
icvCloseCAM_V4L(capture);
return -1;
} else {
int retval = (property_id == CV_CAP_PROP_FRAME_WIDTH)?capture->captureWindow.width:capture->captureWindow.height;
return retval / 0xFFFF;
}
}
return (property_id == CV_CAP_PROP_FRAME_WIDTH)?capture->form.fmt.pix.width:capture->form.fmt.pix.height;
case CV_CAP_PROP_BRIGHTNESS:
sprintf(name, "Brightness");
capture->control.id = V4L2_CID_BRIGHTNESS;
break;
case CV_CAP_PROP_CONTRAST:
sprintf(name, "Contrast");
capture->control.id = V4L2_CID_CONTRAST;
break;
case CV_CAP_PROP_SATURATION:
sprintf(name, "Saturation");
capture->control.id = V4L2_CID_SATURATION;
break;
case CV_CAP_PROP_HUE:
sprintf(name, "Hue");
capture->control.id = V4L2_CID_HUE;
break;
case CV_CAP_PROP_GAIN:
sprintf(name, "Gain");
capture->control.id = V4L2_CID_GAIN;
break;
case CV_CAP_PROP_EXPOSURE:
sprintf(name, "Exposure");
capture->control.id = V4L2_CID_EXPOSURE;
break;
case CV_CAP_PROP_POS_MSEC:
if (capture->FirstCapture) {
return 0;
} else {
return 1000 * capture->timestamp.tv_sec + ((double) capture->timestamp.tv_usec) / 1000;
}
break;
default:
sprintf(name, "<unknown property string>");
capture->control.id = property_id;
}
if(v4l2_ioctl(capture->deviceHandle, VIDIOC_G_CTRL, &capture->control) == 0) {
/* all went well */
is_v4l2_device = 1;
} else {
fprintf(stderr, "HIGHGUI ERROR: V4L2: Unable to get property %s(%u) - %s\n", name, capture->control.id, strerror(errno));
}
if (is_v4l2_device == 1) {
/* get the min/max values */
int v4l2_min = v4l2_get_ctrl_min(capture, capture->control.id);
int v4l2_max = v4l2_get_ctrl_max(capture, capture->control.id);
if ((v4l2_min == -1) && (v4l2_max == -1)) {
fprintf(stderr, "HIGHGUI ERROR: V4L2: Property %s(%u) not supported by device\n", name, property_id);
return -1;
}
/* all was OK, so convert to 0.0 - 1.0 range, and return the value */
return ((float)capture->control.value - v4l2_min) / (v4l2_max - v4l2_min);
} else {
/* TODO: review this section */
int retval = -1;
switch (property_id) {
case CV_CAP_PROP_BRIGHTNESS:
retval = capture->imageProperties.brightness;
break;
case CV_CAP_PROP_CONTRAST:
retval = capture->imageProperties.contrast;
break;
case CV_CAP_PROP_SATURATION:
retval = capture->imageProperties.colour;
break;
case CV_CAP_PROP_HUE:
retval = capture->imageProperties.hue;
break;
case CV_CAP_PROP_GAIN:
fprintf(stderr, "HIGHGUI ERROR: V4L: Gain control in V4L is not supported\n");
return -1;
break;
case CV_CAP_PROP_EXPOSURE:
fprintf(stderr, "HIGHGUI ERROR: V4L: Exposure control in V4L is not supported\n");
return -1;
break;
}
if (retval == -1) {
/* there was a problem */
return -1;
}
/* all was OK, so convert to 0.0 - 1.0 range, and return the value */
return float (retval) / 0xFFFF;
}
}
read_frame_v4l2:
static CvCaptureCAM_V4L * icvCaptureFromCAM_V4L (int index)
{
static int autoindex;
autoindex = 0;
char deviceName[MAX_DEVICE_DRIVER_NAME];
if (!numCameras)
icvInitCapture_V4L(); /* Havent called icvInitCapture yet - do it now! */
if (!numCameras)
return NULL; /* Are there any /dev/video input sources? */
//search index in indexList
if ( (index>-1) && ! ((1 << index) & indexList) )
{
fprintf( stderr, "HIGHGUI ERROR: V4L: index %d is not correct!\n",index);
return NULL; /* Did someone ask for not correct video source number? */
}
/* Allocate memory for this humongus CvCaptureCAM_V4L structure that contains ALL
the handles for V4L processing */
CvCaptureCAM_V4L * capture = (CvCaptureCAM_V4L*)cvAlloc(sizeof(CvCaptureCAM_V4L));
if (!capture) {
fprintf( stderr, "HIGHGUI ERROR: V4L: Could not allocate memory for capture process.\n");
return NULL;
}
#ifdef USE_TEMP_BUFFER
capture->buffers[MAX_V4L_BUFFERS].start = NULL;
#endif
/* Select camera, or rather, V4L video source */
if (index<0) { // Asking for the first device available
for (; autoindex<MAX_CAMERAS;autoindex++)
if (indexList & (1<<autoindex))
break;
if (autoindex==MAX_CAMERAS)
return NULL;
index=autoindex;
autoindex++;// i can recall icvOpenCAM_V4l with index=-1 for next camera
}
/* Print the CameraNumber at the end of the string with a width of one character */
sprintf(deviceName, "/dev/video%1d", index);
/* w/o memset some parts arent initialized - AKA: Fill it with zeros so it is clean */
memset(capture,0,sizeof(CvCaptureCAM_V4L));
/* Present the routines needed for V4L funtionality. They are inserted as part of
the standard set of cv calls promoting transparency. "Vector Table" insertion. */
capture->FirstCapture = 1;
/* set the default size */
capture->width = DEFAULT_V4L_WIDTH;
capture->height = DEFAULT_V4L_HEIGHT;
if (_capture_V4L2 (capture, deviceName) == -1) {
icvCloseCAM_V4L(capture);
capture->is_v4l2_device = 0;
if (_capture_V4L (capture, deviceName) == -1) {
icvCloseCAM_V4L(capture);
return NULL;
}
} else {
capture->is_v4l2_device = 1;
}
return capture;
}; /* End icvOpenCAM_V4L */
#ifdef HAVE_CAMV4L2
static int read_frame_v4l2(CvCaptureCAM_V4L* capture) {
struct v4l2_buffer buf;
CLEAR (buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl (capture->deviceHandle, VIDIOC_DQBUF, &buf)) {
switch (errno) {
case EAGAIN:
return 0;
case EIO:
/* Could ignore EIO, see spec. */
/* fall through */
default:
/* display the error and stop processing */
perror ("VIDIOC_DQBUF");
return -1;
}
}
assert(buf.index < capture->req.count);
#ifdef USE_TEMP_BUFFER
memcpy(capture->buffers[MAX_V4L_BUFFERS].start,
capture->buffers[buf.index].start,
capture->buffers[MAX_V4L_BUFFERS].length );
capture->bufferIndex = MAX_V4L_BUFFERS;
//printf("got data in buff %d, len=%d, flags=0x%X, seq=%d, used=%d)\n",
// buf.index, buf.length, buf.flags, buf.sequence, buf.bytesused);
#else
capture->bufferIndex = buf.index;
#endif
if (-1 == xioctl (capture->deviceHandle, VIDIOC_QBUF, &buf))
perror ("VIDIOC_QBUF");
//set timestamp in capture struct to be timestamp of most recent frame
/** where timestamps refer to the instant the field or frame was received by the driver, not the capture time*/
capture->timestamp = buf.timestamp; //
printf("Flags %0x.X, timestamp = %ld, %ld", buf.flags, buf.timestamp.tv_sec, buf.timestamp.tv_usec);
//prints 0 if the flags is of the 4th nibble from LSB is 2 i.e. of the form .....2xxx H
return 1;
}
I need the timestamp so as to determine velocity of an object I am detecting in the image. I have searched a lot and have not found any solution.
Related
I am trying to recompile an old Win32 software package in order to bring small modifications.
The package is part of the MT SDK from Xsens and allows to get output from a inertial sensor (available here https://content.xsens.com/hubfs/Downloads/Software/mtsdk3.3_setup.zip). Example of a C++ routine using a DLL file responsible for the communication with hardware is located in Program Files\Xsens\MT SDK\Software Developpement\Examples\CMT Example Dll.
From directory I recompiled the source code, linking the DLL such as
i686-w64-mingw32-g++ -o test main.cpp -L. -lxsens_cmt
It requires changing a few scanf_s calls to scanf but compiled properly into test.exe.
However, when I try to run that executable in Windows I get the following error:
enter image description here
On the net I could find recommendation to compile with the following options but none could resolve my issue :
-static
-static-libstdc++
-static-libgcc
Examples folder of the MTSDK can be downloaded here if you don't want to install the software :
https://gitlab.com/lenhofed/mtsdk_modifs.git
main.cpp
/* demo key, replace this key with your own */
#define KEY "b8r6RCoGjQJVsytwUMo8WCRiJiVCCdoL11cCj4HqnaKPHtTn"
#include <stdio.h> // Needed for printf etc
#include <objbase.h> // Needed for COM functionality
#include "xsens_cmt.h"
#include <conio.h> // included for _getch and _kbhit
#include "main.h"
// this macro tests for an error and exits the program with a message if there was one
#define EXIT_ON_ERROR(res,comment) if (res != XRV_OK) { printf("Error %d occurred in " comment ": %s\n",res,cmtGetResultText(res)); exit(1); }
long instance = -1;
// used to signal that the user initiated the exit, so we do not wait for an extra keypress-
int userQuit = 0;
CmtOutputMode mode;
CmtOutputSettings settings;
unsigned short mtCount = 0;
int screenSensorOffset = 0;
int temperatureOffset = 0;
CmtDeviceId deviceIds[256];
CmtVector positionLLA;
int main(void)
{
XsensResultValue res = XRV_OK;
short screenSkipFactor = 10;
short screenSkipFactorCnt = screenSkipFactor;
// Set exit function
atexit(exitFunc);
// lets create the Xsens CMT instance to handle the sensor(s)
printf("Creating an XsensCMT instance\n");
char serialNumber[] = KEY;
if (strcmp(serialNumber,"b8r6RCoGjQJVsytwUMo8WCRiJiVCCdoL11cCj4HqnaKPHtTn") == 0)
printf("Warning: Using the demo key as a serial code will limit CMT functionality to 1000 calls. Enter your own serial code for unlimited CMT functionality.\n");
instance = cmtCreateInstance(serialNumber);
if (instance != -1)
printf("CMT instance created\n\n");
else {
printf("Creation of CMT instance failed, probably because of an invalid serial number\n");
exit(1);
}
// Perform hardware scan
doHardwareScan();
// Give user a (short) chance to see hardware scan results
Sleep(2000);
//clear screen present & get the user output mode selection.
clrscr();
getUserInputs();
// Set device to user input settings
doMtSettings();
// Wait for first data item(s) to arrive. In production code, you would use a callback function instead (see cmtRegisterCallback function)
Sleep(20);
//get the placement offsets, clear the screen and write the fixed headers.
calcScreenOffset();
clrscr();
writeHeaders();
// vars for sample counter & temp.
unsigned short sdata;
double tdata;
//structs to hold data.
CmtCalData caldata;
CmtQuat qat_data;
CmtEuler euler_data;
CmtMatrix matrix_data;
while(!userQuit && res == XRV_OK)
{
//get the bundle of data
res = cmtGetNextDataBundle(instance);
Sleep(10);
//get sample count, goto position & display.
res = cmtDataGetSampleCounter(instance, &sdata, deviceIds[0] ,NULL);
gotoxy(0,0);
printf("Sample Counter %05hu\n", sdata);
if (screenSkipFactorCnt++ == screenSkipFactor) {
screenSkipFactorCnt = 0;
for (unsigned int i = 0; i < mtCount; i++) {
// Output Temperature
if ((mode & CMT_OUTPUTMODE_TEMP) != 0) {
gotoxy(0,4 + i * screenSensorOffset);
res = cmtDataGetTemp(instance, &tdata, deviceIds[i],NULL);
printf("%6.2f", tdata);
}
gotoxy(0,5 + temperatureOffset + i * screenSensorOffset); // Output Calibrated data
if ((mode & CMT_OUTPUTMODE_CALIB) != 0) {
res = cmtDataGetCalData(instance, &caldata, deviceIds[i]);
printf("%6.2f\t%6.2f\t%6.2f" , caldata.m_acc.m_data[0], caldata.m_acc.m_data[1], caldata.m_acc.m_data[2]);
gotoxy(0,7 + temperatureOffset + i * screenSensorOffset);
printf("%6.2f\t%6.2f\t%6.2f", caldata.m_gyr.m_data[0], caldata.m_gyr.m_data[1], caldata.m_gyr.m_data[2] );
gotoxy(0,9 + temperatureOffset + i * screenSensorOffset);
printf("%6.2f\t%6.2f\t%6.2f",caldata.m_mag.m_data[0], caldata.m_mag.m_data[1], caldata.m_mag.m_data[2]);
gotoxy(0,13 + temperatureOffset + i * screenSensorOffset);
}
if ((mode & CMT_OUTPUTMODE_ORIENT) != 0) {
switch(settings & CMT_OUTPUTSETTINGS_ORIENTMODE_MASK) {
case CMT_OUTPUTSETTINGS_ORIENTMODE_QUATERNION:
// Output: quaternion
res = cmtDataGetOriQuat(instance, &qat_data, deviceIds[i]);
printf("%6.3f\t%6.3f\t%6.3f\t%6.3f\n", qat_data.m_data[0], qat_data.m_data[1],qat_data.m_data[2],qat_data.m_data[3]);
break;
case CMT_OUTPUTSETTINGS_ORIENTMODE_EULER:
// Output: Euler
res = cmtDataGetOriEuler(instance, &euler_data, deviceIds[i]);
printf("%6.1f\t%6.1f\t%6.1f\n", euler_data.m_roll,euler_data.m_pitch, euler_data.m_yaw);
break;
case CMT_OUTPUTSETTINGS_ORIENTMODE_MATRIX:
// Output: Cosine Matrix
res = cmtDataGetOriMatrix(instance, &matrix_data, deviceIds[i],NULL);
printf("%6.3f\t%6.3f\t%6.3f\n", matrix_data.m_data[0][0],matrix_data.m_data[0][1], matrix_data.m_data[0][2]);
printf("%6.3f\t%6.3f\t%6.3f\n", matrix_data.m_data[1][0],matrix_data.m_data[1][1], matrix_data.m_data[1][2]);
printf("%6.3f\t%6.3f\t%6.3f\n", matrix_data.m_data[2][0],matrix_data.m_data[2][1], matrix_data.m_data[2][2]);
break;
default:
;
}
}
if ((mode & CMT_OUTPUTMODE_POSITION) != 0) {
/* output position */
printf("\n\n");
if (cmtDataContainsPositionLLA(instance, deviceIds[i]) != 0) {
res = cmtDataGetPositionLLA(instance, &positionLLA, deviceIds[i]);
if (res != XRV_OK) {
printf("error %ud", res);
}
for (int i = 0; i < 2; i++) {
double deg = positionLLA.m_data[i];
double min = (deg - (int)deg)*60;
double sec = (min - (int)min)*60;
printf("%3d\xa7%2d\'%2.2lf\"\t", (int)deg, (int)min, sec);
}
printf(" %3.2lf\n", positionLLA.m_data[2]);
} else {
printf("No position data available\n");
}
}
}
}
if (_kbhit())
userQuit = 1;
}
clrscr();
cmtClose(instance);
return 0;
}
//////////////////////////////////////////////////////////////////////////
// doHardwareScan
//
// Checks available COM ports and scans for MotionTrackers
void doHardwareScan()
{
XsensResultValue res;
CmtPortInfo portInfo[256];
uint32_t portCount = 0;
printf("Scanning for connected Xsens devices...");
res = cmtScanPorts(portInfo, &portCount, 0);
EXIT_ON_ERROR(res,"cmtScanPorts");
printf("done\n");
if (portCount == 0) {
printf("No MotionTrackers found\n\n");
exit(0);
}
for(int i = 0; i < (int)portCount; i++) {
printf("Using COM port %d at %d baud\n\n",
(long) portInfo[i].m_portNr, portInfo[i].m_baudrate);
}
printf("Opening ports...");
//open the port which the device is connected to and connect at the device's baudrate.
for(int p = 0; p < (int)portCount; p++){
res = cmtOpenPort(instance, portInfo[p].m_portNr, portInfo[p].m_baudrate);
EXIT_ON_ERROR(res,"cmtOpenPort");
}
printf("done\n\n");
//get the Mt sensor count.
printf("Retrieving MotionTracker count (excluding attached Xbus Master(s))\n");
res = cmtGetMtCount(instance,&mtCount);
EXIT_ON_ERROR(res,"cmtGetMtCount");
printf("MotionTracker count: %i\n\n",mtCount);
// retrieve the device IDs
printf("Retrieving MotionTrackers device ID(s)\n");
for(unsigned int j = 0; j < mtCount; j++ ){
res = cmtGetMtDeviceId(instance, &deviceIds[j], j);
EXIT_ON_ERROR(res,"cmtGetDeviceId");
printf("Device ID at index %i: %08x\n",j,(long) deviceIds[j]);
}
// make sure that we get the freshest data
printf("\nSetting queue mode so that we always get the latest data\n\n");
res = cmtSetQueueMode(instance,CMT_QM_LAST);
EXIT_ON_ERROR(res,"cmtSetQueueMode");
}
//////////////////////////////////////////////////////////////////////////
// getUserInputs
//
// Request user for output data
void getUserInputs()
{
mode = 0;
while (mode < 1 || mode > 6) {
printf("Select desired output:\n");
printf("1 - Calibrated data\n");
printf("2 - Orientation data and GPS Position (MTi-G only)\n");
printf("3 - Both Calibrated and Orientation data\n");
printf("4 - Temperature and Calibrated data\n");
printf("5 - Temperature and Orientation data\n");
printf("6 - Temperature, Calibrated and Orientation data\n");
printf("Enter your choice: ");
scanf("%d", &mode);
// flush stdin
while (getchar() != '\n') continue;
if (mode < 1 || mode > 6) {
printf("\n\nPlease enter a valid output mode\n");
}
}
clrscr();
switch(mode)
{
case 1:
mode = CMT_OUTPUTMODE_CALIB;
break;
case 2:
mode = CMT_OUTPUTMODE_ORIENT | CMT_OUTPUTMODE_POSITION;
break;
case 3:
mode = CMT_OUTPUTMODE_CALIB | CMT_OUTPUTMODE_ORIENT;
break;
case 4:
mode = CMT_OUTPUTMODE_TEMP | CMT_OUTPUTMODE_CALIB;
break;
case 5:
mode = CMT_OUTPUTMODE_TEMP | CMT_OUTPUTMODE_ORIENT;
break;
case 6:
mode = CMT_OUTPUTMODE_TEMP | CMT_OUTPUTMODE_CALIB | CMT_OUTPUTMODE_ORIENT;
break;
}
if ((mode & CMT_OUTPUTMODE_ORIENT) != 0) {
do{
printf("Select desired output format\n");
printf("1 - Quaternions\n");
printf("2 - Euler angles\n");
printf("3 - Matrix\n");
printf("Enter your choice: ");
scanf("%d", &settings);
// flush stdin
while (getchar() != '\n') continue;
if (settings < 1 || settings > 3) {
printf("\n\nPlease enter a valid choice\n");
}
}while(settings < 1 || settings > 3);
// Update outputSettings to match data specs of SetOutputSettings
switch(settings) {
case 1:
settings = CMT_OUTPUTSETTINGS_ORIENTMODE_QUATERNION;
break;
case 2:
settings = CMT_OUTPUTSETTINGS_ORIENTMODE_EULER;
break;
case 3:
settings = CMT_OUTPUTSETTINGS_ORIENTMODE_MATRIX;
break;
}
}
else{
settings = 0;
}
settings |= CMT_OUTPUTSETTINGS_TIMESTAMP_SAMPLECNT;
}
//////////////////////////////////////////////////////////////////////////
// doMTSettings
//
// Set user settings in MTi/MTx
// Assumes initialized global MTComm class
void doMtSettings(void)
{
XsensResultValue res;
// set sensor to config sate
res = cmtGotoConfig(instance);
EXIT_ON_ERROR(res, "cmtGotoConfig");
unsigned short sampleFreq;
res = cmtGetSampleFrequency(instance, &sampleFreq, deviceIds[0]);
// set the device output mode for the device(s)
printf("Configuring your mode selection");
for (int i=0; i < mtCount; i++) {
if (cmtIdIsMtig(deviceIds[i])) {
res = cmtSetDeviceMode(instance, mode,settings, sampleFreq, deviceIds[i]);
} else {
res = cmtSetDeviceMode(instance, mode & 0xFF0F, settings, sampleFreq, deviceIds[i]);
}
EXIT_ON_ERROR(res, "setDeviceMode");
}
// start receiving data
res = cmtGotoMeasurement(instance);
EXIT_ON_ERROR(res, "cmtGotoMeasurement");
}
//////////////////////////////////////////////////////////////////////////
// writeHeaders
//
// Write appropriate headers to screen
void writeHeaders()
{
for (unsigned int i = 0; i < mtCount; i++) {
gotoxy(0, 2 + i * screenSensorOffset);
printf("MotionTracker %d\n", i + 1);
if ((mode & CMT_OUTPUTMODE_TEMP) != 0) {
temperatureOffset = 3;
gotoxy(0,3 + i * screenSensorOffset);
printf("Temperature");
gotoxy(7,4 + i * screenSensorOffset);
printf("degrees celcius");
gotoxy(0,6 + i * screenSensorOffset);
}
if ((mode & CMT_OUTPUTMODE_CALIB) != 0) {
gotoxy(0,3 + temperatureOffset + i * screenSensorOffset);
printf("Calibrated sensor data");
gotoxy(0,4 + temperatureOffset + i * screenSensorOffset);
printf(" Acc X\t Acc Y\t Acc Z");
gotoxy(23, 5 + temperatureOffset + i * screenSensorOffset);
printf("(m/s^2)");
gotoxy(0,6 + temperatureOffset + i * screenSensorOffset);
printf(" Gyr X\t Gyr Y\t Gyr Z");
gotoxy(23, 7 + temperatureOffset + i * screenSensorOffset);
printf("(rad/s)");
gotoxy(0,8 + temperatureOffset + i * screenSensorOffset);
printf(" Mag X\t Mag Y\t Mag Z");
gotoxy(23, 9 + temperatureOffset + i * screenSensorOffset);
printf("(a.u.)");
gotoxy(0,11 + temperatureOffset + i * screenSensorOffset);
}
if ((mode & CMT_OUTPUTMODE_ORIENT) != 0) {
printf("Orientation data\n");
switch(settings & CMT_OUTPUTSETTINGS_ORIENTMODE_MASK) {
case CMT_OUTPUTSETTINGS_ORIENTMODE_QUATERNION:
printf(" q0\t q1\t q2\t q3\n");
break;
case CMT_OUTPUTSETTINGS_ORIENTMODE_EULER:
printf(" Roll\t Pitch\t Yaw\n");
printf(" degrees\n");
break;
case CMT_OUTPUTSETTINGS_ORIENTMODE_MATRIX:
printf(" Matrix\n");
break;
default:
;
}
}
if ((mode & CMT_OUTPUTMODE_POSITION) != 0) {
printf("\nLongitude\tLatitude\t Altitude\n");
}
}
}
//////////////////////////////////////////////////////////////////////////
// calcScreenOffset
//
// Calculates offset for screen data with multiple sensors.
void calcScreenOffset()
{
// 1 line for "Sensor ..."
screenSensorOffset += 1;
if ((mode & CMT_OUTPUTMODE_TEMP) != 0)
screenSensorOffset += 3;
if ((mode & CMT_OUTPUTMODE_CALIB) != 0)
screenSensorOffset += 8;
if ((mode & CMT_OUTPUTMODE_ORIENT) != 0) {
switch(settings & CMT_OUTPUTSETTINGS_ORIENTMODE_MASK) {
case CMT_OUTPUTSETTINGS_ORIENTMODE_QUATERNION:
screenSensorOffset += 4;
break;
case CMT_OUTPUTSETTINGS_ORIENTMODE_EULER:
screenSensorOffset += 4;
break;
case CMT_OUTPUTSETTINGS_ORIENTMODE_MATRIX:
screenSensorOffset += 6;
break;
default:
;
}
if ((mode & CMT_OUTPUTMODE_POSITION) != 0)
screenSensorOffset += 4;
}
}
//////////////////////////////////////////////////////////////////////////
// clrscr
//
// Clear console screen
void clrscr()
{
#ifdef WIN32
CONSOLE_SCREEN_BUFFER_INFO csbi;
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
COORD coord = {0, 0};
DWORD count;
GetConsoleScreenBufferInfo(hStdOut, &csbi);
FillConsoleOutputCharacter(hStdOut, ' ', csbi.dwSize.X * csbi.dwSize.Y, coord, &count);
SetConsoleCursorPosition(hStdOut, coord);
#else
int i;
for (i = 0; i < 100; i++)
// Insert new lines to create a blank screen
putchar('\n');
gotoxy(0,0);
#endif
}
//////////////////////////////////////////////////////////////////////////
// gotoxy
//
// Sets the cursor position at the specified console position
//
// Input
// x : New horizontal cursor position
// y : New vertical cursor position
void gotoxy(int x, int y)
{
#ifdef WIN32
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
#else
char essq[100]; // String variable to hold the escape sequence
char xstr[100]; // Strings to hold the x and y coordinates
char ystr[100]; // Escape sequences must be built with characters
/*
** Convert the screen coordinates to strings
*/
sprintf(xstr, "%d", x);
sprintf(ystr, "%d", y);
/*
** Build the escape sequence (vertical move)
*/
essq[0] = '\0';
strcat(essq, "\033[");
strcat(essq, ystr);
/*
** Described in man terminfo as vpa=\E[%p1%dd
** Vertical position absolute
*/
strcat(essq, "d");
/*
** Horizontal move
** Horizontal position absolute
*/
strcat(essq, "\033[");
strcat(essq, xstr);
// Described in man terminfo as hpa=\E[%p1%dG
strcat(essq, "G");
/*
** Execute the escape sequence
** This will move the cursor to x, y
*/
printf("%s", essq);
#endif
}
//////////////////////////////////////////////////////////////////////////
// exitFunc
//
// Closes cmt nicely
void exitFunc(void)
{
// if we have a valid instance, we should get rid of it at the end of the program
if (instance != -1) {
// Close any open COM ports
cmtClose(instance);
cmtDestroyInstance(instance);
}
// get rid of keystrokes before we post our message
while (_kbhit()) _getch();
// wait for a keypress
if (!userQuit)
{
//printf("Press a key to exit\n");
//_getch();
}
}
main.h
void doHardwareScan();
void doMtSettings(void);
void getUserInputs(void);
void clrscr(void);
void writeHeaders(void);
void gotoxy(int x, int y);
void calcScreenOffset(void);
void exitFunc(void);
I am trying to write a simple audio capturing application in c++. and i found it is a very hard job to capture audio using c++ library, i couldn't find no more than a few library and I came up with ImediaObject. I found everything is okay in some other pc, but in my pc it can't record audio. When I call ImediaObject.ProcessOutput, it returns S_FALSE. I don't know what's wrong with my pc. I am using Visual Studio 2019.
Can anyone give me any clue ? what is wrong here ?
For your understanding I am sharing my main code here
#include <windows.h>
#include <dmo.h>
#include <Mmsystem.h>
#include <objbase.h>
#include <mediaobj.h>
#include <uuids.h>
#include <propidl.h>
#include <wmcodecdsp.h>
#include <atlbase.h>
#include <ATLComCli.h>
#include <audioclient.h>
#include <MMDeviceApi.h>
#include <AudioEngineEndPoint.h>
#include <DeviceTopology.h>
#include <propkey.h>
#include <strsafe.h>
#include <conio.h>
#include "AecKsBinder.h"
#include "mediabuf.h"
#define SAFE_ARRAYDELETE(p) {if (p) delete[] (p); (p) = NULL;}
#define SAFE_RELEASE(p) {if (NULL != p) {(p)->Release(); (p) = NULL;}}
#define VBFALSE (VARIANT_BOOL)0
#define VBTRUE (VARIANT_BOOL)-1
#define STREAM_BUFFER_LENGTH 0.1f //streaming buffer is 0.1 second long.
#define CHECK_RET(hr, message) if (FAILED(hr)) { puts(message); goto exit;}
#define CHECKHR(x) hr = x; if (FAILED(hr)) {printf("%d: %08X\n", __LINE__, hr); goto exit;}
#define CHECK_ALLOC(pb, message) if (NULL == pb) { puts(message); goto exit;}
class CStaticMediaBuffer : public CBaseMediaBuffer {
public:
STDMETHODIMP_(ULONG) AddRef() { return 2; }
STDMETHODIMP_(ULONG) Release() { return 1; }
void Init(BYTE* pData, ULONG ulSize, ULONG ulData) {
m_pData = pData;
m_ulSize = ulSize;
m_ulData = ulData;
}
};
void OutputUsage();
int __cdecl _tmain()
//int __cdecl _tmain()
{
HRESULT hr = S_OK;
CoInitialize(NULL);
IMediaObject* pDMO = NULL;
IPropertyStore* pPS = NULL;
CStaticMediaBuffer outputBuffer;
DMO_OUTPUT_DATA_BUFFER OutputBufferStruct = { 0 };
OutputBufferStruct.pBuffer = &outputBuffer;
DMO_MEDIA_TYPE mt = { 0 };
ULONG cbProduced = 0;
DWORD dwStatus;
// Parameters to config DMO
int iSystemMode = MODE_NOT_SET; // AEC-MicArray DMO system mode
int iOutFileIdx = -1; // argument index for otuput file name
int iMicDevIdx = -2; // microphone device index
int iSpkDevIdx = -2; // speaker device index
BOOL bFeatrModeOn = 0; // turn feature mode on/off
BOOL bNoiseSup = 1; // turn noise suppression on/off
BOOL bAGC = 0; // turn digital auto gain control on/off
BOOL bCntrClip = 0; // turn center clippng on/off
// control how long the Demo runs
int iDuration = 60; // seconds
int cTtlToGo = 0;
FILE* pfMicOutPCM; // dump output signal using PCM format
DWORD cOutputBufLen = 0;
BYTE* pbOutputBuffer = NULL;
UINT uCapDevCount = 0;
UINT uRenDevCount = 0;
char pcScanBuf[256] = { 0 };
WAVEFORMATEX wfxOut = { WAVE_FORMAT_PCM, 1, 22050, 44100, 2, 16, 0 };
AUDIO_DEVICE_INFO* pCaptureDeviceInfo = NULL, * pRenderDeviceInfo = NULL;
int i;
iMicDevIdx = 0;
iSpkDevIdx = 0;
iSystemMode = 0;
bFeatrModeOn = 1;
bNoiseSup = 1;
bAGC = 1;
bCntrClip = 1;
HANDLE currThread;
HANDLE currProcess;
BOOL iRet;
currProcess = GetCurrentProcess();
currThread = GetCurrentThread();
iRet = SetPriorityClass(currProcess, HIGH_PRIORITY_CLASS);
if (0 == iRet)
{
// call getLastError.
puts("failed to set process priority\n");
goto exit;
}
// DMO initialization
CHECKHR(CoCreateInstance(CLSID_CWMAudioAEC, NULL, CLSCTX_INPROC_SERVER, IID_IMediaObject, (void**)&pDMO));
CHECKHR(pDMO->QueryInterface(IID_IPropertyStore, (void**)&pPS));
// Select capture device
hr = GetCaptureDeviceNum(uCapDevCount);
CHECK_RET(hr, "GetCaptureDeviceNum failed");
pCaptureDeviceInfo = new AUDIO_DEVICE_INFO[uCapDevCount];
hr = EnumCaptureDevice(uCapDevCount, pCaptureDeviceInfo);
CHECK_RET(hr, "EnumCaptureDevice failed");
printf("\nSystem has totally %d capture devices\n", uCapDevCount);
for (i = 0; i < (int)uCapDevCount; i++)
{
_tprintf(_T("Device %d is %s"), i, pCaptureDeviceInfo[i].szDeviceName);
if (pCaptureDeviceInfo[i].bIsMicArrayDevice)
_tprintf(_T(" -- Mic Array Device \n"));
else
_tprintf(_T("\n"));
}
if (iMicDevIdx < -1 || iMicDevIdx >= (int)uCapDevCount)
{
do {
printf("Select device ");
scanf_s("%255s", pcScanBuf, 255);
iMicDevIdx = atoi(pcScanBuf);
if (iMicDevIdx < -1 || iMicDevIdx >= (int)uCapDevCount)
printf("Invalid Capture Device ID \n");
else
break;
} while (1);
}
if (iMicDevIdx == -1)
_tprintf(_T("\n Default device will be used for capturing \n"));
else
_tprintf(_T("\n %s is selected for capturing\n"), pCaptureDeviceInfo[iMicDevIdx].szDeviceName);
SAFE_ARRAYDELETE(pCaptureDeviceInfo);
// Select render device
if (iSystemMode == SINGLE_CHANNEL_AEC ||
iSystemMode == ADAPTIVE_ARRAY_AND_AEC ||
iSystemMode == OPTIBEAM_ARRAY_AND_AEC)
{
hr = GetRenderDeviceNum(uRenDevCount);
CHECK_RET(hr, "GetRenderDeviceNum failed");
pRenderDeviceInfo = new AUDIO_DEVICE_INFO[uRenDevCount];
hr = EnumRenderDevice(uRenDevCount, pRenderDeviceInfo);
CHECK_RET(hr, "EnumRenderDevice failed");
printf("\nSystem has totally %d render devices\n", uRenDevCount);
for (i = 0; i < (int)uRenDevCount; i++)
{
_tprintf(_T("Device %d is %s \n"), i, pRenderDeviceInfo[i].szDeviceName);
}
if (iSpkDevIdx < -1 || iSpkDevIdx >= (int)uRenDevCount)
{
do {
printf("Select device ");
scanf_s("%255s", pcScanBuf, 255);
iSpkDevIdx = atoi(pcScanBuf);
if (iSpkDevIdx < -1 || iSpkDevIdx >= (int)uRenDevCount)
printf("Invalid Render Device ID \n");
else
break;
} while (1);
}
if (iSpkDevIdx == -1)
_tprintf(_T("\n Default device will be used for rendering \n"));
else
_tprintf(_T("\n %s is selected for rendering \n"), pRenderDeviceInfo[iSpkDevIdx].szDeviceName);
}
else {
iSpkDevIdx = -1;
}
SAFE_ARRAYDELETE(pRenderDeviceInfo);
TCHAR* fileName;
fileName = (TCHAR*)"test.raw";
// --- PREPARE OUTPUT --- //
if (NULL != _tfopen_s(&pfMicOutPCM, fileName, _T("wb")))
{
puts("cannot open file for output.\n");
goto exit;
}
// Set AEC mode and other parameters
// Not all user changeable options are given in this sample code.
// Please refer to readme.txt for more options.
// Set AEC-MicArray DMO system mode.
// This must be set for the DMO to work properly
puts("\nAEC settings:");
PROPVARIANT pvSysMode;
PropVariantInit(&pvSysMode);
pvSysMode.vt = VT_I4;
pvSysMode.lVal = (LONG)(iSystemMode);
CHECKHR(pPS->SetValue(MFPKEY_WMAAECMA_SYSTEM_MODE, pvSysMode));
CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_SYSTEM_MODE, &pvSysMode));
printf("%20s %5d \n", "System Mode is", pvSysMode.lVal);
PropVariantClear(&pvSysMode);
// Tell DMO which capture and render device to use
// This is optional. If not specified, default devices will be used
if (iMicDevIdx >= 0 || iSpkDevIdx >= 0)
{
PROPVARIANT pvDeviceId;
PropVariantInit(&pvDeviceId);
pvDeviceId.vt = VT_I4;
pvDeviceId.lVal = (unsigned long)(iSpkDevIdx << 16) + (unsigned long)(0x0000ffff & iMicDevIdx);
CHECKHR(pPS->SetValue(MFPKEY_WMAAECMA_DEVICE_INDEXES, pvDeviceId));
CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_DEVICE_INDEXES, &pvDeviceId));
PropVariantClear(&pvDeviceId);
}
if (bFeatrModeOn)
{
// Turn on feature modes
PROPVARIANT pvFeatrModeOn;
PropVariantInit(&pvFeatrModeOn);
pvFeatrModeOn.vt = VT_BOOL;
pvFeatrModeOn.boolVal = bFeatrModeOn ? VBTRUE : VBFALSE;
CHECKHR(pPS->SetValue(MFPKEY_WMAAECMA_FEATURE_MODE, pvFeatrModeOn));
CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_FEATURE_MODE, &pvFeatrModeOn));
printf("%20s %5d \n", "Feature Mode is", pvFeatrModeOn.boolVal);
PropVariantClear(&pvFeatrModeOn);
// Turn on/off noise suppression
PROPVARIANT pvNoiseSup;
PropVariantInit(&pvNoiseSup);
pvNoiseSup.vt = VT_I4;
pvNoiseSup.lVal = (LONG)bNoiseSup;
CHECKHR(pPS->SetValue(MFPKEY_WMAAECMA_FEATR_NS, pvNoiseSup));
CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_FEATR_NS, &pvNoiseSup));
printf("%20s %5d \n", "Noise suppresion is", pvNoiseSup.lVal);
PropVariantClear(&pvNoiseSup);
// Turn on/off AGC
PROPVARIANT pvAGC;
PropVariantInit(&pvAGC);
pvAGC.vt = VT_BOOL;
pvAGC.boolVal = bAGC ? VBTRUE : VBFALSE;
CHECKHR(pPS->SetValue(MFPKEY_WMAAECMA_FEATR_AGC, pvAGC));
CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_FEATR_AGC, &pvAGC));
printf("%20s %5d \n", "AGC is", pvAGC.boolVal);
PropVariantClear(&pvAGC);
// Turn on/off center clip
PROPVARIANT pvCntrClip;
PropVariantInit(&pvCntrClip);
pvCntrClip.vt = VT_BOOL;
pvCntrClip.boolVal = bCntrClip ? VBTRUE : VBFALSE;
CHECKHR(pPS->SetValue(MFPKEY_WMAAECMA_FEATR_CENTER_CLIP, pvCntrClip));
CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_FEATR_CENTER_CLIP, &pvCntrClip));
printf("%20s %5d \n", "Center clip is", (BOOL)pvCntrClip.boolVal);
PropVariantClear(&pvCntrClip);
}
// Set DMO output format
hr = MoInitMediaType(&mt, sizeof(WAVEFORMATEX));
CHECK_RET(hr, "MoInitMediaType failed");
mt.majortype = MEDIATYPE_Audio;
mt.subtype = MEDIASUBTYPE_PCM;
mt.lSampleSize = 0;
mt.bFixedSizeSamples = TRUE;
mt.bTemporalCompression = FALSE;
mt.formattype = FORMAT_WaveFormatEx;
memcpy(mt.pbFormat, &wfxOut, sizeof(WAVEFORMATEX));
hr = pDMO->SetOutputType(0, &mt, 0);
CHECK_RET(hr, "SetOutputType failed");
MoFreeMediaType(&mt);
// Allocate streaming resources. This step is optional. If it is not called here, it
// will be called when first time ProcessInput() is called. However, if you want to
// get the actual frame size being used, it should be called explicitly here.
hr = pDMO->AllocateStreamingResources();
CHECK_RET(hr, "AllocateStreamingResources failed");
// Get actually frame size being used in the DMO. (optional, do as you need)
int iFrameSize;
PROPVARIANT pvFrameSize;
PropVariantInit(&pvFrameSize);
CHECKHR(pPS->GetValue(MFPKEY_WMAAECMA_FEATR_FRAME_SIZE, &pvFrameSize));
iFrameSize = pvFrameSize.lVal;
PropVariantClear(&pvFrameSize);
// allocate output buffer
cOutputBufLen = wfxOut.nSamplesPerSec * wfxOut.nBlockAlign;
pbOutputBuffer = new BYTE[cOutputBufLen];
CHECK_ALLOC(pbOutputBuffer, "out of memory.\n");
// number of frames to play
cTtlToGo = iDuration * 100;
// main loop to get mic output from the DMO
puts("\nAEC-MicArray is running ... Press \"s\" to stop");
while (1)
{
Sleep(10); //sleep 10ms
if (cTtlToGo-- <= 0)
break;
do {
outputBuffer.Init((byte*)pbOutputBuffer, cOutputBufLen, 0);
OutputBufferStruct.dwStatus = 0;
hr = pDMO->ProcessOutput(0, 1, &OutputBufferStruct, &dwStatus);
CHECK_RET(hr, "ProcessOutput failed");
if (hr == S_FALSE) {
cbProduced = 0;
}
else {
hr = outputBuffer.GetBufferAndLength(NULL, &cbProduced);
CHECK_RET(hr, "GetBufferAndLength failed");
}
// dump output data into a file with PCM format.
if (fwrite(pbOutputBuffer, 1, cbProduced, pfMicOutPCM) != cbProduced)
{
puts("write error");
goto exit;
}
} while (OutputBufferStruct.dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE);
// check keyboard input to stop
if (_kbhit())
{
int ch = _getch();
if (ch == 's' || ch == 'S')
break;
}
}
exit:
SAFE_ARRAYDELETE(pbOutputBuffer);
SAFE_ARRAYDELETE(pCaptureDeviceInfo);
SAFE_ARRAYDELETE(pRenderDeviceInfo);
SAFE_RELEASE(pDMO);
SAFE_RELEASE(pPS);
CoUninitialize();
return hr;
}
void OutputUsage()
{
printf("MFWMAAEC (Aec-MicArray DMO) Demo. \n");
printf("Copyright (c) 2004-2006, Microsoft Corporation. All rights reserved. \n\n");
printf("Usage: AecSDKDemo.exe -out mic_out.pcm -mod 0 [-feat 1] [-ns 1] [-agc 0] \n");
printf(" [-cntrclip 0] [-micdev 0] [-spkdev 0] [-duration 60]\n");
return;
}
NOTE: My main concern is to record audio and use the native noise and echo canceler. If there is any suggestion regarding this, I would very much appriciate it. Thanks.
I want to decode H.264 video from a collection of MPEG-2 Transport Stream packets but I am not clear what to pass to avcodec_decode_video2
The documentation says to pass "the input AVPacket containing the input buffer."
But what should be in the input buffer?
A PES packet will be spread across the payload portion of several TS packets, with NALU(s) inside the PES. So pass a TS fragment? The entire PES? PES payload only?
This Sample Code mentions:
BUT some other codecs (msmpeg4, mpeg4) are inherently frame based, so
you must call them with all the data for one frame exactly. You must
also initialize 'width' and 'height' before initializing them.
But I can find no info on what "all the data" means...
Passing a fragment of a TS packet payload is not working:
AVPacket avDecPkt;
av_init_packet(&avDecPkt);
avDecPkt.data = inbuf_ptr;
avDecPkt.size = esBufSize;
len = avcodec_decode_video2(mpDecoderContext, mpFrameDec, &got_picture, &avDecPkt);
if (len < 0)
{
printf(" TS PKT #%.0f. Error decoding frame #%04d [rc=%d '%s']\n",
tsPacket.pktNum, mDecodedFrameNum, len, av_make_error_string(errMsg, 128, len));
return;
}
output
[h264 # 0x81cd2a0] no frame!
TS PKT #2973. Error decoding frame #0001 [rc=-1094995529 'Invalid data found when processing input']
EDIT
Using the excellent hits from WLGfx, I made this simple program to try decoding TS packets. As input, I prepared a file containing only TS packets from the Video PID.
It feels close but I don't know how to set up the FormatContext. The code below segfaults at av_read_frame() (and internally at ret = s->iformat->read_packet(s, pkt)). s->iformat is zero.
Suggestions?
EDIT II - Sorry, for got post source code **
**EDIT III - Sample code updated to simulate reading TS PKT Queue
/*
* Test program for video decoder
*/
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
extern "C" {
#ifdef __cplusplus
#define __STDC_CONSTANT_MACROS
#ifdef _STDINT_H
#undef _STDINT_H
#endif
#include <stdint.h>
#endif
}
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "libavutil/opt.h"
}
class VideoDecoder
{
public:
VideoDecoder();
bool rcvTsPacket(AVPacket &inTsPacket);
private:
AVCodec *mpDecoder;
AVCodecContext *mpDecoderContext;
AVFrame *mpDecodedFrame;
AVFormatContext *mpFmtContext;
};
VideoDecoder::VideoDecoder()
{
av_register_all();
// FORMAT CONTEXT SETUP
mpFmtContext = avformat_alloc_context();
mpFmtContext->flags = AVFMT_NOFILE;
// ????? WHAT ELSE ???? //
// DECODER SETUP
mpDecoder = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!mpDecoder)
{
printf("Could not load decoder\n");
exit(11);
}
mpDecoderContext = avcodec_alloc_context3(NULL);
if (avcodec_open2(mpDecoderContext, mpDecoder, NULL) < 0)
{
printf("Cannot open decoder context\n");
exit(1);
}
mpDecodedFrame = av_frame_alloc();
}
bool
VideoDecoder::rcvTsPacket(AVPacket &inTsPkt)
{
bool ret = true;
if ((av_read_frame(mpFmtContext, &inTsPkt)) < 0)
{
printf("Error in av_read_frame()\n");
ret = false;
}
else
{
// success. Decode the TS packet
int got;
int len = avcodec_decode_video2(mpDecoderContext, mpDecodedFrame, &got, &inTsPkt);
if (len < 0)
ret = false;
if (got)
printf("GOT A DECODED FRAME\n");
}
return ret;
}
int
main(int argc, char **argv)
{
if (argc != 2)
{
printf("Usage: %s tsInFile\n", argv[0]);
exit(1);
}
FILE *tsInFile = fopen(argv[1], "r");
if (!tsInFile)
{
perror("Could not open TS input file");
exit(2);
}
unsigned int tsPktNum = 0;
uint8_t tsBuffer[256];
AVPacket tsPkt;
av_init_packet(&tsPkt);
VideoDecoder vDecoder;
while (!feof(tsInFile))
{
tsPktNum++;
tsPkt.size = 188;
tsPkt.data = tsBuffer;
fread(tsPkt.data, 188, 1, tsInFile);
vDecoder.rcvTsPacket(tsPkt);
}
}
I've got some code snippets that might help you out as I've been working with MPEG-TS also.
Starting with my packet thread which checks each packet against the stream ID's which I've already found and got the codec contexts:
void *FFMPEG::thread_packet_function(void *arg) {
FFMPEG *ffmpeg = (FFMPEG*)arg;
for (int c = 0; c < MAX_PACKETS; c++)
ffmpeg->free_packets[c] = &ffmpeg->packet_list[c];
ffmpeg->packet_pos = MAX_PACKETS;
Audio.start_decoding();
Video.start_decoding();
Subtitle.start_decoding();
while (!ffmpeg->thread_quit) {
if (ffmpeg->packet_pos != 0 &&
Audio.okay_add_packet() &&
Video.okay_add_packet() &&
Subtitle.okay_add_packet()) {
pthread_mutex_lock(&ffmpeg->packet_mutex); // get free packet
AVPacket *pkt = ffmpeg->free_packets[--ffmpeg->packet_pos]; // pre decrement
pthread_mutex_unlock(&ffmpeg->packet_mutex);
if ((av_read_frame(ffmpeg->fContext, pkt)) >= 0) { // success
int id = pkt->stream_index;
if (id == ffmpeg->aud_stream.stream_id) Audio.add_packet(pkt);
else if (id == ffmpeg->vid_stream.stream_id) Video.add_packet(pkt);
else if (id == ffmpeg->sub_stream.stream_id) Subtitle.add_packet(pkt);
else { // unknown packet
av_packet_unref(pkt);
pthread_mutex_lock(&ffmpeg->packet_mutex); // put packet back
ffmpeg->free_packets[ffmpeg->packet_pos++] = pkt;
pthread_mutex_unlock(&ffmpeg->packet_mutex);
//LOGI("Dumping unknown packet, id %d", id);
}
} else {
av_packet_unref(pkt);
pthread_mutex_lock(&ffmpeg->packet_mutex); // put packet back
ffmpeg->free_packets[ffmpeg->packet_pos++] = pkt;
pthread_mutex_unlock(&ffmpeg->packet_mutex);
//LOGI("No packet read");
}
} else { // buffers full so yield
//LOGI("Packet reader on hold: Audio-%d, Video-%d, Subtitle-%d",
// Audio.packet_pos, Video.packet_pos, Subtitle.packet_pos);
usleep(1000);
//sched_yield();
}
}
return 0;
}
Each decoder for audio, video and subtitles have their own threads which receive the packets from the above thread in ring buffers. I've had to separate the decoders into their own threads because CPU usage was increasing when I started using the deinterlace filter.
My video decoder reads the packets from the buffers and when it has finished with the packet sends it back to be unref'd and can be used again. Balancing the packet buffers doesn't take that much time once everything is running.
Here's the snipped from my video decoder:
void *VideoManager::decoder(void *arg) {
LOGI("Video decoder started");
VideoManager *mgr = (VideoManager *)arg;
while (!ffmpeg.thread_quit) {
pthread_mutex_lock(&mgr->packet_mutex);
if (mgr->packet_pos != 0) {
// fetch first packet to decode
AVPacket *pkt = mgr->packets[0];
// shift list down one
for (int c = 1; c < mgr->packet_pos; c++) {
mgr->packets[c-1] = mgr->packets[c];
}
mgr->packet_pos--;
pthread_mutex_unlock(&mgr->packet_mutex); // finished with packets array
int got;
AVFrame *frame = ffmpeg.vid_stream.frame;
avcodec_decode_video2(ffmpeg.vid_stream.context, frame, &got, pkt);
ffmpeg.finished_with_packet(pkt);
if (got) {
#ifdef INTERLACE_ALL
if (!frame->interlaced_frame) mgr->add_av_frame(frame, 0);
else {
if (!mgr->filter_initialised) mgr->init_filter_graph(frame);
av_buffersrc_add_frame_flags(mgr->filter_src_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF);
int c = 0;
while (true) {
AVFrame *filter_frame = ffmpeg.vid_stream.filter_frame;
int result = av_buffersink_get_frame(mgr->filter_sink_ctx, filter_frame);
if (result == AVERROR(EAGAIN) ||
result == AVERROR(AVERROR_EOF) ||
result < 0)
break;
mgr->add_av_frame(filter_frame, c++);
av_frame_unref(filter_frame);
}
//LOGI("Interlaced %d frames, decode %d, playback %d", c, mgr->decode_pos, mgr->playback_pos);
}
#elif defined(INTERLACE_HALF)
if (!frame->interlaced_frame) mgr->add_av_frame(frame, 0);
else {
if (!mgr->filter_initialised) mgr->init_filter_graph(frame);
av_buffersrc_add_frame_flags(mgr->filter_src_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF);
int c = 0;
while (true) {
AVFrame *filter_frame = ffmpeg.vid_stream.filter_frame;
int result = av_buffersink_get_frame(mgr->filter_sink_ctx, filter_frame);
if (result == AVERROR(EAGAIN) ||
result == AVERROR(AVERROR_EOF) ||
result < 0)
break;
mgr->add_av_frame(filter_frame, c++);
av_frame_unref(filter_frame);
}
//LOGI("Interlaced %d frames, decode %d, playback %d", c, mgr->decode_pos, mgr->playback_pos);
}
#else
mgr->add_av_frame(frame, 0);
#endif
}
//LOGI("decoded video packet");
} else {
pthread_mutex_unlock(&mgr->packet_mutex);
}
}
LOGI("Video decoder ended");
}
As you can see, I'm using a mutex when passing packets back and forth.
Once a frame has been got I just copy the YUV buffers from the frame for later use into another buffer list. I don't convert the YUV, I use a shader which converts the YUV to RGB on the GPU.
The next snippet adds my decoded frame to my buffer list. This may help understand how to deal with the data.
void VideoManager::add_av_frame(AVFrame *frame, int field_num) {
int y_linesize = frame->linesize[0];
int u_linesize = frame->linesize[1];
int hgt = frame->height;
int y_buffsize = y_linesize * hgt;
int u_buffsize = u_linesize * hgt / 2;
int buffsize = y_buffsize + u_buffsize + u_buffsize;
VideoBuffer *buffer = &buffers[decode_pos];
if (ffmpeg.is_network && playback_pos == decode_pos) { // patched 25/10/16 wlgfx
buffer->used = false;
if (!buffer->data) buffer->data = (char*)mem.alloc(buffsize);
if (!buffer->data) {
LOGI("Dropped frame, allocation error");
return;
}
} else if (playback_pos == decode_pos) {
LOGI("Dropped frame, ran out of decoder frame buffers");
return;
} else if (!buffer->data) {
buffer->data = (char*)mem.alloc(buffsize);
if (!buffer->data) {
LOGI("Dropped frame, allocation error.");
return;
}
}
buffer->y_frame = buffer->data;
buffer->u_frame = buffer->y_frame + y_buffsize;
buffer->v_frame = buffer->y_frame + y_buffsize + u_buffsize;
buffer->wid = frame->width;
buffer->hgt = hgt;
buffer->y_linesize = y_linesize;
buffer->u_linesize = u_linesize;
int64_t pts = av_frame_get_best_effort_timestamp(frame);
buffer->pts = pts;
buffer->buffer_size = buffsize;
double field_add = av_q2d(ffmpeg.vid_stream.context->time_base) * field_num;
buffer->frame_time = av_q2d(ts_stream) * pts + field_add;
memcpy(buffer->y_frame, frame->data[0], (size_t) (buffer->y_linesize * buffer->hgt));
memcpy(buffer->u_frame, frame->data[1], (size_t) (buffer->u_linesize * buffer->hgt / 2));
memcpy(buffer->v_frame, frame->data[2], (size_t) (buffer->u_linesize * buffer->hgt / 2));
buffer->used = true;
decode_pos = (++decode_pos) % MAX_VID_BUFFERS;
//if (field_num == 0) LOGI("Video %.2f, %d - %d",
// buffer->frame_time - Audio.pts_start_time, decode_pos, playback_pos);
}
If there's anything else that I may be able to help with just give me a shout. :-)
EDIT:
The snippet how I open my video stream context which automatically determines the codec, whether it is h264, mpeg2, or another:
void FFMPEG::open_video_stream() {
vid_stream.stream_id = av_find_best_stream(fContext, AVMEDIA_TYPE_VIDEO,
-1, -1, &vid_stream.codec, 0);
if (vid_stream.stream_id == -1) return;
vid_stream.context = fContext->streams[vid_stream.stream_id]->codec;
if (!vid_stream.codec || avcodec_open2(vid_stream.context,
vid_stream.codec, NULL) < 0) {
vid_stream.stream_id = -1;
return;
}
vid_stream.frame = av_frame_alloc();
vid_stream.filter_frame = av_frame_alloc();
}
EDIT2:
This is how I've opened the input stream, whether it be file or URL. The AVFormatContext is the main context for the stream.
bool FFMPEG::start_stream(char *url_, float xtrim, float ytrim, int gain) {
aud_stream.stream_id = -1;
vid_stream.stream_id = -1;
sub_stream.stream_id = -1;
this->url = url_;
this->xtrim = xtrim;
this->ytrim = ytrim;
Audio.volume = gain;
Audio.init();
Video.init();
fContext = avformat_alloc_context();
if ((avformat_open_input(&fContext, url_, NULL, NULL)) != 0) {
stop_stream();
return false;
}
if ((avformat_find_stream_info(fContext, NULL)) < 0) {
stop_stream();
return false;
}
// network stream will overwrite packets if buffer is full
is_network = url.substr(0, 4) == "udp:" ||
url.substr(0, 4) == "rtp:" ||
url.substr(0, 5) == "rtsp:" ||
url.substr(0, 5) == "http:"; // added for wifi broadcasting ability
// determine if stream is audio only
is_mp3 = url.substr(url.size() - 4) == ".mp3";
LOGI("Stream: %s", url_);
if (!open_audio_stream()) {
stop_stream();
return false;
}
if (is_mp3) {
vid_stream.stream_id = -1;
sub_stream.stream_id = -1;
} else {
open_video_stream();
open_subtitle_stream();
if (vid_stream.stream_id == -1) { // switch to audio only
close_subtitle_stream();
is_mp3 = true;
}
}
LOGI("Audio: %d, Video: %d, Subtitle: %d",
aud_stream.stream_id,
vid_stream.stream_id,
sub_stream.stream_id);
if (aud_stream.stream_id != -1) {
LOGD("Audio stream time_base {%d, %d}",
aud_stream.context->time_base.num,
aud_stream.context->time_base.den);
}
if (vid_stream.stream_id != -1) {
LOGD("Video stream time_base {%d, %d}",
vid_stream.context->time_base.num,
vid_stream.context->time_base.den);
}
LOGI("Starting packet and decode threads");
thread_quit = false;
pthread_create(&thread_packet, NULL, &FFMPEG::thread_packet_function, this);
Display.set_overlay_timout(3.0);
return true;
}
EDIT: (constructing an AVPacket)
Construct an AVPacket to send to the decoder...
AVPacket packet;
av_init_packet(&packet);
packet.data = myTSpacketdata; // pointer to the TS packet
packet.size = 188;
You should be able to reuse the packet. And it might need unref'ing.
You must first use the avcodec library to get the compressed frames out of the file. Then you can decode them using avcodec_decode_video2. look at this tutorial http://dranger.com/ffmpeg/
I am using libftdi with multiple ftdi devices for a program running on Ubuntu 14.04. I have a udev rule that detects the devices based on a custom manufacturer string and gives them a symlink in the dev directory. It would look similar to /dev/my-device. I would like to use libftdi to open the device using this string instead of the pid/vid/serial number.
I did not see that this capability was available in libftdi so I checked libusb and didn't see that functionality either.
You could try this:
static int usbGetDescriptorString(usb_dev_handle *dev, int index, int langid, char *buf, int buflen) {
char buffer[256];
int rval, i;
// make standard request GET_DESCRIPTOR, type string and given index
// (e.g. dev->iProduct)
rval = usb_control_msg(dev,
USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_ENDPOINT_IN,
USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, langid,
buffer, sizeof(buffer), 1000);
if (rval < 0) // error
return rval;
// rval should be bytes read, but buffer[0] contains the actual response size
if ((unsigned char)buffer[0] < rval)
rval = (unsigned char)buffer[0]; // string is shorter than bytes read
if (buffer[1] != USB_DT_STRING) // second byte is the data type
return 0; // invalid return type
// we're dealing with UTF-16LE here so actual chars is half of rval,
// and index 0 doesn't count
rval /= 2;
/* lossy conversion to ISO Latin1 */
for (i = 1; i < rval && i < buflen; i++) {
if (buffer[2 * i + 1] == 0)
buf[i - 1] = buffer[2 * i];
else
buf[i - 1] = '?'; /* outside of ISO Latin1 range */
}
buf[i - 1] = 0;
return i - 1;
}
static usb_dev_handle * usbOpenDevice(int vendor, char *vendorName, int product, char *productName) {
struct usb_bus *bus;
struct usb_device *dev;
char devVendor[256], devProduct[256];
usb_dev_handle * handle = NULL;
usb_init();
usb_find_busses();
usb_find_devices();
for (bus = usb_get_busses(); bus; bus = bus->next) {
for (dev = bus->devices; dev; dev = dev->next) {
if (dev->descriptor.idVendor != vendor ||
dev->descriptor.idProduct != product)
continue;
/* we need to open the device in order to query strings */
if (!(handle = usb_open(dev))) {
fprintf(stderr, "Warning: cannot open USB device: %sn",
usb_strerror());
continue;
}
/* get vendor name */
if (usbGetDescriptorString(handle, dev->descriptor.iManufacturer, 0x0409, devVendor, sizeof(devVendor)) < 0) {
fprintf(stderr,
"Warning: cannot query manufacturer for device: %sn",
usb_strerror());
usb_close(handle);
continue;
}
/* get product name */
if (usbGetDescriptorString(handle, dev->descriptor.iProduct, 0x0409, devProduct, sizeof(devVendor)) < 0) {
fprintf(stderr,
"Warning: cannot query product for device: %sn",
usb_strerror());
usb_close(handle);
continue;
}
if (strcmp(devVendor, vendorName) == 0 &&
strcmp(devProduct, productName) == 0)
return handle;
else
usb_close(handle);
}
}
return NULL;
}
I'm using WinCE 6.0 device for serial communication. It has 4 serial ports, of which I'm using 2, one is for RS232 and the other is for RS485. serial port configured with RS232 is working fine but for RS485 communication is not working below is the code,
int MbusSerialSlaveProtocol::startupServer(int slaveAddr,
const TCHAR * const portName,
long baudRate, int dataBits,
int stopBits, int parity)
{
int result;
TRACELOG2("Open port: %d \n", portName);
TRACELOG5("Parameters: %d, %d, %d, %d\n",
baudRate, dataBits, stopBits, parity);
TRACELOG2("Configuration: %d\n", timeOut);
if (isStarted())
return (FTALK_ILLEGAL_STATE_ERROR);
if ((slaveAddr <= 0) || (slaveAddr > 255))
return (FTALK_ILLEGAL_ARGUMENT_ERROR);
this->slaveAddr = slaveAddr;
if ((dataBits != SerialPort::SER_DATABITS_7) &&
(dataBits != SerialPort::SER_DATABITS_8))
return(FTALK_ILLEGAL_ARGUMENT_ERROR);
// Close an existing connection
if (serialPort.isOpen())
serialPort.closePort();
result = serialPort.openPort(portName);
if (result == SerialPort::SER_PORT_NO_ACCESS)
return(FTALK_PORT_NO_ACCESS);
if (result == SerialPort::SER_ALREADY_OPEN)
return(FTALK_PORT_ALREADY_OPEN);
if (result != SerialPort::SER_SUCCESS)
return(FTALK_OPEN_ERR);
result = serialPort.config(baudRate, dataBits,
stopBits, parity,
SerialPort::SER_HANDSHAKE_NONE);
if (result != SerialPort::SER_SUCCESS)
{
serialPort.closePort();
return(FTALK_ILLEGAL_ARGUMENT_ERROR);
}
if (serialMode == SER_RS485)
serialPort.clearRts();
return (FTALK_SUCCESS);
}
int main()
{
protocol = new ModbusSerialSlave(dataTable);
int test = atce_uart_set_interface(6, 485);
//result =
((ModbusSerialSlave*)protocol)->startupServer(01,_T("COM6:"),19200,8,1,0);
((ModbusSerialSlave*)protocol)->enableRs485Mode(10);
result = ((ModbusSerialSlave*)protocol)->startupServer(01,_T("COM6:"),port.nBaudRate,port.nDataBit,port.nStopBit,port.nParity);
while(1)
{
protocol->serverLoop();
}
}
///////////////////////////////////////////////////////////////////////
I changed RTS signal to toggle state, but still it's not working. I increased RTS delay to 1000, tried to send data device but not getting result. there is no error in port opening or parameter settings, I monitored those with writing error messages to files. here is a code for port open and config.
int SerialPort::openPort(const TCHAR * const portName)
{
ofstream config_file(TEST_FILE_NAME3);
if (isOpen())
{
config_file.write("open1",5);
closePort();
}
port = CreateFile(portName, GENERIC_READ | GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, 0, NULL);
if (port == INVALID_HANDLE_VALUE)
{
switch (GetLastError())
{
case ERROR_FILE_NOT_FOUND:
{
config_file.write("open2",5);
return (SER_PORT_NOT_FOUND);
}
case ERROR_ACCESS_DENIED:
{
config_file.write("open3",5);
return (SER_ALREADY_OPEN);
}
default:
{
config_file.write("open4",5);
return (SER_API_ERROR);
}
}
}
// SetupComm(port, 256, 256); //ttt
// Save current device control block
if (!GetCommState(port, &savedDevCtrlBlock))
{
config_file.write("open5",5);
return (SER_API_ERROR);
}
this->flush(); // Needed on QNX 6 to clear a filled buffer
config_file.write("open6",5);
return (config(19200, SER_DATABITS_8, SER_STOPBITS_1,
SER_PARITY_NONE, SER_HANDSHAKE_NONE));
}
int SerialPort::config(long baudRate, int dataBits,
int stopBits, int parity, int flowControl)
{
DCB devCtrlBlock;
ofstream config_file(TEST_FILE_NAME);
if (!isOpen())
{
config_file.write("Line1",5);
return (SER_NOT_OPEN);
}
//
// Retrieve current device control block
//
if (!GetCommState(port, &devCtrlBlock))
{
config_file.write("Line2",5);
return (SER_API_ERROR);
}
//
// Modify device control block
//
devCtrlBlock.BaudRate = baudRate;
switch (dataBits)
{
case SER_DATABITS_7:
devCtrlBlock.ByteSize = 7;
break;
case SER_DATABITS_8:
devCtrlBlock.ByteSize = 8;
break;
default:
{
config_file.write("Line3",5);
return (SER_INVALID_PARAMETER);
}
}
switch (stopBits)
{
case SER_STOPBITS_1:
devCtrlBlock.StopBits = ONESTOPBIT;
break;
case SER_STOPBITS_2:
devCtrlBlock.StopBits = TWOSTOPBITS;
break;
default:
{
config_file.write("Line4",5);
return (SER_INVALID_PARAMETER);
}
}
switch (parity)
{
case SER_PARITY_NONE:
devCtrlBlock.fParity = FALSE;
devCtrlBlock.Parity = NOPARITY;
break;
case SER_PARITY_EVEN:
devCtrlBlock.fParity = TRUE;
devCtrlBlock.Parity = EVENPARITY;
break;
case SER_PARITY_ODD:
devCtrlBlock.fParity = TRUE;
devCtrlBlock.Parity = ODDPARITY;
break;
default:
{
config_file.write("Line5",5);
return (SER_INVALID_PARAMETER);
}
}
switch (flowControl)
{
case SER_HANDSHAKE_RTS_CTS:
devCtrlBlock.fOutX = FALSE; // Disable output X-ON/X-OFF
devCtrlBlock.fInX = FALSE; // Disable input X-ON/X-OFF
devCtrlBlock.fOutxCtsFlow = TRUE;
devCtrlBlock.fOutxDsrFlow = FALSE;
devCtrlBlock.fRtsControl = RTS_CONTROL_HANDSHAKE;
devCtrlBlock.fDtrControl = DTR_CONTROL_ENABLE;
devCtrlBlock.fDsrSensitivity = FALSE;
break;
case SER_HANDSHAKE_NONE:
devCtrlBlock.fOutX = FALSE; // Disable output X-ON/X-OFF
devCtrlBlock.fInX = FALSE; // Disable input X-ON/X-OFF
devCtrlBlock.fOutxCtsFlow = FALSE;
devCtrlBlock.fOutxDsrFlow = FALSE;
devCtrlBlock.fRtsControl = RTS_CONTROL_TOGGLE;
devCtrlBlock.fDtrControl = DTR_CONTROL_ENABLE;
devCtrlBlock.fDsrSensitivity = FALSE;
break;
default:
{
config_file.write("Line6",5);
return (SER_INVALID_PARAMETER);
}
}
devCtrlBlock.fBinary = TRUE;
devCtrlBlock.fErrorChar = FALSE;
devCtrlBlock.fNull = FALSE;
devCtrlBlock.fAbortOnError = FALSE;
//
// Store device control block
//
if (!SetCommState(port, &devCtrlBlock))
{
config_file.write("Line7",5);
return (SER_INVALID_PARAMETER);
}
this->baudRate = baudRate;
this->flowControl = flowControl;
config_file.write("Line8",5);
return (SER_SUCCESS);
}
I see that you clear the RTS signal, I suppose that this is used to enable the RS485 transceiver. RS485 is usually half duplex and you need to enable/disable transmission each time you send data. If you don't you can't transmit or, being always in transmission, you can't receive any data from other devices on the same line. You may need to toggle this signal to make the communication work.