Skip to content

Commit

Permalink
Add screenshot feature
Browse files Browse the repository at this point in the history
  • Loading branch information
clementgallet committed Oct 25, 2023
1 parent 606fcde commit eddd618
Show file tree
Hide file tree
Showing 13 changed files with 179 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## [Unreleased]
### Added

* Screenshot

### Changed
### Fixed

Expand Down
1 change: 1 addition & 0 deletions src/library/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ libtas_so_SOURCES = \
checkpoint/ThreadSync.cpp \
encoding/AVEncoder.cpp \
encoding/NutMuxer.cpp \
encoding/Screenshot.cpp \
fileio/dirwrappers.cpp \
fileio/FileHandleList.cpp \
fileio/generaliowrappers.cpp \
Expand Down
82 changes: 82 additions & 0 deletions src/library/encoding/Screenshot.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
Copyright 2015-2023 Clément Gallet <[email protected]>
This file is part of libTAS.
libTAS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
libTAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with libTAS. If not, see <http://www.gnu.org/licenses/>.
*/

#include "Screenshot.h"

#include "../logging.h"
#include "../ScreenCapture.h"
#include "NutMuxer.h"
#include "../global.h" // Global::shared_config
#include "../GlobalState.h"

#include <cstdint>
#include <sstream>

namespace libtas {

int Screenshot::save(const std::string& screenshotfile, bool draw) {

if (!ScreenCapture::isInited()) {
debuglogstdio(LCF_DUMP | LCF_ERROR, "Screen was not inited");
return ESCREENSHOT_NOSCREEN;
}

std::ostringstream commandline;
commandline << "ffmpeg -loglevel warning -hide_banner -y -guess_layout_max 0 -f nut -i - -frames:v 1 -update 1 \"";
commandline << screenshotfile;
commandline << "\"";

FILE *ffmpeg_pipe = nullptr;
NATIVECALL(ffmpeg_pipe = popen(commandline.str().c_str(), "w"));

if (! ffmpeg_pipe) {
debuglogstdio(LCF_DUMP | LCF_ERROR, "Could not create a pipe to ffmpeg");
return ESCREENSHOT_NOPIPE;
}

int width, height;
ScreenCapture::getDimensions(width, height);

const char* pixfmt = ScreenCapture::getPixelFormat();

/* Initialize the muxer. Audio parameters don't matter here for screenshot */
NutMuxer* nutMuxer = new NutMuxer(width, height, Global::shared_config.framerate_num, Global::shared_config.framerate_den, pixfmt, 44100, 1, 1, ffmpeg_pipe);

/* Access to the screen pixels, or last screen pixels if not a draw frame */
uint8_t* pixels = nullptr;
int size = ScreenCapture::getPixelsFromSurface(&pixels, draw);

debuglogstdio(LCF_DUMP, "Perform the screenshot");
nutMuxer->writeVideoFrame(pixels, size);

if (ffmpeg_pipe) {
int ret;
NATIVECALL(ret = pclose(ffmpeg_pipe));
if (ret < 0) {
debuglogstdio(LCF_DUMP | LCF_ERROR, "Could not close the pipe to ffmpeg");
return ESCREENSHOT_NOPIPE;
}
}

return ESCREENSHOT_OK;
}

}

// #endif
43 changes: 43 additions & 0 deletions src/library/encoding/Screenshot.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
Copyright 2015-2023 Clément Gallet <[email protected]>
This file is part of libTAS.
libTAS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
libTAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with libTAS. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef LIBTAS_SCREENSHOT_H_INCL
#define LIBTAS_SCREENSHOT_H_INCL

#include <string>

namespace libtas {
namespace Screenshot {

/* List of error codes */
enum Error {
ESCREENSHOT_OK = 0,
ESCREENSHOT_NOSCREEN = -1, // Screen Capture was not inited
ESCREENSHOT_NOPIPE = -2, // Could not create a pipe to ffmpeg
};

/* Save the screenshot to file, `draw` indicates if the current frame is
* a draw frame. */
int save(const std::string& screenshotfile, bool draw);

};

}

#endif
8 changes: 8 additions & 0 deletions src/library/frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "GlobalState.h"
#include "DeterministicTimer.h"
#include "encoding/AVEncoder.h"
#include "encoding/Screenshot.h"
#include "sdl/sdlwindows.h"
#include "sdl/sdlevents.h"
#include <iomanip>
Expand Down Expand Up @@ -703,6 +704,13 @@ static void receive_messages(std::function<void()> draw, RenderHUD& hud)
receiveCString(AVEncoder::ffmpeg_options);
break;

case MSGN_SCREENSHOT:{
debuglogstdio(LCF_SOCKET, "Receiving screenshot filename");
std::string screenshotfile = receiveString();
Screenshot::save(screenshotfile, !!draw);
break;
}

