I have a folder full of images, I need to save these images into the video while doing so I want to show the user the video being played from these images (frames). I can run two separate processes one for the saving and one for the showing but this is not what I am looking for, I want to do both in one step. If you know a solution please let me know.
My code uses C++ with OpenCV but feel free to share with me any code written with any language, or event a concept.
I use gStreamer, ffmpeg as well for the video generation, so I am not looking how to save a video or how to show the video I am looking for a process that can do both in one operation.
here's my quick brew using SFML. You have to link SFML and include the include folder. Simply set directory to your target folder, I use std::filesystem to add all files from that folder to a std::vector<std::string>.
#include <SFML/Graphics.hpp>
#include <vector>
#include <string>
#include <iostream>
#include <filesystem>
void add_frame_to_video(std::string path) {
//your encoding stuff ...
}
int main() {
auto dimension = sf::VideoMode(1280u, 720u);
sf::RenderWindow window;
window.create(dimension, "Video Rendering");
sf::Texture frame_texture;
std::vector<std::string> frame_paths;
std::string directory = "resources/";
for (auto i : std::filesystem::directory_iterator(directory)) {
frame_paths.push_back({ i.path().string() });
}
std::size_t frame_ctr = 0u;
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
}
if (frame_ctr >= frame_paths.size()) {
window.close();
return EXIT_FAILURE;
}
const auto& current_frame = frame_paths[frame_ctr];
add_frame_to_video(current_frame);
if (!frame_texture.loadFromFile(current_frame)) {
std::cout << "Error: couldn't load file \"" << current_frame << "\".\n";
window.close();
return EXIT_FAILURE;
}
sf::Sprite frame_sprite;
frame_sprite.setTexture(frame_texture);
++frame_ctr;
window.clear();
window.draw(frame_sprite);
window.display();
}
}
this will create a window that shows each picture per frame using sf::Texture to load and sf::Sprite to display the image. If no more frames are available, the window gets closed and the program terminates. If you want to add time between frames, you can set e.g. window.setFramerateLimit(10); to have 10 frames / second.
Related
So I was trying out the SFML tutorial for playing a sound from a local file and came across a weird bug. Whenever I call the playSound function no sound is played and I get an error that I can't find the solution to:
[ALSOFT] (EE) Failed to set real-time priority for thread: Operation not permitted (1)
The strange thing is that if I do the same thing in the main function the sound is played. What is going on here? Here are the only files used in the project, the .wav is just a simple sound file:
main.cpp
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
void playSound() {
sf::SoundBuffer buffer;
if (!buffer.loadFromFile("1-retro-arcade-casino-notification.wav"))
return;
sf::Sound sound;
sound.setBuffer(buffer);
sound.play();
}
int main() {
sf::RenderWindow window(sf::VideoMode(800, 600), "Notification test");
sf::SoundBuffer buffer;
if (!buffer.loadFromFile("1-retro-arcade-casino-notification.wav"))
return -1;
sf::Sound sound;
sound.setBuffer(buffer);
sound.play();
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
if (event.type == sf::Event::TextEntered) {
char key = static_cast<char>(event.text.unicode);
if (key == 'p') {
playSound();
}
if (key == 'q') {
window.close();
}
}
}
}
return 0;
}
Makefile
all:
g++ -lsfml-audio -lsfml-graphics -lsfml-window -lsfml-system -o main main.cpp
The sound data is not stored directly in sf::Sound but instead in the sf::SoundBuffer. This class encapsulates the audio data, which is basically an array of 16-bit signed integers (called "audio samples").
Sounds (and music) are played in a separate thread. This means that you are free to do anything you want after calling
void play(), except destroying the sound or its data - which you are currently doing.
This is because you have declared your sf::SoundBuffer buffer; inside void playSound(), and subsequently, the sound data gets destroyed as it goes out of scope after returning from void playSound().
To fix your problem, don't declare sf::SoundBuffer in the function. You could just declare it outside the main function, or even pass it as a parameter to playSound().
See the relevant documentation for info.
I am confused about how to get inputs from Mouse or Keyboard. As an example, I want to draw little dots on my Mouse position when I pressed the button of my Mouse. Which implementation should I follow?
I have used window.pollEvent function to catch the mouse pressed event in the code below.
#include <SFML/Graphics.hpp>
#include <iostream>
int main()
{
sf::RenderWindow window(sf::VideoMode(640,480), "Paint");
std::vector<sf::CircleShape> dots;
while (window.isOpen()) {
sf::Event event;
if (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
if (event.type == sf::Event::MouseButtonPressed) {
sf::CircleShape shape(10);
shape.setFillColor(sf::Color::Black);
shape.setPosition(event.mouseButton.x, event.mouseButton.y);
dots.push_back(shape);
}
}
window.clear(sf::Color::White);
for (auto& i : dots) {
window.draw(i);
}
window.display();
}
return 0;
}
or should I do it in the way like this?
#include <SFML/Graphics.hpp>
#include <iostream>
int main()
{
sf::RenderWindow window(sf::VideoMode(640,480), "Paint");
std::vector<sf::CircleShape> dots;
while (window.isOpen()) {
sf::Event event;
if (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
sf::CircleShape shape(10);
shape.setFillColor(sf::Color::Black);
shape.setPosition(sf::Mouse::getPosition().x, sf::Mouse::getPosition().y);
dots.push_back(shape);
}
window.clear(sf::Color::White);
for (auto& i : dots) {
window.draw(i);
}
window.display();
}
return 0;
}
If the latter is the appropriate one, then where the if block that checks if the mouse button is pressed should be located, before window.clear() or between window.clear() and window.draw() ?
I could not understand the different between them thoroughly. SFML documentation, for example, shows the latter implemenatation for gunfire action but I could not figure out why. Thanks...
You are essentially asking about the two ways for handling user input:
Events: handling objects that represent an occurrence.
Real-time input: querying the input device about its real-time state.
Your first approach – calling sf::Window::pollEvent() – relies on events. It is an asynchronous mechanism; the button may not be pressed when your code handles the event. Event handling is usually the way to go if all you are interested in is whether the state of the input device has undergone a changeX, e.g., a button has been pressed or released.
Your second approach – calling sf::Mouse::isButtonPressed() – is based on real-time input. It consists of querying the mouse whether a given button is pressed at the moment of calling the function. This approach for handling user input is usually the way to go if you just want to find out the current state of the input device.
XNote, however, that events can be repeated (e.g., if you keep a key pressed for a long time), and therefore they may not necessarily imply a change in the state of the input device. You can disable this with sf::Window::SetKeyRepeatEnabled(), though.
I made this code that shows a timer and pauses when you press spacebar:
#include <SFML/Graphics.hpp>
#include <iostream>
using namespace sf;
using namespace std;
void events();
bool pause, exitPause;
char key;
double timeFrame, timeTot = 0;
Clock timer;
Text text;
Font font;
RenderWindow window(VideoMode(800, 600), "Window", Style::Close);
int main()
{
font.loadFromFile("C:/Windows/Fonts/arial.ttf");
text.setFont(font);
text.setCharacterSize(15);
window.setFramerateLimit(120);
while (window.isOpen())
{
for (Event event; window.pollEvent(event);) {
if (event.type == Event::Closed)
window.close();
if (event.type == Event::TextEntered) {
key = std::tolower(static_cast<char>(event.text.unicode));
if (key == ' ') {
pause = !pause;
if (!pause) {
timer.restart();
}
}
}
}
if (!pause) {
timeFrame = timer.restart().asSeconds();
timeTot += timeFrame;
text.setString(to_string(timeTot));
window.clear();
window.draw(text);
}
window.display();
}
}
If you test, you will see something curious. When pausing by pressing the spacebar, window.display alternates between the last and the current displayed number.
But if I put window.clear and window.draw together with window.display, the problem does not happen.
if (!pause) {
timeFrame = timer.restart().asSeconds();
timeTot += timeFrame;
text.setString(to_string(timeTot));
}
window.clear();
window.draw(text);
window.display();
I thought windows.display, alone, would only show the last buffer.
What is the problem?
The moment you pause you stop updating the draw buffers. SFML is always double-buffered, and in each iteration you always need to parse input, update whatever needs updating, redraw the "hidden" frame, and then flip the buffers. This is basically a "Game Loop" pattern.
In your code you always parse input, update the timer and pause state based on that, and you always flip the buffers (with window.display()). You only redraw the "hidden" frame buffer if the state is not paused, however.
So, you are seeing the expected output, and you found the correct solution.
As an aside, there are indeed several style issues in your code, including uninitialized variables, which is always dangerous in C++.
Hi there fellow Overflowers!
I have recently begun learning SDL. I chose simple directmedia layer as my external API to my C++ knowledge because I found it to offer the most visually enhanced mechanics for game dev. Consider this code below:
#include <iostream>
#include "SDL/SDL.h"
using std::cerr;
using std::endl;
int main(int argc, char* args[])
{
// Initialize the SDL
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
cerr << "SDL_Init() Failed: " << SDL_GetError() << endl;
exit(1);
}
// Set the video mode
SDL_Surface* display;
display = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
if (display == NULL)
{
cerr << "SDL_SetVideoMode() Failed: " << SDL_GetError() << endl;
exit(1);
}
// Set the title bar
SDL_WM_SetCaption("SDL Tutorial", "SDL Tutorial");
// Load the image
SDL_Surface* image;
image = SDL_LoadBMP("LAND.bmp");
if (image == NULL)
{
cerr << "SDL_LoadBMP() Failed: " << SDL_GetError() << endl;
exit(1);
}
// Main loop
SDL_Event event;
while(1)
{
// Check for messages
if (SDL_PollEvent(&event))
{
// Check for the quit message
if (event.type == SDL_QUIT)
{
// Quit the program
break;
}
}
// Game loop will go here...
// Apply the image to the display
if (SDL_BlitSurface(image, NULL, display, NULL) != 0)
{
cerr << "SDL_BlitSurface() Failed: " << SDL_GetError() << endl;
exit(1);
}
//Update the display
SDL_Flip(display);
}
// Tell the SDL to clean up and shut down
SDL_Quit();
return 0;
}
All I have done is just made a screen surface, Double buffered it, made another surface of an image, blit'ed the two together, and for some reason when I build the application, It closes instantly! The application build succeeds but then closes without a window opening! This is really frustrating.
I am using XCode5 and SDL 2.0.3 :)
Help is needed!
EDIT: Turns out in the error log, it says SDL_LoadBMP(): Failed to load LAND.bmp. The bmp is saved in the root directory, the same folder as the main.cpp folder? Why doesn't this work?
You should be able to test your code by using the absolute (full) path to the image. That will verify that the code is actually working.
To be able to use resources with an absolute path you should create a Build Phase to Copy Files. The Destination should be set to 'Product Directory'. You can leave Subpath blank or provide a directory to place the resource in (this will be useful when you get a lot of resources) eg textures. If you supply a Subpath then alter your code so it would be textures/LAND.bmp
You would also use the build phase for packaging the SDL2.framework and any others e.g. SDL2_image etc with your final application. This would allow users who don't have SDL on their machines to run the app. To do this create another build phase with the Detination set to 'Frameworks' and leave the Subpath empty. Then just add any frameworks you want to package with the app. One other setting you will want to make in Build Settings is to change 'Runpath Search Paths' (found under 'Linking') to be #executeable_path/../Frameworks so that the application knows where to find packaged frameworks
I have a tutorial on configuring SDL2 in Xcode along with a template to make it quick
http://zamma.co.uk/how-to-setup-sdl2-in-xcode-osx/
So I've been looking around in my code for hours now and finally I think I found the issue with the code. An SFML method doesn't work or I am using it wrong.
I am trying to load a PNG file from my disk into the program so I can render it on screen.
But the picture is never even loaded into the program to begin with it seems. As when I check the debugger I see no actual info about the file I am trying to load in.
I'll show the entire Engine.cpp file first, and then explain step by step what happens.
My Engine.cpp looks like this:
#include "Engine.h"
#include <SFML\Graphics.hpp>
Engine::Engine()
{
}
Engine::~Engine()
{
}
bool Engine::Init()
{
LoadImages();
window = new sf::RenderWindow(sf::VideoMode(800,600,32), "RPG");
if(!window)
return false;
return true;
}
void Engine::LoadImages()
{
sf::Image sprite;
sprite.loadFromFile("C:/Users/Vipar/Pictures/sprite1.png");
imageManager.AddImage(sprite);
testTile = new Tile(imageManager.GetImage(0));
}
void Engine::RenderFrame()
{
window->clear();
testTile->Draw(0,0,window);
window->display();
}
void Engine::ProcessInput()
{
sf::Event evt;
//Loop through all window events
while(window->pollEvent(evt))
{
if(evt.type == sf::Event::Closed)
window->close();
}
}
void Engine::Update()
{
}
void Engine::MainLoop()
{
sf::Image sprite;
sprite.loadFromFile("C:/Users/Vipar/Pictures/sprite1.png");
imageManager.AddImage(sprite);
testTile = new Tile(imageManager.GetImage(0));
//Loop until our window is closed
while(window->isOpen())
{
ProcessInput();
Update();
RenderFrame();
testTile->Draw(0,0,window);
}
}
void Engine::Go()
{
if(!Init())
throw "Could not initialize Engine";
MainLoop();
}
I call this method:
void Engine::LoadImages()
{
sf::Image sprite;
sprite.loadFromFile("C:/Users/Vipar/Pictures/sprite1.png");
imageManager.AddImage(sprite);
testTile = new Tile(imageManager.GetImage(0));
}
It adds the Image to my ImageManager for later use if need be. This is done for a bit of efficiency. Then I apply the Image to my custom class called Tile. It looks like this:
#include "Tile.h"
#include <SFML\Graphics.hpp>
#include <iostream>
Tile::Tile(sf::Image& image)
{
sf::Texture tex;
tex.loadFromImage(image);
baseSprite.setTexture(tex,true);
}
Tile::~Tile()
{
}
void Tile::Draw(int x, int y, sf::RenderWindow* rw)
{
baseSprite.setPosition(x,y);
rw->draw(baseSprite);
}
void Tile::Destroy()
{
delete &baseSprite;
}
Then I draw the Tile on screen to test it out.
Why does this png file not even load into my program to begin with? If you need additional code, please let me know. I am a bit green on this SFML framework and C++ but I am good at Java and C#.
I only work with SFML.net (maybe you should use it?), and I don't like how you're doing things, but...
Throw some debug messages to make sure that all your appropriate
functions/code is getting run.
Clear with a different color and make sure it works.
Your naming is confusing and misleading.
Make sure you've checked the tutorial to see how they draw.