diff --git a/.devcontainer/dotnet-8.0/scripts/install-dependencies.sh b/.devcontainer/dotnet-8.0/scripts/install-dependencies.sh index 145edc3..2f5bec0 100644 --- a/.devcontainer/dotnet-8.0/scripts/install-dependencies.sh +++ b/.devcontainer/dotnet-8.0/scripts/install-dependencies.sh @@ -3,8 +3,9 @@ set -euo pipefail apt-get update -apt-get install -y cmake python3 python3-pip pkg-config portaudio19-dev +apt-get install -y cmake python3 python3-pip pkg-config portaudio19-dev nasm libwayland-dev libxkbcommon-dev libegl1-mesa-dev #pip3 install clang-format cpplint +pip3 install jinja2 cd /tmp/ # vcpkg diff --git a/.devcontainer/go/scripts/install-dependencies.sh b/.devcontainer/go/scripts/install-dependencies.sh index a2970d7..601d316 100644 --- a/.devcontainer/go/scripts/install-dependencies.sh +++ b/.devcontainer/go/scripts/install-dependencies.sh @@ -3,8 +3,9 @@ set -euo pipefail apt-get update -apt-get install -y cmake python3 python3-pip pkg-config portaudio19-dev +apt-get install -y cmake python3 python3-pip pkg-config portaudio19-dev nasm libwayland-dev libxkbcommon-dev libegl1-mesa-dev # pip3 install clang-format cpplint +pip3 install jinja2 cd /tmp/ # vcpkg diff --git a/.devcontainer/python/scripts/install-dependencies.sh b/.devcontainer/python/scripts/install-dependencies.sh index ed2ae47..9c052e5 100644 --- a/.devcontainer/python/scripts/install-dependencies.sh +++ b/.devcontainer/python/scripts/install-dependencies.sh @@ -3,8 +3,8 @@ set -euo pipefail apt-get update -apt-get install -y cmake python3 python3-pip pkg-config portaudio19-dev -pip3 install clang-format cpplint +apt-get install -y cmake python3 python3-pip pkg-config portaudio19-dev nasm libwayland-dev libxkbcommon-dev libegl1-mesa-dev +pip3 install clang-format cpplint jinja2 cd /tmp/ # vcpkg diff --git a/.devcontainer/rust/scripts/install-dependencies.sh b/.devcontainer/rust/scripts/install-dependencies.sh index ed2ae47..9c052e5 100644 --- a/.devcontainer/rust/scripts/install-dependencies.sh +++ b/.devcontainer/rust/scripts/install-dependencies.sh @@ -3,8 +3,8 @@ set -euo pipefail apt-get update -apt-get install -y cmake python3 python3-pip pkg-config portaudio19-dev -pip3 install clang-format cpplint +apt-get install -y cmake python3 python3-pip pkg-config portaudio19-dev nasm libwayland-dev libxkbcommon-dev libegl1-mesa-dev +pip3 install clang-format cpplint jinja2 cd /tmp/ # vcpkg diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 0042e7f..ca6e0ee 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -15,9 +15,9 @@ jobs: run: | sudo apt-get update sudo apt-get install -y \ - build-essential cmake portaudio19-dev libasound2-dev alsa-utils - - name: Check ALSA Mixer Settings - run: alsamixer + build-essential \ + cmake portaudio19-dev nasm + - name: Install vcpkg run: | git clone https://github.com/Microsoft/vcpkg.git @@ -30,7 +30,6 @@ jobs: build_windows: runs-on: windows-latest - needs: [build_and_test_ubuntu] steps: - name: Checkout repository uses: actions/checkout@v2 @@ -53,13 +52,12 @@ jobs: build_macos: runs-on: macos-latest - needs: [build_and_test_ubuntu] steps: - name: Checkout repository uses: actions/checkout@v2 - name: Install third-party brew dependencies run: | - brew install pkg-config cmake portaudio + brew install pkg-config cmake portaudio nasm - name: Install vcpkg run: | git clone https://github.com/Microsoft/vcpkg.git diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fda0bb8..f92a8fb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,10 +14,10 @@ jobs: - name: Install third-party apt dependencies run: | sudo apt-get update - sudo apt-get install -y \ - build-essential cmake portaudio19-dev libasound2-dev alsa-utils - - name: Check ALSA Mixer Settings - run: alsamixer + apt-get install -y \ + build-essential \ + cmake portaudio19-dev nasm + - name: Install vcpkg run: | git clone https://github.com/Microsoft/vcpkg.git diff --git a/.gitignore b/.gitignore index f151467..9f041fd 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,6 @@ __pycache__ obj bin Cargo.lock -target \ No newline at end of file +target +vcpkg +*.backup \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 84af1a7..5385e9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.7.0] - 20-04-2024 + +### Added + +- [Feature] C++ video module **(Optional, Experimental)**. + ## [0.6.0] - 17-04-2024 ### Updated diff --git a/README.md b/README.md index 795bd6b..f46c2bf 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ A collection of sample code snippets demonstrating how to create bindings for va - Download and install [CMake from the official website](https://cmake.org/download/) or trough a package manager - Install [vcpkg from the official website](https://vcpkg.io/en/getting-started.html) or trough a package manager +- Check dependencies required for your Linux, MacOS or Windows system in [pr.yml workflow](./.github/workflows/pr.yml) ### CMake diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 039ba8a..385da00 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -19,4 +19,4 @@ # SOFTWARE. add_subdirectory(audio-player) -# add_subdirectory(video-player) \ No newline at end of file +add_subdirectory(video-player) \ No newline at end of file diff --git a/apps/video-player/CMakeLists.txt b/apps/video-player/CMakeLists.txt new file mode 100644 index 0000000..680bfe0 --- /dev/null +++ b/apps/video-player/CMakeLists.txt @@ -0,0 +1,23 @@ +# The MIT License +# +# Copyright (c) 2024 MGTheTrain +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +if(BUILD_APP) + add_subdirectory(src) +endif() \ No newline at end of file diff --git a/apps/video-player/include/video-player.h b/apps/video-player/include/video-player.h new file mode 100644 index 0000000..f2e9216 --- /dev/null +++ b/apps/video-player/include/video-player.h @@ -0,0 +1,25 @@ +// The MIT License +// +// Copyright (c) 2024 MGTheTrain +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifdef VIDEO_PLAYER +#include +#endif \ No newline at end of file diff --git a/apps/video-player/src/CMakeLists.txt b/apps/video-player/src/CMakeLists.txt new file mode 100644 index 0000000..7b6e4b3 --- /dev/null +++ b/apps/video-player/src/CMakeLists.txt @@ -0,0 +1,49 @@ +# The MIT License +# +# Copyright (c) 2024 MGTheTrain +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.10) +set(TARGET video_player) +project(${TARGET}) + +add_definitions(-DVIDEO_PLAYER) + +find_package(FFMPEG REQUIRED) +find_package(SDL2 CONFIG REQUIRED) + +include_directories(../include + ../../../modules/video/include + ${FFMPEG_INCLUDE_DIRS} +) +set(VIDEO_PLAYER_SRC video-player.cpp) + +add_executable(${TARGET} ${VIDEO_PLAYER_SRC}) +if(WIN32) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/../../../assets/ + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Debug/assets/) +elseif(APPLE OR UNIX) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/../../../assets/ + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/assets/) +else() + message(WARNING "Unsupported platform") +endif() + +target_link_libraries(${TARGET} PRIVATE video ${FFMPEG_LIBRARIES} SDL2::SDL2) + +install(TARGETS ${TARGET}) \ No newline at end of file diff --git a/apps/video-player/src/video-player.cpp b/apps/video-player/src/video-player.cpp new file mode 100644 index 0000000..8314392 --- /dev/null +++ b/apps/video-player/src/video-player.cpp @@ -0,0 +1,53 @@ +// The MIT License +// +// Copyright (c) 2024 MGTheTrain +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifdef VIDEO_PLAYER +#include + +int main(int argc, char* argv[]) { + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + return -1; + } + + const char* videoFileName = argv[1]; + + VideoPlayer player; + + if (!initVideoPlayer(player)) { + std::cerr << "Failed to initialize video player\n"; + return -1; + } + + if (!loadVideo(player, videoFileName)) { + std::cerr << "Failed to load video: " << videoFileName << "\n"; + closeVideoPlayer(player); + return -1; + } + + playVideo(player); + + closeVideoPlayer(player); + + return 0; +} +#endif \ No newline at end of file diff --git a/assets/mp4/file_example_MP4_1280_10MG.mp4 b/assets/mp4/file_example_MP4_1280_10MG.mp4 new file mode 100644 index 0000000..57e0acf Binary files /dev/null and b/assets/mp4/file_example_MP4_1280_10MG.mp4 differ diff --git a/devops/scripts/bash/compile_source_code.sh b/devops/scripts/bash/compile_source_code.sh index ac08396..21cf91b 100755 --- a/devops/scripts/bash/compile_source_code.sh +++ b/devops/scripts/bash/compile_source_code.sh @@ -51,6 +51,6 @@ echo -e "$BLUE INFO: $NC Compilation of the source code and linking binaries suc if [ "$NoTests" = false ]; then cd "build" - ctest --verbose -E audio_test # ignore audio_test executable in workflows + ctest --verbose -E "video_test|audio_test" # ignore audio_test and video_test executable in workflows # ctest --verbose fi \ No newline at end of file diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 1d2eb47..28cdf70 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -1,3 +1,3 @@ add_subdirectory(core) add_subdirectory(audio) -# add_subdirectory(video) \ No newline at end of file +add_subdirectory(video) \ No newline at end of file diff --git a/modules/audio/src/CMakeLists.txt b/modules/audio/src/CMakeLists.txt index cf1ccae..4c4e437 100644 --- a/modules/audio/src/CMakeLists.txt +++ b/modules/audio/src/CMakeLists.txt @@ -42,4 +42,4 @@ else() target_link_libraries(${TARGET} SndFile::sndfile portaudio) target_link_libraries(${TARGET}_wrapper SndFile::sndfile portaudio) endif() -install(TARGETS ${TARGET}) \ No newline at end of file +install(TARGETS ${TARGET} ${TARGET}_wrapper) \ No newline at end of file diff --git a/modules/core/src/CMakeLists.txt b/modules/core/src/CMakeLists.txt index 4a5b3e1..957ab5e 100644 --- a/modules/core/src/CMakeLists.txt +++ b/modules/core/src/CMakeLists.txt @@ -32,4 +32,4 @@ set(CORE_SRC add_library(${TARGET} STATIC ${CORE_SRC}) add_library(${TARGET}_wrapper SHARED ${CORE_SRC}) # required for bindings -install(TARGETS ${TARGET}) \ No newline at end of file +install(TARGETS ${TARGET} ${TARGET}_wrapper) \ No newline at end of file diff --git a/modules/video/CMakeLists.txt b/modules/video/CMakeLists.txt index 2fd9f95..b067330 100644 --- a/modules/video/CMakeLists.txt +++ b/modules/video/CMakeLists.txt @@ -1 +1,27 @@ -TBD \ No newline at end of file +# The MIT License +# +# Copyright (c) 2024 MGTheTrain +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +if(BUILD_LIB) + add_subdirectory(src) +endif() + +if(BUILD_TEST) + add_subdirectory(test) +endif() \ No newline at end of file diff --git a/modules/video/include/.gitignore b/modules/video/include/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/modules/video/include/video.h b/modules/video/include/video.h new file mode 100644 index 0000000..8ac8440 --- /dev/null +++ b/modules/video/include/video.h @@ -0,0 +1,92 @@ +// The MIT License +// +// Copyright (c) 2024 MGTheTrain +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#define SDL_MAIN_HANDLED +#include +#include +#include +#include +#include +#endif + +/** + * @brief The VideoPlayer struct represents a video player. + */ +struct VideoPlayer { + /** + * @brief Constructs a new VideoPlayer object. + */ + VideoPlayer(); + + AVFormatContext* formatContext; + AVCodecParameters* codecParameters; + AVCodecContext* codecContext; + SDL_Window* window; + SDL_Renderer* renderer; + SDL_Texture* texture; + struct SwsContext* swsContext; + AVFrame* frame; + int videoStream; + AVRational timeBase; + double frameDuration; +}; + +/** + * @brief Initializes the video player. + * + * @param player Reference to the VideoPlayer object. + * @return True if initialization succeeds, false otherwise. + */ +bool initVideoPlayer(VideoPlayer& player); + +/** + * @brief Loads a video file into the video player. + * + * @param player Reference to the VideoPlayer object. + * @param filename The path to the video file. + * @return True if loading succeeds, false otherwise. + */ +bool loadVideo(VideoPlayer& player, const char* filename); + +/** + * @brief Starts playing the loaded video. + * + * @param player Reference to the VideoPlayer object. + */ +void playVideo(VideoPlayer& player); + +/** + * @brief Closes the video player and releases resources. + * + * @param player Reference to the VideoPlayer object. + */ +void closeVideoPlayer(VideoPlayer& player); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/modules/video/src/.gitignore b/modules/video/src/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/modules/video/src/CMakeLists.txt b/modules/video/src/CMakeLists.txt index 2fd9f95..d4f2a97 100644 --- a/modules/video/src/CMakeLists.txt +++ b/modules/video/src/CMakeLists.txt @@ -1 +1,43 @@ -TBD \ No newline at end of file +# The MIT License +# +# Copyright (c) 2024 MGTheTrain +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.10) + +set(TARGET video) +project(${TARGET}) + +find_package(FFMPEG REQUIRED) +find_package(SDL2 CONFIG REQUIRED) + +include_directories(${PROJECT_SOURCE_DIR}/../include + ${FFMPEG_INCLUDE_DIRS}) + +set(VIDEO_SRC + video.cpp +) + +add_library(${TARGET} STATIC ${VIDEO_SRC}) +target_link_libraries(${TARGET} PRIVATE ${FFMPEG_LIBRARIES} SDL2::SDL2) + +# add_library(${TARGET}_wrapper SHARED ${VIDEO_SRC}) # required for bindings +# target_link_libraries(${TARGET}_wrapper PRIVATE ${FFMPEG_LIBRARIES} SDL2::SDL2) + +# install(TARGETS ${TARGET} ${TARGET}_wrapper) +install(TARGETS ${TARGET}) \ No newline at end of file diff --git a/modules/video/src/video.cpp b/modules/video/src/video.cpp new file mode 100644 index 0000000..26b6181 --- /dev/null +++ b/modules/video/src/video.cpp @@ -0,0 +1,218 @@ +// The MIT License +// +// Copyright (c) 2024 MGTheTrain +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include + +/** + * @brief Constructs a new VideoPlayer object. + */ +VideoPlayer::VideoPlayer() { + formatContext = nullptr; + codecParameters = nullptr; + codecContext = nullptr; + window = nullptr; + renderer = nullptr; + texture = nullptr; + swsContext = nullptr; + frame = nullptr; + videoStream = -1; +} + +/** + * @brief Initializes the video player. + * + * @param player Reference to the VideoPlayer object. + * @return True if initialization succeeds, false otherwise. + */ +bool initVideoPlayer(VideoPlayer& player) { + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + std::cerr << "SDL initialization failed: " << SDL_GetError() << "\n"; + return false; + } + + if (avformat_network_init() < 0) { + std::cerr << "Failed to initialize FFmpeg network\n"; + return false; + } + + return true; +} + +/** + * @brief Loads a video file into the video player. + * + * @param player Reference to the VideoPlayer object. + * @param filename The path to the video file. + * @return True if loading succeeds, false otherwise. + */ +bool loadVideo(VideoPlayer& player, const char* filename) { + if (avformat_open_input(&player.formatContext, filename, NULL, NULL) != 0) { + std::cerr << "Couldn't open video file\n"; + return false; + } + + if (avformat_find_stream_info(player.formatContext, NULL) < 0) { + std::cerr << "Couldn't find stream information\n"; + return false; + } + + player.videoStream = -1; + for (int i = 0; i < player.formatContext->nb_streams; i++) { + if (player.formatContext->streams[i]->codecpar->codec_type == + AVMEDIA_TYPE_VIDEO) { + player.videoStream = i; + break; + } + } + + if (player.videoStream == -1) { + std::cerr << "Couldn't find a video stream\n"; + return false; + } + + player.codecParameters = + player.formatContext->streams[player.videoStream]->codecpar; + const AVCodec* codec = avcodec_find_decoder(player.codecParameters->codec_id); + if (!codec) { + std::cerr << "Unsupported codec\n"; + return false; + } + + player.codecContext = avcodec_alloc_context3(codec); + if (avcodec_parameters_to_context(player.codecContext, + player.codecParameters) < 0) { + std::cerr << "Failed to copy codec parameters to codec context\n"; + return false; + } + + if (avcodec_open2(player.codecContext, codec, NULL) < 0) { + std::cerr << "Failed to open codec\n"; + return false; + } + + player.window = SDL_CreateWindow( + "Video Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + player.codecContext->width, player.codecContext->height, + SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); + if (!player.window) { + std::cerr << "Failed to create SDL window: " << SDL_GetError() << "\n"; + return false; + } + + player.renderer = + SDL_CreateRenderer(player.window, -1, SDL_RENDERER_ACCELERATED); + if (!player.renderer) { + std::cerr << "Failed to create SDL renderer: " << SDL_GetError() << "\n"; + return false; + } + + player.texture = SDL_CreateTexture( + player.renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, + player.codecContext->width, player.codecContext->height); + if (!player.texture) { + std::cerr << "Failed to create SDL texture: " << SDL_GetError() << "\n"; + return false; + } + + player.swsContext = + sws_getContext(player.codecContext->width, player.codecContext->height, + player.codecContext->pix_fmt, player.codecContext->width, + player.codecContext->height, AV_PIX_FMT_YUV420P, + SWS_BILINEAR, NULL, NULL, NULL); + if (!player.swsContext) { + std::cerr << "Failed to create SwsContext\n"; + return false; + } + + player.timeBase = + player.formatContext->streams[player.videoStream]->time_base; + player.frameDuration = av_q2d(player.timeBase); + + player.frame = av_frame_alloc(); + if (!player.frame) { + std::cerr << "Failed to allocate frame\n"; + return false; + } + + return true; +} + +/** + * @brief Starts playing the loaded video. + * + * @param player Reference to the VideoPlayer object. + */ +void playVideo(VideoPlayer& player) { + AVPacket packet; + Uint32 previousFrameTime = SDL_GetTicks(); + + while (av_read_frame(player.formatContext, &packet) >= 0) { + if (packet.stream_index == player.videoStream) { + if (avcodec_send_packet(player.codecContext, &packet) < 0) { + std::cerr << "Error sending a packet for decoding\n"; + continue; + } + + if (avcodec_receive_frame(player.codecContext, player.frame) == 0) { + sws_scale(player.swsContext, player.frame->data, player.frame->linesize, + 0, player.frame->height, player.frame->data, + player.frame->linesize); + + SDL_UpdateYUVTexture(player.texture, nullptr, player.frame->data[0], + player.frame->linesize[0], player.frame->data[1], + player.frame->linesize[1], player.frame->data[2], + player.frame->linesize[2]); + SDL_RenderClear(player.renderer); + SDL_RenderCopy(player.renderer, player.texture, nullptr, nullptr); + SDL_RenderPresent(player.renderer); + + Uint32 currentFrameTime = SDL_GetTicks(); + Uint32 elapsedTime = currentFrameTime - previousFrameTime; + previousFrameTime = currentFrameTime; + + int desiredDelay = (int)(player.frameDuration * 1000) - elapsedTime; + + if (desiredDelay > 0) { + SDL_Delay(desiredDelay); + } + } + } + av_packet_unref(&packet); + } +} + +/** + * @brief Closes the video player and releases resources. + * + * @param player Reference to the VideoPlayer object. + */ +void closeVideoPlayer(VideoPlayer& player) { + SDL_DestroyTexture(player.texture); + SDL_DestroyRenderer(player.renderer); + SDL_DestroyWindow(player.window); + SDL_Quit(); + + av_frame_free(&player.frame); + avcodec_close(player.codecContext); + avformat_close_input(&player.formatContext); + avformat_network_deinit(); +} \ No newline at end of file diff --git a/modules/video/test/.gitignore b/modules/video/test/.gitignore deleted file mode 100644 index e69de29..0000000 diff --git a/modules/video/test/CMakeLists.txt b/modules/video/test/CMakeLists.txt index 2fd9f95..c755309 100644 --- a/modules/video/test/CMakeLists.txt +++ b/modules/video/test/CMakeLists.txt @@ -1 +1,49 @@ -TBD \ No newline at end of file +# The MIT License +# +# Copyright (c) 2024 MGTheTrain +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +cmake_minimum_required(VERSION 3.10) + + +set(TESTING_TARGET video_test) +project(${TESTING_TARGET}) + +add_definitions(-DMGTT_VIDEO_TEST) + +enable_testing() +find_package(GTest CONFIG REQUIRED) + +include_directories(${GTEST_INCLUDE_DIRS} + ../include +) +set(VIDEO_TEST_SRC + entrypoint.cpp + video-test.cpp) +add_executable(${TESTING_TARGET} ${VIDEO_TEST_SRC}) +if(WIN32) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/../../../assets/ + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/Debug/assets/) +elseif(APPLE OR UNIX) + file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/../../../assets/ + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/assets/) +else() + message(WARNING "Unsupported platform") +endif() +target_link_libraries(${TESTING_TARGET} PRIVATE GTest::gtest GTest::gtest_main video) +add_test(NAME ${TESTING_TARGET} COMMAND $) diff --git a/modules/video/test/entrypoint.cpp b/modules/video/test/entrypoint.cpp new file mode 100644 index 0000000..45b4b88 --- /dev/null +++ b/modules/video/test/entrypoint.cpp @@ -0,0 +1,30 @@ +// The MIT License +// +// Copyright (c) 2024 MGTheTrain +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifdef MGTT_AUDIO_TEST +#include + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif \ No newline at end of file diff --git a/modules/video/test/video-test.cpp b/modules/video/test/video-test.cpp new file mode 100644 index 0000000..88c1cf1 --- /dev/null +++ b/modules/video/test/video-test.cpp @@ -0,0 +1,55 @@ +// The MIT License +// +// Copyright (c) 2024 MGTheTrain +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED +// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifdef MGTT_VIDEO_TEST + +#include +#include + +// Test fixture for video functions +class VideoPlaybackTest : public ::testing::Test { + protected: + virtual void SetUp() { initVideoPlayer(player); } + + virtual void TearDown() { closeVideoPlayer(player); } + + VideoPlayer player; +}; + +// Test case for loading a video file +TEST_F(VideoPlaybackTest, LoadVideoFileTest) { + const char* filename = "assets/mp4/file_example_MP4_1280_10MG.mp4"; + EXPECT_TRUE(loadVideo(player, filename)); +} + +// Test case for starting video playback +TEST_F(VideoPlaybackTest, StartPlaybackTest) { + const char* filename = "assets/mp4/file_example_MP4_1280_10MG.mp4"; + EXPECT_TRUE(loadVideo(player, filename)); + playVideo(player); +} + +// Test case for closing video file and terminating video player +TEST_F(VideoPlaybackTest, CloseVideoFileTest) { + const char* filename = "assets/mp4/file_example_MP4_1280_10MG.mp4"; + EXPECT_TRUE(loadVideo(player, filename)); + playVideo(player); +} + +#endif diff --git a/vcpkg.json b/vcpkg.json index 8c64ee8..5b21d96 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -4,6 +4,8 @@ "dependencies": [ "portaudio", "libsndfile", + "sdl2", + "ffmpeg", "gtest" ] } \ No newline at end of file diff --git a/version b/version index 09a3acf..bcaffe1 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.6.0 \ No newline at end of file +0.7.0 \ No newline at end of file