case MSGN_ALL_INPUTS:
receiveData(&ai, sizeof(AllInputs));
/* Update framerate if necessary (do we actually need to?) */
Expand Down
4 changes: 4 additions & 0 deletions src/program/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ void Config::save(const std::string& gamepath) {
settings.setValue("gameargs", gameargs.c_str());
settings.setValue("moviefile", moviefile.c_str());
settings.setValue("dumpfile", dumpfile.c_str());
settings.setValue("screenshotfile", screenshotfile.c_str());
settings.setValue("ffmpegoptions", ffmpegoptions.c_str());
settings.setValue("libdir", libdir.c_str());
settings.setValue("rundir", rundir.c_str());
Expand Down Expand Up @@ -244,6 +245,9 @@ void Config::load(const std::string& gamepath) {
std::string default_dumpfile = gamepath + ".mkv";
dumpfile = settings.value("dumpfile", default_dumpfile.c_str()).toString().toStdString();

std::string default_screenshotfile = dirFromPath(gamepath) + "/screenshot.png";
screenshotfile = settings.value("screenshotfile", default_screenshotfile.c_str()).toString().toStdString();

ffmpegoptions = settings.value("ffmpegoptions", ffmpegoptions.c_str()).toString().toStdString();

libdir = settings.value("libdir", "").toString().toStdString();
Expand Down
3 changes: 3 additions & 0 deletions src/program/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ class Config {
/* Were we started up with the -d option? */
bool dumping;

/* Absolute path of the screenshot file */
std::string screenshotfile;

/* Path of the libraries used by the game */
std::string libdir;

Expand Down
5 changes: 5 additions & 0 deletions src/program/GameEvents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,11 @@ bool GameEvents::processEvent(GameEvents::EventType type, struct HotKey &hk)
emit sharedConfigChanged();
return false;

case HOTKEY_SCREENSHOT:
sendMessage(MSGN_SCREENSHOT);
sendString(context->config.screenshotfile);
return false;

} /* switch(hk.type) */
break;

Expand Down
1 change: 1 addition & 0 deletions src/program/KeyMapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ KeyMapping::KeyMapping(void* c)
hotkey_list.push_back({{SingleInput::IT_KEYBOARD, XK_F8 | XK_Control_Flag}, HOTKEY_LOADBRANCH8, "Load Branch 8"});
hotkey_list.push_back({{SingleInput::IT_KEYBOARD, XK_F9 | XK_Control_Flag}, HOTKEY_LOADBRANCH9, "Load Branch 9"});
hotkey_list.push_back({{SingleInput::IT_KEYBOARD, XK_F10 | XK_Control_Flag}, HOTKEY_LOADBRANCH_BACKTRACK, "Load Backtrack Branch"});
hotkey_list.push_back({{SingleInput::IT_NONE, 0}, HOTKEY_SCREENSHOT, "Screenshot"});
hotkey_list.push_back({{SingleInput::IT_NONE, 0}, HOTKEY_TOGGLE_ENCODE, "Toggle encode"});

/* Add flags mapping */
Expand Down
1 change: 1 addition & 0 deletions src/program/KeyMapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ typedef int HotKeyType; enum
HOTKEY_LOADBRANCH9,
HOTKEY_LOADBRANCH_BACKTRACK,
HOTKEY_TOGGLE_FASTFORWARD, // Toggle fastforward
HOTKEY_SCREENSHOT,
HOTKEY_LEN
};

Expand Down
20 changes: 20 additions & 0 deletions src/program/ui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,7 @@ void MainWindow::createMenus()
QMenu *toolsMenu = menuBar()->addMenu(tr("Tools"));
configEncodeAction = toolsMenu->addAction(tr("Configure encode..."), encodeWindow, &EncodeWindow::exec);
toggleEncodeAction = toolsMenu->addAction(tr("Start encode"), this, &MainWindow::slotToggleEncode);
screenshotAction = toolsMenu->addAction(tr("Screenshot..."), this, &MainWindow::slotScreenshot);

toolsMenu->addSeparator();

Expand Down Expand Up @@ -1316,6 +1317,25 @@ void MainWindow::slotToggleEncode()
}
}

void MainWindow::slotScreenshot()
{
/* Prompt for screenshot filename and path */
if (context->interactive) {
QString defaultPath = QString(dirFromPath(context->gamepath).c_str()).append(QString("/screenshot.png"));

QString screenshotPath = QFileDialog::getSaveFileName(this,
tr("Choose a screenshot file"),
defaultPath);

if (!screenshotPath.isNull())
context->config.screenshotfile = screenshotPath.toStdString();
else
return;
}

context->hotkey_pressed_queue.push(HOTKEY_SCREENSHOT);
}

void MainWindow::slotVariableFramerate(bool checked)
{
context->config.sc.variable_framerate = checked;
Expand Down
2 changes: 2 additions & 0 deletions src/program/ui/MainWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class MainWindow : public QMainWindow

QAction *configEncodeAction;
QAction *toggleEncodeAction;
QAction *screenshotAction;

QActionGroup *slowdownGroup;
QActionGroup *fastforwardGroup;
Expand Down Expand Up @@ -234,6 +235,7 @@ private slots:
void slotMovieEnable(bool checked);
void slotMovieRecording();
void slotToggleEncode();
void slotScreenshot();
void slotPauseMovie();
void slotVariableFramerate(bool checked);
void slotRealTimeFormat();
Expand Down
6 changes: 6 additions & 0 deletions src/shared/messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,12 @@ enum {
*/
MSGN_MARKER,

/*
* Send a screenshot path and ask the game to perform a screenshot,
* Argument: size_t (string length) then char[len]
*/
MSGN_SCREENSHOT,

};

#endif

0 comments on commit eddd618

Please sign in to comment.