diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b0aabe5..3e52a6e 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) set(CMAKE_CXX_STANDARD 20) -set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread -stdlib=libc++ -std=c++20" ) +set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread" ) project( video-to-terminal ) find_package( OpenCV REQUIRED ) include_directories( ${OpenCV_INCLUDE_DIRS} ) @@ -18,5 +18,9 @@ target_link_libraries( decoder stdc++fs ) add_executable( img-viewer img-viewer/main.cpp img-viewer/imgviewer.cpp libs/variousutils.cpp ) target_link_libraries( img-viewer stdc++fs ${OpenCV_LIBS} ) -add_executable( media-viewer media-viewer/main.cpp libs/termutils.cpp libs/variousutils.cpp libs/binaryutils.cpp ) +add_executable ( + media-viewer media-viewer/main.cpp media-viewer/mediaviewer.cpp + decoder/vtdidecoder.cpp + libs/termutils.cpp libs/variousutils.cpp libs/binaryutils.cpp libs/_kbhit.cpp +) target_link_libraries( media-viewer stdc++fs ${OpenCV_LIBS} ) diff --git a/src/decoder/main.cpp b/src/decoder/main.cpp index fdfbdfc..c60efb0 100644 --- a/src/decoder/main.cpp +++ b/src/decoder/main.cpp @@ -23,7 +23,7 @@ int main(int argc, char** argv) } VTDIDecoder player = VTDIDecoder(videoPath); - player.getStaticInfo(); + player.readStaticInfo(); std::cout << "VTDI Info:\n"; std::cout << "Version: " << player.getVersion() << "\n"; diff --git a/src/decoder/vtdidecoder.cpp b/src/decoder/vtdidecoder.cpp index b207686..baa2bc0 100755 --- a/src/decoder/vtdidecoder.cpp +++ b/src/decoder/vtdidecoder.cpp @@ -2,16 +2,19 @@ namespace vtt { -VTDIDecoder::VTDIDecoder(std::string path) +VTDIDecoder::VTDIDecoder(std::string path, bool debugInfo) { this->version = 0; this->vtdiFile.open(path); if (!vtdiFile.good()) { - throw std::runtime_error("Cannot open file to read from."); + std::stringstream errorMessage; + errorMessage << "Cannot open file \"" << path << "\" to read from."; + throw std::runtime_error(errorMessage.str()); } - std::cout << "File opened succesfully.\n"; + if (debugInfo) + std::cout << "File opened succesfully.\n"; vtdiFile.close(); vtdiFile.clear(); vtdiPath = path; @@ -37,7 +40,7 @@ T applyAssign(T num, int& index, const Byte* sib) return result; } -void VTDIDecoder::getStaticInfo() +void VTDIDecoder::readStaticInfo() { // these are taken from the spec const int expectedSig[] = {86, 84, 68, 73}; @@ -97,7 +100,7 @@ void VTDIDecoder::playVideo() if (this->version == 0) { - throw std::runtime_error("It seems static info hasn't been initialized yet. Try running VTDIDecoder.getStaticInfo()"); + throw std::runtime_error("It seems static info hasn't been initialized yet. Try running VTDIDecoder.readStaticInfo()"); } auto terminalDimensions = TermUtils::getTerminalDimensions(); diff --git a/src/decoder/vtdidecoder.hpp b/src/decoder/vtdidecoder.hpp index a868ae3..56d5852 100755 --- a/src/decoder/vtdidecoder.hpp +++ b/src/decoder/vtdidecoder.hpp @@ -27,8 +27,8 @@ class VTDIDecoder { float FPS; uint16_t version; public: - VTDIDecoder(std::string path); - void getStaticInfo(); + VTDIDecoder(std::string path, bool debugInfo = true); + void readStaticInfo(); void playVideo(); void readAndDisplayNextFrame(bool display=true, bool save=true); void displayCurrentFrame(); diff --git a/src/libs/_kbhit.h b/src/libs/_kbhit.cpp similarity index 64% rename from src/libs/_kbhit.h rename to src/libs/_kbhit.cpp index 7c912e1..addd41d 100644 --- a/src/libs/_kbhit.h +++ b/src/libs/_kbhit.cpp @@ -1,29 +1,22 @@ -#pragma once +#include "_kbhit.hpp" -#ifdef WIN32 +#ifndef WIN32 -// _kbhit() is originally from this library but it is windows only -#include - -#else // taken from https://www.flipcode.com/archives/_kbhit_for_Linux.shtml -// very slightly modified (#include to #include ) +// very slightly modified in the header file (#include to #include ) /** Linux (POSIX) implementation of _kbhit(). Morgan McGuire, morgan@cs.brown.edu */ -#include -#include -#include -#include - -int _kbhit() { +int _kbhit() +{ static const int STDIN = 0; static bool initialized = false; - if (! initialized) { + if (! initialized) + { // Use termios to turn off line buffering termios term; tcgetattr(STDIN, &term); @@ -37,4 +30,5 @@ int _kbhit() { ioctl(STDIN, FIONREAD, &bytesWaiting); return bytesWaiting; } + #endif diff --git a/src/libs/_kbhit.hpp b/src/libs/_kbhit.hpp new file mode 100644 index 0000000..b481c15 --- /dev/null +++ b/src/libs/_kbhit.hpp @@ -0,0 +1,17 @@ +#pragma once + +#ifdef WIN32 + +// _kbhit() is originally from this library but it is windows only +#include + +#else + +#include +#include +#include +#include + +int _kbhit(); + +#endif diff --git a/src/libs/termutils.cpp b/src/libs/termutils.cpp index 2bafcb6..c952c7d 100644 --- a/src/libs/termutils.cpp +++ b/src/libs/termutils.cpp @@ -7,6 +7,29 @@ TermUtils::TermUtils() hidden = false; } +KeyPress TermUtils::getKeyPress() +{ + KeyPress kp; + kp.keyDown = false; + kp.keyValue = 0; + + if (_kbhit()) + { + kp.keyDown = true; + + char tmp; + std::vector stdinVals; + while (_kbhit()) + { + std::cin >> tmp; + stdinVals.push_back(tmp); + } + kp.keyValue = BinaryUtils::byteArrayToUint((Byte*)stdinVals.data(), stdinVals.size()); + } + + return kp; +} + // taken from https://stackoverflow.com/a/23370070 #ifdef WIN32 diff --git a/src/libs/termutils.hpp b/src/libs/termutils.hpp index 14cb0fe..4adc305 100644 --- a/src/libs/termutils.hpp +++ b/src/libs/termutils.hpp @@ -1,5 +1,11 @@ #pragma once +#include +#include +#include +#include "binaryutils.hpp" +#include "_kbhit.hpp" + #ifdef WIN32 #include #else @@ -8,10 +14,14 @@ #include #include #endif -#include namespace vtt { +struct KeyPress { + bool keyDown; + unsigned int keyValue; +}; + class TermUtils { private: bool hidden; @@ -24,6 +34,7 @@ class TermUtils { #endif public: TermUtils(); + static KeyPress getKeyPress(); static std::array getTerminalDimensions(); void hideInput(); void showInput(); diff --git a/src/media-viewer/main.cpp b/src/media-viewer/main.cpp index 1d1f973..0a778da 100644 --- a/src/media-viewer/main.cpp +++ b/src/media-viewer/main.cpp @@ -1,9 +1,7 @@ -#include -#include +#include "mediaviewer.hpp" #include "../libs/variousutils.hpp" #include "../libs/termutils.hpp" #include "../libs/binaryutils.hpp" -#include "../libs/_kbhit.h" using namespace std; using namespace vtt; @@ -12,51 +10,39 @@ int main(int argc, char** argv) { filesystem::path path; if (argc > 1) - { path = filesystem::absolute(argv[1]); - if (!filesystem::is_directory(path)) - path = path.parent_path(); - } else path = filesystem::current_path(); - auto files = VariousUtils::getFilesInDir(path); + auto mv = MediaViewer(path); + + if (mv.empty()) + { + cout << "The selected folder " << path << " does not contain viewable files.\n"; + return 0; + } TermUtils tu; int fileIndex = 0; - cout << "Selected file: " << files[fileIndex].d_name << "\n"; + cout << "Selected file: " << mv.current()->path << "\n" << flush; unsigned char keyValue; cout << "waiting for a keypress...\npress ESC to close the program.\n" << flush; tu.hideInput(); while (true) { - if (_kbhit()) + KeyPress kp = TermUtils::getKeyPress(); + if (kp.keyDown) { - std::vector vals; - while (_kbhit()) - { - cin >> keyValue; - vals.push_back(keyValue); - } - int combinedKeyCode = BinaryUtils::byteArrayToUint((Byte*)vals.data(), vals.size()); - // right arrow - if (combinedKeyCode == 1792835) - { - fileIndex = (fileIndex + 1) % files.size(); - cout << "Selected file: " << files[fileIndex].d_name << "\n"; - } + if (kp.keyValue == 1792835) + cout << "Selected file: " << mv.next()->path << "\n"; // left arrow - if (combinedKeyCode == 1792836) - { - fileIndex--; - if (fileIndex < 0) fileIndex += files.size(); - cout << "Selected file: " << files[fileIndex].d_name << "\n"; - } + if (kp.keyValue == 1792836) + cout << "Selected file: " << mv.current()->path << "\n"; // ESC - if (combinedKeyCode == 27) + if (kp.keyValue == 27) break; } } diff --git a/src/media-viewer/mediaviewer.cpp b/src/media-viewer/mediaviewer.cpp new file mode 100644 index 0000000..63b9e2d --- /dev/null +++ b/src/media-viewer/mediaviewer.cpp @@ -0,0 +1,132 @@ +#include "mediaviewer.hpp" + +namespace vtt { + +bool validVTDI(std::string path) +{ + auto decoder = VTDIDecoder(path, false); + try + { + decoder.readStaticInfo(); + } + catch (std::runtime_error) + { + return false; + } + + return true; +} + +MediaViewer::MediaViewer(const std::filesystem::path path) +{ + filesIndex = 0; + + bool checkForFile; + std::filesystem::path dirPath; + if (std::filesystem::is_directory(path)) + { + checkForFile = false; + dirPath = path; + } + else + { + checkForFile = true; + dirPath = path.parent_path(); + } + + int i = 0; + for (const auto file : VariousUtils::getFilesInDir(dirPath)) + { + auto dirPathCopy = dirPath; + std::string pathFileName = dirPath.concat(file.d_name); + dirPath = dirPathCopy; + + if (cv::VideoCapture(pathFileName).isOpened()) + { + File fileStruct; + + std::string vtdiPath = ((std::string)pathFileName).substr(0, VariousUtils::rfind(pathFileName, '.')) + ".vtdi"; + if (std::filesystem::exists(vtdiPath) && validVTDI(vtdiPath)) + { + if (pathFileName == path) + { + filesIndex = i; + } + + fileStruct.path = vtdiPath; + fileStruct.type = FileType::VIDTRANS; + } + else + { + if (pathFileName == path) + { + filesIndex = i; + } + + fileStruct.path = pathFileName; + fileStruct.type = FileType::VIDENC; + } + + files.push_back(fileStruct); + } + else if (validVTDI(pathFileName)) + { + if (pathFileName == path) + { + filesIndex = i; + } + + File fileStruct; + fileStruct.path = pathFileName; + fileStruct.type = FileType::VIDTRANS; + + files.push_back(fileStruct); + } + else if (!cv::imread(pathFileName).empty()) + { + if (pathFileName == path) + { + filesIndex = i; + } + + File fileStruct; + fileStruct.path = pathFileName; + fileStruct.type = FileType::IMG; + + files.push_back(fileStruct); + } + + i++; + } +} + +const bool MediaViewer::empty() +{ + return files.empty(); +} + +File* MediaViewer::current() +{ + if (files.empty()) return nullptr; + + return &files[filesIndex]; +} + +File* MediaViewer::next() +{ + if (files.empty()) return nullptr; + + filesIndex = (filesIndex + 1) % files.size(); + return &files[filesIndex]; +} + +File* MediaViewer::prev() +{ + if (files.empty()) return nullptr; + + filesIndex--; + if (filesIndex < 0) filesIndex += files.size(); + return &files[filesIndex]; +} + +} diff --git a/src/media-viewer/mediaviewer.hpp b/src/media-viewer/mediaviewer.hpp index 25b95c5..ca8aa4d 100644 --- a/src/media-viewer/mediaviewer.hpp +++ b/src/media-viewer/mediaviewer.hpp @@ -1,22 +1,36 @@ #pragma once +#include #include "../transcoder/videotranscoder.hpp" #include "../decoder/vtdidecoder.hpp" #include "../img-viewer/imgviewer.hpp" namespace vtt { -enum FileType { image, video }; +// Image, Encoded Video, Transcoded Video +enum FileType { IMG, VIDENC, VIDTRANS }; struct File { std::string path; FileType type; - bool readyToView; }; class MediaViewer { private: std::vector files; + int filesIndex; + public: + MediaViewer(const std::filesystem::path path); + const bool empty(); + /// @brief Get the currently selected file. + /// @return A pointer to the file struct, or nullptr if the directory is empty. + File* current(); + /// @brief Move to the next file in the list. + /// @return A pointer to the next file in the list, or nullptr if the directory is empty. + File* next(); + /// @brief Move to the previous file in the list. + /// @return A pointer to the previous file in the list, or nullptr if the directory is empty. + File* prev(); }; }