├── .github ├── pre-commit │ ├── ignored-words.txt │ └── config.yaml └── workflows │ ├── lint.yaml │ └── ci.yml ├── img ├── cut_video.png ├── info_view.png └── compress_picture.png ├── src ├── resources │ ├── lang_chinese.qm │ ├── OpenConverter.icns │ ├── OpenConverter-logo.png │ └── lang.qrc ├── common │ ├── include │ │ ├── process_observer.h │ │ ├── stream_context.h │ │ ├── process_parameter.h │ │ ├── info.h │ │ └── encode_parameter.h │ └── src │ │ ├── stream_context.cpp │ │ ├── process_parameter.cpp │ │ ├── encode_parameter.cpp │ │ └── info.cpp ├── builder │ ├── include │ │ ├── placeholder_page.h │ │ ├── shared_data.h │ │ ├── transcoder_helper.h │ │ ├── base_page.h │ │ ├── create_gif_page.h │ │ ├── batch_file_dialog.h │ │ ├── info_view_page.h │ │ ├── extract_audio_page.h │ │ ├── compress_picture_page.h │ │ ├── batch_queue.h │ │ ├── batch_item.h │ │ ├── remux_page.h │ │ ├── batch_queue_dialog.h │ │ ├── transcode_page.h │ │ ├── cut_video_page.h │ │ ├── open_converter.h │ │ ├── batch_mode_helper.h │ │ └── converter_runner.h │ └── src │ │ ├── transcoder_helper.cpp │ │ ├── placeholder_page.cpp │ │ ├── shared_data.cpp │ │ ├── base_page.cpp │ │ ├── batch_item.cpp │ │ ├── batch_queue.cpp │ │ └── batch_mode_helper.cpp ├── tests │ └── CMakeLists.txt ├── engine │ ├── include │ │ └── converter.h │ └── src │ │ └── converter.cpp ├── transcoder │ ├── include │ │ ├── transcoder_bmf.h │ │ ├── transcoder_fftool.h │ │ ├── transcoder_ffmpeg.h │ │ └── transcoder.h │ └── src │ │ └── transcoder_bmf.cpp └── component │ ├── src │ ├── progress_widget.cpp │ ├── quality_widget.cpp │ ├── bitrate_widget.cpp │ ├── codec_selector_widget.cpp │ ├── pixel_format_widget.cpp │ ├── resolution_widget.cpp │ └── format_selector_widget.cpp │ └── include │ ├── quality_widget.h │ ├── bitrate_widget.h │ ├── pixel_format_widget.h │ ├── simple_video_player.h │ ├── codec_selector_widget.h │ ├── resolution_widget.h │ ├── progress_widget.h │ ├── format_selector_widget.h │ ├── batch_input_widget.h │ ├── filter_tag_widget.h │ ├── batch_output_widget.h │ └── file_selector_widget.h ├── .gitmodules ├── .clang-format ├── .gitignore ├── doc ├── Qt.md ├── OpenConverter_Deploy.md ├── OpenConverter_Usage.md └── CompileGuide.md ├── tool ├── fix_whitespace.sh ├── setup_test_deps.sh ├── format.sh ├── dmg_install_instructions.txt ├── fix_gatekeeper.sh ├── build_and_package_macos.sh └── create_dmg_simple.sh ├── CONTRIBUTING.md └── README_ZH.md /.github/pre-commit/ignored-words.txt: -------------------------------------------------------------------------------- 1 | anull 2 | bmf 3 | BMF 4 | -------------------------------------------------------------------------------- /img/cut_video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenConverterLab/OpenConverter/HEAD/img/cut_video.png -------------------------------------------------------------------------------- /img/info_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenConverterLab/OpenConverter/HEAD/img/info_view.png -------------------------------------------------------------------------------- /img/compress_picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenConverterLab/OpenConverter/HEAD/img/compress_picture.png -------------------------------------------------------------------------------- /src/resources/lang_chinese.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenConverterLab/OpenConverter/HEAD/src/resources/lang_chinese.qm -------------------------------------------------------------------------------- /src/resources/OpenConverter.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenConverterLab/OpenConverter/HEAD/src/resources/OpenConverter.icns -------------------------------------------------------------------------------- /src/resources/OpenConverter-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenConverterLab/OpenConverter/HEAD/src/resources/OpenConverter-logo.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rd_party/gtest"] 2 | path = 3rd_party/gtest 3 | url = https://github.com/google/googletest.git 4 | ignore = dirty 5 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | AccessModifierOffset: -4 3 | IndentWidth: 4 4 | IndentPPDirectives: BeforeHash 5 | IndentAccessModifiers: false 6 | -------------------------------------------------------------------------------- /src/resources/lang.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | lang_chinese.qm 4 | OpenConverter-logo.png 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | open_converter.pro.user 2 | build-* 3 | .vscode 4 | .DS_Store 5 | ./src/.DS_Store 6 | src/CMakeLists.txt.user 7 | src/build 8 | src/build-* 9 | build 10 | .idea 11 | workload_output.txt 12 | -------------------------------------------------------------------------------- /doc/Qt.md: -------------------------------------------------------------------------------- 1 | # [Qt](https://www.qt.io)介绍 2 | 3 | Qt是一种跨平台的C++应用程序开发框架,由Qt公司(位于挪威和德国)开发。Qt可以用于开发图形用户界面(GUI)程序、命令行工具、服务器端应用程序等多种类型的软件。 4 | 5 | Qt提供了丰富的类库和工具集,使得开发者能够轻松地创建高质量的应用程序。Qt还支持多种操作系统和硬件平台,包括Windows、Linux、macOS、Android等。 6 | 7 | ## Qt的主要特点包括: 8 | 9 | * 跨平台支持:Qt支持多种操作系统和硬件平台,并提供相应的API和工具集。 10 | 11 | * 面向对象设计:Qt基于面向对象的设计思想,提供丰富的类库和模块,使开发者能够快速构建高质量的应用程序。 12 | 13 | * 可扩展性:Qt提供了多种扩展方式,包括插件、组件等,方便开发者进行功能扩展和定制化。 14 | 15 | * 易于学习和使用:Qt的API设计符合直觉,易于理解和记忆,同时也提供了详细的文档和教程,帮助开发者快速上手。 16 | 17 | ## Qt相关网站 18 | 19 | [Qt官方网站](https://www.qt.io/) 20 | 21 | [Qt中文网站](https://www.qtchina.net/) 22 | 23 | [Qt文档](https://doc.qt.io/) 24 | 25 | [Qt论坛](https://forum.qt.io/) 26 | 27 | ## 本项目所使用Qt版本 28 | 29 | 5.14.2 30 | -------------------------------------------------------------------------------- /.github/pre-commit/config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v5.0.0 4 | hooks: 5 | - id: check-case-conflict 6 | - id: check-executables-have-shebangs 7 | - id: check-illegal-windows-names 8 | - id: check-yaml 9 | - id: end-of-file-fixer 10 | - id: file-contents-sorter 11 | files: 12 | .github/pre-commit/ignored-words.txt 13 | args: 14 | - --ignore-case 15 | - id: fix-byte-order-marker 16 | - id: mixed-line-ending 17 | - id: trailing-whitespace 18 | - repo: https://github.com/codespell-project/codespell 19 | rev: v2.4.1 20 | hooks: 21 | - id: codespell 22 | args: 23 | - --ignore-words=.github/pre-commit/ignored-words.txt 24 | - --ignore-multiline-regex=codespell:off.*?(codespell:on|\Z) 25 | exclude: ^tools/(patcheck|clean-diff)$ 26 | -------------------------------------------------------------------------------- /src/common/include/process_observer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef PROCESSOBSERVER_H 19 | #define PROCESSOBSERVER_H 20 | 21 | class ProcessObserver { 22 | public: 23 | virtual ~ProcessObserver() = default; 24 | virtual void on_process_update(double progress) = 0; 25 | virtual void on_time_update(double timeRequired) = 0; 26 | }; 27 | 28 | #endif // PROCESSOBSERVER_H 29 | -------------------------------------------------------------------------------- /tool/fix_whitespace.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Fix trailing whitespace and ensure single newline at end of file 3 | 4 | # Get list of staged files (C++ and header files) 5 | files=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(cpp|h|hpp|cc|cxx)$') 6 | 7 | if [ -z "$files" ]; then 8 | echo "No C++ files to fix" 9 | exit 0 10 | fi 11 | 12 | echo "Fixing whitespace issues in files..." 13 | echo "" 14 | 15 | for file in $files; do 16 | if [ -f "$file" ]; then 17 | echo " $file" 18 | 19 | # Remove trailing whitespace from each line 20 | sed -i '' 's/[[:space:]]*$//' "$file" 21 | 22 | # Ensure file ends with exactly one newline 23 | # This perl command ensures the file ends with exactly one newline 24 | perl -pi -e 'BEGIN{undef $/;} s/\s*\z/\n/' "$file" 25 | fi 26 | done 27 | 28 | echo "" 29 | echo "Done! Fixed whitespace in $(echo "$files" | wc -l | tr -d ' ') files" 30 | echo "" 31 | echo "⚠️ Please review the changes and add them manually:" 32 | echo " git add " 33 | echo " or: git add -u" 34 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | lint: 12 | permissions: 13 | contents: read 14 | runs-on: ubuntu-22.04 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | - name: Install pre-commit CI 19 | id: install 20 | run: | 21 | python3 -m venv ~/pre-commit 22 | ~/pre-commit/bin/pip install --upgrade pip setuptools 23 | ~/pre-commit/bin/pip install pre-commit 24 | echo "envhash=$({ python3 --version && cat .github/pre-commit/config.yaml; } | sha256sum | cut -d' ' -f1)" >> $GITHUB_OUTPUT 25 | - name: Cache 26 | uses: actions/cache@v4 27 | with: 28 | path: ~/.cache/pre-commit 29 | key: pre-commit-${{ steps.install.outputs.envhash }} 30 | - name: Run pre-commit CI 31 | run: ~/pre-commit/bin/pre-commit run -c .github/pre-commit/config.yaml --show-diff-on-failure --color=always --all-files 32 | -------------------------------------------------------------------------------- /src/builder/include/placeholder_page.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef PLACEHOLDER_PAGE_H 19 | #define PLACEHOLDER_PAGE_H 20 | 21 | #include "base_page.h" 22 | #include 23 | #include 24 | 25 | class PlaceholderPage : public BasePage { 26 | Q_OBJECT 27 | 28 | public: 29 | explicit PlaceholderPage(const QString &title, QWidget *parent = nullptr); 30 | ~PlaceholderPage(); 31 | 32 | QString GetPageTitle() const override; 33 | 34 | private: 35 | QString pageTitle; 36 | QLabel *label; 37 | }; 38 | 39 | #endif // PLACEHOLDER_PAGE_H 40 | -------------------------------------------------------------------------------- /src/builder/src/transcoder_helper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "../include/transcoder_helper.h" 19 | #include "../include/open_converter.h" 20 | #include 21 | 22 | QString TranscoderHelper::GetCurrentTranscoderName(QWidget *widget) { 23 | if (!widget) { 24 | return "FFMPEG"; // Default fallback 25 | } 26 | 27 | // Get main window 28 | OpenConverter *mainWindow = qobject_cast(widget->window()); 29 | if (mainWindow) { 30 | return mainWindow->GetCurrentTranscoderName(); 31 | } 32 | 33 | // Fallback to default 34 | return "FFMPEG"; 35 | } 36 | -------------------------------------------------------------------------------- /src/builder/src/placeholder_page.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "../include/placeholder_page.h" 19 | 20 | PlaceholderPage::PlaceholderPage(const QString &title, QWidget *parent) 21 | : BasePage(parent), pageTitle(title) { 22 | QVBoxLayout *layout = new QVBoxLayout(this); 23 | 24 | label = new QLabel(QString("Page: %1\n\nThis page will be implemented later.").arg(title), this); 25 | label->setAlignment(Qt::AlignCenter); 26 | label->setStyleSheet("font-size: 16px; color: #666;"); 27 | 28 | layout->addWidget(label); 29 | setLayout(layout); 30 | } 31 | 32 | PlaceholderPage::~PlaceholderPage() {} 33 | 34 | QString PlaceholderPage::GetPageTitle() const { 35 | return pageTitle; 36 | } 37 | -------------------------------------------------------------------------------- /tool/setup_test_deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Exit on error 4 | set -e 5 | 6 | # Get the root directory of the project 7 | ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" 8 | THIRD_PARTY_DIR="${ROOT_DIR}/third_party" 9 | 10 | # Create third_party directory if it doesn't exist 11 | mkdir -p "${THIRD_PARTY_DIR}" 12 | 13 | # Deinitialize all submodules first 14 | echo "Deinitializing existing submodules..." 15 | git submodule deinit -f --all || true 16 | 17 | # Remove .gitmodules if it exists 18 | if [ -f "${ROOT_DIR}/.gitmodules" ]; then 19 | echo "Removing existing .gitmodules..." 20 | rm "${ROOT_DIR}/.gitmodules" 21 | fi 22 | 23 | # Create fresh .gitmodules 24 | echo "Creating new .gitmodules file..." 25 | touch "${ROOT_DIR}/.gitmodules" 26 | git add "${ROOT_DIR}/.gitmodules" 27 | 28 | # Initialize git submodules 29 | echo "Initializing git submodules..." 30 | # git submodule init 31 | 32 | # Add or update googletest submodule 33 | if [ ! -d "${THIRD_PARTY_DIR}/googletest" ]; then 34 | echo "Adding googletest submodule..." 35 | git submodule add https://github.com/google/googletest.git "${THIRD_PARTY_DIR}/googletest" 36 | # git submodule update --init --recursive "${THIRD_PARTY_DIR}/googletest" 37 | (cd "${THIRD_PARTY_DIR}/googletest" && git checkout release-1.12.1) 38 | fi 39 | 40 | echo "Test dependencies have been set up successfully!" 41 | -------------------------------------------------------------------------------- /src/common/include/stream_context.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * This file is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | */ 15 | 16 | #ifndef STREAMCONTEXT_H 17 | #define STREAMCONTEXT_H 18 | 19 | extern "C" { 20 | #include 21 | #include 22 | #include 23 | }; 24 | 25 | #define OC_INVALID_STREAM_IDX -1 26 | 27 | class StreamContext { 28 | 29 | public: 30 | StreamContext(); 31 | ~StreamContext(); 32 | 33 | AVFormatContext *fmtCtx; 34 | const char *filename; 35 | 36 | int videoIdx; 37 | AVStream *videoStream; 38 | const AVCodec *videoCodec; 39 | AVCodecContext *videoCodecCtx; 40 | 41 | int audioIdx; 42 | AVStream *audioStream; 43 | const AVCodec *audioCodec; 44 | AVCodecContext *audioCodecCtx; 45 | 46 | AVPacket *pkt; 47 | AVFrame *frame; 48 | }; 49 | 50 | #endif // STREAMCONTEXT_H 51 | -------------------------------------------------------------------------------- /src/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | # Enable testing 4 | enable_testing() 5 | 6 | # Add GTest from 3rd_party 7 | add_subdirectory(${CMAKE_SOURCE_DIR}/../3rd_party/gtest ${CMAKE_BINARY_DIR}/gtest) 8 | 9 | # Add test executable 10 | add_executable(transcoder_test transcoder_test.cpp) 11 | 12 | # Set C++17 for std::filesystem 13 | target_compile_features(transcoder_test PRIVATE cxx_std_17) 14 | target_compile_definitions(transcoder_test PRIVATE 15 | _LIBCPP_ENABLE_CXX17_REMOVED_FEATURES 16 | GTEST_HAS_PTHREAD=1 17 | TEST_MEDIA_PATH="${CMAKE_BINARY_DIR}/tests/media" 18 | ) 19 | 20 | # Include directories 21 | target_include_directories(transcoder_test PRIVATE 22 | ${CMAKE_SOURCE_DIR}/common/include 23 | ${CMAKE_SOURCE_DIR}/transcoder/include 24 | ${CMAKE_SOURCE_DIR}/engine/include 25 | ) 26 | 27 | # Link against GTest and core library 28 | target_link_libraries(transcoder_test 29 | PRIVATE 30 | gtest_main 31 | OpenConverterCore 32 | ) 33 | 34 | # Copy GTest libraries to test directory 35 | add_custom_command(TARGET transcoder_test POST_BUILD 36 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 37 | $ 38 | $ 39 | $ 40 | $ 41 | $ 42 | ) 43 | 44 | # Add test to CTest 45 | add_test(NAME transcoder_test COMMAND transcoder_test) 46 | -------------------------------------------------------------------------------- /src/engine/include/converter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef CONVERTER_H 19 | #define CONVERTER_H 20 | 21 | #include "../../common/include/encode_parameter.h" 22 | #include "../../transcoder/include/transcoder.h" 23 | #include 24 | #include 25 | 26 | class Converter { 27 | public: 28 | Converter(); 29 | Converter(ProcessParameter *processParamter, 30 | EncodeParameter *encodeParamter); 31 | ~Converter(); 32 | 33 | bool set_transcoder(std::string transcoderName); 34 | bool convert_format(const std::string &src, const std::string &dst); 35 | 36 | private: 37 | Transcoder *transcoder = NULL; 38 | bool copyVideo; 39 | bool copyAudio; 40 | 41 | public: 42 | ProcessParameter *processParameter = NULL; 43 | EncodeParameter *encodeParameter = NULL; 44 | }; 45 | 46 | #endif // CONVERTER_H 47 | -------------------------------------------------------------------------------- /src/common/src/stream_context.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * This file is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | */ 15 | 16 | #include "../include/stream_context.h" 17 | 18 | StreamContext::StreamContext() { 19 | fmtCtx = NULL; 20 | filename = NULL; 21 | 22 | videoIdx = OC_INVALID_STREAM_IDX; 23 | videoStream = NULL; 24 | videoCodec = NULL; 25 | videoCodecCtx = NULL; 26 | 27 | audioIdx = OC_INVALID_STREAM_IDX; 28 | audioStream = NULL; 29 | audioCodec = NULL; 30 | audioCodecCtx = NULL; 31 | 32 | pkt = NULL; 33 | frame = NULL; 34 | } 35 | 36 | StreamContext::~StreamContext() { 37 | if (videoCodecCtx) { 38 | avcodec_free_context(&videoCodecCtx); 39 | videoCodecCtx = NULL; 40 | } 41 | if (audioCodecCtx) { 42 | avcodec_free_context(&audioCodecCtx); 43 | audioCodecCtx = NULL; 44 | } 45 | if (pkt) { 46 | av_packet_free(&pkt); 47 | pkt = NULL; 48 | } 49 | if (frame) { 50 | av_frame_free(&frame); 51 | frame = NULL; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/transcoder/include/transcoder_bmf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * This file is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | */ 15 | 16 | #ifndef TRANSCODER_BMF_H 17 | #define TRANSCODER_BMF_H 18 | 19 | #include "transcoder.h" 20 | 21 | #include "builder.hpp" 22 | #include "nlohmann/json.hpp" 23 | 24 | #include 25 | 26 | class TranscoderBMF : public Transcoder { 27 | public: 28 | TranscoderBMF(ProcessParameter *process_parameter, 29 | EncodeParameter *encode_parameter); 30 | 31 | bool prepare_info(std::string input_path, std::string output_path); 32 | 33 | bool transcode(std::string input_path, std::string output_path); 34 | 35 | bmf_sdk::CBytes decoder_callback(bmf_sdk::CBytes input); 36 | 37 | bmf_sdk::CBytes encoder_callback(bmf_sdk::CBytes input); 38 | 39 | private: 40 | // encoder's parameters 41 | bool copy_video; 42 | bool copy_audio; 43 | 44 | int width; 45 | int height; 46 | 47 | nlohmann::json decoder_para; 48 | nlohmann::json encoder_para; 49 | }; 50 | 51 | #endif // TRANSCODER_BMF_H 52 | -------------------------------------------------------------------------------- /src/common/include/process_parameter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef PROCESSPARAMETER_H 19 | #define PROCESSPARAMETER_H 20 | 21 | #include "process_observer.h" 22 | #include 23 | #include 24 | 25 | class ProcessParameter { 26 | public: 27 | ProcessParameter(); 28 | ~ProcessParameter(); 29 | 30 | void set_process_number(int64_t frameNumber, int64_t frameTotalNumnber); 31 | void set_process_number(int64_t processNumber); 32 | double get_process_number(); 33 | void set_time_required(double timeRequired); 34 | double get_time_required(); 35 | ProcessParameter get_process_parmeter(); 36 | 37 | // Observer management 38 | void add_observer(ProcessObserver* observer); 39 | void remove_observer(ProcessObserver* observer); 40 | 41 | private: 42 | int64_t processNumber; 43 | double timeRequired; 44 | std::vector observers; 45 | 46 | void notify_process_update(double progress); 47 | void notify_time_update(double timeRequired); 48 | }; 49 | 50 | #endif // PROCESSPARAMETER_H 51 | -------------------------------------------------------------------------------- /doc/OpenConverter_Deploy.md: -------------------------------------------------------------------------------- 1 | # Deploy the OpenConverter for Windows Users 2 | 3 | ## 1. Run in Release Mode 4 | - Click the computer icon on the left side of Qt. 5 | - Select "Release." 6 | - Click "Run" to build the project. 7 | 8 | ## 2. Locate the Release Folder 9 | - After the build completes, navigate to the "build" folder. 10 | - Find the "Release" folder containing the packaged files. 11 | 12 | ## 3. Open Qt MinGW Terminal 13 | 14 | - Go to the Start menu. 15 | - Search for "min." 16 | - Select "Qt MinGW" to open the terminal. 17 | 18 | ## 4. Deploy Dependencies with windeployqt 19 | - Copy the path to the folder containing the newly generated .exe file. 20 | - In the terminal: 21 | - Type `windeployqt`. 22 | - Paste the path (Ctrl+V). 23 | - Append a backslash (`\`) followed by the .exe file name. 24 | - Press Enter to execute. 25 | 26 | ## 5. Download Enigma Protector 27 | - Use a new software tool for advanced packaging. 28 | - Download it from: [https://enigmaprotector.com/en/downloads.html](https://enigmaprotector.com/en/downloads.html). 29 | 30 | ## 6. Package the .exe with Enigma Protector 31 | - Use the "Release" folder from the previous step as the base. 32 | - Load the generated .exe file into the software. 33 | - The software will automatically create a new file named `originalname_boxed.exe`. 34 | 35 | ## 7. Configure and Compress 36 | - In the bottom left corner, click "Add." 37 | - Select "Select Folder (Recursive)." 38 | - Under "File Options": 39 | - Check the "Compress Files" box. 40 | - Proceed with the packaging process. 41 | 42 | ## 8. Create Final Archive 43 | 44 | - Combine the packaged .exe file with the DLL files from FFmpeg’s "bin" directory. 45 | - Create a single compressed archive (e.g., ZIP file). 46 | -------------------------------------------------------------------------------- /src/transcoder/include/transcoder_fftool.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * This file is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | */ 15 | 16 | #ifndef TRANSCODERFFTOOL_H 17 | #define TRANSCODERFFTOOL_H 18 | 19 | #include "transcoder.h" 20 | 21 | class TranscoderFFTool : public Transcoder { 22 | public: 23 | TranscoderFFTool(ProcessParameter *process_parameter, 24 | EncodeParameter *encode_parameter); 25 | ~TranscoderFFTool(); 26 | 27 | bool prepared_opt(); 28 | 29 | bool transcode(std::string input_path, std::string output_path); 30 | 31 | private: 32 | // encoder's parameters 33 | bool copy_video; 34 | bool copy_audio; 35 | 36 | std::string video_codec; 37 | int64_t video_bit_rate; 38 | std::string audio_codec; 39 | int64_t audio_bit_rate; 40 | 41 | // Additional video parameters 42 | uint16_t width; 43 | uint16_t height; 44 | int qscale; 45 | std::string pixel_format; 46 | 47 | // Time range parameters 48 | double start_time; // in seconds 49 | double end_time; // in seconds 50 | 51 | static int frame_number; 52 | 53 | int64_t frame_total_number; 54 | }; 55 | 56 | #endif // TRANSCODERFFTOOL_H 57 | -------------------------------------------------------------------------------- /doc/OpenConverter_Usage.md: -------------------------------------------------------------------------------- 1 | # OpenConverter Usage Guide for macOS Users 2 | 3 | Thank you for downloading **OpenConverter**! Please follow the steps below to resolve any issues if the app does not open after transferring it out of the DMG file. 4 | 5 | --- 6 | 7 | ## Steps to Fix Opening Issues 8 | 9 | 1. **Download and Transfer the App:** 10 | - Download the OpenConverter DMG file. 11 | - Open the DMG and drag the **OpenConverter.app** to your preferred folder, such as the Applications folder. 12 | 13 | 2. **Resolve Gatekeeper Restrictions:** 14 | If macOS prevents the app from opening due to an unidentified developer error, you need to bypass Apple's Gatekeeper restrictions. 15 | 16 | 3. **Open Terminal:** 17 | - Open the **Terminal** app from your macOS utilities. 18 | 19 | 4. **Run the Following Commands:** 20 | 21 | - Remove the quarantine attribute: 22 | ```bash 23 | sudo xattr -r -d com.apple.quarantine OpenConverter.app 24 | ``` 25 | This command removes the quarantine flag set by macOS for apps downloaded from the internet. 26 | 27 | - Re-sign the app: 28 | ```bash 29 | codesign --force --deep --sign - OpenConverter.app 30 | ``` 31 | This command ensures that macOS recognizes the app as valid, even without a registered developer signature. 32 | 33 | 5. **Open the App:** 34 | - Double-click **OpenConverter.app** to launch it. 35 | - If macOS prompts for confirmation, click **Open**. 36 | 37 | --- 38 | 39 | ## Note 40 | - These commands require administrative privileges. You will be asked to enter your macOS password when running `sudo`. 41 | - If you experience any further issues, feel free to contact us through the project's repository or support channel. 42 | 43 | Enjoy using **OpenConverter**! 🎉 44 | -------------------------------------------------------------------------------- /tool/format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | TOP_DIR=$PWD 3 | 4 | # Define the directories to process 5 | DIRECTORIES=("$TOP_DIR/src") 6 | 7 | # Check if a .clang-format file exists in the current directory. If not, create one. 8 | if [ ! -f "$TOP_DIR/.clang-format" ]; then 9 | echo "No .clang-format file found in $TOP_DIR. Creating a temporary one..." 10 | echo "BasedOnStyle: LLVM" > "$TOP_DIR/.clang-format" 11 | echo "IndentWidth: 4" >> "$TOP_DIR/.clang-format" 12 | echo "AccessModifierOffset: -4" >> "$TOP_DIR/.clang-format" 13 | echo "IndentPPDirectives: BeforeHash" >> "$TOP_DIR/.clang-format" 14 | echo "IndentAccessModifiers: false" >> "$TOP_DIR/.clang-format" 15 | fi 16 | 17 | # Iterate through the specified directories and format the files 18 | for dir in "${DIRECTORIES[@]}"; do 19 | echo "Formatting directory: $dir" 20 | FORMATTED_FILES=() 21 | 22 | # Find all .cpp, .c, .h files in the directory 23 | FILES=$(find "$dir" -type f \( -name "*.cpp" -o -name "*.c" -o -name "*.h" \) -print0) 24 | 25 | # Check if any files were found 26 | if [ -z "$FILES" ]; then 27 | echo "No files found with supported extensions in $dir." 28 | continue 29 | fi 30 | 31 | # Use a while loop to process each file (using null characters as separators) 32 | while IFS= read -r -d '' file; do 33 | clang-format -style=file -i "$file" 34 | FORMATTED_FILES+=("$file") 35 | done < <(find "$dir" -type f \( -name "*.cpp" -o -name "*.c" -o -name "*.h" \) -print0) 36 | 37 | # Output the list of formatted files 38 | if [ ${#FORMATTED_FILES[@]} -gt 0 ]; then 39 | printf "\nFormatted files in %s:\n" "$dir" 40 | printf "%s\n" "${FORMATTED_FILES[@]}" 41 | fi 42 | done 43 | 44 | echo "Formatting completed." 45 | -------------------------------------------------------------------------------- /src/builder/include/shared_data.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef SHARED_DATA_H 19 | #define SHARED_DATA_H 20 | 21 | #include 22 | 23 | class SharedData { 24 | public: 25 | SharedData(); 26 | ~SharedData(); 27 | 28 | // Input file path 29 | QString GetInputFilePath() const; 30 | void SetInputFilePath(const QString &filePath); 31 | 32 | // Output file path 33 | QString GetOutputFilePath() const; 34 | void SetOutputFilePath(const QString &filePath); 35 | 36 | // Generate output path from input path and extension 37 | // If user has manually set output path, returns that 38 | // Otherwise, generates: /path/to/input-oc-output.ext 39 | QString GenerateOutputPath(const QString &extension); 40 | 41 | // Check if output path was manually set by user 42 | bool IsOutputPathManuallySet() const; 43 | 44 | // Reset output path to auto-generate mode 45 | void ResetOutputPath(); 46 | 47 | // Clear all data 48 | void Clear(); 49 | 50 | private: 51 | QString inputFilePath; 52 | QString outputFilePath; 53 | bool outputPathManuallySet; 54 | }; 55 | 56 | #endif // SHARED_DATA_H 57 | -------------------------------------------------------------------------------- /src/component/src/progress_widget.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "progress_widget.h" 19 | 20 | ProgressWidget::ProgressWidget(QWidget *parent) : QWidget(parent) { 21 | // Create layout 22 | layout = new QVBoxLayout(this); 23 | layout->setContentsMargins(0, 0, 0, 0); 24 | layout->setSpacing(5); 25 | 26 | // Create progress bar 27 | progressBar = new QProgressBar(this); 28 | progressBar->setRange(0, 100); 29 | progressBar->setValue(0); 30 | progressBar->setVisible(false); 31 | 32 | // Create progress label 33 | progressLabel = new QLabel("", this); 34 | progressLabel->setVisible(false); 35 | 36 | // Add to layout 37 | layout->addWidget(progressBar); 38 | layout->addWidget(progressLabel); 39 | 40 | setLayout(layout); 41 | } 42 | 43 | void ProgressWidget::Show() { 44 | progressBar->setVisible(true); 45 | progressLabel->setVisible(true); 46 | } 47 | 48 | void ProgressWidget::Hide() { 49 | progressBar->setVisible(false); 50 | progressLabel->setVisible(false); 51 | } 52 | 53 | void ProgressWidget::Reset() { 54 | progressBar->setValue(0); 55 | progressLabel->setText(""); 56 | } 57 | -------------------------------------------------------------------------------- /src/builder/include/transcoder_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef TRANSCODER_HELPER_H 19 | #define TRANSCODER_HELPER_H 20 | 21 | #include 22 | 23 | class QWidget; 24 | 25 | /** 26 | * @brief Helper class to get current transcoder name from main window 27 | * 28 | * This class provides a centralized way to retrieve the currently selected 29 | * transcoder name from the OpenConverter main window. It eliminates code 30 | * duplication across pages and batch processing. 31 | * 32 | * Usage: 33 | * // From a page (QWidget) 34 | * QString transcoder = TranscoderHelper::GetCurrentTranscoderName(this); 35 | * 36 | * // From any QObject with parent widget 37 | * QString transcoder = TranscoderHelper::GetCurrentTranscoderName(parentWidget); 38 | */ 39 | class TranscoderHelper { 40 | public: 41 | /** 42 | * @brief Get current transcoder name from main window 43 | * @param widget Any widget in the application (page, dialog, etc.) 44 | * @return Current transcoder name (e.g., "FFMPEG", "BMF", "FFTOOL") 45 | * Returns "FFMPEG" as default if main window not found 46 | */ 47 | static QString GetCurrentTranscoderName(QWidget *widget); 48 | }; 49 | 50 | #endif // TRANSCODER_HELPER_H 51 | -------------------------------------------------------------------------------- /src/builder/include/base_page.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef BASE_PAGE_H 19 | #define BASE_PAGE_H 20 | 21 | #include 22 | 23 | class QLineEdit; 24 | class SharedData; 25 | 26 | class BasePage : public QWidget { 27 | Q_OBJECT 28 | 29 | public: 30 | explicit BasePage(QWidget *parent = nullptr); 31 | virtual ~BasePage(); 32 | 33 | // Virtual method to be called when page becomes active 34 | virtual void OnPageActivated(); 35 | 36 | // Virtual method to be called when page becomes inactive 37 | virtual void OnPageDeactivated(); 38 | 39 | // Get page title 40 | virtual QString GetPageTitle() const = 0; 41 | 42 | // Virtual method to retranslate UI when language changes 43 | virtual void RetranslateUi(); 44 | 45 | protected: 46 | // Helper method to handle shared data updates 47 | // Derived classes should call this in OnPageActivated() 48 | void HandleSharedDataUpdate(QLineEdit *inputFileLineEdit, 49 | QLineEdit *outputFileLineEdit, 50 | const QString &defaultFormat = QString()); 51 | 52 | // Hook method called when input file changes (override in derived classes if needed) 53 | virtual void OnInputFileChanged(const QString &newPath); 54 | 55 | // Hook method called when output path needs updating (override in derived classes if needed) 56 | virtual void OnOutputPathUpdate(); 57 | }; 58 | 59 | #endif // BASE_PAGE_H 60 | -------------------------------------------------------------------------------- /doc/CompileGuide.md: -------------------------------------------------------------------------------- 1 | # OC Project Compilation Guide for Windows 2 | 3 | ## 1. Install Qt 5 or Qt 6 4 | 5 | ### 1.1 Download 6 | 7 | - [Official Website Download](https://download.qt.io/official_releases/online_installers/) 8 | 9 | Domestic mirrors: 10 | 11 | - [University of Science and Technology of China](http://mirrors.ustc.edu.cn/qtproject/) 12 | - [Tsinghua University](https://mirrors.tuna.tsinghua.edu.cn/qt/) 13 | 14 | ### 1.2 Installation 15 | 16 | #### 1.2.1 Qt Account Login 17 | 18 | Before starting the installation, you need to have a Qt account. If you don't have one, click the registration button on the page. (**This guide only covers the important steps for installing Qt.**) 19 | 20 | #### 1.2.2 Component Selection 21 | 22 | On the installation page, select the **Qt for Desktop Development** component and click **Next** to begin the installation. 23 | 24 | ## 2. Install and Configure FFmpeg 25 | 26 | Supported versions are 5.x to 7.x. The following example uses version 5.1.6. 27 | 28 | ### 2.1 Download FFmpeg 5.1.6 29 | 30 | [Download Link](https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2025-02-02-12-58/ffmpeg-n5.1.6-16-g6e63e49496-win64-lgpl-shared-5.1.zip) 31 | 32 | ### 2.2 Configure FFmpeg Environment 33 | 34 | Extract the downloaded ZIP file to a suitable location. Then, add the FFmpeg directory to your system’s environment variables. 35 | 36 | ## 3. Compile the OC Project 37 | 38 | ### 3.1 Project Configuration 39 | 40 | Open the OC project in Qt and configure it. Check the Qt compile suite and click **Configure Project**. 41 | 42 | ### 3.2 Configure FFmpeg Environment in CMake 43 | 44 | In the `CMakeLists.txt` file, find the **FFMPEG_ROOT_PATH** variable and set the FFmpeg environment path. 45 | 46 | ### 3.3 Disable BMF Compilation 47 | 48 | In the `CMakeLists.txt` file, locate the line: 49 | 50 | ```cmake 51 | option(BMF_TRANSCODER "Enable BMF Transcoder" ON) 52 | ``` 53 | 54 | Change **ON** to **OFF**. If you wish to compile BMF, refer to the official [BMF installation guide for Windows](https://babitmf.github.io/docs/bmf/getting_started_yourself/install/#windows). 55 | 56 | ### 3.4 Compile the OC Project 57 | 58 | Once everything is set up, click the **Run** button in Qt to start the compilation process. 59 | -------------------------------------------------------------------------------- /src/common/include/info.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * This file is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | */ 15 | 16 | #ifndef INFO_H 17 | #define INFO_H 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | extern "C" { 24 | #include 25 | #include 26 | #include 27 | #include 28 | }; 29 | 30 | // store some info of video and audio 31 | typedef struct QuickInfo { 32 | // video 33 | int videoIdx; 34 | int width; 35 | int height; 36 | 37 | std::string colorSpace; 38 | std::string videoCodec; 39 | 40 | int64_t videoBitRate; 41 | double frameRate; 42 | // audio 43 | int audioIdx; 44 | std::string audioCodec; 45 | int64_t audioBitRate; 46 | int channels; 47 | std::string sampleFmt; 48 | int sampleRate; 49 | 50 | // subtitle 51 | int subIdx; 52 | std::string subCodec; 53 | std::string subFmt; 54 | std::string displayTime; 55 | int position; 56 | // QSize subSize; 57 | std::string subColor; 58 | } QuickInfo; 59 | 60 | // deal with info of video and audio and stored as QuickInfo type 61 | class Info { 62 | 63 | public: 64 | Info(); 65 | ~Info(); 66 | 67 | private: 68 | void print_error(const char *msg, int ret); 69 | 70 | AVFormatContext *avCtx; 71 | 72 | const AVCodec *audioCodec; 73 | 74 | AVCodecContext *audioCtx; 75 | 76 | QuickInfo *quickInfo; 77 | 78 | char errorMsg[128]; 79 | public: 80 | // init quick info 81 | void init(); 82 | // get qucik info reference 83 | QuickInfo *get_quick_info(); 84 | // send the info to front-end 85 | void send_info(char *src); 86 | }; 87 | 88 | #endif // INFO_H 89 | -------------------------------------------------------------------------------- /tool/dmg_install_instructions.txt: -------------------------------------------------------------------------------- 1 | ═══════════════════════════════════════════════════════════ 2 | 3 | 📦 HOW TO INSTALL OPENCONVERTER 4 | 5 | ═══════════════════════════════════════════════════════════ 6 | 7 | STEP 1: Install the Application 8 | ──────────────────────────────────────────────────────────── 9 | 10 | Drag "OpenConverter.app" to the "Applications" folder 11 | 12 | OpenConverter.app ──────────▶ Applications 13 | 14 | 15 | STEP 2: Fix Gatekeeper Restrictions (if needed) 16 | ──────────────────────────────────────────────────────────── 17 | 18 | If macOS prevents the app from opening with an "unidentified 19 | developer" error, follow these steps: 20 | 21 | Method 1: Simple Terminal Command (Recommended) ⭐ 22 | ──────────────────────────────────────────────────────────── 23 | 24 | 1. Open Terminal: 25 | • Press Cmd+Space, type "Terminal", press Enter 26 | OR 27 | • Go to Applications → Utilities → Terminal 28 | 29 | 2. Copy and paste this command (all on one line): 30 | 31 | sudo xattr -r -d com.apple.quarantine /Applications/OpenConverter.app 32 | 33 | 3. Press Enter 34 | 35 | 4. Enter your password when prompted 36 | (You won't see it as you type - this is normal) 37 | 38 | 5. Done! You can now open OpenConverter normally 39 | 40 | 41 | Method 2: Use Bundled Script (Alternative) 42 | ──────────────────────────────────────────────────────────── 43 | 44 | 1. Open Terminal (see Method 1 for how to open Terminal) 45 | 46 | 2. Copy and paste this command: 47 | 48 | /Applications/OpenConverter.app/Contents/Resources/fix_gatekeeper.sh 49 | 50 | 3. Press Enter and follow the prompts 51 | 52 | 4. Done! You can now open OpenConverter 53 | 54 | 55 | WHAT DOES THIS DO? 56 | ──────────────────────────────────────────────────────────── 57 | 58 | This removes the "quarantine" flag that macOS sets on apps 59 | downloaded from the internet. It's safe and only affects 60 | this specific app. 61 | 62 | 63 | NEED HELP? 64 | ──────────────────────────────────────────────────────────── 65 | 66 | Visit: https://github.com/TSGU-OSC/OpenConverter 67 | Email: jacklau1222gm@gmail.com 68 | 69 | 70 | ═══════════════════════════════════════════════════════════ 71 | 72 | Enjoy using OpenConverter! 🎉 73 | 74 | ═══════════════════════════════════════════════════════════ 75 | -------------------------------------------------------------------------------- /src/component/include/quality_widget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef QUALITY_WIDGET_H 19 | #define QUALITY_WIDGET_H 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | /** 26 | * @brief Reusable widget for quality/qscale selection 27 | * 28 | * Features: 29 | * - SpinBox for quality value (qscale) 30 | * - Configurable range (default: 2-31 for images, 18-28 for video) 31 | * - Lower value = better quality 32 | * - Optional label with description 33 | * 34 | * Usage: 35 | * QualityWidget *quality = new QualityWidget(QualityWidget::Image, this); 36 | * quality->SetQuality(5); 37 | * int qscale = quality->GetQuality(); 38 | */ 39 | class QualityWidget : public QWidget { 40 | Q_OBJECT 41 | 42 | public: 43 | enum QualityType { 44 | Image, // Image quality (2-31, default: 5) 45 | Video // Video quality (18-28, default: 23) 46 | }; 47 | 48 | explicit QualityWidget(QualityType type = Image, QWidget *parent = nullptr); 49 | explicit QualityWidget(const QString &labelText, QualityType type = Image, QWidget *parent = nullptr); 50 | 51 | // Getters 52 | int GetQuality() const; 53 | 54 | // Setters 55 | void SetQuality(int quality); 56 | void SetRange(int min, int max); 57 | 58 | // Enable/disable 59 | void SetEnabled(bool enabled); 60 | 61 | // Translation support 62 | void RetranslateUi(); 63 | 64 | signals: 65 | void QualityChanged(int quality); 66 | 67 | private slots: 68 | void OnQualityChanged(int value); 69 | 70 | private: 71 | void Initialize(QualityType type); 72 | 73 | QLabel *label; 74 | QSpinBox *qualitySpinBox; 75 | QualityType qualityType; 76 | }; 77 | 78 | #endif // QUALITY_WIDGET_H 79 | -------------------------------------------------------------------------------- /src/common/include/encode_parameter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef ENCODEPARAMETER_H 19 | #define ENCODEPARAMETER_H 20 | 21 | #include 22 | #include 23 | 24 | class EncodeParameter { 25 | private: 26 | bool available; 27 | 28 | std::string videoCodec; 29 | int64_t videoBitRate; 30 | 31 | std::string pixelFormat; 32 | uint16_t width; 33 | uint16_t height; 34 | 35 | std::string audioCodec; 36 | int64_t audioBitRate; 37 | 38 | int qscale; 39 | 40 | std::string preset; 41 | 42 | double startTime; // in seconds 43 | double endTime; // in seconds 44 | 45 | public: 46 | EncodeParameter(); 47 | ~EncodeParameter(); 48 | 49 | bool get_available(); 50 | 51 | void set_video_codec_name(std::string vc); 52 | 53 | void set_qscale(int q); 54 | 55 | void set_pixel_format(std::string p); 56 | 57 | void set_width(uint16_t w); 58 | 59 | void set_height(uint16_t h); 60 | 61 | void set_audio_codec_name(std::string ac); 62 | 63 | void set_video_bit_rate(int64_t vbr); 64 | 65 | void set_audio_bit_rate(int64_t abr); 66 | 67 | void set_preset(std::string p); 68 | 69 | void set_start_time(double t); 70 | 71 | void set_end_time(double t); 72 | 73 | std::string get_video_codec_name(); 74 | 75 | int get_qscale(); 76 | 77 | std::string get_pixel_format(); 78 | 79 | uint16_t get_width(); 80 | 81 | uint16_t get_height(); 82 | 83 | std::string get_audio_codec_name(); 84 | 85 | int64_t get_video_bit_rate(); 86 | 87 | int64_t get_audio_bit_rate(); 88 | 89 | std::string get_preset(); 90 | 91 | double get_start_time(); 92 | 93 | double get_end_time(); 94 | }; 95 | 96 | #endif // ENCODEPARAMETER_H 97 | -------------------------------------------------------------------------------- /src/component/include/bitrate_widget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef BITRATE_WIDGET_H 19 | #define BITRATE_WIDGET_H 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | /** 26 | * @brief Reusable widget for bitrate selection 27 | * 28 | * Features: 29 | * - SpinBox with "auto" (0) support 30 | * - Configurable range (default: 0-50000 for video, 0-320 for audio) 31 | * - Unit suffix: " kbps" 32 | * - Optional label: "Bitrate:" 33 | * 34 | * Usage: 35 | * BitrateWidget *bitrate = new BitrateWidget(BitrateWidget::Video, this); 36 | * bitrate->SetBitrate(5000); // 5000 kbps 37 | * int kbps = bitrate->GetBitrate(); // Returns 0 for "auto" 38 | */ 39 | class BitrateWidget : public QWidget { 40 | Q_OBJECT 41 | 42 | public: 43 | enum BitrateType { 44 | Video, // Range: 0-50000 kbps 45 | Audio // Range: 0-320 kbps 46 | }; 47 | 48 | explicit BitrateWidget(BitrateType type = Video, QWidget *parent = nullptr); 49 | explicit BitrateWidget(const QString &labelText, BitrateType type = Video, QWidget *parent = nullptr); 50 | 51 | // Getters 52 | int GetBitrate() const; // Returns bitrate in kbps, 0 for "auto" 53 | bool IsAuto() const; 54 | 55 | // Setters 56 | void SetBitrate(int kbps); // Set bitrate in kbps, 0 for "auto" 57 | void SetToAuto(); 58 | void SetRange(int min, int max); 59 | 60 | // Enable/disable 61 | void SetEnabled(bool enabled); 62 | 63 | // Translation support 64 | void RetranslateUi(); 65 | 66 | signals: 67 | void BitrateChanged(int kbps); 68 | 69 | private slots: 70 | void OnValueChanged(int value); 71 | 72 | private: 73 | void SetupUI(const QString &labelText, BitrateType type); 74 | 75 | QLabel *label; 76 | QSpinBox *bitrateSpinBox; 77 | }; 78 | 79 | #endif // BITRATE_WIDGET_H 80 | -------------------------------------------------------------------------------- /src/component/include/pixel_format_widget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef PIXEL_FORMAT_WIDGET_H 19 | #define PIXEL_FORMAT_WIDGET_H 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | /** 26 | * @brief Reusable widget for pixel format selection 27 | * 28 | * Features: 29 | * - ComboBox with common pixel formats 30 | * - "auto" option (default) 31 | * - Separate presets for video and image formats 32 | * - Optional label: "Pixel Format:" 33 | * 34 | * Usage: 35 | * PixelFormatWidget *pixFmt = new PixelFormatWidget(PixelFormatWidget::Video, this); 36 | * pixFmt->SetPixelFormat("yuv420p"); 37 | * QString format = pixFmt->GetPixelFormat(); // Returns "" for "auto" 38 | */ 39 | class PixelFormatWidget : public QWidget { 40 | Q_OBJECT 41 | 42 | public: 43 | enum FormatType { 44 | Video, // Common video formats (yuv420p, yuv422p, yuv444p, rgb24, bgr24) 45 | Image // Image formats (rgb24, rgba, yuv420p, yuv422p, yuv444p, yuvj420p, gray, etc.) 46 | }; 47 | 48 | explicit PixelFormatWidget(FormatType type = Video, QWidget *parent = nullptr); 49 | explicit PixelFormatWidget(const QString &labelText, FormatType type = Video, QWidget *parent = nullptr); 50 | 51 | // Getters 52 | QString GetPixelFormat() const; // Returns "" for "auto" 53 | bool IsAuto() const; 54 | 55 | // Setters 56 | void SetPixelFormat(const QString &format); 57 | void SetToAuto(); 58 | 59 | // Enable/disable 60 | void SetEnabled(bool enabled); 61 | 62 | // Translation support 63 | void RetranslateUi(); 64 | 65 | signals: 66 | void PixelFormatChanged(const QString &format); 67 | 68 | private slots: 69 | void OnFormatChanged(int index); 70 | 71 | private: 72 | void SetupUI(const QString &labelText, FormatType type); 73 | void PopulateFormats(FormatType type); 74 | 75 | QLabel *label; 76 | QComboBox *formatComboBox; 77 | }; 78 | 79 | #endif // PIXEL_FORMAT_WIDGET_H 80 | -------------------------------------------------------------------------------- /src/component/include/simple_video_player.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * This file is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | */ 15 | 16 | #ifndef SIMPLE_VIDEO_PLAYER_H 17 | #define SIMPLE_VIDEO_PLAYER_H 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | extern "C" { 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | } 33 | 34 | // Simple FFmpeg-based video player widge 35 | class SimpleVideoPlayer : public QLabel { 36 | Q_OBJECT 37 | 38 | public: 39 | explicit SimpleVideoPlayer(QWidget *parent = nullptr); 40 | ~SimpleVideoPlayer() override; 41 | 42 | // Load video file 43 | bool LoadVideo(const QString &filePath); 44 | 45 | // Playback control 46 | void Play(); 47 | void Pause(); 48 | void Stop(); 49 | bool IsPlaying() const { return isPlaying; } 50 | 51 | // Seeking 52 | void Seek(qint64 positionMs); 53 | qint64 GetPosition() const { return currentPositionMs; } 54 | qint64 GetDuration() const { return durationMs; } 55 | 56 | signals: 57 | void PositionChanged(qint64 position); 58 | void DurationChanged(qint64 duration); 59 | 60 | private slots: 61 | void OnPlaybackTimer(); 62 | 63 | private: 64 | void CloseVideo(); 65 | bool DecodeNextFrame(); 66 | void DisplayFrame(AVFrame *frame); 67 | QImage ConvertFrameToQImage(AVFrame *frame); 68 | 69 | // FFmpeg components 70 | AVFormatContext *formatCtx; 71 | AVCodecContext *codecCtx; 72 | AVFrame *frame; 73 | AVFrame *frameRGB; 74 | AVPacket *packet; 75 | SwsContext *swsCtx; 76 | uint8_t *buffer; 77 | 78 | int videoStreamIndex; 79 | qint64 durationMs; 80 | qint64 currentPositionMs; 81 | double timeBase; 82 | 83 | // Playback state 84 | bool isPlaying; 85 | bool isLoaded; 86 | QTimer *playbackTimer; 87 | 88 | // Thread safety 89 | QMutex mutex; 90 | }; 91 | 92 | #endif // SIMPLE_VIDEO_PLAYER_H 93 | -------------------------------------------------------------------------------- /tool/fix_gatekeeper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # OpenConverter Gatekeeper Fix Script 4 | # This script removes the quarantine attribute from OpenConverter.app 5 | # to bypass macOS Gatekeeper restrictions 6 | 7 | set -e 8 | 9 | # Colors 10 | GREEN='\033[0;32m' 11 | YELLOW='\033[1;33m' 12 | RED='\033[0;31m' 13 | BLUE='\033[0;34m' 14 | NC='\033[0m' # No Color 15 | 16 | echo "" 17 | echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}" 18 | echo -e "${BLUE} OpenConverter - Gatekeeper Fix Script${NC}" 19 | echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}" 20 | echo "" 21 | 22 | # Determine the app path 23 | if [ -f "$0" ]; then 24 | # Script is inside the app bundle 25 | SCRIPT_PATH="$(cd "$(dirname "$0")" && pwd)" 26 | APP_PATH="$(dirname "$(dirname "$(dirname "$SCRIPT_PATH")")")" 27 | else 28 | # Fallback to /Applications 29 | APP_PATH="/Applications/OpenConverter.app" 30 | fi 31 | 32 | echo -e "${YELLOW}App location: ${APP_PATH}${NC}" 33 | echo "" 34 | 35 | # Check if app exists 36 | if [ ! -d "$APP_PATH" ]; then 37 | echo -e "${RED}Error: OpenConverter.app not found at ${APP_PATH}${NC}" 38 | echo -e "${YELLOW}Please make sure OpenConverter.app is installed in /Applications${NC}" 39 | exit 1 40 | fi 41 | 42 | # Check if quarantine attribute exists 43 | if xattr "$APP_PATH" 2>/dev/null | grep -q "com.apple.quarantine"; then 44 | echo -e "${YELLOW}Quarantine attribute detected. Removing...${NC}" 45 | echo "" 46 | echo -e "${YELLOW}This will require administrator privileges.${NC}" 47 | echo -e "${YELLOW}Please enter your password when prompted.${NC}" 48 | echo "" 49 | 50 | # Remove quarantine attribute 51 | if sudo xattr -r -d com.apple.quarantine "$APP_PATH"; then 52 | echo "" 53 | echo -e "${GREEN}✓ Success! Quarantine attribute removed.${NC}" 54 | echo -e "${GREEN}✓ You can now open OpenConverter normally.${NC}" 55 | echo "" 56 | else 57 | echo "" 58 | echo -e "${RED}✗ Failed to remove quarantine attribute.${NC}" 59 | echo -e "${YELLOW}Please try running this command manually:${NC}" 60 | echo -e " sudo xattr -r -d com.apple.quarantine \"$APP_PATH\"" 61 | echo "" 62 | exit 1 63 | fi 64 | else 65 | echo -e "${GREEN}✓ No quarantine attribute found.${NC}" 66 | echo -e "${GREEN}✓ OpenConverter should open normally.${NC}" 67 | echo "" 68 | fi 69 | 70 | echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}" 71 | echo -e "${BLUE} Done! Enjoy using OpenConverter! 🎉${NC}" 72 | echo -e "${BLUE}═══════════════════════════════════════════════════════════${NC}" 73 | echo "" 74 | -------------------------------------------------------------------------------- /src/builder/src/shared_data.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "../include/shared_data.h" 19 | #include 20 | 21 | SharedData::SharedData() { 22 | inputFilePath = ""; 23 | outputFilePath = ""; 24 | outputPathManuallySet = false; 25 | } 26 | 27 | SharedData::~SharedData() { 28 | } 29 | 30 | QString SharedData::GetInputFilePath() const { 31 | return inputFilePath; 32 | } 33 | 34 | void SharedData::SetInputFilePath(const QString &filePath) { 35 | inputFilePath = filePath; 36 | // When input path changes, reset output path to auto-generate mode 37 | if (!outputPathManuallySet) { 38 | outputFilePath = ""; 39 | } 40 | } 41 | 42 | QString SharedData::GetOutputFilePath() const { 43 | return outputFilePath; 44 | } 45 | 46 | void SharedData::SetOutputFilePath(const QString &filePath) { 47 | outputFilePath = filePath; 48 | outputPathManuallySet = true; 49 | } 50 | 51 | QString SharedData::GenerateOutputPath(const QString &extension) { 52 | // If user manually set output path, return it 53 | if (outputPathManuallySet && !outputFilePath.isEmpty()) { 54 | return outputFilePath; 55 | } 56 | 57 | // If no input file, return empty 58 | if (inputFilePath.isEmpty()) { 59 | return ""; 60 | } 61 | 62 | // Generate output path: /path/to/input-oc-output.ext 63 | QFileInfo fileInfo(inputFilePath); 64 | QString baseName = fileInfo.completeBaseName(); 65 | QString dir = fileInfo.absolutePath(); 66 | 67 | QString generatedPath = QString("%1/%2-oc-output.%3").arg(dir, baseName, extension); 68 | 69 | // Cache the generated path 70 | outputFilePath = generatedPath; 71 | 72 | return generatedPath; 73 | } 74 | 75 | bool SharedData::IsOutputPathManuallySet() const { 76 | return outputPathManuallySet; 77 | } 78 | 79 | void SharedData::ResetOutputPath() { 80 | outputFilePath = ""; 81 | outputPathManuallySet = false; 82 | } 83 | 84 | void SharedData::Clear() { 85 | inputFilePath = ""; 86 | outputFilePath = ""; 87 | outputPathManuallySet = false; 88 | } 89 | -------------------------------------------------------------------------------- /src/builder/include/create_gif_page.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef CREATE_GIF_PAGE_H 19 | #define CREATE_GIF_PAGE_H 20 | 21 | #include "base_page.h" 22 | #include "file_selector_widget.h" 23 | #include "batch_output_widget.h" 24 | #include "batch_mode_helper.h" 25 | #include "resolution_widget.h" 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | class Converter; 34 | class EncodeParameter; 35 | class ProcessParameter; 36 | 37 | class CreateGifPage : public BasePage { 38 | Q_OBJECT 39 | 40 | public: 41 | explicit CreateGifPage(QWidget *parent = nullptr); 42 | ~CreateGifPage(); 43 | 44 | QString GetPageTitle() const override; 45 | void OnPageActivated() override; 46 | void RetranslateUi() override; 47 | 48 | protected: 49 | void OnOutputPathUpdate() override; 50 | 51 | private slots: 52 | void OnInputFileSelected(const QString &filePath); 53 | void OnOutputFileSelected(const QString &filePath); 54 | void OnConvertClicked(); 55 | void OnFpsChanged(int value); 56 | 57 | private: 58 | void SetupUI(); 59 | void UpdateOutputPath(); 60 | EncodeParameter* CreateEncodeParameter(); 61 | 62 | // UI Components - Input/Output Section 63 | QVBoxLayout *mainLayout; 64 | FileSelectorWidget *inputFileSelector; 65 | FileSelectorWidget *outputFileSelector; 66 | BatchOutputWidget *batchOutputWidget; 67 | 68 | // UI Components - Settings Section 69 | QGroupBox *settingsGroupBox; 70 | QLabel *resolutionLabel; 71 | ResolutionWidget *resolutionWidget; 72 | QLabel *fpsLabel; 73 | QSpinBox *fpsSpinBox; 74 | QLabel *qualityLabel; 75 | QSpinBox *qualitySpinBox; 76 | 77 | // UI Components - Action Section 78 | QPushButton *convertButton; 79 | 80 | // Backend 81 | EncodeParameter *encodeParameter; 82 | ProcessParameter *processParameter; 83 | Converter *converter; 84 | 85 | // Batch mode helper 86 | BatchModeHelper *batchModeHelper; 87 | }; 88 | 89 | #endif // CREATE_GIF_PAGE_H 90 | -------------------------------------------------------------------------------- /src/component/src/quality_widget.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "quality_widget.h" 19 | #include 20 | 21 | QualityWidget::QualityWidget(QualityType type, QWidget *parent) 22 | : QWidget(parent), qualityType(type), label(nullptr) { 23 | Initialize(type); 24 | } 25 | 26 | QualityWidget::QualityWidget(const QString &labelText, QualityType type, QWidget *parent) 27 | : QWidget(parent), qualityType(type) { 28 | Initialize(type); 29 | if (label) { 30 | label->setText(labelText); 31 | } 32 | } 33 | 34 | void QualityWidget::Initialize(QualityType type) { 35 | QHBoxLayout *layout = new QHBoxLayout(this); 36 | layout->setContentsMargins(0, 0, 0, 0); 37 | layout->setSpacing(5); 38 | 39 | // Create spin box 40 | qualitySpinBox = new QSpinBox(this); 41 | 42 | // Set range and default based on type 43 | if (type == Image) { 44 | qualitySpinBox->setRange(2, 31); 45 | qualitySpinBox->setValue(5); 46 | } else { // Video 47 | qualitySpinBox->setRange(18, 28); 48 | qualitySpinBox->setValue(23); 49 | } 50 | 51 | connect(qualitySpinBox, QOverload::of(&QSpinBox::valueChanged), 52 | this, &QualityWidget::OnQualityChanged); 53 | 54 | layout->addWidget(qualitySpinBox); 55 | layout->addStretch(); 56 | } 57 | 58 | int QualityWidget::GetQuality() const { 59 | return qualitySpinBox->value(); 60 | } 61 | 62 | void QualityWidget::SetQuality(int quality) { 63 | qualitySpinBox->setValue(quality); 64 | } 65 | 66 | void QualityWidget::SetRange(int min, int max) { 67 | qualitySpinBox->setRange(min, max); 68 | } 69 | 70 | void QualityWidget::SetEnabled(bool enabled) { 71 | qualitySpinBox->setEnabled(enabled); 72 | if (label) { 73 | label->setEnabled(enabled); 74 | } 75 | } 76 | 77 | void QualityWidget::RetranslateUi() { 78 | // Quality widget doesn't have translatable text in the widget itself 79 | // The label is typically set by the parent page 80 | } 81 | 82 | void QualityWidget::OnQualityChanged(int value) { 83 | emit QualityChanged(value); 84 | } 85 | -------------------------------------------------------------------------------- /src/component/include/codec_selector_widget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef CODEC_SELECTOR_WIDGET_H 19 | #define CODEC_SELECTOR_WIDGET_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | /** 27 | * @brief Reusable widget for codec selection with "auto" option 28 | * 29 | * Features: 30 | * - ComboBox with common codecs 31 | * - "auto" option (default) 32 | * - Separate presets for video and audio codecs 33 | * - Optional label: "Codec:" 34 | * - Support for "copy" codec (stream copy) 35 | * 36 | * Usage: 37 | * CodecSelectorWidget *codec = new CodecSelectorWidget(CodecSelectorWidget::Video, this); 38 | * codec->SetCodec("libx264"); 39 | * QString codecName = codec->GetCodec(); // Returns "" for "auto" 40 | */ 41 | class CodecSelectorWidget : public QWidget { 42 | Q_OBJECT 43 | 44 | public: 45 | enum CodecType { 46 | VideoCodec, // Video codecs (libx264, libx265, libvpx, libvpx-vp9, mpeg4, copy) 47 | AudioCodec // Audio codecs (aac, libmp3lame, libvorbis, libopus, copy) 48 | }; 49 | 50 | explicit CodecSelectorWidget(CodecType type = VideoCodec, QWidget *parent = nullptr); 51 | explicit CodecSelectorWidget(const QString &labelText, CodecType type = VideoCodec, QWidget *parent = nullptr); 52 | 53 | // Getters 54 | QString GetCodec() const; // Returns "" for "auto" 55 | bool IsAuto() const; 56 | bool IsCopy() const; 57 | 58 | // Setters 59 | void SetCodec(const QString &codec); 60 | void SetToAuto(); 61 | void SetCodecList(const QStringList &codecs); // Custom codec list 62 | 63 | // Enable/disable 64 | void SetEnabled(bool enabled); 65 | 66 | // Translation support 67 | void RetranslateUi(); 68 | 69 | signals: 70 | void CodecChanged(const QString &codec); 71 | 72 | private slots: 73 | void OnCodecChanged(int index); 74 | 75 | private: 76 | void Initialize(CodecType type); 77 | void PopulateCodecs(CodecType type); 78 | 79 | QLabel *label; 80 | QComboBox *codecComboBox; 81 | CodecType codecType; 82 | }; 83 | 84 | #endif // CODEC_SELECTOR_WIDGET_H 85 | -------------------------------------------------------------------------------- /src/common/src/process_parameter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "../include/process_parameter.h" 19 | #include 20 | 21 | ProcessParameter::ProcessParameter() : processNumber(0), timeRequired(0.0) {} 22 | 23 | ProcessParameter::~ProcessParameter() = default; 24 | 25 | void ProcessParameter::set_process_number(int64_t frameNumber, 26 | int64_t frameTotalNumnber) { 27 | if (frameTotalNumnber > 0) { 28 | double progress = 29 | static_cast(frameNumber) / frameTotalNumnber * 100.0; 30 | processNumber = frameNumber; 31 | notify_process_update(progress); 32 | } 33 | } 34 | 35 | void ProcessParameter::set_process_number(int64_t processNumber) { 36 | this->processNumber = processNumber; 37 | notify_process_update(static_cast(processNumber)); 38 | } 39 | 40 | double ProcessParameter::get_process_number() { 41 | return static_cast(processNumber); 42 | } 43 | 44 | void ProcessParameter::set_time_required(double timeRequired) { 45 | this->timeRequired = timeRequired; 46 | notify_time_update(timeRequired); 47 | } 48 | 49 | double ProcessParameter::get_time_required() { return timeRequired; } 50 | 51 | ProcessParameter ProcessParameter::get_process_parmeter() { return *this; } 52 | 53 | void ProcessParameter::add_observer(ProcessObserver* observer) { 54 | if (observer) { 55 | observers.push_back(observer); 56 | } 57 | } 58 | 59 | void ProcessParameter::remove_observer(ProcessObserver* observer) { 60 | auto it = std::find(observers.begin(), observers.end(), observer); 61 | if (it != observers.end()) { 62 | observers.erase(it); 63 | } 64 | } 65 | 66 | void ProcessParameter::notify_process_update(double progress) { 67 | for (auto observer : observers) { 68 | if (observer) { 69 | observer->on_process_update(progress); 70 | } 71 | } 72 | } 73 | 74 | void ProcessParameter::notify_time_update(double timeRequired) { 75 | for (auto observer : observers) { 76 | if (observer) { 77 | observer->on_time_update(timeRequired); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/builder/include/batch_file_dialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef BATCH_FILE_DIALOG_H 19 | #define BATCH_FILE_DIALOG_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "filter_tag_widget.h" 28 | 29 | /** 30 | * @brief Small dialog for batch file selection 31 | * 32 | * This dialog allows users to: 33 | * 1. Add multiple files 34 | * 2. Add files from a directory with filter 35 | * 3. Remove selected files 36 | * 4. Clear all files 37 | */ 38 | class BatchFileDialog : public QDialog { 39 | Q_OBJECT 40 | 41 | public: 42 | explicit BatchFileDialog(QWidget *parent = nullptr); 43 | ~BatchFileDialog() override = default; 44 | 45 | /** 46 | * @brief Set the file filter for file/directory selection 47 | * @param filter Comma-separated extensions (e.g., "*.mp4,*.avi,*.mkv") 48 | */ 49 | void SetFileFilter(const QString &filter); 50 | 51 | /** 52 | * @brief Get the list of selected files 53 | */ 54 | QStringList GetSelectedFiles() const; 55 | 56 | /** 57 | * @brief Set initial files 58 | */ 59 | void SetFiles(const QStringList &files); 60 | 61 | /** 62 | * @brief Retranslate UI when language changes 63 | */ 64 | void RetranslateUi(); 65 | 66 | private slots: 67 | void OnAddFilesClicked(); 68 | void OnAddDirectoryClicked(); 69 | void OnRemoveSelectedClicked(); 70 | void OnClearAllClicked(); 71 | void OnOkClicked(); 72 | void OnCancelClicked(); 73 | 74 | private: 75 | void SetupUI(); 76 | QString ConvertFilterToDialogFormat(const QString &filter); 77 | 78 | QListWidget *fileListWidget; 79 | QPushButton *addFilesButton; 80 | QPushButton *addDirectoryButton; 81 | QPushButton *removeSelectedButton; 82 | QPushButton *clearAllButton; 83 | QLabel *filterLabel; 84 | FilterTagWidget *filterTagWidget; 85 | QPushButton *okButton; 86 | QPushButton *cancelButton; 87 | 88 | QString fileFilter; // Format: "*.mp4,*.avi,*.mkv" 89 | QStringList selectedFiles; 90 | }; 91 | 92 | #endif // BATCH_FILE_DIALOG_H 93 | -------------------------------------------------------------------------------- /src/component/include/resolution_widget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef RESOLUTION_WIDGET_H 19 | #define RESOLUTION_WIDGET_H 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | /** 26 | * @brief Reusable widget for resolution (width x height) selection 27 | * 28 | * Features: 29 | * - Width and Height spin boxes with "auto" (0) support 30 | * - Configurable range (default: 0-16384) 31 | * - "x" label between width and height 32 | * - Horizontal layout: [Width SpinBox] x [Height SpinBox] 33 | * - Optional label: "Dimension:" or "Resolution:" 34 | * 35 | * Usage: 36 | * ResolutionWidget *resolution = new ResolutionWidget(this); 37 | * resolution->SetResolution(1920, 1080); 38 | * int width = resolution->GetWidth(); // Returns 0 for "auto" 39 | * int height = resolution->GetHeight(); // Returns 0 for "auto" 40 | */ 41 | class ResolutionWidget : public QWidget { 42 | Q_OBJECT 43 | 44 | public: 45 | explicit ResolutionWidget(QWidget *parent = nullptr); 46 | explicit ResolutionWidget(const QString &labelText, QWidget *parent = nullptr); 47 | 48 | // Getters 49 | int GetWidth() const; 50 | int GetHeight() const; 51 | 52 | // Setters 53 | void SetWidth(int width); 54 | void SetHeight(int height); 55 | void SetResolution(int width, int height); 56 | void SetRange(int min, int max); 57 | void SetWidthRange(int min, int max); 58 | void SetHeightRange(int min, int max); 59 | 60 | // Enable/disable 61 | void SetEnabled(bool enabled); 62 | 63 | // Translation support 64 | void RetranslateUi(); 65 | 66 | signals: 67 | void ResolutionChanged(int width, int height); 68 | void WidthChanged(int width); 69 | void HeightChanged(int height); 70 | 71 | private slots: 72 | void OnWidthChanged(int value); 73 | void OnHeightChanged(int value); 74 | 75 | private: 76 | void SetupUI(const QString &labelText); 77 | 78 | QLabel *label; // Optional label (e.g., "Dimension:") 79 | QSpinBox *widthSpinBox; 80 | QLabel *xLabel; // "x" between width and height 81 | QSpinBox *heightSpinBox; 82 | }; 83 | 84 | #endif // RESOLUTION_WIDGET_H 85 | -------------------------------------------------------------------------------- /src/component/include/progress_widget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef PROGRESS_WIDGET_H 19 | #define PROGRESS_WIDGET_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | /** 27 | * @brief Reusable progress widget component 28 | * 29 | * Encapsulates the common pattern of QProgressBar + QLabel for progress tracking. 30 | * Used across all conversion pages (ExtractAudioPage, RemuxPage, CutVideoPage, TranscodePage). 31 | * 32 | * Features: 33 | * - Progress bar (0-100 range) 34 | * - Progress label (for time remaining or status messages) 35 | * - Initially hidden, shown during conversion 36 | * - Provides direct access to bar and label for ConverterRunner 37 | * 38 | * Usage: 39 | * @code 40 | * ProgressWidget *progressWidget = new ProgressWidget(this); 41 | * mainLayout->addWidget(progressWidget); 42 | * 43 | * // Access components for ConverterRunner 44 | * converterRunner = new ConverterRunner( 45 | * progressWidget->GetProgressBar(), 46 | * progressWidget->GetProgressLabel(), 47 | * actionButton, ... 48 | * ); 49 | * @endcode 50 | */ 51 | class ProgressWidget : public QWidget { 52 | Q_OBJECT 53 | 54 | public: 55 | /** 56 | * @brief Constructor 57 | * @param parent Parent widget 58 | */ 59 | explicit ProgressWidget(QWidget *parent = nullptr); 60 | 61 | /** 62 | * @brief Get the progress bar component 63 | * @return Pointer to QProgressBar 64 | */ 65 | QProgressBar *GetProgressBar() const { return progressBar; } 66 | 67 | /** 68 | * @brief Get the progress label component 69 | * @return Pointer to QLabel 70 | */ 71 | QLabel *GetProgressLabel() const { return progressLabel; } 72 | 73 | /** 74 | * @brief Show the progress widget 75 | */ 76 | void Show(); 77 | 78 | /** 79 | * @brief Hide the progress widget 80 | */ 81 | void Hide(); 82 | 83 | /** 84 | * @brief Reset progress to 0 and clear label 85 | */ 86 | void Reset(); 87 | 88 | private: 89 | QProgressBar *progressBar; 90 | QLabel *progressLabel; 91 | QVBoxLayout *layout; 92 | }; 93 | 94 | #endif // PROGRESS_WIDGET_H 95 | -------------------------------------------------------------------------------- /src/component/include/format_selector_widget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef FORMAT_SELECTOR_WIDGET_H 19 | #define FORMAT_SELECTOR_WIDGET_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | /** 27 | * @brief Reusable widget for output format selection 28 | * 29 | * Features: 30 | * - ComboBox with common formats 31 | * - Optional "auto" option 32 | * - Separate presets for video, audio, and image formats 33 | * - Optional label: "Output Format:" 34 | * 35 | * Usage: 36 | * FormatSelectorWidget *format = new FormatSelectorWidget(FormatSelectorWidget::Video, this); 37 | * format->SetFormat("mp4"); 38 | * QString formatName = format->GetFormat(); 39 | */ 40 | class FormatSelectorWidget : public QWidget { 41 | Q_OBJECT 42 | 43 | public: 44 | enum FormatType { 45 | Video, // Video formats (mp4, mkv, avi, mov, flv, webm, ts) 46 | Audio, // Audio formats (mp3, aac, wav, flac, ogg, m4a) 47 | Image // Image formats (jpg, png, bmp, webp, tiff, gif) 48 | }; 49 | 50 | explicit FormatSelectorWidget(FormatType type = Video, bool includeAuto = false, QWidget *parent = nullptr); 51 | explicit FormatSelectorWidget(const QString &labelText, FormatType type = Video, bool includeAuto = false, QWidget *parent = nullptr); 52 | 53 | // Getters 54 | QString GetFormat() const; // Returns "" for "auto" if includeAuto is true 55 | bool IsAuto() const; 56 | 57 | // Setters 58 | void SetFormat(const QString &format); 59 | void SetToAuto(); 60 | void SetFormatList(const QStringList &formats); // Custom format list 61 | 62 | // Enable/disable 63 | void SetEnabled(bool enabled); 64 | 65 | // Translation support 66 | void RetranslateUi(); 67 | 68 | signals: 69 | void FormatChanged(const QString &format); 70 | 71 | private slots: 72 | void OnFormatChanged(int index); 73 | 74 | private: 75 | void Initialize(FormatType type, bool includeAuto); 76 | void PopulateFormats(FormatType type, bool includeAuto); 77 | 78 | QLabel *label; 79 | QComboBox *formatComboBox; 80 | FormatType formatType; 81 | bool hasAutoOption; 82 | }; 83 | 84 | #endif // FORMAT_SELECTOR_WIDGET_H 85 | -------------------------------------------------------------------------------- /tool/build_and_package_macos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script to build and package OpenConverter for macOS locally 4 | # Usage: ./build_and_package_macos.sh 5 | 6 | set -e 7 | 8 | # Colors for output 9 | RED='\033[0;31m' 10 | GREEN='\033[0;32m' 11 | YELLOW='\033[1;33m' 12 | BLUE='\033[0;34m' 13 | NC='\033[0m' # No Color 14 | 15 | echo -e "${BLUE}========================================${NC}" 16 | echo -e "${BLUE}OpenConverter macOS Build & Package${NC}" 17 | echo -e "${BLUE}========================================${NC}" 18 | 19 | # Get script directory 20 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 21 | PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" 22 | 23 | cd "$PROJECT_ROOT" 24 | 25 | # Check if FFmpeg is installed 26 | if ! command -v ffmpeg &> /dev/null; then 27 | echo -e "${RED}Error: FFmpeg not found!${NC}" 28 | echo -e "${YELLOW}Please install FFmpeg 5:${NC}" 29 | echo -e " brew install ffmpeg@5" 30 | exit 1 31 | fi 32 | 33 | # Check if Qt is installed 34 | if ! command -v qmake &> /dev/null; then 35 | echo -e "${RED}Error: Qt not found!${NC}" 36 | echo -e "${YELLOW}Please install Qt 5:${NC}" 37 | echo -e " brew install qt@5" 38 | echo -e " export PATH=\"\$(brew --prefix qt@5)/bin:\$PATH\"" 39 | exit 1 40 | fi 41 | 42 | # Set up environment 43 | export PATH="$(brew --prefix ffmpeg@5)/bin:$PATH" 44 | export CMAKE_PREFIX_PATH="$(brew --prefix qt@5):$CMAKE_PREFIX_PATH" 45 | export QT_DIR="$(brew --prefix qt@5)/lib/cmake/Qt5" 46 | export PATH="$(brew --prefix qt@5)/bin:$PATH" 47 | 48 | echo -e "${GREEN}✓ FFmpeg found: $(ffmpeg -version | head -n 1)${NC}" 49 | echo -e "${GREEN}✓ Qt found: $(qmake -version | grep "Using Qt version")${NC}" 50 | 51 | # Clean previous build 52 | if [ -d "src/build" ]; then 53 | echo -e "${YELLOW}Cleaning previous build...${NC}" 54 | rm -rf src/build 55 | fi 56 | 57 | # Build 58 | echo -e "${GREEN}Building OpenConverter...${NC}" 59 | cd src 60 | cmake -B build \ 61 | -DFFMPEG_ROOT_PATH="$(brew --prefix ffmpeg@5)" \ 62 | -DBMF_TRANSCODER=OFF 63 | 64 | cd build 65 | make -j$(sysctl -n hw.ncpu) 66 | 67 | echo -e "${GREEN}✓ Build completed${NC}" 68 | 69 | # Fix libraries 70 | echo -e "${GREEN}Fixing macOS library paths...${NC}" 71 | cd .. 72 | chmod +x ../tool/fix_macos_libs.sh 73 | ../tool/fix_macos_libs.sh 74 | 75 | echo -e "${GREEN}✓ Libraries fixed${NC}" 76 | 77 | # Create DMG 78 | echo -e "${GREEN}Creating DMG...${NC}" 79 | cd ../.. 80 | chmod +x tool/create_dmg_simple.sh 81 | tool/create_dmg_simple.sh src/build/OpenConverter.app 82 | 83 | echo -e "${BLUE}========================================${NC}" 84 | echo -e "${GREEN}✓ Build and packaging completed!${NC}" 85 | echo -e "${BLUE}========================================${NC}" 86 | echo -e "${GREEN}DMG location: ${PROJECT_ROOT}/src/build/OpenConverter.dmg${NC}" 87 | echo -e "${YELLOW}You can now test the DMG by opening it:${NC}" 88 | echo -e " open src/build/OpenConverter.dmg" 89 | -------------------------------------------------------------------------------- /src/builder/include/info_view_page.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef INFO_VIEW_PAGE_H 19 | #define INFO_VIEW_PAGE_H 20 | 21 | #include "base_page.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | class Info; 30 | class QuickInfo; 31 | 32 | class InfoViewPage : public BasePage { 33 | Q_OBJECT 34 | 35 | public: 36 | explicit InfoViewPage(QWidget *parent = nullptr); 37 | ~InfoViewPage(); 38 | 39 | QString GetPageTitle() const override; 40 | void OnPageActivated() override; 41 | void RetranslateUi() override; 42 | 43 | // Handle file drop 44 | void HandleFileDrop(const QString &filePath); 45 | 46 | private slots: 47 | void OnBrowseButtonClicked(); 48 | void OnAnalyzeButtonClicked(); 49 | 50 | private: 51 | void SetupUI(); 52 | void AnalyzeFile(const QString &filePath); 53 | void DisplayInfo(QuickInfo *quickInfo); 54 | void ClearInfo(); 55 | QString FormatBitrate(int64_t bitsPerSec); 56 | QString FormatFrequency(int64_t hertz); 57 | 58 | // UI Components 59 | QVBoxLayout *mainLayout; 60 | QGroupBox *inputGroupBox; 61 | QLineEdit *filePathLineEdit; 62 | QPushButton *browseButton; 63 | 64 | QGroupBox *videoGroupBox; 65 | QLabel *videoStreamLabel; 66 | QLabel *videoStreamValue; 67 | QLabel *widthLabel; 68 | QLabel *widthValue; 69 | QLabel *heightLabel; 70 | QLabel *heightValue; 71 | QLabel *colorSpaceLabel; 72 | QLabel *colorSpaceValue; 73 | QLabel *videoCodecLabel; 74 | QLabel *videoCodecValue; 75 | QLabel *videoBitRateLabel; 76 | QLabel *videoBitRateValue; 77 | QLabel *frameRateLabel; 78 | QLabel *frameRateValue; 79 | 80 | QGroupBox *audioGroupBox; 81 | QLabel *audioStreamLabel; 82 | QLabel *audioStreamValue; 83 | QLabel *audioCodecLabel; 84 | QLabel *audioCodecValue; 85 | QLabel *audioBitRateLabel; 86 | QLabel *audioBitRateValue; 87 | QLabel *channelsLabel; 88 | QLabel *channelsValue; 89 | QLabel *sampleFmtLabel; 90 | QLabel *sampleFmtValue; 91 | QLabel *sampleRateLabel; 92 | QLabel *sampleRateValue; 93 | 94 | // Backend 95 | Info *info; 96 | }; 97 | 98 | #endif // INFO_VIEW_PAGE_H 99 | -------------------------------------------------------------------------------- /src/builder/include/extract_audio_page.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef EXTRACT_AUDIO_PAGE_H 19 | #define EXTRACT_AUDIO_PAGE_H 20 | 21 | #include "base_page.h" 22 | #include "converter_runner.h" 23 | #include "file_selector_widget.h" 24 | #include "progress_widget.h" 25 | #include "batch_output_widget.h" 26 | #include "batch_mode_helper.h" 27 | #include "bitrate_widget.h" 28 | #include "format_selector_widget.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | class ExtractAudioPage : public BasePage { 36 | Q_OBJECT 37 | 38 | public: 39 | explicit ExtractAudioPage(QWidget *parent = nullptr); 40 | ~ExtractAudioPage() override; 41 | 42 | void OnPageActivated() override; 43 | void OnPageDeactivated() override; 44 | QString GetPageTitle() const override { return "Extract Audio"; } 45 | void RetranslateUi() override; 46 | 47 | protected: 48 | void OnOutputPathUpdate() override; 49 | 50 | private slots: 51 | void OnInputFileSelected(const QString &filePath); 52 | void OnOutputFileSelected(const QString &filePath); 53 | void OnFormatChanged(const QString &format); 54 | void OnExtractClicked(); 55 | void OnExtractFinished(bool success); 56 | 57 | signals: 58 | void ExtractComplete(bool success); 59 | 60 | private: 61 | void SetupUI(); 62 | void UpdateOutputPath(); 63 | QString DetectAudioCodecFromFile(const QString &filePath); 64 | QString MapCodecToFormat(const QString &codec); 65 | EncodeParameter* CreateEncodeParameter(); 66 | 67 | // Input/Output section 68 | FileSelectorWidget *inputFileSelector; 69 | FileSelectorWidget *outputFileSelector; 70 | BatchOutputWidget *batchOutputWidget; 71 | 72 | // Settings section 73 | QGroupBox *settingsGroupBox; 74 | QLabel *formatLabel; 75 | FormatSelectorWidget *formatWidget; 76 | QLabel *bitrateLabel; 77 | BitrateWidget *bitrateWidget; 78 | 79 | // Progress section 80 | ProgressWidget *progressWidget; 81 | 82 | // Action section 83 | QPushButton *extractButton; 84 | 85 | // Conversion runner 86 | ConverterRunner *converterRunner; 87 | 88 | // Batch mode helper 89 | BatchModeHelper *batchModeHelper; 90 | }; 91 | 92 | #endif // EXTRACT_AUDIO_PAGE_H 93 | -------------------------------------------------------------------------------- /src/builder/include/compress_picture_page.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef COMPRESS_PICTURE_PAGE_H 19 | #define COMPRESS_PICTURE_PAGE_H 20 | 21 | #include "base_page.h" 22 | #include "file_selector_widget.h" 23 | #include "batch_output_widget.h" 24 | #include "batch_mode_helper.h" 25 | #include "resolution_widget.h" 26 | #include "pixel_format_widget.h" 27 | #include "quality_widget.h" 28 | #include "format_selector_widget.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | class Converter; 36 | class EncodeParameter; 37 | class ProcessParameter; 38 | 39 | class CompressPicturePage : public BasePage { 40 | Q_OBJECT 41 | 42 | public: 43 | explicit CompressPicturePage(QWidget *parent = nullptr); 44 | ~CompressPicturePage(); 45 | 46 | QString GetPageTitle() const override; 47 | void OnPageActivated() override; 48 | void RetranslateUi() override; 49 | 50 | protected: 51 | void OnOutputPathUpdate() override; 52 | 53 | private slots: 54 | void OnInputFileSelected(const QString &filePath); 55 | void OnOutputFileSelected(const QString &filePath); 56 | void OnConvertClicked(); 57 | void OnFormatChanged(const QString &format); 58 | 59 | private: 60 | void SetupUI(); 61 | void UpdateOutputPath(); 62 | EncodeParameter* CreateEncodeParameter(); 63 | 64 | // UI Components - Input/Output Section 65 | QVBoxLayout *mainLayout; 66 | FileSelectorWidget *inputFileSelector; 67 | FileSelectorWidget *outputFileSelector; 68 | BatchOutputWidget *batchOutputWidget; 69 | 70 | // UI Components - Settings Section 71 | QGroupBox *settingsGroupBox; 72 | QLabel *formatLabel; 73 | FormatSelectorWidget *formatWidget; 74 | QLabel *resolutionLabel; 75 | ResolutionWidget *resolutionWidget; 76 | QLabel *pixelFormatLabel; 77 | PixelFormatWidget *pixelFormatWidget; 78 | QLabel *qualityLabel; 79 | QualityWidget *qualityWidget; 80 | 81 | // UI Components - Action Section 82 | QPushButton *convertButton; 83 | 84 | // Backend 85 | EncodeParameter *encodeParameter; 86 | ProcessParameter *processParameter; 87 | Converter *converter; 88 | 89 | // Batch mode helper 90 | BatchModeHelper *batchModeHelper; 91 | }; 92 | 93 | #endif // COMPRESS_PICTURE_PAGE_H 94 | -------------------------------------------------------------------------------- /src/builder/include/batch_queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef BATCH_QUEUE_H 19 | #define BATCH_QUEUE_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include "batch_item.h" 25 | 26 | /** 27 | * @brief Singleton class to manage batch processing queue 28 | * 29 | * The BatchQueue manages a list of BatchItems to be processed. 30 | * It provides thread-safe access to the queue and emits signals 31 | * when items are added, removed, or their status changes. 32 | * 33 | * Usage: 34 | * BatchQueue *queue = BatchQueue::Instance(); 35 | * queue->AddItem(item); 36 | * queue->ProcessNext(); 37 | */ 38 | class BatchQueue : public QObject { 39 | Q_OBJECT 40 | 41 | public: 42 | // Singleton access 43 | static BatchQueue* Instance(); 44 | 45 | // Queue management 46 | void AddItem(BatchItem *item); 47 | void AddItems(const QList &items); 48 | void RemoveItem(int index); 49 | void Clear(); 50 | 51 | // Queue access 52 | int GetCount() const; 53 | int GetWaitingCount() const; 54 | int GetProcessingCount() const; 55 | int GetFinishedCount() const; 56 | int GetFailedCount() const; 57 | BatchItem* GetItem(int index) const; 58 | QList GetAllItems() const; 59 | BatchItem* GetNextWaitingItem() const; 60 | 61 | // Queue state 62 | bool IsEmpty() const; 63 | bool HasWaitingItems() const; 64 | bool IsProcessing() const; 65 | 66 | // Helper methods 67 | int GetItemIndex(BatchItem *item) const; 68 | void NotifyItemStatusChanged(int index, BatchItemStatus status); 69 | void NotifyItemProgressChanged(int index, double progress); 70 | 71 | signals: 72 | void ItemAdded(int index); 73 | void ItemRemoved(int index); 74 | void ItemStatusChanged(int index, BatchItemStatus status); 75 | void ItemProgressChanged(int index, double progress); 76 | void QueueCleared(); 77 | void AllItemsFinished(); 78 | 79 | private: 80 | BatchQueue(); // Private constructor for singleton 81 | ~BatchQueue(); 82 | BatchQueue(const BatchQueue&) = delete; 83 | BatchQueue& operator=(const BatchQueue&) = delete; 84 | 85 | static BatchQueue *instance; 86 | static QMutex instanceMutex; 87 | 88 | QList items; 89 | mutable QMutex queueMutex; 90 | }; 91 | 92 | #endif // BATCH_QUEUE_H 93 | -------------------------------------------------------------------------------- /src/component/src/bitrate_widget.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "bitrate_widget.h" 19 | #include 20 | 21 | BitrateWidget::BitrateWidget(BitrateType type, QWidget *parent) 22 | : QWidget(parent) { 23 | SetupUI("", type); 24 | } 25 | 26 | BitrateWidget::BitrateWidget(const QString &labelText, BitrateType type, QWidget *parent) 27 | : QWidget(parent) { 28 | SetupUI(labelText, type); 29 | } 30 | 31 | void BitrateWidget::SetupUI(const QString &labelText, BitrateType type) { 32 | QHBoxLayout *layout = new QHBoxLayout(this); 33 | layout->setContentsMargins(0, 0, 0, 0); 34 | 35 | // Optional label 36 | if (!labelText.isEmpty()) { 37 | label = new QLabel(labelText, this); 38 | layout->addWidget(label); 39 | } else { 40 | label = nullptr; 41 | } 42 | 43 | // Bitrate spin box 44 | bitrateSpinBox = new QSpinBox(this); 45 | if (type == Video) { 46 | bitrateSpinBox->setRange(0, 50000); 47 | } else { // Audio 48 | bitrateSpinBox->setRange(0, 320); 49 | } 50 | bitrateSpinBox->setValue(0); 51 | bitrateSpinBox->setSpecialValueText(tr("auto")); 52 | bitrateSpinBox->setSuffix(tr(" kbps")); 53 | layout->addWidget(bitrateSpinBox); 54 | 55 | // Connect signals 56 | connect(bitrateSpinBox, QOverload::of(&QSpinBox::valueChanged), 57 | this, &BitrateWidget::OnValueChanged); 58 | 59 | setLayout(layout); 60 | } 61 | 62 | int BitrateWidget::GetBitrate() const { 63 | return bitrateSpinBox->value(); 64 | } 65 | 66 | bool BitrateWidget::IsAuto() const { 67 | return bitrateSpinBox->value() == 0; 68 | } 69 | 70 | void BitrateWidget::SetBitrate(int kbps) { 71 | bitrateSpinBox->setValue(kbps); 72 | } 73 | 74 | void BitrateWidget::SetToAuto() { 75 | bitrateSpinBox->setValue(0); 76 | } 77 | 78 | void BitrateWidget::SetRange(int min, int max) { 79 | bitrateSpinBox->setRange(min, max); 80 | } 81 | 82 | void BitrateWidget::SetEnabled(bool enabled) { 83 | bitrateSpinBox->setEnabled(enabled); 84 | if (label) { 85 | label->setEnabled(enabled); 86 | } 87 | } 88 | 89 | void BitrateWidget::RetranslateUi() { 90 | bitrateSpinBox->setSpecialValueText(tr("auto")); 91 | bitrateSpinBox->setSuffix(tr(" kbps")); 92 | } 93 | 94 | void BitrateWidget::OnValueChanged(int value) { 95 | emit BitrateChanged(value); 96 | } 97 | -------------------------------------------------------------------------------- /src/component/include/batch_input_widget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef BATCH_INPUT_WIDGET_H 19 | #define BATCH_INPUT_WIDGET_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /** 30 | * @brief Widget for batch input file selection 31 | * 32 | * Provides two modes: 33 | * 1. Select multiple files directly 34 | * 2. Select a directory and filter by extension (.jpg, .png, .mp4, etc.) 35 | * 36 | * Features: 37 | * - Add Files button: Opens file dialog for multiple file selection 38 | * - Add Directory button: Opens directory dialog 39 | * - Filter input: Comma-separated extensions (e.g., ".jpg,.png,.mp4") 40 | * - File list: Shows all selected files 41 | * - Remove/Clear buttons: Manage the file list 42 | */ 43 | class BatchInputWidget : public QWidget { 44 | Q_OBJECT 45 | 46 | public: 47 | explicit BatchInputWidget(QWidget *parent = nullptr); 48 | ~BatchInputWidget(); 49 | 50 | // Get selected files 51 | QStringList GetInputFiles() const; 52 | 53 | // Set file filter for directory mode 54 | void SetFileFilter(const QString &filter); 55 | QString GetFileFilter() const; 56 | 57 | // Clear all files 58 | void Clear(); 59 | 60 | // Retranslate UI when language changes 61 | void RetranslateUi(); 62 | 63 | signals: 64 | void FilesChanged(const QStringList &files); 65 | 66 | private slots: 67 | void OnAddFilesClicked(); 68 | void OnAddDirectoryClicked(); 69 | void OnRemoveSelectedClicked(); 70 | void OnClearAllClicked(); 71 | void OnFilterChanged(const QString &text); 72 | 73 | private: 74 | void SetupUI(); 75 | void AddFiles(const QStringList &files); 76 | void AddDirectory(const QString &dirPath); 77 | QStringList FilterFilesByExtension(const QStringList &files, const QString &filter); 78 | 79 | // UI Components 80 | QGroupBox *groupBox; 81 | QPushButton *addFilesButton; 82 | QPushButton *addDirectoryButton; 83 | QPushButton *removeSelectedButton; 84 | QPushButton *clearAllButton; 85 | QListWidget *fileListWidget; 86 | QLineEdit *filterLineEdit; 87 | QLabel *filterLabel; 88 | QLabel *fileCountLabel; 89 | 90 | QString currentFilter; // e.g., ".jpg,.png,.mp4" 91 | }; 92 | 93 | #endif // BATCH_INPUT_WIDGET_H 94 | -------------------------------------------------------------------------------- /src/builder/include/batch_item.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef BATCH_ITEM_H 19 | #define BATCH_ITEM_H 20 | 21 | #include 22 | #include 23 | #include "encode_parameter.h" 24 | 25 | /** 26 | * @brief Status of a batch item 27 | */ 28 | enum class BatchItemStatus { 29 | Waiting, // Waiting to be processed 30 | Processing, // Currently being processed 31 | Finished, // Successfully completed 32 | Failed // Failed with error 33 | }; 34 | 35 | /** 36 | * @brief Represents a single item in the batch processing queue 37 | * 38 | * Each BatchItem contains: 39 | * - Input file path 40 | * - Output file path 41 | * - Encoding parameters (codec, bitrate, resolution, etc.) 42 | * - Processing status (waiting, processing, finished, failed) 43 | * - Error message (if failed) 44 | * - Timestamps for tracking 45 | */ 46 | class BatchItem { 47 | public: 48 | BatchItem(); 49 | BatchItem(const QString &inputPath, const QString &outputPath); 50 | ~BatchItem(); 51 | 52 | // Getters 53 | QString GetInputPath() const; 54 | QString GetOutputPath() const; 55 | BatchItemStatus GetStatus() const; 56 | QString GetStatusString() const; 57 | QString GetErrorMessage() const; 58 | EncodeParameter* GetEncodeParameter() const; 59 | QString GetTranscoderName() const; 60 | QDateTime GetCreatedTime() const; 61 | QDateTime GetStartedTime() const; 62 | QDateTime GetFinishedTime() const; 63 | double GetProgress() const; 64 | 65 | // Setters 66 | void SetInputPath(const QString &path); 67 | void SetOutputPath(const QString &path); 68 | void SetStatus(BatchItemStatus status); 69 | void SetErrorMessage(const QString &message); 70 | void SetEncodeParameter(EncodeParameter *param); 71 | void SetTranscoderName(const QString &name); 72 | void SetProgress(double progress); 73 | 74 | // Status management 75 | void MarkAsProcessing(); 76 | void MarkAsFinished(); 77 | void MarkAsFailed(const QString &errorMessage); 78 | 79 | private: 80 | QString inputPath; 81 | QString outputPath; 82 | BatchItemStatus status; 83 | QString errorMessage; 84 | EncodeParameter *encodeParameter; 85 | QString transcoderName; // Transcoder to use (e.g., "FFMPEG", "BMF", "FFTOOL") 86 | QDateTime createdTime; 87 | QDateTime startedTime; 88 | QDateTime finishedTime; 89 | double progress; // 0.0 to 100.0 90 | }; 91 | 92 | #endif // BATCH_ITEM_H 93 | -------------------------------------------------------------------------------- /src/builder/src/base_page.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "../include/base_page.h" 19 | #include "../include/open_converter.h" 20 | #include "../include/shared_data.h" 21 | #include 22 | 23 | BasePage::BasePage(QWidget *parent) : QWidget(parent) {} 24 | 25 | BasePage::~BasePage() {} 26 | 27 | void BasePage::OnPageActivated() { 28 | // Default implementation - can be overridden by derived classes 29 | } 30 | 31 | void BasePage::OnPageDeactivated() { 32 | // Default implementation - can be overridden by derived classes 33 | } 34 | 35 | void BasePage::HandleSharedDataUpdate(QLineEdit *inputFileLineEdit, 36 | QLineEdit *outputFileLineEdit, 37 | const QString &defaultFormat) { 38 | if (!inputFileLineEdit || !outputFileLineEdit) { 39 | return; 40 | } 41 | 42 | // Get shared data from main window 43 | OpenConverter *mainWindow = qobject_cast(window()); 44 | if (!mainWindow || !mainWindow->GetSharedData()) { 45 | return; 46 | } 47 | 48 | SharedData *sharedData = mainWindow->GetSharedData(); 49 | QString sharedPath = sharedData->GetInputFilePath(); 50 | 51 | if (sharedPath.isEmpty()) { 52 | return; 53 | } 54 | 55 | // Check if shared path is different from current input 56 | if (sharedPath != inputFileLineEdit->text()) { 57 | // Update input field 58 | inputFileLineEdit->setText(sharedPath); 59 | 60 | // Call hook for derived class to handle input change 61 | OnInputFileChanged(sharedPath); 62 | 63 | // Reset output path to auto-generate when input changes 64 | sharedData->ResetOutputPath(); 65 | 66 | // Call hook for derived class to update output path 67 | OnOutputPathUpdate(); 68 | } else { 69 | // Input path is the same, but update output path in case format changed 70 | QString format = defaultFormat; 71 | QString outputPath = sharedData->GenerateOutputPath(format); 72 | outputFileLineEdit->setText(outputPath); 73 | } 74 | } 75 | 76 | void BasePage::OnInputFileChanged(const QString &newPath) { 77 | // Default implementation - can be overridden by derived classes 78 | Q_UNUSED(newPath); 79 | } 80 | 81 | void BasePage::OnOutputPathUpdate() { 82 | // Default implementation - can be overridden by derived classes 83 | } 84 | 85 | void BasePage::RetranslateUi() { 86 | // Default implementation - can be overridden by derived classes 87 | } 88 | -------------------------------------------------------------------------------- /src/component/include/filter_tag_widget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef FILTER_TAG_WIDGET_H 19 | #define FILTER_TAG_WIDGET_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /** 31 | * @brief A single filter tag with delete button 32 | */ 33 | class FilterTag : public QWidget { 34 | Q_OBJECT 35 | 36 | public: 37 | explicit FilterTag(const QString &extension, QWidget *parent = nullptr); 38 | QString GetExtension() const { return extension; } 39 | 40 | signals: 41 | void DeleteClicked(); 42 | 43 | private: 44 | QString extension; 45 | QLabel *label; 46 | QPushButton *deleteButton; 47 | }; 48 | 49 | /** 50 | * @brief Widget for managing file filter extensions with visual tags 51 | * 52 | * Displays file extensions as tags (e.g., ".mp4", ".avi") with individual delete buttons. 53 | * Users can add new extensions via an "Add" button. 54 | */ 55 | class FilterTagWidget : public QWidget { 56 | Q_OBJECT 57 | 58 | public: 59 | explicit FilterTagWidget(QWidget *parent = nullptr); 60 | 61 | /** 62 | * @brief Set filter extensions from comma-separated string 63 | * @param filter Format: "*.mp4,*.avi,*.mkv" or ".mp4,.avi,.mkv" 64 | */ 65 | void SetFilter(const QString &filter); 66 | 67 | /** 68 | * @brief Get filter as comma-separated string 69 | * @return Format: "*.mp4,*.avi,*.mkv" 70 | */ 71 | QString GetFilter() const; 72 | 73 | /** 74 | * @brief Get list of extensions 75 | * @return List of extensions (e.g., ["*.mp4", "*.avi"]) 76 | */ 77 | QStringList GetExtensions() const; 78 | 79 | /** 80 | * @brief Clear all filter tags 81 | */ 82 | void Clear(); 83 | 84 | /** 85 | * @brief Retranslate UI text 86 | */ 87 | void RetranslateUi(); 88 | 89 | signals: 90 | /** 91 | * @brief Emitted when filter changes (tag added/removed) 92 | */ 93 | void FilterChanged(const QString &filter); 94 | 95 | private slots: 96 | void OnAddClicked(); 97 | void OnTagDeleted(); 98 | 99 | private: 100 | void SetupUI(); 101 | void AddTag(const QString &extension); 102 | void UpdateLayout(); 103 | 104 | QScrollArea *scrollArea; 105 | QWidget *tagsContainer; 106 | QHBoxLayout *tagsLayout; 107 | QPushButton *addButton; 108 | QStringList extensions; 109 | }; 110 | 111 | #endif // FILTER_TAG_WIDGET_H 112 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Welcome to contribute to this repository! Your help is greatly appreciated and here are some suggestions and guidance to ensure the contribution goes smoothly. 4 | 5 | ## Contribution mode 6 | 7 | You can contribute by: 8 | 9 | 1. **Reporting problems**: Found an error or improvement in the documentation? Please create an Issue to report the problem. 10 | 2. **Propose improvements**: If you have suggestions for this project, send us through an issue. 11 | 12 | ## Report problems and make recommendations 13 | 14 | 1. Please create a new Issue in the [Issues](https://github.com/OpenConverterLab/OpenConverter/issues) page. 15 | 16 | 2. Select the appropriate label, such as "bug" or "enhancement". 17 | 18 | 3. Provide clear and detailed instructions, including steps to reproduce the problem (if reporting the problem) or your suggestions. 19 | 20 | ## Submit a Pull Request 21 | Before you submit your Pull Request (PR) consider the following guidelines: 22 | 1. Search [GitHub](https://github.com/OpenConverterLab/OpenConverter/pulls) for an open or closed PR that relates to your submission. You don't want to duplicate existing efforts. 23 | 2. Please submit an issue instead of PR if you have a better suggestion for format tools. We won't accept a lot of file changes directly without issue statement and assignment. 24 | 3. Be sure that the issue describes the problem you're fixing, or documents the design for the feature you'd like to add. Before we accepting your work, we need to conduct some checks and evaluations. So, It will be better if you can discuss the design with us. 25 | 4. [Fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the BabitMF/bmf repo. 26 | 5. In your forked repository, make your changes in a new git branch: 27 | ``` 28 | git checkout -b my-fix-branch main 29 | ``` 30 | 6. Create your patch. See our [Patch Guidelines](#patch-guidelines) for details 31 | 32 | 7. Push your branch to GitHub: 33 | ``` 34 | git push origin my-fix-branch 35 | ``` 36 | 8. In GitHub, send a pull request to `OpenConverter:main` with a clear and unambiguous title. 37 | 38 | 9. Typically, we review your patch within a week after submission. If the patch involves significant changes, it may take more time. Therefore, if you haven't received any response after a week, please initiate a request for review. 39 | 40 | ## Contribution Prerequisites 41 | - You are familiar with [Github](https://github.com) 42 | - Maybe you need familiar with [Actions](https://github.com/features/actions)(our default workflow tool). 43 | 44 | ## Patch Guidelines 45 | - If you add some code inside the framework, please include appropriate test cases 46 | - Commit your changes using a descriptive commit message that follows [AngularJS Git Commit Message Conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit). 47 | - Do not commit unrelated changes together. 48 | - Cosmetic changes should be kept in separate patches. 49 | - Follow our [Style Guides](#code-style-guides). 50 | 51 | ## Code Style Guides 52 | 53 | - See [C++ Code clang-format](https://marketplace.visualstudio.com/items?itemName=xaver.clang-format). 54 | After clang-format installed(version 16.0.6, can be installed by: pip install clang-format==16.0.6), using [tool/format.sh](tool/format.sh). 55 | -------------------------------------------------------------------------------- /src/transcoder/include/transcoder_ffmpeg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * This file is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | */ 15 | 16 | #ifndef TRANSCODERFFMPEG_H 17 | #define TRANSCODERFFMPEG_H 18 | 19 | #include "transcoder.h" 20 | 21 | extern "C" { 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | }; 29 | 30 | #define ENCODE_BIT_RATE 5000000 31 | 32 | typedef struct FilteringContext { 33 | AVFilterContext *buffersrc_ctx; 34 | AVFilterContext *buffersink_ctx; 35 | AVFilterGraph *filter_graph; 36 | } FilteringContext; 37 | 38 | class TranscoderFFmpeg : public Transcoder { 39 | public: 40 | TranscoderFFmpeg(ProcessParameter *process_parameter, 41 | EncodeParameter *encode_parameter); 42 | ~TranscoderFFmpeg(); 43 | 44 | bool transcode(std::string input_path, std::string output_path); 45 | 46 | int open_media(); 47 | 48 | int init_filter(AVCodecContext *dec_ctx, FilteringContext *filter_ctx, const char *filters_descr); 49 | 50 | int init_filters_wrapper(); 51 | 52 | void adjust_frame_pts_to_encoder_timebase(AVFrame *frame, int index, AVRational& tb); 53 | 54 | int encode_video(AVStream *inStream, AVFrame *frame); 55 | 56 | int encode_write_video(AVFrame *frame); 57 | 58 | int transcode_video(bool skip_encode = false); 59 | 60 | int encode_audio(AVStream *inStream, AVFrame *frame); 61 | 62 | int encode_write_audio(AVFrame *frame); 63 | 64 | int transcode_audio(bool skip_encode = false); 65 | 66 | int prepare_decoder(); 67 | 68 | int prepare_encoder_video(); 69 | 70 | int prepare_encoder_audio(); 71 | 72 | int prepare_copy(AVFormatContext *avCtx, AVStream **stream, 73 | AVCodecParameters *codecParam); 74 | 75 | int remux(AVPacket *pkt, AVFormatContext *avCtx, AVStream *inStream, 76 | AVStream *outStream); 77 | 78 | private: 79 | char error_msg[128]; 80 | // encoder's parameters 81 | bool copy_video; 82 | bool copy_audio; 83 | 84 | // Decoder and encoder contexts 85 | StreamContext *decoder; 86 | StreamContext *encoder; 87 | 88 | int64_t start_time; 89 | 90 | FilteringContext *filters_ctx; 91 | 92 | // Progress tracking 93 | int64_t total_duration; // Total duration in microseconds 94 | int64_t current_duration; // Current processed duration in microseconds 95 | 96 | // Helper function to update progress 97 | void update_progress(int64_t current_pts, AVRational time_base); 98 | void print_error(const char *msg, int ret); 99 | }; 100 | 101 | #endif // TRANSCODERFFMPEG_H 102 | -------------------------------------------------------------------------------- /src/component/include/batch_output_widget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef BATCH_OUTPUT_WIDGET_H 19 | #define BATCH_OUTPUT_WIDGET_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /** 31 | * @brief Widget for batch output configuration 32 | * 33 | * Provides options for: 34 | * 1. Output directory selection (optional) 35 | * 2. Output suffix (default: "-oc-output") 36 | * 3. Keep original filename option (only when output directory is different) 37 | * 38 | * Output path generation: 39 | * - If output directory is set: 40 | * - Keep original name: /output/dir/filename.ext 41 | * - With suffix: /output/dir/filename-oc-output.ext 42 | * - If output directory is not set (same as input): 43 | * - /input/dir/filename-oc-output.ext 44 | */ 45 | class BatchOutputWidget : public QWidget { 46 | Q_OBJECT 47 | 48 | public: 49 | explicit BatchOutputWidget(QWidget *parent = nullptr); 50 | ~BatchOutputWidget(); 51 | 52 | // Get output configuration 53 | QString GetOutputDirectory() const; 54 | QString GetOutputSuffix() const; 55 | bool IsKeepOriginalName() const; 56 | bool IsUseOutputDirectory() const; 57 | 58 | // Set output configuration 59 | void SetOutputDirectory(const QString &dir); 60 | void SetOutputSuffix(const QString &suffix); 61 | void SetKeepOriginalName(bool keep); 62 | 63 | // Generate output path for a given input file 64 | QString GenerateOutputPath(const QString &inputPath, const QString &outputExtension = QString()) const; 65 | 66 | // Retranslate UI when language changes 67 | void RetranslateUi(); 68 | 69 | signals: 70 | void OutputConfigChanged(); 71 | 72 | private slots: 73 | void OnBrowseOutputDirClicked(); 74 | void OnUseOutputDirToggled(bool checked); 75 | void OnSuffixChanged(const QString &text); 76 | void OnKeepOriginalNameToggled(bool checked); 77 | 78 | private: 79 | void SetupUI(); 80 | void UpdateKeepOriginalNameState(); 81 | void UpdateExampleLabel(); 82 | 83 | // UI Components 84 | QGroupBox *groupBox; 85 | QCheckBox *useOutputDirCheckBox; 86 | QLineEdit *outputDirLineEdit; 87 | QPushButton *browseOutputDirButton; 88 | QLabel *outputDirLabel; 89 | QLabel *suffixLabel; 90 | QLineEdit *suffixLineEdit; 91 | QCheckBox *keepOriginalNameCheckBox; 92 | QLabel *exampleLabel; 93 | 94 | QString outputDirectory; 95 | QString outputSuffix; 96 | bool keepOriginalName; 97 | }; 98 | 99 | #endif // BATCH_OUTPUT_WIDGET_H 100 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | ci-linux: 12 | runs-on: ubuntu-22.04 13 | concurrency: 14 | group: "build-linux-${{ github.event.pull_request.number }}" 15 | cancel-in-progress: true 16 | 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v3 20 | with: 21 | submodules: recursive 22 | 23 | - name: Install dependencies 24 | run: | 25 | sudo apt-get update 26 | sudo apt-get install -y build-essential cmake pkg-config 27 | sudo apt-get install -y nasm yasm libx264-dev libx265-dev libnuma-dev 28 | git clone https://github.com/google/googletest.git 3rd_party/gtest 29 | 30 | - name: Get FFmpeg 31 | run: | 32 | wget https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2024-11-30-13-12/ffmpeg-n5.1.6-11-gcde3c5fc0c-linux64-gpl-shared-5.1.tar.xz 33 | tar xJvf ffmpeg-n5.1.6-11-gcde3c5fc0c-linux64-gpl-shared-5.1.tar.xz 34 | ls ffmpeg-n5.1.6-11-gcde3c5fc0c-linux64-gpl-shared-5.1 35 | echo "FFMPEG_ROOT_PATH=$(pwd)/ffmpeg-n5.1.6-11-gcde3c5fc0c-linux64-gpl-shared-5.1" >> $GITHUB_ENV 36 | 37 | - name: Build with CMake 38 | run: | 39 | export PATH=$PATH:$FFMPEG_ROOT_PATH/bin 40 | (cd src && cmake -B build -DENABLE_TESTS=ON -DENABLE_GUI=OFF && cd build && make -j$(nproc)) 41 | 42 | - name: Run tests 43 | run: | 44 | export LD_LIBRARY_PATH=$FFMPEG_ROOT_PATH/lib 45 | wget https://github.com/JackLau1222/OpenConverter/releases/download/files/oc-test.zip 46 | unzip -j oc-test.zip -d src/build/tests/media 47 | cd src/build/tests && ./transcoder_test 48 | 49 | ci-macos: 50 | runs-on: macos-latest 51 | concurrency: 52 | group: "build-macos-${{ github.event.pull_request.number }}" 53 | cancel-in-progress: true 54 | 55 | steps: 56 | - name: Checkout code 57 | uses: actions/checkout@v3 58 | with: 59 | submodules: recursive 60 | 61 | - name: Install dependencies 62 | run: | 63 | brew install cmake pkg-config 64 | brew install nasm yasm x264 x265 65 | git clone https://github.com/google/googletest.git 3rd_party/gtest 66 | 67 | - name: Install FFmpeg via Homebrew 68 | run: | 69 | # Install FFmpeg 5 with x264, x265 support (pre-built from Homebrew) 70 | brew install ffmpeg@5 71 | 72 | # Set FFmpeg path 73 | export FFMPEG_ROOT_PATH=$(brew --prefix ffmpeg@5) 74 | echo "FFMPEG_ROOT_PATH=$FFMPEG_ROOT_PATH" >> $GITHUB_ENV 75 | 76 | # Verify FFmpeg has x264 and x265 77 | echo "FFmpeg configuration:" 78 | $FFMPEG_ROOT_PATH/bin/ffmpeg -version | head -n 1 79 | $FFMPEG_ROOT_PATH/bin/ffmpeg -encoders 2>/dev/null | grep -E "libx264|libx265" || echo "Warning: x264/x265 not found" 80 | 81 | - name: Build with CMake 82 | run: | 83 | export PATH=$PATH:$FFMPEG_ROOT_PATH/bin 84 | (cd src && cmake -B build -DENABLE_TESTS=ON -DENABLE_GUI=OFF && cd build && make -j$(sysctl -n hw.ncpu)) 85 | 86 | - name: Run tests 87 | run: | 88 | export DYLD_LIBRARY_PATH=$FFMPEG_ROOT_PATH/lib 89 | wget https://github.com/JackLau1222/OpenConverter/releases/download/files/oc-test.zip 90 | unzip -j oc-test.zip -d src/build/tests/media 91 | cd src/build/tests && ./transcoder_test 92 | -------------------------------------------------------------------------------- /src/builder/include/remux_page.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef REMUX_PAGE_H 19 | #define REMUX_PAGE_H 20 | 21 | #include "base_page.h" 22 | #include "converter_runner.h" 23 | #include "file_selector_widget.h" 24 | #include "progress_widget.h" 25 | #include "format_selector_widget.h" 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | extern "C" { 36 | #include 37 | } 38 | 39 | class EncodeParameter; 40 | class ProcessParameter; 41 | 42 | struct StreamInfo { 43 | int index; 44 | QString type; // "Video", "Audio", "Subtitle", "Data", "Unknown" 45 | QString codec; 46 | QString details; // Additional info like resolution, bitrate, etc. 47 | QCheckBox *checkbox; 48 | }; 49 | 50 | class RemuxPage : public BasePage { 51 | Q_OBJECT 52 | 53 | public: 54 | explicit RemuxPage(QWidget *parent = nullptr); 55 | ~RemuxPage() override; 56 | 57 | void OnPageActivated() override; 58 | void OnPageDeactivated() override; 59 | QString GetPageTitle() const override { return "Remux"; } 60 | void RetranslateUi() override; 61 | 62 | protected: 63 | void OnInputFileChanged(const QString &newPath) override; 64 | void OnOutputPathUpdate() override; 65 | 66 | private slots: 67 | void OnInputFileSelected(const QString &filePath); 68 | void OnOutputFileSelected(const QString &filePath); 69 | void OnFormatChanged(const QString &format); 70 | void OnRemuxClicked(); 71 | void OnRemuxFinished(bool success); 72 | 73 | signals: 74 | void RemuxComplete(bool success); 75 | 76 | private: 77 | void SetupUI(); 78 | void UpdateOutputPath(); 79 | void AnalyzeStreams(const QString &filePath); 80 | void ClearStreams(); 81 | QString GetStreamTypeName(int codecType); 82 | QString FormatBitrate(int64_t bitsPerSec); 83 | 84 | // Input/Output section 85 | FileSelectorWidget *inputFileSelector; 86 | FileSelectorWidget *outputFileSelector; 87 | 88 | // Streams section 89 | QGroupBox *streamsGroupBox; 90 | QScrollArea *streamsScrollArea; 91 | QWidget *streamsContainer; 92 | QVBoxLayout *streamsLayout; 93 | QVector streams; 94 | 95 | // Settings section 96 | QGroupBox *settingsGroupBox; 97 | QLabel *formatLabel; 98 | FormatSelectorWidget *formatWidget; 99 | 100 | // Progress section 101 | ProgressWidget *progressWidget; 102 | 103 | // Action section 104 | QPushButton *remuxButton; 105 | 106 | // Conversion runner 107 | ConverterRunner *converterRunner; 108 | }; 109 | 110 | #endif // REMUX_PAGE_H 111 | -------------------------------------------------------------------------------- /src/builder/include/batch_queue_dialog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef BATCH_QUEUE_DIALOG_H 19 | #define BATCH_QUEUE_DIALOG_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "batch_item.h" 27 | #include "batch_queue.h" 28 | #include "../../common/include/process_parameter.h" 29 | 30 | /** 31 | * @brief Dialog to display and manage the batch processing queue 32 | * 33 | * Shows a table with columns: 34 | * - Status (icon + text: Waiting, Processing, Finished, Failed) 35 | * - Input File (full path) 36 | * - Output File (full path) 37 | * - Progress (percentage for processing items) 38 | * 39 | * Features: 40 | * - Real-time updates as items are processed 41 | * - Remove selected items 42 | * - Clear finished/failed items 43 | * - Clear all items 44 | * - Start/Stop batch processing 45 | * - Summary statistics (total, waiting, processing, finished, failed) 46 | */ 47 | class BatchQueueDialog : public QDialog, public ProcessObserver { 48 | Q_OBJECT 49 | 50 | public: 51 | explicit BatchQueueDialog(QWidget *parent = nullptr); 52 | ~BatchQueueDialog(); 53 | 54 | // Refresh the queue display 55 | void RefreshQueue(); 56 | 57 | private slots: 58 | void OnItemAdded(int index); 59 | void OnItemRemoved(int index); 60 | void OnItemStatusChanged(int index, BatchItemStatus status); 61 | void OnItemProgressChanged(int index, double progress); 62 | void OnQueueCleared(); 63 | 64 | void OnStartClicked(); 65 | void OnStopClicked(); 66 | void OnRemoveSelectedClicked(); 67 | void OnClearFinishedClicked(); 68 | void OnClearAllClicked(); 69 | void OnCloseClicked(); 70 | 71 | void OnConversionFinished(bool success); 72 | void ProcessNextItem(); 73 | 74 | // ProcessObserver interface 75 | void on_process_update(double progress) override; 76 | void on_time_update(double timeRequired) override; 77 | 78 | private: 79 | void SetupUI(); 80 | void UpdateStatistics(); 81 | void AddItemToTable(BatchItem *item, int row); 82 | void UpdateItemInTable(int row, BatchItem *item); 83 | QString GetStatusIcon(BatchItemStatus status) const; 84 | QColor GetStatusColor(BatchItemStatus status) const; 85 | 86 | // UI Components 87 | QTableWidget *queueTable; 88 | QLabel *statisticsLabel; 89 | QPushButton *startButton; 90 | QPushButton *stopButton; 91 | QPushButton *removeSelectedButton; 92 | QPushButton *clearFinishedButton; 93 | QPushButton *clearAllButton; 94 | QPushButton *closeButton; 95 | 96 | BatchQueue *batchQueue; 97 | 98 | bool isProcessing; 99 | int currentItemIndex; 100 | }; 101 | 102 | #endif // BATCH_QUEUE_DIALOG_H 103 | -------------------------------------------------------------------------------- /tool/create_dmg_simple.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Simple DMG creation script without appdmg 4 | # Usage: ./create_dmg_simple.sh [path_to_app] 5 | 6 | set -e 7 | 8 | # Get script directory 9 | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 10 | 11 | # Get app path from parameter or use default 12 | if [ -z "$1" ]; then 13 | # No parameter, use default path 14 | APP_PATH="$SCRIPT_DIR/../build/OpenConverter.app" 15 | else 16 | # Parameter provided, resolve to absolute path 17 | INPUT_PATH="$1" 18 | 19 | # Ensure .app extension 20 | if [[ "$INPUT_PATH" != *.app ]]; then 21 | INPUT_PATH="${INPUT_PATH}.app" 22 | fi 23 | 24 | # Convert to absolute path 25 | if [[ "$INPUT_PATH" = /* ]]; then 26 | # Already absolute path 27 | APP_PATH="$INPUT_PATH" 28 | else 29 | # Relative path, resolve from current directory 30 | APP_PATH="$(cd "$(dirname "$INPUT_PATH")" 2>/dev/null && pwd)/$(basename "$INPUT_PATH")" 31 | fi 32 | fi 33 | 34 | # Check if app exists 35 | if [ ! -d "$APP_PATH" ]; then 36 | echo "Error: App not found at: $APP_PATH" 37 | exit 1 38 | fi 39 | 40 | # Get app name and base name 41 | APP_NAME="$(basename "$APP_PATH")" 42 | BASE_NAME="${APP_NAME%.app}" 43 | 44 | # Get directory where app is located 45 | APP_DIR="$(dirname "$APP_PATH")" 46 | 47 | # Set output paths 48 | OUTPUT_DMG="${APP_DIR}/${BASE_NAME}.dmg" 49 | VOLUME_NAME="Install ${BASE_NAME}" 50 | STAGING_DIR="${APP_DIR}/dmg_staging" 51 | 52 | # Colors 53 | GREEN='\033[0;32m' 54 | YELLOW='\033[1;33m' 55 | RED='\033[0;31m' 56 | NC='\033[0m' 57 | 58 | echo -e "${GREEN}Creating DMG (simple approach)...${NC}" 59 | echo -e "${YELLOW} App: $APP_PATH${NC}" 60 | echo -e "${YELLOW} Output: $OUTPUT_DMG${NC}" 61 | 62 | # Clean up 63 | rm -rf "$STAGING_DIR" 64 | rm -f "$OUTPUT_DMG" 65 | rm -f "${APP_DIR}/temp.dmg" 66 | 67 | # Create staging directory 68 | echo -e "${GREEN}Step 1: Creating staging folder...${NC}" 69 | mkdir -p "$STAGING_DIR" 70 | 71 | # Copy app to staging 72 | echo -e "${GREEN}Step 2: Copying app...${NC}" 73 | cp -R "$APP_PATH" "$STAGING_DIR/" 74 | 75 | # Bundle the Gatekeeper fix script inside the app 76 | echo -e "${GREEN}Step 3: Bundling Gatekeeper fix script...${NC}" 77 | RESOURCES_DIR="$STAGING_DIR/$APP_NAME/Contents/Resources" 78 | if [ -d "$RESOURCES_DIR" ]; then 79 | cp "$SCRIPT_DIR/fix_gatekeeper.sh" "$RESOURCES_DIR/" 80 | chmod +x "$RESOURCES_DIR/fix_gatekeeper.sh" 81 | echo -e "${YELLOW} ✓ fix_gatekeeper.sh bundled in app${NC}" 82 | else 83 | echo -e "${YELLOW} ⚠ Resources directory not found, skipping fix script${NC}" 84 | fi 85 | 86 | # Copy installation instructions to DMG 87 | echo -e "${GREEN}Step 4: Adding installation instructions...${NC}" 88 | cp "$SCRIPT_DIR/dmg_install_instructions.txt" "$STAGING_DIR/📖 How to Install.txt" 89 | 90 | # Create Applications symlink 91 | echo -e "${GREEN}Step 5: Creating Applications link...${NC}" 92 | ln -s /Applications "$STAGING_DIR/Applications" 93 | 94 | # Create DMG from staging folder 95 | echo -e "${GREEN}Step 6: Creating DMG...${NC}" 96 | hdiutil create -volname "$VOLUME_NAME" \ 97 | -srcfolder "$STAGING_DIR" \ 98 | -ov -format UDZO \ 99 | "$OUTPUT_DMG" 100 | 101 | # Clean up staging 102 | rm -rf "$STAGING_DIR" 103 | 104 | if [ -f "$OUTPUT_DMG" ]; then 105 | SIZE=$(du -h "$OUTPUT_DMG" | awk '{print $1}') 106 | echo "" 107 | echo -e "${GREEN}✓ DMG created successfully!${NC}" 108 | echo -e " File: ${OUTPUT_DMG}" 109 | echo -e " Size: ${SIZE}" 110 | else 111 | echo -e "${RED}Error: Failed to create DMG${NC}" 112 | exit 1 113 | fi 114 | -------------------------------------------------------------------------------- /src/common/src/encode_parameter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "../include/encode_parameter.h" 19 | 20 | EncodeParameter::EncodeParameter() { 21 | videoCodec = ""; 22 | audioCodec = ""; 23 | 24 | videoBitRate = 0; 25 | audioBitRate = 0; 26 | 27 | qscale = -1; 28 | pixelFormat = ""; 29 | width = 0; 30 | height = 0; 31 | 32 | preset = ""; 33 | 34 | startTime = -1.0; 35 | endTime = -1.0; 36 | 37 | available = false; 38 | } 39 | 40 | void EncodeParameter::set_qscale(int q) { 41 | if (q < 0) { 42 | return; 43 | } 44 | qscale = q; 45 | available = true; 46 | } 47 | 48 | void EncodeParameter::set_pixel_format(std::string p) { 49 | pixelFormat = p; 50 | available = true; 51 | } 52 | 53 | void EncodeParameter::set_width(uint16_t w) { 54 | width = w; 55 | available = true; 56 | } 57 | 58 | void EncodeParameter::set_height(uint16_t h) { 59 | height = h; 60 | available = true; 61 | } 62 | 63 | int EncodeParameter::get_qscale() { return qscale; } 64 | 65 | std::string EncodeParameter::get_pixel_format() { return pixelFormat; } 66 | 67 | uint16_t EncodeParameter::get_width() { return width; } 68 | 69 | uint16_t EncodeParameter::get_height() { return height; } 70 | 71 | bool EncodeParameter::get_available() { return available; } 72 | 73 | void EncodeParameter::set_video_codec_name(std::string vc) { 74 | if (vc == "") { 75 | return; 76 | } 77 | videoCodec = vc; 78 | available = true; 79 | } 80 | 81 | void EncodeParameter::set_audio_codec_name(std::string ac) { 82 | if (ac == "") { 83 | return; 84 | } 85 | audioCodec = ac; 86 | available = true; 87 | } 88 | 89 | void EncodeParameter::set_video_bit_rate(int64_t vbr) { 90 | if (vbr == 0) { 91 | return; 92 | } 93 | videoBitRate = vbr; 94 | available = true; 95 | } 96 | 97 | void EncodeParameter::set_audio_bit_rate(int64_t abr) { 98 | if (abr == 0) { 99 | return; 100 | } 101 | audioBitRate = abr; 102 | available = true; 103 | } 104 | 105 | std::string EncodeParameter::get_video_codec_name() { return videoCodec; } 106 | 107 | std::string EncodeParameter::get_audio_codec_name() { return audioCodec; } 108 | 109 | int64_t EncodeParameter::get_video_bit_rate() { return videoBitRate; } 110 | 111 | int64_t EncodeParameter::get_audio_bit_rate() { return audioBitRate; } 112 | 113 | void EncodeParameter::set_preset(std::string p) { 114 | preset = p; 115 | available = true; 116 | } 117 | 118 | std::string EncodeParameter::get_preset() { return preset; } 119 | 120 | void EncodeParameter::set_start_time(double t) { 121 | startTime = t; 122 | available = true; 123 | } 124 | 125 | void EncodeParameter::set_end_time(double t) { 126 | endTime = t; 127 | available = true; 128 | } 129 | 130 | double EncodeParameter::get_start_time() { return startTime; } 131 | 132 | double EncodeParameter::get_end_time() { return endTime; } 133 | 134 | EncodeParameter::~EncodeParameter() {} 135 | -------------------------------------------------------------------------------- /src/builder/include/transcode_page.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef TRANSCODE_PAGE_H 19 | #define TRANSCODE_PAGE_H 20 | 21 | #include "base_page.h" 22 | #include "converter_runner.h" 23 | #include "file_selector_widget.h" 24 | #include "progress_widget.h" 25 | #include "batch_output_widget.h" 26 | #include "batch_mode_helper.h" 27 | #include "resolution_widget.h" 28 | #include "pixel_format_widget.h" 29 | #include "bitrate_widget.h" 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | class EncodeParameter; 38 | class ProcessParameter; 39 | 40 | class TranscodePage : public BasePage { 41 | Q_OBJECT 42 | 43 | public: 44 | explicit TranscodePage(QWidget *parent = nullptr); 45 | ~TranscodePage() override; 46 | 47 | void OnPageActivated() override; 48 | void OnPageDeactivated() override; 49 | QString GetPageTitle() const override { return "Transcode"; } 50 | void RetranslateUi() override; 51 | 52 | protected: 53 | void OnInputFileChanged(const QString &newPath) override; 54 | void OnOutputPathUpdate() override; 55 | 56 | private slots: 57 | void OnInputFileSelected(const QString &filePath); 58 | void OnOutputFileSelected(const QString &filePath); 59 | void OnFormatChanged(int index); 60 | void OnTranscodeClicked(); 61 | void OnVideoCodecChanged(int index); 62 | void OnTranscodeFinished(bool success); 63 | 64 | signals: 65 | void TranscodeComplete(bool success); 66 | 67 | private: 68 | void SetupUI(); 69 | void UpdateOutputPath(); 70 | QString GetFileExtension(const QString &filePath); 71 | EncodeParameter* CreateEncodeParameter(); 72 | 73 | // Input/Output section 74 | FileSelectorWidget *inputFileSelector; 75 | FileSelectorWidget *outputFileSelector; 76 | 77 | // Batch output widget (shown when batch files selected) 78 | BatchOutputWidget *batchOutputWidget; 79 | 80 | // Video settings section 81 | QGroupBox *videoGroupBox; 82 | QLabel *videoCodecLabel; 83 | QComboBox *videoCodecComboBox; 84 | QLabel *videoBitrateLabel; 85 | BitrateWidget *videoBitrateWidget; 86 | QLabel *resolutionLabel; 87 | ResolutionWidget *resolutionWidget; 88 | QLabel *pixelFormatLabel; 89 | PixelFormatWidget *pixelFormatWidget; 90 | 91 | // Audio settings section 92 | QGroupBox *audioGroupBox; 93 | QLabel *audioCodecLabel; 94 | QComboBox *audioCodecComboBox; 95 | BitrateWidget *audioBitrateWidget; 96 | 97 | // Preset section 98 | QGroupBox *presetGroupBox; 99 | QLabel *presetLabel; 100 | QComboBox *presetComboBox; 101 | 102 | // Format section 103 | QGroupBox *formatGroupBox; 104 | QLabel *formatLabel; 105 | QComboBox *formatComboBox; 106 | 107 | // Progress section 108 | ProgressWidget *progressWidget; 109 | 110 | // Action section 111 | QPushButton *transcodeButton; 112 | 113 | // Conversion runner 114 | ConverterRunner *converterRunner; 115 | 116 | // Batch mode helper 117 | BatchModeHelper *batchModeHelper; 118 | }; 119 | 120 | #endif // TRANSCODE_PAGE_H 121 | -------------------------------------------------------------------------------- /src/transcoder/include/transcoder.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * This file is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | */ 15 | 16 | #ifndef TRANSCODER_H 17 | #define TRANSCODER_H 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "../../common/include/encode_parameter.h" 24 | #include "../../common/include/process_parameter.h" 25 | #include "../../common/include/stream_context.h" 26 | 27 | class Transcoder { 28 | public: 29 | Transcoder(ProcessParameter *process_parameter, 30 | EncodeParameter *encode_parameter) 31 | : process_parameter(process_parameter), encode_parameter(encode_parameter) { 32 | first_frame_time = std::chrono::system_clock::time_point{}; 33 | last_ui_update = std::chrono::system_clock::now(); 34 | } 35 | 36 | virtual ~Transcoder() = default; 37 | 38 | virtual bool transcode(std::string input_path, std::string output_path) = 0; 39 | 40 | void send_process_parameter(int64_t frame_number, int64_t frame_total_number) { 41 | if (first_frame_time == std::chrono::system_clock::time_point{}) { 42 | first_frame_time = std::chrono::system_clock::now(); 43 | } 44 | 45 | double fraction = 0.0; 46 | if (frame_total_number > 0) { 47 | fraction = static_cast(frame_number) / static_cast(frame_total_number); 48 | if (fraction > 1.0) fraction = 1.0; // clamp just in case 49 | if (fraction < 0.0) fraction = 0.0; 50 | } 51 | process_number = static_cast(fraction * 100.0); 52 | 53 | auto now = std::chrono::system_clock::now(); 54 | 55 | auto elapsed_ms = std::chrono::duration_cast( 56 | now - first_frame_time) 57 | .count(); 58 | 59 | if (frame_number > 0 && frame_total_number > 0) { 60 | double elapsed_ms_d = static_cast(elapsed_ms); 61 | remain_seconds = (elapsed_ms / fraction - elapsed_ms_d) / 1000.0; 62 | } 63 | 64 | // Only update UI if enough time has passed (100ms) 65 | auto time_since_last_ui_update = 66 | std::chrono::duration_cast( 67 | now - last_ui_update) 68 | .count(); 69 | if (time_since_last_ui_update >= 100) { 70 | process_parameter->set_process_number(process_number); 71 | if (frame_number > 0 && frame_total_number > 0) { 72 | process_parameter->set_time_required(remain_seconds); 73 | } 74 | last_ui_update = now; 75 | } 76 | 77 | std::cout << "Process Number (percentage): " << process_number << "%\t" 78 | << "Elapsed Time (milliseconds): " << elapsed_ms << "\t" 79 | << "Estimated Rest Time (seconds): " << remain_seconds 80 | << std::endl; 81 | } 82 | 83 | ProcessParameter *process_parameter = NULL; 84 | EncodeParameter *encode_parameter = NULL; 85 | 86 | int64_t frame_number = 0; 87 | int64_t frame_total_number = 0; 88 | int process_number = 0; 89 | double remain_seconds = 0; 90 | 91 | std::chrono::system_clock::time_point first_frame_time; 92 | std::chrono::system_clock::time_point 93 | last_ui_update; // Track last UI update time 94 | 95 | }; 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /src/engine/src/converter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "../include/converter.h" 19 | 20 | #if defined(ENABLE_BMF) 21 | #include "../../transcoder/include/transcoder_bmf.h" 22 | #endif 23 | #if defined(ENABLE_FFMPEG) 24 | #include "../../transcoder/include/transcoder_ffmpeg.h" 25 | #endif 26 | #if defined(ENABLE_FFTOOL) 27 | #include "../../transcoder/include/transcoder_fftool.h" 28 | #endif 29 | 30 | Converter::Converter() {} 31 | /* Receive pointers from widget */ 32 | Converter::Converter(ProcessParameter *processParamter, 33 | EncodeParameter *encodeParamter) 34 | : processParameter(processParamter), encodeParameter(encodeParamter) { 35 | // #if defined(USE_BMF) 36 | // transcoder = new TranscoderBMF(this->processParameter, 37 | // this->encodeParameter); 38 | // #elif defined(USE_FFMPEG) 39 | // transcoder = new TranscoderFFmpeg(this->processParameter, 40 | // this->encodeParameter); 41 | // #elif defined(USE_FFTOOL) 42 | // transcoder = new TranscoderFFTool(this->processParameter, 43 | // this->encodeParameter); 44 | // #endif 45 | 46 | // Default transcoder 47 | #if defined(ENABLE_FFMPEG) 48 | transcoder = 49 | new TranscoderFFmpeg(this->processParameter, this->encodeParameter); 50 | #endif 51 | 52 | this->encodeParameter = encodeParamter; 53 | } 54 | 55 | bool Converter::set_transcoder(std::string transcoderName) { 56 | if (transcoder) { 57 | delete transcoder; 58 | transcoder = NULL; 59 | } 60 | 61 | if (transcoder == NULL) { 62 | if (transcoderName == "FFMPEG") { 63 | #if defined(ENABLE_FFMPEG) 64 | transcoder = new TranscoderFFmpeg(this->processParameter, 65 | this->encodeParameter); 66 | #endif 67 | std::cout << "Set FFmpeg Transcoder!" << std::endl; 68 | } else if (transcoderName == "BMF") { 69 | #if defined(ENABLE_BMF) 70 | transcoder = new TranscoderBMF(this->processParameter, 71 | this->encodeParameter); 72 | std::cout << "Set BMF Transcoder!" << std::endl; 73 | #endif 74 | } else if (transcoderName == "FFTOOL") { 75 | #if defined(ENABLE_FFTOOL) 76 | transcoder = new TranscoderFFTool(this->processParameter, 77 | this->encodeParameter); 78 | std::cout << "Set FFTool Transcoder!" << std::endl; 79 | #endif 80 | } else { 81 | std::cout << "Wrong Transcoder Name!" << std::endl; 82 | return false; 83 | } 84 | } else { 85 | std::cout << "Init transcoder failed!" << std::endl; 86 | return false; 87 | } 88 | return true; 89 | } 90 | 91 | bool Converter::convert_format(const std::string &src, const std::string &dst) { 92 | if (encodeParameter->get_video_codec_name() == "") { 93 | copyVideo = true; 94 | } else { 95 | copyVideo = false; 96 | } 97 | 98 | if (encodeParameter->get_audio_codec_name() == "") { 99 | copyAudio = true; 100 | } else { 101 | copyAudio = false; 102 | } 103 | 104 | return transcoder->transcode(src, dst); 105 | } 106 | 107 | Converter::~Converter() { 108 | if (transcoder) { 109 | delete transcoder; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/builder/include/cut_video_page.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef CUT_VIDEO_PAGE_H 19 | #define CUT_VIDEO_PAGE_H 20 | 21 | #include "base_page.h" 22 | #include "converter_runner.h" 23 | #include "file_selector_widget.h" 24 | #include "progress_widget.h" 25 | #include "simple_video_player.h" 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | class EncodeParameter; 34 | class ProcessParameter; 35 | 36 | class CutVideoPage : public BasePage { 37 | Q_OBJECT 38 | 39 | public: 40 | explicit CutVideoPage(QWidget *parent = nullptr); 41 | ~CutVideoPage() override; 42 | 43 | void OnPageActivated() override; 44 | void OnPageDeactivated() override; 45 | QString GetPageTitle() const override { return "Cut Video"; } 46 | void RetranslateUi() override; 47 | 48 | protected: 49 | void OnInputFileChanged(const QString &newPath) override; 50 | void OnOutputPathUpdate() override; 51 | 52 | private slots: 53 | void OnInputFileSelected(const QString &filePath); 54 | void OnOutputFileSelected(const QString &filePath); 55 | void OnCutClicked(); 56 | void OnPlayPauseClicked(); 57 | void OnVideoPlayerPositionChanged(qint64 position); 58 | void OnVideoPlayerDurationChanged(qint64 duration); 59 | void OnTimelineSliderPressed(); 60 | void OnTimelineSliderReleased(); 61 | void OnTimelineSliderMoved(int position); 62 | void OnStartTimeChanged(const QTime &time); 63 | void OnEndTimeChanged(const QTime &time); 64 | void OnSetStartClicked(); 65 | void OnSetEndClicked(); 66 | void OnCutFinished(bool success); 67 | 68 | signals: 69 | void CutComplete(bool success); 70 | 71 | private: 72 | void SetupUI(); 73 | void UpdateOutputPath(); 74 | void LoadVideo(const QString &filePath); 75 | void UpdateTimeLabels(); 76 | void UpdateDurationLabel(); 77 | QString FormatTime(qint64 milliseconds); 78 | 79 | // Input/Output section 80 | FileSelectorWidget *inputFileSelector; 81 | FileSelectorWidget *outputFileSelector; 82 | 83 | // Media Duration section 84 | QGroupBox *durationGroupBox; 85 | QLabel *totalDurationLabel; 86 | QLabel *totalDurationValueLabel; 87 | 88 | // Video Player section 89 | QGroupBox *playerGroupBox; 90 | SimpleVideoPlayer *videoPlayer; 91 | QPushButton *playPauseButton; 92 | QSlider *timelineSlider; 93 | QLabel *currentTimeLabel; 94 | QLabel *endTimeDisplayLabel; 95 | bool isSliderPressed; 96 | 97 | // Time Selection section 98 | QGroupBox *timeSelectionGroupBox; 99 | QLabel *startTimeLabel; 100 | QTimeEdit *startTimeEdit; 101 | QPushButton *setStartButton; 102 | QLabel *endTimeLabel; 103 | QTimeEdit *endTimeEdit; 104 | QPushButton *setEndButton; 105 | QLabel *cutDurationLabel; 106 | QLabel *cutDurationValueLabel; 107 | 108 | // Progress section 109 | ProgressWidget *progressWidget; 110 | 111 | // Action section 112 | QPushButton *cutButton; 113 | 114 | // Conversion runner 115 | ConverterRunner *converterRunner; 116 | 117 | // State 118 | qint64 videoDuration; // in milliseconds 119 | qint64 startTime; // in milliseconds 120 | qint64 endTime; // in milliseconds 121 | }; 122 | 123 | #endif // CUT_VIDEO_PAGE_H 124 | -------------------------------------------------------------------------------- /src/builder/src/batch_item.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "../include/batch_item.h" 19 | 20 | BatchItem::BatchItem() 21 | : status(BatchItemStatus::Waiting), 22 | encodeParameter(nullptr), 23 | progress(0.0) { 24 | createdTime = QDateTime::currentDateTime(); 25 | } 26 | 27 | BatchItem::BatchItem(const QString &inputPath, const QString &outputPath) 28 | : inputPath(inputPath), 29 | outputPath(outputPath), 30 | status(BatchItemStatus::Waiting), 31 | encodeParameter(nullptr), 32 | progress(0.0) { 33 | createdTime = QDateTime::currentDateTime(); 34 | } 35 | 36 | BatchItem::~BatchItem() { 37 | // Note: encodeParameter is managed externally, don't delete here 38 | } 39 | 40 | QString BatchItem::GetInputPath() const { 41 | return inputPath; 42 | } 43 | 44 | QString BatchItem::GetOutputPath() const { 45 | return outputPath; 46 | } 47 | 48 | BatchItemStatus BatchItem::GetStatus() const { 49 | return status; 50 | } 51 | 52 | QString BatchItem::GetStatusString() const { 53 | switch (status) { 54 | case BatchItemStatus::Waiting: 55 | return "Waiting"; 56 | case BatchItemStatus::Processing: 57 | return "Processing"; 58 | case BatchItemStatus::Finished: 59 | return "Finished"; 60 | case BatchItemStatus::Failed: 61 | return "Failed"; 62 | default: 63 | return "Unknown"; 64 | } 65 | } 66 | 67 | QString BatchItem::GetErrorMessage() const { 68 | return errorMessage; 69 | } 70 | 71 | EncodeParameter* BatchItem::GetEncodeParameter() const { 72 | return encodeParameter; 73 | } 74 | 75 | QString BatchItem::GetTranscoderName() const { 76 | return transcoderName.isEmpty() ? "FFMPEG" : transcoderName; 77 | } 78 | 79 | QDateTime BatchItem::GetCreatedTime() const { 80 | return createdTime; 81 | } 82 | 83 | QDateTime BatchItem::GetStartedTime() const { 84 | return startedTime; 85 | } 86 | 87 | QDateTime BatchItem::GetFinishedTime() const { 88 | return finishedTime; 89 | } 90 | 91 | double BatchItem::GetProgress() const { 92 | return progress; 93 | } 94 | 95 | void BatchItem::SetInputPath(const QString &path) { 96 | inputPath = path; 97 | } 98 | 99 | void BatchItem::SetOutputPath(const QString &path) { 100 | outputPath = path; 101 | } 102 | 103 | void BatchItem::SetStatus(BatchItemStatus newStatus) { 104 | status = newStatus; 105 | } 106 | 107 | void BatchItem::SetErrorMessage(const QString &message) { 108 | errorMessage = message; 109 | } 110 | 111 | void BatchItem::SetEncodeParameter(EncodeParameter *param) { 112 | encodeParameter = param; 113 | } 114 | 115 | void BatchItem::SetTranscoderName(const QString &name) { 116 | transcoderName = name; 117 | } 118 | 119 | void BatchItem::SetProgress(double newProgress) { 120 | progress = newProgress; 121 | } 122 | 123 | void BatchItem::MarkAsProcessing() { 124 | status = BatchItemStatus::Processing; 125 | startedTime = QDateTime::currentDateTime(); 126 | progress = 0.0; 127 | } 128 | 129 | void BatchItem::MarkAsFinished() { 130 | status = BatchItemStatus::Finished; 131 | finishedTime = QDateTime::currentDateTime(); 132 | progress = 100.0; 133 | } 134 | 135 | void BatchItem::MarkAsFailed(const QString &errorMsg) { 136 | status = BatchItemStatus::Failed; 137 | finishedTime = QDateTime::currentDateTime(); 138 | errorMessage = errorMsg; 139 | } 140 | -------------------------------------------------------------------------------- /src/component/src/codec_selector_widget.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "codec_selector_widget.h" 19 | #include 20 | 21 | CodecSelectorWidget::CodecSelectorWidget(CodecType type, QWidget *parent) 22 | : QWidget(parent), codecType(type), label(nullptr) { 23 | Initialize(type); 24 | } 25 | 26 | CodecSelectorWidget::CodecSelectorWidget(const QString &labelText, CodecType type, QWidget *parent) 27 | : QWidget(parent), codecType(type) { 28 | Initialize(type); 29 | if (label) { 30 | label->setText(labelText); 31 | } 32 | } 33 | 34 | void CodecSelectorWidget::Initialize(CodecType type) { 35 | QHBoxLayout *layout = new QHBoxLayout(this); 36 | layout->setContentsMargins(0, 0, 0, 0); 37 | layout->setSpacing(5); 38 | 39 | // Create combo box 40 | codecComboBox = new QComboBox(this); 41 | PopulateCodecs(type); 42 | 43 | connect(codecComboBox, QOverload::of(&QComboBox::currentIndexChanged), 44 | this, &CodecSelectorWidget::OnCodecChanged); 45 | 46 | layout->addWidget(codecComboBox); 47 | layout->addStretch(); 48 | } 49 | 50 | void CodecSelectorWidget::PopulateCodecs(CodecType type) { 51 | codecComboBox->clear(); 52 | 53 | if (type == VideoCodec) { 54 | codecComboBox->addItems({ 55 | "auto", 56 | "libx264", 57 | "libx265", 58 | "libvpx", 59 | "libvpx-vp9", 60 | "mpeg4", 61 | "copy" 62 | }); 63 | } else { // AudioCodec 64 | codecComboBox->addItems({ 65 | "auto", 66 | "aac", 67 | "libmp3lame", 68 | "libvorbis", 69 | "libopus", 70 | "copy" 71 | }); 72 | } 73 | 74 | codecComboBox->setCurrentText("auto"); 75 | } 76 | 77 | QString CodecSelectorWidget::GetCodec() const { 78 | QString codec = codecComboBox->currentText(); 79 | return (codec == "auto") ? "" : codec; 80 | } 81 | 82 | bool CodecSelectorWidget::IsAuto() const { 83 | return codecComboBox->currentText() == "auto"; 84 | } 85 | 86 | bool CodecSelectorWidget::IsCopy() const { 87 | return codecComboBox->currentText() == "copy"; 88 | } 89 | 90 | void CodecSelectorWidget::SetCodec(const QString &codec) { 91 | if (codec.isEmpty()) { 92 | codecComboBox->setCurrentText("auto"); 93 | } else { 94 | codecComboBox->setCurrentText(codec); 95 | } 96 | } 97 | 98 | void CodecSelectorWidget::SetToAuto() { 99 | codecComboBox->setCurrentText("auto"); 100 | } 101 | 102 | void CodecSelectorWidget::SetCodecList(const QStringList &codecs) { 103 | QString currentCodec = codecComboBox->currentText(); 104 | codecComboBox->clear(); 105 | codecComboBox->addItems(codecs); 106 | 107 | // Try to restore previous selection 108 | int index = codecComboBox->findText(currentCodec); 109 | if (index >= 0) { 110 | codecComboBox->setCurrentIndex(index); 111 | } 112 | } 113 | 114 | void CodecSelectorWidget::SetEnabled(bool enabled) { 115 | codecComboBox->setEnabled(enabled); 116 | if (label) { 117 | label->setEnabled(enabled); 118 | } 119 | } 120 | 121 | void CodecSelectorWidget::RetranslateUi() { 122 | // Codec names are not translated (they are technical identifiers) 123 | // Only the label would be translated, which is handled by the parent page 124 | } 125 | 126 | void CodecSelectorWidget::OnCodecChanged(int index) { 127 | Q_UNUSED(index); 128 | emit CodecChanged(GetCodec()); 129 | } 130 | -------------------------------------------------------------------------------- /src/builder/include/open_converter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef OPEN_CONVERTER_H 19 | #define OPEN_CONVERTER_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #include "../../common/include/encode_parameter.h" 50 | #include "../../common/include/info.h" 51 | #include "../../common/include/process_observer.h" 52 | #include "../../common/include/process_parameter.h" 53 | 54 | QT_BEGIN_NAMESPACE 55 | namespace Ui { 56 | class OpenConverter; 57 | } 58 | QT_END_NAMESPACE 59 | 60 | class EncodeSetting; 61 | class Converter; 62 | class BasePage; 63 | class InfoViewPage; 64 | class CompressPicturePage; 65 | class CreateGifPage; 66 | class ExtractAudioPage; 67 | class RemuxPage; 68 | class TranscodePage; 69 | class SharedData; 70 | class BatchQueueDialog; 71 | 72 | class OpenConverter : public QMainWindow, public ProcessObserver { 73 | Q_OBJECT 74 | 75 | public: 76 | explicit OpenConverter(QWidget *parent = nullptr); 77 | ~OpenConverter(); 78 | 79 | // ProcessObserver interface implementation 80 | void on_process_update(double progress) override; 81 | void on_time_update(double timeRequired) override; 82 | 83 | protected: 84 | void dragEnterEvent(QDragEnterEvent *event) override; 85 | void dropEvent(QDropEvent *event) override; 86 | void changeEvent(QEvent *event) override; 87 | 88 | private slots: 89 | void SlotLanguageChanged(QAction *action); 90 | void SlotTranscoderChanged(QAction *action); 91 | void OnNavigationButtonClicked(int pageIndex); 92 | void OnQueueButtonClicked(); 93 | 94 | private: 95 | Ui::OpenConverter *ui; 96 | QTranslator m_translator; 97 | QString m_currLang; 98 | QString m_langPath; 99 | QString currentInputPath; 100 | QString currentOutputPath; 101 | 102 | Info *info; 103 | EncodeParameter *encodeParameter; 104 | EncodeSetting *encodeSetting; 105 | ProcessParameter *processParameter; 106 | Converter *converter; 107 | QMessageBox *displayResult; 108 | QActionGroup *transcoderGroup; 109 | QActionGroup *languageGroup; 110 | 111 | // Navigation and page management 112 | QButtonGroup *navButtonGroup; 113 | QList pages; 114 | SharedData *sharedData; 115 | BatchQueueDialog *batchQueueDialog; 116 | 117 | void LoadLanguage(const QString &rLanguage); 118 | void HandleConverterResult(bool flag); 119 | void InfoDisplay(QuickInfo *info); 120 | QString FormatBitrate(int64_t bitsPerSec); 121 | QString FormatFrequency(int64_t hertz); 122 | 123 | // Page management methods 124 | void InitializePages(); 125 | void SwitchToPage(int pageIndex); 126 | 127 | public: 128 | // Shared data across pages 129 | SharedData* GetSharedData() const; 130 | 131 | // Get current transcoder name 132 | QString GetCurrentTranscoderName() const; 133 | }; 134 | 135 | #endif // OPEN_CONVERTER_H 136 | -------------------------------------------------------------------------------- /README_ZH.md: -------------------------------------------------------------------------------- 1 | # OpenConverter 2 | 3 | [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=OpenConverterLab_OpenConverter&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=OpenConverterLab_OpenConverter) 4 | 5 | ## 📢 项目介绍 6 | 7 | OpenConverter 是一个基于 **FFmpeg**源代码、**Qt**开源框架、**BMF框架**构建的软件,它提供了一些简单易用的工具,可以方便地转换、编辑和处理音视频文件。 8 | 9 | 这款转换器具有以下主要功能: 10 | 11 | 1. 支持更改音视频编解码器以进行编码(例如libx264,libx265,aac,ac3),支持分辨率缩放和像素格式转换 12 | 2. 支持无编码转换多媒体。 13 | 3. 支持显示多媒体文件中视频和音频流的信息。 14 | 4. 支持图片压缩,可调整格式和质量。 15 | 5. 支持从视频中提取音频。 16 | 6. 支持视频剪切,内置FFmpeg播放器,精确时间选择。 17 | 7. **支持批量处理,队列管理多个文件。** 18 | 8. 支持平滑的进度跟踪和准确的剩余时间显示。 19 | 9. 运行时切换转码内核(FFmpeg、FFTool、BMF) 20 | 10. 提供图形界面和命令行界面(CLI)双重支持。 21 | 22 | 本项目使用[Qt框架](./doc/Qt.md)、FFmpeg库、[BMF框架](https://github.com/BabitMF/bmf)完成开发。 23 | 24 | FFmpeg 开发教程可参考 [Learn FFmpeg the Hard Way](https://github.com/TSGU-OSC/Learn_FFmpeg_the_Hard_Way) 25 | 26 | ## 🌟 功能详解 27 | 28 | 运行 OpenConverter 后,您可以看到并使用以下功能: 29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
39 | 40 | ### 1. 支持更改音视频编解码器以进行编码(例如libx264,libx265,aac,ac3) 41 | 42 | 这款转换器允许用户轻松地更改音视频编解码器。例如,您可以选择使用libx264或libx265编码器,以获得更高的压缩率和更好的视频质量。 43 | 44 | 此外还支持: 45 | - **分辨率缩放**(例如:1920x1080 → 1280x720,4K → 1080p) 46 | - **像素格式转换**(例如:yuv420p、yuv444p、rgb24) 47 | - 视频和音频的比特率控制 48 | - 使用qscale参数进行质量控制 49 | 50 | ### 2. 支持无编码转换多媒体。 51 | 52 | 该转换器还支持无需编码即可转换多媒体文件。这意味着您可以在不改变原始视频和音频流的情况下,直接将文件从一种格式转换为另一种格式。这对于快速转换文件非常有用。 53 | 54 | ### 3. 支持显示多媒体文件中视频和音频流的信息 55 | 56 | 这款转换器可以显示有关视频和音频流的详细信息,包括分辨率、帧率、比特率等。这有助于您了解多媒体文件的属性,以便在转换过程中进行相应的调整。 57 | 58 | ### 4. 图片压缩 59 | 60 | 支持图片压缩,可自定义设置: 61 | - 格式转换(JPEG、PNG、WebP、BMP) 62 | - 分辨率调整 63 | - 质量控制(1-31) 64 | 65 | ### 5. 音频提取 66 | 67 | 从视频中提取音频,支持编码器选择: 68 | - 多种音频格式(MP3、AAC、AC3、FLAC、WAV) 69 | - 比特率控制 70 | 71 | ### 6. 视频剪切 72 | 73 | 精确剪切视频片段: 74 | - 内置FFmpeg视频播放器 75 | - 实时播放和定位 76 | - 精确的开始/结束时间选择 77 | - 支持所有FFmpeg兼容格式 78 | 79 | ### 7. 批量处理 80 | 81 | 高效处理多个文件: 82 | - **可视化文件过滤器管理**,基于标签的界面 83 | - 单独添加文件或扫描整个目录 84 | - 队列管理,进度监控 85 | - 可配置输出目录和文件后缀 86 | - 支持转码、提取音频、压缩图片和创建GIF操作 87 | - 队列中每个文件的实时进度跟踪 88 | 89 | ### 8. 高级进度跟踪和时间估算 90 | 91 | 在转换文件时,该播放器提供: 92 | - 平滑的进度更新,具有UI友好的刷新率 93 | - 使用持续时间平滑的准确剩余时间估算 94 | - 实时进度百分比和持续时间跟踪 95 | - 详细的控制台输出,用于监控转换状态 96 | 97 | ### 9. 运行时切换转码内核(FFmpeg、FFTool、BMF) 98 | 99 | 该软件提供三种不同的转码内核供选择: 100 | - 基于FFmpeg API的内核,用于直接库集成 101 | - FFTool内核,用于命令行工具集成 102 | - 基于BMF框架的内核,用于高级处理 103 | 您还可以根据需求选择性编译这些内核。 104 | 105 | ### 10. 命令行界面(CLI)支持 106 | 107 | 非图形界面模式使用方法: 108 | ```bash 109 | > ./OpenConverter 110 | Usage: ./OpenConverter [options] input_file output_file 111 | Options: 112 | --transcoder TYPE Set transcoder type (FFMPEG, BMF, FFTOOL) 113 | -v, --video-codec CODEC Set video codec (could set copy) 114 | -q, --qscale QSCALE Set qscale for video codec 115 | -a, --audio-codec CODEC Set audio codec (could set copy) 116 | -b:v, --bitrate:video BITRATE Set bitrate for video codec 117 | -b:a, --bitrate:audio BITRATE Set bitrate for audio codec 118 | -pix_fmt PIX_FMT Set pixel format for video 119 | -scale SCALE(w)x(h) Set scale for video (width x height) 120 | -ss START_TIME Set start time for cutting (format: HH:MM:SS or seconds) 121 | -to END_TIME Set end time for cutting (format: HH:MM:SS or seconds) 122 | -t DURATION Set duration for cutting (format: HH:MM:SS or seconds) 123 | -h, --help Show this help message 124 | 125 | Note: Use either -to or -t, not both. If both are specified, -to takes precedence. 126 | ``` 127 | 128 | 使用示例: 129 | ```bash 130 | # 使用FFmpeg内核和H.264编码器转换视频 131 | ./OpenConverter -t FFMPEG -v libx264 input.mp4 output.mp4 132 | 133 | # 使用BMF内核,H.265视频编码器和AAC音频编码器转换视频 134 | ./OpenConverter -t BMF -v libx265 -a aac input.mp4 output.mp4 135 | ``` 136 | 137 | ## 使用指南 138 | 如果在运行过程遇到问题,欢迎查看我们的[使用指南文档](./doc/OpenConverter_Usage.md) 139 | 140 | ## 📖 贡献指南 141 | 142 | 如果你有兴趣贡献项目或发现了错误,请参考我们的 [贡献指南](./CONTRIBUTING.md) 143 | 144 | ## ☘️ 许可证 145 | 146 | OpenConverter 是基于 Apache 2.0 许可证开源的。请在使用前阅读 [LICENSE](./LICENSE) 文件。 147 | -------------------------------------------------------------------------------- /src/component/src/pixel_format_widget.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "pixel_format_widget.h" 19 | #include 20 | 21 | PixelFormatWidget::PixelFormatWidget(FormatType type, QWidget *parent) 22 | : QWidget(parent) { 23 | SetupUI("", type); 24 | } 25 | 26 | PixelFormatWidget::PixelFormatWidget(const QString &labelText, FormatType type, QWidget *parent) 27 | : QWidget(parent) { 28 | SetupUI(labelText, type); 29 | } 30 | 31 | void PixelFormatWidget::SetupUI(const QString &labelText, FormatType type) { 32 | QHBoxLayout *layout = new QHBoxLayout(this); 33 | layout->setContentsMargins(0, 0, 0, 0); 34 | 35 | // Optional label 36 | if (!labelText.isEmpty()) { 37 | label = new QLabel(labelText, this); 38 | layout->addWidget(label); 39 | } else { 40 | label = nullptr; 41 | } 42 | 43 | // Format combo box 44 | formatComboBox = new QComboBox(this); 45 | PopulateFormats(type); 46 | layout->addWidget(formatComboBox); 47 | 48 | // Connect signals 49 | connect(formatComboBox, QOverload::of(&QComboBox::currentIndexChanged), 50 | this, &PixelFormatWidget::OnFormatChanged); 51 | 52 | setLayout(layout); 53 | } 54 | 55 | void PixelFormatWidget::PopulateFormats(FormatType type) { 56 | formatComboBox->clear(); 57 | formatComboBox->addItem("auto"); 58 | 59 | if (type == Video) { 60 | // Common video formats 61 | formatComboBox->addItems({ 62 | "yuv420p", "yuv422p", "yuv444p", 63 | "rgb24", "bgr24" 64 | }); 65 | } else { // Image 66 | // Image formats with more options 67 | formatComboBox->addItems({ 68 | // RGB formats 69 | "rgb24", "rgba", "rgb48be", "rgba64be", 70 | // YUV formats (limited range) 71 | "yuv420p", "yuv422p", "yuv444p", 72 | // YUVJ formats (full range, common for JPEG) 73 | "yuvj420p", "yuvj422p", "yuvj444p", 74 | // Grayscale 75 | "gray", "gray16be", 76 | // Other common formats 77 | "bgr24", "bgra" 78 | }); 79 | } 80 | } 81 | 82 | QString PixelFormatWidget::GetPixelFormat() const { 83 | QString format = formatComboBox->currentText(); 84 | return (format == "auto") ? "" : format; 85 | } 86 | 87 | bool PixelFormatWidget::IsAuto() const { 88 | return formatComboBox->currentText() == "auto"; 89 | } 90 | 91 | void PixelFormatWidget::SetPixelFormat(const QString &format) { 92 | if (format.isEmpty() || format == "auto") { 93 | formatComboBox->setCurrentText("auto"); 94 | } else { 95 | int index = formatComboBox->findText(format); 96 | if (index >= 0) { 97 | formatComboBox->setCurrentIndex(index); 98 | } else { 99 | // Format not in list, add it and select it 100 | formatComboBox->addItem(format); 101 | formatComboBox->setCurrentText(format); 102 | } 103 | } 104 | } 105 | 106 | void PixelFormatWidget::SetToAuto() { 107 | formatComboBox->setCurrentText("auto"); 108 | } 109 | 110 | void PixelFormatWidget::SetEnabled(bool enabled) { 111 | formatComboBox->setEnabled(enabled); 112 | if (label) { 113 | label->setEnabled(enabled); 114 | } 115 | } 116 | 117 | void PixelFormatWidget::RetranslateUi() { 118 | // Pixel format names are not translated (they are FFmpeg identifiers) 119 | // Only the label would be translated, but it's set externally 120 | } 121 | 122 | void PixelFormatWidget::OnFormatChanged(int index) { 123 | Q_UNUSED(index); 124 | emit PixelFormatChanged(GetPixelFormat()); 125 | } 126 | -------------------------------------------------------------------------------- /src/component/src/resolution_widget.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "resolution_widget.h" 19 | #include 20 | 21 | ResolutionWidget::ResolutionWidget(QWidget *parent) 22 | : QWidget(parent) { 23 | SetupUI(""); 24 | } 25 | 26 | ResolutionWidget::ResolutionWidget(const QString &labelText, QWidget *parent) 27 | : QWidget(parent) { 28 | SetupUI(labelText); 29 | } 30 | 31 | void ResolutionWidget::SetupUI(const QString &labelText) { 32 | QHBoxLayout *layout = new QHBoxLayout(this); 33 | layout->setContentsMargins(0, 0, 0, 0); 34 | 35 | // Optional label 36 | if (!labelText.isEmpty()) { 37 | label = new QLabel(labelText, this); 38 | layout->addWidget(label); 39 | } else { 40 | label = nullptr; 41 | } 42 | 43 | // Width spin box 44 | widthSpinBox = new QSpinBox(this); 45 | widthSpinBox->setRange(0, 16384); 46 | widthSpinBox->setValue(0); 47 | widthSpinBox->setSpecialValueText(tr("auto")); 48 | widthSpinBox->setSuffix(tr(" px")); 49 | layout->addWidget(widthSpinBox); 50 | 51 | // "x" label 52 | xLabel = new QLabel(tr("x"), this); 53 | layout->addWidget(xLabel); 54 | 55 | // Height spin box 56 | heightSpinBox = new QSpinBox(this); 57 | heightSpinBox->setRange(0, 16384); 58 | heightSpinBox->setValue(0); 59 | heightSpinBox->setSpecialValueText(tr("auto")); 60 | heightSpinBox->setSuffix(tr(" px")); 61 | layout->addWidget(heightSpinBox); 62 | 63 | // Connect signals 64 | connect(widthSpinBox, QOverload::of(&QSpinBox::valueChanged), 65 | this, &ResolutionWidget::OnWidthChanged); 66 | connect(heightSpinBox, QOverload::of(&QSpinBox::valueChanged), 67 | this, &ResolutionWidget::OnHeightChanged); 68 | 69 | setLayout(layout); 70 | } 71 | 72 | int ResolutionWidget::GetWidth() const { 73 | return widthSpinBox->value(); 74 | } 75 | 76 | int ResolutionWidget::GetHeight() const { 77 | return heightSpinBox->value(); 78 | } 79 | 80 | void ResolutionWidget::SetWidth(int width) { 81 | widthSpinBox->setValue(width); 82 | } 83 | 84 | void ResolutionWidget::SetHeight(int height) { 85 | heightSpinBox->setValue(height); 86 | } 87 | 88 | void ResolutionWidget::SetResolution(int width, int height) { 89 | widthSpinBox->setValue(width); 90 | heightSpinBox->setValue(height); 91 | } 92 | 93 | void ResolutionWidget::SetRange(int min, int max) { 94 | widthSpinBox->setRange(min, max); 95 | heightSpinBox->setRange(min, max); 96 | } 97 | 98 | void ResolutionWidget::SetWidthRange(int min, int max) { 99 | widthSpinBox->setRange(min, max); 100 | } 101 | 102 | void ResolutionWidget::SetHeightRange(int min, int max) { 103 | heightSpinBox->setRange(min, max); 104 | } 105 | 106 | void ResolutionWidget::SetEnabled(bool enabled) { 107 | widthSpinBox->setEnabled(enabled); 108 | heightSpinBox->setEnabled(enabled); 109 | if (label) { 110 | label->setEnabled(enabled); 111 | } 112 | xLabel->setEnabled(enabled); 113 | } 114 | 115 | void ResolutionWidget::RetranslateUi() { 116 | widthSpinBox->setSpecialValueText(tr("auto")); 117 | widthSpinBox->setSuffix(tr(" px")); 118 | heightSpinBox->setSpecialValueText(tr("auto")); 119 | heightSpinBox->setSuffix(tr(" px")); 120 | xLabel->setText(tr("x")); 121 | } 122 | 123 | void ResolutionWidget::OnWidthChanged(int value) { 124 | emit WidthChanged(value); 125 | emit ResolutionChanged(widthSpinBox->value(), heightSpinBox->value()); 126 | } 127 | 128 | void ResolutionWidget::OnHeightChanged(int value) { 129 | emit HeightChanged(value); 130 | emit ResolutionChanged(widthSpinBox->value(), heightSpinBox->value()); 131 | } 132 | -------------------------------------------------------------------------------- /src/component/src/format_selector_widget.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "format_selector_widget.h" 19 | #include 20 | 21 | FormatSelectorWidget::FormatSelectorWidget(FormatType type, bool includeAuto, QWidget *parent) 22 | : QWidget(parent), formatType(type), hasAutoOption(includeAuto), label(nullptr) { 23 | Initialize(type, includeAuto); 24 | } 25 | 26 | FormatSelectorWidget::FormatSelectorWidget(const QString &labelText, FormatType type, bool includeAuto, QWidget *parent) 27 | : QWidget(parent), formatType(type), hasAutoOption(includeAuto) { 28 | Initialize(type, includeAuto); 29 | if (label) { 30 | label->setText(labelText); 31 | } 32 | } 33 | 34 | void FormatSelectorWidget::Initialize(FormatType type, bool includeAuto) { 35 | QHBoxLayout *layout = new QHBoxLayout(this); 36 | layout->setContentsMargins(0, 0, 0, 0); 37 | layout->setSpacing(5); 38 | 39 | // Create combo box 40 | formatComboBox = new QComboBox(this); 41 | PopulateFormats(type, includeAuto); 42 | 43 | connect(formatComboBox, QOverload::of(&QComboBox::currentIndexChanged), 44 | this, &FormatSelectorWidget::OnFormatChanged); 45 | 46 | layout->addWidget(formatComboBox); 47 | layout->addStretch(); 48 | } 49 | 50 | void FormatSelectorWidget::PopulateFormats(FormatType type, bool includeAuto) { 51 | formatComboBox->clear(); 52 | 53 | QStringList formats; 54 | 55 | if (includeAuto) { 56 | formats << "auto"; 57 | } 58 | 59 | if (type == Video) { 60 | formats << "mp4" << "mkv" << "avi" << "mov" << "flv" << "webm" << "ts"; 61 | } else if (type == Audio) { 62 | formats << "mp3" << "aac" << "wav" << "flac" << "ogg" << "m4a"; 63 | } else { // Image 64 | formats << "jpg" << "png" << "bmp" << "webp" << "tiff" << "gif"; 65 | } 66 | 67 | formatComboBox->addItems(formats); 68 | 69 | if (includeAuto) { 70 | formatComboBox->setCurrentText("auto"); 71 | } else { 72 | formatComboBox->setCurrentIndex(0); 73 | } 74 | } 75 | 76 | QString FormatSelectorWidget::GetFormat() const { 77 | QString format = formatComboBox->currentText(); 78 | return (format == "auto") ? "" : format; 79 | } 80 | 81 | bool FormatSelectorWidget::IsAuto() const { 82 | return hasAutoOption && formatComboBox->currentText() == "auto"; 83 | } 84 | 85 | void FormatSelectorWidget::SetFormat(const QString &format) { 86 | if (format.isEmpty() && hasAutoOption) { 87 | formatComboBox->setCurrentText("auto"); 88 | } else { 89 | formatComboBox->setCurrentText(format); 90 | } 91 | } 92 | 93 | void FormatSelectorWidget::SetToAuto() { 94 | if (hasAutoOption) { 95 | formatComboBox->setCurrentText("auto"); 96 | } 97 | } 98 | 99 | void FormatSelectorWidget::SetFormatList(const QStringList &formats) { 100 | QString currentFormat = formatComboBox->currentText(); 101 | formatComboBox->clear(); 102 | formatComboBox->addItems(formats); 103 | 104 | // Try to restore previous selection 105 | int index = formatComboBox->findText(currentFormat); 106 | if (index >= 0) { 107 | formatComboBox->setCurrentIndex(index); 108 | } 109 | } 110 | 111 | void FormatSelectorWidget::SetEnabled(bool enabled) { 112 | formatComboBox->setEnabled(enabled); 113 | if (label) { 114 | label->setEnabled(enabled); 115 | } 116 | } 117 | 118 | void FormatSelectorWidget::RetranslateUi() { 119 | // Format names are not translated (they are technical identifiers) 120 | // Only the label would be translated, which is handled by the parent page 121 | } 122 | 123 | void FormatSelectorWidget::OnFormatChanged(int index) { 124 | Q_UNUSED(index); 125 | emit FormatChanged(GetFormat()); 126 | } 127 | -------------------------------------------------------------------------------- /src/builder/include/batch_mode_helper.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef BATCH_MODE_HELPER_H 19 | #define BATCH_MODE_HELPER_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "file_selector_widget.h" 27 | #include "batch_output_widget.h" 28 | #include "batch_item.h" 29 | #include "../../common/include/encode_parameter.h" 30 | 31 | /** 32 | * @brief Helper class to manage batch mode functionality for pages 33 | * 34 | * This class abstracts the common batch mode logic that's shared across 35 | * multiple pages (TranscodePage, CompressPicturePage, ExtractAudioPage, RemuxPage). 36 | * 37 | * Features: 38 | * - Connects to FileSelectorWidget's BatchFilesSelected signal 39 | * - Shows/hides BatchOutputWidget based on batch mode state 40 | * - Updates action button text (e.g., "Transcode" vs "Add to Queue") 41 | * - Creates batch items with encode parameters 42 | * - Adds items to BatchQueue 43 | * 44 | * Usage: 45 | * BatchModeHelper *helper = new BatchModeHelper( 46 | * inputFileSelector, batchOutputWidget, actionButton, 47 | * "Transcode", "Add to Queue", this); 48 | * 49 | * helper->SetEncodeParameterCreator([this]() { 50 | * return CreateEncodeParameter(); 51 | * }); 52 | * 53 | * connect(actionButton, &QPushButton::clicked, [this, helper]() { 54 | * if (helper->IsBatchMode()) { 55 | * helper->AddToQueue("mp4"); 56 | * } else { 57 | * // Single file mode 58 | * } 59 | * }); 60 | */ 61 | class BatchModeHelper : public QObject { 62 | Q_OBJECT 63 | 64 | public: 65 | /** 66 | * @brief Constructor 67 | * @param inputFileSelector Input file selector widget 68 | * @param batchOutputWidget Batch output widget 69 | * @param actionButton Action button (e.g., Transcode, Extract, etc.) 70 | * @param singleModeText Button text for single file mode 71 | * @param batchModeText Button text for batch mode 72 | * @param parent Parent QObject 73 | */ 74 | explicit BatchModeHelper(FileSelectorWidget *inputFileSelector, 75 | BatchOutputWidget *batchOutputWidget, 76 | QPushButton *actionButton, 77 | const QString &singleModeText, 78 | const QString &batchModeText, 79 | QObject *parent = nullptr); 80 | 81 | /** 82 | * @brief Set the single output widget to hide/show when switching modes 83 | * @param singleOutputWidget Widget to hide in batch mode (e.g., output file selector) 84 | */ 85 | void SetSingleOutputWidget(QWidget *singleOutputWidget); 86 | 87 | /** 88 | * @brief Set the function to create encode parameters 89 | * @param creator Function that creates and returns EncodeParameter* 90 | */ 91 | void SetEncodeParameterCreator(std::function creator); 92 | 93 | /** 94 | * @brief Check if batch mode is active 95 | * @return true if batch mode is active, false otherwise 96 | */ 97 | bool IsBatchMode() const; 98 | 99 | /** 100 | * @brief Add batch files to queue 101 | * @param outputFormat Output format extension (e.g., "mp4", "jpg") 102 | * @return true if items were added successfully, false otherwise 103 | */ 104 | bool AddToQueue(const QString &outputFormat); 105 | 106 | private slots: 107 | void OnBatchFilesSelected(const QStringList &files); 108 | 109 | private: 110 | FileSelectorWidget *inputFileSelector; 111 | BatchOutputWidget *batchOutputWidget; 112 | QPushButton *actionButton; 113 | QString singleModeText; 114 | QString batchModeText; 115 | QWidget *singleOutputWidget; // Optional: widget to hide in batch mode 116 | 117 | std::function encodeParameterCreator; 118 | }; 119 | 120 | #endif // BATCH_MODE_HELPER_H 121 | -------------------------------------------------------------------------------- /src/common/src/info.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * This file is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | */ 15 | 16 | #include "../include/info.h" 17 | 18 | Info::Info() { 19 | avCtx = NULL; 20 | audioCodec = NULL; 21 | audioCtx = NULL; 22 | quickInfo = new QuickInfo(); 23 | init(); 24 | } 25 | 26 | void Info::init() { 27 | // Init QuickInfo 28 | quickInfo->videoIdx = -1; 29 | quickInfo->width = 0; 30 | quickInfo->height = 0; 31 | quickInfo->colorSpace = ""; 32 | quickInfo->videoCodec = ""; 33 | quickInfo->videoBitRate = 0; 34 | quickInfo->frameRate = 0; 35 | 36 | quickInfo->audioIdx = -1; 37 | quickInfo->audioCodec = ""; 38 | quickInfo->audioBitRate = 0; 39 | quickInfo->channels = 0; 40 | quickInfo->sampleFmt = ""; 41 | quickInfo->sampleRate = 0; 42 | 43 | quickInfo->subIdx = 0; 44 | } 45 | 46 | void Info::print_error(const char *msg, int ret) { 47 | av_strerror(ret, errorMsg, sizeof(errorMsg)); 48 | av_log(NULL, AV_LOG_ERROR, " %s: %s \n", msg, errorMsg); 49 | } 50 | 51 | 52 | QuickInfo *Info::get_quick_info() { return quickInfo; } 53 | 54 | void Info::send_info(char *src) { 55 | init(); 56 | int ret = 0; 57 | av_log_set_level(AV_LOG_DEBUG); 58 | ret = avformat_open_input(&avCtx, src, NULL, NULL); 59 | if (ret < 0) { 60 | print_error("open failed", ret); 61 | goto end; 62 | } 63 | ret = avformat_find_stream_info(avCtx, NULL); 64 | if (ret < 0) { 65 | print_error("find stream info failed", ret); 66 | } 67 | // find the video and audio stream from container 68 | quickInfo->videoIdx = 69 | av_find_best_stream(avCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0); 70 | quickInfo->audioIdx = 71 | av_find_best_stream(avCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0); 72 | 73 | if (quickInfo->videoIdx >= 0) { 74 | quickInfo->height = 75 | avCtx->streams[quickInfo->videoIdx]->codecpar->height; 76 | quickInfo->width = avCtx->streams[quickInfo->videoIdx]->codecpar->width; 77 | 78 | if (avCtx->streams[quickInfo->videoIdx]->codecpar->color_space != AVCOL_SPC_UNSPECIFIED) { 79 | quickInfo->colorSpace = av_color_space_name( 80 | avCtx->streams[quickInfo->videoIdx]->codecpar->color_space); 81 | } 82 | if (avCtx->streams[quickInfo->videoIdx]->codecpar->codec_id != AV_CODEC_ID_NONE) 83 | quickInfo->videoCodec = avcodec_get_name( 84 | avCtx->streams[quickInfo->videoIdx]->codecpar->codec_id); 85 | quickInfo->videoBitRate = 86 | avCtx->streams[quickInfo->videoIdx]->codecpar->bit_rate; 87 | quickInfo->frameRate = 88 | avCtx->streams[quickInfo->videoIdx]->r_frame_rate.num / 89 | avCtx->streams[quickInfo->videoIdx]->r_frame_rate.den; 90 | 91 | } else { 92 | av_log(avCtx, AV_LOG_ERROR, "There is no video stream!\n"); 93 | // goto end; 94 | } 95 | 96 | if (quickInfo->audioIdx < 0) { 97 | av_log(avCtx, AV_LOG_ERROR, "There is no audio stream!\n"); 98 | goto end; 99 | } 100 | 101 | audioCtx = avcodec_alloc_context3(NULL); 102 | if (!audioCtx) { 103 | av_log(audioCtx, AV_LOG_ERROR, 104 | "Could not allocate audio codec context\n"); 105 | goto end; 106 | } 107 | // Open the audio codec 108 | if (avcodec_parameters_to_context( 109 | audioCtx, avCtx->streams[quickInfo->audioIdx]->codecpar) < 0) { 110 | av_log(avCtx, AV_LOG_ERROR, "Failed to initialize codec context\n"); 111 | goto end; 112 | } 113 | 114 | quickInfo->audioCodec = avcodec_get_name( 115 | avCtx->streams[quickInfo->audioIdx]->codecpar->codec_id); 116 | quickInfo->audioBitRate = 117 | avCtx->streams[quickInfo->audioIdx]->codecpar->bit_rate; 118 | quickInfo->channels = 119 | avCtx->streams[quickInfo->audioIdx]->codecpar->ch_layout.nb_channels; 120 | quickInfo->sampleFmt = av_get_sample_fmt_name(audioCtx->sample_fmt); 121 | quickInfo->sampleRate = 122 | avCtx->streams[quickInfo->audioIdx]->codecpar->sample_rate; 123 | 124 | end: 125 | avformat_close_input(&avCtx); 126 | 127 | avcodec_free_context(&audioCtx); 128 | } 129 | 130 | Info::~Info() { 131 | delete quickInfo; 132 | } 133 | -------------------------------------------------------------------------------- /src/builder/src/batch_queue.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "../include/batch_queue.h" 19 | 20 | BatchQueue *BatchQueue::instance = nullptr; 21 | QMutex BatchQueue::instanceMutex; 22 | 23 | BatchQueue::BatchQueue() : QObject(nullptr) { 24 | } 25 | 26 | BatchQueue::~BatchQueue() { 27 | Clear(); 28 | } 29 | 30 | BatchQueue* BatchQueue::Instance() { 31 | if (instance == nullptr) { 32 | QMutexLocker locker(&instanceMutex); 33 | if (instance == nullptr) { 34 | instance = new BatchQueue(); 35 | } 36 | } 37 | return instance; 38 | } 39 | 40 | void BatchQueue::AddItem(BatchItem *item) { 41 | if (!item) return; 42 | 43 | QMutexLocker locker(&queueMutex); 44 | items.append(item); 45 | int index = items.size() - 1; 46 | emit ItemAdded(index); 47 | } 48 | 49 | void BatchQueue::AddItems(const QList &newItems) { 50 | if (newItems.isEmpty()) return; 51 | 52 | QMutexLocker locker(&queueMutex); 53 | for (BatchItem *item : newItems) { 54 | if (item) { 55 | items.append(item); 56 | int index = items.size() - 1; 57 | emit ItemAdded(index); 58 | } 59 | } 60 | } 61 | 62 | void BatchQueue::RemoveItem(int index) { 63 | QMutexLocker locker(&queueMutex); 64 | if (index >= 0 && index < items.size()) { 65 | BatchItem *item = items.takeAt(index); 66 | delete item; 67 | emit ItemRemoved(index); 68 | } 69 | } 70 | 71 | void BatchQueue::Clear() { 72 | QMutexLocker locker(&queueMutex); 73 | qDeleteAll(items); 74 | items.clear(); 75 | emit QueueCleared(); 76 | } 77 | 78 | int BatchQueue::GetCount() const { 79 | QMutexLocker locker(&queueMutex); 80 | return items.size(); 81 | } 82 | 83 | int BatchQueue::GetWaitingCount() const { 84 | QMutexLocker locker(&queueMutex); 85 | int count = 0; 86 | for (const BatchItem *item : items) { 87 | if (item->GetStatus() == BatchItemStatus::Waiting) { 88 | count++; 89 | } 90 | } 91 | return count; 92 | } 93 | 94 | int BatchQueue::GetProcessingCount() const { 95 | QMutexLocker locker(&queueMutex); 96 | int count = 0; 97 | for (const BatchItem *item : items) { 98 | if (item->GetStatus() == BatchItemStatus::Processing) { 99 | count++; 100 | } 101 | } 102 | return count; 103 | } 104 | 105 | int BatchQueue::GetFinishedCount() const { 106 | QMutexLocker locker(&queueMutex); 107 | int count = 0; 108 | for (const BatchItem *item : items) { 109 | if (item->GetStatus() == BatchItemStatus::Finished) { 110 | count++; 111 | } 112 | } 113 | return count; 114 | } 115 | 116 | int BatchQueue::GetFailedCount() const { 117 | QMutexLocker locker(&queueMutex); 118 | int count = 0; 119 | for (const BatchItem *item : items) { 120 | if (item->GetStatus() == BatchItemStatus::Failed) { 121 | count++; 122 | } 123 | } 124 | return count; 125 | } 126 | 127 | BatchItem* BatchQueue::GetItem(int index) const { 128 | QMutexLocker locker(&queueMutex); 129 | if (index >= 0 && index < items.size()) { 130 | return items.at(index); 131 | } 132 | return nullptr; 133 | } 134 | 135 | QList BatchQueue::GetAllItems() const { 136 | QMutexLocker locker(&queueMutex); 137 | return items; 138 | } 139 | 140 | BatchItem* BatchQueue::GetNextWaitingItem() const { 141 | QMutexLocker locker(&queueMutex); 142 | for (BatchItem *item : items) { 143 | if (item->GetStatus() == BatchItemStatus::Waiting) { 144 | return item; 145 | } 146 | } 147 | return nullptr; 148 | } 149 | 150 | bool BatchQueue::IsEmpty() const { 151 | QMutexLocker locker(&queueMutex); 152 | return items.isEmpty(); 153 | } 154 | 155 | bool BatchQueue::HasWaitingItems() const { 156 | return GetWaitingCount() > 0; 157 | } 158 | 159 | bool BatchQueue::IsProcessing() const { 160 | return GetProcessingCount() > 0; 161 | } 162 | 163 | int BatchQueue::GetItemIndex(BatchItem *item) const { 164 | QMutexLocker locker(&queueMutex); 165 | return items.indexOf(item); 166 | } 167 | 168 | void BatchQueue::NotifyItemStatusChanged(int index, BatchItemStatus status) { 169 | emit ItemStatusChanged(index, status); 170 | } 171 | 172 | void BatchQueue::NotifyItemProgressChanged(int index, double progress) { 173 | emit ItemProgressChanged(index, progress); 174 | } 175 | -------------------------------------------------------------------------------- /src/builder/src/batch_mode_helper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "../include/batch_mode_helper.h" 19 | #include "../include/batch_queue.h" 20 | #include "../include/transcoder_helper.h" 21 | #include 22 | 23 | BatchModeHelper::BatchModeHelper(FileSelectorWidget *inputFileSelector, 24 | BatchOutputWidget *batchOutputWidget, 25 | QPushButton *actionButton, 26 | const QString &singleModeText, 27 | const QString &batchModeText, 28 | QObject *parent) 29 | : QObject(parent), 30 | inputFileSelector(inputFileSelector), 31 | batchOutputWidget(batchOutputWidget), 32 | actionButton(actionButton), 33 | singleModeText(singleModeText), 34 | batchModeText(batchModeText), 35 | singleOutputWidget(nullptr), 36 | encodeParameterCreator(nullptr) { 37 | 38 | // Connect to batch files selected signal 39 | connect(inputFileSelector, &FileSelectorWidget::BatchFilesSelected, 40 | this, &BatchModeHelper::OnBatchFilesSelected); 41 | } 42 | 43 | void BatchModeHelper::SetSingleOutputWidget(QWidget *widget) { 44 | singleOutputWidget = widget; 45 | } 46 | 47 | void BatchModeHelper::SetEncodeParameterCreator(std::function creator) { 48 | encodeParameterCreator = creator; 49 | } 50 | 51 | bool BatchModeHelper::IsBatchMode() const { 52 | return inputFileSelector->IsBatchMode(); 53 | } 54 | 55 | bool BatchModeHelper::AddToQueue(const QString &outputFormat) { 56 | if (!IsBatchMode()) { 57 | return false; 58 | } 59 | 60 | QStringList inputFiles = inputFileSelector->GetBatchFiles(); 61 | if (inputFiles.isEmpty()) { 62 | QMessageBox::warning(qobject_cast(parent()), QObject::tr("No Files"), 63 | QObject::tr("Please select files to process.")); 64 | return false; 65 | } 66 | 67 | if (!encodeParameterCreator) { 68 | QMessageBox::critical(qobject_cast(parent()), QObject::tr("Error"), 69 | QObject::tr("Encode parameter creator not set.")); 70 | return false; 71 | } 72 | 73 | // Get current transcoder name from main window 74 | QWidget *parentWidget = qobject_cast(parent()); 75 | QString transcoderName = TranscoderHelper::GetCurrentTranscoderName(parentWidget); 76 | 77 | // Create batch items 78 | QList items; 79 | for (const QString &inputFile : inputFiles) { 80 | // Generate output path 81 | QString outputPath = batchOutputWidget->GenerateOutputPath(inputFile, outputFormat); 82 | 83 | // Create batch item 84 | BatchItem *item = new BatchItem(inputFile, outputPath); 85 | 86 | // Create encode parameter for this item 87 | EncodeParameter *encodeParam = encodeParameterCreator(); 88 | item->SetEncodeParameter(encodeParam); 89 | 90 | // Set transcoder name 91 | item->SetTranscoderName(transcoderName); 92 | 93 | items.append(item); 94 | } 95 | 96 | // Add items to queue 97 | BatchQueue::Instance()->AddItems(items); 98 | 99 | // Show confirmation 100 | QMessageBox::information(qobject_cast(parent()), QObject::tr("Added to Queue"), 101 | QObject::tr("Added %1 file(s) to the batch queue.").arg(items.count())); 102 | 103 | // Clear batch files and reset UI 104 | inputFileSelector->ClearBatchFiles(); 105 | batchOutputWidget->setVisible(false); 106 | if (singleOutputWidget) { 107 | singleOutputWidget->setVisible(true); 108 | } 109 | actionButton->setText(singleModeText); 110 | actionButton->setEnabled(false); 111 | 112 | return true; 113 | } 114 | 115 | void BatchModeHelper::OnBatchFilesSelected(const QStringList &files) { 116 | if (files.isEmpty()) { 117 | // Batch mode disabled - show single output widget, hide batch output widget 118 | batchOutputWidget->setVisible(false); 119 | if (singleOutputWidget) { 120 | singleOutputWidget->setVisible(true); 121 | } 122 | actionButton->setText(singleModeText); 123 | actionButton->setEnabled(!inputFileSelector->GetFilePath().isEmpty()); 124 | } else { 125 | // Batch mode enabled - hide single output widget, show batch output widget 126 | if (singleOutputWidget) { 127 | singleOutputWidget->setVisible(false); 128 | } 129 | batchOutputWidget->setVisible(true); 130 | actionButton->setText(batchModeText); 131 | actionButton->setEnabled(true); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/builder/include/converter_runner.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef CONVERTER_RUNNER_H 19 | #define CONVERTER_RUNNER_H 20 | 21 | #include "../../common/include/process_observer.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | class EncodeParameter; 30 | class ProcessParameter; 31 | 32 | /** 33 | * @brief Helper class to manage conversion operations with progress tracking 34 | * 35 | * This class encapsulates the common pattern of: 36 | * - Input validation 37 | * - Progress bar management 38 | * - Button state management 39 | * - Thread-based conversion execution 40 | * - Completion handling with success/error messages 41 | * 42 | * Usage: 43 | * @code 44 | * ConverterRunner *runner = new ConverterRunner( 45 | * progressBar, progressLabel, convertButton, 46 | * "Converting...", "Convert", 47 | * "Success", "File converted successfully!", 48 | * "Error", "Failed to convert file.", 49 | * this 50 | * ); 51 | * 52 | * connect(runner, &ConverterRunner::ConversionFinished, this, &MyPage::OnConversionFinished); 53 | * 54 | * runner->RunConversion(inputPath, outputPath, encodeParam, processParam); 55 | * @endcode 56 | */ 57 | class ConverterRunner : public QObject, public ProcessObserver { 58 | Q_OBJECT 59 | 60 | public: 61 | /** 62 | * @brief Construct a ConverterRunner 63 | * @param progressBar Progress bar widget to show conversion progress 64 | * @param progressLabel Label to show time remaining 65 | * @param actionButton Button to trigger conversion (will be disabled during conversion) 66 | * @param runningButtonText Text to show on button during conversion (e.g., "Converting...") 67 | * @param idleButtonText Text to show on button when idle (e.g., "Convert") 68 | * @param successTitle Title for success message box 69 | * @param successMessage Message for success message box 70 | * @param errorTitle Title for error message box 71 | * @param errorMessage Message for error message box 72 | * @param parent Parent QObject 73 | */ 74 | explicit ConverterRunner(QProgressBar *progressBar, 75 | QLabel *progressLabel, 76 | QPushButton *actionButton, 77 | const QString &runningButtonText, 78 | const QString &idleButtonText, 79 | const QString &successTitle, 80 | const QString &successMessage, 81 | const QString &errorTitle, 82 | const QString &errorMessage, 83 | QObject *parent = nullptr); 84 | 85 | ~ConverterRunner() override; 86 | 87 | /** 88 | * @brief Run conversion in a separate thread 89 | * @param inputPath Input file path 90 | * @param outputPath Output file path 91 | * @param encodeParam Encoding parameters (ownership transferred to runner) 92 | * @param processParam Process parameters (ownership transferred to runner) 93 | * @param transcoderName Transcoder name (default: "FFMPEG") 94 | * @return true if conversion started successfully, false if validation failed 95 | */ 96 | bool RunConversion(const QString &inputPath, 97 | const QString &outputPath, 98 | EncodeParameter *encodeParam, 99 | ProcessParameter *processParam, 100 | const QString &transcoderName = "FFMPEG"); 101 | 102 | /** 103 | * @brief Set custom validation function 104 | * @param validator Function that returns true if validation passes, false otherwise 105 | */ 106 | void SetValidator(std::function validator); 107 | 108 | /** 109 | * @brief Set custom completion handler (called before showing message box) 110 | * @param handler Function called with success status 111 | */ 112 | void SetCompletionHandler(std::function handler); 113 | 114 | // ProcessObserver interface 115 | void on_process_update(double progress) override; 116 | void on_time_update(double timeRequired) override; 117 | 118 | signals: 119 | /** 120 | * @brief Emitted when conversion finishes 121 | * @param success true if conversion succeeded, false otherwise 122 | */ 123 | void ConversionFinished(bool success); 124 | 125 | private: 126 | void ShowProgressUI(); 127 | void HideProgressUI(); 128 | void SetButtonRunning(); 129 | void SetButtonIdle(); 130 | void ShowCompletionMessage(bool success); 131 | 132 | QProgressBar *progressBar; 133 | QLabel *progressLabel; 134 | QPushButton *actionButton; 135 | QString runningButtonText; 136 | QString idleButtonText; 137 | QString successTitle; 138 | QString successMessage; 139 | QString errorTitle; 140 | QString errorMessage; 141 | 142 | std::function customValidator; 143 | std::function customCompletionHandler; 144 | }; 145 | 146 | #endif // CONVERTER_RUNNER_H 147 | -------------------------------------------------------------------------------- /src/component/include/file_selector_widget.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef FILE_SELECTOR_WIDGET_H 19 | #define FILE_SELECTOR_WIDGET_H 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | /** 27 | * @brief Reusable widget for file selection (input or output) 28 | * 29 | * This widget encapsulates the common pattern of: 30 | * - QGroupBox with title 31 | * - QLineEdit for file path (read-only) 32 | * - QPushButton for browsing 33 | * 34 | * Reduces code duplication across all page classes. 35 | */ 36 | class FileSelectorWidget : public QGroupBox { 37 | Q_OBJECT 38 | 39 | public: 40 | enum SelectorType { 41 | InputFile, // For selecting existing files (QFileDialog::getOpenFileName) 42 | OutputFile // For selecting save location (QFileDialog::getSaveFileName) 43 | }; 44 | 45 | /** 46 | * @brief Construct a file selector widget (simple version) 47 | * @param title Group box title (e.g., "Input File", "Output File") 48 | * @param type Whether this is for input or output file selection 49 | * @param parent Parent widget 50 | */ 51 | explicit FileSelectorWidget(const QString &title, SelectorType type, QWidget *parent = nullptr); 52 | 53 | /** 54 | * @brief Construct a file selector widget with full configuration 55 | * @param title Group box title (e.g., "Input File", "Output File") 56 | * @param type Whether this is for input or output file selection 57 | * @param placeholder Placeholder text for the line edit 58 | * @param fileFilter File dialog filter (e.g., "Video Files (*.mp4);;All Files (*.*)") 59 | * @param dialogTitle File dialog title 60 | * @param parent Parent widget 61 | */ 62 | explicit FileSelectorWidget(const QString &title, 63 | SelectorType type, 64 | const QString &placeholder, 65 | const QString &fileFilter, 66 | const QString &dialogTitle, 67 | QWidget *parent = nullptr); 68 | 69 | /** 70 | * @brief Set the file path displayed in the line edit 71 | */ 72 | void SetFilePath(const QString &path); 73 | 74 | /** 75 | * @brief Get the current file path 76 | */ 77 | QString GetFilePath() const; 78 | 79 | /** 80 | * @brief Set placeholder text for the line edit 81 | */ 82 | void SetPlaceholder(const QString &text); 83 | 84 | /** 85 | * @brief Set the file dialog filter 86 | * @param filter Qt file dialog filter format (e.g., "Video Files (*.mp4 *.avi);;All Files (*.*)") 87 | */ 88 | void SetFileFilter(const QString &filter); 89 | 90 | /** 91 | * @brief Set the file dialog title 92 | */ 93 | void SetDialogTitle(const QString &title); 94 | 95 | /** 96 | * @brief Enable or disable the browse button 97 | */ 98 | void SetBrowseEnabled(bool enabled); 99 | 100 | /** 101 | * @brief Get the line edit widget (for direct access if needed) 102 | */ 103 | QLineEdit* GetLineEdit() const { return fileLineEdit; } 104 | 105 | /** 106 | * @brief Get the browse button widget (for direct access if needed) 107 | */ 108 | QPushButton* GetBrowseButton() const { return browseButton; } 109 | 110 | /** 111 | * @brief Get the batch button widget (for direct access if needed) 112 | */ 113 | QPushButton* GetBatchButton() const { return batchButton; } 114 | 115 | /** 116 | * @brief Enable or disable batch mode button 117 | */ 118 | void SetBatchEnabled(bool enabled); 119 | 120 | /** 121 | * @brief Get the list of batch files (if batch mode was used) 122 | */ 123 | QStringList GetBatchFiles() const { return batchFiles; } 124 | 125 | /** 126 | * @brief Check if batch mode is active (multiple files selected) 127 | */ 128 | bool IsBatchMode() const { return !batchFiles.isEmpty(); } 129 | 130 | /** 131 | * @brief Clear batch files and exit batch mode 132 | */ 133 | void ClearBatchFiles(); 134 | 135 | /** 136 | * @brief Retranslate UI when language changes 137 | */ 138 | void RetranslateUi(); 139 | 140 | signals: 141 | /** 142 | * @brief Emitted when user selects a file via the browse button 143 | * @param filePath The selected file path 144 | */ 145 | void FileSelected(const QString &filePath); 146 | 147 | /** 148 | * @brief Emitted when the file path text changes 149 | */ 150 | void FilePathChanged(const QString &filePath); 151 | 152 | /** 153 | * @brief Emitted when batch files are selected 154 | * @param files List of selected files 155 | */ 156 | void BatchFilesSelected(const QStringList &files); 157 | 158 | private slots: 159 | void OnBrowseClicked(); 160 | void OnBatchClicked(); 161 | 162 | private: 163 | void SetupUI(); 164 | QString ConvertFilterToBatchFormat(const QString &qtFilter) const; 165 | 166 | SelectorType selectorType; 167 | QLineEdit *fileLineEdit; 168 | QPushButton *browseButton; 169 | QPushButton *batchButton; 170 | QString fileFilter; // Qt dialog filter format: "Video Files (*.mp4);;All Files (*.*)" 171 | QString dialogTitle; 172 | QStringList batchFiles; 173 | }; 174 | 175 | #endif // FILE_SELECTOR_WIDGET_H 176 | -------------------------------------------------------------------------------- /src/transcoder/src/transcoder_bmf.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2025 Jack Lau 3 | * Email: jacklau1222gm@gmail.com 4 | * 5 | * This file is free software; you can redistribute it and/or 6 | * modify it under the terms of the GNU Lesser General Public 7 | * License as published by the Free Software Foundation; either 8 | * version 2.1 of the License, or (at your option) any later version. 9 | * 10 | * This file is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | * Lesser General Public License for more details. 14 | */ 15 | 16 | #include "../include/transcoder_bmf.h" 17 | 18 | /* Receive pointers from converter */ 19 | TranscoderBMF::TranscoderBMF(ProcessParameter *process_parameter, 20 | EncodeParameter *encode_parameter) 21 | : Transcoder(process_parameter, encode_parameter) { 22 | frame_total_number = 0; 23 | } 24 | 25 | bmf_sdk::CBytes TranscoderBMF::decoder_callback(bmf_sdk::CBytes input) { 26 | std::string str_info; 27 | str_info.assign(reinterpret_cast(input.buffer), input.size); 28 | // BMFLOG(BMF_INFO) << "====Callback==== " << str_info; 29 | 30 | std::regex frame_regex(R"(\btotal frame number:\s*(\d+))"); 31 | std::smatch match; 32 | 33 | if (std::regex_search(str_info, match, frame_regex) && match.size() > 1) { 34 | std::istringstream(match[1]) >> frame_total_number; // Convert to int 35 | BMFLOG(BMF_DEBUG) << "Extracted Frame Number: " << frame_total_number; 36 | } else { 37 | BMFLOG(BMF_WARNING) << "Failed to extract frame number"; 38 | } 39 | 40 | uint8_t bytes[] = {97, 98, 99, 100, 101, 0}; 41 | return bmf_sdk::CBytes{bytes, 6}; 42 | } 43 | 44 | bmf_sdk::CBytes TranscoderBMF::encoder_callback(bmf_sdk::CBytes input) { 45 | std::string str_info; 46 | str_info.assign(reinterpret_cast(input.buffer), input.size); 47 | // BMFLOG(BMF_INFO) << "====Callback==== " << str_info; 48 | 49 | std::regex frame_regex(R"(\bframe number:\s*(\d+))"); 50 | std::smatch match; 51 | 52 | if (std::regex_search(str_info, match, frame_regex) && match.size() > 1) { 53 | std::istringstream(match[1]) >> frame_number; // Convert to int 54 | BMFLOG(BMF_DEBUG) << "Extracted Total Frame Number: " << frame_number; 55 | 56 | send_process_parameter(frame_number, frame_total_number); 57 | 58 | if (frame_number == frame_total_number) { 59 | BMFLOG(BMF_INFO) << "====Callback==== Finish"; 60 | } 61 | 62 | } else { 63 | BMFLOG(BMF_WARNING) << "Failed to extract frame number"; 64 | } 65 | 66 | uint8_t bytes[] = {97, 98, 99, 100, 101, 0}; 67 | return bmf_sdk::CBytes{bytes, 6}; 68 | } 69 | 70 | bool TranscoderBMF::prepare_info(std::string input_path, 71 | std::string output_path) { 72 | // decoder init 73 | if (encode_parameter->get_video_codec_name() == "") { 74 | copy_video = true; 75 | } else { 76 | copy_video = false; 77 | } 78 | 79 | if (encode_parameter->get_audio_codec_name() == "") { 80 | copy_audio = true; 81 | } else { 82 | copy_audio = false; 83 | } 84 | 85 | nlohmann::json de_video_codec = {"video_codec", ""}; 86 | nlohmann::json de_audio_codec = {"audio_codec", ""}; 87 | 88 | if (copy_video) { 89 | de_video_codec = {"video_codec", "copy"}; 90 | } 91 | if (copy_audio) { 92 | de_audio_codec = {"audio_codec", "copy"}; 93 | } 94 | 95 | decoder_para = { 96 | {"input_path", input_path}, 97 | de_video_codec, 98 | de_audio_codec, 99 | }; 100 | 101 | // encoder init 102 | // Build video_params object with only valid parameters 103 | nlohmann::json video_params = nlohmann::json::object(); 104 | 105 | // Always add codec and bitrate 106 | video_params["codec"] = encode_parameter->get_video_codec_name(); 107 | video_params["bit_rate"] = encode_parameter->get_video_bit_rate(); 108 | 109 | // Only add width if it's set (> 0) 110 | width = encode_parameter->get_width(); 111 | if (width > 0) { 112 | video_params["width"] = width; 113 | } 114 | 115 | // Only add height if it's set (> 0) 116 | height = encode_parameter->get_height(); 117 | if (height > 0) { 118 | video_params["height"] = height; 119 | } 120 | 121 | // Only add qscale if it's set (not -1) 122 | int qscale = encode_parameter->get_qscale(); 123 | if (qscale >= 0) { 124 | video_params["qscale"] = qscale; 125 | } 126 | 127 | // Only add pixel format if it's set (not empty) 128 | std::string pixel_format = encode_parameter->get_pixel_format(); 129 | if (!pixel_format.empty()) { 130 | video_params["pixel_format"] = pixel_format; 131 | } 132 | 133 | // Build audio_params object 134 | nlohmann::json audio_params = nlohmann::json::object(); 135 | audio_params["codec"] = encode_parameter->get_audio_codec_name(); 136 | audio_params["bit_rate"] = encode_parameter->get_audio_bit_rate(); 137 | 138 | encoder_para = {{"output_path", output_path}, 139 | {"video_params", video_params}, 140 | {"audio_params", audio_params}}; 141 | return true; 142 | } 143 | 144 | bool TranscoderBMF::transcode(std::string input_path, std::string output_path) { 145 | 146 | prepare_info(input_path, output_path); 147 | int scheduler_cnt = 0; 148 | 149 | auto graph = bmf::builder::Graph(bmf::builder::NormalMode); 150 | 151 | auto decoder = 152 | graph.Decode(bmf_sdk::JsonParam(decoder_para), "", scheduler_cnt++); 153 | 154 | auto encoder = 155 | graph.Encode(decoder["video"], decoder["audio"], 156 | bmf_sdk::JsonParam(encoder_para), "", scheduler_cnt++); 157 | 158 | auto de_callback = std::bind(&TranscoderBMF::decoder_callback, this, 159 | std::placeholders::_1); 160 | auto en_callback = std::bind(&TranscoderBMF::encoder_callback, this, 161 | std::placeholders::_1); 162 | 163 | decoder.AddCallback( 164 | 0, std::function(de_callback)); 165 | encoder.AddCallback( 166 | 0, std::function(en_callback)); 167 | 168 | nlohmann::json graph_para = {{"dump_graph", 1}}; 169 | graph.SetOption(bmf_sdk::JsonParam(graph_para)); 170 | 171 | if (graph.Run() == 0) { 172 | return true; 173 | } else { 174 | return false; 175 | } 176 | } 177 | --------------------------------------------------------------------------------