From 9c9d280b432b7da5b059c991f40184e2d3acf17c Mon Sep 17 00:00:00 2001 From: nlburgin Date: Thu, 18 Apr 2019 21:27:01 -0400 Subject: [PATCH 1/8] add support for automatically merging encode segments via helper script --- CMakeLists.txt | 6 ++++++ libTasConcat.sh | 23 ++++++++++++++++++++++ src/program/Config.cpp | 2 ++ src/program/Config.h | 3 +++ src/program/GameLoop.cpp | 34 +++++++++++++++++++++++++++++++-- src/program/GameLoop.h | 2 ++ src/program/ui/EncodeWindow.cpp | 12 ++++++++++++ src/program/ui/EncodeWindow.h | 5 +++++ 8 files changed, 85 insertions(+), 2 deletions(-) create mode 100755 libTasConcat.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 4aeef3c96..c2a31bea3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -292,8 +292,14 @@ else() message(WARNING "HUD is disabled") endif() +#add helper script to build directory. +configure_file(libTasConcat.sh ./libTasConcat.sh COPYONLY) + # Install program and library install(TARGETS libTAS tas DESTINATION bin) +# Install dump segment automerge helper script +install(FILES libTasConcat.sh DESTINATION bin) + # Install desktop entry install(FILES libTAS.desktop DESTINATION share/applications) diff --git a/libTasConcat.sh b/libTasConcat.sh new file mode 100755 index 000000000..453b19f45 --- /dev/null +++ b/libTasConcat.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# absolute path to the first video (eg. /home/user/tasproject01/video.avi) is passed as command line argument 1 + +VID_PATH=$1 +VID_EXT=$(echo $VID_PATH | sed 's/^.*\././g') +VID_DIR=$(dirname $VID_PATH) +VID_NAME=$(basename -s $VID_EXT $VID_PATH) + +getChunks(){ + #first video is a special case + echo file $VID_PATH + + #sort command can ensure the rest are in order + temp_var="$VID_DIR"/"$VID_NAME"_ + for i in $(sort -nk 1.$(( ${#temp_var} + 1)) <(ls -1 "$VID_DIR"/"$VID_NAME"_*"$VID_EXT")) + do + echo file $i + done +} + +sync +yes | ffmpeg -f concat -safe 0 -i <(getChunks) -c copy $VID_DIR/$VID_NAME-merged.mkv diff --git a/src/program/Config.cpp b/src/program/Config.cpp index 705b15d00..a1a71fbd0 100644 --- a/src/program/Config.cpp +++ b/src/program/Config.cpp @@ -78,6 +78,7 @@ void Config::save(const std::string& gamepath) { settings.setValue("autosave_frames", autosave_frames); settings.setValue("autosave_count", autosave_count); settings.setValue("auto_restart", auto_restart); + settings.setValue("merge_dump_segments", merge_dump_segments); settings.beginGroup("keymapping"); @@ -195,6 +196,7 @@ void Config::load(const std::string& gamepath) { autosave_frames = settings.value("autosave_frames", autosave_frames).toInt(); autosave_count = settings.value("autosave_count", autosave_count).toInt(); auto_restart = settings.value("auto_restart", auto_restart).toBool(); + merge_dump_segments = settings.value("merge_dump_segments", merge_dump_segments).toBool(); /* Load key mapping */ diff --git a/src/program/Config.h b/src/program/Config.h index fdd30ca9b..83d072e5d 100644 --- a/src/program/Config.h +++ b/src/program/Config.h @@ -63,6 +63,9 @@ class Config { /* Were we started up with the -d option? */ bool dumping; + + /* Were we started up with the -d option? */ + bool merge_dump_segments = false; /* Path of the libraries used by the game */ std::string libdir; diff --git a/src/program/GameLoop.cpp b/src/program/GameLoop.cpp index 8ca788233..236f4e91a 100644 --- a/src/program/GameLoop.cpp +++ b/src/program/GameLoop.cpp @@ -36,7 +36,7 @@ #include #include #include -#include // fork() +#include // fork(), readlink, getpid #include // O_RDWR, O_CREAT #include #include // kill @@ -44,6 +44,11 @@ #include // waitpid #include +#include //sprintf +#include //PATH_MAX +#include //dirname +#include //std::min + #include #ifndef HAVE_PERSONALITY # include @@ -1108,6 +1113,8 @@ bool GameLoop::processEvent(uint8_t type, struct HotKey &hk) * allows to start a new encode on the same frame */ sendMessage(MSGN_STOP_ENCODE); + if (context->config.merge_dump_segments) + mergeSegments(); } emit sharedConfigChanged(); return false; @@ -1421,7 +1428,10 @@ void GameLoop::loopExit() emit statusChanged(); return; - } + } + /* If we're not restarting, then check if we need to merge the dumps */ + else if (context->config.sc.av_dumping && context->config.merge_dump_segments) + mergeSegments(); if (movie.modifiedSinceLastSave) { @@ -1451,3 +1461,23 @@ void GameLoop::loopExit() context->status = Context::INACTIVE; emit statusChanged(); } + +void GameLoop::mergeSegments() +{ + /*expect script to be in the same directory as executable. This uses a method from + https://stackoverflow.com/questions/143174/how-do-i-get-the-directory-that-a-program-is-running-from#198099 + which was tweaked until the compiler stopped complaining */ + const ssize_t len = PATH_MAX; + char szTmp[32]; + char exe[len]; + sprintf(szTmp, "/proc/%d/exe", getpid()); + const ssize_t bytes = std::min(readlink(szTmp, exe, len), len - 1); + if(bytes >= 0) + exe[bytes] = '\0'; + std::ostringstream mergeCommand; + mergeCommand << dirname(exe); + mergeCommand << "/libTasConcat.sh "; + mergeCommand << context->config.dumpfile; + sleep(2); //make sure encode has time to finish before merging + system(mergeCommand.str().c_str()); +} diff --git a/src/program/GameLoop.h b/src/program/GameLoop.h index d84abbde2..65426f6a8 100644 --- a/src/program/GameLoop.h +++ b/src/program/GameLoop.h @@ -91,6 +91,8 @@ class GameLoop : public QObject { bool haveFocus(); void loopExit(); + + void mergeSegments(); signals: void statusChanged(); diff --git a/src/program/ui/EncodeWindow.cpp b/src/program/ui/EncodeWindow.cpp index c1bfb1862..d3d8b51de 100644 --- a/src/program/ui/EncodeWindow.cpp +++ b/src/program/ui/EncodeWindow.cpp @@ -71,6 +71,9 @@ EncodeWindow::EncodeWindow(Context* c, QWidget *parent, Qt::WindowFlags flags) : connect(audioBitrate, static_cast(&QSpinBox::valueChanged), this, &EncodeWindow::slotUpdate); ffmpegOptions = new QLineEdit(); + + mergeCheck = new QCheckBox("Automatically merge dump segments"); + connect(mergeCheck, &QAbstractButton::clicked, this, &EncodeWindow::slotMerge); QGroupBox *codecGroupBox = new QGroupBox(tr("Encode codec settings")); QGridLayout *encodeCodecLayout = new QGridLayout; @@ -86,6 +89,8 @@ EncodeWindow::EncodeWindow(Context* c, QWidget *parent, Qt::WindowFlags flags) : encodeCodecLayout->addWidget(new QLabel(tr("ffmpeg options:")), 2, 0); encodeCodecLayout->addWidget(ffmpegOptions, 2, 1, 1, 4); + encodeCodecLayout->addWidget(mergeCheck, 3, 1, 1, 4); + encodeCodecLayout->setColumnMinimumWidth(2, 50); encodeCodecLayout->setColumnStretch(2, 1); @@ -128,6 +133,8 @@ void EncodeWindow::update_config() /* Set ffmpeg options */ ffmpegOptions->setText(context->config.ffmpegoptions.c_str()); + + mergeCheck->setChecked(context->config.merge_dump_segments); if (context->config.ffmpegoptions.empty()) { slotUpdate(); @@ -167,3 +174,8 @@ void EncodeWindow::slotBrowseEncodePath() if (!filename.isNull()) encodePath->setText(filename); } + +void EncodeWindow::slotMerge(bool checked) +{ + context->config.merge_dump_segments = checked; +} diff --git a/src/program/ui/EncodeWindow.h b/src/program/ui/EncodeWindow.h index a3f71cf31..533ebd0ef 100644 --- a/src/program/ui/EncodeWindow.h +++ b/src/program/ui/EncodeWindow.h @@ -25,6 +25,8 @@ #include #include #include +#include + #include "../Context.h" @@ -47,11 +49,14 @@ class EncodeWindow : public QDialog { QComboBox *audioChoice; QSpinBox *audioBitrate; QLineEdit *ffmpegOptions; + QCheckBox *mergeCheck; + private slots: void slotBrowseEncodePath(); void slotUpdate(); void slotOk(); + void slotMerge(bool checked); }; #endif From 4d9f1c41fffa0ac49070e8be088d5932ed70cb92 Mon Sep 17 00:00:00 2001 From: nlburgin <30382228+nlburgin@users.noreply.github.com> Date: Fri, 19 Apr 2019 12:32:12 -0400 Subject: [PATCH 2/8] Make the comment actually document this. Instead of just leaving the copy-pasted one intact :man_facepalming: --- src/program/Config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/program/Config.h b/src/program/Config.h index 83d072e5d..cf9506a90 100644 --- a/src/program/Config.h +++ b/src/program/Config.h @@ -64,7 +64,7 @@ class Config { /* Were we started up with the -d option? */ bool dumping; - /* Were we started up with the -d option? */ + /* Are we supposed to merge the segments after dumping? */ bool merge_dump_segments = false; /* Path of the libraries used by the game */ From 2cdffcbfcf634b634d3ca0078f24f31eef96ddaf Mon Sep 17 00:00:00 2001 From: nlburgin <30382228+nlburgin@users.noreply.github.com> Date: Fri, 19 Apr 2019 13:00:21 -0400 Subject: [PATCH 3/8] ffmpeg has a built-in option for this No need to pipe `yes` to overwrite without prompting. --- libTasConcat.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libTasConcat.sh b/libTasConcat.sh index 453b19f45..184f6e330 100755 --- a/libTasConcat.sh +++ b/libTasConcat.sh @@ -20,4 +20,4 @@ getChunks(){ } sync -yes | ffmpeg -f concat -safe 0 -i <(getChunks) -c copy $VID_DIR/$VID_NAME-merged.mkv +ffmpeg -f concat -safe 0 -i <(getChunks) -c copy -y $VID_DIR/$VID_NAME-merged.mkv From 4c4a22acb0adde6f37247bafbdf7580fb659ad33 Mon Sep 17 00:00:00 2001 From: nlburgin Date: Fri, 19 Apr 2019 19:07:05 -0400 Subject: [PATCH 4/8] how about we just embed the script into the program, and not have to cart it around? --- CMakeLists.txt | 6 ----- libTasConcat.sh | 23 ------------------- src/program/GameLoop.cpp | 23 +++++-------------- src/program/MergeHelperScript.h | 40 +++++++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 47 deletions(-) delete mode 100755 libTasConcat.sh create mode 100644 src/program/MergeHelperScript.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c2a31bea3..4aeef3c96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -292,14 +292,8 @@ else() message(WARNING "HUD is disabled") endif() -#add helper script to build directory. -configure_file(libTasConcat.sh ./libTasConcat.sh COPYONLY) - # Install program and library install(TARGETS libTAS tas DESTINATION bin) -# Install dump segment automerge helper script -install(FILES libTasConcat.sh DESTINATION bin) - # Install desktop entry install(FILES libTAS.desktop DESTINATION share/applications) diff --git a/libTasConcat.sh b/libTasConcat.sh deleted file mode 100755 index 184f6e330..000000000 --- a/libTasConcat.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -# absolute path to the first video (eg. /home/user/tasproject01/video.avi) is passed as command line argument 1 - -VID_PATH=$1 -VID_EXT=$(echo $VID_PATH | sed 's/^.*\././g') -VID_DIR=$(dirname $VID_PATH) -VID_NAME=$(basename -s $VID_EXT $VID_PATH) - -getChunks(){ - #first video is a special case - echo file $VID_PATH - - #sort command can ensure the rest are in order - temp_var="$VID_DIR"/"$VID_NAME"_ - for i in $(sort -nk 1.$(( ${#temp_var} + 1)) <(ls -1 "$VID_DIR"/"$VID_NAME"_*"$VID_EXT")) - do - echo file $i - done -} - -sync -ffmpeg -f concat -safe 0 -i <(getChunks) -c copy -y $VID_DIR/$VID_NAME-merged.mkv diff --git a/src/program/GameLoop.cpp b/src/program/GameLoop.cpp index 236f4e91a..959a5ad1f 100644 --- a/src/program/GameLoop.cpp +++ b/src/program/GameLoop.cpp @@ -24,6 +24,7 @@ #include "GameLoop.h" #include "utils.h" #include "AutoSave.h" +#include "MergeHelperScript.h" #include "../shared/sockethelpers.h" #include "../shared/SharedConfig.h" @@ -44,11 +45,6 @@ #include // waitpid #include -#include //sprintf -#include //PATH_MAX -#include //dirname -#include //std::min - #include #ifndef HAVE_PERSONALITY # include @@ -1463,20 +1459,11 @@ void GameLoop::loopExit() } void GameLoop::mergeSegments() -{ - /*expect script to be in the same directory as executable. This uses a method from - https://stackoverflow.com/questions/143174/how-do-i-get-the-directory-that-a-program-is-running-from#198099 - which was tweaked until the compiler stopped complaining */ - const ssize_t len = PATH_MAX; - char szTmp[32]; - char exe[len]; - sprintf(szTmp, "/proc/%d/exe", getpid()); - const ssize_t bytes = std::min(readlink(szTmp, exe, len), len - 1); - if(bytes >= 0) - exe[bytes] = '\0'; +{ std::ostringstream mergeCommand; - mergeCommand << dirname(exe); - mergeCommand << "/libTasConcat.sh "; + mergeCommand << "bash -c '"; + mergeCommand << MERGE_HELPER_SCRIPT; + mergeCommand << "' -- "; mergeCommand << context->config.dumpfile; sleep(2); //make sure encode has time to finish before merging system(mergeCommand.str().c_str()); diff --git a/src/program/MergeHelperScript.h b/src/program/MergeHelperScript.h new file mode 100644 index 000000000..dea5682e4 --- /dev/null +++ b/src/program/MergeHelperScript.h @@ -0,0 +1,40 @@ +#ifndef MERGE_HELPER_SCRIPT_HEADER +#define MERGE_HELPER_SCRIPT_HEADER + +/* Embeds Bash helper script as a C++11 raw string. + * + * This avoids having to cart around the script as a separate file. + * + * A "raw" string also avoids having to deal with escape sequences. + * However, if your syntax highlighter isn't aware of raw strings, + * then this may look a bit like a train wreck. */ + + /*try to avoid using single-quoted strings within the script, it may make it harder to run later.*/ + +const static char* MERGE_HELPER_SCRIPT = R"( + +# absolute path to the first video (eg. /home/user/tasproject01/video.avi) is passed as command line argument 1 + +VID_PATH=$1 +VID_EXT=$(echo $VID_PATH | sed "s/^.*\././g") +VID_DIR=$(dirname $VID_PATH) +VID_NAME=$(basename -s $VID_EXT $VID_PATH) + +getChunks(){ + #first video is a special case + echo file $VID_PATH + + #sort command can ensure the rest are in order + temp_var="$VID_DIR"/"$VID_NAME"_ + for i in $(ls -v "$VID_DIR"/"$VID_NAME"_*"$VID_EXT") + do + echo file $i + done +} + +sync +ffmpeg -f concat -safe 0 -i <(getChunks) -c copy -y $VID_DIR/$VID_NAME-merged.mkv + +)"; + +#endif From 5c9ca28de0d50b04368127b406420c741a7bbf2a Mon Sep 17 00:00:00 2001 From: nlburgin Date: Fri, 19 Apr 2019 21:03:38 -0400 Subject: [PATCH 5/8] minor cleanup --- src/program/GameLoop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/program/GameLoop.cpp b/src/program/GameLoop.cpp index 959a5ad1f..0d0c597e1 100644 --- a/src/program/GameLoop.cpp +++ b/src/program/GameLoop.cpp @@ -37,7 +37,7 @@ #include #include #include -#include // fork(), readlink, getpid +#include // fork() #include // O_RDWR, O_CREAT #include #include // kill From 3c523cda4d48ce70bcfe334b55ae7332ca2caeff Mon Sep 17 00:00:00 2001 From: nlburgin Date: Fri, 19 Apr 2019 21:18:24 -0400 Subject: [PATCH 6/8] Wasn't thinking about it earlier, but I guess every source file in the project is supposed to have a copy of this at the top. --- src/program/MergeHelperScript.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/program/MergeHelperScript.h b/src/program/MergeHelperScript.h index dea5682e4..b0ce344d3 100644 --- a/src/program/MergeHelperScript.h +++ b/src/program/MergeHelperScript.h @@ -1,3 +1,22 @@ +/* + Copyright 2015-2019 Clément Gallet + + 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 . + */ + #ifndef MERGE_HELPER_SCRIPT_HEADER #define MERGE_HELPER_SCRIPT_HEADER From 7317a2f0d1d7c1ceb768b5dfbc9ae78fbbe150b9 Mon Sep 17 00:00:00 2001 From: nlburgin <30382228+nlburgin@users.noreply.github.com> Date: Sat, 20 Apr 2019 01:52:17 -0400 Subject: [PATCH 7/8] Include stdlib.h It worked even though I forgot this, so I assume it was already getting included by one of the other headers. Still, I think it's good practice to explicitly include the correct headers. --- src/program/GameLoop.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/program/GameLoop.cpp b/src/program/GameLoop.cpp index 0d0c597e1..0cffbfeb6 100644 --- a/src/program/GameLoop.cpp +++ b/src/program/GameLoop.cpp @@ -44,6 +44,7 @@ #include // stat #include // waitpid #include +#include //system() #include #ifndef HAVE_PERSONALITY From 6ee15a50eee3a8fb2ea64677088eb33f76013739 Mon Sep 17 00:00:00 2001 From: nlburgin <30382228+nlburgin@users.noreply.github.com> Date: Sat, 20 Apr 2019 13:42:42 -0400 Subject: [PATCH 8/8] Simplify/cleanup script --- src/program/MergeHelperScript.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/program/MergeHelperScript.h b/src/program/MergeHelperScript.h index b0ce344d3..0710b72a4 100644 --- a/src/program/MergeHelperScript.h +++ b/src/program/MergeHelperScript.h @@ -40,11 +40,6 @@ VID_DIR=$(dirname $VID_PATH) VID_NAME=$(basename -s $VID_EXT $VID_PATH) getChunks(){ - #first video is a special case - echo file $VID_PATH - - #sort command can ensure the rest are in order - temp_var="$VID_DIR"/"$VID_NAME"_ for i in $(ls -v "$VID_DIR"/"$VID_NAME"_*"$VID_EXT") do echo file $i