Sorry for the bad code and English,
So I was testing out SFML Audio and I was trying to make an audio player,
but It crashes while I tried to run it,
So basically it was supposed to play an audio file and play it.
Also, there's another thing I'm having an issue with that is if the audio is paused it rewinds itself
as though it has been stopped
No arguments were given in and it didn't print anything either it just crashed.
#include <windows.h>
#include <iostream>
#include <SFML/Audio.hpp>
#include <string>
#include <thread>
sf::Music audio_player;
bool looping = false;
/*
*
* audio_state = 2 // Playing
* audio_state = 1 // Paused
* audio_state = 0 // Stopped
*
*/
bool audio_playable() // Checks if the audio is either paused or stopped
{
if(audio_player.getStatus() == 0 ||
audio_player.getStatus() == 1 )
return true;
else return false;
}
void loop()
{
looping = true;
while(1)
{
if(GetKeyState('P') & 0x8000 && audio_player.getStatus() == 2)
{
audio_player.pause();
std::cout << "Paused\n";
}
if(GetKeyState('S') & 0x8000 && audio_player.getStatus() == 2)
{
audio_player.stop();
std::cout << "Stoped\n";
}
if(GetKeyState('L') & 0x8000 && audio_playable() )
{
audio_player.play();
std::cout << "Started\n";
}
if(GetKeyState(VK_ESCAPE) & 0x8000)
{
looping = false;
}
}
}
int main(int argc, char* argv[])
{
std::string file_name = ""; //Initialize the file location
if(argc < 1) // If there are no arguments like the file location ask for the file location
{
std::cout << "File Location: ";
std::cin >> file_name;
if(file_name == "")
{
std::cout << "File Location cannot be null" << std::endl;
main(argc, argv);
}
}
if(file_name == "")file_name = argv[1];
audio_player.openFromFile(file_name);
audio_player.play();
std::thread pauseFunc(loop); // new thread for the pause play function
while(looping)
{
}
return 0;
}
Related
I've been implementing a dirty hack for a "panic" keyboard shortcut in a RHEL 8.4 environment to kill our application in the event that it hangs because we use a Mutter environment which prevents the user from being able to interact with the OS and only shows our application.
At first I looked into Gnome keyboard shortcuts, but it appears those get overridden or blocked entirely by Mutter, because that didn't work. So I repurposed some old driver code I wrote to write a standalone process that runs in the background and monitors the /dev/input/event file for keyboard events and responds to the Ctrl+Alt+Delete key sequence. When pressed, I use Qt to spawn a QMessageBox that when "Yes" is selected, it calls the script that kills and relaunches our application, and "No" just closes the dialog.
However, there's an issue where regardless of what you click, the dialog reappears. Through some testing, it appears this happens if you press Ctrl+Alt+Delete again while the dialog is visible, but I don't understand how that can happen, because the QMessageBox is modal and calling exec() on it should block the main thread, since the code immediately after reads the dialog result. So I also tried zeroing out the event data at the start of each loop to make sure a previous event isn't being read repeatedly while no events are taking place, but that had no effect either.
Can anyone tell what's going on here?
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <linux/input.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <QApplication>
#include <QMessageBox>
using namespace std;
#define BITS_PER_LONG (sizeof(long) * 8)
#define NBITS(x) (((x - 1) / BITS_PER_LONG) + 1)
#define OFF(x) (x % BITS_PER_LONG)
#define LONG(x) (x / BITS_PER_LONG)
#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
string detect();
void poll(string handler);
int main(int argc, char ** argv) {
QApplication app(argc, argv);
bool found = false;
string handler = detect();
poll(handler);
return app.exec();
}
string detect() {
int eventX = 0;
string handler = "";
bool found = false;
while(!found) {
ostringstream oss;
oss << "/dev/input/event" << eventX;
handler = oss.str();
int fd = open(handler.c_str(), O_RDONLY);
if(fd < 0) {
if((errno == EACCES) && (getuid() != 0)) {
cout << "You must run as root to detect the keyboard." << endl;
exit(1);
} else {
// No more event handler files.
break;
}
}
// Read event code bits from event handler file descriptor.
unsigned long bit[EV_MAX][NBITS(KEY_MAX)];
memset(bit, 0, sizeof(bit));
ioctl(fd, EVIOCGBIT(0, EV_MAX), bit[0]);
// Read key events from file descriptor.
ioctl(fd, EVIOCGBIT(EV_KEY, KEY_MAX), bit[EV_KEY]);
// Test to make sure the event file defines the Ctrl, Alt, and Delete key events.
if(
( test_bit( KEY_LEFTCTRL, bit[EV_KEY] ) || test_bit( KEY_RIGHTCTRL, bit[EV_KEY] ) ) &&
( test_bit( KEY_LEFTALT, bit[EV_KEY] ) || test_bit( KEY_RIGHTALT, bit[EV_KEY] ) ) &&
test_bit(KEY_DELETE, bit[EV_KEY])
) {
cout << "Keyboard Found # " << handler << endl;
found = true;
}
close(fd);
eventX++;
}
if(!found) {
cout << "No keyboard event file found." << endl;
exit(2);
}
return handler;
}
void poll(string handler) {
ifstream keyboard(handler.c_str(), ios::in | ios::binary);
struct input_event event;
char data[sizeof(event)];
int states[2] = { 0, 0 };
bool dialogBlocked = false;
bool done = false;
while(!done) {
if(keyboard.is_open() && keyboard.good()) {
memset(data, 0, sizeof(event));
keyboard.read(data, sizeof(event));
memcpy(&event, data, sizeof(event));
if((event.code == KEY_LEFTCTRL) || (event.code == KEY_RIGHTCTRL)) {
if(event.value > 0) {
states[0] = 1;
} else {
states[0] = 0;
}
}
if((event.code == KEY_LEFTALT) || (event.code == KEY_RIGHTALT)) {
if(event.value > 0) {
states[1] = 1;
} else {
states[1] = 0;
}
}
if((event.code == KEY_DELETE) && (event.value > 0)) {
if((states[0] > 0) && (states[1] > 0) && !dialogBlocked) {
// Ctrl+Alt+Delete detected. Call killX.
dialogBlocked = true;
QMessageBox msgBox;
msgBox.setModal(true);
msgBox.setText("Do you want to reset?");
msgBox.setWindowTitle("Reset");
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
msgBox.setStyleSheet(
"QMessageBox {"
" background: rgb(220, 220, 220);"
" color: rgb(0, 0, 0);"
"}"
"QPushButton {"
" border: 1px solid rgb(20, 20, 20);"
" border-radius: 5px;"
" padding: 2px;"
"}"
"QPushButton:enabled {"
" background: rgb(240, 240, 240);"
" color: rgb(20, 20, 20);"
"}"
"QPushButton:!enabled {"
" background: rgb(200, 200, 200);"
" color: rgb(250, 250, 250);"
"}"
);
int ret = msgBox.exec();
if(ret == QMessageBox::Yes) {
system("sudo /opt/eds/bin/killX");
}
}
}
if(dialogBlocked) {
// Wait until Ctrl+Alt keys are released to let operator open dialog again.
if((states[0] == 0) && (states[1] == 0)) {
dialogBlocked = false;
}
}
} else {
cout << "Lost connection to keyboard. Attempting to re-establish..." << endl;
bool detected = false;
if(keyboard.is_open()) keyboard.close();
while(!detected) {
string redetectedHandler = detect();
if(!redetectedHandler.empty()) {
keyboard.open(redetectedHandler.c_str(), ios::in | ios::binary);
if(keyboard.is_open()) {
cout << "Connection to keyboard re-established." << endl;
detected = true;
}
}
}
}
}
if(keyboard.is_open()) keyboard.close();
}
You are never setting done to true, and by this you have a infinite loop which reopens the dialog regardless if you clicked on the yes option.
Also your I am not sure what system("sudo /opt/eds/bin/killX"); is intended to do. If you intend to kill the current process then it does not do the job. For simplicity sake you can call std::terminate then indeed, you would not have to set done to true, since you are exiting the loop abnormally.
So you have 2 options:
if(ret == QMessageBox::Yes) {
system("sudo /opt/eds/bin/killX");
done = true;
}
or
#include <exception>
....
if(ret == QMessageBox::Yes) {
std::terminate();
}
TL;DR
I am trying to capture keyboard events (more specifically, the Ctrl+c command) in my own C++ program. I am attempting this through generic keyboard presses in SDL2.
END TL;DR
I have found links on SO and the internet that cover the subject of handling keyboard events with SDL2. I have a few of them listed here.
https://stackoverflow.com/questions/28105533/sdl2-joystick-dont-capture-pressed-event
https://lazyfoo.net/tutorials/SDL/04_key_presses/index.php
http://www.cplusplus.com/forum/windows/182214/
http://gigi.nullneuron.net/gigilabs/handling-keyboard-and-mouse-events-in-sdl2/
The major issue I think is causing the problem is that I am also using an Xbox-style joystick at the same time. I have had no issues whatsoever with capturing joystick events. I have been doing that for a long time now. I am having issues trying to get anything with the keyboard to throw an event. I have tried if(event.type == SDL_KEYDOWN) and then checking which key it was, but that appears to return nothing. I feel like there is some macro that I need to define to allow this since I keep finding the same solutions on the internet.
I have included the entire script that I am running at the moment.
#include <boost/thread.hpp>
// Time library
#include <chrono>
// vector data structure
#include <vector>
// Thread-safe base variables
#include <atomic>
// std::cout
#include <iostream>
// Joystick library
#include <SDL2/SDL.h>
// Counters for printing
std::atomic_int printcounter{ 0 };
// This is every 3 * 1000 milliseconds
const int printer = 300;
// If an event is found, allow for printing.
std::atomic_bool eventupdate{ false };
// This function converts the raw joystick axis from the SDL library to proper double precision floating-point values.
double intToDouble(int input)
{
return (double) input / 32767.0 ;
}
// Prevent joystick values from going outside the physical limit
double clamp(double input)
{
return (input < -1.0) ? -1.0 : ( (input > 1.0) ? 1.0 : input);
}
// SDL library joystick deadband
const int JOYSTICK_DEAD_ZONE = 5000;
// These are the raw read in values from the joystick in XInput (XBox) mode.
//Normalized direction
int leftX = 0;
int leftY = 0;
int rightX = 0;
int rightY = 0;
int leftTrigger = -32768;
int rightTrigger = -32768;
// Button array
uint buttons[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// Tbe pov hat is only 4 bits - 1, 2, 4, 8
int povhat = 0;
// These are the rectified joystick values
double leftstickx = 0;
double leftsticky = 0;
double rightstickx = 0;
double rightsticky = 0;
double lefttrigger = 0;
double righttrigger = 0;
// These are the rectified boolean buttons
bool leftstickbut = false;
bool rightstickbut = false;
bool xbutton = false;
bool ybutton = false;
bool abutton = false;
bool bbutton = false;
bool rightbut = false;
bool leftbut = false;
bool startbut = false;
bool backbut = false;
bool centbut = false;
// This is the boolean that controls running the robot.
std::atomic_bool quitrobot{false};
// Joystick values
static double joyvalues[6] = { 0, 0, 0, 0, 0, 0};
static bool joybuttons[11] = { false };
// Sleep function
void wait(int milliseconds)
{
boost::this_thread::sleep_for(boost::chrono::milliseconds{milliseconds});
}
// Now the main code
int main(int argc, char** argv)
{
// Now the robot goes through the looping code until a quit flag is set to true
while ( ! quitrobot)
{
// Now we look for an Xbox-style joystick
std::cout << "Looking for gamepad..." << std::endl;
while(true)
{
// Now the program waits until an Xbox-style joystick is plugged in.
// resetting SDL makes things more stable
SDL_Quit();
// restart SDL with the expectation that a jostick is required.
SDL_Init(SDL_INIT_JOYSTICK);
// SDL_HINT_GRAB_KEYBOARD
// check for a joystick
int res = SDL_NumJoysticks();
if (res > 0) { break; } // Here a joystick has been detected.
if (res < 0)
{
std::cout << "Joystick detection error: " << std::to_string(res) << std::endl;
}
// we don't want the program running super fast when detecting hardware.
wait(20);
}
// Now we check to make sure that the joystick is valid.
// Open the joystick for reading and store its handle in the joy variable
SDL_Joystick *joy = SDL_JoystickOpen(0);
if (joy == NULL) {
/* back to top of while loop */
continue;
}
// Get information about the joystick
const char *name = SDL_JoystickName(joy);
const int num_axes = SDL_JoystickNumAxes(joy);
const int num_buttons = SDL_JoystickNumButtons(joy);
const int num_hats = SDL_JoystickNumHats(joy);
printf("Now reading from joystick '%s' with:\n"
"%d axes\n"
"%d buttons\n"
"%d hats\n\n",
name,
num_axes,
num_buttons,
num_hats);
/* I'm using a logitech F350 wireless in X mode.
If num axis is 4, then gamepad is in D mode, so neutral drive and wait for X mode.
[SAFETY] This means 'D' becomes our robot-disable button.
This can be removed if that's not the goal. */
if (num_axes < 5) {
/* back to top of while loop */
continue;
}
// This is the read joystick and drive robot loop.
while(true)
{
// poll for disconnects or bad things
SDL_Event e;
if (SDL_PollEvent(&e)) {
// SDL generated quit command
if (e.type == SDL_QUIT) { break; }
// Checking for Ctrl+c on the keyboard
// SDL_Keymod modstates = SDL_GetModState();
// if (modstates & KMOD_CTRL)
// {
// One of the Ctrl keys are being held down
// std::cout << "Pressed Ctrl key." << std::endl;
// }
if(e.key.keysym.scancode == SDLK_RCTRL || e.key.keysym.scancode == SDLK_LCTRL || SDL_SCANCODE_RCTRL == e.key.keysym.scancode || e.key.keysym.scancode == SDL_SCANCODE_LCTRL)
{
std::cout << "Pressed QQQQ." << std::endl;
}
if (e.type == SDL_KEYDOWN)
{
switch(e.key.keysym.sym){
case SDLK_UP:
std::cout << "Pressed up." << std::endl;
break;
case SDLK_RCTRL:
std::cout << "Pressed up." << std::endl;
break;
case SDLK_LCTRL:
std::cout << "Pressed up." << std::endl;
break;
}
// Select surfaces based on key press
switch( e.key.keysym.sym )
{
case SDLK_UP:
std::cout << "Pressed Up." << std::endl;
break;
case SDLK_DOWN:
std::cout << "Pressed Up." << std::endl;
break;
case SDLK_LEFT:
std::cout << "Pressed Up." << std::endl;
break;
case SDLK_RIGHT:
std::cout << "Pressed Up." << std::endl;
break;
}
std::cout << "Pressed blah di blah blah please print me." << std::endl;
}
// Checking which joystick event occured
if (e.jdevice.type == SDL_JOYDEVICEREMOVED) { break; }
// Since joystick is not erroring out, we can
else if( e.type == SDL_JOYAXISMOTION )
{
//Motion on controller 0
if( e.jaxis.which == 0 )
{
// event happened
eventupdate = true;
// Left X axis
if( e.jaxis.axis == 0 )
{
// dead zone check
if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
{
leftX = 0;
}
else
{
leftX = e.jaxis.value;
}
}
// Right Y axis
else if( e.jaxis.axis == 1 )
{
// dead zone check
if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
{
leftY = 0;
}
else
{
leftY = e.jaxis.value;
}
}
// Left trigger
else if ( e.jaxis.axis == 2 )
{
// dead zone check
if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
{
leftTrigger = 0;
}
else
{
leftTrigger = e.jaxis.value;
}
}
// Right X axis
else if( e.jaxis.axis == 3 )
{
// dead zone check
if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
{
rightX = 0;
}
else
{
rightX = e.jaxis.value;
}
}
// Right Y axis
else if( e.jaxis.axis == 4 )
{
// dead zone check
if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
{
rightY = 0;
}
else
{
rightY = e.jaxis.value;
}
}
// Right trigger
else if( e.jaxis.axis == 5 )
{
// dead zone check
if( e.jaxis.value > -JOYSTICK_DEAD_ZONE && e.jaxis.value < JOYSTICK_DEAD_ZONE)
{
rightTrigger = 0;
}
else
{
rightTrigger = e.jaxis.value;
}
}
}
}
else if ( e.type == SDL_JOYBUTTONUP || e.type == SDL_JOYBUTTONDOWN )
{
// now we are looking for button events.
if (e.jbutton.which == 0)
{
// event happened
eventupdate = true;
// buttons[e.jbutton.button] = e.jbutton.state;
if (e.jbutton.button == 0)
{
buttons[0] = e.jbutton.state;
}
if (e.jbutton.button == 1)
{
buttons[1] = e.jbutton.state;
}
if (e.jbutton.button == 2)
{
buttons[2] = e.jbutton.state;
}
if (e.jbutton.button == 3)
{
buttons[3] = e.jbutton.state;
}
if (e.jbutton.button == 4)
{
buttons[4] = e.jbutton.state;
}
if (e.jbutton.button == 5)
{
buttons[5] = e.jbutton.state;
}
if (e.jbutton.button == 6)
{
buttons[6] = e.jbutton.state;
}
if (e.jbutton.button == 7)
{
buttons[7] = e.jbutton.state;
}
if (e.jbutton.button == 8)
{
buttons[8] = e.jbutton.state;
}
if (e.jbutton.button == 9)
{
buttons[9] = e.jbutton.state;
}
if (e.jbutton.button == 10)
{
buttons[10] = e.jbutton.state;
}
if (e.jbutton.button == 11)
{
buttons[11] = e.jbutton.state;
}
}
}
else if ( e.type == SDL_JOYHATMOTION)
{
if (e.jhat.which == 0)
{
// event happened
eventupdate = true;
povhat = e.jhat.value;
}
}
}
// Now that we have read in the values directly from the joystick we need top convert the values properly.
leftstickx = clamp(intToDouble(leftX));
leftsticky = clamp(intToDouble(leftY));
rightstickx = clamp(intToDouble(rightX));
rightsticky = clamp(intToDouble(rightY));
lefttrigger = clamp(intToDouble(leftTrigger));
righttrigger = clamp(intToDouble(rightTrigger));
// rectify the buttons to become boolean values instead of integers.
abutton = buttons[0] > 0;
bbutton = buttons[1] > 0;
xbutton = buttons[2] > 0;
ybutton = buttons[3] > 0;
//
rightbut = buttons[4] > 0;
leftbut = buttons[5] > 0;
//
centbut = buttons[8] > 0;
startbut = buttons[7] > 0;
backbut = buttons[6] > 0;
//
leftstickbut = buttons[9] > 0;
rightstickbut = buttons[10] > 0;
// Transfer axis to the array.
joyvalues[0] = leftstickx;
joyvalues[1] = leftsticky;
joyvalues[2] = rightstickx;
joyvalues[3] = rightsticky;
joyvalues[4] = lefttrigger;
joyvalues[5] = righttrigger;
// We are using the "B" button to quit the program
if (bbutton)
{
quitrobot = true;
std::cout << "Shutting down program." << std::endl;
break;
}
if (eventupdate)
{
// This section of code is meant for running code that happens when SDL has detected an event.
// This code section can be used for something else as well.
if (e.key.keysym.sym == SDL_SCANCODE_RCTRL || e.key.keysym.sym == SDL_SCANCODE_LCTRL || e.key.keysym.sym == SDLK_LCTRL || e.key.keysym.sym == SDLK_RCTRL)
{
std::cout << "SDL Event: Ctrl pressed.\n" << std::endl;
}
// Simply print the event
eventupdate = false;
} else {}
if ( ! (printcounter = ((printcounter + 1) % printer)))
{
// const Uint8 *state = SDL_GetKeyboardState(NULL);
// if (state[SDL_SCANCODE_RETURN]) {
// printf("<RETURN> is pressed.\n");
// }
}
// Sleep the program for a short bit
wait(5);
}
// Reset SDL since the robot is no longer being actuated.
SDL_JoystickClose(joy);
// We get here only if the joystick has been disconnected.
std::cout << "Gamepad disconnected.\n" << std::endl;
}
// The program then completes.
return 0;
}
The most important part of that huge block of code is lines 129 to 179. I was doing more fooling around trying to get key capture to work but I could not get a response. Everywhere else is logic for the joystick reading (which has worked for me flawlessly). I have been referring to this link for all of the macros available to the programmer. I have not been able to capture the left control button or the right control button. I have also been trying the arrow keys for kicks as well and those are not working either. I know there are remnants of other code snippets thrown in there as I was testing. Given all of my testing, I am just not sure how to capture any keyboard keys, let alone Ctrl+c. None of my desired print statements print.
I am able to run the code on Ubuntu 1804 LTS with the stock GUI manager and window manager. I have a feeling the problem might also have something to do with the operating system not letting SDL2 capture the keyboard, but I don't know what to do to allow only the keyboard or certain keys to be consumed by SDL2.
I am trying to not use platform-specific code since I already have successfully used platform-specific signal interrupts. My goal is to simply make a certain combination of depressed keys result in a program terminating. I figured that, since SDL2 can access all keys on a keyboard, that I should be able to simply
Unless you want to read keyboard input from stdin you need to open a window and focus it to get key events in SDL. Here's an example (note the call to SDL_Init uses SDL_INIT_VIDEO and there's some code in there for rendering a background and handling resize events).
#include <iostream>
#include <SDL2/SDL.h>
int main(int argc, char** argv)
{
if (SDL_Init(SDL_INIT_VIDEO) < 0) { // also initialises the events subsystem
std::cout << "Failed to init SDL.\n";
return -1;
}
SDL_Window *window = SDL_CreateWindow(
"Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
680, 480, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if(!window) {
std::cout << "Failed to create window.\n";
return -1;
}
// Create renderer and select the color for drawing.
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
SDL_SetRenderDrawColor(renderer, 200, 200, 200, 255);
while(true)
{
// Clear the entire screen and present.
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
SDL_Event event;
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT) {
SDL_Quit();
return 0;
}
if(event.type == SDL_WINDOWEVENT) {
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
int width = event.window.data1;
int height = event.window.data2;
std::cout << "resize event: " << width << "," << height << std::endl;
}
}
if (event.type == SDL_KEYDOWN) {
int key = event.key.keysym.sym;
if (key == SDLK_ESCAPE) {
SDL_Quit();
return 0;
}
std::cout << "key event: " << key << std::endl;
}
}
}
return 0;
}
Key events are sent to the currently focused window and SDL uses the underlying OS to handle this. E.g. In Linux this means SDL calls X11 functions.
edit:
As detailed in this question it appears you can also get a snapshot of the state of the keys. In either case I think a window needs to be opened to receive events even though I've edited this multiple times to come to that conclusion (apologies if that caused any confusion). Your OS may provide functions for polling the state of the keyboard without using events or windows, such as GetAsyncKeyState in Windows.
I need to make small video player with OpenCV, which have to support the following functionality. Key 'p' on keyboard - pause/unpause, 'q' - exit, left and right arrow keys - play video frame by frame straight and reverse when it is paused. So the problem is when I try to show video with high quality and I hold arrow key for several seconds it does not run, but freeze and then jump to current frame after I release key. I tried to fix this with adding this_thread::sleep after cv::imshow() to give time to draw, but it did not help at all. So here is the code. Also, I have some reasons to use boost instead of C++11, so it is ok.
main.cpp
#include "VideoPlayer.hpp"
#include <iostream>
int main(int argc, char *argv[])
{
if (argc < 2) {
std::cerr << "Video file full name required as argument." << std::endl;
}
VideoPlayer vp(argv[1]);
if (!vp.play())
return 1;
return 0;
}
VideoPlayer.hpp
#pragma once
#include <opencv/cxcore.hpp>
#include <opencv/highgui.h>
#include <string>
class VideoPlayer
{
public:
VideoPlayer(const std::string &video, const std::string &windowName = "Output window",
unsigned int delay = 30);
bool play();
private:
cv::VideoCapture videoCapture_;
std::string windowName_;
unsigned int delay_;
private:
bool processKey(int key);
void setFrame(int frameNum);
};
VideoPlayer.cpp
#include "VideoPlayer.hpp"
#include <iostream>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/thread.hpp>
VideoPlayer::VideoPlayer(const std::string &video, const std::string &windowName,
unsigned int delay)
: videoCapture_(video)
, windowName_(windowName)
, delay_(delay)
{}
bool VideoPlayer::play()
{
if (!videoCapture_.isOpened()) {
std::cerr << "Unable to open video." << std::endl;
return false;
}
cv::namedWindow(windowName_);
for (;;) {
cv::Mat frame;
videoCapture_ >> frame;
cv::imshow(windowName_, frame);
int key = cv::waitKey(delay_);
if (processKey(key))
break;
}
return true;
}
bool VideoPlayer::processKey(int key)
{
if (key == 'p') {
for (;;) {
int pausedKey = cv::waitKey(0);
if (pausedKey == 'p') {
break;
} else if (pausedKey == 'q') {
return true;
} else if (pausedKey == 65363) {
int frameNum = videoCapture_.get(CV_CAP_PROP_POS_FRAMES);
setFrame(frameNum);
} else if (pausedKey == 65361) {
int frameNum = videoCapture_.get(CV_CAP_PROP_POS_FRAMES);
setFrame(frameNum - 2);
}
}
} else if (key == 'q') {
return true;
}
return false;
}
void VideoPlayer::setFrame(int frameNum)
{
if (frameNum > 0 && frameNum < videoCapture_.get(CV_CAP_PROP_FRAME_COUNT)) {
std::cerr << frameNum << std::endl;
videoCapture_.set(CV_CAP_PROP_POS_FRAMES, frameNum);
cv::Mat frame;
videoCapture_ >> frame;
cv::imshow(windowName_, frame);
boost::this_thread::sleep(boost::posix_time::milliseconds(10));
}
}
I also created a multithreading implementation with buffer based on std::queue with lock, but it didn't solve the problem. And I tried to use boost::lockfree::queue, but I could not manage to finish it because of some strange behavior. I will share this code later, if it is necessary.
So, if somebody knows some good practice, how to avoid this problem, help me please.
EDIT:
Replacing boost::this_thread::sleep(boost::posix_time::milliseconds(10)); with cv::waitKey(0) is bad, because is makes me to do two short presses on arrow key to change one frame, and it does not help, because holding key skips it very fast. So the following code helped, but it is too strange and it is necessary to select times each for each video.
void VideoPlayer::setFrame(int frameNum)
{
if (frameNum > 0 && frameNum < videoCapture_.get(CV_CAP_PROP_FRAME_COUNT)) {
std::cerr << frameNum << std::endl;
videoCapture_.set(CV_CAP_PROP_POS_FRAMES, frameNum);
cv::Mat frame;
videoCapture_ >> frame;
cv::imshow(windowName_, frame);
int times = 7;
for (int i = 0; i < times; i++)
cv::waitKey(10);
}
}
Also, I'm unable to use Qt or something else, only C++03 with boost and OpenCV.
I think I need some trick to make cv::waitKey(time) wait fortime whether any key pressed or not.
it need a way to call function whose name is stored in a string similar to eval. Can you help?
C++ doesn't have reflection so you must hack it, i. e.:
#include <iostream>
#include <map>
#include <string>
#include <functional>
void foo() { std::cout << "foo()"; }
void boo() { std::cout << "boo()"; }
void too() { std::cout << "too()"; }
void goo() { std::cout << "goo()"; }
int main() {
std::map<std::string, std::function<void()>> functions;
functions["foo"] = foo;
functions["boo"] = boo;
functions["too"] = too;
functions["goo"] = goo;
std::string func;
std::cin >> func;
if (functions.find(func) != functions.end()) {
functions[func]();
}
return 0;
}
There are at least 2 alternatives:
The command pattern.
On windows, you can use GetProcAddress to get a callback by name, and dlopen + dlsym on *nix.
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
double eval( string expression );
int main( int argc, char *argv[] )
{
string expression = "";
for ( int i = 1; i < argc; i++ )
{
expression = expression + argv[i];
}
cout << "Expression [ " << expression << " ] = " << endl;
eval( expression );
}
double eval( string expression )
{
string program = "";
program = program + "#include <cmath>\n";
program = program + "#include <iostream>\n";
program = program + "using namespace std;\n";
program = program + "int main()\n";
program = program + "{\n";
program = program + " cout << ";
program = program + expression;
program = program + " << endl;\n";
program = program + "}";
ofstream out( "abc.cpp" );
out << program;
out.close();
system( "g++ -o abc.exe abc.cpp" );
system( "abc" );
}
You could try to adopt an existing scripting engine, expose the functions you like to this and then use this to evaluate your statements. One such enging could be the V8 engine: https://developers.google.com/v8/intro but there are many alternatives and different languages to choose from.
Here are some examples:
Boost Python
V8
LUA
AngelScript
Except using the function map in the program and hack it on the Makefile, you can access it through ELF.
I think this method is better as it did not need to write duplicate code and compile it every time on different machine.
Here is my demo C/C++ equivalent of eval(“function(arg1, arg2)”)
#include<stdio.h>
#include<stdlib.h>
#include<elf.h>
#include<libelf.h>
#include<unistd.h>
#include<fcntl.h>
#include<gelf.h>
#include<string.h>
void my_fun()
{
int a = 19;
printf("my_fun is excute, a is %d \n", a);
}
void my_fun2()
{
printf("my_fun2 is excute\n");
return;
}
void my_fun3()
{
return;
}
void excute_fun(char *program_name, char *function_name)
{
int i, count;
Elf32_Ehdr *ehdr;
GElf_Shdr shdr;
Elf *elf;
Elf_Scn *scn = NULL;
Elf_Data *data;
int flag = 0;
int fd = open(program_name, O_RDONLY);
if(fd < 0) {
perror("open\n");
exit(1);
}
if(elf_version(EV_CURRENT) == EV_NONE) {
perror("elf_version == EV_NONE");
exit(1);
}
elf = elf_begin(fd, ELF_C_READ, (Elf *) NULL);
if(!elf) {
perror("elf error\n");
exit(1);
}
/* Elf32_Off e_shoff; */
/* if ((ehdr = elf32_getehdr(elf)) != 0) { */
/* e_shoff = ehdr->e_shoff; */
/* } */
/* scn = elf_getscn(elf, 0); */
/* printf("e_shoff is %u\n", e_shoff); */
/* scn += e_shoff; */
while ((scn = elf_nextscn(elf, scn)) != NULL) {
gelf_getshdr(scn, &shdr);
if (shdr.sh_type == SHT_SYMTAB) {
/* found a symbol table. */
break;
}
}
data = elf_getdata(scn, NULL);
if(!shdr.sh_entsize)
count = 0;
else
count = shdr.sh_size / shdr.sh_entsize;
for (i = 0; i < count; ++i) {
GElf_Sym sym;
gelf_getsym(data, i, &sym);
char *sym_name = elf_strptr(elf, shdr.sh_link, sym.st_name);
if(sym_name != NULL && sym_name[0] != '_' && sym_name[0] != '\0' && sym_name[0] != ' ' && sym.st_value != 0)
{
/* printf("sym_name is %s\n", sym_name); */
/* printf("%s = %X\n", elf_strptr(elf, shdr.sh_link, sym.st_name), sym.st_value); */
if(!strcmp(sym_name, function_name)) {
void (*fun)(void) = (void*)sym.st_value;
(*fun)();
flag = 1;
}
}
}
if(!flag)
printf("can not find this function\n");
elf_end(elf);
close(fd);
}
int main(int argc, char *argv[])
{
char *input = (char*)malloc(100);
for(;;) {
printf("input function_name to excute: ");
scanf("%s", input);
excute_fun(argv[0], input);
memset(input, 0, sizeof(input));
printf("\n");
}
free(input);
return 0;
}
This implementation is based on Example of Printing the ELF Symbol Table
I am very new to C++. In my code i am using readline to get the user input which is a command and that input will be processed accordingly. When user enters a command named "setflag", i should set a flag to TRUE in all the active sessions of my processes.
The code works just fine. I am able to update the flag in all the processes simultaneously using signals.
Problem:
Please compile the code as: g++ -lreadline -lncurses alex123.cpp -o alex123
Spawn three processes (am refering to them as P1, P2, P3) by executing the binary "alex123" on separate terminals. In P1 issue "setflag" command. You can see that "MYFLAG" is set to TRUE in P1. Also you can see that P2 and P3 are able to receive the signal. But in P2 and P3 you wont see the command prompt. Only if you press the "enter" key on P2 and P3, you will get the command prompt back.
I want this in such a way that command prompt should automatically come. Pls help me.
alex123.cpp
## Heading ##
#include "stdio.h"
#include "iostream"
#include "signal.h"
#include "sys/time.h"
#include "string"
#include "stdlib.h"
#include "sstream"
#include "sys/types.h"
#include "unistd.h"
#include "/usr/include/readline/readline.h"
#include "/usr/include/readline/history.h"
using namespace std;
bool myFlag = false;
void updateFlag(int i)
{
cout << "\nGot the signal.";
myFlag= true;
//To give line feed from within the code itself. I need the fix here.
putc(10,stdout); // This doesn't work.
}
int getOtherPids(string &pidList)
{
char pid[10];
string pidStr;
FILE *stream;
//Command the search the process alex123
std::string cmd = "pgrep -x alex123";
stream = popen(cmd.c_str(),"r");
if (stream == NULL)
{
cout << "\nWhat the hell\n";
return 0;
}
while (fgets(pid, 10, stream) != NULL)
{
for(int i=0;i<10;i++)
{
int p = (int)pid[i] - 48;
if ((p >= 0) && (p <= 9))
pidStr.append(1,pid[i]);
}
pidStr.append(1,' ');
pidList.append(pidStr);
pidStr.clear();
}
int status = pclose(stream);
if (status == -1)
{
cout << "\nWhat the hell!! AGAIN !!\n"; return 0;
}
return 1;
}
void notifyOthers()
{
string pidList;
string notifyCmd;
//Convert pid of type int to a string
int my_pid = getpid();
ostringstream os;
os << my_pid;
string myPid = os.str();
//Get other existing alex123 process list to which notification has to be sent.
if (!getOtherPids(pidList))
{
cout << "!!WARNING!! Couldn't get active pids\n";
return;
}
size_t pos = pidList.find(myPid.c_str());
if (pos == string::npos)
{
cout << "!!WARNING!! Other Active process list is empty.\n";
return;
}
//Remove current session pid from the pid list string.
pidList.replace(pos,myPid.length()+1,"");
//If there are no other existing alex123 processes, return from the function.
if(strcmp(pidList.c_str(),"") == 0) return;
pidList.replace(pidList.length()-1,1,"\0");
//Send SIGUSR1 signal to others.
notifyCmd = "kill -SIGUSR1 " + pidList;
system(notifyCmd.c_str());
}
int main()
{
char *foo;
for(;;)
{
//If SIGUSR1 is caught it means that some other process has set the myFlag to TRUE by issuing a "setflag" command.
//So it should be set here also.
signal(SIGUSR1, updateFlag);
cout << "\nMYFLAG:"<< myFlag <<endl;
foo = readline("<alex> ");
if(strcmp(foo,"exit") ==0) { return 0; }
if(strcmp(foo,"setflag") == 0)
{
//Set myFlag to TRUE in the current process from where "setflag" command was issued.
myFlag= true;
//Inform all other processes that myFlag has been updated.
notifyOthers();
}
}
return 0;
}